Greenplum的PL/Perl语言扩展

Greenplum的PL/Perl语言扩展

关于Greenplum的PL/Perl

通过Greenplum数据库的PL/Perl扩展,用户可以用Perl写用户定义的函数,可以利用Perl先进的字符串处理操作和函数。 PL/Perl同时提供了可信和不可信的语言变体。

PL/Perl内嵌在用户的Greenplum数据库发布中。 Greenplum数据库的PL/Perl要求系统中每台数据库主机都要事先装好Perl。

参考PostgreSQL PL/Perl文档获取额外信息。

Greenplum数据库中PL/Perl限制

Greenplum数据库PL/Perl的限制包括:
  • Greenplum数据库不支持PL/Perl触发器。
  • PL/Perl函数不能直接相互调用。
  • SPI还没有完全的实现。
  • 如果用户使用spi_exec_query()获取非常大的数据集,用户应该要意识到它们都将进入内存中。 用户可以通过使用spi_query()/spi_fetchrow()来防止这个问题的发生。 一个相似的问题也可能发生,当集合返回函数通过一个return语句传递一个大量行的数据集到Greenplum数据库中。 对每一返回行使用return_next能够避免该问题的出现。
  • 当一个会话正常结束而不是由于一个致命错误结束时,PL/Perl会执行用户定义好的任何END块。 当前没有其它的动作执行(文件处理不会被自动地刷写并且对象也不会被自动销毁)。

可信/不可信语言

PL/Perl 包含了可信和不可信两种语言的变体。

PL/Perl可信语言被命名为plperl。 可信PL/Perl语言限制文件系统操作,和requireuse以及其它可能同操作系统或者数据库服务进程进行交互的语句。 有了这些限制,任何Greenplum用户能够创建和执行可信的plperl语言的函数。

PL/Perl不可信语言被命名为plperlu。 您不能使用plperlu不受信任的语言限制您创建的函数的操作。 只有数据库的超级用户才有权限创建有不可信PL/Perl语言的用户定义函数。 同时只有数据库的超级用户以及其他被显式授予特权的用户能够执行不可信PL/Perl的用户定义函数。

在解释器以及运行在单个进程中的多个解释器之间的通信方面,PL/Perl有限制。 请参考 PostgreSQL的可信与不可信PL/PerlPL/Perl文档获取更多的信息。

启用和移除PL/Perl支持

在用户能在数据库中创建以及执行一个PL/Perl的用户定义函数前,必须要在该数据库上注册PL/Perl语言。 要实现移除PL/Perl,用户必须显式的从每一个它注册的数据库中移除扩展。 您必须是数据库超级用户或所有者才能在Greenplum数据库中注册或删除受信任的语言。

Note: 只用数据库超级用户可能注册或者移除对不可信PL/Perl语言plperlu的支持。
在用户在数据库中开启或者移除PL/Perl支持之前,确保:
  • 用户的Greenplum数据库正在运行。
  • 用户已经执行(source)了greenplum_path.sh
  • 用户已经设置了$MASTER_DATA_DIRECTORY$GPHOME环境变量。

开启PL/Perl支持

对于要在其中启用PL/Perl的每个数据库,请使用SQL CREATE EXTENSION命令注册该语言。 例如,以gpadmin用户身份运行以下命令,为名为testdb的数据库注册可信PL/Perl语言:

$ psql -d testdb -c 'CREATE EXTENSION plperl;'

移除PL/Perl

要从数据库中删除对PL/Perl的支持,请运行SQL DROP EXTENSION命令。 例如,以gpadmin用户身份运行以下命令,从名为testdb的数据库中删除对可信PL/Perl语言的支持:

$ psql -d testdb -c 'DROP EXTENSION plperl;'

如果任何现有对象(如函数)依赖于语言,则默认命令将失败。 指定CASCADE选项也可以删除所有依赖对象,包括使用PL/Perl创建的函数。

用PL/Perl开发函数

您可以使用标准SQL CREATE FUNCTION语法定义PL/Perl函数。 PL/Perl用户定义函数的主体是普通的Perl代码。 PL/Perl解释器将此代码包装在Perl子例程中。

您还可以使用PL/Perl创建匿名代码块。 使用SQL DO命令调用的匿名代码块不接收任何参数,并且丢弃它可能返回的任何值。 否则,PL/Perl匿名代码块的行为就像一个函数。 只有数据库超级用户使用不受信任的plperlu语言创建匿名代码块。

CREATE FUNCTION命令的语法要求您将PL/Perl函数体写为字符串常量。 虽然使用美元引用更方便,但您可以选择使用转义字符串语法(E''),前提是您将函数体中使用的任何单引号和反斜杠加倍。

PL/Perl参数和结果在Perl中处理。 传递给PL/Perl函数的参数可通过@_数组访问。 使用return语句返回结果值,或者作为函数中计算的最后一个表达式返回结果值。 PL/Perl函数不能直接返回非标量类型,因为您在标量上下文中调用它。 您可以通过返回引用来返回PL/Perl函数中的非标量类型(如数组,记录和集)。

PL/Perl将null参数值视为“未定义”。 将STRICT关键字添加到LANGUAGE子句指示Greenplum数据库在任何输入参数为null时立即返回null。 当创建为STRICT时,函数本身不需要执行null检查。

以下PL/Perl函数使用STRICT关键字返回两个整数中的较大者,如果任何输入为null,则返回null:

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

SELECT perl_max( 1, 3 );
 perl_max
----------
        3
(1 row)

SELECT perl_max( 1, null );
 perl_max
----------

(1 row)

PL/Perl认为函数参数中的任何内容都不是对字符串的引用,即标准的Greenplum数据库外部文本表示。 提供给PL/Perl函数的参数值只是转换为文本形式的输入参数(就像它们已经被SELECT语句显示一样)。 如果函数参数不是普通的数字或文本类型,则必须将Greenplum数据库类型转换为Perl更易使用的形式。 相反,returnreturn_next语句接受任何字符串,该字符串是函数声明的返回类型的可接受输入格式。

有关其他信息,请参阅PostgreSQL PL/Perl函数和参数文档,包括复合类型和结果集操作。

内置PL/Perl函数

PL/Perl包含用于访问数据库的内置函数,包括用于准备和执行查询以及操作查询结果的函数。 该语言还包括用于错误记录和字符串操作的实用程序函数。

以下示例创建一个包含整数和文本列的简单表。 它创建一个PL/Perl用户定义函数,该函数接受输入字符串参数并调用spi_exec_query()内置函数来选择表的所有列和行。 该函数返回查询结果中的所有行,其中v列包含函数输入字符串。

CREATE TABLE test (
    i int,
    v varchar
);
INSERT INTO test (i, v) VALUES (1, 'first line');
INSERT INTO test (i, v) VALUES (2, 'line2');
INSERT INTO test (i, v) VALUES (3, '3rd line');
INSERT INTO test (i, v) VALUES (4, 'different');

CREATE OR REPLACE FUNCTION return_match(varchar) RETURNS SETOF test AS $$
    # store the input argument
    $ss = $_[0];

    # run the query
    my $rv = spi_exec_query('select i, v from test;');

    # retrieve the query status
    my $status = $rv->{status};

    # retrieve the number of rows returned in the query
    my $nrows = $rv->{processed};

    # loop through all rows, comparing column v value with input argument
    foreach my $rn (0 .. $nrows - 1) {
        my $row = $rv->{rows}[$rn];
        my $textstr = $row->{v};
        if( index($textstr, $ss) != -1 ) {
            # match!  return the row.
            return_next($row);
        }
    }
    return undef;
$$ LANGUAGE plperl EXECUTE ON MASTER ;

SELECT return_match( 'iff' );
 return_match
---------------
 (4,different)
(1 row)

有关可用函数的详细讨论,请参阅PostgreSQL PL/Perl内置函数文档

PL/Perl中的全局值

您可以使用全局哈希映射%_SHARED在当前会话的生命周期内在PL/Perl函数调用之间共享数据,包括代码引用。

以下示例使用%_SHARED在用户定义的set_var()get_var() PL/Perl函数之间共享数据:

CREATE OR REPLACE FUNCTION set_var(name text, val text) RETURNS text AS $$
    if ($_SHARED{$_[0]} = $_[1]) {
        return 'ok';
    } else {
        return "cannot set shared variable $_[0] to $_[1]";
    }
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION get_var(name text) RETURNS text AS $$
    return $_SHARED{$_[0]};
$$ LANGUAGE plperl;

SELECT set_var('key1', 'value1');
 set_var
---------
 ok
(1 row)

SELECT get_var('key1');
 get_var
---------
 value1
(1 row)

出于安全原因,PL/Perl为每个角色创建一个单独的Perl解释器。 这可以防止一个用户对另一个用户的PL/Perl函数的行为进行意外或恶意干扰。 每个这样的解释器都保留其自己的%_SHARED变量值和其他全局状态。 当且仅当它们由相同的SQL角色执行时,两个PL/Perl函数共享相同的%_SHARED值。

在某些情况下,您必须采取明确的步骤来确保PL/Perl函数可以在%_SHARED中共享数据。 例如,如果应用程序在单个会话中在多个SQL角色(通过SECURITY DEFINER函数,使用SET ROLE等)下执行, 请确保需要通信的函数由同一用户拥有,并将这些函数标记为SECURITY DEFINER

备注

在开发PL/Perl函数其它要考虑的:
  • PL/Perl内部使用UTF-8编码。 它将其他编码中提供的任何参数转换为UTF-8,并将UTF-8的返回值转换回原始编码。
  • 嵌套命名的PL/Perl子例程保留了与Perl相同的危险。
  • 只有不受信任的PL/Perl语言变体支持模块导入。 小心使用plperlu
  • 您在plperlu函数中使用的任何模块都必须在所有Greenplum数据库主机上的相同位置可用。