sqll 0.1.3

Usable low-level interface to sqlite that doesn't get in your way
Documentation
sqll-0.1.3 has been yanked.

sqll

Low-level interface to the SQLite database.

This is a rewrite of the sqlite crate, and components used from there have been copied under the MIT license.

Examples

  • examples/axum.rs - Create an in-memory database connection and serve it using axum. This showcases how do properly handle external synchronization for the best performance.

Why do we need another sqlite interface?

It is difficult to set up and use prepared statements with existing crates, because they are all implemented in a manner which requires the caller to borrow the connection in use.

Prepared statements can be expensive to create and should be cached and re-used to achieve the best performance. Statements can also benefit from using the Prepare::PERSISTENT option This library uses sqlite3_close_v2 when the connection is dropped, causing the closing of the connection to be delayed until resources associated with it has been closed.

We've also designed this library to avoid intermediary allocations. So for example calling execute doesn't allocate externally of the sqlite3 bindings. This was achieved by porting the execute implementation from the sqlite library and works because sqlite actually uses UTF-8 internally but this is not exposed in the legacy C API that other crates use to execute statements.

Example

Open an in-memory connection, create a table, and insert some rows:

use sqll::Connection;

let c = Connection::open_memory()?;

c.execute(
    r#"
    CREATE TABLE users (name TEXT, age INTEGER);

    INSERT INTO users VALUES ('Alice', 42);
    INSERT INTO users VALUES ('Bob', 69);
    "#,
)?;

Prepared Statements

Correct handling of prepared statements are crucial to get good performance out of sqlite. They contain all the state associated with a query and are expensive to construct so they should be re-used.

Using a Prepare::PERSISTENT prepared statement to perform multiple queries:

use sqll::{Connection, Prepare};

let c = Connection::open_memory()?;
c.execute(r#"
    CREATE TABLE users (name TEXT, age INTEGER);

    INSERT INTO users VALUES ('Alice', 42);
    INSERT INTO users VALUES ('Bob', 69);
"#)?;

let mut stmt = c.prepare_with("SELECT * FROM users WHERE age > ?", Prepare::PERSISTENT)?;

let mut results = Vec::new();

for age in [40, 50] {
    stmt.reset()?;
    stmt.bind(1, age)?;

    while let Some(row) = stmt.next()? {
        results.push((row.read::<String>(0)?, row.read::<i64>(1)?));
    }
}

let expected = vec![
    (String::from("Alice"), 42),
    (String::from("Bob"), 69),
    (String::from("Bob"), 69),
];

assert_eq!(results, expected);