web-dev-qa-db-ja.com

Postgresは例外後にコミットまたはロールバックを必要とします

ここで考えを理解しようとしています...

この バグ は、他のデータベースでは、SQLExceptionの発生後に追加のコマンドを実行できることを説明しています。 2001年に書かれましたが、触れられていません。現在、PostgresとSQL Serverを使用しているため、この問題にぶつかります。

あなたが持っていると仮定します:

start transaction;
create table test(id int primary key);
insert into test values (1);
commit;

-- Following statement throws a SQLException(duplicate key) in
-- PG, SS and Oracle
insert into test values (1);

-- Following statement behaves differently for different DBMS:
-- SS and OR: No error...statement runs fine
-- PG: Another SQLException thrown...must rollback or commit
insert into test values (99);

Postgresがこれを認めない理由を誰かが私に理解するのを手伝ってくれる?またはさらに良いことに、動作を変更するための接続フラグはありますか? (私は見て、何も見つけることができませんでした。)

上記の「トランザクション開始」ビットを追加して、自動コミット送信ではなかったことを示しました。

これはJavaアプリなので、PG 9.0 JDBCドライバーを使用しています。同じドライバーを使用してクエリツールで上記をテストし、アプリケーションを図から外しました。2番目上記のステートメントを挿入すると、次の結果が得られます。

エラー:現在のトランザクションは中止され、コマンドはトランザクションブロックの終わりまで無視されました
[SQL状態:25P02]

3
DaveyBob

あなたの例で私が持っている問題は、JDBCの動作について話しているだけでなく、明示的な「トランザクションの開始」などのコマンドも使用していることです。これは、ちょっとした衝突のようです。JDBCの自動コミットモードを使用すると思います。トランザクションを管理します。

自動コミットモードの場合、2つの挿入はそれぞれ独自のトランザクション内にあり、最初の挿入に対するSQLExceptionのスローは2番目の挿入には影響しません。

自動コミットモードではない場合、最初の挿入の前に暗黙の「開始トランザクション」が生成され、トランザクションがロールバックされるまで2番目の挿入を処理できません。この動作は、psqlでスクリプトを実行した場合とはかなり異なります。

(JDBCは、ドライバー/接続がデフォルトで自動コミットをオンにするかオフにするかを指定しません。常に明示的に設定する必要があります)

Postgresqlは、ステートメントを処理するすべてのエラーを、トランザクションを即座に中止するものとして扱います-基本的にSQL ServerのXACT_ABORTモードと同様です。コマンドのシーケンスをトランザクションとして送信すると、それぞれが前のコマンドに依存するため、いずれかのエラーが発生すると、後続のコマンドはすべて無効になります。

これがトランザクション内で必要な動作でない場合は、異常終了する可能性のある更新をセーブポイントの作成で囲み、エラーが発生した場合はそのセーブポイントにロールバックする必要があります。

Postgresqlの歴史のある時点で、autocommitというセッション変数があり、動作がまったく異なる可能性があるため、動作に関する非常に古い議論(10歳以上のバグは間違いなくカウントされます)に注意してください。この変数はなくなり、データベースの概念またはJDBCドライバがトランザクション内で自動的にコマンドをラップするように(私が理解しているように)置き換えられました(実際、postgresqlとの非トランザクションの相互作用などは実際にはありません)。

Psqlで提案したスクリプトを実行すると、次のようになります。

steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "test_pkey" for table "test"
CREATE TABLE
steve@steve@[local] *=# insert into test values (1);
INSERT 0 1
steve@steve@[local] *=# commit;
COMMIT
steve@steve@[local] =# 
steve@steve@[local] =# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] =# -- PG, SS and Oracle
steve@steve@[local] =# insert into test values (1);
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.
steve@steve@[local] =# 
steve@steve@[local] =# -- Following statement behaves differently for different DBMS:
steve@steve@[local] =# -- SS and OR: No error...statement runs fine
steve@steve@[local] =# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] =# insert into test values (99);
INSERT 0 1

スクリプトで記述したのと同じ動作を得るには、挿入を実行する前に自動コミットをオフにする必要があります。これにより、JDBCドライバーが暗黙の「開始トランザクション」を発行しないようにしてから、次のステートメントを実行します。暗黙的に生成されたトランザクションをpsqlスクリプトに入れると、記述したエラーが発生します。

steve@steve@[local] =# start transaction; -- generated by JDBC driver
START TRANSACTION
steve@steve@[local] *=# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] *=# -- PG, SS and Oracle
steve@steve@[local] *=# insert into test values (1);
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.
steve@steve@[local] !=# 
steve@steve@[local] !=# -- Following statement behaves differently for different DBMS:
steve@steve@[local] !=# -- SS and OR: No error...statement runs fine
steve@steve@[local] !=# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] !=# insert into test values (99);
ERROR:  current transaction is aborted, commands ignored until end of transaction block

この動作が存在する理由の例として、最初のトランザクションを再度実行するとどうなるかを考えます。その意図は、「テーブルを作成し、単一の行を設定する」ことです。

steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
ERROR:  relation "test" already exists
steve@steve@[local] !=# insert into test values (1);
ERROR:  current transaction is aborted, commands ignored until end of transaction block
steve@steve@[local] !=# commit;
ROLLBACK

したがって、問題が検出されると(「テスト」がすでに存在する)、残りのデータ操作は適切ではありません(とにかく、行もすでに存在しています)。

6
araqnid