Crate rust_query

Source
Expand description

§Type safe queries using the Rust type system

The goal of this library is to allow writing relational database queries using familiar Rust syntax. The library should guarantee that a query can not fail if it compiles. This already includes preventing use after free for row ids passed between queries and even database migrations!

Writing queries using this library involves:

  • Interact with row/column references as Rust values.
  • Lifetimes to check the scopes of row/column references.
  • Procedural mutation of row sets with methods like filter and join.

Notably it does not involve any new syntax or macro, while still being completely type safe.

§Roadmap

This project is under development and there are many things missing.

Query types:

  • SELECT
  • INSERT
  • UPDATE
  • DELETE

Basic operations:

  • Eq, Add, Not, And, Lt, UnwrapOr, IsNotNull, AsFloat, Like
  • Everything else

Advanced operations:

  • Aggregate
  • Window
  • Limit

Backend support:

  • sqlite
  • postgres
  • duckdb

Despite these limitations, I am dogfooding this query builder and using it in my own project: advent-of-wasm.

§What it looks like

Define a schema using enum syntax:

use rust_query::migration::schema;

#[schema]
enum MySchema {
    // Enum variants are database tables
    User {
        // This table has one column with String type.
        name: String,
    },
    Image {
        description: String,
        // This column has a foreign key constraint to the User table
        uploaded_by: User,
    },
}

Get proof that we are running on a unique thread:

let mut client = LocalClient::try_new().unwrap();

Initialize a database:

let database = client
    .migrator(Config::open("my_database.sqlite"))
    .expect("database version is before supported versions")
    // migrations go here
    .finish()
    .expect("database version is after supported versions");

Perform a transaction!

let mut transaction = client.transaction_mut(&database);
do_stuff_with_database(&mut transaction);
// After we are done we commit the changes!
transaction.commit();

Insert in the database:

// Lets make a new user 'mike',
let mike = User { name: "mike" };
let mike_id = db.insert(mike);
// and also insert a dog picture for 'mike'.
let dog_picture = Image {
    description: "dog",
    uploaded_by: mike_id,
};
let _picture_id = db.insert(dog_picture);

Query from the database:

// Now we want to get all pictures for 'mike'.
let mike_pictures = db.query(|rows| {
    // Initially there is one empty row.
    // Lets join the pictures table.
    let picture = Image::join(rows);
    // Now lets filter for pictures from mike,
    rows.filter(picture.uploaded_by().eq(mike_id));
    // and finally turn the rows into a vec.
    rows.into_vec(picture.description())
});

println!("{mike_pictures:?}"); // This should print `["dog"]`.

The full example code can be found in insert_and_select.rs

Some features not shown in this example are:

  • Migrations and unique constraints
  • Lookups by unique constraint
  • Aggregations

§Examples

For more example queries you can look at the chinook test.

Modules§

args
Types that are used as closure arguments.
migration
Types to declare schemas and migrations.

Structs§

Column
Values of this type reference a collumn in a query.
Database
Database is a proof that the database has been configured.
LocalClient
The primary interface to the database.
Rows
Rows keeps track of all rows in the current query.
TableRow
Row reference that can be used in any query in the same transaction.
Transaction
Transaction can be used to query the database.
TransactionMut
Same as Transaction, but allows inserting new rows.
TransactionWeak
This is the weak version of TransactionMut.
UnixEpoch
Use this a value in a query to get the current datetime as a number.

Traits§

Dummy
This trait is implemented by everything that can be retrieved from the database.
IntoColumn
Trait for all values that can be used in queries.
Table
This trait is implemented for all table types as generated by the crate::migration::schema macro.

Functions§

aggregate
Perform an aggregate that returns a single result for each of the current rows.

Derive Macros§

FromDummy
Derive FromDummy to create a new *Dummy struct.