42.1. Функции PL/Perl и аргументы#

42.1. Функции PL/Perl и аргументы

42.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 обернута в подпрограмму, любая именованная подпрограмма, которую вы помещаете внутрь, будет вложенной. В целом, намного безопаснее создавать анонимные подпрограммы , которые вызываются через ссылку на код. Дополнительную информацию см. в записях для Variable "%s" will not stay shared и Variable "%s" is not available в perldiag man page, или искать в Интернете perl вложенные именованные подпрограммы .

Синтаксис команды 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 Если аргумент передается в функцию, значение аргумента будет отображаться как undefined в Perl. Вышеуказанное определение функции не будет вести себя очень хорошо с нулевыми значениями (фактически, оно будет вести себя так, как будто они равны нулю). Можно добавить STRICT к определению функции, чтобы Tantor BE делал что-то более разумное: если передается нулевое значение, функция вообще не будет вызываться, а просто автоматически вернет нулевой результат. В качестве альтернативы, можно проверить наличие неопределенных значений в теле функции. Например, предположим, что мы хотим, чтобы perl_max с одним нулевым и одним ненулевым аргументом возвращал ненулевой аргумент, а не нулевое значение:

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;

Как показано выше, чтобы вернуть SQL-значение NULL из функции PL/Perl, верните неопределенное значение. Это можно сделать независимо от того, является ли функция строгой или нет.

Все, что не является ссылкой в аргументе функции, является строкой, которая представляет соответствующий тип данных внешним текстовым представлением стандартного Tantor BE. В случае обычных числовых или текстовых типов Perl просто сделает правильное преобразование, и программисту обычно не придется беспокоиться об этом. Однако в других случаях аргумент должен быть преобразован в форму, которая более удобна в Perl. Например, функция decode_bytea может использоваться для преобразования аргумента типа bytea в неэкранированный двоичный формат.

Аналогично, значения, передаваемые обратно в Tantor BE, должны быть в формате внешнего текстового представления. Например, функция encode_bytea может быть использована для экранирования двоичных данных возвращаемого значения типа bytea.

Один особенно важный случай - это логические значения. Как только было сказано, поведение по умолчанию для значений типа bool заключается в том, что они передаются в Perl в виде текста, то есть 't' или 'f'. Это проблематично, поскольку Perl не будет рассматривать 'f' как ложное значение! Возможно улучшить ситуацию, используя трансформацию (см. CREATE TRANSFORM). Подходящие трансформации предоставляются расширением bool_plperl. Чтобы использовать его, установите расширение:

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

Затем используйте атрибут функции TRANSFORM для функции PL/Perl, которая принимает или возвращает bool, например:

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

Когда применяется этот преобразователь, аргументы типа bool будут восприниматься Perl как 1 или пустые, соответственно истинные или ложные. Если результат функции имеет тип bool, он будет true или ложным в зависимости от того, как Perl оценивает возвращаемое значение как истинное. Аналогичные преобразования также выполняются для логических аргументов запросов и результатов SPI-запросов, выполняемых внутри функции (Раздел 42.3.1).

Perl может возвращать массивы Tantor BE в виде ссылок на массивы 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, который является объектом с типом данных "blessed". Этот объект может быть использован как ссылка на массив или строка, что позволяет обеспечить обратную совместимость с Perl-кодом, написанным для версий PostgreSQL ниже 9.1. Например:

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.

Аргументы типа Composite передаются в функцию в виде ссылок на хеши. Ключи хеша являются именами атрибутов составного типа. Вот пример:

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 с вашим кодом, у вас есть несколько вариантов. Для временного глобального использования вы можете использовать команду SET и установить значение plperl.use_strict в true. Это повлияет на последующие компиляции функций PL/Perl, но не на функции, уже скомпилированные в текущей сессии. Для постоянного глобального использования вы можете установить значение plperl.use_strict в true в файле postgresql.conf.

Для постоянного использования в конкретных функциях вы можете просто поместить:

use strict;

в начале тела функции.

Прагма feature также доступна для use, если ваш Perl имеет версию 5.10.0 или выше.