https://github.com/udoprog/sqll
Efficient interface to SQLite that doesn't get in your way
https://github.com/udoprog/sqll
rust sqlite sqlite3
Last synced: 21 days ago
JSON representation
Efficient interface to SQLite that doesn't get in your way
- Host: GitHub
- URL: https://github.com/udoprog/sqll
- Owner: udoprog
- License: other
- Created: 2022-04-25T05:02:13.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2026-01-12T13:10:22.000Z (22 days ago)
- Last Synced: 2026-01-12T19:39:54.961Z (22 days ago)
- Topics: rust, sqlite, sqlite3
- Language: C
- Homepage:
- Size: 2.66 MB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# sqll
[
](https://github.com/udoprog/sqll)
[
](https://crates.io/crates/sqll)
[
](https://docs.rs/sqll)
[
](https://github.com/udoprog/sqll/actions?query=branch%3Amain)
Efficient interface to [SQLite] that doesn't get in your way.
## Usage
The two primary methods to interact with an SQLite database through this
crate is through [`execute`] and [`prepare`].
The [`execute`] function is used for batch statements, and allows for
multiple queries to be specified. [`prepare`] only allows for a single
statement to be specified, but in turn permits [reading rows] and [binding
query parameters].
Special consideration needs to be taken about the thread safety of
connections. You can read more about that in the [`Connection`]
documentation.
You can find simple examples of this below.
#### Examples
* [`examples/persons.rs`] - A simple table with users, a primary key,
inserting and querying.
* [`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.
#### Connecting and querying
Here is a simple example of setting up an in-memory connection, creating a
table, insert and query some rows:
```rust
use sqll::{Connection, Result};
let c = Connection::open_in_memory()?;
c.execute(r#"
CREATE TABLE users (name TEXT, age INTEGER);
INSERT INTO users VALUES ('Alice', 42);
INSERT INTO users VALUES ('Bob', 52);
"#)?;
let results = c.prepare("SELECT name, age FROM users ORDER BY age")?
.iter::<(String, u32)>()
.collect::>>()?;
assert_eq!(results, [("Alice".to_string(), 42), ("Bob".to_string(), 52)]);
```
#### The [`Row`] trait.
The [`Row`] trait can be used to conveniently read rows from a statement
using [`next`]. It can be conveniently implemented using the [`Row`
derive].
```rust
use sqll::{Connection, Row};
#[derive(Row)]
struct Person<'stmt> {
name: &'stmt str,
age: u32,
}
let mut c = Connection::open_in_memory()?;
c.execute(r#"
CREATE TABLE users (name TEXT, age INTEGER);
INSERT INTO users VALUES ('Alice', 42);
INSERT INTO users VALUES ('Bob', 52);
"#)?;
let mut results = c.prepare("SELECT name, age FROM users ORDER BY age")?;
while let Some(person) = results.next::>()? {
println!("{} is {} years old", person.name, person.age);
}
```
#### The [`Bind`] trait.
The [`Bind`] trait can be used to conveniently [`bind`] parameters to
prepared statements, and it can conveniently be implemented for structs
using the [`Bind` derive].
```rust
use sqll::{Bind, Connection, Row};
#[derive(Bind, Row, PartialEq, Debug)]
#[sql(named)]
struct Person<'stmt> {
name: &'stmt str,
age: u32,
}
let c = Connection::open_in_memory()?;
c.execute(r#"
CREATE TABLE persons (name TEXT, age INTEGER);
"#)?;
let mut stmt = c.prepare("INSERT INTO persons (name, age) VALUES (:name, :age)")?;
stmt.execute(Person { name: "Alice", age: 30 })?;
stmt.execute(Person { name: "Bob", age: 40 })?;
let mut query = c.prepare("SELECT name, age FROM persons ORDER BY age")?;
let p = query.next::>()?;
assert_eq!(p, Some(Person { name: "Alice", age: 30 }));
let p = query.next::>()?;
assert_eq!(p, Some(Person { name: "Bob", age: 40 }));
```
#### Efficient use of 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:
```rust
use sqll::{Connection, Prepare};
let c = Connection::open_in_memory()?;
c.execute(r#"
CREATE TABLE users (name TEXT, age INTEGER);
INSERT INTO users VALUES ('Alice', 42);
INSERT INTO users VALUES ('Bob', 52);
"#)?;
let mut stmt = c.prepare_with("SELECT * FROM users WHERE age > ?", Prepare::PERSISTENT)?;
let mut rows = Vec::new();
for age in [40, 50] {
stmt.bind(age)?;
while let Some(row) = stmt.next::<(String, i64)>()? {
rows.push(row);
}
}
let expected = vec![
(String::from("Alice"), 42),
(String::from("Bob"), 52),
(String::from("Bob"), 52),
];
assert_eq!(rows, expected);
```
## Features
* `std` - Enable usage of the Rust standard library. Enabled by default.
* `alloc` - Enable usage of the Rust alloc library. This is required and is
enabled by default. Disabling this option will currently cause a compile
error.
* `derive` - Add a dependency to and re-export of the [`Row` derive]
macro.
* `bundled` - Use a bundled version of sqlite. The bundle is provided by the
[`sqll-sys`] crate and the sqlite version used is part of the build
metadata of that crate[^sqll-sys].
* `threadsafe` - Enable usage of sqlite with the threadsafe option set. We
assume any system level libraries have this build option enabled. If this
is disabled the `bundled` feature has to be enabled. If `threadsafe` is
disabled, `Connection` and `Statement` does not implement `Send`. But it
is also important to understand that if this option is not set, sqlite
**may not be used by multiple threads at all** even if threads have
distinct connections. To disable mutexes which allows for efficient one
connection per thread the [`OpenOptions::no_mutex`] option should be used
instead[^sqll-sys].
* `strict` - Enable usage of sqlite with the strict compiler options
enabled[^sqll-sys].
[^sqll-sys]: This is a forwarded sqll-sys option, see .
## Why do we need another sqlite interface?
For other low-level crates, it is difficult to set up and use prepared
statements, They are mostly implemented in a manner which requires the
caller to borrow the connection in use.
This library implements database objects through the v2 API which ensures
that the database remains alive for as long as objects associated with it
are alive. This is implemented in the SQLite library itself.
Prepared statements can be expensive to create and *should* be stored and
re-used to achieve the best performance. This is something that crates like
`rusqlite` implements, but can easily be done manually, with no overhead, by
simply storing the [`Statement`] object directly behind a mutex. Statements
can also benefit from using the [`Prepare::PERSISTENT`] option which this
library supports through [`prepare_with`].
This library is designed to the extent possible to avoid intermediary
allocations. For example [calling `execute`] doesn't allocate externally of
the sqlite3 bindings or we require that c-strings are used when SQLite
itself doesn't provide an API for using Rust strings directly. It's also
implemented as a thing layer on top of SQLite with minimal added
abstractions ensuring you get the best possible performance.
## License
This is a rewrite of the [`sqlite` crate], and components used from there
have been copied under the MIT license.
[`axum`]: https://docs.rs/axum
[`Bind` derive]: https://docs.rs/sqll/latest/sqll/derive.Bind.html
[`bind`]: https://docs.rs/sqll/latest/sqll/struct.Statement.html#method.bind
[`Bind`]: https://docs.rs/sqll/latest/sqll/trait.Bind.html
[`Connection`]: https://docs.rs/sqll/latest/sqll/struct.Connection.html#thread-safety
[`examples/axum.rs`]: https://github.com/udoprog/sqll/blob/main/examples/axum.rs
[`examples/persons.rs`]: https://github.com/udoprog/sqll/blob/main/examples/persons.rs
[`execute`]: https://docs.rs/sqll/latest/sqll/struct.Connection.html#method.execute
[`Row` derive]: https://docs.rs/sqll/latest/sqll/derive.Row.html
[`Row`]: https://docs.rs/sqll/latest/sqll/trait.Row.html
[`next`]: https://docs.rs/sqll/latest/sqll/struct.Statement.html#method.next
[`OpenOptions::no_mutex`]: https://docs.rs/sqll/latest/sqll/struct.OpenOptions.html#method.no_mutex
[`prepare_with`]: https://docs.rs/sqll/latest/sqll/struct.Connection.html#method.prepare_with
[`Prepare::PERSISTENT`]: https://docs.rs/sqll/latest/sqll/struct.Prepare.html#associatedconstant.PERSISTENT
[`prepare`]: https://docs.rs/sqll/latest/sqll/struct.Connection.html#method.prepare
[`sqlite` crate]: https://github.com/stainless-steel/sqlite
[`sqll-sys`]: https://crates.io/crates/sqll-sys
[`Statement`]: https://docs.rs/sqll/latest/sqll/struct.Statement.html
[binding query parameters]: https://docs.rs/sqll/latest/sqll/struct.Statement.html#method.bind
[calling `execute`]: https://docs.rs/sqll/latest/sqll/struct.Connection.html#method.execute
[reading rows]: https://docs.rs/sqll/latest/sqll/struct.Statement.html#method.next
[SQLite]: https://www.sqlite.org