Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nimdeveloper/better-duck/llms.txt

Use this file to discover all available pages before exploring further.

This page is the complete type mapping reference for the better-duck crate family. It covers every SQL type supported by better-duck-core — the low-level wrapper — as well as the Diesel SQL type layer added by better-duck-diesel. Use it to know exactly which Rust type you will receive when reading a column of a given DuckDB SQL type, and which Rust type to supply when writing a value back.

better-duck-core Type Mapping

The table below maps every DuckDB SQL type to the Rust type used by better-duck-core. Where the Rust type depends on a feature flag, both options are listed.
DuckDB SQL typeRust typeNotes
BOOLEANbool
TINYINTi8
UTINYINTu8
SMALLINTi16
USMALLINTu16
INTEGERi32
UINTEGERu32
BIGINTi64
UBIGINTu64
HUGEINTi128
UHUGEINTu128
FLOATf32
DOUBLEf64
DECIMALrust_decimal::DecimalRequires feature decimal (default ✓)
VARCHAR / TEXTString
BLOBbetter_duck_core::types::blob::BlobNewtype wrapping Vec<u8>; Vec<u8> is reserved for LIST<TINYINT>
DATEchrono::NaiveDate (chrono) / DuckDateDuckDate has year: i32, month: u8, day: u8 fields
TIMEchrono::NaiveTime (chrono) / DuckTimeDuckTime has hour, min, sec, micros fields
TIMESTAMPchrono::NaiveDateTime (chrono) / std::time::SystemTimeMicrosecond precision
TIMESTAMP_Schrono::NaiveDateTime (chrono) / std::time::SystemTimeSecond precision
TIMESTAMP_MSchrono::NaiveDateTime (chrono) / std::time::SystemTimeMillisecond precision
TIMESTAMP_NSchrono::NaiveDateTime (chrono) / std::time::SystemTimeNanosecond precision
TIMESTAMPTZchrono::DateTime<Utc> (chrono) / std::time::SystemTimeUTC timestamp with timezone
TIME WITH TIME ZONE / TIME_TZdate_chrono::TimeTz (chrono) / DuckTimeTzUTC offset fully preserved
TIME_NSchrono::NaiveTime (chrono) / DuckTimeNsNanosecond precision; DuckTimeNs has hour, min, sec, nanos
INTERVALchrono::Duration (chrono) / std::time::Duration
LISTVec<DuckValue>Dynamic element type; use Vec<T: DuckLogicalType> for typed lists
ARRAYBox<[DuckValue]>Fixed-length; use Box<[T]> for typed arrays
STRUCTHashMap<String, DuckValue>String-keyed field map; also DuckStruct newtype
MAPHashMap<DuckValue, DuckValue>Any DuckValue-compatible key type
UNIONBox<DuckValue>Active member only; member name not preserved
ENUMStringDictionary-encoded; variant name returned as owned String
UUIDbetter_duck_core::types::uuid::DuckUuid128-bit; DuckUuid(u128) in standard big-endian bit order
BITbetter_duck_core::types::bit::DuckBitWire-format bytes: padding-count byte + packed bit data
BIGNUMbetter_duck_core::types::bignum::DuckBignumArbitrary-precision integer; magnitude: Vec<u8> (little-endian) + is_negative: bool

better-duck-diesel Type Mapping

better-duck-diesel is a full Diesel 2.3 backend. It supports all standard Diesel SQL types out of the box, plus a set of DuckDB-specific SQL types available under better_duck_diesel::sql_types.

Standard Diesel types

These types work with any Diesel backend and need no extra imports beyond diesel::sql_types::*.
Diesel SQL typeDuckDB typeRust type
BoolBOOLEANbool
SmallIntSMALLINTi16
IntegerINTEGERi32
BigIntBIGINTi64
FloatFLOATf32
DoubleDOUBLEf64
TextVARCHARString
BinaryBLOBVec<u8>
DateDATEchrono::NaiveDate (feature: chrono)
TimeTIMEchrono::NaiveTime (feature: chrono)
TimestampTIMESTAMPchrono::NaiveDateTime (feature: chrono)
NumericDECIMALrust_decimal::Decimal (feature: decimal, default ✓)

DuckDB-specific Diesel types

Import via use better_duck_diesel::sql_types::* in your diesel::table! macro. These cover types that have no standard Diesel equivalent.
Diesel SQL typeDuckDB typeRust type
DuckTinyIntTINYINTi8
DuckUTinyIntUTINYINTu8
DuckUSmallIntUSMALLINTu16
DuckUIntUINTEGERu32
DuckUBigIntUBIGINTu64
DuckHugeIntHUGEINTi128
DuckUHugeIntUHUGEINTu128
DuckTimestamptzTIMESTAMPTZchrono::DateTime<Utc> (feature: chrono)
DuckIntervalINTERVALchrono::Duration (feature: chrono)
DuckTimeTzTIME WITH TIME ZONECoreTimeTz (feature: chrono)
DuckTimeNsTIME_NSchrono::NaiveTime (feature: chrono)
DuckEnumENUMString
DuckListLISTVec<DuckValue>
DuckArrayARRAYVec<DuckValue>
DuckStructSTRUCTHashMap<String, DuckValue>
DuckMapMAPHashMap<DuckValue, DuckValue>
DuckUnionUNIONBox<DuckValue> (active member)
DuckUuidUUIDbetter_duck_core::types::uuid::DuckUuid
DuckBitBITbetter_duck_core::types::bit::DuckBit
DuckBignumBIGNUMbetter_duck_core::types::bignum::DuckBignum
Example table! declaration using DuckDB-specific types:
diesel::table! {
    use diesel::sql_types::*;
    use better_duck_diesel::sql_types::*;

    readings (id) {
        id      -> Integer,
        sensor  -> DuckEnum,         // DuckDB ENUM column
        value   -> Double,
        ts      -> DuckTimestamptz,  // TIMESTAMPTZ column
        tags    -> DuckList,         // LIST column
    }
}

The AppendAble Trait

The AppendAble trait is the bridge between a Rust value and DuckDB’s two write paths: prepared-statement parameter binding (execute_with) and the bulk appender (Appender::append).
pub trait AppendAble {
    /// Binds this value to a prepared statement parameter.
    /// `idx` is **1-based** — the first parameter is `idx = 1`.
    fn stmt_append(&mut self, idx: u64, stmt: duckdb_prepared_statement) -> Result<()>;

    /// Appends this value to an appender row.
    fn appender_append(&mut self, appender: duckdb_appender) -> Result<()>;
}
DuckValue implements AppendAble and delegates to DuckValueRef internally. All primitive Rust numeric types (bool, i8 through i128, u8 through u128, f32, f64) implement it directly via generated impls that call the corresponding duckdb_append_* / duckdb_bind_* FFI functions. For types that have no dedicated FFI function (such as TimestampS, TimeTz, Decimal, DuckUuid, DuckBit, DuckBignum), the impl_appendable_via_to_duck_native! macro generates an AppendAble impl that goes through DuckDialect::to_duck()duckdb_append_value / duckdb_bind_valueduckdb_destroy_value. To implement AppendAble for a custom struct (for example, to bulk-insert rows without allocating per-column DuckValues), implement both methods manually:
use better_duck_core::types::appendable::AppendAble;
use better_duck_core::ffi::{duckdb_appender, duckdb_prepared_statement, duckdb_append_int32, duckdb_bind_int32};
use better_duck_core::error::Result;

struct IntRow(i32);

impl AppendAble for IntRow {
    fn appender_append(&mut self, appender: duckdb_appender) -> Result<()> {
        // SAFETY: appender is valid; the table has one INTEGER column.
        unsafe { duckdb_append_int32(appender, self.0) };
        Ok(())
    }
    fn stmt_append(&mut self, idx: u64, stmt: duckdb_prepared_statement) -> Result<()> {
        // SAFETY: stmt is valid; idx is a 1-based parameter index.
        unsafe { duckdb_bind_int32(stmt, idx, self.0) };
        Ok(())
    }
}

Unsupported Types

The following DuckDB SQL types are not supported by better-duck-core or better-duck-diesel. Attempting to read a column of one of these types will panic at runtime because the DuckDB C API provides no value accessor for them.
DuckDB typeStatus
GEOMETRYNo C API value accessor — panics on read
VARIANTNo C API value accessor — panics on read
ANYNo C API value accessor — panics on read
INTEGER_LITERALNo C API value accessor — panics on read
If you need to work with these types today, cast them to VARCHAR in your SQL query (CAST(geom AS VARCHAR)) and parse the result in Rust.

Build docs developers (and LLMs) love