| name | db-kernel-func-skill |
| description | 数据库内核 SQL 内置函数合成(SQLite / PostgreSQL / DuckDB / ClickHouse)。 覆盖各库的函数注册入口与模式、四大开发规范(功能准确/代码集成/鲁棒性/内存安全)、 各库错误处理与内存管理 API、以及编译/测试命令。用于给数据库写 C/C++ 原生函数、 扩展 SQL 能力时。知识提炼自 SIGMOD 2026 DBCooker/OpenCook 论文与源码。 |
数据库内核函数合成
给数据库内核(SQLite/PostgreSQL/DuckDB/ClickHouse)实现新的 SQL 内置函数,让它能被
SQL 语句直接调用。这是"repository-level code completion"——不是写个独立脚本,而是把
代码正确织入官方仓库的注册体系、内存模型、类型系统和构建流程。
核心纪律(来自 DBCooker 论文的实证发现)
- 先查重,再动手。写代码前先确认目标函数是否已存在,避免 duplicate symbol 错误。
如果已有实现满足规格,不要重写,直接验证并收工。
- 先定位入口点,别盲目搜文件。通用编码 agent 有 63.7% 的时间浪费在文件搜索上;
先搞清"这个函数该注册在哪、依赖哪些宏/辅助函数",再写第一行。
- 声明正确性优先。81.76% 的合成错误是 declaration 相关(注册表项、头文件声明、
函数签名),不是算法逻辑。先把注册和声明写对,逻辑反而不难。
各库注册入口与模式
| 数据库 | 语言 | 注册入口 | 模式 |
|---|
| SQLite | C | FuncDef aBuiltinFunc[],src/func.c | FUNCTION(name, nArg, flags, xFlags, funcPtr) |
| PostgreSQL | C | src/backend/utils/adt/*.c + pg_proc.dat + builtins.h | PG_FUNCTION_INFO_V1(name) + Datum name(PG_FUNCTION_ARGS) |
| DuckDB | C++ | ScalarFunctionSet / FunctionFactory,extension/ | CreateScalarFunctionInfo + 注册到 Catalog |
| ClickHouse | C++ | FunctionFactory::instance() | registerFunction<FunctionX>() |
SQLite 最小例子:sign 的计算逻辑写在 signFunc(func.c 内),通过
FUNCTION(sign, 1, 0, 0, signFunc) 注册到 FuncDef aBuiltinFunc[],之后
SELECT sign(x) 即可用。
四大开发规范
1. 功能准确(Functional Accuracy)
只实现规格指定的行为,不引入额外功能。
- PostgreSQL:匹配既有 SQL 语义的 NULL 处理与类型强转。
- 不要顺手"优化"或加参数默认值,内核函数的行为会被无数下游查询依赖。
2. 代码集成(Code Integration)
识别所有需要改的文件、调用路径、API。注意数据库版本差异,遵循仓库的编码风格、
错误处理和日志约定。
- PostgreSQL 错误处理:
elog() / ereport() / errcode()。
- ClickHouse 日志:
LOG_ERROR / LOG_WARNING / LOG_INFO,遵循 C++ 风格。
3. 鲁棒性(Robustness)
主动覆盖边界:NULL 值、输入范围、类型转换、非法输入、溢出/下溢。
- PostgreSQL:
int8 算术要防溢出;NULL 输入应传播为 NULL。
- ClickHouse:
Nullable 列上的行为要明确;大整数不能意外回绕(wrap)。
4. 内存与安全(Memory & Safety)
防止未定义行为,不泄漏、不悬挂指针、不做不安全操作。
- PostgreSQL:在内存上下文里用
palloc/pfree;绝不返回指向 transient 缓冲区的指针。
- SQLite:
sqlite3_malloc 的结果必须用 sqlite3_free 释放;使用前检查分配失败。
实现计划格式(Plan → Code)
先写实现计划,含有序步骤,每步:(1) 目标文件绝对路径 (2) 函数签名 (3) 步骤描述
(4) 代码骨架占位符 + 本库可用的 code elements。示例(PG 的 text_substring):
/* Step 1: 初始化字符串编码与子串位置变量
Potential code elements: pg_database_encoding_max_length(), Max(), Datum */
[ code to be filled ]
/* Step 2: 处理单字节编码 (eml == 1)
Potential code elements: DatumGetTextPSlice(), pg_add_s32_overflow() */
[ code to be filled ]
实现函数时,参考其他数据库里相同/相似功能的实现(reference function),能少走弯路。
编译与测试命令
SQLite
mkdir -p build && cd build
../configure && make sqlite3 -j$(nproc) && make tclextension -j$(nproc)
函数相关测试文件:func.test ~ func9.test、coalesce.test、window9.test 等。
DuckDB
make release -j$(nproc)
解析 unittest 输出的 summary:test cases: N | N passed | N failed | N skipped。
PostgreSQL
./configure --prefix=$INSTALL && make -j$(nproc) -s && make install
(需先 initdb + pg_ctl start 起实例。)
内核原理深度参考(本地 reference,随 skill 自带)
写函数不只是「填注册表项」,要理解它在内核执行上下文里的行为。这些课程笔记已复制进本 skill 的
references/ 目录,写函数时直接读本地文件(相对路径,随 skill 打包分发),不依赖 skill 目录之外的任何外部路径。
遇到原理问题,按下面这张表定位到具体的 ch 文件:
| 写函数时的问题 | 查哪门课(本地 references/ 相对路径,精确到文件) |
|---|
| 标量/聚合函数在执行器里怎么被调用(火山/向量化/编译执行模型) | references/query-opt/ch07_执行模型:火山、物化与向量化.md、references/15445/ch11_查询执行I.md、references/15445/ch12_查询执行II_并行.md |
| 聚合函数状态在分组/排序里的传递、累加器生命周期 | references/15445/ch09_排序与聚合.md |
| 内存分配与 buffer pool 交互(大结果集别越界、别泄漏) | references/15445/ch05_缓冲池.md |
| NULL 传播语义、类型强转、函数重载解析 | references/15445/ch02_高级SQL.md |
| 函数并发安全(要不要加锁、与 MVCC/2PL 怎么配合) | references/15445/ch15_并发控制理论.md、references/15445/ch16_两阶段锁.md、references/15445/ch17_时间戳排序.md、references/15445/ch18_多版本并发控制.md、references/transaction/ch04_两阶段锁协议.md、references/transaction/ch08_MVCC基础.md |
| 崩溃恢复里函数副作用(WAL / ARIES 语义) | references/15445/ch19_日志协议与方案.md、references/15445/ch20_崩溃恢复算法_ARIES.md |
| 向量/相似度函数(距离度量、量化/图索引下的行为) | references/vectordb/ch02_向量表示与相似度度量.md、references/vectordb/ch03_精确与近似最近邻搜索.md、references/vectordb/ch04_基于量化的索引.md、references/vectordb/ch05_基于图的索引.md、references/vectordb/ch06_过滤向量搜索与系统挑战.md |
| 窗口/分析函数语义(frame、partition) | references/15445/ch09_排序与聚合.md、references/duckdb-analytics/index.md |
| 函数内联与代码生成(查询编译,tum-codegen 那套) | references/tum-codegen/ch12_查询编译.md、references/query-opt/ch07_执行模型:火山、物化与向量化.md |
要点:先查原理再动手。比如写聚合函数,先确认目标库的执行模型是火山还是向量化,
累加器在哪分配、怎么 reset,这决定函数签名和生命周期——比盲目照抄注册表项靠谱得多。
验证闭环
写完必须走三级验证,缺一不可:
- 语法:编译通过(各库的 compile 命令)。
- 合规:符合仓库风格与注册约定(宏、声明、命名)。
- 语义:跑函数相关测试,输出里
0 errors out of 才算过。
结论的置信度 = 验证走到哪一级。只编译过不算完成,测试没过就贴失败输出。