Skip to content

simmsb/rust-rel8

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust Rel8

Rust reimplementation of Rel8

Crates.io Version docs.rs

This is a WIP library attempting to emulate the Haskell Rel8 library.

In essence, it allows you to write queries like so:

#[derive(Debug, PartialEq, rust_rel8_derive::TableStruct)]
struct User<'scope, Mode: TableMode> {
    id: Mode::T<'scope, i32>,
    name: Mode::T<'scope, String>,
    age: Mode::T<'scope, i32>,
}

impl<'scope> User<'scope, NameMode> {
    const SCHEMA: TableSchema<Self> = TableSchema {
        name: "users",
        columns: User {
            id: "id",
            name: "name",
            age: "age",
        },
    };
}

#[derive(Debug, PartialEq, rust_rel8_derive::TableStruct)]
struct Post<'scope, Mode: TableMode> {
    id: Mode::T<'scope, i32>,
    user_id: Mode::T<'scope, i32>,
    contents: Mode::T<'scope, String>,
}

impl<'scope> Post<'scope, NameMode> {
    const SCHEMA: TableSchema<Self> = TableSchema {
        name: "posts",
        columns: Post {
            id: "id",
            user_id: "user_id",
            contents: "contents",
        },
    };
}

// We're injecting the values manually here, but we could as well use Query::each
// to select from a real table.
let demo_users = vec![
    User::<ValueMode> {id: 0, name: "Undine".to_owned(), age: 3,},
    User {id: 1, name: "Leschy".to_owned(), age: 3,},
    User {id: 2, name: "Huldra".to_owned(), age: 2,},
];

let demo_posts = vec![
    Post::<ValueMode> {id: 0, user_id: 0, contents: "Croak".to_owned(),},
    Post {id: 1, user_id: 1, contents: "Quak!".to_owned(),},
    Post {id: 2, user_id: 1, contents: "Hello".to_owned(),},
];

let q = query::<(Expr<String>, Expr<String>)>(|q| {
    // select all rows from the users and posts tables.
    let user = q.q(Query::values(demo_users.shorten_lifetime()));
    let post = q.q(Query::values(demo_posts.shorten_lifetime()));
    
    // introduce a join condition using a filter
    // this will cause postgres to turn this from a cross join into an inner join.
    q.where_(user.id.clone().equals(post.user_id.clone()));
    
    // select the user name and post contents out, and return a tuple
    (user.name, post.contents)
})
.order_by(|x| (x.clone(), sea_query::Order::Asc));

let rows = q.all(&mut *pool).await.unwrap();

assert_eq!(
    vec![
        ("Leschy".to_owned(), "Hello".to_owned()),
        ("Leschy".to_owned(), "Quak!".to_owned()),
        ("Undine".to_owned(), "Croak".to_owned())
    ],
    rows
)

In this query builder, joins are also left to the database to figure out. The user thinks of each let ... = q.q(...); inside query as being cartesian produced. The database narrows these cartesian products down to inner or left joins depending on the where clauses.

I'll add some more examples in the future, but you can also look at the tests in lib.rs.

About

A new way to write database queries in rust

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages