Chapter 37. PL/Perl - Perl 過程語言

Table of Contents
37.1. PL/Perl 函數和參數
37.2. 從 PL/Perl 裡存取資料庫
37.3. PL/Perl 裡的資料值
37.4. PL/Perl 裡的全局變量
37.5. 可信的和不可信的 PL/Perl
37.6. PL/Perl 觸發器
37.7. 限制及缺少的特性

PL/Perl 是一種可裝載的過程語言,透過它我們可以用 Perl 編程語言寫 PostgreSQL 函數。

要在特定資料庫裡安裝 PL/Perl,使用 createlang plperl dbname

提示: 如果某種編程語言安裝到 template1,那麼所有隨後建立的資料庫都會自動安裝這種語言。

注意: 使用源碼包的用戶必須在安裝過程中特別打開 PL/Perl 的製作。 (請參考 Section 14.1 獲取更多訊息)。 二進制包的用戶可能會在一些獨立的子包中找到 PL/Perl。

37.1. PL/Perl 函數和參數

要用 PL/Perl 語言建立一個函數,可以使用標準的語法:

CREATE FUNCTION funcname (argument-types) RETURNS return-type AS $$
    # PL/Perl 函數體
$$ LANGUAGE plperl;

函數體是普通 Perl 代碼。

CREATE FUNCTION 命令的語法要求把函數體寫成字串常量。 通常處理字串文本用美元符包圍更方便(參閱 Section 4.1.2.2), 如果您想使用傳統的單引號語法,您必須逃逸函數體裡面使用的單引號(')和反斜槓(\), 通常是寫雙份(參閱 Section 4.1.2.1 )。

參數和結果都是和任何其它 Perl 子過程裡那樣處理的: 參數是放在 @_ 裡傳遞的, 結果值是用 return 返回或者作為函數中最後計算的資料表達式的值返回。

比如,一個返回兩個整數中較大值的函數可以這麼寫:

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

如果給函數傳遞一個 SQL 空值 那麼其參數值將以 Perl 中 "undefined" 的形式出現。上面的函數定義在輸入為空值時的行為不是很正常(實際上, 它將資料表現得好像它們都是零一樣)。我們可以給函數定義增加 STRICT,讓 PostgreSQL 做一些更合理的事情:如果傳遞進來一個空值,那麼該函數則根本不會被調用, 而只是自動返回一個空值結果。另外,我們可以在函數體裡檢查未定義(undefined)的輸入。 比如,假設我們想收到一個空值和一個非空值參數的 perl_max 返回非空值的參數,而不是空值:

CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
    my ($a,$b) = @_;
    if (! defined $a) {
        if (! defined $b) { return undef; }
        return $b;
    }
    if (! defined $b) { return $a; }
    if ($a > $b) { return $a; }
    return $b;
$$ LANGUAGE plperl;

如上所述,要從 PL/Perl 函數中返回一個 SQL 空值, 我們可以返回一個未定義(undef)的數值。 不管該函數是否嚴格,我們都可以這麼做。

復合類型的參數是當做指向散列的引用傳遞給函數的。 散列的鍵字是復合類型的屬性名。下面是一個例子:

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 返回。

PL/Perl 函數也能返回標量或者符合類型的集合。要實現這個目的, 我們可以分別返回一個指向標量或者指向散列引用的數組的引用。下面是一些簡單的例子:

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();

請注意在您做這些事情的時候,Perl 會在內存裡製作整個數組; 因此這個技巧對特別大的結果集而言,伸縮性不太好。

PL/Perl 目前還沒有對域類型的完整支援: 它把域類型當作下層的標量類型看待。這就意味著跟域相關的約束將不會被強制。 對於函數參數而言,這不算個問題,但是如果您聲明 PL/Perl 函數返回一個域類型, 那麼就有危險了。