MCPcopy Index your code
hub / github.com/duckdb/duckdb-rs

github.com/duckdb/duckdb-rs @v1.10504.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.10504.0 ↗ · + Follow
1,750 symbols 4,891 edges 63 files 233 documented · 13% updated 1d agov1.4.5 · 2026-06-17★ 93213 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

duckdb-rs

Latest Version Documentation MIT License Downloads CI

duckdb-rs is an ergonomic Rust wrapper for DuckDB.

You can use it to:

  • Query DuckDB with type-safe bindings and an API inspired by rusqlite.
  • Read and write Arrow, Parquet, JSON, and CSV formats natively.
  • Create DuckDB extensions in Rust with custom scalar and table functions.

Quickstart

Create a new project and add the duckdb crate:

cargo new quack-in-rust
cd quack-in-rust
cargo add duckdb -F bundled

Update src/main.rs with the following code:

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

struct Duck {
    id: i32,
    name: String,
}

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

    conn.execute(
        "CREATE TABLE ducks (id INTEGER PRIMARY KEY, name TEXT)",
        [], // empty list of parameters
    )?;

    conn.execute_batch(
        r#"
        INSERT INTO ducks (id, name) VALUES (1, 'Donald Duck');
        INSERT INTO ducks (id, name) VALUES (2, 'Scrooge McDuck');
        "#,
    )?;

    conn.execute(
        "INSERT INTO ducks (id, name) VALUES (?, ?)",
        params![3, "Darkwing Duck"],
    )?;

    let ducks = conn
        .prepare("FROM ducks")?
        .query_map([], |row| {
            Ok(Duck {
                id: row.get(0)?,
                name: row.get(1)?,
            })
        })?
        .collect::<Result<Vec<_>>>()?;

    for duck in ducks {
        println!("{}) {}", duck.id, duck.name);
    }

    Ok(())
}

Execute the program with cargo run and watch DuckDB in action!

Examples

The following examples demonstrate various features and use cases of duckdb-rs:

  • basic - Basic usage including creating tables, inserting data, and querying with and without Arrow.
  • appender - Bulk data insertion using the appender API with transactions.
  • parquet - Reading Parquet files directly using DuckDB's Parquet extension.
  • vscalar - Defining and registering a custom scalar function (requires the vscalar feature).
  • vtab - Defining a custom table function returning primitive, LIST, and STRUCT columns (requires the vtab feature).
  • repl - Interactive SQL REPL.
  • hello-ext - A DuckDB extension written in Rust (see Building and loading extensions).

Run any example with cargo run --example <name>.

Feature flags

The duckdb crate provides a number of Cargo features that can be enabled to add functionality:

Virtual tables and functions

  • vtab - Base support for creating custom table functions and virtual tables.
  • vtab-arrow - Apache Arrow integration for virtual tables. Enables conversion between Arrow RecordBatch and DuckDB data chunks.
  • vtab-excel - Deprecated no-op retained for feature compatibility.
  • vscalar - Create custom scalar functions that operate on individual values or rows.
  • vscalar-arrow - Arrow-optimized scalar functions for vectorized operations.

File formats

  • json - Enables reading and writing JSON files. Implies bundled.
  • parquet - Enables reading and writing Parquet files. Implies bundled.

Bundled DuckDB extensions

These extensions are only available through the CMake build backend and imply bundled-cmake.

  • autocomplete - DuckDB's autocomplete extension.
  • icu - DuckDB's ICU extension for locale-aware operations.
  • tpch - DuckDB's TPC-H benchmark extension.
  • tpcds - DuckDB's TPC-DS benchmark extension.

Data integration

  • appender-arrow - Efficient bulk insertion of Arrow data into DuckDB tables.
  • polars - Integration with Polars DataFrames.

Convenience features

  • vtab-full - Enables virtual table features: vtab-arrow and appender-arrow; retains the deprecated vtab-excel compatibility flag.
  • extensions-full - Enables all major extensions: json, parquet, and vtab-full.
  • modern-full - Enables modern Rust ecosystem integrations: chrono, serde_json, url, r2d2, uuid, and polars.

Build configuration

  • bundled - Uses a bundled version of DuckDB's source code and compiles it during build. This is the simplest way to get started and avoids needing DuckDB system libraries.
  • bundled-cmake - Experimental. Builds DuckDB via its upstream CMake build system instead of cc. Requires a duckdb-rs checkout (not available from crates.io). See step 2 below for details.
  • buildtime_bindgen - Use bindgen at build time to generate fresh bindings instead of using pre-generated ones.
  • loadable-extension - Experimental support for creating loadable DuckDB extensions. Includes procedural macros for extension development. Only enable this when building an extension, never for a client application - see Database client or extension?

Installation

Versioning

Starting with DuckDB v1.5.0, the duckdb-rs version encodes the DuckDB version in its second semver component. The format is 1.MAJOR_MINOR_PATCH.x, e.g., DuckDB v1.5.0 maps to duckdb-rs 1.10500.x. Use a tilde requirement to receive duckdb-rs patch releases without automatically moving to a different bundled DuckDB version.

Using stable releases from crates.io

The recommended way to use duckdb-rs is to add it from crates.io:

cargo add duckdb -F bundled

Or manually add it to your Cargo.toml:

[dependencies]
duckdb = { version = "~1.10504.0", features = ["bundled"] }

Using the development version from git

To use the latest development version from the main branch, you can specify a git dependency in your Cargo.toml:

# Use a specific branch
duckdb = { git = "https://github.com/duckdb/duckdb-rs", branch = "main", features = ["bundled"] }

# Use a specific commit
duckdb = { git = "https://github.com/duckdb/duckdb-rs", rev = "abc123def", features = ["bundled"] }

Note: Using the main branch of duckdb-rs means you'll get the latest Rust bindings and features, but you'll still be using whatever version of DuckDB core is bundled with that commit (when using the bundled feature).

Notes on building duckdb and libduckdb-sys

libduckdb-sys is a separate crate from duckdb-rs that provides the Rust declarations for DuckDB's C API. By default, libduckdb-sys attempts to find a DuckDB library that already exists on your system using pkg-config, or a Vcpkg installation for MSVC ABI builds.

You can adjust this behavior in a number of ways:

  1. If you use the bundled feature, libduckdb-sys will use the cc crate to compile DuckDB from source and link against that. This source is embedded in the libduckdb-sys crate and tracks the DuckDB version vendored for that duckdb-rs release. This is probably the simplest solution to any build problems. You can enable this by adding the following in your Cargo.toml file:

bash cargo add duckdb --features bundled

Cargo.toml will be updated.

toml [dependencies] duckdb = { version = "~1.10504.0", features = ["bundled"] }

  1. If you use the bundled-cmake feature, libduckdb-sys will build DuckDB from the local checkout in crates/libduckdb-sys/duckdb-sources using upstream CMake. This keeps plain bundled unchanged while allowing CMake-only extensions such as icu.

Example:

toml [dependencies] duckdb = { git = "https://github.com/duckdb/duckdb-rs", branch = "main", features = ["bundled-cmake", "icu"] }

Notes: - bundled-cmake is experimental and requires a git/workspace checkout. Published crates omit the full duckdb-sources tree. - It implies bundled for conditional-compilation gates, but uses CMake instead of the cc backend. Any CMake-only extension feature (e.g. icu) enables it automatically. - It always links DuckDB's default static extensions (core_functions and parquet) and therefore implies the parquet Cargo feature. - Plain bundled uses DuckDB's standard allocator and skips jemalloc. bundled-cmake enables upstream jemalloc on supported 64-bit, non-musl Linux targets. Other targets follow DuckDB's CMake checks. Set DUCKDB_DISABLE_JEMALLOC=1 to force the standard allocator. - Extension autoload/autoinstall are enabled to match bundled, despite upstream CMake defaults. Set DUCKDB_DISABLE_EXTENSION_LOAD=1 or DISABLE_EXTENSION_LOAD=1 to disable external extension install/load support and force autoload/autoinstall off. Statically linked extensions remain available. - If ninja is on PATH, the build uses Ninja by default. Set CMAKE_GENERATOR to override. - DuckDB builds in Release mode by default, even for Rust debug builds, avoiding DuckDB's much slower debug/sanitizer profile. Override with DUCKDB_CMAKE_BUILD_TYPE or CMAKE_BUILD_TYPE. DUCKDB_CMAKE_BUILD_TYPE takes precedence. - DUCKDB_EXTENSION_CONFIGS is unsupported. Setting it fails fast. - Use cargo build -vv -F bundled-cmake for CMake configure/build logs.

  1. When linking against a DuckDB library already on the system (so not using any of the bundled features), you can set the DUCKDB_LIB_DIR environment variable to point to a directory containing the library. You can also set the DUCKDB_INCLUDE_DIR variable to point to the directory containing duckdb.h.

Linux example:

```shell wget https://github.com/duckdb/duckdb/releases/download/v1.5.4/libduckdb-linux-arm64.zip unzip libduckdb-linux-arm64.zip -d libduckdb

export DUCKDB_LIB_DIR=$PWD/libduckdb export DUCKDB_INCLUDE_DIR=$DUCKDB_LIB_DIR export LD_LIBRARY_PATH=$DUCKDB_LIB_DIR

cargo build --examples ```

macOS example:

```shell wget https://github.com/duckdb/duckdb/releases/download/v1.5.4/libduckdb-osx-universal.zip unzip libduckdb-osx-universal.zip -d libduckdb

export DUCKDB_LIB_DIR=$PWD/libduckdb export DUCKDB_INCLUDE_DIR=$DUCKDB_LIB_DIR export DYLD_FALLBACK_LIBRARY_PATH=$DUCKDB_LIB_DIR

cargo build --examples ```

  1. Setting DUCKDB_DOWNLOAD_LIB=1 makes the build script download pre-built DuckDB binaries from GitHub Releases. This always links against the dynamic library in the archive (setting DUCKDB_STATIC has no effect), and it effectively automates the manual steps above. The archives are cached in target/duckdb-download/<target>/<version> and that directory is automatically added to the linker search path. The downloaded version always matches the DuckDB version encoded in the libduckdb-sys crate version.

shell DUCKDB_DOWNLOAD_LIB=1 cargo test

  1. Installing the DuckDB development packages will usually be all that is required, but the build helpers for pkg-config and vcpkg have some additional configuration options. The default when using vcpkg is to dynamically link, which must be enabled by setting VCPKGRS_DYNAMIC=1 environment variable before build.

When none of the options above are used, the build script falls back to this discovery path and will emit the appropriate cargo:rustc-link-lib directives if DuckDB is found on your system.

Cross-compiling with bundled is best-effort (not covered by CI): it compiles DuckDB from C++ source and so needs a working C++ cross-compiler for the target, not just the Rust toolchain. If you can't get one working, prefer one of the non-bundled options above (e.g. DUCKDB_LIB_DIR or DUCKDB_DOWNLOAD_LIB=1) and link a pre-built library for the target instead.

ICU extension and the bundled features

When using the bundled feature, the ICU extension is not included due to crates.io's 10MB package size limit. This means some date/time operations (like now() - interval '1 day' or ts::date casts) will fail. You can load ICU at runtime:

rust,ignore conn.execute_batch("INSTALL icu; LOAD icu;")?;

Alternatively, link against a system libduckdb that was compiled with ICU (see build instructions above).

If you are working from a duckdb-rs checkout, you can also use bundled-cmake,icu to compile ICU in through DuckDB's CMake build.

Binding generation

We use bindgen to generate the Rust declarations from DuckDB's C header file. bindgen recommends running this as part of the build process for libraries that use it. We tried this briefly (duckdb 0.10.0, specifically), but it had some annoyances:

  • The build time for libduckdb-sys (and therefore duckdb) increased dramatically.
  • Running bindgen requires a relatively recent version of Clang, which many systems do not have installed by default.
  • Running bindgen also requires the DuckDB header file to be present.

So we try to avoid running bindgen at build time by shipping pregenerated bindings for DuckDB.

If you use the bundled feature, you will get pregenerated bindings for the bundled version of DuckDB. If you want to run bindgen at build time to produce your own bindings, use the buildtime_bindgen Cargo feature.

Thread safety

A Connection is Send but not Sync: you may move it between threads, but you cannot share a single connection across threads at the same time. DuckDB does support concurrent access through multiple connections to the same database (see [DuckDB's concurrenc

Extension points exported contracts — how you extend this code

Core symbols most depended-on inside this repo

Shape

Function 1,020
Method 448
Class 247
Enum 20
Interface 15

Languages

Rust100%
Python1%

Modules by API surface

crates/libduckdb-sys/src/bindgen_bundled_version_loadable.rs626 symbols
crates/duckdb/src/vtab/arrow.rs95 symbols
crates/duckdb/src/statement.rs94 symbols
crates/duckdb/src/lib.rs86 symbols
crates/libduckdb-sys/src/bindgen_bundled_version.rs78 symbols
crates/duckdb/src/appender/mod.rs42 symbols
crates/duckdb/src/core/vector.rs38 symbols
crates/duckdb/src/core/logical_type.rs36 symbols
crates/duckdb/src/row.rs35 symbols
crates/duckdb/src/pragma.rs34 symbols
crates/libduckdb-sys/build.rs33 symbols
crates/duckdb/src/vtab/function.rs32 symbols

For agents

$ claude mcp add duckdb-rs \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page