基本的な文

PostgreSQL 9.0.0文書
前のページ | 巻戻し | 第 39章PL/pgSQL - SQL手続き言語 | 早送り | 次のページ

# 39.5. 基本的な文

本節および次節では、明示的にPL/pgSQLで解釈される、全ての種類の文について説明します。 これらの種類の文として認められないものは全て、SQLコマンドであると仮定され、項39.5.2および項39.5.3において記述したように、メインデータベースエンジンに送信され実行されます。

## 39.5.1. 代入

値をPL/pgSQL変数に代入する場合は以下のように記述します。

    variable := expression;

上述した通り、このような文中にある式は、メインデータベースエンジンに送信される`SELECT` SQLコマンドによって評価されます。 式は1つの値を生成しなければなりません (変数が行変数またはレコード変数の場合は行値となるかもしれません)。 対象の変数は単純な変数(ブロック名で修飾可能)、行変数またはレコード変数のフィールド、または単純な変数またはフィールドとなる配列要素とすることができます。

式の結果データ型が変数のデータ型に一致しない場合、または、変数が（`char(20)`のように）特定の大きさ/精度を持つ場合、結果の値は PL/pgSQLインタプリタによって、結果の型の出力関数と変数の型の入力関数を使用して暗黙的に変換されます。 これにより、結果値の文字列形式を入力関数で受け付けることができない場合に、入力関数において実行時エラーが発生する可能性があることに注意してください。

例：

    tax := subtotal * 0.06;
    my_record.user_id := 20;

## 39.5.2. 結果を伴わないコマンドの実行

例えば、`RETURNING`句のない`INSERT`のように、行を返さない任意のSQLのコマンドについては、単にそのコマンドを記述することによってPL/pgSQL関数の内部でコマンドを実行できます。

コマンドテキストに現れる全てのPL/pgSQL変数名は、パラメータとして扱われます。 その後、実行時のパラメータ値として、その時点の変数値が提供されます。 これは以前に述べた式に関する処理と同じです。 項39.10.1を参照してください。

SQLコマンドがこのように実行されると、PL/pgSQLはコマンドを一度だけ計画し、データベース接続が切断されるまで、以後の実行でその計画を再利用します。 これに関する詳細については項39.10.2を参照してください。

式または`SELECT`問い合わせを評価して結果を破棄することが、役に立つ場合があります。 例えば、関数の呼び出しにおいて、副次的な成果を取得できるが、結果は無用である場合です。 このような時PL/pgSQLでは、`PERFORM`文を使用してください。

    PERFORM query;

これは`query`を実行し、その結果を破棄します。 SQLの`SELECT`文と同じ方法で`query`を記述しますが、最初のキーワード`SELECT`を`PERFORM`に置き換えてください。 結果を返さないコマンドと同様に、PL/pgSQL変数は問い合わせ内に置き換えられ、計画は同様にキャッシュされます。 また、特殊な変数である`FOUND`は問い合わせ結果が1行でも生成された場合は真に設定され、生成されない場合は偽に設定されます（項39.5.5を参照してください）。

> 注意: 直接`SELECT`を記述すれば、この結果を得ることができると考えるかもしれませんが、現時点でこれを行う方法は`PERFORM`しかありません。 `SELECT`のように行を返すSQLコマンドは、エラーとして拒絶されます。 なお、`INTO`句を有する時は例外であり、次節で説明します。

以下に例を示します。

    PERFORM create_mv('cs_session_page_requests_mv', my_query);

## 39.5.3. 1行の結果を返す問い合わせの実行

（多分、複数列の）1行を返すSQLコマンドの結果は、レコード変数、行型の変数、スカラ変数のリストに代入することができます。 これは、基本的なSQLコマンドを記述して、それに`INTO`句を追加することによって行われます。 以下に例を示します。

    SELECT select_expressions INTO [STRICT] target FROM ...;
    INSERT ... RETURNING expressions INTO [STRICT] target;
    UPDATE ... RETURNING expressions INTO [STRICT] target;
    DELETE ... RETURNING expressions INTO [STRICT] target;

ここで、`target`はレコード変数、行変数、あるいは、単純な変数とレコード/行変数のフィールドをカンマで区切ったリストです。 PL/pgSQL変数により残りの問い合わせが置換され、行を返さないコマンドにおいて述べたように計画がキャッシュされます。 このように作動するのは、`RETURNING`を伴った`INSERT`/`UPDATE`/`DELETE`と`SELECT`および行セットの結果を返すユーティリティコマンド（例えば、`EXPLAIN`）です。 `INTO`句以外では、SQLコマンドはPL/pgSQLの外部に記述したものと同じです。

> ティップ: 通常のPostgreSQLの`SELECT INTO`文では、`INTO`の対象は新しく作成されるテーブルです。 しかし、`INTO`を伴った`SELECT`では、この解釈が通常と大きく異なることに注意してください。 PL/pgSQL関数内部で`SELECT`の結果からテーブルを作成したい場合は、`CREATE TABLE ... AS SELECT`構文を使用してください。

行または変数リストが対象に使用された場合、列数とデータ型において問い合わせの結果と対象の構造が正確に一致しなければなりません。 さもないと、実行時エラーが発生します。 レコード変数が対象の場合は、問い合わせ結果の列の行型に自身を自動的に設定します。

`INTO`句はSQLコマンドのほとんど任意の場所に記述することができます。 習慣的には、`SELECT`文においては`select_expressions`の直前または直後に、他のコマンドにおいては文の終わりに記述されます。 将来のバージョンでPL/pgSQLのパーサがより厳格になる場合に備えて、この習慣に従うことを推奨します。

`INTO`句において`STRICT`が指定されない場合、`target`は問い合わせが返す最初の行となり、行を返さない時はNULLとなります。 （"最初の行"とは`ORDER BY`を使用しないと定義できないことに注意してください。） 2行目以降の行の結果は、全て破棄されます。 以下のように、特殊な`FOUND`変数（項39.5.5を参照してください）を調べて、行が返されたかどうかを検査することができます。

    SELECT * INTO myrec FROM emp WHERE empname = myname;
    IF NOT FOUND THEN
        RAISE EXCEPTION 'employee % not found', myname;
    END IF;

`STRICT`オプションが指定された場合、問い合わせは正確に1行を返さなければなりません。 さもないと、行がない時は`NO_DATA_FOUND`、2行以上が返った時は`TOO_MANY_ROWS`という実行時エラーが生じます。 エラーを捕捉したい時は、例外ブロックを使用できます。 以下に例を示します。

    BEGIN
        SELECT * INTO STRICT myrec FROM emp WHERE empname = myname;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RAISE EXCEPTION 'employee % not found', myname;
            WHEN TOO_MANY_ROWS THEN
                RAISE EXCEPTION 'employee % not unique', myname;
    END;

`STRICT`を指定したコマンドが成功すると、`FOUND`変数は常に真に設定されます。

`STRICT`が指定されない場合でも、`RETURNING`を伴った`INSERT`/`UPDATE`/`DELETE`が2行以上を返した時は、エラーとなります。 なぜなら、どの1行を返すか決定する`ORDER BY`のようなオプションが存在しないからです。

> 注意: `STRICT`オプションは、OracleのPL/SQLの`SELECT INTO`および関連した文に対応します。

SQLの問い合わせが返す複数行の結果を処理したい場合は、項39.6.4を参照してください。

## 39.5.4. 動的コマンドの実行

PL/pgSQL関数の内部で、動的コマンド、つまり実行する度に別のテーブルや別のデータ型を使用するコマンドを生成したいということがよくあるでしょう。 PL/pgSQLが通常行うコマンドの計画のキャッシュは（項39.10.2で述べたように）このような状況では動作しません。 この種の問題を扱うために、以下の`EXECUTE`文が用意されています。

    EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];

ここで、`command-string`は実行されるコマンドを含む（`text`型の）文字列を生成する式です。 オプションの`target`はレコード変数、行変数、あるいは、単純な変数とレコード/行変数のフィールドをカンマで区切ったリストで、その中にコマンドの結果が格納されます。 オプションの`USING`式は コマンドに挿入される値を与えます。

PL/pgSQL変数は、この演算用のコマンド文字列へ置換されません。 必要な変数の値はすべてコマンド文字列を作成する時に埋め込まなければなりません。 もしくは、以下に説明するパラメータを使用することもできます。

また、`EXECUTE`を介して実行されるコマンド計画をキャッシュすることはありません。 代わりに、コマンドは文が実行される時に準備されます。 したがって、異なるテーブルと列に対する操作を実行できるように、コマンド文字列を関数内部で動的に作成することができます。

`INTO`句は、行を返すSQLコマンドの結果を代入するべき場所を指定します。 行または変数リストが用いられる時、それは問い合わせの結果の構造と正確に一致しなければなりません (レコード変数が使用される時、自動的に結果の構造と一致するように自身を構築させます）。 複数の行が返された時、最初の行だけが`INTO`変数に代入されます。 1行も返されない時、NULL が`INTO`変数に代入されます。 `INTO`句が指定されない時、問い合わせの結果は捨てられます。

`STRICT`オプションが指定された時、問い合わせの結果が正確に1行の場合を除き、エラーとなります。

コマンド文字列はパラメータ値を使用可能で、それらは`$1`、`$2`等としてコマンドの中で参照されます。 これらの記号は`USING`で与えられる値を参照します。 この方式はデータの値をテキストとしてコマンド文字列の中に挿入する際、よく好まれます。 それは値をテキストに変換、そしてその逆を行う場合の実行時オーバーヘッドを防止するとともに、引用符付けするとか、エスケープをする必要がないため、SQLインジェクション攻撃に対してより襲われにくくなります。 以下に例を示します。

    EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
       INTO c
       USING checked_user, checked_date;

パラメータ記号はデータ値のみ使用可能です。 もし動的に決定されるテーブルや列名を使用したい場合、テキストでコマンド文字列にそれらを挿入する必要があります。 例えば、先行する問い合わせが、動的に選択されたテーブルに対して処理される必要がある時は、次のようにします。

    EXECUTE 'SELECT count(*) FROM '
        || tabname::regclass
        || ' WHERE inserted_by = $1 AND inserted <= $2'
       INTO c
       USING checked_user, checked_date;

他にもパラメータ記号は`SELECT`、`INSERT`、`UPDATE`、`DELETE`コマンドでしか動作しない、という制限があります。 他の種類の文(一般的にユーティリティ文と呼ばれます)では、単なるデータ値であったとしてもテキストの値として埋め込まなければなりません。

最初の例のように、単純な定数コマンドと`USING`パラメータを使った`EXECUTE`は、コマンドを直接PL/pgSQLで書いて、PL/pgSQL変数を自動的に置換したものと機能的に同じです。 重要な差異として、`EXECUTE`が現在のパラメータ値に特化した計画を生成し、コマンドを実行する度に計画を再作成することです。 一方、PL/pgSQLは通常一般的な計画を作成し、再使用に備えキャッシュします。 最適な計画がパラメータ値に大きく依存する場合、`EXECUTE`は大幅に高速化されますが、計画がパラメータ値に依存しない場合、計画の再作成は無駄になります。

`SELECT INTO`は`EXECUTE`では現在サポートされません。 代わりに、普通の`SELECT`コマンドを実行し、`EXECUTE`の一部として`INTO`を記述してください。

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

例 39-1. 動的問い合わせの中の値の引用符付け

動的コマンドを使用する時、しばしばPL/pgSQLでは単一引用符をエスケープしなければなりません。 関数本体における固定のテキストを引用符付けする推奨方法は、ドル引用符を使用する方法です。 （ドル引用符を用いない旧式のコードを保有している場合は、項39.11.1の概要を参照することが、理解しやすいコードへの変換作業の手助けになります）。

作成した問い合わせに挿入すべき動的な値は、それ自身の内部に引用符を含む可能性があるため、注意深い処理が必要です。 以下に例を示します（ここでは関数にドル引用符を用いる方法を使用すると仮定しているので、引用符を二重化する必要はありません）。

    EXECUTE 'UPDATE tbl SET '
            || quote_ident(colname)
            || ' = '
            || quote_literal(newvalue)
            || ' WHERE key = '
            || quote_literal(keyvalue);

この例は、`quote_ident`と`quote_literal`関数（項9.4を参照）の使用方法を示しています。 安全のため、列またはテーブル識別子を含む式は動的問い合わせに挿入する前に`quote_ident`を介して渡されなくてはなりません。 組み立てられるコマンドの中のリテラル文字列となるはずの値を含む式は、`quote_literal`を介して渡されなければなりません。 これらの関数は、すべての特殊文字を適切にエスケープして埋め込んだ、二重引用符または単一引用符で囲まれた入力テキストを返すために、適切な手順を踏みます。

`quote_literal`は`STRICT`ラベル付けされているため、NULL引数で呼び出された場合、常にNULLを返します。 上記の例で、`newvalue`または`keyvalue`がNULLの場合、動的問合せ文字列全体がNULLとなり、`EXECUTE`からのエラーを導きます。 `quote_nullable`関数を使用することで、この問題を回避することができます。 その動作は、NULL引数付きで呼び出された場合に文字列`NULL`を返すことを除いて`quote_literal`と同一です。 以下に例を示します。

    EXECUTE 'UPDATE tbl SET '
            || quote_ident(colname)
            || ' = '
            || quote_nullable(newvalue)
            || ' WHERE key = '
            || quote_nullable(keyvalue);

NULLの可能性のある値を処理するのであれば、通常`quote_literal`の代わりに`quote_nullable`を使用しなければなりません。

いつものように、問い合わせの中のNULL値は意図しない結果を確実にもたらさないよう配慮をしなければなりません。 例えば次のような`WHERE`句の結果はどうなるのでしょう。

    'WHERE key = ' || quote_nullable(keyvalue)

これは`keyvalue`がNULLである限り成功しません。 その理由は、等価演算子`=`をNULLオペランドで使用するとその結果は常にNULLとなるからです。 NULLを通常のキーの値と同じように動作させたい場合、上記を、以下のように書き換えなければなりません。

    'WHERE key IS NOT DISTINCT FROM ' || quote_nullable(keyvalue)

（現時点では、`IS NOT DISTINCT FROM`は`=`よりもより効率性が少なく扱われますので、必要に迫られた場合以外は行わないようにしてください。 NULLと`IS DISTINCT`についての更なる情報は項9.2を参照してください。）

ドル引用符は固定のテキストを引用符付けする場合のみ有用であるということに注意してください。 この例を次のように記述するのは非常に悪い考えです。

    EXECUTE 'UPDATE tbl SET '
            || quote_ident(colname)
            || ' = $$'
            || newvalue
            || '$$ WHERE key = '
            || quote_literal(keyvalue);

なぜなら、`newvalue`の内容がたまたま`$$`を含む時は、途中で次の処理へ移ってしまうからです。 同様の不測事態は、ドル引用符の他の区切り文字を選んだ時も起こります。 したがって、テキストの内容を把握していない時は、安全にテキストを引用符付けするために、`quote_literal`、`quote_nullable`、または`quote_ident`関数を適切に使用しなければなりません。

動的問い合わせと`EXECUTE`の長大な例は例39-7にあります。 それは新しい関数を定義するために`CREATE FUNCTION`コマンドを組み立て実行するものです。

## 39.5.5. 結果ステータスの取得

コマンドの効果を判断するにはいくつか方法があります。 最初の方法は以下のような形式の`GET DIAGNOSTICS`を使用する方法です。

    GET DIAGNOSTICS variable = item [ , ... ];

このコマンドによってシステムステータスインジケータを取り出すことができます。 各`item`は、指定された変数（これは受け取るために正しいデータ型でなければなりません）に代入される状態値を識別するキーワードです。 現在使用可能なステータス項目は、SQLエンジンに送信された最終SQLコマンドにより処理された行数を示す`ROW_COUNT`、および、最も最近のSQLコマンドにより挿入された最後の行のOIDを示す`RESULT_OID`です。 `RESULT_OID`はOIDを保有するテーブルへの`INSERT`コマンドの後でのみ有意であることに注意してください。

以下に例を示します。

    GET DIAGNOSTICS integer_var = ROW_COUNT;

コマンドの効果を判断する2番目の方法は、`FOUND`という`boolean`型の特殊な変数を検査することです。 PL/pgSQLの各関数呼び出しで使用される際、`FOUND`は最初は偽に設定されています。 以下のように、それぞれの文の種類によって設定が変更されます。

- `SELECT INTO`文は、行が代入された場合は真、返されなかった場合は偽を`FOUND`に設定します。

- `PERFORM`文は、1つ以上の行が生成（破棄）された場合は真、まったく生成されなかった場合は偽を`FOUND`に設定します。

- `UPDATE`、`INSERT`、および`DELETE`文は、少なくとも1行が影響を受けた場合は真、まったく影響を受けなかった場合は偽を`FOUND`に設定します。

- `FETCH`文は、行が返された場合は真、まったく返されなかった場合は偽を`FOUND`に設定します。

- `MOVE`文は、カーソルの移動が成功したが場合は真、失敗した場合は偽を`FOUND`に設定します。

- `FOR`文は1回以上繰り返しが行われた場合は真、行われなかった場合は偽を`FOUND`に設定します。 これは４つの`FOR`文全ての変異体（整数`FOR`ループ、レコードセット`FOR`ループ、動的レコードセット`FOR`ループ、そしてカーソル`FOR`）に適用されます。 `FOUND`は`FOR`ループが終了した際、このように設定されます。 つまり、ループ実行中は`FOR`文による`FOUND`の変更はありません。 ただし、ループ本体内の他種類の文を実行することによって、変更されるかもしれません。

- `RETURN QUERY`と`RETURN QUERY EXECUTE`文は、問い合わせが行を１つでも返せば真、行が返されなければ偽を設定します。

他のPL/pgSQL文は`FOUND`の状態を変更しません。 特に、`EXECUTE`は`GET DIAGNOSTICS`の出力を変更しますが、`FOUND`を変更しないことに注意してください。

`FOUND`はそれぞれのPL/pgSQL関数内部のローカル変数です。 `FOUND`に対して行われた全ての変更は、現在の関数にのみ影響します。

## 39.5.6. まったく何もしない

何もしないプレースホルダ文が有用になることがあります。 例えば、IF/THEN/ELSE文の一部が空文であることを明示したい時です。 このような目的には`NULL`文を使用します。

    NULL;

例えば、次の2つのコードは同等です。

    BEGIN
        y := x / 0;
    EXCEPTION
        WHEN division_by_zero THEN
            NULL;  -- 誤りを無視する
    END;
        END;

    BEGIN
        y := x / 0;
    EXCEPTION
        WHEN division_by_zero THEN  -- 誤りを無視する
    END;

どちらが望ましいと思うかは、好みの問題です。

> 注意: OracleのPL/SQLでは無記述の文は許されませんので、こうした状況では`NULL`文が必須です。 しかしPL/pgSQLでは無記述の文が許可されています。

前のページ | ホーム | 次のページ
式 | 上に戻る | 制御構造