10个版本

0.1.23 2022年3月6日
0.1.22 2022年3月6日
0.1.21 2022年2月25日
0.1.11 2022年1月28日
0.1.7 2021年12月30日

#2087 in 数据库接口

每月 34次下载
用于 5 crates

Apache-2.0

220KB
4.5K SLoC

cdbc

Coroutine Database driver Connectivity,基于 mco

  • 高并发,基于协程
  • 不使用 Future<'q,Output=*>,不使用 async fn,不使用 .await ,没有 Poll* 函数,不使用 Pin
  • 优化了特质系统,使其具有基类方法的智能提示
  • 支持原生Tls和TCP连接
  • 低耦合,数据库驱动和抽象层分别设计
  • 轻量级,没有过度设计,只有带有智能提示的宏
  • 受golang, mco, sqlx 启发
为什么选择cdbc ?
crates 并发 特性级别 所有智能提示 Libc 具有进程宏 分离驱动 支持env/crates
cdbc CSP(mco) 较低 仅sqlite 不需要 mcomco/std/httpnative-threadtokio-spawn_blocking
rbatis Future(tokio) 重量级 仅sqlite 仅py_sql,html_sql x tokio, async_std, smol
sqlx Future(tokio) 较低 x 仅sqlite 仅derive(StructOpt) x tokio, async_std, smol
diesel 原生线程 较低 x 所有Libc derive(Queryable) x 原生线程

并发基准性能(比较SQLX/Tokio/Async-std)

crates 每秒请求数 内存 CPU负载
cdbc-mco-http 4606 30MB 6%
sqlx-axum-tokio 4560 17MB 8%
sqlx-actix-async-std 559.00 22MB 2%
diesel * * *

数据库支持

  • cdbc 驱动抽象库。
  • cdbc-mysql CDBC mysql 驱动库
  • cdbc-pg CDBC postgres 驱动库
  • cdbc-sqlite CDBC sqlite 驱动库
  • cdbc-mssql CDBC 微软 mssql 驱动库

支持的功能

  • execute:执行查询并返回受影响的行数。
  • execute_many:执行多个查询,并以流的形式返回每个查询受影响的行数。
  • fetch:执行查询并将生成的结果作为流返回。
  • fetch_many:执行多个查询,并以流的形式返回每个查询生成的结果,每个查询的结果作为一个流。
  • fetch_all:执行查询并返回所有生成的结果,收集到一个 [Vec] 中。
  • fetch_one:执行查询并返回正好一行。
  • fetch_optional:执行查询并返回最多一行。
  • prepare:准备SQL查询以检查其参数和结果的数据类型信息。
  • prepare_with:准备带参数类型信息的SQL查询,以检查其参数和结果的数据类型信息。

支持事务

  • Pool:begin(),commit(),rollback()
  • Connection:begin(),commit(),rollback()

使用示例

cargo.toml

#must dep
cdbc = {version = "0.1"}
#optional dep
cdbc-mysql = {version = "0.1"}
cdbc-pg = {version = "0.1"}
cdbc-sqlite = {version = "0.1"}
  • CRUD
#[cdbc::crud]
#[derive(Debug, Clone)]
pub struct BizActivity {
    pub id: Option<String>,
    pub name: Option<String>,
    pub age: Option<i32>,
    pub delete_flag: Option<i32>,
}
fn main() -> cdbc::Result<()> {
    let pool = make_sqlite()?;
    let arg = BizActivity {
        id: Some("2".to_string()),
        name: Some("2".to_string()),
        age: Some(2),
        delete_flag: Some(1),
    };
    CRUD::insert(&mut pool,arg.clone());
    let v:BizActivity = CRUD::find(&mut tx,"id = 1")?;
    CRUD::update( &mut pool.clone(), arg.clone(),"id = 1");
    CRUD::delete(&mut pool.clone(),"id = 1");
}

fn make_sqlite() -> cdbc::Result<SqlitePool> {
    //first. create sqlite dir/file
    std::fs::create_dir_all("target/db/");
    File::create("target/db/sqlite.db");
    //next create table and query result
    let pool = SqlitePool::connect("sqlite://target/db/sqlite.db")?;
    let mut conn = pool.acquire()?;
    conn.execute("CREATE TABLE biz_activity(  id string, name string,age int, delete_flag int) ");
    conn.execute("INSERT INTO biz_activity (id,name,age,delete_flag) values (\"1\",\"1\",1,0)");
    Ok(pool)
}
  • impl扫描宏
 use cdbc::{impl_scan};
 use cdbc::scan::{Scan,Scans};
 pub struct BizActivity {
     pub id: Option<String>,
     pub name: Option<String>,
     pub delete_flag: Option<i32>,
 }
 impl_scan!(SqliteRow,BizActivity{id:None,name:None,delete_flag:None});

 let v:Vec<BizActivity > = query!("select * from biz_activity limit 1").fetch_all(pool)?.scan()?;
  • row_scan宏
use std::fs::File;
use cdbc::Executor;
use cdbc_sqlite::SqlitePool;

fn main() -> cdbc::Result<()> {
    let pool = make_sqlite()?;
    #[derive(Debug)]
    pub struct BizActivity {
        pub id: Option<String>,
        pub name: Option<String>,
        pub delete_flag: Option<i32>,
    }

    //execute
    let data = pool.acquire()?.execute("update biz_activity set delete_flag where id = \"1\"")?;
    println!("{:?}", data.rows_affected());
    
    //fetch_all
    let query = cdbc::query("select * from biz_activity where id = ?")
        .bind("1");
    let row = pool.acquire()?.fetch_all(query)?;
    let data = cdbc::row_scans!(row,BizActivity{id:None,name:None,delete_flag:None})?;
    println!("{:?}", data);
    
    //fetch_one
    let data = cdbc::row_scan!(
        cdbc::query("select * from biz_activity where id = ?")
        .bind("1")
        .fetch_one(pool)?,
        BizActivity{id:None,name:None,delete_flag:None})?;
    println!("{:?}", data);

    //transaction
    let mut tx = pool.acquire()?.begin()?;
    let data=tx.execute("update biz_activity set delete_flag where id = \"1\"")?;
    println!("{:?}", data.rows_affected());
    tx.commit()?;
    Ok(())
}

fn make_sqlite() -> cdbc::Result<SqlitePool> {
    //first. create sqlite dir/file
    std::fs::create_dir_all("target/db/");
    File::create("target/db/sqlite.db");
    //next create table and query result
    let pool = SqlitePool::connect("sqlite://target/db/sqlite.db")?;
    let mut conn = pool.acquire()?;
    conn.execute("CREATE TABLE biz_activity(  id string, name string,age int, delete_flag int) ");
    conn.execute("INSERT INTO biz_activity (id,name,age,delete_flag) values (\"1\",\"1\",1,0)");
    Ok(pool)
}
  • 处理读取流

main.rs

use std::collections::BTreeMap;
use cdbc::{Column, Decode, Executor, Row};
use cdbc::io::chan_stream::{ChanStream, TryStream};
use cdbc_sqlite::{Sqlite, SqliteRow};
use crate::make_sqlite;
#[test]
fn test_stream_sqlite() -> cdbc::Result<()> {
    //first. create sqlite dir/file
    let pool = make_sqlite().unwrap();
    //next create table and query result
    let mut conn = pool.acquire()?;
    let mut data: ChanStream<SqliteRow> = conn.fetch("select * from biz_activity;");
    data.try_for_each(|item| {
        let mut m = BTreeMap::new();
        for column in item.columns() {
            let v = item.try_get_raw(column.name())?;
            let r: Option<String> = Decode::<'_, Sqlite>::decode(v)?;
            m.insert(column.name().to_string(), r);
        }
        println!("{:?}", m);
        drop(m);
        Ok(())
    })?;
    Ok(())
}

依赖项

~7–36MB
~571K SLoC