支援的版本:目前 (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 page 中 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 空值傳遞給函數,則引數值在 Perl 中將顯示為 未定義。上述函數定義對於空輸入的行為不太友善(事實上,它的行為就像它們是零一樣)。我們可以將 STRICT 新增至函數定義,以使 PostgreSQL 執行更合理的動作:如果傳遞空值,則根本不會呼叫該函數,而是會自動傳回空結果。或者,我們可以在函數主體中檢查未定義的輸入。例如,假設我們希望具有一個空值和一個非空值引數的 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;

如上所示,若要從 PL/Perl 函數傳回 SQL 空值,請傳回未定義的值。無論函數是否嚴格,都可以執行此操作。

函數引數中不是參考的任何內容都是字串,該字串採用相關資料類型的標準 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 或空,因此會正確地視為 true 或 false。如果函數結果的類型為 bool,則根據 Perl 是否會將傳回的值評估為 true,它將為 true 或 false。也會對在函數內部執行的 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 陣列以一個被 *bless* 的 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();

已宣告的結果資料類型中,任何在雜湊中不存在的欄位將會以 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 函式,但不會影響目前工作階段中已經編譯的函式。 對於永久的全域使用,您可以在 postgresql.conf 檔案中將 plperl.use_strict 設定為 true。

對於在特定函式中永久使用,您可以簡單地放入

use strict;

在函式主體的頂部。

如果您的 Perl 版本是 5.10.0 或更高版本,也可以 use feature 語法。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的地方,請使用此表單回報文件問題。