DB::DuckDB — DuckDB-C-API-FFI特征核查报告
DuckDB C API FFI 适配特征核查报告(本机实测)
核查时间:2026-10-22
核查方式:全盘只读文件检索 + duckdb.h 静态解析 + duckdb.dll 导出表读取 + 运行时版本调用
结论定位:可直接用 Raku NativeCall 绑定的档位,无需 V/Zig/C3 中间层(细节见文末)
一、本机 DuckDB 资产定位(唯一头文件来源)
| 文件 | 绝对路径 | 大小 | 版本/说明 |
|---|---|---|---|
| duckdb.h | C:\rakudo\bin\duckdb.h | 250,351 B / 6,295 行 | 全盘唯一 C 头文件;自动生成(generate_c_api.py),#pragma once |
| duckdb.dll | C:\rakudo\bin\duckdb.dll | 36,724,520 B | FileVersion v1.5.5;运行时 duckdb_library_version() 返回 v1.5.5 |
| duckdb.lib | C:\rakudo\bin\duckdb.lib | 1,991,572 B | MSVC 导入库 |
| duckdb.hpp | C:\rakudo\bin\duckdb.hpp | 2,103,253 B | C++ API,#define DUCKDB_VERSION "v1.5.5" |
关键点:头文件与动态库同目录放在 rakudo 的 bin 下,即 Raku 进程可被 Windows 加载器直接找到 duckdb.dll,这是给 NativeCall 用的部署位形。
其他 DuckDB 副本(非头文件,供版本参照)
C:\Windows\duckdb.exe— CLI v1.5.2C:\Users\Admin\AppData\Local\Microsoft\WinGet\Packages\DuckDB.cli_...\duckdb.exe— CLI v1.5.5Python 扩展:
_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_database | struct _duckdb_database { void* internal_ptr; } * |
| duckdb_connection | 同上 opaque 指针 |
| duckdb_prepared_statement | opaque 指针 |
| duckdb_appender | opaque 指针 |
| duckdb_data_chunk | opaque 指针 |
| duckdb_vector / duckdb_selection_vector | opaque 指针 |
| duckdb_logical_type / duckdb_value | opaque 指针 |
| duckdb_config / duckdb_config_option | opaque 指针 |
| duckdb_result(注意) | 按值结构体,非句柄 |
| duckdb_client_context / duckdb_error_data | opaque 指针 |
| duckdb_extension_info / duckdb_function_info / duckdb_bind_info / duckdb_init_info | opaque 指针(回调侧) |
完整 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_date | int32_t days; | 8 字节内 |
| duckdb_date_struct | int32_t year; int8_t month; int8_t day; | 有尾部 padding |
| duckdb_time | int64_t micros; | |
| duckdb_time_struct | int8_t hour; int8_t min; int8_t sec; int32_t micros; | 有对齐 padding |
| duckdb_time_ns | int64_t nanos; | |
| duckdb_time_tz | uint64_t bits; | 40 位 micros + 24 位 offset 打包在整数内,不是位域 |
| duckdb_time_tz_struct | duckdb_time_struct time; int32_t offset; | 嵌套 |
| duckdb_timestamp | int64_t micros; | |
| duckdb_timestamp_struct | duckdb_date_struct date; duckdb_time_struct time; | 嵌套 |
| duckdb_timestamp_s / _ms / _ns | int64_t seconds / millis / nanos; | |
| duckdb_interval | int32_t months; int32_t days; int64_t micros; | |
| duckdb_hugeint | uint64_t lower; int64_t upper; | |
| duckdb_uhugeint | uint64_t lower; uint64_t upper; | |
| duckdb_decimal | uint8_t width; uint8_t scale; duckdb_hugeint value; | 嵌套 + 6 字节 padding |
| duckdb_query_progress_type | double 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_entry | uint64_t offset; uint64_t length; | |
| duckdb_column | void* deprecated_data; bool* deprecated_nullmask; duckdb_type deprecated_type; char* deprecated_name; void* internal_data; | 数组元素 |
| duckdb_string | char* data; idx_t size; | 需 duckdb_free |
| duckdb_blob | void* data; idx_t size; | 需 duckdb_free |
| duckdb_bit | uint8_t* data; idx_t size; | 需 duckdb_free |
| duckdb_bignum | uint8_t* data; idx_t size; bool is_negative; | 需 duckdb_free |
| duckdb_result | idx_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宏展开/字符串化等绑定障碍。
⑥ 错误返回机制 —— 双轨,均结构化
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)。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 特征)
属「可直接 FFI 绑定」档:546 个函数全部在单个
extern "C"块内、DLL 导出 547 个未修饰符号;无变参、无位域、无 C++ 名称修饰、句柄全 opaque、常量全 enum、错误全结构化、释放单入口。不需要 V / Zig / C3 / ZenC 中间层:没有一类特性是这几门语言能简化而 NativeCall 做不到的。
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 对齐)的字段顺序。
唯一可考虑写 shim 的场景(可选、非必需):注册回调式 UDF(scalar / aggregate / table / copy function)与
duckdb_extension_access函数指针表。属高级扩展能力,基础读写查询链路完全不需要。 (内容由AI生成,仅供参考) (内容由AI生成,仅供参考)