37.1. 概要

PL/pgSQL呼び出しハンドラは関数のソーステキストを解析し、初めてその関数が(各セッションで)呼び出された時にバイナリ形式の命令ツリーを内部で作成します。 この命令ツリーは完全にPL/pgSQL文構造に変換されますが、関数内部の個々のSQL式とSQLコマンドは即座に変換されません。

各式やSQLコマンドが初めてその関数で使用される時に、PL/pgSQLインタプリタは(SPIマネージャのSPI_prepareSPI_saveplan関数を使用して)実行計画の準備を行います。 その後にその式やコマンドが行われる時には、その準備された計画を再利用します。 こうして、実行計画が必要とされる問い合わせを内包した条件付きコードを持つ関数では、そのデータベース接続が有効な間実際に使用された部分についてのみ、計画の準備と保存が行われます。 これにより、解析にかかる総時間をかなり短縮し、PL/pgSQL関数の文の問い合わせ計画を生成することができます。 欠点は特定の式や問い合わせのエラーが、関数の該当部分が実行されるまで検出されないことです。

PL/pgSQLが関数内の特定のコマンド用の問い合わせ計画を作成すると、そのデータベース接続が有効な間、その計画は再利用されます。 通常これにより性能は向上しますが、動的にデータベーススキーマを変更する場合は問題がいくつか発生します。 例えば、以下のようにします。

CREATE FUNCTION populate() RETURNS integer AS $$
DECLARE
    -- 宣言部
BEGIN
    PERFORM my_function();
END;
$$ LANGUAGE plpgsql;

上の関数を実行すると、PERFORM文用に生成された問い合わせ計画では、my_function()のOIDを参照します。 後に、my_function()を削除し、再作成すると、populate()my_function()を見つけることができなくなります。 その場合、新たにコンパイルされるようにpopulate()を再作成、または、少なくともデータベースセッションを新しく起動しなければなりません。 この他に、my_function()の定義を更新する時に、CREATE OR REPLACE FUNCTIONを使用することでこの問題を防ぐことができます (関数が"置き換えられる"時に、そのOIDが変わりません)。

このようにPL/pgSQLは実行計画を保存しますので、PL/pgSQL関数内に直接現れるSQLコマンドは実行の度に同じテーブルとフィールドを参照しなければなりません。 つまり、SQLコマンドにて、テーブルやフィールドの名前としてパラメータを使用することができません。 実行の度に新しく問い合わせ計画を作成する無駄を覚悟で、PL/pgSQLEXECUTE文を使った動的問い合わせを構成することで、この制限を回避できます。

注意: PL/pgSQL EXECUTE文はPostgreSQLサーバでサポートされているEXECUTESQL文とは関連がありません。 サーバのEXECUTE文はPL/pgSQL関数内で使用することはできません(使用する必要もありません)。

37.1.1. PL/pgSQLを使用することの利点

SQLPostgreSQLおよびその他のほとんどのリレーショナルデータベースが問い合わせ言語として使用している言語です。 移植性があり、習得が容易です。 しかし、あらゆるSQL文はデータベースサーバによって個々に実行されなければいけません。

これはクライアントアプリケーションに対して以下のようなことを要求しています。 まず、データベースサーバに問い合わせを送信します。 次にそれが処理されるのを待ちます。 次に、結果を取得して処理します。 次に若干の計算を行います。 そして、サーバに次の問い合わせを送信します。 クライアントがデータベースサーバマシンと異なるマシンの場合、プロセス間通信を招き、ネットワーク・オーバーヘッドを起こすでしょう。

PL/pgSQLを使うことで、計算と複数の問い合わせをデータベースサーバ内部でひとまとめに実行することができます。 このように、手続き言語の能力とSQLの使いやすさを持ち合わせているにもかかわらず、全てにおいてクライアント/サーバ通信のオーバーヘッドがないのでかなりの節約を行うことができます。

これにより、組み込み関数を使用しない応用に比較して、かなり性能を向上させることができます。

また、PL/pgSQLではSQL全てのデータ型、演算子、関数を使用することができます。

37.1.2. 引数と結果データ型のサポート

PL/pgSQLで作成された関数は、サーバでサポートされる任意のスカラデータ型や配列データ型を引数として受け付けることができ、また、これらの型を結果として返すことができます。 また、任意の、名前で指定された複合型(行型)を受け付けたり、返したりすることもできます。 さらに、項7.2.1.4で説明されているように、PL/pgSQL関数がrecordを返すように宣言することも可能で、この場合、結果は行型で、その列は呼び出す問い合わせの中での指定で決まります。

また、PL/pgSQL関数を、anyelementanyarray多相型を受け付けたり、返したりするように宣言することもできます。 項33.2.5の説明の通り、多相型関数で扱われるデータ型は呼び出しごとに変動することができます。 例を項37.4.1に示します。

PL/pgSQL関数を、"集合"、テーブル、1つのインスタンスを返す任意のデータ型を返すように宣言することもできます。 こうした関数は、結果集合の必要な要素に対してRETURN NEXTを実行することで、その出力を生成します。

最後に、有用な戻り値を持たない場合、PL/pgSQL関数は、voidを返すように宣言することができます。

PL/pgSQL関数は戻り値の型を明確に指定する代わりに、出力パラメータと共に宣言することもできます。 これは言語に対して基本的な能力を追加するものではありませんが、特に複数の値を返すときにしばしば便利です。

特殊な例が項37.4.1及び項37.7.1に見られます。