2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2

43.1. PL/Perl 函数和参数 #

要创建一个 PL/Perl 语言的函数,请使用标准的 CREATE FUNCTION 语法

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

函数体是普通的 Perl 代码。实际上,PL/Perl 的胶水代码将它包装在一个 Perl 子程序中。PL/Perl 函数在标量上下文中调用,所以它不能返回列表。你可以通过返回一个引用来返回非标量值(数组、记录和集合),如下文所述。

在 PL/Perl 过程中,Perl 代码的任何返回值都会被忽略。

PL/Perl 还支持使用 DO 语句调用的匿名代码块

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

匿名代码块不接收参数,并且它可能返回的任何值都会被丢弃。否则,它的行为与函数一样。

注意

在 Perl 中使用命名嵌套子程序是危险的,尤其是当它们引用外部作用域中的词法变量时。因为 PL/Perl 函数被包装在一个子程序中,所以你放在其中的任何命名子程序都将被嵌套。通常,通过代码引用创建匿名子程序并调用它们要安全得多。有关更多信息,请参阅 perldiag man 页中的 Variable "%s" will not stay sharedVariable "%s" is not available 条目,或在互联网上搜索 perl nested named subroutine

命令 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;

注意

参数将从数据库的编码转换为 UTF-8 以在 PL/Perl 中使用,然后返回时再从 UTF-8 转换回数据库编码。

如果一个 SQL null 值被传递给一个函数,则参数值在 Perl 中将显示为 undefined。上面的函数定义对于 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 的值也必须是外部文本表示格式。例如,可以使用 encode_bytea 函数来转义二进制数据,以用于 bytea 类型的返回值。

一个特别重要的情况是布尔值。如前所述,bool 值的默认行为是将它们作为文本传递给 Perl,即 't''f'。这很成问题,因为 Perl 不会将 'f' 视为 false!可以通过使用 转换(参见 CREATE TRANSFORM)来改进这一点。由 bool_plperl 扩展提供了合适的转换。要使用它,请安装该扩展

CREATE EXTENSION bool_plperl;  -- or bool_plperlu for PL/PerlU

然后,对于接受或返回 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;

当应用此转换时,Perl 将看到 bool 参数为 1 或空,因此分别为真或假。如果函数结果的类型是 bool,它将根据 Perl 是否会将返回的值评估为真而为真或假。对于布尔查询参数和函数内部执行的 SPI 查询结果(第 43.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 数组作为已绑定的 PostgreSQL::InServer::ARRAY 对象传递。此对象可以被视为数组引用或字符串,从而允许与为 PostgreSQL 9.1 及以下版本编写的 Perl 代码向后兼容。例如

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 值返回。

同样,过程的输出参数可以作为哈希引用返回

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 函数还可以返回标量或复合类型的集合。通常,你会希望一次返回一行,既可以加快启动时间,也可以避免在内存中排队整个结果集。你可以使用 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 pragma,你有几个选择。对于临时全局使用,你可以将 plperl.use_strict SET 为 true。这将影响后续 PL/Perl 函数的编译,但不会影响当前会话中已编译的函数。对于永久全局使用,可以在 postgresql.conf 文件中将 plperl.use_strict 设置为 true。

对于特定函数中的永久使用,你可以简单地将

use strict;

放在函数体顶部。

如果你的 Perl 版本是 5.10.0 或更高版本,也可以使用 feature pragma进行 use

提交更正

如果你在文档中看到任何不正确的内容,与你对特定功能的体验不符,或者需要进一步澄清,请使用 此表单 来报告文档问题。