42.1. Функции PL/Perl и аргументы#
42.1. Функции PL/Perl и аргументы #
Для создания функции на языке PL/Perl используйте стандартный синтаксис CREATE FUNCTION:
CREATE FUNCTIONfuncname
(argument-types
) RETURNSreturn-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 или выше.