跳到主要内容

rxdb-adapter-sqlite-core

@aiao/rxdb 的 SQLite 适配器共享内核。它把「实体 → SQL」的映射、表名解析、规则组构建、基础 Repository 与变更事件等能力抽象为后端无关的基类,供各具体 SQLite 适配器复用。

本包一般不直接安装,而是作为 @aiao/rxdb-adapter-sqlite、@aiao/rxdb-adapter-sqlite-wasm、@aiao/rxdb-adapter-sqliteai 等适配器的依赖被间接引入。

提供的能力​

  • RxDBAdapterSqliteBase:SQLite 适配器基类,封装事务、建表与变更钩子
  • SqliteTransactionExecutor:TransactionExecutor 在 SQLite 侧的实现;transaction() 回调收到的就是它
  • SqliteRepository:基于 SQLite 的类型安全 Repository 实现
  • buildRuleGroup:将查询规则编译为 SQL 条件
  • sqliteGetTableName / sqliteGetTableNameByMetadata:实体 → 表名解析
  • 后端契约类型:SqliteBackend、SqliteChangeEvent、SQLiteChangeType 等

具体后端只需实现 SqliteBackend 契约即可接入。

事务 API(C2 已落地第一步)​

RxDBAdapterSqliteBase.transaction() / runInTransaction() 的回调签名已收紧为接收 SqliteTransactionExecutor:

import type { TransactionFun } from '@aiao/rxdb-adapter-sqlite-core';

const fun: TransactionFun = async executor => {
// 持有 executor 才算「在本事务内」
const repo = executor.getRepository(Todo);
await repo.create({ title: 'inside tx' });

// executor.execute(sql, bindings) 透传底层 client.execute,
// 既有的 `transaction(async tx => tx.execute(sql))` 写法不受影响
await executor.execute('SELECT 1');

// 嵌套内层工作
await executor.run(async inner => inner.getRepository(Todo).count());

// 合并远端变更
await executor.mergeChanges(actions, localChanges, /* disableTriggers */ false);
};

零参回调仍然兼容(TS 允许形参更少)。SqliteTransactionExecutor 不直接导出 —— 它是 RxDBAdapterSqliteBase 的内部产物,外部代码只通过 @aiao/rxdb 的 TransactionExecutor 接口与之交互。

备份与恢复​

六个 SQLite 适配器(四个浏览器端,加上桌面的 sqlite-electron 与 sqlite-tauri)共用这里的实现:adapter.backup(sink) 把整个数据库写成一个 .rxdb-backup 归档流, adapter.restore(source) 把归档恢复进该 adapter 配置的空存储。两端都逐块流式处理,不会把整个库读进内存。

外部文件不在备份范围内。 归档只包含数据库本身(scope: { database: 'included', externalFiles: 'excluded' })。rxdb-plugin-storage 等插件存放在数据库之外的文件需要另行备份。

// 备份:写入任意 WritableStream(例如 File System Access 的文件句柄)
const handle = await showSaveFilePicker({ suggestedName: 'notes.rxdb-backup' });
const result = await adapter.backup(await handle.createWritable());

// 恢复:目标 RxDB 实例已注册 adapter,但尚未 connect
const target = await rxdb.getAdapter('wa-sqlite');
await target.restore(file.stream());
await rxdb.connect('wa-sqlite');

要点:

  • 逻辑转储:归档是全部结构语句(表 / 索引 / 视图 / 触发器 / 虚表、user_version、application_id、sqlite_sequence) 加上 quote() 生成的行字面量,在 adapter 串行队列里的同一个读事务中取得,因此落在一个已提交事务边界上, 也不存在「只复制主文件、漏掉 WAL」的问题。写出期间数据库被占用:输出流有背压时读写都会排队; 锁等待上限由 lockTimeoutMs(默认 30s)控制,超时抛 lock_timeout。
  • 兼容性:adapter 名、SQLite 大版本、归档用到的虚表模块(例如 fts5)、系统表 / 变更编码版本、实体结构指纹、 加密认证域全部在写入第一条语句之前判定;不兼容报 incompatible_archive / auth_domain_mismatch。 完整性(SHA-256)读到末尾才能确认,失败时已写的内容在同一事务里回滚(corrupt_archive / truncated_archive)。
  • 空目标:目标只能是引擎新建空库本来的样子。引擎在每条连接上自动建的对象(例如 sqliteai 内置扩展的表) 由客户端的 describeBlankDatabase() 在一条临时 :memory: 连接上描述,只有对象清单与内容都与之完全一致才算空; 已有业务数据、只有 RxDB 系统表、或引擎表里写过数据的目标都报 target_not_empty。
  • 中断:持久化目标先提交一张「恢复进行中」标记表,再在一个事务里写入结构与数据。页面在恢复中途被关后, 连接与再次恢复都会报 restore_incomplete,调用 adapter.cleanupIncompleteRestore() 清理后即可重新恢复; 清理失败时报 cleanup_pending。内存目标没有残留,恢复出来的库由下一次 connect() 接管。
  • 独占:持久化目标由 Web Lock rxdb-sqlite-storage:<storageKey> 保护;同一目标上并发的恢复只有一个能赢(target_busy), 恢复期间的连接尝试报 restore_in_progress。桌面的库文件还可能被别的窗口、别的进程打开,Web Lock 挡不住它们: 恢复与清理先让自己那条连接拿到 SQLite 的文件级独占锁(locking_mode = EXCLUSIVE),库文件开在别处时报 target_busy (details.field 为 storage)。
  • 加密库:归档里只有密文和 keyring 元数据,不含口令与密钥;恢复后的库保持锁定,用原口令 unlock()。
  • 恢复写入的行不产生变更历史。

能力矩阵​

adapter内存存储持久化存储其余配置持久化 journal_modeFTS5引擎自建对象
@aiao/rxdb-adapter-wa-sqliteMemoryVFS / MemoryAsyncVFSIDBBatchAtomicVFS(idb)其余 VFS:unsupported_combination(vfs)delete❌无
@aiao/rxdb-adapter-sqlite-wasmvfs: 'memory'vfs: 'idb'其余 VFS:unsupported_combination(vfs)delete✅无
@aiao/rxdb-adapter-sqlite不开 opfsopfs: true(opfs)opfsFallback: 'memory':unsupported_combinationdelete✅无
@aiao/rxdb-adapter-sqliteai不开 opfsopfs: true(opfs)opfsFallback: 'memory':unsupported_combinationdelete✅vector / memory 扩展的表
@aiao/rxdb-adapter-electron(sqlite-electron)无库文件(file)全部选项都在矩阵内wal❌无
@aiao/rxdb-adapter-tauri(sqlite-tauri)无库文件(file)全部选项都在矩阵内wal✅无
  • 同一 adapter 的内存与持久化存储互为源 / 目标;跨 adapter 的归档报 incompatible_archive。
  • WAL:连接初始化会请求 WAL。四个浏览器 adapter 的持久化 VFS 都不提供 WAL 需要的共享内存,SQLite 静默保留 delete; 两个桌面 adapter 开的是真实库文件,WAL 生效。备份本身是 SQL 层的读事务,与日志模式无关;WAL 专属用例 (关掉自动 checkpoint,已提交、只在 WAL 里的帧也要进快照)在两个桌面后端上跑。
  • FTS5:sqlite-electron 的 SQLite 编进了 FTS5,但 host 以 defensive 模式运行,写不回虚表的影子表, 含 FTS5 的库备份与恢复都报 unsupported_combination(details.field 为 adapter.extensions);sqlite-tauri 的 Rust 宿主不开 defensive,FTS5 可用。
  • 所有错误都是 RxDBBackupError,按 error.code 分支处理(isRxDBBackupError() 可做类型收窄)。
  • 自定义后端要支持恢复,客户端需实现 setChangeEventsMuted() 与 describeBlankDatabase()(可用本包导出的 describeSqliteDatabase());缺少时恢复报 unsupported_combination(details.field 为 client.<方法名>)。 这两个方法调用失败(例如请求被宿主应用的 kind 守卫挡下)报 io_error,原始错误在 cause,此时目标还没被写过。

文档​

License​

MIT

Enumerations​

EnumerationDescription
SQLiteChangeType与 C API 一致的 SQLite 变更类型常量

Classes​

ClassDescription
Oo1ClientBase包装 oo1.DB 风格运行时(@sqlite.org/sqlite-wasm、@sqliteai/sqlite-wasm 等)的 SQLite 客户端基类。
RxDBAdapterSqliteBase与后端无关的 SQLite adapter 基类。
RxDBAdapterSqliteErrorRxDB SQLite 适配器错误类
RxDBQueryCacheRowContractErrorQueryCache 拉取落地时,远端行不满足本地表列契约。
SqliteCoreKeyringStorage把密钥环单例行持久化到适配器自带的 SQLite 数据库。 wa-sqlite 与 sqliteai 两个继承类都会用到。
SqliteRepository操作实体仓库
SqliteRepositoryBase操作实体仓库
SqliteTransactionExecutorSQLite 侧的 TransactionExecutor 实现。
SqliteTreeRepository树形实体仓库

Interfaces​

InterfaceDescription
AdapterEncryptionFacade暴露在 adapter.encryption 上的开发者门面,内部转发给 Keyring。 如果数据库中没有实体声明加密列,所有方法都会抛 EncryptedConfigurationError(code: 'no_encrypted_columns')。
ChangeRecordEventSQLite update_hook 派发的单行变更事件载荷。 由各后端 SqliteClient 收集后批量分发给 RxDB 上层。
CreateSqliteClientOptions创建支持 Worker 的 SQLite 客户端的选项。
EncryptionContext加密上下文:密钥环、命名空间与实体元数据解析器。
ExistsSubqueryEXISTS 子查询的 where 编译结果
FtsFieldFTS5 字段描述符
FtsTriggerOptionsbuildFtsTriggersSql 的可选项。
GenerateSqlResultSQL 生成结果:完整的 SQL 语句与可选的参数绑定。
InsertSqlOptions生成插入 SQL 时的选项。
IRxDBAdapterDataChangeRxDB adapter 数据变更接口
JoinContextJOIN 规划过程中的共享上下文:别名表、关系别名与字段别名映射。
Oo1Capisqlite3.capi 的最小契约:这里只保留注册更新钩子所需的那一部分。
Oo1ClientEventsOo1ClientBase 派发的事件签名表,供 EventDispatcher 类型推断。
Oo1ClientLoadOptionsOo1ClientBase.init 接受的运行期选项。 子类可通过泛型扩展(如 WaSqliteLoadOptions extends Oo1ClientLoadOptions)。
Oo1Databasesqlite3.oo1.DB / OpfsDb 的最小契约:执行 SQL、注册函数与关闭连接。
Oo1LoadFingerprint决定「加载到哪个 WASM 模块」的配置指纹。
Oo1LoadOptionsoo1 WASM 加载选项,所有 sqlite/sqliteai 适配器共享。
Oo1PreparedStatementoo1 API 的结构化类型别名,被 @sqlite.org/sqlite-wasm 与 @sqliteai/sqlite-wasm 共用。
Oo1Staticsqlite-wasm 模块导出对象的最小契约:capi、oo1 命名空间与版本信息。
RelationPair关系路径上的一段:关系所属实体的 metadata 与关系本身
RuleGroupJoinOptionsJOIN 规划选项
SqliteBackendSQLite WASM 后端实现的抽象接口。
SqliteBackendOptions打开 SQLite 后端的选项
SqliteBackupInput备份一次需要的全部上下文。
SqliteBaseOptionsSQLite 适配器的基础选项(与后端无关)。
SqliteBlankDatabaseSqliteClientLike.describeBlankDatabase 的结果。
SqliteChangeErrorEvent变更处理失败事件
SqliteChangeEventSQLite 变更事件接口
SqliteClientLikeSQLite 适配器的最小客户端接口。 wa-sqlite 的 SqliteClient 与 sqliteai 的 SqliteaiClient 都满足该契约。
SqliteData单个结果集,包含列名与行数据
SqliteExecResult通过后端执行 SQL 的结果
SqliteRestoreInput恢复一次需要的全部上下文,由 adapter 提供。
SqliteRestoreOptionsSQLite 恢复选项。
SqliteRestoreOutcomerestoreSqliteDatabase 的结果。
SqliteStatement一条待执行的 SQL 语句及其绑定参数。
SqliteSuccessResult带耗时信息的 SQLite 查询结果
SwitchVersionSqlItem版本切换中某一类操作(删除/插入/更新)的 SQL 集合及其变更记录。
SwitchVersionSqlResult版本切换生成的完整 SQL 结果:按删除/插入/更新三类分组。

Type Aliases​

Type AliasDescription
OpfsFallback当 OPFS 后端数据库无法打开时的行为(例如浏览器不支持 Atomics.wait、 缺少 crossOriginIsolated 等)。
RowIdSQLite rowId 类型(bigint)
SqlExecutor能直发 SQL 的事务门面。
SqliteBackupStorageadapter 当前配置对应的存储后端,决定能否备份 / 恢复以及恢复走哪条路径。
SqliteChangeErrorListenerSqliteChangeErrorEvent 的监听器
SqliteChangeErrorPhase变更处理失败的阶段
SQLiteCompatibleTypeSQLite 参数绑定所兼容的数据类型
SqliteDataTypeSQLite 数据类型名
SqliteRestoreStage恢复进行到的阶段,按顺序各触发一次。
SqliteResultSqliteSuccessResult 的别名
SqliteSupportedBackupStorage已交付备份与恢复的存储后端。
TransactionFun事务回调。
UpdateHookCallbackSQLite update_hook 通知回调

Variables​

VariableDescription
BATCH_TIMEOUT变更事件批处理超时档位(毫秒)。 越短延迟越低但 CPU 唤醒越频繁;越长越省电但 UI 响应延后。 - IMMEDIATE (0): 同步派发,仅在测试场景使用 - FAST (4): 约一帧内合并 - BALANCED (16): 默认值,约一帧(60fps) - POWER_SAVE (50): 移动端 / 后台场景
DEFAULT_BATCH_TIMEOUT默认批处理超时(毫秒),约一帧 60fps。
DEFAULT_CACHE_SIZE_KB默认 SQLite 页缓存大小(KB),50 MB。
deserializeFromEnvelope按持久化时的属性类型把已认证明文字节还原为业务值。
envelopePlaintextPatches遍历明文补丁的顶层键;对于 entity.encryptedPropertyMap 中存在的键, 将值替换为 keyring.encrypt 生成的信封字符串。未加密的键直接复制。
FTS_BIGRAM_SQL_FUNCTION注册到 SQLite 的索引侧变换函数名,trigger 与 backfill 共用。
MAIN_TABLE_ALIAS表别名相关的叶子工具模块
MAX_BATCH_WAIT_MS批处理事件强制 flush 的硬上限(毫秒)。
normalizeCreateEntity规范化创建数据(过滤未赋值字段)。
normalizeUpdateEntity规范化更新数据并过滤 readonly 字段。
ROWIDsqlite 行 id 列名
serializeForEnvelope按属性类型把单个非空业务值编码为信封明文字节。
SQLITE_BACKUP_ENGINE归档里记录的引擎名。
SQLITE_BACKUP_ENGINE_COMPATIBILITY引擎数据兼容键。
SQLITE_BACKUP_LOCK_TIMEOUT_MSRxDBBackupOptions.lockTimeoutMs 的默认值。
SQLITE_MAX_BIND_VARIABLES-
unenvelopePlaintextPatchesenvelopePlaintextPatches 的逆操作:将顶层信封字符串解密回明文。 应用于 undo/redo 的 inversePatch 时使用。
WAL_AUTOCHECKPOINT_PAGESWAL 模式下自动 checkpoint 的页阈值。
WATCH_TABLES触发实体事件派发的系统表集合。 仅这三张表的 update_hook 事件会被上抛为 RxDBChange / RxDBBranch / RxDBMigration 事件。

Functions​

FunctionDescription
assertLoadOptionsTransferable断言即将跨 worker 传递的 load options 可被结构化克隆。
assertOo1Static断言第三方 sqlite-wasm 初始化结果满足共享 oo1 客户端的最小运行时契约。
assertQueryCacheRowContract落地前校验远端行的列集,不合契约就 fail-fast。
build_order_by构建排序 SQL
build_rule生成 rule sql 查询条件
build_rule_group_join计算查询需要的 JOIN 字符串
build_substring_condition生成子串类操作符的 SQL 条件
buildCreateFtsTableSql生成单个 collection 的 FTS5 外部内容虚拟表 DDL。
buildFtsTriggersSql生成 FTS5 同步 trigger 三件套(_ai / _ad / _au)。
buildOo1InitOptions从 Oo1LoadOptions 构造 Emscripten 模块初始化对象(喂给 sqlite3InitModule)。
buildRuleGroup生成 ruleGroup sql 查询条件
cleanupIncompleteSqliteRestore清理一次没做完的恢复(页面在恢复中途关闭、或恢复失败后清理本身也失败)。
compileCjkToken查询侧变换:把一个查询 token 编译成 FTS5 MATCH 片段。
convertSwitchResultToSql-
count_sql生成 count 查询
create_table_sql计算创建表的 sql
create_tables_sql生成多张创建表的 SQL
defaultPrintErr默认 printErr 实现:过滤掉已知噪音后转发到 console.error。
defaultWarn默认 warn 实现:过滤掉已知噪音后转发到 console.warn。
describeSqliteDatabase描述一个库的结构与全部行,供恢复判断目标是否「恰好等于新建空库」。
dispatch_switch_events发送 switch 操作对应的本地事件
execute_switch_actions执行 SwitchVersionSqlResult 中的 SQL 操作并发送事件
executeOo1Helper适用于任何 oo1.DB 风格运行时的 SQL 执行助手。
find_by_row_ids_sql生成 findByRowIds 查询
find_sql生成 find 查询
format_table_alias-
generate_entity_count_ancestors_sql查询祖先节点数量
generate_entity_count_descendants_sql查询子孙节点数量
generate_entity_delete_sql生成删除实体的 sql 语句
generate_entity_deletes_sql生成批量删除实体的 SQL 语句
generate_entity_find_ancestors_sql查询祖先节点
generate_entity_find_descendants_sql查询子孙节点
generate_entity_insert_sql生成创建实体的 sql 语句
generate_entity_inserts_sql生成批量创建实体的参数化 SQL
generate_sql生成 SQL 查询语句
generate_table_trigger_sql生成表的触发器
generate_tree_sql生成树查询
generateSwitchBranchSql生成切换分支的 SQL 语句(generateSwitchBranchStatements 拼成的一整段)。
generateSwitchBranchStatements生成切换分支要执行的 SQL 语句序列,按执行顺序返回。
get_cached_regexp带 LRU-ish 缓存的 RegExp 工厂,供 SQLite regexp / regexp_replace 自定义函数复用。
get_field_sql获取字段的 SQL 表示
get_init_sql生成数据库连接初始化 PRAGMA SQL。
get_or_create_relation_alias-
get_persistent_db_file_name标准化数据库名为持久化文件路径。
get_relation_key获取关系键
get_rule_value获取规则值的 SQL 表示
get_sql_operator获取SQL操作符
get_sql_value获取 SQL 值表示
get_sql_with_params将 SQL 模板和参数合并为完整 SQL
get_table_name获取表名
get_table_name_by_entity_type通过实体类型获取表名
get_table_name_by_metadata通过元数据获取表名
get_table_name_info解析表名信息
getEntityObjectFromResult从查询结果行创建实体对象
getRxDBChangeEventType把 SQLite 变更类型映射成 RxDB 变更事件字符串
getTableColumnIndexName获取表列索引名称
handle_array_in处理数组字段的 in/notIn 查询
handle_exists处理 EXISTS/NOT EXISTS 操作符
handle_flatmap_contains处理 keyValue 字段的 contains/notContains 查询
handle_rxdb_change处理 SQLite 变更事件,将 update_hook 触发的行变更转换为 RxDB 实体事件。
hasCjk判断字符串是否含有连续 CJK 文本。
indexTextForFts索引侧变换:把文本中的每段连续 CJK 替换为空格分隔的 unigram + bigram 序列。
isReadOnlyStatement判断语句是否为纯读(SELECT / EXPLAIN)。
isSameOo1LoadConfig两次加载是否指向同一个 WASM 模块。
isSqlResultEmpty检查 sqlite 结果是否为空
isTableExistedSql检查 sqlite 表是否存在
normalizeSingleStatementSql去除末尾分号与空白,便于后续判断是否为单语句。
process_relation_joins处理关系字段的 JOIN
quote_sql_identifier将标识符(表名、列名、触发器名等)转义为双引号引用的 SQL 标识符。
readCurrentBranchId读当前分支 id(activated 优先,否则 main),供重建触发器使用。
releaseComlinkProxy释放 Comlink 代理占用的 MessagePort。
remove_all_triggers_sql-
remove_entity_ids_from_cache从缓存中移除已删除实体(按 id),并将其状态标记为 removed
requiredQueryCacheColumns算出「远端行必须自带」的列:本地表上 NOT NULL 且建表时拿不到默认值的那些。
resolve_column_name将 JS 属性名解析为数据库列名 如果 entityMetadata 存在,优先使用 columnName 支持嵌套路径,如 keyValue.string
resolveLocateFile解析 Emscripten locateFile 钩子。
restoreSqliteDatabase把 writeSqliteBackup 产出的归档恢复进一个空的 SQLite 库。
rewriteOpfsProxyWorkerUrl把 sqlite3 内置的 OPFS proxy worker URL 重写到用户提供的路径。
rxdb_adapter_mutations-
rxDBColumnTypeToSqliteType将 RxDB 属性类型转换为 SQLite 数据类型
shouldIgnoreSqliteMessage判断 SQLite 输出是否属于已知可忽略噪音(如 OPFS 主线程降级提示)。
shouldUsePreparedStatementPath判断是否应当走 prepared statement 路径(而非批量 exec)。
sqliteStorageLockName保护同一份 SQLite 持久化库的锁名。
switch_branch切换当前活跃分支并应用所需的数据迁移。
switch_transaction_id切换分支版本
toOo1LoadFingerprint从加载选项算出 Oo1LoadFingerprint。
transaction_sqlite_result处理 SQLite 事务结果,返回实体数组
transformEntityValueSqliteToJs将 SQLite 实体值转换为 JS 对象值
transformEntityValueToSql将实体值转换为 SQLite 格式
transformValueJsToSqlite将 JS 类型值转换为 SQLite 兼容类型
transformValueSqliteToJs将 SQLite 类型值转换为 JS 类型
try_process_relation_flatmap处理关系上的 keyValue 字段
try_resolve_relation_path-
update_entity_from_sqlite_result更新实体缓存 只更新已有的实体,委托给 transaction_sqlite_result
update_sql-
validateSqliteNumericOption校验 SQLite 客户端的整数数值选项。
withGlobalOo1LoadLock将一次完整的 oo1 模块加载串行化到全局唯一通道。
withPatchedOpfsProxyWorker用 Proxy 临时替换 globalThis.Worker,把 OPFS proxy worker 的脚本 URL 重写到 opfsProxyPath。
withSqliteApiConfig临时设置 globalThis.sqlite3ApiConfig,执行 run 后恢复原值。
withTriggersDisabled在触发器停用的窗口内执行一段写入:删触发器 → 跑 body → 重建触发器。
wrapWithComlink提供 Worker/SharedWorker 选项时使用 Comlink 包装客户端,否则返回直接客户端。
wrapWithComlinkEndpoint为一次客户端连接创建独立的 Comlink 子端口。
writeSqliteBackup把已连接的 SQLite 库写成一份归档。