# 45.1.PL/Perl函数和参数

要使用PL/Perl语言创建函数,请使用标准创建函数语法:

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 $$
    # PL/Perl code
$$ LANGUAGE plperl;

匿名代码块不接收任何参数,它可能返回的任何值都将被丢弃。否则它的行为就像一个函数。

# 笔记

在Perl中使用命名嵌套子例程是危险的,尤其是当它们引用封闭范围内的词法变量时。因为PL/Perl函数被包装在一个子例程中,所以放置在其中的任何命名子例程都将被嵌套。一般来说,创建通过coderef调用的匿名子例程要安全得多。有关更多信息,请参阅的条目变量“%s”将不会保持共享状态变量“%s”不可用在perldiag手册页中,或在Internet上搜索“perl嵌套命名子例程”。

的语法创建函数命令要求函数体作为字符串常量写入。使用美元报价通常是最方便的(参见第4.1.2.4节)对于字符串常量。如果选择使用转义字符串语法E“,任何单引号必须加倍(')还有反斜杠(\)在函数体中使用(请参见第4.1.2.1节).

参数和结果的处理与任何其他Perl子例程中的处理一样:参数被传入@_,并返回一个结果值回来或作为函数中计算的最后一个表达式。

例如,返回两个整数值中较大值的函数可以定义为:

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空值传递给函数时,参数值在Perl中显示为“未定义”。上面的函数定义在使用空输入时不会表现得很好(事实上,它的作用就像它们是零一样)。我们可以加上严格的对函数定义进行修改,使PostgreSQL做一些更合理的事情:如果传递了null值,则根本不会调用函数,只会自动返回null结果。或者,我们可以检查函数体中未定义的输入。例如,假设我们想要perl_max使用一个null和一个nonnull参数来返回nonnull参数,而不是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空值,请返回未定义的值。无论函数是否严格,都可以这样做。

函数参数中任何非引用的内容都是字符串,它位于相关数据类型的标准PostgreSQL外部文本表示形式中。对于普通的数字或文本类型,Perl只会做正确的事情,程序员通常不必担心它。然而,在其他情况下,需要将参数转换为在Perl中更可用的形式。例如用茶解码函数可用于转换类型为的参数二进制数据转换为未替换的二进制文件。

同样,传递回PostgreSQL的值必须采用外部文本表示格式。例如用茶编码函数可用于转义类型为的返回值的二进制数据二进制数据.

一个特别重要的例子是布尔值。如前所述,的默认行为布尔值是作为文本传递给Perl的,因此“不”“f”.这是有问题的,因为 Perl 不会处理'f'假的!可以通过使用“变换”来改进问题(参见创建转换)。合适的变换由bool_plperl延期。要使用它,请安装扩展:

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

然后使用转变接受或返回的 PL/Perl 函数的函数属性布尔, 例如:

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

应用此变换时,布尔Perl 将把参数视为1或空,因此正确或正确。如果函数结果是类型布尔, 根据 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 数组作为有福的传递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);

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

散列中不存在的声明结果数据类型中的任何列都将作为空值返回。

类似地,过程的输出参数可以作为哈希引用返回:

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, 你必须把返回或更好)返回 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();

如果您想使用严格的使用你的代码的编译指示你有几个选择。对于临时全球使用,您可以 plperl.use_strict为真。这将影响 PL/Perl 函数的后续编译,但不会影响当前会话中已编译的函数。对于永久全球使用,您可以设置plperl.use_strict在真实的postgresql.conf文件。

对于特定功能的永久使用,您可以简单地输入:

use strict;

在函数体的顶部。

特征pragma 也可用于采用如果您的 Perl 是 5.10.0 或更高版本。