2011-06-08 7 views
18

うまくいけば簡単な質問ですが、まともな回答はすぐに見つかりませんでした。 PostgreSQL(特にバージョン9.0.4)のストアドプロシージャ(ユーザ定義のDB関数)は、それ自体がトランザクションであるSELECT文を介して呼び出されるため、本質的にトランザクショナルです。だから、どのようにストアドプロシージャの分離レベルを選択するのですか?他のDBMSでは、望ましいトランザクション・ブロックが、START TRANSACTIONブロックにラップされると考えられます。このブロックでは、望ましい分離レベルがオプションのパラメータです。postgresqlストアドプロシージャの隔離レベルを設定します

CREATE FUNCTION add_new_row(rowtext TEXT) 
RETURNS VOID AS 
$$ 
BEGIN 
     INSERT INTO data_table VALUES (rowtext); 
     UPDATE row_counts_table SET count=count+1; 
END; 
$$ 
LANGUAGE plpgsql 
SECURITY DEFINER; 

と私は確信してこの関数は常にシリアライザブルトランザクション(はい、はい、PostgreSQLのSERIALIZABLE ISNのように行われるようにしたいと想像:特定作らアップの例として、私はこれをしたい

を言います適切なシリアライズ可能ですが、それはポイントではありません)。

START TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
SELECT add_new_row('foo'); 
COMMIT; 

したがって、必要な分離レベルを関数にプッシュするにはどうすればよいですか?同じ名前の SQLとPL/pgSQLで文 をまとめるためのBEGIN/ENDの使用 を混同しないことが重要であるthe manual says

ように私は、私はちょうどBEGIN文の分離レベルを置くことができないと信じてトランザクション制御のコマンド。 PL/pgSQLのBEGIN/ENDは、グループ化が の場合のみです。 トランザクションを開始または終了しません。関数と 手順をトリガーは常に 内、外 クエリによって確立されたトランザクションを実行している - 。彼らはそのトランザクションを開始したり、 コミットすることはできません、彼らがで実行するための 何の関連もないので、

最も明白なこれは受け入れられるだろうが、私へのアプローチは、私が仕事にこれに頼ることができるよりも、それは明らかではない、関数定義のSET TRANSACTIONどこか、例えば,:

CREATE FUNCTION add_new_row(rowtext TEXT) 
RETURNS VOID AS 
$$ 
BEGIN 
     SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
     INSERT INTO data_table VALUES (rowtext); 
     UPDATE row_counts_table SET count=count+1; 
END; 
$$ 
LANGUAGE plpgsql 
SECURITY DEFINER; 

を使用することです。 SET TRANSACTIONためdocumentationは、SET TRANSACTIONが せずに前のSTART TRANSACTIONを実行またはBEGINされた場合、 が トランザクションはすぐに終了するので、効果がないように見えます

言います。 (Iが無効に自動コミットを持っていない提供)私は孤独SELECT add_new_row('foo');文を呼び出す場合、私は期待しているので、困惑私の葉

SELECTは、セッションのデフォルトの分離レベルでシングルライントランザクションとして実行することができます。

manualも書かれています:

トランザクション分離レベルが が最初の問い合わせや データ変更ステートメントの後に変更することはできません(FETCH、UPDATE、DELETE、 INSERTを選択するか、 COPY)のトランザクションは が実行されました。。関数は、下の分離レベルでトランザクション内から呼び出された場合例えば

は、だから何が起こる:ボーナスの質問については

START TRANSACTION ISOLATION LEVEL READ COMMITTED; 
UPDATE row_counts_table SET count=0; 
SELECT add_new_row('foo'); 
COMMIT; 

:関数の言語は任意の違いを作るのですか? PL/pgSQLでプレーンSQLとは違った分離レベルを設定するでしょうか?

私はスタンダードとベストプラクティスを文書化しているので、まともな参考文献があれば幸いです。

+1

関数内で 'SET TRANSACTION'を使用しようとしたときにはどうでしたか? –

+0

@a_horse_with_no_name:私が言及したように、私は 'SET TRANSACTION'が私の必要とするものであり、その機能は受け入れられますが、時にはそれほど多くのことを意味するものではありません(時にはいくつかのオプションが時々飲み込まれる)ので、ちょうどうまくいくようなものではなく、文書化されたアプローチです。 – beldaz

+1

Postgresはあなたの言うことをまったく飲み込むことはほとんどありません。 –

答えて

15

これはできません。

あなたの機能は、現在のトランザクション分離レベルが何であるかをチェックし、必要でない場合は中止します。 SELECT current_setting('transaction_isolation')を実行し、結果を確認することでこれを行うことができます。

+0

はい、私はむしろ、唯一のアプローチになると思っていました。私はあなたの "反 - 回答"は私が望むものに最も近いものだと思う;) – beldaz

1

機能の言語はまったく違いはありません。

これは失敗します。あなたの特定の例では、あなたが最初のテーブルにトリガーを使用してこれを行うことができることを

test=# create function test() returns int as $$ 
    set transaction isolation level serializable; 
    select 1; 
$$ language sql; 
CREATE FUNCTION 
test=# select test(); 
ERROR: SET TRANSACTION ISOLATION LEVEL must be called before any query 
CONTEXT: SQL function "test" statement 1 

注意を。デッドロックを避けるために行数の更新が行われていることを確認してください。繰り返し読み取りモードで正常に動作します。

私は、PL /言語はプラットフォーム固有のものの標準

のファンです。

+0

これをどこに設定すべきかを示すことで答えを拡大できますか?私が言及したように、この例は完全に構成されていたので、私はトリガーで別のアプローチをしていたと思うようにあなたを誤解することを意味しませんでした。 PL言語がプラットフォーム固有であるとしても、彼らはまだドキュメントを持つことができます。私はちょうど何か特定のものへの適切な答えのために浸透しにくいポストグルの文書を見つけるので、正確に見える場所の指示は他の読者には私には役に立ちます。 – beldaz

+0

関数の本文にインクルードするだけです。 'begin'を' begin isolation level serializable'で置き換えます(または、エラーが起きた場合は余分なbegin/endブロックを追加してください)。 –

+0

@Denis:あなたが提案するように 'begin'をpostgres v9と矛盾するように見せかけるマニュアル39.2 - " PL/pgSQLのBEGIN/ENDはグループ化のためのもので、トランザクションを開始または終了しません "。 – beldaz

0

トランザクション分離とは、アクセス可能な他のコンカレントトランザクションで行われた変更を意味します。

実行をシリアル化する場合は、ロックを使用する必要があります。

行トリガーおよび更新カウント後に使用できます。 "UPDATE row_counts_table"はテーブルをロックし、すべてのトランザクションはシリアル化されます。 遅いです。

この例では、2つのステートメントがあります。挿入が実行されますが、更新は他のトランザクションを待機しなければならず、この期間にカウントは無効です。

+0

10年前、おそらく。しかし、一般的な意味であれば、いいえ、シリアライズではロックを使用する必要はありません。元のSQL標準では、ロックが使用されると想定されていました。その理由は、分離レベルがロック固有の異常を受け入れる準備ができているためです。しかし、PostgresとSQL ServerによるMVCCのアプローチはロックとは根本的に異なりますが、書き込みスキューの問題がありますが、これを回避してシリアライズ可能な実行を提供する方法が考案されています(Feketeなど)。 – beldaz

0

PGでは、あなたの手続は別々の取引ではありません。つまり、ストアドプロシージャは既存のトランザクションに参加します。

BEGIN TRAN 

SELECT 1; 
SELECT my_proc(99); 

ROLLBACK TRAN; 

これで、ストアドプロシージャの外にあるトランザクションが開始されるトランザクションレベルを設定する必要があります。

1つのオプションは、主に使用する分離で実行するようにサーバーを構成し、サーバーの設定と異なるエッジの場合にSETを実行することです。

+0

ああ、私のためにストアドプロシージャの利点を破って、私はいくつかの操作を一緒にサーバー側のユニットで特定のトランザクション分離レベル。他の取引が残高譲渡を行っている場合は、断続的な当座預金口座の値を使用したくない利息付勘定を貸方記入してください。このためにクライアント側のトランザクションに頼らなければならないのは残念であり、セッションのデフォルトのトランザクションレベルが正しいことを望んでいるのは、まさに馬鹿ではないということです。 – beldaz

関連する問題