★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

45.1. PL/Perl関数と引数

PL/Perl言語で関数を作成するには、以下の標準的なCREATE FUNCTION構文を使用してください。

CREATE FUNCTION funcname (argument-types)
RETURNS return-type

-- 関数の属性はここに来る
AS $$

    # PL/Perl関数本体はここに来る
$$ LANGUAGE plperl;

関数本体は通常のPerlのコードです。 実際、PL/Perlの糊付けコードは、これをPerlのサブルーチンの内部に格納します。 PL/Perl関数はスカラコンテキストとして呼び出されます。このためリストを返すことはできません。 後述の通り、参照を返すことによりスカラ以外の値(配列、レコード、集合)を返すことができます。

PL/Perlプロシージャでは、Perlコードからのあらゆる戻り値は無視されます。

またPL/PerlはDO文で呼び出される匿名コードブロックをサポートします。

DO $$
    # PL/Perl code
$$ LANGUAGE plperl;

匿名コードブロックは引数を取りません。 また何らかの値を返したとしても破棄されます。 その他は関数と同様に動作します。

注記

Perl、特にその閉ざされたスコープで局所変数を参照するような場合では、名前付きの入れ子状サブルーチンの使用は危険です。 PL/Perl関数はサブルーチン内に格納されますので、内部に記述した名前付きのサブルーチンはすべて入れ子にされます。 一般的に、コード参照を介して呼び出す匿名サブルーチンを作成する方がかなり安全です。 詳細はperldiagマニュアルページ内のVariable "%s" will not stay sharedおよびVariable "%s" is not availableを参照してください。 またはインターネットでperl nested named subroutineを検索してください。

CREATE FUNCTIONコマンドの構文では、関数本体は文字列定数として記述されることを必須としています。 通常、文字列定数にはドル引用符付け(4.1.2.4を参照)を使用することが最も便利です。 エスケープ文字列構文E''を使用することを選択した場合、関数本体で使用される単一引用符(')とバックスラッシュ(\)をすべて二重にしなければなりません(4.1.2.1を参照)。

引数と結果は他のPerlサブルーチンと同様に扱われます。 引数は@_の中に渡され、結果値はreturn、または、その関数で最後に評価された式として返されます。

例えば、2つの整数のうち大きな方を返す関数は以下のように定義できます。

CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
    if ($_[0] > $_[1]) { return $_[0]; }
    return $_[1];
$$ LANGUAGE plperl;

注記

PL/Perl内部での使用のため、引数はデータベースの符号化方式からUTF-8に変換され、返されるときにUTF-8からデータベースの符号化方式に戻されます。

SQLのNULL値が関数に渡された場合、その引数値はPerlにおける未定義として現れます。 上の関数定義では、NULL値が入力された場合うまく動作しないでしょう(実際はそれがゼロであるかのように動作するでしょう)。 STRICTを関数定義に加えることで、PostgreSQLの動作をより合理的にすることができます。 NULL値が渡された場合、関数はまったく呼び出されず、単にNULLという結果が自動的に返されます。 他の方法として、関数本体で未定義な入力を検査することもできます。 例えば、perl_maxの引数の片方がNULL、もう片方が非NULLの場合に、NULL値ではなく非NULLの引数を返すようにするとします。

CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
    my ($x, $y) = @_;
    if (not defined $x) {
        return undef if not defined $y;
        return $y;
    }
    return $x if not defined $y;
    return $x if $x > $y;
    return $y;
$$ LANGUAGE plperl;

上で示した通り、PL/Perl関数からSQLのNULL値を返すためには、未定義値を返すようにしてください。 これは、関数が厳密かどうかに関係なく、実行することができます。

関数引数の内で参照ではないものは、対応するデータ型向けのPostgreSQLの標準的な外部テキスト表現で表された文字列です。 通常の数値やテキスト型では、Perlは正確に処理を行いますので、通常プログラマは心配することはありません。 しかし、この他の場合では、引数をPerlでより使用しやすいように変換する必要があります。 例えば、decode_bytea関数はbytea型の引数をエスケープしないバイナリに変換するために使用することができます。

同様に、PostgreSQLに戻される値を外部テキスト表現書式で表さなければなりません。 例えば、bytea型の戻り値をバイナリデータにエスケープするためにencode_byteaを使用することができます。

特に重要な場合の1つは真偽値です。 つい先ほど述べたように、bool値のデフォルトの振舞いはPerlにテキストとして、すなわち't'または'f'で渡されるというものです。 Perlは'f'を偽とは扱いませんので、これは問題をはらんでいます。 変換(CREATE TRANSFORMを参照してください)を使って問題を改善することができます。 適切な変換がbool_plperl拡張で提供されています。 使うには、拡張をインストールします。


CREATE EXTENSION bool_plperl;  -- PL/PerlUに対してはbool_plperlu

次にboolを受け取ったり返したりするPL/Perl関数に対してTRANSFORM関数属性を使います。例えば以下の通りです。

CREATE FUNCTION perl_and(bool, bool) RETURNS bool
TRANSFORM FOR TYPE bool
AS $$
  my ($a, $b) = @_;
  return $a && $b;
$$ LANGUAGE plperl;

この変換が適用されると、bool引数はPerlからは1もしくは空、すなわち正しく真または偽と見えます。 関数の結果が型boolなら、Perlが戻り値を真と評価したかどうかに従って真または偽となります。 同様の変換は、関数の内部で行われる真偽値の問い合わせ引数やSPI問い合わせの結果(45.3.1)でも実行されます。

Perlは、PostgreSQLの配列をPerl配列への参照として返すことができます。 以下に例を示します。

CREATE OR REPLACE function returns_array()
RETURNS text[][] AS $$
    return [['a"b','c,d'],['e\\f','g']];
$$ LANGUAGE plperl;

select returns_array();

PerlはPostgreSQLの配列をblessされたPostgreSQL::InServer::ARRAYオブジェクトとして渡します。 9.1より過去のPostgreSQLで作成されたPerlコードを実行させるための後方互換性のため、このオブジェクトは配列への参照または文字列として扱うことができます。 以下に例を示します。

CREATE OR REPLACE FUNCTION concat_array_elements(text[]) RETURNS TEXT AS $$
    my $arg = shift;
    my $result = "";
    return undef if (!defined $arg);


    # 配列への参照として
    for (@$arg) {
        $result .= $_;
    }


    # 文字列としても働く
    $result .= $arg;

    return $result;
$$ LANGUAGE plperl;

SELECT concat_array_elements(ARRAY['PL','/','Perl']);

注記

Perlプログラマの常識のように、多次元配列は低次元配列の参照への参照として表現されます。

複合型の引数はハッシュへの参照として関数に渡されます。 ハッシュのキーは複合型の属性名です。 以下に例を示します。

CREATE TABLE employee (
    name text,
    basesalary integer,
    bonus integer
);

CREATE FUNCTION empcomp(employee) RETURNS integer AS $$
    my ($emp) = @_;
    return $emp->{basesalary} + $emp->{bonus};
$$ LANGUAGE plperl;

SELECT name, empcomp(employee.*) FROM employee;

必要な属性を持つハッシュの参照を返すという同じ方法で、PL/Perl関数は複合型の結果を返すことができます。 以下に例を示します。

CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text);

CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$
    return {f2 => 'hello', f1 => 1, f3 => 'world'};
$$ LANGUAGE plperl;

SELECT * FROM perl_row();

宣言された結果データ型の任意の列の内、ハッシュ内に存在しないものはNULL値として返されます。

同様に、プロシージャの出力引数はハッシュ参照で返すことができます。

CREATE PROCEDURE perl_triple(INOUT a integer, INOUT b integer) AS $$
    my ($a, $b) = @_;
    return {a => $a * 3, b => $b * 3};
$$ LANGUAGE plperl;

CALL perl_triple(5, 10);

また、PL/Perl関数はスカラ型の配列や複合型の配列を返すこともできます。 通常ならば、起動処理の高速化とメモリ内の結果セット全体を待ち行列に保持できることから、1度に1行を返す方がよいでしょう。 以下に示すreturn_nextを使用して、これを行うことができます。 最後のreturn_nextの後で、returnまたはreturn undef(推奨)を記述しなければならないことに注意してください。

CREATE OR REPLACE FUNCTION perl_set_int(int)
RETURNS SETOF INTEGER AS $$
    foreach (0..$_[0]) {
        return_next($_);
    }
    return undef;
$$ LANGUAGE plperl;

SELECT * FROM perl_set_int(5);

CREATE OR REPLACE FUNCTION perl_set()
RETURNS SETOF testrowperl AS $$
    return_next({ f1 => 1, f2 => 'Hello', f3 => 'World' });
    return_next({ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' });
    return_next({ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' });
    return undef;
$$ LANGUAGE plperl;

小規模な結果セットでは、それぞれ単純な型、配列型、複合型に対応する、スカラ、配列への参照、ハッシュへの参照を含む配列への参照を返すことができます。 以下に、配列への参照として結果セット全体を返す単純な例をいくつか示します。

CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$
    return [0..$_[0]];
$$ LANGUAGE plperl;

SELECT * FROM perl_set_int(5);

CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
    return [
        { f1 => 1, f2 => 'Hello', f3 => 'World' },
        { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
        { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
    ];
$$ LANGUAGE plperl;

SELECT * FROM perl_set();

コード内でstrictプラグマを使用したいのであればいくつか選択肢があります。 一時的に大域的に使用するために、SET plperl.use_strictを真にすることができます。 このパラメータは、その後のPL/Perl関数のコンパイルに影響しますが、現在のセッションでコンパイル済みの関数には影響しません。 永続的に大域的に使用するためには、postgresql.confファイル内でplperl.use_strictを真に設定します。

特定の関数で永続的に使用するためには単純に以下を関数本体の先頭に記載してください。

use strict;

また、Perlのバージョンが5.10.0以上であればusefeatureプラグマが利用可能です。