duckdb

    MoonBit bindings for DuckDB (fork of f4ah6o/duckdb that builds on current MoonBit toolchains)

    duckdb
    Download zip
    Author
    Version
    0.6.6
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    671

    #mizchi/duckdb

    Fork of f4ah6o/duckdb (Apache-2.0) with the fixes needed to build on current MoonBit toolchains, published as mizchi/duckdb until they land upstream (upstream PR). The API is unchanged; switch the module name back once upstream releases.

    MoonBit bindings for DuckDB on native and JavaScript targets.

    #Targets

    • Native: links against libduckdb via the DuckDB C API.
    • Native builds target DuckDB 1.4.x (CI uses 1.4.4); the JavaScript Node backend uses @duckdb/node-api 2.0 independently.
    • JavaScript: compile with the MoonBit JS target and pick a backend at runtime:
      • JsBackend::Node uses @duckdb/node-api.
      • JsBackend::Wasm uses @duckdb/duckdb-wasm in the browser.
    • MoonBit wasm/wasm-gc targets are not supported (they use stub implementations).

    #Feature Support Matrix

    FeatureNativeJS (Node)JS (WASM)
    Connection & Query✅✅✅
    Prepared Statements✅✅✅
    Streaming Results✅✅✅
    Appender✅✅ (Node only)❌
    Arrow Integration✅✅✅
    Advanced Types⚠️⚠️⚠️

    Legend: ✅ Full support | ⚠️ Partial support | ❌ Not supported

    #Advanced Types Detailed Support

    TypeNative BindNative AppendNode BindNode AppendWASM Bind
    Decimal✅ 128-bit✅ 128-bit✅ 128-bit✅ 128-bit✅ direct string param + SQL cast
    Interval✅✅✅✅✅ direct string param + SQL cast
    Blob✅✅✅✅❌ unsupported
    List✅ VARCHAR✅ VARCHAR✅ VARCHAR (Node only)❌❌ unsupported
    Struct✅ VARCHAR✅ VARCHAR✅ VARCHAR (Node only)❌❌ unsupported
    Map✅ VARCHAR✅ VARCHAR✅ VARCHAR (Node only)❌❌ unsupported

    Notes:
    • List/Struct/Map are represented as string arrays (VARCHAR-only) and rely on DuckDB casting.
    • JS (WASM) uses direct duckdb-wasm prepared parameters only. Verified advanced prepared-statement bind support is limited to Decimal and Interval string parameters with an explicit SQL cast, for example ?::DECIMAL(10,2) or ?::INTERVAL.
    • JS (WASM) Blob, List, Struct, and Map direct prepared parameters are explicitly unsupported because the browser smoke test fails against @duckdb/duckdb-wasm 1.33.1-dev18.0.
    • Appender date/timestamp helpers are only implemented for native targets.

    #Arrow Integration

    Basic support is available on all targets:
    • Arrow query result type
    • Schema extraction
    • Column-based data access (native also exposes nullable getters)
    • Supported types: BOOLEAN, INTEGER, VARCHAR, DOUBLE, BIGINT

    Note: Complex types (List, Struct, Map) are not yet supported.

    #Installation

    #Native Target

    The native target links against libduckdb using the DuckDB C API.

    #Install libduckdb

    macOS (Homebrew):
    brew install duckdb

    Ubuntu/Debian:
    # Download a release that matches your platform wget https://github.com/duckdb/duckdb/releases/download/<version>/libduckdb-linux-amd64.zip unzip libduckdb-linux-amd64.zip sudo cp libduckdb.so /usr/local/lib/ sudo ldconfig

    From Source:
    git clone https://github.com/duckdb/duckdb.git cd duckdb mkdir build && cd build cmake .. make -j$(nproc) sudo make install sudo ldconfig

    #Linker Configuration

    When compiling, you may need to specify the library path:

    moon build --target-native -- -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lduckdb

    Or set PKG_CONFIG_PATH if libduckdb provides a pkg-config file. The default src/moon.pkg includes common include/library search paths for both macOS and Ubuntu:

    • Include: /opt/homebrew/include, /usr/local/include, /usr/include
    • Library: /opt/homebrew/lib, /usr/local/lib, /usr/lib

    The linker still requires -lduckdb, so libduckdb must be installed on the machine.

    If you hit errors like Undefined symbols ... _duckdb_*, check:

    1. duckdb.h exists in one of the include paths above.
    2. libduckdb.dylib (macOS) or libduckdb.so (Linux) exists in one of the library paths above.
    3. moon test --target native runs in an environment where those paths are visible to the linker.

    #JavaScript Targets

    #Node.js

    The Node.js backend uses @duckdb/node-api. Install JavaScript dependencies with pnpm:

    pnpm install

    #Browser (WASM)

    The browser backend uses @duckdb/duckdb-wasm and requires browser Worker support. The package is installed by the same pnpm setup:

    pnpm install

    Run the browser smoke check to verify the local setup:

    pnpm test:wasm-browser

    If Chromium is not installed for Playwright yet, install it once:

    pnpm test:wasm-browser:install

    The smoke check serves the local @duckdb/duckdb-wasm bundle over HTTP, verifies Worker support, and exercises direct duckdb-wasm prepared parameters for Decimal, Blob, Interval, List, Struct, and Map. Decimal and Interval are expected to pass; Blob, List, Struct, and Map are expected to remain unsupported. Cross-origin isolation may be required for future pthread/SharedArrayBuffer paths, but the current smoke check uses the MVP worker bundle.

    #JavaScript Limitations

    • WASM Appender - Not supported for WASM backend (use INSERT statements instead)
    • WASM Advanced Types - Decimal and Interval prepared-statement binds are supported as direct string parameters with explicit SQL casts; Blob, List, Struct, and Map direct prepared parameters are unsupported
    • Node.js Advanced Types - Decimal, Interval, Blob are supported for bind/append; List/Struct/Map are VARCHAR-only
    • JS Appender Date/Timestamp - Not implemented (native only)

    #Quack Remote Protocol

    Quack support is exposed as thin SQL helpers on Connection. The helpers use DuckDB's quack extension instead of implementing the low-level application/duckdb wire format in MoonBit.

    Quack is experimental in DuckDB 1.5.x and is distributed from DuckDB's core_nightly extension repository. Function names, defaults, and protocol details may change before DuckDB 2.0.

    connect(on_ready=fn(result) {
    match result {
    Ok(conn) => {
    conn.install_quack(on_done=fn(_) { () })
    conn.load_quack(on_done=fn(_) { () })
    conn.start_quack_server(
    "quack:localhost",
    token="super_secret",
    on_done=fn(started) {
    match started {
    Ok(info) => println("quack server: \{info.rows}")
    Err(err) => println("quack serve failed: \{err}")
    }
    },
    )
    }
    Err(err) => println("connect failed: \{err}")
    }
    })

    Client helpers cover scoped secrets, stateless remote queries, and attached remote catalogs:

    conn.create_quack_secret(
    "super_secret",
    scope="quack:localhost",
    on_done=fn(_) { () },
    )

    conn.quack_query(
    "quack:localhost",
    "SELECT 42 AS answer",
    token="super_secret",
    on_done=fn(result) {
    match result {
    Ok(rows) => println("\{rows.rows}")
    Err(err) => println("quack query failed: \{err}")
    }
    },
    )

    conn.attach_quack(
    "quack:localhost",
    "remote_db",
    token="super_secret",
    on_done=fn(_) { () },
    )

    For non-local endpoints, DuckDB's Quack client assumes HTTPS by default. Use disable_ssl=true only when a remote endpoint is intentionally served over plain HTTP, and prefer a TLS-terminating reverse proxy for exposed services.

    #Usage

    connect(on_ready=fn (result) {
    match result {
    Ok(conn) => {
    conn.query(
    "select 1 as a, NULL as b, 'duck' as c",
    on_done=fn (query_result) {
    match query_result {
    Ok(result) => {
    println("columns: \{result.columns}")
    println("rows: \{result.rows}")
    println("nulls: \{result.nulls}")
    }
    Err(err) => println("query failed: \{err}")
    }
    },
    )
    conn.close(on_done=fn (closed) {
    match closed {
    Ok(_) => ()
    Err(err) => println("close failed: \{err}")
    }
    })
    }
    Err(err) => println("connect failed: \{err}")
    }
    })

    #Typed Results

    QueryResult stores rows as strings plus a null mask. Use the typed helpers for convenience, or convert to a TypedQueryResult for repeated access:

    conn.query("select 1 as a, 2.5 as b, NULL as c", on_done=fn (query_result) {
    match query_result {
    Ok(result) => {
    let value = result.get_int(0, 0) // Some(1)
    let typed = result.to_typed()
    let b0 = typed.get_double(0, 1)
    let c0 = typed.get_string(0, 2) // None
    println("\{value} \{b0} \{c0}")
    }
    Err(err) => println("query failed: \{err}")
    }
    })

    #Streaming Results

    Use query_stream to process large datasets in chunks without materializing the full result in MoonBit memory:

    #Basic Streaming (Count Rows)

    connect(on_ready=fn (result) {
    match result {
    Ok(conn) => {
    conn.query_stream(
    "SELECT i FROM RANGE(1000000) tbl(i)",
    on_done=fn (stream_result) {
    match stream_result {
    Ok(stream) => {
    let mut total = 0
    let done = Ref::new(false)
    while !done.val {
    stream.next(on_done=fn (chunk_result) {
    match chunk_result {
    Ok(Some(chunk)) => total = total + chunk.row_count()
    Ok(None) => done.val = true
    Err(err) => {
    done.val = true
    println("stream failed: \{err}")
    }
    }
    })
    }
    stream.close(on_done=fn (_) { () })
    println("rows: \{total}")
    }
    Err(err) => println("stream failed: \{err}")
    }
    },
    )
    }
    Err(err) => println("connect failed: \{err}")
    }
    })

    #Aggregation Example

    For more advanced use cases, you can aggregate data while streaming:

    // Aggregate state to track running totals
    pub struct Aggregates {
    mut total_rows : Int
    mut sum_values : Int
    mut min_value : Int?
    mut max_value : Int?
    }

    let agg_ref = Ref::new({ total_rows: 0, sum_values: 0, min_value: None, max_value: None })
    let done_ref = Ref::new(false)

    conn.query_stream(
    "SELECT value FROM measurements",
    on_done=fn (stream_result) {
    match stream_result {
    Ok(stream) => {
    while !done_ref.val {
    stream.next(on_done=fn (chunk_result) {
    match chunk_result {
    Ok(Some(chunk)) => {
    // Process each row in the chunk
    for row = 0; row < chunk.row_count(); row = row + 1 {
    match chunk.cell(row, 0) {
    Some(v) => {
    let value = parse_int(v)
    agg_ref.val.total_rows = agg_ref.val.total_rows + 1
    agg_ref.val.sum_values = agg_ref.val.sum_values + value
    // Update min/max...
    }
    None => ()
    }
    }
    }
    Ok(None) => done_ref.val = true
    Err(err) => { done_ref.val = true; println("error: \{err}") }
    }
    })
    }
    stream.close(on_done=fn (_) {
    println("Total: \{agg_ref.val.total_rows}")
    println("Sum: \{agg_ref.val.sum_values}")
    })
    }
    Err(err) => println("stream failed: \{err}")
    }
    },
    )

    #Streaming Limitations

    • Streamed DataChunk values are strings plus a null mask, consistent with QueryResult.
    • Always call ResultStream::close when finished to release resources.

    #JS Backend Selection

    Use JsBackend::Auto (default), JsBackend::Node, or JsBackend::Wasm:

    connect(
    on_ready=fn (result) { /* ... */ },
    backend=JsBackend::Wasm,
    )

    • Auto - Detects environment (Node.js uses Node, browser uses WASM)
    • Node - Forces @duckdb/node-api
    • Wasm - Forces @duckdb/duckdb-wasm

    #Configuration

    Configuration is only available on native/JS targets (not wasm/wasm-gc). Create a config, set options, then connect with it:

    let config = Config::create()
    match config.set("memory_limit", "1GB") {
    Ok(_) => ()
    Err(err) => println("config set failed: \{err}")
    }

    connect_with_config(
    on_ready=fn (result) { /* ... */ },
    config=Some(config),
    path=":memory:",
    )

    #Error Handling

    All bind_* methods and Config::set return Result[Unit, DuckDBError] on both native and JS targets:
    • Success: Returns Ok(())
    • Failure: Returns Err(DuckDBError::Message(reason))

    On JS targets, bind operations are synchronous and errors are properly propagated. Use pattern matching to handle errors:

    match stmt.bind_int(1, 42) {
    Ok(_) =>
    match stmt.bind_varchar(2, "hello") {
    Ok(_) => ()
    Err(e) => println("bind failed: \{e}")
    }
    Err(e) => println("bind failed: \{e}")
    }

    DuckDBError

    pub suberror DuckDBError {
    Message(String)
    }

    DuckDB error wrapper.

    Appender

    #external
    pub type Appender

    Appender::append_bigint

    fn Appender::append_bigint(self : Appender, value : Int) -> Result[Unit, DuckDBError]

    Appender::append_blob

    fn Appender::append_blob(self : Appender, value : Bytes) -> Result[Unit, DuckDBError]

    Appender::append_bool

    fn Appender::append_bool(self : Appender, value : Bool) -> Result[Unit, DuckDBError]

    Appender::append_date

    fn Appender::append_date(self : Appender, days : Int) -> Result[Unit, DuckDBError]

    Appender::append_decimal

    fn Appender::append_decimal(self : Appender, value : Decimal) -> Result[Unit, DuckDBError]

    Appender::append_double

    fn Appender::append_double(self : Appender, value : Double) -> Result[Unit, DuckDBError]

    Appender::append_int

    fn Appender::append_int(self : Appender, value : Int) -> Result[Unit, DuckDBError]

    Appender::append_interval

    fn Appender::append_interval(self : Appender, value : Interval) -> Result[Unit, DuckDBError]

    Appender::append_list_varchar

    fn Appender::append_list_varchar(self : Appender, values : Array[String]) -> Result[Unit, DuckDBError]

    Append a list of string values to the appender.

    Appender::append_list_varchar_value

    fn Appender::append_list_varchar_value(self : Appender, values : Array[String]) -> Result[Unit, DuckDBError]

    Append a list literal as VARCHAR. This serializes the list and relies on DuckDB casting when appropriate.

    Appender::append_map

    fn Appender::append_map(self : Appender, map : Map) -> Result[Unit, DuckDBError]

    Append a map value to the appender.

    Appender::append_null

    fn Appender::append_null(self : Appender) -> Result[Unit, DuckDBError]

    Appender::append_struct

    fn Appender::append_struct(self : Appender, value : Struct) -> Result[Unit, DuckDBError]

    Append a struct value to the appender.

    Appender::append_timestamp

    fn Appender::append_timestamp(self : Appender, micros : Int64) -> Result[Unit, DuckDBError]

    Appender::append_varchar

    fn Appender::append_varchar(self : Appender, value : String) -> Result[Unit, DuckDBError]

    Appender::begin_row

    fn Appender::begin_row(self : Appender) -> Result[Unit, DuckDBError]

    Appender::close

    fn Appender::close(self : Appender, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Appender::end_row

    fn Appender::end_row(self : Appender) -> Result[Unit, DuckDBError]

    Appender::flush

    fn Appender::flush(self : Appender) -> Result[Unit, DuckDBError]

    ArrowField

    pub struct ArrowField {
    name : String
    nullable : Bool
    type_id : String
    }

    ArrowResult

    #external
    pub type ArrowResult

    ArrowResult::close

    fn ArrowResult::close(self : ArrowResult, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    ArrowResult::column_count

    fn ArrowResult::column_count(self : ArrowResult) -> Int

    ArrowResult::get_column_bool

    fn ArrowResult::get_column_bool(self : ArrowResult, col : Int) -> Array[Bool]

    ArrowResult::get_column_bool_nullable

    fn ArrowResult::get_column_bool_nullable(self : ArrowResult, col : Int) -> (Array[Bool], Array[Bool])

    ArrowResult::get_column_double

    fn ArrowResult::get_column_double(self : ArrowResult, col : Int) -> Array[Double]

    ArrowResult::get_column_double_nullable

    fn ArrowResult::get_column_double_nullable(self : ArrowResult, col : Int) -> (Array[Double], Array[Bool])

    ArrowResult::get_column_int32

    fn ArrowResult::get_column_int32(self : ArrowResult, col : Int) -> Array[Int]

    ArrowResult::get_column_int32_nullable

    fn ArrowResult::get_column_int32_nullable(self : ArrowResult, col : Int) -> (Array[Int], Array[Bool])

    ArrowResult::get_column_int64

    fn ArrowResult::get_column_int64(self : ArrowResult, col : Int) -> Array[Int]

    ArrowResult::get_column_int64_nullable

    fn ArrowResult::get_column_int64_nullable(self : ArrowResult, col : Int) -> (Array[Int], Array[Bool])

    ArrowResult::get_column_string

    fn ArrowResult::get_column_string(self : ArrowResult, col : Int) -> Array[String]

    ArrowResult::get_column_string_nullable

    fn ArrowResult::get_column_string_nullable(self : ArrowResult, col : Int) -> (Array[String], Array[Bool])

    ArrowResult::get_schema

    ArrowResult::row_count

    fn ArrowResult::row_count(self : ArrowResult) -> Int

    ArrowSchemaInfo

    pub struct ArrowSchemaInfo {
    fields : Array[ArrowField]
    }

    CheckConfig

    type CheckConfig

    CheckConfig::default

    fn CheckConfig::default() -> CheckConfig

    CheckConfig::new

    fn CheckConfig::new(cases : Int, max_size : Int, seed : Int, max_shrinks : Int, discard_ratio? : Int) -> CheckConfig

    CheckConfig::seed

    fn CheckConfig::seed(self : CheckConfig) -> Int

    CheckResult

    type CheckResult

    CheckResult::passed

    fn CheckResult::passed(self : CheckResult) -> Bool

    CheckResult::stats

    fn CheckResult::stats(self : CheckResult) -> String?

    ColumnType

    pub enum ColumnType {
    Invalid
    Boolean
    TinyInt
    SmallInt
    Integer
    BigInt
    UTinyInt
    USmallInt
    UInteger
    UBigInt
    Float
    Double
    Timestamp
    Date
    Time
    Interval
    HugeInt
    UHugeInt
    Varchar
    Blob
    Decimal
    TimestampS
    TimestampMs
    TimestampNs
    Enum
    List
    Struct
    Map
    Array
    Uuid
    Union
    Bit
    TimeTz
    TimestampTz
    Any
    Bignum
    SqlNull
    StringLiteral
    IntegerLiteral
    TimeNs
    Unknown(Int)
    }

    DuckDB column type identifiers.

    Config

    #external
    pub type Config

    Config::create

    fn Config::create() -> Config

    Config::set

    fn Config::set(self : Config, key : String, value : String) -> Result[Unit, DuckDBError]

    Connection

    #external
    pub type Connection

    Connection::attach_quack

    fn Connection::attach_quack(self : Connection, uri : String, db_alias : String, token? : String, disable_ssl? : Bool, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Attach a Quack endpoint as a remote DuckDB catalog.

    Connection::close

    fn Connection::close(self : Connection, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Connection::create_appender

    fn Connection::create_appender(self : Connection, schema : String, table : String, on_done~ : (Result[Appender, DuckDBError]) -> Unit) -> Unit

    Connection::create_quack_secret

    fn Connection::create_quack_secret(self : Connection, token : String, scope? : String, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Create a scoped or unscoped Quack secret for client authentication.

    Connection::install_quack

    fn Connection::install_quack(self : Connection, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Install the experimental Quack extension from DuckDB's core_nightly repository.

    Connection::load_quack

    fn Connection::load_quack(self : Connection, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Load the Quack extension in the current DuckDB connection.

    Connection::prepare

    fn Connection::prepare(self : Connection, sql : String, on_done~ : (Result[PreparedStatement, DuckDBError]) -> Unit) -> Unit

    Connection::quack_query

    fn Connection::quack_query(self : Connection, uri : String, sql : String, token? : String, disable_ssl? : Bool, on_done~ : (Result[QueryResult, DuckDBError]) -> Unit) -> Unit

    Run a stateless query against a Quack endpoint.

    Connection::query

    fn Connection::query(self : Connection, sql : String, on_done~ : (Result[QueryResult, DuckDBError]) -> Unit) -> Unit

    Connection::query_arrow

    fn Connection::query_arrow(self : Connection, sql : String, on_done~ : (Result[ArrowResult, DuckDBError]) -> Unit) -> Unit

    Connection::query_stream

    fn Connection::query_stream(self : Connection, sql : String, on_done~ : (Result[ResultStream, DuckDBError]) -> Unit) -> Unit

    Connection::start_quack_server

    fn Connection::start_quack_server(self : Connection, uri : String, token? : String, allow_other_hostname? : Bool, on_done~ : (Result[QueryResult, DuckDBError]) -> Unit) -> Unit

    Start a Quack server for this connection.

    Connection::stop_quack_server

    fn Connection::stop_quack_server(self : Connection, uri : String, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    Stop a Quack server started for this connection.

    DataChunk

    pub struct DataChunk {
    columns : Array[String]
    rows : Array[Array[String]]
    nulls : Array[Array[Bool]]
    }

    Chunked query data with column metadata.

    DataChunk::cell

    fn DataChunk::cell(self : DataChunk, row : Int, col : Int) -> String?

    DataChunk::column_count

    fn DataChunk::column_count(self : DataChunk) -> Int

    DataChunk::row_count

    fn DataChunk::row_count(self : DataChunk) -> Int

    Decimal

    pub struct Decimal {
    width : Int
    scale : Int
    lower : Int
    upper : Int
    }

    Fixed-point decimal type for financial calculations. Uses 128-bit integer representation (lower/upper parts).

    FixtureCase

    pub struct FixtureCase {
    name : String
    sql : String
    columns : Array[String]
    rows : Array[Array[String]]
    nulls : Array[Array[Bool]]
    }

    Interval

    pub struct Interval {
    months : Int
    days : Int
    micros : Int64
    }

    Date/time interval type. Represents a span of time in months, days, and microseconds.

    JsBackend

    pub(all) enum JsBackend {
    Auto
    Node
    Wasm
    }

    JS backend selection for connect.

    List

    pub struct List {
    elements : Array[String]
    }

    List/array type. Elements are stored as strings and converted on demand.

    LogicalType

    #external
    pub type LogicalType

    Map

    pub struct Map {
    keys : Array[String]
    values : Array[String]
    }

    Key-value pair map type. DuckDB maps are implemented as lists of key-value structs.

    NativeDataChunk

    #external
    pub type NativeDataChunk

    PreparedStatement

    #external
    pub type PreparedStatement

    PreparedStatement::bind_bigint

    fn PreparedStatement::bind_bigint(self : PreparedStatement, index : Int, value : Int) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_blob

    fn PreparedStatement::bind_blob(self : PreparedStatement, index : Int, value : Bytes) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_bool

    fn PreparedStatement::bind_bool(self : PreparedStatement, index : Int, value : Bool) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_date

    fn PreparedStatement::bind_date(self : PreparedStatement, index : Int, days : Int) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_decimal

    fn PreparedStatement::bind_decimal(self : PreparedStatement, index : Int, value : Decimal) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_double

    fn PreparedStatement::bind_double(self : PreparedStatement, index : Int, value : Double) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_int

    fn PreparedStatement::bind_int(self : PreparedStatement, index : Int, value : Int) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_interval

    fn PreparedStatement::bind_interval(self : PreparedStatement, index : Int, value : Interval) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_list_varchar

    fn PreparedStatement::bind_list_varchar(self : PreparedStatement, index : Int, values : Array[String]) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_map

    fn PreparedStatement::bind_map(self : PreparedStatement, index : Int, map : Map) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_null

    fn PreparedStatement::bind_null(self : PreparedStatement, index : Int) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_struct

    fn PreparedStatement::bind_struct(self : PreparedStatement, index : Int, value : Struct) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_timestamp

    fn PreparedStatement::bind_timestamp(self : PreparedStatement, index : Int, micros : Int64) -> Result[Unit, DuckDBError]

    PreparedStatement::bind_varchar

    fn PreparedStatement::bind_varchar(self : PreparedStatement, index : Int, value : String) -> Result[Unit, DuckDBError]

    PreparedStatement::clear_bindings

    fn PreparedStatement::clear_bindings(self : PreparedStatement) -> Result[Unit, DuckDBError]

    PreparedStatement::close

    fn PreparedStatement::close(self : PreparedStatement, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    PreparedStatement::execute

    fn PreparedStatement::execute(self : PreparedStatement, on_done~ : (Result[QueryResult, DuckDBError]) -> Unit) -> Unit

    PreparedStatement::execute_stream

    fn PreparedStatement::execute_stream(self : PreparedStatement, on_done~ : (Result[ResultStream, DuckDBError]) -> Unit) -> Unit

    QueryResult

    pub struct QueryResult {
    columns : Array[String]
    column_types : Array[ColumnType]
    rows : Array[Array[String]]
    nulls : Array[Array[Bool]]
    }

    Query result data is represented as strings plus a null mask.

    QueryResult::cell

    fn QueryResult::cell(self : QueryResult, row : Int, col : Int) -> String?

    QueryResult::column_count

    fn QueryResult::column_count(self : QueryResult) -> Int

    QueryResult::get_blob

    fn QueryResult::get_blob(self : QueryResult, row : Int, col : Int) -> Bytes?

    Get the blob value at the specified row and column. Returns None if the value is null or not a blob.

    QueryResult::get_bool

    fn QueryResult::get_bool(self : QueryResult, row : Int, col : Int) -> Bool?

    Get the boolean value at the specified row and column. Returns None if the value is null or not a boolean.

    QueryResult::get_date

    fn QueryResult::get_date(self : QueryResult, row : Int, col : Int) -> Int?

    Get the date value at the specified row and column. Returns None if the value is null or not a date.

    QueryResult::get_decimal

    fn QueryResult::get_decimal(self : QueryResult, row : Int, col : Int) -> Decimal?

    Get the decimal value at the specified row and column. Returns None if the value is null or not a decimal.

    QueryResult::get_double

    fn QueryResult::get_double(self : QueryResult, row : Int, col : Int) -> Double?

    Get the double value at the specified row and column. Returns None if the value is null or not a double.

    QueryResult::get_int

    fn QueryResult::get_int(self : QueryResult, row : Int, col : Int) -> Int?

    Get the integer value at the specified row and column. Returns None if the value is null or not an integer.

    QueryResult::get_string

    fn QueryResult::get_string(self : QueryResult, row : Int, col : Int) -> String?

    Get the string value at the specified row and column. Returns None if the value is null.

    QueryResult::get_timestamp

    fn QueryResult::get_timestamp(self : QueryResult, row : Int, col : Int) -> Int64?

    Get the timestamp value at the specified row and column. Returns None if the value is null or not a timestamp.

    QueryResult::get_value

    fn QueryResult::get_value(self : QueryResult, row : Int, col : Int) -> Value?

    Get the typed value at the specified row and column. Returns the Value directly without requiring to_typed() conversion.

    QueryResult::row_count

    fn QueryResult::row_count(self : QueryResult) -> Int

    QueryResult::to_typed

    fn QueryResult::to_typed(self : QueryResult) -> TypedQueryResult

    Convert a QueryResult to a TypedQueryResult by parsing string values.

    ResultStream

    #external
    pub type ResultStream

    ResultStream::close

    fn ResultStream::close(self : ResultStream, on_done~ : (Result[Unit, DuckDBError]) -> Unit) -> Unit

    ResultStream::column_count

    fn ResultStream::column_count(self : ResultStream) -> Int

    ResultStream::columns

    fn ResultStream::columns(self : ResultStream) -> Array[String]

    ResultStream::next

    fn ResultStream::next(self : ResultStream, on_done~ : (Result[DataChunk?, DuckDBError]) -> Unit) -> Unit

    Struct

    pub struct Struct {
    fields : Array[String]
    values : Array[String]
    }

    Composite type with named fields. Represents a DuckDB STRUCT type.

    TypedQueryResult

    pub struct TypedQueryResult {
    columns : Array[String]
    data : Array[Array[Value]]
    }

    Type-safe query result with typed value access. Stores data column-wise for efficient columnar access.

    TypedQueryResult::column_count

    fn TypedQueryResult::column_count(self : TypedQueryResult) -> Int

    Get the number of columns in the result.

    TypedQueryResult::get_blob

    fn TypedQueryResult::get_blob(self : TypedQueryResult, row : Int, col : Int) -> Bytes?

    Get a Blob value at the specified position.

    TypedQueryResult::get_bool

    fn TypedQueryResult::get_bool(self : TypedQueryResult, row : Int, col : Int) -> Bool?

    Get a Bool value at the specified position.

    TypedQueryResult::get_bool_column

    fn TypedQueryResult::get_bool_column(self : TypedQueryResult, col : Int) -> Array[Bool?]?

    Get an entire column as Option[Bool] values.

    TypedQueryResult::get_column

    fn TypedQueryResult::get_column(self : TypedQueryResult, col : Int) -> Array[Value]?

    Get an entire column as typed values.

    TypedQueryResult::get_date

    fn TypedQueryResult::get_date(self : TypedQueryResult, row : Int, col : Int) -> Int?

    Get a Date value (days since epoch) at the specified position.

    TypedQueryResult::get_date_column

    fn TypedQueryResult::get_date_column(self : TypedQueryResult, col : Int) -> Array[Int?]?

    Get an entire column as Option[Int] date values.

    TypedQueryResult::get_decimal

    fn TypedQueryResult::get_decimal(self : TypedQueryResult, row : Int, col : Int) -> Decimal?

    Get a Decimal value at the specified position.

    TypedQueryResult::get_decimal_column

    fn TypedQueryResult::get_decimal_column(self : TypedQueryResult, col : Int) -> Array[Decimal?]?

    Get an entire column as Option[Decimal] values.

    TypedQueryResult::get_double

    fn TypedQueryResult::get_double(self : TypedQueryResult, row : Int, col : Int) -> Double?

    Get a Double value at the specified position.

    TypedQueryResult::get_double_column

    fn TypedQueryResult::get_double_column(self : TypedQueryResult, col : Int) -> Array[Double?]?

    Get an entire column as Option[Double] values.

    TypedQueryResult::get_int

    fn TypedQueryResult::get_int(self : TypedQueryResult, row : Int, col : Int) -> Int?

    Get an Int value at the specified position.

    TypedQueryResult::get_int_column

    fn TypedQueryResult::get_int_column(self : TypedQueryResult, col : Int) -> Array[Int?]?

    Get an entire column as Option[Int] values.

    TypedQueryResult::get_string

    fn TypedQueryResult::get_string(self : TypedQueryResult, row : Int, col : Int) -> String?

    Get a String value at the specified position.

    TypedQueryResult::get_string_column

    fn TypedQueryResult::get_string_column(self : TypedQueryResult, col : Int) -> Array[String?]?

    Get an entire column as Option[String] values.

    TypedQueryResult::get_timestamp

    fn TypedQueryResult::get_timestamp(self : TypedQueryResult, row : Int, col : Int) -> Int64?

    Get a Timestamp value (microseconds since epoch) at the specified position.

    TypedQueryResult::get_timestamp_column

    fn TypedQueryResult::get_timestamp_column(self : TypedQueryResult, col : Int) -> Array[Int64?]?

    Get an entire column as Option[Int64] timestamp values.

    TypedQueryResult::get_value

    fn TypedQueryResult::get_value(self : TypedQueryResult, row : Int, col : Int) -> Value?

    Get a value at the specified row and column.

    TypedQueryResult::is_null

    fn TypedQueryResult::is_null(self : TypedQueryResult, row : Int, col : Int) -> Bool

    Check if the value at the specified position is NULL.

    TypedQueryResult::row_count

    fn TypedQueryResult::row_count(self : TypedQueryResult) -> Int

    Get the number of rows in the result.

    Value

    pub enum Value {
    Int(Int)
    Double(Double)
    Bool(Bool)
    String(String)
    Date(Int)
    Timestamp(Int64)
    Decimal(Decimal)
    Blob(Bytes)
    Null
    }

    Typed value representing a single DuckDB cell.

    Value::as_blob

    fn Value::as_blob(self : Value) -> Bytes?

    Get the blob value if present, None otherwise.

    Value::as_bool

    fn Value::as_bool(self : Value) -> Bool?

    Get the boolean value if present, None otherwise.

    Value::as_date

    fn Value::as_date(self : Value) -> Int?

    Get the date value if present, None otherwise.

    Value::as_decimal

    fn Value::as_decimal(self : Value) -> Decimal?

    Get the decimal value if present, None otherwise.

    Value::as_double

    fn Value::as_double(self : Value) -> Double?

    Get the double value if present, None otherwise.

    Value::as_int

    fn Value::as_int(self : Value) -> Int?

    Get the integer value if present, None otherwise.

    Value::as_string

    fn Value::as_string(self : Value) -> String?

    Get the string value if present, None otherwise.

    Value::as_timestamp

    fn Value::as_timestamp(self : Value) -> Int64?

    Get the timestamp value if present, None otherwise.

    Value::is_null

    fn Value::is_null(self : Value) -> Bool

    Check if the value is null.

    Value::to_string

    fn Value::to_string(self : Value) -> String

    Convert a Value to its string representation.

    Vector

    #external
    pub type Vector

    array_of

    Size-driven array generator compatible with previous Gen::array_of.

    assert_check

    fn[A :
    Debug
    ] assert_check(name : String, gen :
    Generator
    [A], check : (A) -> Result[Unit, String], config? : CheckConfig, shrink? : (A) -> Iter[A]) -> Unit

    check_with_stats

    fn[A :
    Debug
    ] check_with_stats(gen :
    Generator
    [A], check : (A) -> (Result[Unit, String], String?), config? : CheckConfig) -> CheckResult

    column_type_from_id

    fn column_type_from_id(id : Int) -> ColumnType

    Map DuckDB type id to ColumnType.

    connect

    fn connect(on_ready~ : (Result[Connection, DuckDBError]) -> Unit, path? : String, backend? : JsBackend) -> Unit

    connect_with_config

    fn connect_with_config(on_ready~ : (Result[Connection, DuckDBError]) -> Unit, config : Config?, path? : String, backend? : JsBackend) -> Unit

    date_from_ymd

    fn date_from_ymd(year : Int, month : Int, day : Int) -> Int

    Convert year, month, day to days since 1970-01-01 (Unix epoch). Uses accurate Gregorian calendar calculation with leap year support.

    date_to_days

    fn date_to_days(year : Int, month : Int, day : Int) -> Int

    Convert year, month, day to days since epoch (1970-01-01).

    date_to_ymd

    fn date_to_ymd(total_days : Int) -> (Int, Int, Int)

    Convert days since 1970-01-01 to year, month, day. Uses accurate Gregorian calendar calculation with leap year support.

    days_in_month

    fn days_in_month(year : Int, month : Int) -> Int

    Get the number of days in a month.

    days_to_ymd

    fn days_to_ymd(days : Int) -> (Int, Int, Int)

    Convert days since epoch to year, month, day.

    decimal_from_double

    fn decimal_from_double(value : Double, width : Int, scale : Int) -> Decimal

    Create a decimal from a floating-point value with specified precision. Note: This is approximate due to floating-point representation. For values exceeding 64-bit range, use decimal_from_hugeint instead.

    decimal_from_hugeint

    fn decimal_from_hugeint(lower : Int, upper : Int, width : Int, scale : Int) -> Decimal

    Create a decimal from 128-bit parts. Allows specifying the full 128-bit value for maximum precision.

    decimal_from_parts

    fn decimal_from_parts(whole : Int, fractional : Int, scale : Int) -> Decimal

    Create a decimal from integer parts (64-bit range). For values exceeding 64-bit range, use decimal_from_hugeint instead.

    decimal_to_double

    fn decimal_to_double(decimal : Decimal) -> Double

    Convert decimal to floating-point (approximate). For decimals with upper != 0, returns approximation based on lower part only.

    decimal_to_parts

    fn decimal_to_parts(decimal : Decimal) -> (Int, Int)

    Get the whole and fractional parts of a decimal (64-bit range).

    expect_fixture_case

    fn expect_fixture_case(case : FixtureCase, columns : Array[String], rows : Array[Array[String]], nulls : Array[Array[Bool]]) -> Unit raise

    expect_query_result

    fn expect_query_result(case : FixtureCase, result : QueryResult) -> Unit raise

    fixture_cases

    let fixture_cases : Array[FixtureCase]

    int_pow10

    fn int_pow10(n : Int) -> Int64

    Compute 10^n for decimal scaling.

    interval_from_days

    fn interval_from_days(days : Int) -> Interval

    Create an interval from days.

    interval_from_hours

    fn interval_from_hours(hours : Int) -> Interval

    Create an interval from hours.

    interval_from_minutes

    fn interval_from_minutes(minutes : Int) -> Interval

    Create an interval from minutes.

    interval_from_months

    fn interval_from_months(months : Int) -> Interval

    Create an interval from months.

    interval_from_parts

    fn interval_from_parts(months : Int, days : Int, micros : Int64) -> Interval

    Create an interval from months, days, and microseconds.

    interval_from_seconds

    fn interval_from_seconds(seconds : Int) -> Interval

    Create an interval from seconds.

    interval_to_micros

    fn interval_to_micros(interval : Interval) -> Int64

    Get the total microseconds equivalent of this interval. Note: This is approximate since months have varying lengths.

    is_double

    fn is_double(s : String) -> Bool

    Check if string represents a double.

    is_integer

    fn is_integer(s : String) -> Bool

    Check if string represents an integer.

    is_leap_year

    fn is_leap_year(year : Int) -> Bool

    Check if a year is a leap year.

    is_special_float_string

    fn is_special_float_string(s : String) -> Bool

    list_from_strings

    fn list_from_strings(elements : Array[String]) -> List

    Create a list from string array.

    list_get

    fn list_get(list : List, index : Int) -> String?

    Get element at index.

    list_length

    fn list_length(list : List) -> Int

    Get list length.

    map_from_arrays

    fn map_from_arrays(keys : Array[String], values : Array[String]) -> Map

    Create a map from key and value arrays.

    map_from_pairs

    fn map_from_pairs(pairs : Array[(String, String)]) -> Map

    Create a map from an array of key-value pairs.

    map_get

    fn map_get(m : Map, key : String) -> String?

    Get value by key.

    map_size

    fn map_size(map : Map) -> Int

    Get map size.

    parse_date

    fn parse_date(s : String) -> Result[Int, String]

    Parse ISO date string to days since epoch.

    parse_double

    fn parse_double(s : String) -> Double

    Parse string to Double.

    parse_fraction_to_micros

    fn parse_fraction_to_micros(s : String) -> Int

    Parse fractional seconds (up to 6 digits) into microseconds.

    parse_int

    fn parse_int(s : String) -> Int

    Parse string to Int.

    parse_time_to_micros

    fn parse_time_to_micros(s : String) -> Result[Int64, String]

    Parse time string (HH:MM:SS[.sss...]) to microseconds since midnight.

    parse_timestamp

    fn parse_timestamp(s : String) -> Result[Int64, String]

    Parse ISO timestamp string to microseconds since epoch.

    parse_value

    fn parse_value(s : String) -> Value

    Infer and parse a string value into an appropriate Value type.

    shrink_int

    fn shrink_int(value : Int) -> Iter[Int]

    Simple integer shrinker for legacy shrink_int usage.

    struct_field_count

    fn struct_field_count(s : Struct) -> Int

    Get struct field count.

    struct_from_arrays

    fn struct_from_arrays(fields : Array[String], values : Array[String]) -> Struct

    Create a struct from field names and values.

    struct_from_pairs

    fn struct_from_pairs(pairs : Array[(String, String)]) -> Struct

    Create a struct from key-value pairs.

    struct_get

    fn struct_get(s : Struct, field_name : String) -> String?

    Get field value by name.

    timestamp_from_ymd_hms

    fn timestamp_from_ymd_hms(year : Int, month : Int, day : Int, hour : Int, minute : Int, second : Int) -> Int64

    Convert date components to a timestamp (microseconds since 1970-01-01). Note: This is a simplified calculation. For production use, use a proper date library.

    timestamp_to_ymd_hms

    fn timestamp_to_ymd_hms(micros : Int64) -> (Int, Int, Int, Int, Int, Int)

    Convert timestamp (microseconds since 1970-01-01) to date components. Note: This is a simplified calculation. For production use, use a proper date library.