mcpField
S
sqlc-gen-moonbit-safety

sqlc-gen-moonbit-safety

github.com/mizchi测试与质量41641 次安装1 周前更新
代码仓库

关于 sqlc-gen-moonbit-safety

这组回归防护脚本和测试模式面向使用 sqlc-gen-moonbit 与 Cloudflare D1 的项目,针对 BigInt 绑定挂起和 SQL 占位符混用问题,可用于代码生成检查和 CI。

主要功能

  1. 01为不安全的 Int64 绑定添加包装
  2. 02验证生成代码中的绑定包装
  3. 03拒绝混用 SQL 占位符
  4. 04提供回归测试模式

适用场景

  1. 01在 sqlc 代码生成后添加检查
  2. 02排查 Cloudflare D1 的 1101 错误
  3. 03在 CI 中运行数据库验证

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mizchi/skills --skill sqlc-gen-moonbit-safety

安装后在 Claude Code 中输入/sqlc-gen-moonbit-safety确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
assets/scripts/
check-sql-placeholder-mix.ts
patch-int64-binds.ts
assets/tests/
patch-int64-binds.test.ts
references/
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(这些模式的来源)也会运行它们。

评论

支持 Markdown