所有非當前“版本 1”介面編譯語言(包括使用者定義的語言以及用 SQL 編寫的函式)的函式呼叫,都會透過特定語言的呼叫處理程式。呼叫處理程式的職責是以有意義的方式執行函式,例如透過解釋提供的原始碼。本章概述瞭如何編寫新的過程語言的呼叫處理程式。
過程語言的呼叫處理程式是一個“普通”函式,必須使用版本 1 介面用 C 等編譯語言編寫,並向 PostgreSQL 註冊為不接受任何引數並返回 language_handler
型別。這個特殊的偽型別標識該函式為呼叫處理程式,並防止它在 SQL 命令中被直接呼叫。有關 C 語言呼叫約定和動態載入的更多詳細資訊,請參閱 第 36.10 節。
呼叫處理程式以與其他任何函式相同的方式呼叫:它接收指向包含引數值和有關被呼叫函式資訊的 FunctionCallInfoBaseData
struct
的指標,並期望返回一個 Datum
結果(如果希望返回 SQL null 結果,還可以設定 FunctionCallInfoBaseData
結構的 isnull
欄位)。呼叫處理程式與普通被呼叫函式之間的區別在於,FunctionCallInfoBaseData
結構中的 flinfo->fn_oid
欄位將包含實際被呼叫函式的 OID,而不是呼叫處理程式本身的 OID。呼叫處理程式必須使用此欄位來確定要執行哪個函式。此外,傳入的引數列表已根據目標函式的宣告進行設定,而不是根據呼叫處理程式的宣告進行設定。
呼叫處理程式需要從 pg_proc
系統目錄中獲取函式條目,並分析被呼叫函式的引數和返回型別。CREATE FUNCTION
命令中函式的 AS
子句將在 pg_proc
行的 prosrc
列中找到。這通常是過程語言的原始碼文字,但理論上也可以是其他內容,例如檔案的路徑名,或者任何其他告訴呼叫處理程式詳細操作的內容。
通常,同一個函式在一個 SQL 語句中會被呼叫很多次。呼叫處理程式可以透過使用 flinfo->fn_extra
欄位來避免對被呼叫函式資訊的重複查詢。該欄位初始為 NULL
,但可以由呼叫處理程式設定為指向被呼叫函式的資訊。在後續呼叫中,如果 flinfo->fn_extra
已經非 NULL
,則可以使用它,並跳過資訊查詢步驟。呼叫處理程式必須確保 flinfo->fn_extra
指向的記憶體至少在當前查詢結束之前都有效,因為 FmgrInfo
資料結構可能會保留那麼長時間。一種方法是在 flinfo->fn_mcxt
指定的記憶體上下文中分配額外資料;這些資料通常具有與 FmgrInfo
本身相同的生命週期。但處理程式也可以選擇使用生命週期更長的記憶體上下文,以便跨查詢快取函式定義資訊。
當過程語言函式作為觸發器呼叫時,不會像通常那樣傳遞引數,但 FunctionCallInfoBaseData
的 context
欄位指向一個 TriggerData
結構,而不是在普通函式呼叫中的 NULL
。語言處理程式應提供機制,以便過程語言函式可以訪問觸發器資訊。
在 src/test/modules/plsample
中提供了一個用 C 擴充套件編寫的過程語言處理程式的模板。這是一個實際的示例,演示了建立過程語言處理程式、處理引數和返回值的一種方法。
雖然提供呼叫處理程式足以建立一個最小的過程語言,但還可以選擇提供另外兩個函式,使語言更易於使用。它們是驗證器和內聯處理程式。可以提供驗證器,以便在 CREATE FUNCTION 期間進行語言特定的檢查。可以提供內聯處理程式,以便語言支援透過 DO 命令執行的匿名程式碼塊。
如果過程語言提供了驗證器,它必須被宣告為一個接受單個 oid
型別引數的函式。驗證器的結果被忽略,因此通常宣告其返回 void
。在建立或更新使用該過程語言編寫的函式後,將在 CREATE FUNCTION
命令結束時呼叫驗證器。傳入的 OID 是函式 pg_proc
行的 OID。驗證器必須以通常的方式獲取此行,並執行任何適當的檢查。首先,呼叫 CheckFunctionValidatorAccess()
來診斷使用者無法透過 CREATE FUNCTION
實現的顯式呼叫驗證器的行為。典型的檢查包括驗證函式的引數和返回型別是否被該語言支援,以及函式的體在該語言中是否語法正確。如果驗證器認為函式沒問題,它應該直接返回。如果發現錯誤,它應該透過標準的 ereport()
錯誤報告機制來報告。丟擲錯誤將強制進行事務回滾,從而阻止不正確的函式定義被提交。
驗證器函式通常應遵循 check_function_bodies 引數:如果該引數被停用,則應跳過任何昂貴或上下文相關的檢查。如果該語言支援編譯時程式碼執行,則驗證器必須禁止會觸發此類執行的檢查。特別是,pg_dump 會停用此引數,以便在不擔心函式體的副作用或對其他資料庫物件的依賴的情況下載入過程語言函式。(由於此要求,呼叫處理程式應避免假設驗證器已完全檢查了函式。提供驗證器的目的是不讓呼叫處理程式省略檢查,而是立即通知使用者 CREATE FUNCTION
命令中是否存在明顯錯誤。)雖然具體檢查什麼主要留給驗證器函式自行決定,但請注意,核心 CREATE FUNCTION
程式碼僅在 check_function_bodies
開啟時執行附加到函式的 SET
子句。因此,結果可能受 GUC 引數影響的檢查在 check_function_bodies
關閉時絕對應該跳過,以避免在恢復轉儲時出現誤報。
如果過程語言提供了內聯處理程式,它必須被宣告為一個接受單個 internal
型別引數的函式。內聯處理程式的返回值被忽略,因此通常宣告其返回 void
。當執行指定了過程語言的 DO
語句時,將呼叫內聯處理程式。實際傳遞的引數是指向 InlineCodeBlock
結構的指標,該結構包含有關 DO
語句引數的資訊,特別是要執行的匿名程式碼塊的文字。內聯處理程式應該執行此程式碼並返回。
建議將所有這些函式宣告以及 CREATE LANGUAGE
命令本身包裝在一個擴充套件中,以便使用一個簡單的 CREATE EXTENSION
命令即可安裝該語言。有關編寫擴充套件的資訊,請參閱 第 36.17 節。
標準發行版中包含的過程語言是編寫自己的語言處理程式的良好參考。檢視源樹的 src/pl
子目錄。CREATE LANGUAGE 參考頁面也包含一些有用的細節。
如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。