web-dev-qa-db-ja.com

PostgreSQLの機能はトランザクションですか?

次のようなPostgreSQL関数は自動的にトランザクション対応ですか?

CREATE OR REPLACE FUNCTION refresh_materialized_view(name)
  RETURNS integer AS
$BODY$
 DECLARE
     _table_name ALIAS FOR $1;
     _entry materialized_views%ROWTYPE;
     _result INT;
 BEGIN          

     EXECUTE 'TRUNCATE TABLE ' || _table_name;

     UPDATE materialized_views
     SET    last_refresh = CURRENT_TIMESTAMP
     WHERE  table_name = _table_name;

     RETURN 1;
END
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER;


つまり、関数の実行中にエラーが発生した場合、変更はロールバック?これがデフォルトの動作ではない場合、関数transactionalを作成するにはどうすればよいですか?

63
Dónal

PostgreSQL 12 updateトランザクション制御が可能なトップレベルのPROCEDUREsのサポートは制限されています 。通常のSQL呼び出し可能関数でトランザクションを管理することはまだできないので、新しいトップレベルプロシージャを使用する場合を除いて、以下は真のままです。


関数は、呼び出し元のトランザクションの一部です。トランザクションがロールバックされると、それらの効果はロールバックされます。トランザクションがコミットされると、彼らの作業はコミットされます。関数内のBEGIN ... EXCEPTブロックは、SAVEPOINTおよびROLLBACK TO SAVEPOINT SQLステートメントのようなセーブポイントのように(そして内部で使用するために)動作します。

関数は、BEGIN ... EXCEPTエラー処理を除いて、完全に成功するか完全に失敗します。関数内でエラーが発生し、処理されない場合、関数を呼び出しているトランザクションは中止されます。中止されたトランザクションはコミットできません。コミットしようとすると、COMMITROLLBACKとして扱われ、エラーの他のトランザクションと同じです。観察する:

regress=# BEGIN;
BEGIN
regress=# SELECT 1/0;
ERROR:  division by zero
regress=# COMMIT;
ROLLBACK

ゼロ除算によりエラー状態にあるトランザクションがCOMMIT?でどのようにロールバックするかを確認してください。

明示的なサラウンドトランザクションなしで関数を呼び出す場合、ルールは他のPgステートメントとまったく同じです。

BEGIN;
SELECT refresh_materialized_view(name);
COMMIT;

COMMITでエラーが発生した場合、SELECTは失敗します)。

PostgreSQLは(まだ)関数の自律型トランザクションをサポートしていません。この場合、プロシージャ/関数は呼び出し側トランザクションとは無関係にコミット/ロールバックできます。これは、 dblink を介して新しいセッションを使用してシミュレートできます。

[〜#〜] but [〜#〜]、PostgreSQLにはトランザクションではない、または不完全なトランザクションが存在します。通常のBEGIN; do stuff; COMMIT;ブロックで非トランザクション動作を行う場合、関数でも非トランザクション動作を行います。たとえば、nextvalおよびsetvalTRUNCATEなど。

75
Craig Ringer

私のPostgreSQLの知識はCraig Ringerの知識よりも浅いので、もっと短い答えをしようとします:はい。

エラーのある関数を実行した場合、データベースに影響を与えるステップはありません。

また、PgAdminでクエリを実行すると、同じことが起こります。

たとえば、クエリで実行する場合:

_update your_table yt set column1 = 10 where yt.id=20;

select anything_that_do_not_exists;
_

行の更新、_id = 20_の_your_table_はデータベースに保存されません。

2018年9月更新

概念を明確にするために、非トランザクション関数nextvalを使用した小さな例を作成しました。

まず、シーケンスを作成しましょう。

_create sequence test_sequence start 100;_

次に、実行してみましょう。

update your_table yt set column1 = 10 where yt.id=20; select nextval('test_sequence'); select anything_that_do_not_exists;

さて、別のクエリを開いて実行すると

select nextval('test_sequence');

更新がコミットされなかったにもかかわらず、最初の値(100)が後者のクエリで使用されたため(つまり、シーケンスがトランザクションではないため)101になります。

26
Ignacio

機能レベルでは、国境を越えていません。つまり、関数内の各ステートメントは、デフォルトのdb自動コミット値である単一のトランザクションに属します。自動コミットはデフォルトでtrueです。しかし、とにかく、を使用して関数を呼び出す必要があります

select schemaName.functionName()

上記のステートメント 'select schemaName.functionName()'は単一のトランザクションです。トランザクションT1に名前を付けましょう。したがって、関数内のすべてのステートメントはトランザクションT1に属します。このように、関数は単一のトランザクション内にあります。

5
Robin

https://www.postgresql.org/docs/current/static/plpgsql-structure.html

PL/pgSQLのステートメントをグループ化するためのBEGIN/ENDの使用と、トランザクション制御のための同様の名前のSQLコマンドを混同しないことが重要です。 PL/pgSQLのBEGIN/ENDはグループ化専用です。トランザクションを開始または終了しません。関数とトリガープロシージャは、外部クエリによって確立されたトランザクション内で常に実行されます。実行するコンテキストがないため、トランザクションを開始またはコミットできません。ただし、EXCEPTION句を含むブロックは、サブトランザクションを効果的に形成します。外部トランザクションに影響を与えることなくロールバックされます。詳細については、セクション39.6.6を参照してください。

4
Ivan Strelets