PL/pgSQL 是一種面向塊的語言。一個塊像下面這樣定義
[<<label>>] [DECLARE declarations] BEGIN statements END;在一個語句塊(statement section)裡面可以有任意個子語句塊。子語句塊可以用於屏蔽語句塊外面的變數。在語句塊前面的聲明段(declarations section)裡定義的變數在每次進入語句塊時都初始化為它們的預設值,而不是每次函數調用時初始化一次。
我們一定不要把 PL/pgSQL 裡的分組語句 BEGIN/END 和用於事務控制的資料庫命令搞混了。函數和觸發器不能開始或提交一個事務並且Postgres 也沒有嵌套事務的用法。
定義一個指明了的基本型態的變數。如果變數被定義為 CONSTANT,該變數值就不能修改。如果聲明了 NOT NULL,則賦予一個 NULL 值給該變數將導致一個運行時錯誤。因為所有變數的預設值都是 SQL NULL,因而所有定義為 NOT NULL 的變數都必須有指明的預設值。
預設值在每次函數調用時都被計算。所以賦予 'now' 給一個型態為
datetime
的變數將導致變數擁有實際函數調用時的時間,而不是函數被編譯為字節碼的時間。
定義一個帶有指定表的結構的行。表必須是一個資料庫中現存的表或視圖的名稱。行的字段是通過點定義來訪問的。函數的參數可以是復合型態(表裡面完整的行)。這時,對應的標識 $n 將是一個行型態(rowtype),但是它必須用下面描述的 ALIAS 命令取個別名。在一行裡只有用戶字段可以被訪問,而 OID 或其他系統字段都不可訪問(因為行可能來自一個視圖,而視圖的行並沒有有用的系統字段)。
行型態(rowtype)字段繼承表中 char() 等數據型態的字段尺寸或精度。
記錄(Records)類似於行型態(rowtypes),但它們沒有預定義的結構。它們在選擇和 FOR 循環中使用,用於從一個 SELECT 操作中保存一個具體的資料庫的行。該條記錄可以在不同的選擇中使用。當沒有實際的行存在於其中時試圖訪問一條記錄或賦予某個變數的值將導致一個運行時錯誤產生。
觸發器裡的 NEW 和 OLD 行是作為記錄傳給程序的。這一點是必要的,因為 Postgres
裡同樣的觸發器程序可以為不同的表控制觸發器事件。
為了讓程式碼更具可讀性,我們可以為函數的位置參數定義一個別名。
改變變數,記錄或者行的名稱。這一點當 NEW 或 OLD 在觸發器裡要被做為另一個名字引用時很有用。
variable%TYPE
class.field%TYPE
class 是現存表或視圖,field 是一個字段的名稱。
使用 class.field%TYPE 導致 PL/pgSQL 在後端的生存期裡第一次調用該函數時查看該字段的定義。如果我們有一個表有一個字段定義為 char(20) 而且一些 PL/pgSQL 函數在局部變數裡處理該字段的內容。現在我們發現 char(20) 不夠長,把表倒出來,刪除,重新把有問題的字段定義為 char(40) 然後恢復數據。哈-他忘了函數。函數裡的運算將把數值裁斷成20個字符。但是如果他們用 class.field%TYPE 做定義,它們將自動控制字段的長度的變化或新表把該字段定義為 text 型態。
SELECT expression來計算。在表達式裡出現的變數標識都被參數和放在參數數組裡傳入執行器的變數實際值所代替。所有在 PL/pgSQL 函數裡用到的表達式都只準備和儲存一次。
Postgres 的主分析器做的型態檢查對轉換常數有一些副作用。詳細說來,下面的兩個函數有一些區別
CREATE FUNCTION logfunc1 (text) RETURNS datetime AS ' DECLARE logtxt ALIAS FOR $1; BEGIN INSERT INTO logtable VALUES (logtxt, ''now''); RETURN ''now''; END; ' LANGUAGE 'plpgsql';和
CREATE FUNCTION logfunc2 (text) RETURNS datetime AS ' DECLARE logtxt ALIAS FOR $1; curtime datetime; BEGIN curtime := ''now''; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; ' LANGUAGE 'plpgsql';在 logfunc1() 裡,Postgres 的主分析器在準備 INSERT 的規劃時認為,字符串 'now' 應該解釋為 datetime,因為 logtable 的目標字段是該型態。因此,這時它將從刈谹成一個常數並且在所有後端生存期內所有對 logfunc1() 的調用時使用該常數。不用說這可不是程式員希望的。
在 logfunc2() 裡,Postgres 的主分析器並不知道 'now' 應該轉換的型態,因此它返回一個包含字符串 'now' 的 text 數據型態。在給局部變數 curtime 賦值時,PL/pgSQL 解釋器通過調用 text_out()和 datetime_in() 把這個字符串轉換成 datetime 型態的變數。
這個 Postgres 主分析器的型態檢查是在 PL/pgSQL 接近完成的時候實現的。在版本 6.3 和版本 6.4 之間有所不同並且影響所有使用 SPI 管理器的規劃特性的函數。使用上面提到的局部變數的方法是目前能讓 PL/pgSQL 對那些數值正確解釋的唯一方法。
如果在表達式或語句裡用到記錄(record)字段,字段的數據型態在同一個表達式的不同調用中不應該改變。書寫控制多個表的觸發器程序時應該注意這一點。
給一個變數或行/記錄賦值用下面方法
identifier := expression;如果表達式的結果數據型態和變數數據型態不一致,或者變數具有已知的尺寸/精度(例如 char(20)),結果值將隱含的被 PL/pgSQL 字節碼解釋器用結果型態的輸出函數和變數型態的輸入函數轉換。要注意這樣做可能潛在地導致型態輸入函數產生的運行時錯誤。
下面方法可以把一個完整的選擇放到一條記錄或者行裡
SELECT expressions INTO target FROM ...;target 可以是一條記錄,一行變數或一個逗號分隔的變數列表和記錄/行字段(域)。
如果一行或者一個變數列表當做目標,選擇的數值必須與目標列的結構完全一樣,否則會產生一條運行時錯誤。FORM 關鍵字可以跟隨著任意可在 SELECT 語句裡使用的有效的資格(條件),分組,排序等。
一個名為 FOUND 的特殊的布爾型態的變數可以在 SELECT INTO 之後立刻用於檢查賦值是否成功。
SELECT * INTO myrec FROM EMP WHERE empname = myname; IF NOT FOUND THEN RAISE EXCEPTION ''employee % not found'', myname; END IF;如果選擇返回多行,只有第一行被放到目標域裡面。其他所有都被悄悄地丟棄掉了。
所有在 Prostgres 資料庫裡的函數返回一個值。因此,通常的調用函數的方法是執行一條 SELECT 查詢或者做一個賦值(導致一個 PL/pgSQL 的內部 SELECT )。但是有時候我們對函數結果並不感興趣。
PERFORM query將在 SPI 管理器上執行一個 'SELECT query' 然後丟棄結果。像局部變數這樣的標識仍然代入參數。
RETURN expression函數結束並且表達式 expression 的值將返回給上層執行器。函數的返回值不能取消定義。如果控制到達一個函數體的最頂層而沒有碰到一個 RETURN 語句,將產生一個運行時錯誤。
表達式的結果將被自動轉換成函數返回型態--像我們在賦值裡描述的那樣。
像上面的例子指明的那樣,有一個 RAISE 語句可以丟出一條資訊到 Postgres elog 機制裡面。
RAISE level ''format'' [, identifier [...]];在格式 ( format )裡面,“%” 用做一個後面跟隨的逗號分隔的標識的占位符。可能的級別(level)是 DEBUG (安靜地停止運行的資料庫),NOTICE (向資料庫書寫日誌並向前端客戶應用發送資訊)和 EXCEPTION (向資料庫書寫日誌並且退出事務 )。
IF expression THEN statements [ELSE statements] END IF;表達式 expression 必須返回一個最起碼可以轉換成布爾型態的數值。
有許多循環的型態。
[<<label>>] LOOP statements END LOOP;一個無條件循環必須用一條 EXIT 語句顯式終止。可選的標記 label 可以被 EXIT 用於嵌套循環用於聲明應該結束哪一層循環。
[<<label>>] WHILE expression LOOP statements END LOOP;一個條件循環在表達式 expression 為真時執行。
[<<label>>] FOR name IN [ REVERSE ] expression .. expression LOOP statements END LOOP;一個在某一整數範圍內執行的循環,變數 name 自動做為整數型態創建,並且只存在於循環內部。兩個表達式給出循環範圍的區間,它們只是在進入循環後才被計算出數值。循環步進值總是 1。
[<<label>>] FOR record | row IN select_clause LOOP statements END LOOP;記錄或行被賦予了 select 子句來的所有行,並且語句 statement 對每一條記錄/行都運行一次。如果用 EXIT 語句退出循環,最後賦值的行仍然在循環後可以被訪問。
EXIT [ label ] [ WHEN expression ];如果沒有標記 label ,最內層的循環將被結束並且 END LOOP 後面的語句將被接著執行。如果給出了標記 label ,該標記必須是當前或者上層嵌套的循環塊。那麼命名的循環或者語句塊被終止並且控制落到循環/語句塊的對應 END 的後面一條語句。
數據型態是 text 的數組﹔是 CREATE TRIGGER 語句裡的參數。下標從 0 開始記數,並且可以由一個表達式來表示。非法下標(< 0 或 >= tg_nargs)導致一個 NULL 值的返回。
我們可以'鉤'在錯誤機制上來提示這種情況的發生。但是目前我們沒有能力告訴(用戶)是什麼導致了退出(輸入/輸出轉換錯誤,浮點數錯誤,分析錯誤)。並且此時的資料庫後端可能處在一種不連貫的狀態,所以退回到上層執行器或執行更多的命令可能摧毀整個資料庫。而且此時事務退出的資訊可能已經發送給了客戶端應用,所以繼續操作沒有任何意義。
因此,PL/pgSQL 在函數或觸發器操作時遇到退出的唯一一項操作是在 DEBUG 級別運行時輸出一些附加的日誌資訊,報告在哪個函數和在那裡(行號和語句型態)出了錯。