2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 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 頁中的 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 null 值被傳遞給一個函式,則引數值在 Perl 中將顯示為 undefined。上面的函式定義對於 null 輸入將不會有很好的表現(實際上,它會將它們當作零處理)。我們可以向函式定義新增 STRICT 來讓 PostgreSQL 做一些更合理的事情:如果傳遞了一個 null 值,函式根本不會被呼叫,只會自動返回一個 null 結果。或者,我們可以在函式體中檢查未定義輸入。例如,假設我們希望 perl_max 在有一個 null 引數和一個非 null 引數時返回非 null 引數,而不是 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 null 值,請返回一個未定義的值。無論函式是否嚴格,都可以做到這一點。

函式引數中任何不是引用的東西都是字串,它以相關的內建資料型別的標準 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 或空,因此分別為真或假。如果函式結果的型別是 bool,它將根據 Perl 是否會將返回的值評估為真而為真或假。對於布林查詢引數和函式內部執行的 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 陣列作為已繫結的 PostgreSQL::InServer::ARRAY 物件傳遞。此物件可以被視為陣列引用或字串,從而允許與為 PostgreSQL 9.1 及以下版本編寫的 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 pragma,你有幾個選擇。對於臨時全域性使用,你可以將 plperl.use_strict SET 為 true。這將影響後續 PL/Perl 函式的編譯,但不會影響當前會話中已編譯的函式。對於永久全域性使用,可以在 postgresql.conf 檔案中將 plperl.use_strict 設定為 true。

對於特定函式中的永久使用,你可以簡單地將

use strict;

放在函式體頂部。

如果你的 Perl 版本是 5.10.0 或更高版本,也可以使用 feature pragma進行 use

提交更正

如果你在文件中看到任何不正確的內容,與你對特定功能的體驗不符,或者需要進一步澄清,請使用 此表單 來報告文件問題。