news 2026/9/29 8:19:42

SQLCipher 中的 Miscellaneous Extensions:从可加载扩展源码到虚拟表实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQLCipher 中的 Miscellaneous Extensions:从可加载扩展源码到虚拟表实战指南
  • 数据库
  • 关系型数据库
  • 嵌入式数据库
  • 密码学

【免费下载链接】sqlcipher

SQLCipher is a standalone fork of SQLite that adds 256 bit AES encryption of database files and other security features.

项目地址:https://gitcode.com/gh_mirrors/sq/sqlcipher
点击查看免费下载

本篇指南以 SQLCipher 仓库ext/misc/目录中的官方文档为核心,系统讲解该目录下 CSV 虚拟表、generate_series、ZIP 归档读写、unionvtab/swarmvtab 分库虚拟表等轻量级可加载扩展的实现原理与实战用法。读完本文,你将掌握如何编译、加载这些扩展,理解每个扩展的入口函数、虚拟表机制与典型 SQL 调用方式,并能直接在本仓库源码中定位对应实现。

一、ext/misc:一个"单文件可加载扩展"的工具箱

ext/misc/README.md是 SQLCipher 仓库(SQLite 的一个独立分支,增加了 256 位 AES 数据库文件加密)中"杂项扩展"目录的官方说明。它的定位非常明确:该目录存放了一批小型的、可加载的 SQLite 扩展,每个扩展都由单个 C 源文件实现,并在文件头部注释中给出完整的功能描述。

从源码结构看,这批扩展可分为三类:

  • 虚拟表类:csv.c、series.c、unionvtab.c、zipfile.c,以及目录中未被 README 单独列出但同样存在的amatch.c、closure.c、completion.c、fileio.c、fuzzer.c、spellfix.c、templatevtab.c、vtablog.c等;
  • SQL 函数类:rot13.c、shathree.c、sha1.c,以及base64.c、base85.c、decimal.c、ieee754.c、percentile.c、regexp.c、uuid.c等;
  • 库/工具类:dbdump.c(近似实现命令行.dump的 C 库)、appendvfs.c、cksumvfs.c、memstat.c、vfslog.c等 VFS 层组件。

所有扩展都遵循 SQLite 可加载扩展的标准约定:以#include "sqlite3ext.h"+SQLITE_EXTENSION_INIT1开头,导出sqlite3_<扩展名>_init入口函数(如 csv.c 中的sqlite3_csv_init)。编译与加载的通用方法请参考 SQLite 官方 loadable extensions 文档(sqlite.org/loadext.html),下文将给出基于本仓库的具体命令。

二、csv.c:用虚拟表直接查询 CSV 文件

csv.c 实现了一个用于读取逗号分隔值(CSV)文件的虚拟表,是"用 SQL 查询文件"最直接的例子。

2.1 基本用法

.load ./csv -- 在 sqlite3 命令行中加载扩展 CREATE VIRTUAL TABLE temp.csv USING csv(filename=FILENAME); SELECT * FROM csv;
  • 默认情况下,列被命名为c1、c2、c3……,列数由 CSV 首行推断;
  • 通过columns=N参数可显式指定列数;
  • 通过schema=参数可用自定义CREATE TABLE语句定义列名与类型;
  • 通过data=参数可直接把 CSV 文本作为字符串传入,无需文件。

2.2 带自定义 schema 的示例

CREATE VIRTUAL TABLE temp.csv2 USING csv( filename = '../http.log', schema = 'CREATE TABLE x(date,ipaddr,url,referrer,userAgent)' );

2.3 源码实现要点

从源码结构看,csv 虚拟表的核心是一个CsvReader状态机(csv.c):它维护输入文件流、字段文本缓冲区、当前行号与缓冲读取位置,按字段为单位解析引号转义、逗号分隔与换行。模块还定义了CSV_MXERR 200(错误消息上限)与CSV_INBUFSZ 1024(输入缓冲大小)。若以-DSQLITE_TEST编译,模块还会启用专用于虚拟表测试的调试特性。仓库的 test/csv01.test 覆盖了该扩展的核心场景,包括参数缺省、schema=/columns=/data=组合等。

三、series.c:generate_series 表值函数

series.c 实现 PostgreSQL 风格的generate_series()表值函数,是"用虚拟表实现表值函数"的教科书级模板。

3.1 行为语义

其内部等价于如下虚拟表定义:

CREATE TABLE generate_series( value, -- 输出列,rowid 是该列的别名 start HIDDEN, -- 起始值(必填) stop HIDDEN, -- 结束值(默认 4294967295,即 0xffffffff) step HIDDEN -- 步长(默认 1,0 视为 1) );

函数参数会被翻译为对隐藏列的等值约束,例如下面两对查询完全等价:

SELECT * FROM generate_series(0,100,5); SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5;

3.2 典型调用

SELECT * FROM generate_series(0,100,5); -- 0,5,10,...,95,100,共 21 行 SELECT * FROM generate_series(0,100); -- 0..100,步长 1,共 101 行 SELECT * FROM generate_series(20) LIMIT 10; -- 20..29,共 10 行 SELECT * FROM generate_series(0,-100,-5); -- 0,-5,...,-100,共 21 行 SELECT * FROM generate_series(0,-1); -- 空序列

3.3 查询优化原理

该实现将xCreate置为 NULL,因此无法通过CREATE VIRTUAL TABLE ... USING generate_series显式创建,而是始终可用的"无名虚拟表"。xBestIndex会寻找对隐藏列start/stop/step的等值约束:

  • 命中start与stop时返回较小代价,鼓励查询规划器优先确定序列边界;
  • 缺失边界时使用默认值 0 / 4294967295 / 1;
  • 2024-08-22 起(见 series.c 头注释),xBestIndex还识别对value列的等值与不等值约束并将其作为序列范围的附加边界,因此:
    SELECT value FROM generate_series($SA,$EA) WHERE value BETWEEN $SB AND $EB;

    逻辑上等价于generate_series(max($SA,$SB), min($EA,$EB))。

generate_series的约束边界仅限有符号 64 位整数,相关行为在 test/tabfunc01.test 等测试中有覆盖。

四、rot13.c:自定义 SQL 函数的最小模板

rot13.c 实现了极简单的rot13()替换函数,官方文档明确推荐它作为编写新自定义 SQL 函数的最佳起点模板。

4.1 实现要点

核心是一个 10 行左右的单字符转换函数(rot13.c):对a-z/A-Z的 ASCII 字母向后旋转 13 位并回绕,非字母字符保持不变,因此rot13(rot13(X)) == X恒成立。SQL 层函数rot13func遵循标准sqlite3_context/sqlite3_value约定:NULL 输入返回 NULL;短文本使用栈上 100 字节临时缓冲,长文本才走sqlite3_malloc64堆分配,避免频繁小分配。此外它还注册了rot13排序序列(collating sequence),使x = y COLLATE rot13等价于对rot13(x)与rot13(y)做二进制比较。

4.2 模板价值

从该文件可以学到一个完整 SQL 函数扩展需要的全部骨架:SQLITE_EXTENSION_INIT1初始化宏、参数数量校验、类型检查、内存管理与sqlite3_result_text结果回传——这些正是编写任何自定义 SQL 函数(包括 SQLCipher 场景下对加密字段做应用层变换)的通用范式。

五、shathree.c:sha3 / sha3_query 哈希函数

shathree.c 按 NIST FIPS 202 SHA-3 标准实现了三个 SQL 函数:

  • sha3(X, SIZE):对输入 X 计算 SHA3 哈希。文本按 UTF-8 字节、BLOB 按原始二进制、数字先转 UTF-8 文本再哈希;SIZE可选,缺省为 256,可取 224 / 256 / 384 / 512;
  • sha3_agg(Y, SIZE):聚合函数,对全部 Y 输入求哈希。因顺序影响结果,官方建议配合ORDER BY使用;各数据类型被编码为带类型前缀的字节序列(如I+8 字节大端整数、Tnnn:+文本、Bnnn:+BLOB、N表示 NULL),因此sha3(1) = sha3('1')之类的恒等式成立(详见 shathree.c 头注释);
  • sha3_query(Z, SIZE):执行 Z 中 SQL 语句产生的所有查询,对每行结果额外加前缀R后哈希,可用于对查询结果集做完整性校验。

该文件命名为shathree.c而非sha3.c,是因为 SQLite 默认入口点名称基于源文件名去掉数字生成——若叫sha3.c会与此前sha1.c扩展的入口点冲突(README 与源码注释均说明了这一点)。文件头部还给出了一组可直接验证的恒等式,例如:

SELECT sha3('hello') = sha3(x'68656c6c6f'); WITH a(x) AS (VALUES('xyzzy')) SELECT sha3_agg(x) = sha3('T5:xyzzy') FROM a;

六、unionvtab.c 与 swarmvtab.c:跨库虚拟表

unionvtab.c 同时实现unionvtab与swarmvtab两个虚拟表,核心能力是把一张逻辑大表横向切分存储到多个数据库文件,再通过单一虚拟表只读访问。二者对源表的要求相同:

  • 必须是 rowid 表(不能是虚拟表、WITHOUT ROWID 表或视图);
  • 各表列集合、顺序与声明类型完全一致;
  • 不允许有用户自定义的_rowid_列;
  • 各表必须持有互不重叠的 rowid 区间。

区别在于:unionvtab要求所有源表位于主库或用户 ATTACH 的库中;swarmvtab则允许源表位于磁盘上任意数据库文件,由实现自动打开/关闭文件,且支持按需挂载(attach on demand)。

6.1 unionvtab 创建方式

CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>);

该 SQL 语句在虚拟表每次创建或打开时执行,为每个源表返回一行,每行四列:所在数据库名(main/temp/ATTACH 名,或 NULL 表示按常规方式全库查找)、表名、该表可能存储的最小 rowid、最大 rowid。

6.2 swarmvtab 新旧两种语法

旧语法:

CREATE VIRTUAL TABLE <name> USING swarmvtab(<sql-statement>, <callback>);

其中首列必须是可打开源库文件的路径或 URI;callback可选,当文件尚不存在时会被调用。

新语法:

CREATE VIRTUAL TABLE <name> USING swarmvtab( <sql-statement> [, <options>] );

合法选项包括:missing=<udf>(文件缺失时的回调函数)、openclose=<udf>、maxopen=<integer>(最多同时打开的文件数)以及任意<sql-parameter>=<text-value>。SQL 语句除旧语法的 4 列外,还可返回第 5 列 "context" 文本(供回调使用)。

仓库中 test/unionvtab.test、test/unionvtabfault.test 与 test/swarmvtab.test、test/swarmvtab2.test、test/swarmvtab3.test 提供了完整的分库、按需打开与故障注入测试。

七、zipfile.c:可读可写的 ZIP 归档虚拟表

zipfile.c 提供一个能读取并写入 ZIP 归档文件的虚拟表。典型用法:

SELECT name, sz, datetime(mtime,'unixepoch') FROM zipfile($filename);

当前实现的明确限制(源码头注释列出,zipfile.c):

  • 不支持加密;
  • 不支持跨多文件(spanning)归档;
  • 不支持 zip64 扩展;
  • 仅支持 zlib 的 inflate/deflate 压缩方法。

实现依赖 zlib 库,且与 CLI 的sqlite3_stdio.h模块配合时可复用其fopen定义;若未包含该头文件,则自行定义sqlite3_fopen(zipfile.c)。相关功能在 test/zipfile.test、test/zipfile2.test、test/zipfilefault.test 中验证。

八、dbdump.c:可嵌入的 .dump 等价库

dbdump.c不是可加载扩展,而是一个 C 语言库,提供对命令行.dump命令的近似等价实现:

int sqlite3_db_dump( sqlite3 *db, /* 数据库连接 */ const char *zSchema, /* 模式名:main / temp / 任意 ATTACH 库 */ const char *zTable, /* 为 NULL 时导出全部表,否则仅导出该表 */ void (*xCallback)(void*, const char*), /* 输出回调,签名兼容 fputs() */ void *pArg );

其输出为可精确重建原库的 UTF-8 文本 SQL 语句,并保留 ROWID 值。回调签名特意设计为与fputs()兼容,便于直接接入输出流。若以-DDBDUMP_STANDALONE编译,文件会附带main()成为命令行工具,用法为dbdump <数据库文件> [schema] [table]。在仓库构建体系中,main.mk已将 dbdump 编为独立可执行目标(main.mk)。

九、编译、集成与在 SQLCipher 中使用

9.1 可加载扩展的标准编译路径

在 SQLCipher 仓库中,扩展编译进 CLI/库的方式已固化于 main.mk:

  • main.mk、main.mk、main.mk、main.mk 将csv.c、series.c、unionvtab.c、zipfile.c等列入 CLI 或核心库的编译源;
  • main.mk 将series.c、shathree.c、sha1.c编译进测试构建;
  • main.mk 在另一组构建目标中同样引入series.c、sha1.c、shathree.c、zipfile.c。

即:这些扩展既可随构建产物静态集成,也可单独编译为.so/.dll动态加载(sqlite3 命令行下使用.load ./csv等命令)。由于 SQLCipher 是 SQLite 的加密分支,其可加载扩展机制与上游一致,唯一需要注意的是扩展需链接到 SQLCipher 提供的sqlite3API 之上(编译时使用本仓库 src/sqlite3ext.h 与 src/sqlcipher.h 导出的接口)。

9.2 通用加载流程(适用于任一扩展)

# 编译单个扩展(以 rot13 为例) gcc -O2 -fPIC -shared ext/misc/rot13.c -o rot13.so # 在 sqlite3 命令行中加载并使用 sqlite3 test.db sqlite> .load ./rot13 sqlite> SELECT rot13('Hello, World!');

十、总结:从文档到源码的完整阅读路径

ext/misc/README.md篇幅虽短,却精准勾勒出这批扩展的设计哲学——单文件、头注释即文档、以虚拟表和自定义函数两种机制扩展 SQL。在本仓库中,每个条目都能找到对应的完整实现与测试证据:

README 条目实现文件仓库内测试
CSV 虚拟表ext/misc/csv.ctest/csv01.test
近似 .dump 的库ext/misc/dbdump.cmain.mk 独立目标
JSON 处理(已内置于 amalgamation)核心实现位于 src/json.ctest/json 目录
rot13 函数模板ext/misc/rot13.c—
generate_series 虚拟表ext/misc/series.ctest/tabfunc01.test
sha3 系列函数ext/misc/shathree.c—
unionvtab / swarmvtabext/misc/unionvtab.ctest/unionvtab.test、test/swarmvtab.test
ZIP 归档虚拟表ext/misc/zipfile.ctest/zipfile.test、test/zipfile2.test

对于想深入 SQLCipher 扩展机制、或需要为加密数据库编写自定义 SQL 函数与虚拟表的开发者而言,ext/misc/是仓库中最值得研读的入门区:先读每个文件头部的注释(即"扩展自带文档"),再结合xBestIndex、sqlite3_context等接口源码,即可快速掌握 SQLite/SQLCipher 扩展开发的完整链路。

  • 数据库
  • 关系型数据库
  • 嵌入式数据库
  • 密码学

【免费下载链接】sqlcipher

SQLCipher is a standalone fork of SQLite that adds 256 bit AES encryption of database files and other security features.

项目地址:https://gitcode.com/gh_mirrors/sq/sqlcipher
点击查看免费下载
上一篇:Anarlog 1.4.4 深度解析:分享体验升级与实时会话稳定性修复
下一篇:Gutenberg ListView 组件完全指南:块编辑器层级结构面板的实现原理与开发实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 8:16:59

Claude Code Skill 体系实战:用 settings.json 骨架打通 Prompt 到 Workflow

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 8:14:47

连接池又双叒枯竭了:Hikari + Stream/Cursor 未关闭的排查与配置复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 8:14:03

godot + vscode ai开发:用 TaoToken 统一 Key 打通编辑器补全与调试配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华