#orm #database-driver #sqlite #mysql #postgresql #sql-orm #metadata

rbdc

The Rust SQL Toolkit and ORM Library. An async, pure Rust SQL crate featuring compile-time Dynamic SQL

37 stable releases

4.6.0 May 22, 2025
4.5.52 Apr 29, 2025
4.5.48 Feb 22, 2025
4.5.45 Dec 12, 2024
0.1.22 Sep 15, 2022

#1069 in Database interfaces

Download history 552/week @ 2025-02-18 485/week @ 2025-02-25 263/week @ 2025-03-04 483/week @ 2025-03-11 445/week @ 2025-03-18 311/week @ 2025-03-25 269/week @ 2025-04-01 306/week @ 2025-04-08 351/week @ 2025-04-15 698/week @ 2025-04-22 920/week @ 2025-04-29 578/week @ 2025-05-06 1421/week @ 2025-05-13 946/week @ 2025-05-20 615/week @ 2025-05-27 229/week @ 2025-06-03

3,339 downloads per month
Used in 33 crates (15 directly)

Apache-2.0

185KB
5K SLoC

RBDC driver abstract

  • rbdc is safe code(#![forbid(unsafe_code)])
  • an database driver abstract for rbatis
  • supported database drivers see rbatis

how to define my driver to support rbdc driver?

just only impl this traits(6)

use rbdc::db::{Driver, MetaData, Row, Connection, ConnectOptions, Placeholder};

pub struct YourDriver{}
impl Driver for YourDriver{}

pub struct YourMetaData{}
impl MetaData for YourMetaData{}

pub struct YourRow{}
impl Row for YourRow{}

pub struct YourConnection{}
impl Connection for YourConnection{}

pub struct YourConnectOptions{}
impl ConnectOptions for YourConnectOptions{}

pub struct YourPlaceholder{}
impl Placeholder for YourPlaceholder{}

how to use my driver?

use rbdc_sqlite::SqliteDriver;
use rbdc::db::{Connection};
use rbdc::Error;
use rbdc::pool::ConnManager;
use rbdc::pool::Pool;
use rbdc_pool_fast::FastPool;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let pool = FastPool::new(ConnManager::new(SqliteDriver {}, "sqlite://target/test.db")?)?;
    let mut conn = pool.get().await?;
    // select
    let v = conn.get_values("select * from sqlite_master", vec![]).await?;
    println!("{}", rbs::Value::Array(v));
    // update/delete
    let r = conn.exec("update table set name='a' where id = 1", vec![]).await?;
    println!("{}", r);
    Ok(())
}

FAQ

How should I implement a driver for databases with blocking APIs?

For database drivers with blocking APIs, follow the pattern in rbdc-sqlite using the flume channel library:

// Key components:
// 1. Dedicated worker thread per connection
// 2. Command channels for communication

pub struct YourConnection {
    worker: ConnectionWorker,
}

struct ConnectionWorker {
    command_tx: flume::Sender<Command>,
}

enum Command {
    Execute { /* ... */ },
    Query { /* ... */ },
}

Benefits:

  • Prevents blocking the async runtime
  • Provides thread safety
  • Maintains a clean async interface

Why does Connection require both Send and Sync?

Connection: Send + Sync is required because:

  1. Thread Safety: Connections may be shared across tasks on different threads when using Tokio
  2. Pool Implementation: Connection pools need thread-safe access to connections

When implementing for non-thread-safe databases:

// SAFETY: YourConnection is thread-safe because:
// 1. Database operations run on a dedicated worker thread
// 2. Communication uses thread-safe channels
unsafe impl Sync for YourConnection {}

Improper implementation can cause data races and undefined behavior.

Dependencies

~7–20MB
~306K SLoC