关于 sqlc-gen-moonbit-safety
这组回归防护脚本和测试模式面向使用 sqlc-gen-moonbit 与 Cloudflare D1 的项目,针对 BigInt 绑定挂起和 SQL 占位符混用问题,可用于代码生成检查和 CI。
主要功能
- 01为不安全的 Int64 绑定添加包装
- 02验证生成代码中的绑定包装
- 03拒绝混用 SQL 占位符
- 04提供回归测试模式
适用场景
- 01在 sqlc 代码生成后添加检查
- 02排查 Cloudflare D1 的 1101 错误
- 03在 CI 中运行数据库验证
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mizchi/ skills --skill sqlc-gen-moonbit-safety安装后在 Claude Code 中输入/sqlc-gen-moonbit-safety确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdassets/scripts/check-sql-placeholder-mix.tspatch-int64-binds.tsassets/tests/patch-int64-binds.test.tsreferences/d1-bind-hang.md详细介绍
sqlc-gen-moonbit 安全防护脚本
两个回归防护脚本,任何使用 sqlc-gen-moonbit + Cloudflare D1 的项目都应在每次代码生成时及 CI 中运行。它们都源于 mizchi/mnemo 中的真实生产环境故障,每个问题都花了一个下午才排查清楚。
何时调用
适用于以下情况:
- 初始化一个使用
sqlc-gen-moonbit连接 Cloudflare D1 的项目。 - 排查可能由 D1
.bind()挂起导致的 1101(Worker 抛出异常)。 - 为 sqlc 代码生成输出添加
pnpm run db:verify门禁(或等效门禁)。
包含内容
assets/scripts/patch-int64-binds.ts
在运行 sqlc generate 后执行的补丁,会将 src/db/gen/sqlc_queries.mbt 中所有 @core.any(params.<Int64-field>) 都包装为 int64_bind_safe(...)。该包装器会在 D1 .bind() 之前将 JS BigInt 强制转换为 Number。
为什么需要它:D1 的 .bind() 无法处理 BigInt。传入原始 BigInt 会导致 .run() / .all() 一直无法返回结果。约 30 秒后,Cloudflare 会终止 Worker,并报出 1101 Worker threw exception。默认情况下,sqlc-gen-moonbit 会在绑定边界将 Int64 字段生成为 BigInt。
两种模式:
--apply(默认):就地重写文件。将其接入db:generate,确保每次sqlc generate后都会运行。--verify:如果仍有未包装的 Int64 绑定位置,则以非零状态码退出。将其接入 CI /db:verify,以发现有人手动编辑生成文件或跳过补丁步骤导致的回归。
实现说明:
- 基于代码块解析(
source.split(/^(?=pub async fn )/m))。早期的单正则表达式实现使用\{[^}]+\}匹配函数体;当绑定列表包含(match params.x { Some(v) => @core.any(v); None => @core.null() })时,它会静默截断,导致内层}之后的所有绑定都被跳过。按代码块拆分可以避免此问题。 - 同时支持
Int64(直接使用@core.any(params.f))和Int64?(也会包装Some(v) => @core.any(v)分支,因为v仍然是 BigInt)。 - 兼容新建仓库:如果
src/db/gen/sqlc_queries.mbt尚不存在,则会干净地跳过(这样在首次运行db:generate前,db:verify不会造成阻塞)。
assets/scripts/check-sql-placeholder-mix.ts
拒绝 db/sqlite/query.sql 中同时混用匿名 ? 和 sqlc.arg(...) 的 -- name: 语句。
为什么需要它:sqlc-gen-moonbit 会将每个 sqlc.arg('name') 编译为固定的 ?N,并保留匿名 ? 供 SQLite 自动编号(“已用最大编号 + 1”)。两种风格混用时,末尾的匿名 ? 可能被分配到大于绑定数组长度的编号,进而导致 D1 报错,并以 1101 的形式暴露出来。
规则:每条语句只选用一种风格。对于 sqlc-gen-moonbit,建议全程使用 sqlc.arg(...)。
assets/tests/patch-int64-binds.test.ts
参考测试模式:创建包含合成 sqlc_types.mbt + sqlc_queries.mbt 的临时目录项目,使用 node:child_process 运行脚本,并断言绑定已被包装。其中包含针对嵌套大括号问题的回归用例,确保继续使用基于代码块的解析器。
建议的接入方式
// package.json
{
"scripts": {
"db:generate": "sqlc generate && node scripts/patch-int64-binds.ts",
"db:verify": "node scripts/patch-int64-binds.ts --verify && node scripts/check-sql-placeholder-mix.ts"
}
}
CI(最好也包括构建流水线)应运行 db:verify,以免重新生成导致的偏差进入发布版本。
参考资料
references/d1-bind-hang.md— 详细介绍 BigInt → 1101 的因果链,包括如何从Workers Logs会话中识别该问题。
来源
当前版本位于 mizchi/cloudflare-starterkit-mbt 的 scripts/ 目录下。该模板项目的 CI 会将两者作为 pnpm run db:verify 运行。mnemo(这些模式的来源)也会运行它们。