37.5. 表達式

所有在PL/pgSQL 語句裡使用的表達式都是用服務器的普通SQL執行器進行處理的。 那些看上去象是包含常量的表達式實際上需要運行時計算(比如,用于 timestamp 類型的 'now'),因此除了NULL關鍵字之外, 對于PL/pgSQL分析器而言, 實際上是不可能標識真正的常量數值的。 所有表達式都是使用 SPI 管理器通過在內部執行一個查詢計算的

SELECT expression

計算的時候,出現PL/pgSQL變量標識符的地方先被參數代替,然後變量的實際值放在參數數組裡傳遞給執行器。 這樣就允許SELECT的執行計劃只需要準備一次,並且在隨後的計算中復用。

PostgreSQL 的主分析器做的類型檢查對常量數值的代換有一些副作用。 詳細說來就是下面這兩個函數做的事情有些區別:

CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS '
    DECLARE
        logtxt ALIAS FOR $1;
    BEGIN
        INSERT INTO logtable VALUES (logtxt, ''now'');
        RETURN ''now'';
    END;
' LANGUAGE plpgsql;

CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
    DECLARE
        logtxt ALIAS FOR $1;
        curtime timestamp;
    BEGIN
        curtime := ''now'';
        INSERT INTO logtable VALUES (logtxt, curtime);
        RETURN curtime;
    END;
' LANGUAGE plpgsql;

logfunc1() 的實例裡, PostgreSQL 的主分析器在為 INSERT 準備執行計劃的時候知道字串 'now' 應該解釋成 timestamp 類型,因為 logtable 的目標字段就是該類型。所以,它會在這個時候從這個字串中計算一個常量, 然後在該服務器的整個生存期中的所有 logfunc1 調用中使用這個常量。不消說,這可不是程序員想要的。

logfunc2裡, PostgreSQL 的主分析器並不知道 now 應該轉換成什麼類型, 因此它返回一個包含字符串 now 的類型為 text 的數據值。 在隨後給局部變量curtime賦值時, PL/pgSQL解釋器通過調用 text_outtimestamp_in 把這個字符串轉換成 timestamp 類型的變量。 因此,計算出的時戳就會按照程序員希望的那樣在每次執行的時候都更新。

記錄變量的易變性天性在這種結合上提出了一個問題。 在一個記錄變量在語句或者表達式中使用時, 該字段的數據類型在同一個表達式的不同調用期間不能修改, 因為該表達式準備使用的是運行第一次到達該表達式時出現的數據類型。 在寫處理超過一個表的事件的觸發器過程的時候一定要把這個記住。(必要時可以用EXECUTE繞開這個問題。)