PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 42. PL/Perl - Perl 过程语言 | 下一页

# 42.1. PL/Perl 函数和参数

要用 PL/Perl 语言创建一个函数，可以使用标准的CREATE FUNCTION语法：

    CREATE FUNCTION funcname (argument-types) RETURNS return-type AS $$
        # PL/Perl function body
    $$ LANGUAGE plperl;

函数体是普通 Perl 代码。实际上，PL/Perl 胶水代码将其封装在一个 Perl 子过程里。 一个 PL/Perl 函数在一个标量环境中调用，所以不能返回一个列表。 你可以像下面描述的那样用返回引用的方法返回非标量值（arrays, records, 和 sets）。

PL/Perl也支持DO语句调用匿名代码块：

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

一个匿名代码块不接收参数，而且丢弃任何返回值。否则它的行为就像一个函数。

> 注意: 在 Perl 里使用命名的嵌套子过程是很危险的，特别是它们在闭包里引用了词法变量的时候。 因为 PL/Perl 是封装在一个子过程里，因此，任何你放进去的命名子过程都将被嵌套。通常， 创建一个用 coderef 调用的匿名子过程要安全得多。想要获取更多细节，请参阅 `Variable "%s" will not stay shared`里的记录或 perldiag手册页中的`Variable "%s" is not available`， 或在Internet上搜索"perl 嵌套命名子过程"。

`CREATE FUNCTION`命令的语法要求把函数体写成字符串常量。 通常处理字符串文本用美元符界定更方便(参阅第 4.1.2.4 节)， 如果你想使用传统的`E''`转义语法，必须双写函数体里使用的任何单引号(`'`) 和反斜杠(`\`)(参见第 4.1.2.1 节)。

参数和结果都是和任何其它 Perl 子过程里那样处理的：参数是放在`@_`里传递的， 结果值是用`return`返回或者作为函数中最后计算的表达式的值返回。

比如，一个返回两个整数中较大值的函数可以这么写：

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

> 注意: 为了能够在PL/Perl里使用，参数将从数据库编码转换为UTF-8，然后在返回时从UTF-8回到数据库编码。

如果给函数传递一个 NULL 那么其参数值将以 Perl 中"undefined" 的形式出现。上面的函数定义在输入为 NULL 时的行为不是很正常(实际上， 它将表现得好像它们都是零一样)。可以给函数定义增加`STRICT`让 PostgreSQL做一些更合理的事情：如果传递进来一个 NULL ， 那么该函数则根本不会被调用，而只是自动返回一个 NULL 结果。另外， 可以在函数体里检查未定义的输入。比如，假设想收到一个 NULL 和一个非 NULL 参数的 `perl_max`返回非 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 函数中返回一个 NULL ，可以返回一个未定义的数值。 不管该函数是否严格，都可以这么做。

任何一个不是引用的函数参数是一个字符串，这是相关数据类型在标准PostgreSQL 外部文本里的表示。普通数字或文本类型的情况下，Perl将只是做正确的事情，程序员不需要担心。 然而，在其他情况下，需要将参数转换为Perl可用的形式。例如，`decode_bytea` 函数可以用来转换类型`bytea`的参数为非转义的二进制。

相似的，传回PostgreSQL的值必须是外部文本表示格式。 例如，`encode_bytea`函数可以用来为一个类型为`bytea` 的返回值转义二进制数据。

Perl 可以用 Perl 数组引用的方式返回PostgreSQL数组。 下面是一个例子：

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

    select returns_array();

Perl作为一个`PostgreSQL::InServer::ARRAY`对象传递PostgreSQL 数组。这个对象可以被视为一个数组引用或一个字符串，Perl为PostgreSQL 低于9.1的版本编写了代码，允许向后兼容。例如：

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

        # as an array reference
        for (@$arg) {
            $result .= $_;
        }

        # also works as a string
        $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 返回。

PL/Perl 函数也能返回标量或者复合类型的集合。通常你希望一次返回一行， 一方面加速函数启动时间，另外一方面防止在内存里堆积整个结果集。 可以用下面说明的函数`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或更高，那么`feature`程序也适用于`use`。

上一页 | 起始页 | 下一页
PL/Perl - Perl 过程语言 | 上一级 | PL/Perl里的数据值