iakioの日記 RSSフィード

2008-11-20

エラーの後にロールバックしなければいけない理由

| 02:37 | エラーの後にロールバックしなければいけない理由 - iakioの日記 を含むブックマーク はてなブックマーク - エラーの後にロールバックしなければいけない理由 - iakioの日記

PostgreSQLでは、トランザクションブロック内でエラーが発生した後はロールバックするまでクエリを実行できなくなります。

=> begin;
BEGIN
=> select x;
ERROR:  column "x" does not exist
LINE 1: select x;
               ^
=> select 1;
ERROR:  current transaction is aborted, commands ignored until end of transaction block

「なんか面倒だなぁ」と思いながら何年間もPostgreSQLを使っていましたが、今まで理由なんて考えたこともありませんでした。

で、偶然理由を見つけました。src/backend/access/transam/README によると、

begin;
正常なSQL1;
エラーになるSQL2;
正常なSQL3;
commit;

のようなファイルをpsqlで実行した場合、エラーになった後に自動でrollbackしてしまうと正常のSQL3だけは実行されてしまうので、一度エラーになったら以降はロールバックするまで他のクエリを受け付けないそうです。

これは納得ですが、プログラムを書く上では気をつけなければいけませんね。

8.1以降のpsqlでは、\set ON_ERROR_ROLLBACK onとすると、エラーが発生した時に自動でロールバックします(これはバックエンドの機能ではありません)。これは、psqlが内部で、すべてのクエリの実行をSAVEPOINT/RELEASEで括ることで実現しています。冒頭のSQLを実行した場合はpsqlは、

LOG:  statement: begin;
LOG:  statement: SAVEPOINT pg_psql_temporary_savepoint
LOG:  statement: select x;
LOG:  statement: ROLLBACK TO pg_psql_temporary_savepoint
LOG:  statement: SAVEPOINT pg_psql_temporary_savepoint
LOG:  statement: select 1;
LOG:  statement: RELEASE pg_psql_temporary_savepoint

のようなSQLを実行します。

あと、最初の例で「ロールバックするまで」と書きましたが、実際には「rollback」コマンドではなく「commit」を行ってもロールバックされます。

=> select 1;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
=> commit;
ROLLBACK

L.starL.star2008/11/21 08:43commitするまでが1トランザクションですよー。ということですよね。