テクノロジー · 実用ガイド

SQLite RETURNING は1行取得してもコミットできない?全ての書き込みカーソルを完了する

ID の取得は文の完了とは限りません。書き込みを再実行したり結果を捨てたりする前に、1行・複数行・2つのカーソルを比較します。

RETURNING の取得方法8通りとコミット結果のイラスト
独自の実測イラスト。CPython3.12.14/SQLite3.53.1 の一時ファイル8ケースで、旧式トランザクション制御を明示しています。 Watch at Ease

INSERT ... RETURNING 後の COMMIT が「SQL statements in progress」で失敗したら、別プロセスのロックと決めつける前に、自分の未完了の書き込み結果を確認します。ID を1つ受け取ることと、文の完了は別の出来事です。

一時ファイル8ケースで fetchone が有効な条件と一般化できない理由を検証しました。カーソルを閉じると書き込みをコミットできても、必要な返却 ID を失う場合があります。

1. ドライバーとトランザクション管理側を記録

Python、sqlite3.sqlite_version、実際の SQL、全ての生きた結果カーソル、トランザクション設定を記録します。検証環境は Linux の CPython3.12.14/SQLite3.53.1 で、autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL と isolation_level="DEFERRED" を明示しました。標準ライブラリ sqlite3 の限定的な結果であり、APSW や ORM の結果ではありません。

例外の全文と失敗段階を残します。SQLite は未完了 SELECT があっても明示的 COMMIT を許しますが、未完了の書き込みはコミットを妨げ得ます。この接続の RETURNING を調べる前に、全ての SQLITE_BUSY を外部競合と分類したり、タイムアウトを増やしたりしないでください。モード変更を意図しないコミットの近道にもしないでください。

2. 1行の成功を一般化しない

各ケースで新しいファイルと t(x integer) テーブルを作りました。未取得の INSERT INTO t VALUES(1) RETURNING x はコミットを妨げ、このドライバーでは唯一の行を fetchone() で取得すると成功しました。一方、INSERT INTO t VALUES(1),(2),(3) RETURNING x から1行だけ取得してもコミットは失敗しました。

別の3行ケースでは fetchall()、または空のバッチまで繰り返す fetchmany(1) によりコミットでき、再度開くと3値とも残っていました。続行前に必要な全 ID を保存してください。SQLite は RETURNING の順序を保証しないため、順序を仮定して入力と対応付けてはいけません。バッチ取得は Python リストの大きさを抑えますが、エンジン内の既にバッファーされた出力は減らしません。

3. 必要な出力を失わず全書き込みカーソルを完了

6番目のケースでは未取得の3行カーソルを閉じました。コミットは成功し、再度開くと3つの挿入値がありましたが、アプリは RETURNING の行を1つも回収していません。Cursor.close() はロールバックでも、必要な ID の引き渡しでもありません。出力を放棄すると意図的に決めた場合だけ使います。

さらに2ケースで同じ接続に2つの INSERT ... RETURNING カーソルを残しました。最初だけ読み切ると2つ目が未完了でコミットに失敗し、両方を読み切ると成功して、再度開いたファイルに 1 と99 がありました。トランザクション管理側でカーソル参照を保持します。補助関数が1行を返す裏で別の書き込み結果を残さないようにしてください。

4. 返却行とコミットを別々に検証

コミットに失敗した全ケースで、直後の in_transaction は true でした。その後テストが未完了カーソルを閉じ、明示的にロールバックしたため、再度開いたファイルには挿入行がありませんでした。空の結果は意図的な後処理によるもので、例外が自動で書き込みを取り消した証拠ではありません。アクティブなトランザクションで INSERT を再実行すると追加の書き込みになり得ます。

必要な結果を取得・検証し、カーソルを閉じてから、決められた管理側でコミットします。新しい接続で永続化した値を確認し、正確な行数の回帰テストを残します。エラー時はアプリのロールバック方針に従い、ID が返っただけで成功と報告しないでください。同時リーダー、トリガー、大量出力、他の autocommit モードは未検証なので、別途診断します。

行の取得、カーソル終了、コミットの違いを示すイラスト
独自の診断イラスト。出力の引き渡し、文の完了、トランザクションのコミットを別々に検証。カーソル終了は挿入の取消ではありません。 Watch at Ease