sqlc-gen-moonbit-safety について
sqlc-gen-moonbit と Cloudflare D1 を使うプロジェクト向けの回帰チェック用スクリプトとテストパターンです。BigInt のバインド停止や SQL プレースホルダーの混在を扱い、コード生成時や CI で検証できます。
主な機能
- 01安全でない Int64 バインドのラップ
- 02生成コードのバインドラップ検証
- 03SQL プレースホルダー混在の拒否
- 04回帰テストパターンの提供
活用シーン
- 01sqlc コード生成後のチェック追加
- 02Cloudflare D1 の 1101 エラー調査
- 03CI でのデータベース検証
インストール
コマンドをコピーしてターミナルで実行します。
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 で実行すべき、回帰を防ぐ 2 つのスクリプトです。どちらも、デバッグにそれぞれ半日かかった mizchi/mnemo の実際の本番バグをもとにしています。
実行するタイミング
次のような場合に使用します。
- Cloudflare D1 と
sqlc-gen-moonbitを使用するプロジェクトをセットアップするとき。 - 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 は 1101 Worker threw exception で Worker を終了させます。sqlc-gen-moonbit はデフォルトで、バインド境界における Int64 フィールドを BigInt として出力します。
2 つのモード:
--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 内の -- name: ステートメントで、匿名の ? と sqlc.arg(...) が混在しているものを拒否します。
必要な理由:sqlc-gen-moonbit は各 sqlc.arg('name') を固定の ?N にコンパイルし、匿名の ? は SQLite の自動採番(「使用済みの最大値 + 1」)に任せます。両方の形式が共存すると、末尾の匿名 ? に、バインド配列の長さを超える番号が割り当てられることがあります。その結果、D1 がエラーを出し、1101 として表面化します。
ルール:ステートメントごとに形式を 1 つ選びます。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 でも実行されています。