Chapter 37. PL/Perl - Perl 过程语言

Table of Contents
37.1. PL/Perl 函数和参数
37.2. 从 PL/Perl 里访问数据库
37.3. PL/Perl 里的数据值
37.4. PL/Perl 里的全局变量
37.5. 可信的和不可信的 PL/Perl
37.6. PL/Perl 触发器
37.7. 限制及缺少的特性

PL/Perl 是一种可装载的过程语言,通过它我们可以用 Perl 编程语言写 PostgreSQL 函数。

要在特定数据库里安装 PL/Perl,使用 createlang plperl dbname

提示: 如果某种编程语言安装到 template1,那么所有随后创建的数据库都会自动安装这种语言。

注意: 使用源码包的用户必须在安装过程中特别打开 PL/Perl 的制作。 (请参考 Section 14.1 获取更多信息)。 二进制包的用户可能会在一些独立的子包中找到 PL/Perl。

37.1. PL/Perl 函数和参数

要用 PL/Perl 语言创建一个函数,可以使用标准的语法:

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

函数体是普通 Perl 代码。

CREATE FUNCTION 命令的语法要求把函数体写成字串常量。 通常处理字串文本用美元符包围更方便(参阅 Section 4.1.2.2), 如果你想使用传统的单引号语法,你必须逃逸函数体里面使用的单引号(')和反斜杠(\), 通常是写双份(参阅 Section 4.1.2.1 )。

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

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

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

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

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

如上所述,要从 PL/Perl 函数中返回一个 SQL 空值, 我们可以返回一个未定义(undef)的数值。 不管该函数是否严格,我们都可以这么做。

复合类型的参数是当做指向散列的引用传递给函数的。 散列的键字是复合类型的属性名。下面是一个例子:

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 函数也能返回标量或者符合类型的集合。要实现这个目的, 我们可以分别返回一个指向标量或者指向散列引用的数组的引用。下面是一些简单的例子:

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();

请注意在你做这些事情的时候,Perl 会在内存里制作整个数组; 因此这个技巧对特别大的结果集而言,伸缩性不太好。

PL/Perl 目前还没有对域类型的完整支持: 它把域类型当作下层的标量类型看待。这就意味着跟域相关的约束将不会被强制。 对于函数参数而言,这不算个问题,但是如果你声明 PL/Perl 函数返回一个域类型, 那么就有危险了。