Ormlite - An ORM in Rust for developers that love SQL.

Overview

ormlite

ormlite is an ORM in Rust for developers that love SQL.

It provides the following, while staying close to SQL, both in syntax and performance:

  • Struct methods for database interface (e.g. .insert(), .delete(), .update())
  • Builder syntax for select queries and partial column insertions and updates

We prioritize these objectives in the project:

  • Fast: We aim for minimal to no measurable overhead for using the ORM.
  • True to SQL: Where logical, the API interface is Just Plain Old SQL. We eschew custom query syntax so that users don't have to learn yet another query syntax.
  • async-first: We built on top of the great foundation of sqlx, allowing our API design to be fully async.
  • No Surprises: We want an API that is explicit and locally understandable and doesn't require cross-referencing other parts of the codebase or specific knowledge of the ORM. We maintain ergonomics by making decisions driven by real-world usage of the library.

NOTE: This is alpha-quality and being actively developed. In usage so far, the software is functional, performant, and correct, but until it undergoes more rigorous battle testing, we recommend vetting the code yourself before using the crate in production environments.

Usage

use ormlite::model::*;

#[derive(ormlite::Model)]
pub struct Person {
    pub id: u32,
    pub name: String,
    pub age: u8,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// Start by making a sqlx connection.
    let mut conn = sqlx::SqliteConnection::connect_with(&sqlx::sqlite::SqliteConnectOptions::from_str("sqlite://:memory:").unwrap()).await?;
    
    /// You can insert the model directly.
    let mut john = Person {
        id: 1,
        name: "John".to_string(),
        age: 99,
    }.insert(&mut conn).await;
    
    /// After modifying the object, you can update all its fields.
    john.age += 1;
    john.update_all_fields(&mut conn).await?;
    
    /// Lastly, you can delete the object.
    john.delete(&mut conn).await?;
    
    /// You can use builder syntax to do insertion with only certain fields.
    let john = Person::build()
        .name("John".to_string())
        .age(99)
        .insert(&mut conn).await?;
    
    /// You can also use builder syntax to update only certain fields.
    let john = john.update_partial()
        .age(100)
        .update(&mut conn).await?;
    
    /// You can get a single user.
    let john = Person::get_one(1, &mut conn).await?;
  
    /// You can create a query builder.
    let people = Person::select()
            .filter("age > ?").bind(50)
            .fetch_all(&mut conn).await?;
  
    /// You can also fall back to raw sql.
    let people = Person::query("SELECT * FROM person WHERE age > ?")
            .bind(50)
            .fetch_all(&mut conn).await?;
    Ok(())
}

Partial Structs

If, instead of builder syntax, you prefer to create partial structs to statically enforce which columns are affected for insertions, use the following:

use ormlite::model::*;

#[derive(ormlite::Model)]
#[ormlite(table = "person", insert = InsertPerson)]
pub struct Person {
    pub id: u32,
    pub name: String,
    pub age: u8,
}

async fn do_partial_insertion() {
    let mut john = InsertPerson {
        name: "John".to_string(),
        age: 99,
    }.insert(&mut conn).await;
}

If you want to customize the fields that are inserted, just create a new model with the custom fields.

#[derive(ormlite::Model)]
#[ormlite(table = "person")]
pub struct InsertPerson {
    pub name: String,
    pub age: u8,
}

Installation

For postgres:

[dependencies]
ormlite = { version = "0.1.0", features = ["postgres", "runtime-tokio-rustls"]

For sqlite:

[dependencies]
ormlite = { version = "0.1.0", features = ["sqlite", "runtime-tokio-rustls"]

Other databases (mysql) and runtimes should work smoothly, but might not be 100% wired up yet. Please submit an issue if you encounter issues.

Roadmap

  • insert, update, delete directly on model instances
  • builder for partial update and insertions
  • user can create insert models that ignore default values
  • select query builder
  • build the derive macro
  • get() function for fetching a single entity.
  • ability to specify the name of a table and name of primary column
  • automatically generate insert models
  • make sure features are wired up correctly to support mysql and different runtimes & SSL libraries.
  • macro option to auto adjust columns like updated_at
  • upsert functionality
  • joins
  • bulk insertions
  • query builder for bulk update
  • handle on conflict clauses for bulk update
  • benchmarks against raw sql, sqlx, ormx, seaorm, sqlite3-sys, pg, diesel
  • macro option to delete with deleted_at rather than DELETE
  • support for patch records, i.e. update with static fields.
  • Consider a blocking interface, perhaps for sqlite/Rusqlite only.

Documentation

Logging

You can log queries using sqlx's logger: RUST_LOG=sqlx=info

Comments
  • Provide get_optional

    Provide get_optional

    Since there is no easy way to check why sqlx query failed it would be nice to have something similar to https://docs.rs/sqlx/0.5.10/sqlx/trait.Executor.html#tymethod.fetch_optional

    opened by heroin-moose 10
  • Unable to insert when passing value by reference to another function

    Unable to insert when passing value by reference to another function

    Code:

    use ormlite::Model;
    use ormlite::model::*;
    use sqlx::FromRow;
    use sqlx::SqlitePool;
    
    #[derive(Model, FromRow)]
    struct Example {
        #[ormlite(primary_key)]
        id: i64
    }
    
    async fn insert(e: &Example, pool: &SqlitePool) -> ormlite::Result<Example> {
        e.insert(pool).await
    }
    
    #[tokio::main]
    async fn main() -> anyhow::Result<()> {
        let pool = SqlitePool::connect("/tmp/database").await?;
        let e1 = Example { id: 20 };
    
        insert(&e1, &pool).await?;
    
        Ok(())
    }
    

    Error:

       Compiling ormlite-issue v0.1.0 (/tmp/ormlite-issue)
    error[E0507]: cannot move out of `*e` which is behind a shared reference
      --> src/main.rs:13:5
       |
    13 |     e.insert(pool).await
       |     ^^^^^^^^^^^^^^ move occurs because `*e` has type `Example`, which does not implement the `Copy` trait
    
    For more information about this error, try `rustc --explain E0507`.
    error: could not compile `ormlite-issue` due to previous error
    
    opened by heroin-moose 6
  • Provide get_many

    Provide get_many

    Currently it's possible to get one single entry using Example::get_one(). However, it's not clear how to get many entries without manually writing the query. So Example::get_many(&mut conn) would be nice to have.

    opened by heroin-moose 6
  • docs.rs fails to build documentation

    docs.rs fails to build documentation

    [INFO] [stderr]    Compiling sqlx-rt v0.5.10
    [INFO] [stderr]    Compiling dotenv v0.15.0
    [INFO] [stderr]     Checking slab v0.4.5
    [INFO] [stderr] error: one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', 'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', 'runtime-tokio-rustls'] must be enabled
    [INFO] [stderr]   --> /opt/rustwide/cargo-home/registry/src/github.com-1ecc6299db9ec823/sqlx-rt-0.5.10/src/lib.rs:9:1
    [INFO] [stderr]    |
    [INFO] [stderr] 9  | / compile_error!(
    [INFO] [stderr] 10 | |     "one of the features ['runtime-actix-native-tls', 'runtime-async-std-native-tls', \
    [INFO] [stderr] 11 | |      'runtime-tokio-native-tls', 'runtime-actix-rustls', 'runtime-async-std-rustls', \
    [INFO] [stderr] 12 | |      'runtime-tokio-rustls'] must be enabled"
    [INFO] [stderr] 13 | | );
    [INFO] [stderr]    | |_^
    [INFO] [stderr] 
    [INFO] [stderr] error: could not compile `sqlx-rt` due to previous error
    [INFO] [stderr] warning: build failed, waiting for other jobs to finish...
    [INFO] [stderr] error: build failed
    
    opened by heroin-moose 3
  • fix: escape raw identifier

    fix: escape raw identifier

    fix a unexpected behavior when we use r#x

    Example

    #[derive(Model, FromRow)]
    #[ormlite(table = "test")]
    pub struct TestTable {
       r#type: i32
    }
    
    TestTable { r#type: 1 }.insert(&mut db); // INSERT INTO "test" (r#type) VALUES (?)
    
    // The query should be "INSERT INTO "test" (type) VALUES (?)"
    
    opened by abdulrahman1s 2
  • Cannot pass &str to get_one()

    Cannot pass &str to get_one()

    The following example fails to compile:

    use anyhow::Result;
    use ormlite::Model;
    use ormlite::model::*;
    use sqlx::SqlitePool;
    use sqlx::FromRow;
    
    #[derive(Model, FromRow)]
    struct Example {
        #[ormlite(primary_key)]
        name: String
    }
    
    async fn get(pool: &SqlitePool, name: impl AsRef<str>) -> Result<Example> {
        Example::get_one(name.as_ref(), pool).await.map_err(|e| e.into())
    }
    
    #[tokio::main]
    async fn main() -> Result<()> {
        let pool = SqlitePool::connect("/tmp/database").await?;
        let example = get(&pool, "name");
        Ok(())
    }
    

    The error:

    error[E0277]: the trait bound `for<'r> &str: Encode<'r, sqlx::Sqlite>` is not satisfied
      --> src/main.rs:14:37
       |
    14 |     Example::get_one(name.as_ref(), pool).await.map_err(|e| e.into())
       |     ----------------                ^^^^ the trait `for<'r> Encode<'r, sqlx::Sqlite>` is not implemented for `&str`
       |     |
       |     required by a bound introduced by this call
       |
       = help: the following implementations were found:
                 <&'q str as Encode<'q, sqlx::Any>>
                 <&'q str as Encode<'q, sqlx::Sqlite>>
                 <&str as Encode<'_, sqlx::Postgres>>
    note: required by `get_one`
      --> /home/consus/.cargo/registry/src/github.com-1ecc6299db9ec823/ormlite-core-0.1.3/src/model.rs:77:5
       |
    77 | /     fn get_one<'e, 'a, Arg, E>(id: Arg, db: E) -> BoxFuture<'e, Result<Self>>
    78 | |     where
    79 | |         'a: 'e,
    80 | |         E: 'e + sqlx::Executor<'e, Database = DB>,
    81 | |         Arg: 'a + Send + for<'r> sqlx::Encode<'r, DB> + sqlx::Type<DB>;
       | |_______________________________________________________________________^
    
    For more information about this error, try `rustc --explain E0277`.
    error: could not compile `ormlite-issue` due to previous error
    

    However, changing name.as_ref() into name.as_ref().to_string() fixes it.

    opened by heroin-moose 2
  • Consider using #[error(transparent)]

    Consider using #[error(transparent)]

    Currently enum Error uses {0} to show the source error message. However, when printing with AnyHow {:#} format this results in duplicated error message:

    sqlx error: foobar: foobar
    

    It seems that #[error(transparent)] is a better choice for nested errors.

    opened by heroin-moose 0
  • 0.3.2 cannot use primary keys that are non Copy

    0.3.2 cannot use primary keys that are non Copy

    test tests/01-update-partial.rs ... error
    ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
    error[E0507]: cannot move out of a shared reference
     --> tests/01-update-partial.rs:8:10
      |
    8 | #[derive(Model, FromRow)]
      |          ^^^^^
      |          |
      |          move occurs because value has type `String`, which does not implement the `Copy` trait
      |          help: consider borrowing here: `&Model`
      |
      = note: this error originates in the derive macro `Model` (in Nightly builds, run with -Z macro-backtrace for more info)
    ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
    
    
    
    test tests ... FAILED
    
    failures:
    
    ---- tests stdout ----
    thread 'tests' panicked at '1 of 1 tests failed', /Users/andrew/.cargo/registry/src/github.com-1ecc6299db9ec823/trybuild-1.0.63/src/run.rs:101:13
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    
    failures:
        tests
    
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.52s
    
    error: test failed, to rerun pass '--test tests'
    error: Recipe `test` failed on line 28 with exit code 101
    error: Recipe `test` failed on line 2 with exit code 101
    

    After modifying the test example to be:

    #[derive(Model, FromRow)]
    pub struct Person {
        id: Uuid,
        #[ormlite(primary_key)]
        name: String,
        age: u8,
    }
    

    I'm hunting for the issue, but I'm fairly new to defining macros.

    opened by AndrewRademacher 3
  • Add ormlite to the Diesel benchmark

    Add ormlite to the Diesel benchmark

    I would like to invent you to add ormlite to the relational database connection crates benchmark suite located inside of the diesel repository. These benchmarks are run regularly and results are published here. This would give potential users a good overview over the current state + you would also have a comparison with other crates for free.

    opened by weiznich 0
  • Provide support for composite keys

    Provide support for composite keys

    It's common to have the table that is natural to describe as two columns (or more) columns instead of just one. For example, consider a Device that has multiple child Interface. Say, for servers ns[1-4].example.com there are interfaces eth0, eth1 and eth2. So, it can be described as such:

    CREATE TABLE devices (
    	name TEXT PRIMARY KEY
    );
    
    CREATE TABLE interfaces (
    	name        TEXT,
    	device_name TEXT,
    
    	PRIMARY KEY (name, device_name),
    
    	FOREIGN KEY (device_name)
    	 REFERENCES devices(name)
    	  ON DELETE CASCADE
    	  ON UPDATE CASCADE
    );
    

    In this case interface record is uniquely identified by (name, device_name) tuple, making it easy to search and delete. Having a single unique id column does not bring any value because removing an interface still happens by (name, device_name) query. It would be good to have composite keys in the table like sea-orm for example.

    opened by heroin-moose 3
Owner
Kurt Wolf
Kurt Wolf
🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, SQLite, and MSSQL.

SQLx ?? The Rust SQL Toolkit Install | Usage | Docs Built with ❤️ by The LaunchBadge team SQLx is an async, pure Rust† SQL crate featuring compile-tim

launchbadge 7.6k Dec 31, 2022
A Rust SQL query builder with a pleasant fluent API closely imitating actual SQL

Scooby An SQL query builder with a pleasant fluent API closely imitating actual SQL. Meant to comfortably build dynamic queries with a little bit of s

Aleksei Voronov 100 Nov 11, 2022
Gh-sql - Query GitHub Projects (beta) with SQL

gh-sql: Query GitHub Projects (beta) with SQL Installation gh extension install KOBA789/gh-sql Features SELECT items DELETE items UPDATE item fields

Hidekazu Kobayashi 108 Dec 7, 2022
SQL validator tool for BigQuery standard SQL.

bqvalid What bqvalid does bqvalid is the SQL validator tool for BigQuery standard SQL. bqvalid fails with error message if there's the expression that

null 10 Dec 25, 2022
rust_arango enables you to connect with ArangoDB server, access to database, execute AQL query, manage ArangoDB in an easy and intuitive way, both async and plain synchronous code with any HTTP ecosystem you love.

rust_arango enables you to connect with ArangoDB server, access to database, execute AQL query, manage ArangoDB in an easy and intuitive way, both async and plain synchronous code with any HTTP ecosystem you love.

Foretag 3 Mar 24, 2022
A safe, extensible ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Diesel 9.7k Jan 3, 2023
an orm for rust

rustorm Rustorm Rustorm is an SQL-centered ORM with focus on ease of use on conversion of database types to their appropriate rust type. Selecting rec

Jovansonlee Cesar 236 Dec 19, 2022
🐚 An async & dynamic ORM for Rust

SeaORM ?? An async & dynamic ORM for Rust SeaORM SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic lan

SeaQL 3.5k Jan 6, 2023
Diesel - A safe, extensible ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Takayuki Maeda 0 Aug 31, 2020
Diesel - ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Diesel 9.7k Jan 6, 2023
Rust High Performance compile-time ORM(RBSON based)

WebSite | 简体中文 | Showcase | 案例 A highly Performant,Safe,Dynamic SQL(Compile time) ORM framework written in Rust, inspired by Mybatis and MybatisPlus.

rbatis 1.7k Jan 7, 2023
ORM for ScyllaDb and Cassandra

ScyllaDb/Cassandra Object-Relation Mapper Features This library contains several crates with the following features: Automatic map tables to Rust stru

null 36 Jan 1, 2023
CRUD system of book-management with ORM and JWT for educational purposes.

Book management English | 中文 Required Rust MySQL 5.7 Usage Execute init.sql to create tables. Set environment variable DATABASE_URL and JWT_SECRET in

null 32 Dec 28, 2022
Bind the Prisma ORM query engine to any programming language you like ❤️

Prisma Query Engine C API Bind the Prisma ORM query engine to any programming language you like ❤️ Features Rust bindings for the C API Static link li

Prisma ORM for community 10 Dec 15, 2022
Bind the Prisma ORM query engine to any programming language you like ❤️

Prisma Query Engine C API Bind the Prisma ORM query engine to any programming language you like ❤️ Features Rust bindings for the C API Static link li

Odroe 6 Sep 9, 2022
TDS 7.2+ (mssql / Microsoft SQL Server) async driver for rust

Tiberius A native Microsoft SQL Server (TDS) client for Rust. Supported SQL Server versions Version Support level Notes 2019 Tested on CI 2017 Tested

Prisma 189 Dec 25, 2022
FeOphant - A SQL database server written in Rust and inspired by PostreSQL.

A PostgreSQL inspired SQL database written in Rust.

Christopher Hotchkiss 27 Dec 7, 2022
GlueSQL is a SQL database library written in Rust

GlueSQL is a SQL database library written in Rust. It provides a parser (sqlparser-rs), execution layer, and optional storage (sled) packaged into a single library.

GlueSQL 2.1k Jan 8, 2023
Fully typed SQL query builder for Rust [deprecated]

What is Deuterium? Deuterium is a fancy SQL builder for Rust. It's designed to provide a DSL to easily build SQL queries in safe and typed way. Like R

Stanislav Panferov 169 Nov 20, 2022