PL/pgSQL文で使用される式は全て、サーバの通常のSQLエクゼキュータを使用して処理されます。 実際には、SPIマネージャを使用して、以下の問い合わせが実行されます。
SELECT expression
評価の前に、式の中のPL/pgSQL変数識別子はパラメータによって置換され、変数の実際の値はエクゼキュータのパラメータ配列に渡されます。 これにより、SELECTの問い合わせ計画は一度だけ準備することができ、その後の評価で再利用されます。
PostgreSQLのメインパーサによって行われるこの評価には、定数値の解釈に多少の副作用があります。 詳しくは、以下の2つの関数の結果に違いが現れます。
CREATE FUNCTION logfunc1(logtxt text) RETURNS timestamp AS $$ BEGIN INSERT INTO logtable VALUES (logtxt, 'now'); RETURN 'now'; END; $$ LANGUAGE plpgsql;
および
CREATE FUNCTION logfunc2(logtxt text) RETURNS timestamp AS $$ DECLARE curtime timestamp; BEGIN curtime := 'now'; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; $$ LANGUAGE plpgsql;
logfunc1
の場合では、PostgreSQLメインパーサは、INSERT用の計画を準備する時に、logtable
の対象列の型から'now'をtimestampと解釈しなければならないことを把握しています。
こうして、パーサはINSERTが計画された時点で'now'を定数に変換し、その定数値をその後のセッションの有効期間におけるlogfunc1
の全ての呼び出しで使用します。
言うまでもありませんが、これはプログラマが意図した動作ではありません。
logfunc2
の場合では、PostgreSQLメインパーサは'now'の型を決定することができません。
そのため、nowという文字列を持つtext型のデータ値を返します。
curtimeローカル変数に代入する時に、PL/pgSQLインタプリタはこの文字列をtext_out
とtimestamp_in
関数を変換に使用してtimestamp型にキャストします。
ですから、演算されたタイムスタンプは、プログラマが意図した通り、実行の度に更新されます。
レコード変数の変わりやすいという性質はこの接続において問題となります。 レコード変数のフィールドが式や文の中で使用される場合、そのフィールドのデータ型を同じ式を呼び出す間で変更してはいけません。 その式が最初に実行された時のデータ型を使用して、その式の計画が作成されているからです。 複数のテーブル用のイベントを扱うトリガプロシージャを作成する時に、これに注意してください (必要な場合EXECUTEを使用してこの問題を回避することができます)。