1 个不稳定版本

0.8.1 2023年7月14日

#1849 in 数据库接口

MIT 许可证

3MB
7.5K SLoC

包含 (ZIP 文件,2.5MB) examples/Movies_Social_metadata.xlsx,(ZIP 文件,10KB) examples/date.xlsx

duckdb-rs

Downloads Build Status dependency status codecov Latest Version Docs

duckdb-rs 是 Rust 中使用 DuckDB 的便捷封装。它试图提供一个类似于 rusqlite 的接口。实际上,初始代码甚至这份 README 也是从 rusqlite 分支出来的,因为 DuckDB 也试图提供一个与 sqlite3 兼容的 API。

use duckdb::{params, Connection, Result};

// In your project, we need to keep the arrow version same as the version used in duckdb.
// Refer to https://github.com/wangfenjin/duckdb-rs/issues/92
// You can either:
use duckdb::arrow::record_batch::RecordBatch;
// Or in your Cargo.toml, use * as the version; features can be toggled according to your needs
// arrow = { version = "*", default-features = false, features = ["prettyprint"] }
// Then you can:
// use arrow::record_batch::RecordBatch;

use duckdb::arrow::util::pretty::print_batches;

#[derive(Debug)]
struct Person {
    id: i32,
    name: String,
    data: Option<Vec<u8>>,
}

fn main() -> Result<()> {
    let conn = Connection::open_in_memory()?;

    conn.execute_batch(
        r"CREATE SEQUENCE seq;
          CREATE TABLE person (
                  id              INTEGER PRIMARY KEY DEFAULT NEXTVAL('seq'),
                  name            TEXT NOT NULL,
                  data            BLOB
                  );
        ")?;

    let me = Person {
        id: 0,
        name: "Steven".to_string(),
        data: None,
    };
    conn.execute(
        "INSERT INTO person (name, data) VALUES (?, ?)",
        params![me.name, me.data],
    )?;

    // query table by rows
    let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
    let person_iter = stmt.query_map([], |row| {
        Ok(Person {
            id: row.get(0)?,
            name: row.get(1)?,
            data: row.get(2)?,
        })
    })?;

    for person in person_iter {
        println!("Found person {:?}", person.unwrap());
    }

    // query table by arrow
    let rbs: Vec<RecordBatch> = stmt.query_arrow([])?.collect();
    print_batches(&rbs).unwrap();
    Ok(())
}

DuckDB 和 libduckdb-sys 构建说明

libduckdb-sys 是一个与 duckdb-rs 分离的 crate,它提供了 DuckDB C API 的 Rust 声明。默认情况下,libduckdb-sys 尝试使用 pkg-config 在您的系统上查找已存在的 DuckDB 库,或者为 MSVC ABI 构建使用 Vcpkg 安装。

您可以通过多种方式调整此行为

  • 如果您使用 bundled 功能,libduckdb-sys 将使用 cc crate 从源代码编译 DuckDB 并将其链接。这个源代码嵌入在 libduckdb-sys crate 中,由于我们仍在开发中,我们将定期更新它。在我们更稳定之后,我们将使用来自 duckdb 的稳定发布版本。这可能是解决任何构建问题的最简单方法。您可以在 Cargo.toml 文件中添加以下内容以启用此功能
    [dependencies.duckdb]
    version = "0.1"
    features = ["bundled"]
    
  • 当链接到系统上已存在的 DuckDB 库(因此 使用任何 bundled 功能)时,您可以将 DUCKDB_LIB_DIR 环境变量设置为指向包含库的目录。您还可以将 DUCKDB_INCLUDE_DIR 变量设置为指向包含 duckdb.h 的目录。
  • 安装 duckdb 开发包通常就足够了,但 pkg-configvcpkg 的构建辅助程序有一些额外的配置选项。使用 vcpkg 的默认情况下是动态链接,必须在构建之前设置 VCPKGRS_DYNAMIC=1 环境变量。

绑定生成

我们使用 bindgen 从 DuckDB 的 C 头文件生成 Rust 声明。 bindgen 推荐将其作为使用此库的库的构建过程的一部分运行。我们尝试了一下(具体是 duckdb 0.10.0),但它有一些令人烦恼的地方。

  • libduckdb-sys(以及因此 duckdb)的构建时间显著增加。
  • 运行 bindgen 需要一个相对较新的 Clang 版本,而许多系统默认没有安装。
  • 运行 bindgen 还需要存在 DuckDB 头文件。

因此,我们尝试通过提供预生成的 DuckDB 绑定来避免在构建时运行 bindgen

如果您使用 bundled 功能,您将获得捆绑版 DuckDB 的预生成绑定。如果您想在构建时运行 bindgen 来生成自己的绑定,请使用 buildtime_bindgen Cargo 功能。

贡献

请参阅 Contributing.md

清单

  • 运行 cargo fmt 确保您的 Rust 代码格式正确。
  • 运行 cargo clippy --fix --allow-dirty --all-targets --workspace --all-features -- -D warnings 修复所有 clippy 问题。
  • 确保 cargo test --all-targets --workspace --features "modern-full extensions-full" 报告没有失败。

待办事项

  • 重构 ErrorCode 部分,它是从 rusqlite 借用的,我们应该有自己的
  • 支持更多类型
  • 更新 duckdb.h
  • 调整代码示例和文档
  • 删除未使用的代码/函数
  • 添加 CI
  • 发布到 crate

许可

DuckDB 和 libduckdb-sys 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。

依赖关系

~18–31MB
~419K SLoC