DB::DuckDB — DuckDB-C-API-FFI特征核查报告

DuckDB C API FFI 适配特征核查报告(本机实测)

  • 核查时间:2026-10-22

  • 核查方式:全盘只读文件检索 + duckdb.h 静态解析 + duckdb.dll 导出表读取 + 运行时版本调用

  • 结论定位:可直接用 Raku NativeCall 绑定的档位,无需 V/Zig/C3 中间层(细节见文末)

一、本机 DuckDB 资产定位(唯一头文件来源)

文件绝对路径大小版本/说明
duckdb.hC:\rakudo\bin\duckdb.h250,351 B / 6,295 行全盘唯一 C 头文件;自动生成(generate_c_api.py),#pragma once
duckdb.dllC:\rakudo\bin\duckdb.dll36,724,520 BFileVersion v1.5.5;运行时 duckdb_library_version() 返回 v1.5.5
duckdb.libC:\rakudo\bin\duckdb.lib1,991,572 BMSVC 导入库
duckdb.hppC:\rakudo\bin\duckdb.hpp2,103,253 BC++ API,#define DUCKDB_VERSION "v1.5.5"

关键点:头文件与动态库同目录放在 rakudo 的 bin 下,即 Raku 进程可被 Windows 加载器直接找到 duckdb.dll,这是给 NativeCall 用的部署位形。

其他 DuckDB 副本(非头文件,供版本参照)

  • C:\Windows\duckdb.exe — CLI v1.5.2

  • C:\Users\Admin\AppData\Local\Microsoft\WinGet\Packages\DuckDB.cli_...\duckdb.exe — CLI v1.5.5

  • Python 扩展:_duckdb.cp314-win_amd64.pyd、.cp313、.cp312(多处 site-packages)

  • C:\Users\Admin\.dbx\agents\drivers\duckdb\agent.exe

  • 历史文件 C:\Users\Admin\.duckdb_history

搜索范围(已穷尽)

  • dir /s /b C:\duckdb.* 全 C 盘递归 → 命中 4 个(均在 C:\rakudo\bin)

  • dir /s /b D:\duckdb.* D 盘递归 → 无命中

  • dir /s /b C:\*duckdb*.dll|*.lib|*.pyd → 仅上述列表

  • 结论:本机只有一份 duckdb.h / duckdb.lib,版本链一致,均为 v1.5.5

二、FFI 特征逐项统计

① extern "C" 扁平函数 —— 100% 满足

  • 头文件结构:#pragma once → 导出宏定义 → #include <stdbool.h> <stdint.h> <stddef.h> → 单个 #ifdef __cplusplus / extern "C" { 块包住全部内容。

  • 顶层 DUCKDB_C_API 函数声明 546 个(全部位于该 extern "C" 块内,无 C++ 重载、无模板、无命名空间)。

  • DLL 导出表实测:未修饰 duckdb_* 符号 547 个(与 546 条声明对应,差异来自 1 处声明形式)。

  • 同 DLL 另有 2,996 个 C++ 修饰符号(??$Append@... 等),属 duckdb.hpp 的 C++ 类接口,与 C API 绑定无关,不影响 NativeCall。

  • 头文件本身不含任何类、模板或 C++ 语法(在 C 模式下可直接编译)。

适配含义:Raku sub duckdb_query(...) is native('duckdb') { ... } 直连即可,零 mangling 猜测成本。

② 对外句柄 —— 50 个 opaque 指针

统一形式:typedef struct _duckdb_xxx { void *internal_ptr; } * duckdb_xxx;(结构体体不可见,仅指针可见)。核心 12 个:

句柄形态
duckdb_databasestruct _duckdb_database { void* internal_ptr; } *
duckdb_connection同上 opaque 指针
duckdb_prepared_statementopaque 指针
duckdb_appenderopaque 指针
duckdb_data_chunkopaque 指针
duckdb_vector / duckdb_selection_vectoropaque 指针
duckdb_logical_type / duckdb_valueopaque 指针
duckdb_config / duckdb_config_optionopaque 指针
duckdb_result(注意)按值结构体,非句柄
duckdb_client_context / duckdb_error_dataopaque 指针
duckdb_extension_info / duckdb_function_info / duckdb_bind_info / duckdb_init_infoopaque 指针(回调侧)

完整 opaque 列表(50):vector, selection_vector, instance_cache, database, connection, client_context, prepared_statement, extracted_statements, pending_result, appender, table_description, config, config_option, logical_type, create_type_info, data_chunk, value, profiling_info, error_data, expression, extension_info, function_info, bind_info, init_info, scalar_function, scalar_function_set, aggregate_function, aggregate_function_set, aggregate_state, table_function, copy_function, copy_function_bind_info, copy_function_global_init_info, copy_function_sink_info, copy_function_finalize_info, cast_function, replacement_scan_info, arrow, arrow_stream, arrow_schema, arrow_converted_schema, arrow_array, arrow_options, file_open_options, file_system, file_handle, catalog, catalog_entry, log_storage。

适配含义:句柄在 NativeCall 侧一律映射为不透明指针(Pointer / is repr('CPointer') 的空类),无字段布局负担。

③ 按值返回结构体的函数 —— 21 种结构体、48 处

基础类型:typedef uint64_t idx_t;(第 299 行)。

结构体布局(NativeCall 需逐字段照录)

结构体字段(原样)备注
duckdb_dateint32_t days;8 字节内
duckdb_date_structint32_t year; int8_t month; int8_t day;有尾部 padding
duckdb_timeint64_t micros;
duckdb_time_structint8_t hour; int8_t min; int8_t sec; int32_t micros;有对齐 padding
duckdb_time_nsint64_t nanos;
duckdb_time_tzuint64_t bits;40 位 micros + 24 位 offset 打包在整数内,不是位域
duckdb_time_tz_structduckdb_time_struct time; int32_t offset;嵌套
duckdb_timestampint64_t micros;
duckdb_timestamp_structduckdb_date_struct date; duckdb_time_struct time;嵌套
duckdb_timestamp_s / _ms / _nsint64_t seconds / millis / nanos;
duckdb_intervalint32_t months; int32_t days; int64_t micros;
duckdb_hugeintuint64_t lower; int64_t upper;
duckdb_uhugeintuint64_t lower; uint64_t upper;
duckdb_decimaluint8_t width; uint8_t scale; duckdb_hugeint value;嵌套 + 6 字节 padding
duckdb_query_progress_typedouble percentage; uint64_t rows_processed; uint64_t total_rows_to_process;
duckdb_string_t匿名 union:pointer{uint32 length; char prefix[4]; char* ptr;} / inlined{uint32 length; char inlined[12];}唯一匿名 union,最棘手
duckdb_list_entryuint64_t offset; uint64_t length;
duckdb_columnvoid* deprecated_data; bool* deprecated_nullmask; duckdb_type deprecated_type; char* deprecated_name; void* internal_data;数组元素
duckdb_stringchar* data; idx_t size;需 duckdb_free
duckdb_blobvoid* data; idx_t size;需 duckdb_free
duckdb_bituint8_t* data; idx_t size;需 duckdb_free
duckdb_bignumuint8_t* data; idx_t size; bool is_negative;需 duckdb_free
duckdb_resultidx_t deprecated_column_count; idx_t deprecated_row_count; idx_t deprecated_rows_changed; duckdb_column* deprecated_columns; char* deprecated_error_message; void* internal_data;按值结构体(48 字节),out 参数常用

按值返回结构体的准确签名(节选 + 全量函数族)

DUCKDB_C_API duckdb_hugeint      duckdb_value_hugeint(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_uhugeint     duckdb_value_uhugeint(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_decimal      duckdb_value_decimal(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_date         duckdb_value_date(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_time         duckdb_value_time(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_timestamp    duckdb_value_timestamp(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_interval     duckdb_value_interval(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_string       duckdb_value_string(duckdb_result *result, idx_t col, idx_t row);
DUCKDB_C_API duckdb_blob         duckdb_value_blob(duckdb_result *result, idx_t col, idx_t row);

DUCKDB_C_API duckdb_date_struct      duckdb_from_date(duckdb_date date);
DUCKDB_C_API duckdb_date             duckdb_to_date(duckdb_date_struct date);
DUCKDB_C_API duckdb_time_struct      duckdb_from_time(duckdb_time time);
DUCKDB_C_API duckdb_time             duckdb_to_time(duckdb_time_struct time);
DUCKDB_C_API duckdb_time_tz_struct   duckdb_from_time_tz(duckdb_time_tz micros);
DUCKDB_C_API duckdb_time_tz          duckdb_create_time_tz(int64_t micros, int32_t offset);
DUCKDB_C_API duckdb_timestamp_struct duckdb_from_timestamp(duckdb_timestamp ts);
DUCKDB_C_API duckdb_timestamp        duckdb_to_timestamp(duckdb_timestamp_struct ts);

DUCKDB_C_API duckdb_hugeint  duckdb_double_to_hugeint(double val);
DUCKDB_C_API duckdb_decimal  duckdb_double_to_decimal(double val, uint8_t width, uint8_t scale);

DUCKDB_C_API bool        duckdb_string_is_inlined(duckdb_string_t string);   // 结构体按值传参
DUCKDB_C_API uint32_t    duckdb_string_t_length(duckdb_string_t string);     // 结构体按值传参
DUCKDB_C_API const char *duckdb_string_t_data(duckdb_string_t *string);      // 结构体指针传参

结构体按值传参(入参方向,同样需 CStruct 映射):

DUCKDB_C_API duckdb_state duckdb_bind_hugeint(duckdb_prepared_statement ps, idx_t param_idx, duckdb_hugeint val);
DUCKDB_C_API duckdb_state duckdb_bind_uhugeint(duckdb_prepared_statement ps, idx_t param_idx, duckdb_uhugeint val);
DUCKDB_C_API duckdb_state duckdb_bind_decimal(duckdb_prepared_statement ps, idx_t param_idx, duckdb_decimal val);
DUCKDB_C_API duckdb_value duckdb_create_time_tz_value(duckdb_time_tz value);

按返回值类型聚合(546 条声明):void 134、duckdb_state 106、duckdb_value 48、idx_t 30、duckdb_logical_type 24、bool 17、duckdb_error_data 10,其余为标量与结构体。返回结构体的函数共 48 处、涉及 21 种结构体类型。

适配含义:全部是小型(≤48 字节)聚合体按值返回,x64 下部分走隐藏 sret 指针,但属标准 C ABI,NativeCall 调用端自动处理;只需在 Raku 侧为每个结构体定义 repr('CStruct') 类。唯一需要特别处理的是 duckdb_string_t 的匿名 union。

④ 变参 / 位域 / 函数指针表 —— 全部为 0,除 1 个扩展表

项目结果证据
C 变参函数 ...0 个全文仅 2 处 ... 出现在注释里(COPY ... FROM (...) 语法说明),无 va_list
位域 field : N;0 处正则 ^\s*\w[\w\s\*]*\s:\s*\d+; 零命中;duckdb_time_tz 的 40/24 位拆包用 uint64_t bits + duckdb_from_time_tz() 函数完成,不用位域
结构体内函数指针表1 个:struct duckdb_extension_access三个成员 void (*set_error)(duckdb_extension_info, const char*)、duckdb_database *(*get_database)(duckdb_extension_info)、const void *(*get_api)(duckdb_extension_info, const char *version);仅用于 loadable extension 入口,普通应用不接触
回调函数指针 typedef~60 个(scalar/aggregate/table/copy/cast/replacement scan/log)如 typedef void (*duckdb_scalar_function_t)(duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output);

适配含义:无变参、无位域,消除了 FFI 最容易翻车的两类不可移植特性;函数指针仅出现在「注册 UDF / 扩展」这类高级路径,NativeCall 可用 &sub 形态传入,不属于基础绑定阻塞项。

⑤ 常量定义 —— 纯 enum,无值常量宏

  • 10 个 typedef enum(全部显式赋值,注释声明「数值不得变更,新增只能追加在末尾」,即 ABI 稳定): DUCKDB_TYPE(61 行)、duckdb_state(148)、duckdb_pending_state(151)、duckdb_result_type(159)、duckdb_statement_type(167)、duckdb_error_type(202)、duckdb_cast_mode(249)、duckdb_file_flag(251)、duckdb_config_option_scope(269)、duckdb_catalog_entry_type(281)

  • 7 个 #define,全部是导出/可见性宏:DUCKDB_C_API 的 4 个分支定义、DUCKDB_EXTENSION_API 的 3 个分支定义。没有任何数值常量宏。

  • 枚举命名不一致点:类型名 DUCKDB_TYPE 全大写,成员 DUCKDB_TYPE_BOOLEAN 等;其余枚举为小写蛇形(DuckDBSuccess / DuckDBError 例外)。

适配含义:常量侧只需按 int32 映射枚举常量,无 #define 宏展开/字符串化等绑定障碍。

⑥ 错误返回机制 —— 双轨,均结构化

  1. duckdb_state 枚举 + char** out 参数(主路径)

    typedef enum duckdb_state { DuckDBSuccess = 0, DuckDBError = 1 } duckdb_state;   // 106 个函数返回它
    DUCKDB_C_API duckdb_state duckdb_open(const char *path, duckdb_database *out_database);
    DUCKDB_C_API duckdb_state duckdb_open_ext(const char *path, duckdb_database *out_database,
                                              duckdb_config config, char **out_error);
    DUCKDB_C_API duckdb_state duckdb_query(duckdb_connection connection, const char *query,
                                           duckdb_result *out_result);
    DUCKDB_C_API duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,
                                             duckdb_prepared_statement *out_prepared_statement);

    注意 duckdb_query / duckdb_prepare 的错误信息不在 out_error,而是从 duckdb_result_error(&result) / duckdb_prepare_error(...) 取得 const char*(需 duckdb_free)。

  2. duckdb_error_data opaque 句柄(新式)

    DUCKDB_C_API duckdb_error_type  duckdb_error_data_error_type(duckdb_error_data error_data);
    DUCKDB_C_API const char        *duckdb_error_data_message(duckdb_error_data error_data);
    DUCKDB_C_API bool               duckdb_error_data_has_error(duckdb_error_data error_data);
    DUCKDB_C_API void               duckdb_destroy_error_data(duckdb_error_data *error_data);

    10 个函数以 duckdb_error_data 为返回值(如 config / appender 相关新接口)。

  • 无 C++ 异常跨越边界,无 errno,无线程局部 last-error;错误一律「返回值 + 出参/句柄」。

适配含义:char** out_error 是唯一需要 Raku 侧手工解引用的点(Pointer[Pointer[uint8]] → deref → copy → duckdb_free),但不构成需要 C shim 的理由。

⑦ 字符串/内存释放约定 —— 单一入口 duckdb_free

  • 不存在 duckdb_free_string;全库统一:

    DUCKDB_C_API void *duckdb_malloc(size_t size);
    DUCKDB_C_API void  duckdb_free(void *ptr);

    文档明确:duckdb_free 用于释放 duckdb_malloc / duckdb_value_varchar / duckdb_value_blob / duckdb_value_string 的产物。

  • 需 duckdb_free 的返回串(文档逐条标注):duckdb_value_varchar、duckdb_value_string.data、duckdb_value_blob.data、duckdb_value_bit.data、duckdb_bignum.data、各 duckdb_*_to_string、duckdb_result_error、duckdb_prepare_error、枚举/结构体名字查询、duckdb_pending_error 等。

  • 明确 NOT free:duckdb_value_varchar_internal / duckdb_value_string_internal(指向结果内部内存)、duckdb_string_t(非拥有结构,注释写明「does not have to be freed」)。

  • 资源释放另一族:duckdb_destroy_*(value/logical_type/data_chunk/result/error_data/...)、duckdb_close、duckdb_disconnect、duckdb_destroy_instance_cache。

适配含义:释放语义单点收敛,Raku 侧只需暴露一个 duckdb_free(Pointer) 并在 wrapper 里统一调用,无多套 allocator 不匹配风险。

三、对全局档位判断的支撑结论(仅就本机 v1.5.5 C API 特征)

  1. 属「可直接 FFI 绑定」档:546 个函数全部在单个 extern "C" 块内、DLL 导出 547 个未修饰符号;无变参、无位域、无 C++ 名称修饰、句柄全 opaque、常量全 enum、错误全结构化、释放单入口。

  2. 不需要 V / Zig / C3 / ZenC 中间层:没有一类特性是这几门语言能简化而 NativeCall 做不到的。

  3. Raku 侧需自备的适配(属绑定层,不是 C shim)仅 3 处:

    • char** out_error → 用 Pointer[Pointer[uint8]] 承载后 deref + duckdb_free;

    • duckdb_string_t 匿名 union → 用 repr('CUnion') 嵌套两个 CStruct,或直接绕开改用 duckdb_string_t_data / duckdb_string_t_length;

    • 21 种按值结构体 → 各自 repr('CStruct') 声明,注意 duckdb_decimal(6 字节 padding)、duckdb_bignum(尾部 bool padding)、duckdb_time_struct(int8→int32 对齐)的字段顺序。

  4. 唯一可考虑写 shim 的场景(可选、非必需):注册回调式 UDF(scalar / aggregate / table / copy function)与 duckdb_extension_access 函数指针表。属高级扩展能力,基础读写查询链路完全不需要。 (内容由AI生成,仅供参考) (内容由AI生成,仅供参考)

DB::DuckDB v0.9.2

DuckDB driver for the Raku DB interface - NativeCall binding over libduckdb

Authors

  • skyter10086

License

Artistic-2.0

Dependencies

DBAlien::Libduckdb:ver<1.1.0>

Test Dependencies

Provides

  • DB::DuckDB
  • DB::DuckDB::Appender
  • DB::DuckDB::Arrow
  • DB::DuckDB::ArrowShim
  • DB::DuckDB::ArrowShim::Native
  • DB::DuckDB::Bridge
  • DB::DuckDB::Chunk
  • DB::DuckDB::ColumnView
  • DB::DuckDB::Connection
  • DB::DuckDB::Decoder
  • DB::DuckDB::Error
  • DB::DuckDB::Native
  • DB::DuckDB::Parquet
  • DB::DuckDB::Result
  • DB::DuckDB::ShimCache
  • DB::DuckDB::Statement
  • DB::DuckDB::TextColumn
  • DB::DuckDB::VarcharShim
  • DB::DuckDB::VarcharShim::Native

The Camelia image is copyright 2009 by Larry Wall. "Raku" is a trademark of the Yet Another Society. All rights reserved.

Built with Podlite — the markup and publishing tools behind this site.