PL/Perl 是一种可装载的过程语言,通过它我们可以用 Perl 编程语言写 PostgreSQL 函数。
要在特定数据库里安装 PL/Perl,使用 createlang plperl dbname。
提示: 如果某种编程语言安装到 template1,那么所有随后创建的数据库都会自动安装这种语言。
注意: 使用源码包的用户必须在安装过程中特别打开 PL/Perl 的制作。 (请参考 Section 14.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 函数必须总是返回标量数值。 你可以用返回引用的方法返回更复杂的结构,像下面描述的那样。 绝对不要返回一个列表。
注意: 在 Perl 里使用命名的嵌套子过程是很危险的,特别是它们在闭包里引用了词法变量的事后。 因为 PL/Perl 是封装在一个子过程里,因此,任何你创建的命名子过程都将被嵌套。 通常,创建一个用 coderef 调用的匿名子过程要安全得多。参阅 perldiag 手册页获取更多细节。
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 ($x,$y) = @_; if (! defined $x) { if (! defined $y) { return undef; } return $y; } if (! defined $y) { return $x; } if ($x > $y) { return $x; } return $y; $$ LANGUAGE plperl;
如上所述,要从 PL/Perl 函数中返回一个 SQL 空值, 我们可以返回一个未定义(undef)的数值。 不管该函数是否严格,我们都可以这么做。
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();
复合类型的参数是当做指向散列的引用传递给函数的。 散列的键字是复合类型的属性名。下面是一个例子:
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();
PL/Perl 目前还没有对域类型的完整支持: 它把域类型当作下层的标量类型看待。这就意味着跟域相关的约束将不会被强制。 对于函数参数而言,这不算个问题,但是如果你声明 PL/Perl 函数返回一个域类型, 那么就有危险了。
如果你想在自己的代码里使用 strice 用法, 最简单的方法是 SET plperl.use_strict 为真。 这个参数影响随后的 PL/Perl 函数的编译, 但是不影响在当前会话里已经编译了的函数。 要想在 PL/Perl 装载之前设置这个参数, 我们必须把 "plperl" 添加到 postgresql.conf 的 custom_variable_classes 列表里。
另外一个使用 strict 用法的方法是把
use strict;
放在函数体里。但是这个只能在 PL/PerlU 函数里有用, 因为 use 不是可信任的操作。在 PL/Perl 里你可以这么用
BEGIN { strict->import(); }