バージョンごとのドキュメント一覧

43.11. PL/pgSQLの秘訣 #

<title><application>PL/pgSQL</application> under the Hood</title>

This section discusses some implementation details that are frequently important for <application>PL/pgSQL</application> users to know. 本節では、PL/pgSQL利用者の知識として重要な、実装の詳細を述べます。

43.11.1. 変数置換 #

<title>Variable Substitution</title>

SQL statements and expressions within a <application>PL/pgSQL</application> function can refer to variables and parameters of the function. Behind the scenes, <application>PL/pgSQL</application> substitutes query parameters for such references. Query parameters will only be substituted in places where they are syntactically permissible. As an extreme case, consider this example of poor programming style: PL/pgSQL関数内のSQL文および式は変数および関数のパラメータを参照することができます。 背後では、PL/pgSQLはこうした参照を問い合わせパラメータに置き換えます。 文法的に許されているところでのみ問い合わせパラメータは置換されます。 極端な場合として、以下のよろしくないプログラミングスタイルの例を考えてみましょう。

INSERT INTO foo (foo) VALUES (foo(foo));

The first occurrence of <literal>foo</literal> must syntactically be a table name, so it will not be substituted, even if the function has a variable named <literal>foo</literal>. The second occurrence must be the name of a column of that table, so it will not be substituted either. Likewise the third occurrence must be a function name, so it also will not be substituted for. Only the last occurrence is a candidate to be a reference to a variable of the <application>PL/pgSQL</application> function. 最初に現れるfooの場所は文法的にはテーブル名でなければなりません。 このため関数がfooという名前の変数を持っていたとしても、置換されません。 2番目の場所はそのテーブルの列名でなければなりません。このためこれも置換されません。 同様に、3番目の場所は関数名でなければなりません。このためこれも置換されません。 最後の場所のみがPL/pgSQLの関数の変数参照の候補です。

Another way to understand this is that variable substitution can only insert data values into an SQL command; it cannot dynamically change which database objects are referenced by the command. (If you want to do that, you must build a command string dynamically, as explained in <xref linkend="plpgsql-statements-executing-dyn"/>.) これを理解する別の方法は、変数の置換はSQLコマンドへデータ値を挿入できるだけだということです。コマンドが参照するデータベースオブジェクトを動的には変更できません。 (そのようにしたければ、43.5.4に書かれているように、コマンド文字列を動的に構成しなければなりません。)

Since the names of variables are syntactically no different from the names of table columns, there can be ambiguity in statements that also refer to tables: is a given name meant to refer to a table column, or a variable? Let's change the previous example to 変数名は文法的にはテーブル列名と違いがありませんので、テーブルを参照する文の中であいまいさが出る可能性があります。 与えられた名前はテーブル列を意味するのでしょうか、それとも変数なのでしょうか。 前の例を次のように変えてみましょう。

INSERT INTO dest (col) SELECT foo + bar FROM src;

Here, <literal>dest</literal> and <literal>src</literal> must be table names, and <literal>col</literal> must be a column of <literal>dest</literal>, but <literal>foo</literal> and <literal>bar</literal> might reasonably be either variables of the function or columns of <literal>src</literal>. ここでは、destおよびsrcはテーブル名でなければなりません。 また、coldestの列でなければなりませんが、fooおよびbarは理論上関数の変数かもしれませんし、srcの列かもしれません。

By default, <application>PL/pgSQL</application> will report an error if a name in an SQL statement could refer to either a variable or a table column. You can fix such a problem by renaming the variable or column, or by qualifying the ambiguous reference, or by telling <application>PL/pgSQL</application> which interpretation to prefer. デフォルトでPL/pgSQLはSQL文における名前が変数かテーブル列のいずれかを参照可能な場合にエラーを報告します。 変数または列の名前を変更することやあいまいな参照を修飾すること、PL/pgSQLにどちらを優先して解釈するかを通知することで、こうした問題を解消することができます。

The simplest solution is to rename the variable or column. A common coding rule is to use a different naming convention for <application>PL/pgSQL</application> variables than you use for column names. For example, if you consistently name function variables <literal>v_<replaceable>something</replaceable></literal> while none of your column names start with <literal>v_</literal>, no conflicts will occur. 最も簡単な解法は変数名または列名を変更することです。 一般的なコーディング法として、列の命名とPL/pgSQL変数の命名とで規約を分ける方法があります。 例えば、一貫して関数の変数はv_somethingという名前とし、列名はv_で始まらないようにすれば、競合は起こりません。

Alternatively you can qualify ambiguous references to make them clear. In the above example, <literal>src.foo</literal> would be an unambiguous reference to the table column. To create an unambiguous reference to a variable, declare it in a labeled block and use the block's label (see <xref linkend="plpgsql-structure"/>). For example, その他、あいまいな参照を明確にするために修飾することができます。 上の例では、src.fooによりテーブル列への参照についてあいまいさが解消します。 あいまい性のない変数参照を行うためには、ラベル付けしたブロック内で変数を宣言し、そのブロックのラベルを使用します(43.2参照)。 以下に例を示します。

<<block>>
DECLARE
    foo int;
BEGIN
    foo := ...;
    INSERT INTO dest (col) SELECT block.foo + bar FROM src;

Here <literal>block.foo</literal> means the variable even if there is a column <literal>foo</literal> in <literal>src</literal>. Function parameters, as well as special variables such as <literal>FOUND</literal>, can be qualified by the function's name, because they are implicitly declared in an outer block labeled with the function's name. ここでblock.foosrcfoo列があったとしても、変数を意味することになります。 FOUNDなどの特別な変数を含め、関数パラメータを関数名で修飾することができます。 これらは暗黙的に関数名をラベル名とした上位ブロック内で宣言されているためです。

Sometimes it is impractical to fix all the ambiguous references in a large body of <application>PL/pgSQL</application> code. In such cases you can specify that <application>PL/pgSQL</application> should resolve ambiguous references as the variable (which is compatible with <application>PL/pgSQL</application>'s behavior before <productname>PostgreSQL</productname> 9.0), or as the table column (which is compatible with some other systems such as <productname>Oracle</productname>). PL/pgSQLの大規模な本体コードにおける、すべてのあいまいな参照を修正することが現実的ではない場合があります。 こうした場合、PL/pgSQLにあいまいな参照を変数として解決すべき(この動作はPostgreSQL 9.0より前のPL/pgSQLの動作と互換性を持ちます)、または、テーブル列参照として解決すべき(Oracleなどの他のシステムと互換性を持ちます)と指定することができます。

To change this behavior on a system-wide basis, set the configuration parameter <literal>plpgsql.variable_conflict</literal> to one of <literal>error</literal>, <literal>use_variable</literal>, or <literal>use_column</literal> (where <literal>error</literal> is the factory default). This parameter affects subsequent compilations of statements in <application>PL/pgSQL</application> functions, but not statements already compiled in the current session. Because changing this setting can cause unexpected changes in the behavior of <application>PL/pgSQL</application> functions, it can only be changed by a superuser. システム全体に対してこの動作を変更するためにはplpgsql.variable_conflict設定パラメータをerroruse_variableuse_columnのいずれかに設定します(errorが標準配布におけるデフォルトです)。 このパラメータは以降のPL/pgSQL関数の文のコンパイルに影響しますが、現在のセッションでコンパイル済みの文には影響を与えません。 この設定を変更することで、PL/pgSQLの動作において予期できない変化が発生することがありますので、これはスーパーユーザのみが変更することができます。

You can also set the behavior on a function-by-function basis, by inserting one of these special commands at the start of the function text: また、関数テキストの先頭に以下の特殊なコマンドの1つをいれることで、関数単位で動作を設定することもできます。

#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column

These commands affect only the function they are written in, and override the setting of <literal>plpgsql.variable_conflict</literal>. An example is これらのコマンドを記述した関数に対してのみ、コマンドは影響を与え、plpgsql.variable_conflictの設定を上書きします。 以下に例を示します。

CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
    #variable_conflict use_variable
    DECLARE
        curtime timestamp := now();
    BEGIN
        UPDATE users SET last_modified = curtime, comment = comment
          WHERE users.id = id;
    END;
$$ LANGUAGE plpgsql;

In the <literal>UPDATE</literal> command, <literal>curtime</literal>, <literal>comment</literal>, and <literal>id</literal> will refer to the function's variable and parameters whether or not <literal>users</literal> has columns of those names. Notice that we had to qualify the reference to <literal>users.id</literal> in the <literal>WHERE</literal> clause to make it refer to the table column. But we did not have to qualify the reference to <literal>comment</literal> as a target in the <literal>UPDATE</literal> list, because syntactically that must be a column of <literal>users</literal>. We could write the same function without depending on the <literal>variable_conflict</literal> setting in this way: UPDATEコマンドにおいて、curtimecommentおよびidは、usersに同名の列があるか否かに関わらず、関数の変数またはパラメータを参照します。 テーブル列を参照させるためにWHERE句においてusers.idと参照を修飾する必要があったことに注意して下さい。 しかしUPDATEリストの対象としてのcommentへの参照は修飾させる必要がありませんでした。 これは文法的にusersの列でなければならないためです。 以下のようにvariable_conflictの設定に依存せずに同じ関数を作成することもできます。

CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
    <<fn>>
    DECLARE
        curtime timestamp := now();
    BEGIN
        UPDATE users SET last_modified = fn.curtime, comment = stamp_user.comment
          WHERE users.id = stamp_user.id;
    END;
$$ LANGUAGE plpgsql;

Variable substitution does not happen in a command string given to <command>EXECUTE</command> or one of its variants. If you need to insert a varying value into such a command, do so as part of constructing the string value, or use <literal>USING</literal>, as illustrated in <xref linkend="plpgsql-statements-executing-dyn"/>. 変数置換はEXECUTEコマンドまたはその亜種におけるコマンド文字列の中では起こりません。 そのようなコマンドに可変値を挿入する時は、43.5.4に述べたように、文字列の値を構成するものの一部とするかUSINGを使用してください。

Variable substitution currently works only in <command>SELECT</command>, <command>INSERT</command>, <command>UPDATE</command>, <command>DELETE</command>, and commands containing one of these (such as <command>EXPLAIN</command> and <command>CREATE TABLE ... AS SELECT</command>), because the main SQL engine allows query parameters only in these commands. To use a non-constant name or value in other statement types (generically called utility statements), you must construct the utility statement as a string and <command>EXECUTE</command> it. 今のところ変数置換は、SELECTINSERTUPDATEDELETEコマンドと(EXPLAINCREATE TABLE ... AS SELECTのような)このうちの1つを含むコマンドの中だけで作動します。 メインSQLエンジンが問い合わせパラメータをこれらのコマンドでしか許可しないからです。 他の種類の文(通常ユーティリティ文といいます)において可変名または可変値を使用するには、文字列としてユーティリティ文を構成しEXECUTEしてください。

43.11.2. 計画のキャッシュ #

<title>Plan Caching</title>

The <application>PL/pgSQL</application> interpreter parses the function's source text and produces an internal binary instruction tree the first time the function is called (within each session). The instruction tree fully translates the <application>PL/pgSQL</application> statement structure, but individual <acronym>SQL</acronym> expressions and <acronym>SQL</acronym> commands used in the function are not translated immediately. PL/pgSQLインタプリタは、初めてその関数が(各セッションで)呼び出された時に、関数のソーステキストを解析し、バイナリ形式の命令ツリーを内部で作成します。 この命令ツリーは完全にPL/pgSQL文構造に変換されますが、関数内部の個々のSQL式とSQLコマンドは即座に変換されません。

As each expression and <acronym>SQL</acronym> command is first executed in the function, the <application>PL/pgSQL</application> interpreter parses and analyzes the command to create a prepared statement, using the <acronym>SPI</acronym> manager's <function>SPI_prepare</function> function. Subsequent visits to that expression or command reuse the prepared statement. Thus, a function with conditional code paths that are seldom visited will never incur the overhead of analyzing those commands that are never executed within the current session. A disadvantage is that errors in a specific expression or command cannot be detected until that part of the function is reached in execution. (Trivial syntax errors will be detected during the initial parsing pass, but anything deeper will not be detected until execution.) 各式やSQLコマンドが初めてその関数で実行される時に、PL/pgSQLインタプリタはSPIマネージャのSPI_prepare関数を使用して、プリペアドステートメントを作成するためにコマンドを解析します。 その後にその式やコマンドが行われる時には、そのプリペアドステートメントを再利用します。 こうして、めったに分岐されない条件付きコードパスを持つ関数では、現在のセッションで実行されないそれらのコマンドの解析によるオーバーヘッドを背負いこむことはありません。 欠点は特定の式や問い合わせのエラーが、関数の該当部分が実行されるまで検出されないことです。 (典型的な構文エラーは、最初の解釈において検出されますが、それより深いエラーは、実行の時まで検出されません)。

<application>PL/pgSQL</application> (or more precisely, the SPI manager) can furthermore attempt to cache the execution plan associated with any particular prepared statement. If a cached plan is not used, then a fresh execution plan is generated on each visit to the statement, and the current parameter values (that is, <application>PL/pgSQL</application> variable values) can be used to optimize the selected plan. If the statement has no parameters, or is executed many times, the SPI manager will consider creating a <firstterm>generic</firstterm> plan that is not dependent on specific parameter values, and caching that for re-use. Typically this will happen only if the execution plan is not very sensitive to the values of the <application>PL/pgSQL</application> variables referenced in it. If it is, generating a plan each time is a net win. See <xref linkend="sql-prepare"/> for more information about the behavior of prepared statements. PL/pgSQLは(正確にはSPIマネージャは)さらに特定のプリペアドステートメントに関する実行計画のキャッシュを試行できます。 キャッシュした実行計画が使用されなかった場合、プリペアドステートメントが呼び出される度に新しい実行計画が作成され、選択した実行計画を最適にするために、最新のパラメータ値(すなわちPL/pgSQLの変数値)が使用されます。 プリペアドステートメントがパラメータを持たないか何回も使用される場合、SPIマネージャは特定のパラメータ値に依存しない一般的な実行計画の作成を考え、再使用のためにキャッシュします。 典型的には、これは参照したPL/pgSQLの変数値が、実行計画にさほど影響しない場合にだけ起こります。 それならば、毎回の実行計画の作成の方が優れています。 プリペアドステートメントに関する詳細はPREPAREを参照してください。

Because <application>PL/pgSQL</application> saves prepared statements and sometimes execution plans in this way, SQL commands that appear directly in a <application>PL/pgSQL</application> function must refer to the same tables and columns on every execution; that is, you cannot use a parameter as the name of a table or column in an SQL command. To get around this restriction, you can construct dynamic commands using the <application>PL/pgSQL</application> <command>EXECUTE</command> statement &mdash; at the price of performing new parse analysis and constructing a new execution plan on every execution. このようにPL/pgSQLはプリペアドステートメントおよび時には実行計画を保存しますので、PL/pgSQL関数内に直接現れるSQLコマンドは実行の度に同じテーブルとフィールドを参照しなければなりません。 つまり、SQLコマンドにて、テーブルやフィールドの名前としてパラメータを使用することができません。 実行の度に新しく実行計画を作成して解析する無駄を覚悟で、PL/pgSQLEXECUTE文を使った動的問い合わせを構成することで、この制限を回避できます。

The mutable nature of record variables presents another problem in this connection. When fields of a record variable are used in expressions or statements, the data types of the fields must not change from one call of the function to the next, since each expression will be analyzed using the data type that is present when the expression is first reached. <command>EXECUTE</command> can be used to get around this problem when necessary. レコード変数の変わりやすいという性質はこの接続において別の問題となります。 レコード変数のフィールドが式や文の中で使用される場合、そのフィールドのデータ型を関数を呼び出す度に変更してはいけません。 それぞれの式が最初に実行された時のデータ型を使用して、その式が解析されているからです。 必要な場合EXECUTEを使用してこの問題を回避することができます。

If the same function is used as a trigger for more than one table, <application>PL/pgSQL</application> prepares and caches statements independently for each such table &mdash; that is, there is a cache for each trigger function and table combination, not just for each function. This alleviates some of the problems with varying data types; for instance, a trigger function will be able to work successfully with a column named <literal>key</literal> even if it happens to have different types in different tables. 同一の関数が2つ以上のテーブルのトリガとして使用される場合、PL/pgSQLはテーブルごとのプリペアドステートメントをキャッシュします。 すなわち、各々のトリガ関数とテーブルの組ごとにキャッシュするのであり、トリガ関数ごとではありません。 このため、データ型の変更に伴う問題の一部を軽減します。 例えば、別のテーブルにある異なったデータ型であっても、keyと命名した列に対してトリガ関数は有効に作動します。

Likewise, functions having polymorphic argument types have a separate statement cache for each combination of actual argument types they have been invoked for, so that data type differences do not cause unexpected failures. 同様に、多様型の引数を持った関数は、実際に呼び出す引数の型の組み合わせごとに別々のプリペアドステートメントをキャッシュします。 そのため、データ型の差異が原因で予期しない失敗が起こることはありません。

Statement caching can sometimes have surprising effects on the interpretation of time-sensitive values. For example there is a difference between what these two functions do: プリペアドステートメントのキャッシュにより、時間に依存する値の解釈の結果に違いが現れることがあります。 例えば、以下の2つの関数の結果は異なります。

CREATE FUNCTION logfunc1(logtxt text) RETURNS void AS $$
    BEGIN
        INSERT INTO logtable VALUES (logtxt, 'now');
    END;
$$ LANGUAGE plpgsql;

and: および

CREATE FUNCTION logfunc2(logtxt text) RETURNS void AS $$
    DECLARE
        curtime timestamp;
    BEGIN
        curtime := 'now';
        INSERT INTO logtable VALUES (logtxt, curtime);
    END;
$$ LANGUAGE plpgsql;

In the case of <function>logfunc1</function>, the <productname>PostgreSQL</productname> main parser knows when analyzing the <command>INSERT</command> that the string <literal>'now'</literal> should be interpreted as <type>timestamp</type>, because the target column of <classname>logtable</classname> is of that type. Thus, <literal>'now'</literal> will be converted to a <type>timestamp</type> constant when the <command>INSERT</command> is analyzed, and then used in all invocations of <function>logfunc1</function> during the lifetime of the session. Needless to say, this isn't what the programmer wanted. A better idea is to use the <literal>now()</literal> or <literal>current_timestamp</literal> function. logfunc1の場合では、PostgreSQLのメインパーサは、INSERTを解析する時に、logtableの対象列の型から'now'timestampと解釈しなければならないことを把握しています。 こうして、パーサはINSERTが解析された時点で'now'timestamp定数に変換し、その定数値をその後のセッションの有効期間におけるlogfunc1の全ての呼び出しで使用します。 言うまでもありませんが、これはプログラマが意図した動作ではありません。 now()またはcurrent_timestamp関数の使用が優れています。

In the case of <function>logfunc2</function>, the <productname>PostgreSQL</productname> main parser does not know what type <literal>'now'</literal> should become and therefore it returns a data value of type <type>text</type> containing the string <literal>now</literal>. During the ensuing assignment to the local variable <varname>curtime</varname>, the <application>PL/pgSQL</application> interpreter casts this string to the <type>timestamp</type> type by calling the <function>textout</function> and <function>timestamp_in</function> functions for the conversion. So, the computed time stamp is updated on each execution as the programmer expects. Even though this happens to work as expected, it's not terribly efficient, so use of the <literal>now()</literal> function would still be a better idea. logfunc2の場合では、PostgreSQLのメインパーサは'now'の型を決定することができません。 そのため、nowという文字列を持つtext型のデータ値を返します。 curtimeローカル変数に代入する時に、PL/pgSQLインタプリタはこの文字列をtextouttimestamp_in関数を変換に使用してtimestamp型にキャストします。 ですから、演算されたタイムスタンプは、プログラマが意図した通り、実行の度に更新されます。 この方法でたまたま意図した通り動くけれど、それほど効率的ではありません。 ですから、now()関数の使用の方が優れています。