W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
所有對用不是當(dāng)前“版本 1”接口(用于編譯型語言)語言編寫的函數(shù)(這包括用戶定義的過程語言中的函數(shù)、SQL 編寫的函數(shù))的調(diào)用都會流經(jīng)一個用于指定語言的調(diào)用處理器。調(diào)用處理器負責(zé)以一種有意義的方式執(zhí)行該函數(shù),例如通過解釋所提供的源文本。本章勾勒了如何編寫一個新的過程語言調(diào)用處理器的輪廓。
一個過程語言的調(diào)用處理器是一個“正常的”函數(shù),它必須以一種編譯型語言(如 C)編寫、使用版本-1接口并且在PostgreSQL中注冊為無參數(shù)且返回類型language_handler
。這種特殊的偽類型標(biāo)識該函數(shù)是一個調(diào)用處理器并且阻止它在 SQL 命令中被直接調(diào)用。關(guān)于 C
語言調(diào)用慣例和動態(tài)載入的更多細節(jié),請見第 37.10 節(jié)。
調(diào)用處理器的調(diào)用方式和其他任何函數(shù)相同:它接收一個包含參數(shù)值和有關(guān)被調(diào)用函數(shù)信息的FunctionCallInfoBaseData
結(jié)構(gòu)
,并且它被期望返回一個Datum
結(jié)果(并且如果它希望返回一個 SQL 空值結(jié)果,它可能設(shè)置FunctionCallInfoBaseData
結(jié)構(gòu)的
isnull
域)。一個調(diào)用處理器和一個普通被調(diào)用函數(shù)之間的區(qū)別是FunctionCallInfoBaseData
結(jié)構(gòu)的flinfo->fn_oid
域?qū)徽{(diào)用的實際函數(shù)的 OID,而不是調(diào)用處理器本身。調(diào)用處理器必須使用這個域來決定要執(zhí)行哪個函數(shù)。同樣,被傳遞的參數(shù)列表已經(jīng)被根據(jù)目標(biāo)函數(shù)而不是調(diào)用處理器的聲明被設(shè)置好。
調(diào)用處理器負責(zé)從pg_proc
系統(tǒng)目錄中取得該函數(shù)的項并且分析被調(diào)用函數(shù)的參數(shù)和返回類型。該函數(shù)的CREATE FUNCTION
命令的AS
子句可以在pg_proc
行的prosrc
列中被找到。通常這是過程語言中的源文本,但是在理論上它可以是其他某種東西,例如一個文件的路徑名或其他任何詳細告訴調(diào)用處理器做什么的東西。
同一個函數(shù)在每個 SQL 命令中常常被調(diào)用多次。一個調(diào)用處理器可以通過使用flinfo->fn_extra
域來避免重復(fù)查找關(guān)于被調(diào)用函數(shù)的信息。這個域最初將為NULL
,但是可以被調(diào)用處理器設(shè)置為指向關(guān)于被調(diào)用函數(shù)的信息。在后續(xù)調(diào)用中,如果flinfo->fn_extra
已經(jīng)為非-NULL
,則它可以被使用并且信息查找步驟將被跳過。調(diào)用處理器必須確保
flinfo->fn_extra
被指向直到當(dāng)前查詢的末尾都存活的內(nèi)存,因為一個FmgrInfo
數(shù)據(jù)接口可以被保持那么久。一種方式是在flinfo->fn_mcxt
指定的內(nèi)存上下文中分配額外的數(shù)據(jù);這樣的數(shù)據(jù)通常必須與FmgrInfo
本身具有相同的生命期。但是處理器也可以選擇使用一個生存時間更長的內(nèi)存上下文,這樣它能夠在查詢之間緩存函數(shù)定義信息。
當(dāng)一個過程語言函數(shù)被作為一個觸發(fā)器調(diào)用時,不會有參數(shù)通過常用方式被傳遞,但是FunctionCallInfoBaseData
的context
域指向一個TriggerData
結(jié)構(gòu)而不是為NULL
(就像它在一個普通函數(shù)調(diào)用中那樣)。一個語言處理器應(yīng)該為過程語言函數(shù)提供機制來得到觸發(fā)器信息。
這是一個用 C 編寫的過程語言處理器的模板:
#include "postgres.h"
#include "executor/spi.h"
#include "commands/trigger.h"
#include "fmgr.h"
#include "access/heapam.h"
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(plsample_call_handler);
Datum
plsample_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
if (CALLED_AS_TRIGGER(fcinfo))
{
/*
* Called as a trigger function
*/
TriggerData *trigdata = (TriggerData *) fcinfo->context;
retval = ...
}
else
{
/*
* Called as a function
*/
retval = ...
}
return retval;
}
要完成該調(diào)用處理器,只需要加入幾千行代碼來替代點號即可。
在將處理器函數(shù)編譯成一個可載入模塊(第 37.10.5 節(jié))后,接著用下列命令注冊例子過程語言:
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
AS 'filename
'
LANGUAGE C;
CREATE LANGUAGE plsample
HANDLER plsample_call_handler;
盡管提供一個調(diào)用處理器對于創(chuàng)建一個最小過程語言已經(jīng)足夠,還可以提供其他兩個可選的函數(shù)來讓該函數(shù)更易用。它們是驗證器和內(nèi)聯(lián)處理器。一個驗證器可以被提供來允許在CREATE FUNCTION期間完成語言相關(guān)的檢查。一個內(nèi)聯(lián)處理器可以被提供來允許語言支持通過 DO 命令執(zhí)行匿名代碼塊。
如果一個驗證器被一個過程語言提供,它必須被聲明為一個采用一個單一oid
類型參數(shù)的函數(shù)。該驗證器的結(jié)果被忽略,因為它通常被聲明為返回void
。驗證器將在一個已經(jīng)創(chuàng)建了或更新了一個以該過程語言編寫的函數(shù)的CREATE FUNCTION
命令之后被調(diào)用。被傳入的 OID 是函數(shù)的pg_proc
行的
OID。驗證器必須用通常方式取得這個行,并且做任何合適的檢查。首先,調(diào)用CheckFunctionValidatorAccess()
來診斷對用戶通過CREATE FUNCTION
無法達到的驗證器的顯式調(diào)用。典型的檢查包括驗證函數(shù)的參數(shù)和結(jié)果類型是否被該語言支持,以及該函數(shù)體在該語言中語法是否正確。如果驗證器發(fā)現(xiàn)該函數(shù)是好的,它應(yīng)該只是返回。如果它發(fā)現(xiàn)一個錯誤,它應(yīng)該通過通常的
ereport()
錯誤報告機制報告該錯誤。拋出一個錯誤將強制一次事務(wù)回滾并且因此阻止不正確的函數(shù)定義被提交。
驗證器函數(shù)通常應(yīng)該尊重check_function_bodies參數(shù):如果它被關(guān)閉那么任何代價大的或上下文敏感的檢查應(yīng)該被跳過。如果該語言提供了編譯時代碼執(zhí)行,驗證器必須抑制可能引起這種執(zhí)行的檢查。特別地,這個參數(shù)會被pg_dump關(guān)閉,這樣它能載入過程語言函數(shù)而不用擔(dān)心副作用或那些函數(shù)體對其他數(shù)據(jù)庫對象的依賴(因為這種要求,調(diào)用處理器應(yīng)該避免假設(shè)驗證器已經(jīng)完整地檢查過該函數(shù)。擁有一個驗證器的要點不是讓調(diào)用處理器忽略檢查,而是如果在一個
CREATE FUNCTION
命令中發(fā)現(xiàn)明顯錯誤時立即提示用戶)。然而究竟檢查什么的選擇大部分都留給了驗證器函數(shù),注意當(dāng)check_function_bodies
為打開時,核心CREATE FUNCTION
代碼只執(zhí)行附加到一個函數(shù)的SET
子句。 因此,為了避免在重新載入一個轉(zhuǎn)儲時的偽失敗,當(dāng)check_function_bodies
為關(guān)閉時,結(jié)果可能會被
GUC 參數(shù)影響的檢查絕對應(yīng)當(dāng)被跳過。
如果一個過程語言提供了一個內(nèi)聯(lián)處理器,它必須被聲明為一個采用一個單一internal
類型參數(shù)的函數(shù)。內(nèi)聯(lián)處理器的結(jié)果會被忽略,因此它通常被聲明為返回void
。當(dāng)一個DO
語句被調(diào)用執(zhí)行指定過程語言時,內(nèi)聯(lián)處理器將被調(diào)用。實際被傳遞的參數(shù)是一個指向一個InlineCodeBlock
結(jié)構(gòu)的指針,它包含有關(guān)
DO
語句參數(shù)的而信息,特別是將被執(zhí)行的匿名代碼塊的文本。內(nèi)聯(lián)處理器應(yīng)該執(zhí)行該代碼并返回。
我們推薦你包裝所有這些函數(shù)聲明,以及CREATE LANGUAGE
命令本身到一個extension中,這樣一個簡單的CREATE EXTENSION
命令就足以安裝該語言。關(guān)于編寫擴展的信息請見第 37.17 節(jié)。
在嘗試編寫你自己的語言處理器時,包括在標(biāo)準發(fā)布中的過程語言是很好的參考??纯丛创a樹中的src/pl
子目錄。CREATE LANGUAGE參考頁也有一些有用的細節(jié)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: