何が変わったか

sqlite-utils 4.1.1は、直前の4.1リリースに対する小さな修正版です。中心はtable.transform()の安全弁の追加でした。

具体的には、(1) トランザクションが開いた状態で、(2) PRAGMA foreign_keysが有効で、(3) 対象テーブルが破壊的なON DELETEアクション(CASCADESET NULLSET DEFAULT)を持つ外部キーから参照されている——この3条件が揃ったときに、table.transform()TransactionErrorを送出します。

あわせて、CLIドキュメントとPython APIドキュメントが相互リンクされました。CLIの各セクションから対応するPython APIの機能へ、Python APIの各セクションから対応するCLIコマンドへ飛べます(#791)。

なぜ「静かなデータ消失」だったのか

SQLiteのtable.transform()は、カラム変更などを「新テーブルを作ってデータを移し、古いテーブルを落とす」という手順で実現します。このとき古いテーブルのDROPが、参照している側のテーブルのON DELETE CASCADEを発火させ得ます。つまりスキーマ変更のつもりが、関連レコードの削除やNULL化を巻き込むわけです。

通常この危険はPRAGMA foreign_keysを一時的に切ることで回避しますが、このpragmaはトランザクションの内側では変更できません。だからこそ「トランザクション中にtransformする」経路だけが穴として残っていた。しかもエラーにならず静かに壊れるため、気づくのはずっと後になります。今回の変更は、その穴を「動くが壊れる」から「明示的に失敗する」に変えたものです。回避策はドキュメントのForeign keys and transactionsセクションにまとめられています。

発見の経緯が示すもの

注目すべきは、このエッジケースを見つけたのが人間のバグ報告ではなく、ON DELETEに関する質問に答えるために4.1を触っていた通常のClaudeチャットだった点です。仕様書を読んで机上で導いたのではなく、実際に動かして挙動の矛盾に行き当たった。ライブラリ作者が自作ツールをAIに触らせる過程が、そのままQAとして機能した形です。

💼 事業会社視点:これは自社にどう効くか

実務上の要点は「スキーマ移行は静かに失敗しうる」という一点です。ECや業務SaaSのように、注文と明細、顧客と契約といった親子テーブルをON DELETE CASCADEで結んでいるシステムでは、テーブル定義の変更が参照側データの削除を巻き込む構造的リスクがあります。SQLiteに限らず、RDBのスキーマ変更をトランザクションで包んで「安全にしたつもり」になっている現場は多く、今回のケースはその安心が条件次第で成立しないことを示しました。

受託開発やSIの立場では、納品後のマイグレーションスクリプトが最大の地雷です。テストDBは行数が少なく外部キーの参照も浅いため、CASCADEの巻き込みが表面化しません。本番だけで壊れます。事業責任者が今日打てる手は、マイグレーション実行前後の行数差分をアサートする仕組みを入れること、そしてON DELETE CASCADEを使っている箇所の棚卸しです。

もう一つ、経営視点で拾うべきは発見の経緯です。仕様を読むAIではなく、実際に動かして矛盾に気づくAIがQAとして機能した。自社プロダクトをAIに触らせる時間を、レビュー工程の一部として予算化する価値があります。

関連リンク