SQLite 扩展库
SQLite 扩展库
功能
examples/extensions/sqlite 是 BT 官方 SQLite 文件数据库扩展库示例。它通过 shared WASM 扩展在 worker 内保留 SQLite 连接,支持链式 SQL 构建、参数绑定、事务、并发读、busy timeout、结果上限和对象释放。
该扩展不是内置标准库。使用前需要把扩展编译成 module.wasm,再打包为 .bts 并安装到项目 extensions/ 目录。官方扩展库安装可直接使用:
bt install sqlite
语法
db = sqlite_open(path, options) row = db.query(sql).bind(value).one() rows = db.query(sql).bind(value).all() ret = db.query(sql).bind(value).exec() ret = db.query(sql).bind(prefix).binds(rows).batch(size).workers(count).exec() changed = db.transaction(statements) db.close()
sqlite_open
打开 SQLite 数据库文件,并返回 Sqlite 连接对象。
语法
db = sqlite_open(path, options)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | String | 是 | 无 | SQLite 数据库文件路径。bindings 使用 path_write role,路径必须位于项目根目录内。 |
options | Object | 是 | {} | 连接配置对象。 |
options 字段
| 字段 | 类型 | 必填 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|---|
wal | Bool | 否 | false | true 或 false | 是否启用 SQLite WAL 日志模式。true 会执行 PRAGMA journal_mode = WAL,读写并发体验通常更好,但数据库旁边会出现 -wal、-shm 辅助文件。单脚本临时数据库可以不启用;Web 服务、桌面应用、长期运行项目建议启用。 |
busy_timeout_ms | Int | 否 | 1000 | 0..=300000 | 数据库被其他连接锁住时,最多等待多少毫秒再报错。1000 表示最多等 1 秒;0 表示不等待。它只处理 SQLite 文件锁等待,不是 SQL 查询执行超时。 |
max_rows | Int | 否 | 1000 | 1..=100000 | all() 单次最多允许返回多少行。超过后会报错,防止一次把大量数据读进 BT VM。只想取一页数据时,建议 SQL 自己加 LIMIT。 |
max_result_bytes | Int | 否 | 4194304 | 1..=16777216 | one() 和 all() 返回结果的字节估算上限。默认约 4MB,最大 16MB。它用来保护常驻进程内存,避免大文本或大 BLOB 一次性返回过多。 |
新手可以先传 {} 使用默认值;当你知道结果集可能很大、会有并发读写或想限制内存占用时,再按需配置这些字段。
返回值
| 类型 | 说明 |
|---|---|
Sqlite | SQLite 连接对象,type(db) 返回 Sqlite。 |
Sqlite.query
创建链式查询对象。query() 只保存 SQL,不执行数据库操作。
语法
query = db.query(sql)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
sql | String | 是 | 无 | 待执行 SQL,使用 ? 作为参数占位符。 |
返回值
| 类型 | 说明 |
|---|---|
SqliteQuery | 链式查询对象,type(query) 返回 SqliteQuery。 |
SqliteQuery.bind
追加一个普通绑定参数。绑定值会按调用顺序对应 SQL 中的 ? 占位符。
语法
query = db.query(sql).bind(value)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
value | Any | 是 | 无 | 绑定值。支持 null、Bool、Int、Float、String、Bytes;不允许 empty。 |
返回值
| 类型 | 说明 |
|---|---|
SqliteQuery | 返回同一个查询对象,便于继续链式调用。 |
SqliteQuery.binds
追加多行批量绑定参数,只用于 exec()。
语法
query = db.query(sql).bind(prefix).binds(rows)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
rows | Array | 是 | 无 | 二维数组。每一行是一组绑定值;如果行元素不是数组,会按单值行处理。 |
返回值
| 类型 | 说明 |
|---|---|
SqliteQuery | 返回同一个查询对象。 |
SqliteQuery.batch
设置批量 exec() 的批大小统计值。它主要用于和 MySQL 标准库的批量写入写法保持一致。
SQLite 当前会在同一个事务里串行执行这些绑定行;batch(size) 会影响返回对象里的 batch_count 和 batch_size,方便迁移代码和观察批量规模。
语法
query = db.query(sql).binds(rows).batch(size)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
size | Int | 是 | 未调用时为 0 | 批大小。小于 0 按 0 处理;0 表示使用全部绑定行作为一批。 |
返回值
| 类型 | 说明 |
|---|---|
SqliteQuery | 返回同一个查询对象。 |
SqliteQuery.workers
设置迁移兼容的工作数统计值。这个方法是为了让从 MySQL 标准库迁移过来的代码可以保留相近写法。
SQLite 的同一个连接不能像 MySQL 连接池那样并发写入;当前实现仍会在一个事务内串行执行。也就是说,workers(4) 不会让同一个 SQLite 连接同时并发写 4 条 SQL。
语法
query = db.query(sql).binds(rows).workers(count)
参数
| 参数 | 类型 | 必填 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|---|
count | Int | 是 | 未调用时为 1 | 1..=4096 | 迁移兼容工作数。小于 1 会按 1 处理,大于 4096 会按 4096 处理。 |
返回值
| 类型 | 说明 |
|---|---|
SqliteQuery | 返回同一个查询对象。 |
SqliteQuery.one
执行查询并返回第一行。
语法
row = db.query(sql).bind(value).one()
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| Object/empty | 查询到行时返回对象;无结果返回 empty。SQLite NULL 返回 BT null,BLOB 返回 BT Bytes。 |
SqliteQuery.all
执行查询并返回多行。
语法
rows = db.query(sql).bind(value).all()
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| Array | 返回行对象数组,受 max_rows 和 max_result_bytes 限制。 |
SqliteQuery.exec
执行不需要返回结果集的 SQL。普通 bind() 执行一次;binds() 会在 SQLite 事务中按绑定行串行执行。
语法
ret = db.query(sql).bind(value).exec() ret = db.query(sql).bind(prefix).binds(rows).batch(size).workers(count).exec()
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| Object | 返回 SQL 执行统计对象。 |
执行结果字段
| 字段 | 类型 | 必定存在 | 说明 |
|---|---|---|---|
total | Int | 是 | 本次执行处理的绑定行数。普通 bind() 执行时为 1;binds() 批量执行时为绑定数组行数;空绑定行时为 0。 |
rows_affected | Int | 是 | SQLite 报告的受影响行数。批量执行时为逐行累加值。 |
last_insert_id | Int | 是 | SQLite 当前连接的 last_insert_rowid()。 |
batch_count | Int | 是 | 按 batch() 计算出的批次数。SQLite 当前执行仍在一个事务内完成。 |
batch_size | Int | 是 | 当前查询对象配置的批大小;未调用 batch() 时为 0。 |
workers | Int | 是 | 当前查询对象配置并规范化后的工作数。SQLite 当前不并发写同一个连接。 |
SqliteQuery.sql
返回当前 SQL 的调试文本。
语法
text = db.query(sql).bind(value).sql()
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| String | 返回把绑定值渲染到 ? 占位符后的 SQL 预览文本。该文本只用于调试,不参与执行。 |
Sqlite.transaction
在同一个 SQLite 事务中串行执行多条写语句。
语法
changed = db.transaction(statements)
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
statements | Array | 是 | 无 | 事务语句数组。元素可以是 SQL 字符串,也可以是 { sql, binds } 对象。 |
statements 对象字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
sql | String | 是 | 无 | 待执行 SQL。 |
binds | Array | 否 | [] | SQL 参数数组。支持 null、Bool、Int、Float、String、Bytes;不允许 empty。 |
params | Array | 否 | [] | 旧字段别名,建议新代码使用 binds。 |
返回值
| 类型 | 说明 |
|---|---|
| Int | 事务内累计影响行数。 |
close
释放查询对象或数据库连接对象。
语法
query.close() db.close()
参数
无参数。
返回值
| 类型 | 说明 |
|---|---|
| Bool | 成功释放返回 true;释放后旧句柄失效。 |
代码示例
fs('@/data').create_dir() db = sqlite_open('@/data/sqlite-demo.db', { wal: true, busy_timeout_ms: 1000, max_rows: 100, max_result_bytes: 1048576 }) // 输出:Sqlite print type(db) db.query('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, payload BLOB, note TEXT)').exec() db.query('DELETE FROM users').exec() ret = db.query('INSERT INTO users (name, payload, note) VALUES (?, ?, ?)') .bind('Alice') .bind(bytes('4254', 'hex')) .bind(null) .exec() // 输出:1 print ret.rows_affected db.query('INSERT INTO users (name, payload, note) VALUES (?, ?, ?)') .binds([ ['Bob', bytes('0102', 'hex'), 'writer'], ['Carol', bytes('0304', 'hex'), 'writer'] ]) .batch(2) .workers(1) .exec() row = db.query('SELECT name, payload, note FROM users WHERE name = ?').bind('Alice').one() // 输出:Alice print row.name // 输出:true print is_null(row.note) missing = db.query('SELECT name FROM users WHERE name = ?').bind('Missing').one() // 输出:true print is_empty(missing) rows = db.query('SELECT id, name FROM users ORDER BY id').all() // 输出:3 print rows.len() changed = db.transaction([ { sql: 'UPDATE users SET note = ? WHERE name = ?', binds: ['reader', 'Bob'] }, { sql: 'UPDATE users SET note = ? WHERE name = ?', binds: ['reader', 'Carol'] } ]) // 输出:2 print changed db.close()
构建
rustup target add wasm32-wasip1 cargo build --manifest-path examples/extensions/sqlite/Cargo.toml --target wasm32-wasip1 --release copy examples\extensions\sqlite\target\wasm32-wasip1\release\sqlite.wasm examples\extensions\sqlite\module.wasm cargo run -- ext build examples/extensions/sqlite -o examples/extensions/sqlite/sqlite.bts
rusqlite 的 bundled SQLite 在 WASI 目标下需要 C 编译器。Windows 环境需要先安装 LLVM clang 或 WASI SDK,并确保 clang 在 PATH 中。
注意事项
- SQLite 扩展的主用法和 MySQL 标准库保持一致:先
query(sql),再bind()或binds(),最后调用one()、all()或exec()。 - 扩展方法参数数量由
bindings.json严格校验;bind()每次只绑定一个值,需要多个参数时连续调用多次。 -
binds()只支持exec(),不支持one()和all()。 -
workers()为 MySQL 迁移兼容接口;SQLite 当前不会并发写同一个连接。 -
all()必须设置合理的max_rows和max_result_bytes,避免大结果无控进入 BT VM。 -
close()/SqliteQuery.close()应显式调用;bindings 已标记lifecycle: "dispose",调用成功后旧句柄失效。 -
busy_timeout_ms只处理 SQLite 锁等待,不是 SQL 逻辑超时;shared worker 的call_timeout_ms仍由宿主 ExtensionService 负责。