CREATE FUNCTION — 定義新的函式
CREATE [ OR REPLACE ] FUNCTIONname
( [ [argmode
] [argname
]argtype
[ { DEFAULT | = }default_expr
] [, ...] ] ) [ RETURNSrettype
| RETURNS TABLE (column_name
column_type
[, ...] ) ] { LANGUAGElang_name
| TRANSFORM { FOR TYPEtype_name
} [, ... ] | WINDOW | { IMMUTABLE | STABLE | VOLATILE } | [ NOT ] LEAKPROOF | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT } | { [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER } | PARALLEL { UNSAFE | RESTRICTED | SAFE } | COSTexecution_cost
| ROWSresult_rows
| SUPPORTsupport_function
| SETconfiguration_parameter
{ TOvalue
| =value
| FROM CURRENT } | AS 'definition
' | AS 'obj_file
', 'link_symbol
' |sql_body
} ...
CREATE FUNCTION
定義一個新的函式。CREATE OR REPLACE FUNCTION
將會建立一個新的函式,或取代現有的定義。若要能夠定義函式,使用者必須對該語言擁有 USAGE
權限。
如果包含綱要名稱,則該函式會在指定的綱要中建立。否則,它會在目前的綱要中建立。新函式的名稱不得與同一綱要中具有相同輸入引數類型的任何現有函式或程序相符。但是,不同引數類型的函式和程序可以共用一個名稱(這稱為多載)。
若要取代現有函式的目前定義,請使用 CREATE OR REPLACE FUNCTION
。無法透過這種方式變更函式的名稱或引數類型(如果您嘗試這麼做,實際上會建立一個新的、不同的函式)。此外,CREATE OR REPLACE FUNCTION
不會讓您變更現有函式的傳回類型。若要執行此操作,您必須刪除並重新建立函式。(當使用 OUT
參數時,表示您無法變更任何 OUT
參數的類型,除非刪除函式。)
當 CREATE OR REPLACE FUNCTION
用於取代現有函式時,函式的擁有權和權限不會變更。所有其他函式屬性都會被賦予命令中指定或暗示的值。您必須擁有函式才能取代它(包括成為擁有角色的成員)。
如果您刪除然後重新建立函式,則新函式與舊函式不是相同的實體;您必須刪除引用舊函式的現有規則、視窗、觸發程序等。使用 CREATE OR REPLACE FUNCTION
來變更函式定義,而不會破壞引用該函式的物件。此外,ALTER FUNCTION
可用於變更現有函式的大部分輔助屬性。
建立函式的使用者會成為函式的擁有者。
若要能夠建立函式,您必須對引數類型和傳回類型擁有 USAGE
權限。
請參閱 第 36.3 節,以取得有關撰寫函式的更多資訊。
name
要建立的函式的名稱(可選擇使用綱要限定)。
argmode
引數的模式:IN
、OUT
、INOUT
或 VARIADIC
。如果省略,則預設為 IN
。只有 OUT
引數可以跟在 VARIADIC
引數之後。此外,OUT
和 INOUT
引數不能與 RETURNS TABLE
表示法一起使用。
argname
引數的名稱。某些語言(包括 SQL 和 PL/pgSQL)允許您在函式主體中使用名稱。對於其他語言,輸入引數的名稱只是額外的文件,就函式本身而言;但是,您可以在呼叫函式時使用輸入引數名稱來提高可讀性(請參閱第 4.3 節)。無論如何,輸出引數的名稱非常重要,因為它定義了結果列類型中的欄位名稱。(如果您省略輸出引數的名稱,系統將選擇預設欄位名稱。)
argtype
函式引數的資料類型(可選擇使用綱要限定),如果有的話。引數類型可以是基本類型、複合類型或網域類型,也可以參考資料表欄位的類型。
根據實作語言,也可能允許指定「偽類型」,例如 cstring
。偽類型表示實際的引數類型未完全指定,或者超出普通 SQL 資料類型的集合。
欄位的類型可以透過寫入
來參考。有時候,使用此功能可以幫助函式獨立於資料表定義的變更。table_name
.column_name
%TYPE
default_expr
如果未指定參數,則用作預設值的表達式。該表達式必須可強制轉換為參數的引數類型。只有輸入(包括 INOUT
)參數可以具有預設值。所有跟在具有預設值的參數之後的輸入參數也必須具有預設值。
rettype
傳回資料類型(可選擇使用綱要限定)。傳回類型可以是基本類型、複合類型或網域類型,也可以參考資料表欄位的類型。根據實作語言,也可能允許指定「偽類型」,例如 cstring
。如果函式不應該傳回值,請指定 void
作為傳回類型。
當有 OUT
或 INOUT
參數時,可以省略 RETURNS
子句。 如果存在,則它必須與輸出參數所暗示的結果類型一致:如果有多个输出参数,则为 RECORD
;如果只有一个输出参数,则为与该输出參數相同的類型。
SETOF
修飾詞表示該函數將返回一組項目,而不是單個項目。
欄位的類型可以通過書寫
來引用。table_name
.column_name
%TYPE
column_name
RETURNS TABLE
語法中輸出欄位的名稱。 這實際上是宣告具名 OUT
參數的另一種方式,但 RETURNS TABLE
也暗示著 RETURNS SETOF
。
column_type
RETURNS TABLE
語法中輸出欄位的資料類型。
lang_name
函數所使用的語言名稱。 它可以是 sql
、c
、internal
,或是使用者定義的程序語言的名稱,例如 plpgsql
。 如果指定了 sql_body
,則預設值為 sql
。將名稱用單引號括起來已被棄用,並且需要區分大小寫。
TRANSFORM { FOR TYPE type_name
} [, ... ] }
列出函數呼叫應套用的轉換。轉換會在 SQL 類型和特定語言的資料類型之間進行轉換;請參閱 CREATE TRANSFORM。 程序語言的實作通常具有內建類型的硬式編碼知識,因此不需要在此處列出它們。 如果程序語言的實作不知道如何處理某種類型,並且沒有提供轉換,它將退回到預設行為以轉換資料類型,但這取決於實作。
WINDOW
WINDOW
表示該函數是一個視窗函數,而不是普通函數。 目前僅適用於用 C 編寫的函數。 更換現有的函數定義時,無法變更 WINDOW
屬性。
IMMUTABLE
STABLE
VOLATILE
這些屬性會告知查詢優化器有關函數的行為。 最多可以指定一個選擇。 如果沒有顯示任何一個,則 VOLATILE
是預設假設。
IMMUTABLE
表示該函數無法修改資料庫,並且在給定相同引數值時始終傳回相同的結果; 也就是說,它不會執行資料庫查找,也不會以其他方式使用未直接存在於其引數清單中的資訊。 如果給出了此選項,則可以使用函數值立即替換所有引數皆為常數的函數呼叫。
STABLE
表示該函數無法修改資料庫,並且在單個表格掃描中,它將針對相同的引數值始終如一地傳回相同的結果,但是其結果可能會跨 SQL 語句變更。 這適用於其結果取決於資料庫查找、參數變數(例如目前時區)等的函數的選擇。(這不適用於希望查詢當前命令修改的列的 AFTER
觸發器。) 另請注意,current_timestamp
函數系列符合 stable 的條件,因為它們的值在交易中不會變更。
VOLATILE
表示函數值甚至可以在單個表格掃描中變更,因此無法進行任何優化。 相對而言,很少有資料庫函數在本質上是不穩定的; 一些範例是 random()
、currval()
、timeofday()
。 但是請注意,任何具有副作用的函數都必須歸類為 volatile,即使其結果非常可預測,以防止呼叫被優化掉; 一個範例是 setval()
。
有關更多詳細資訊,請參閱 Section 36.7。
LEAKPROOF
LEAKPROOF
表示該函數沒有副作用。 除傳回值外,它不會透露有關其引數的任何資訊。 例如,對於某些引數值拋出錯誤訊息但不對其他引數值拋出錯誤訊息,或者在任何錯誤訊息中包含引數值的函數,都不是 leakproof。 這會影響系統如何針對使用 security_barrier
選項建立的視窗或啟用了列級別安全性的表格執行查詢。 系統將在查詢本身中包含非 leakproof 函數的任何使用者提供的條件之前,強制執行來自安全策略和安全屏障視窗的條件,以防止意外暴露資料。 標記為 leakproof 的函數和運算符被認為是可信任的,並且可以在來自安全策略和安全屏障視窗的條件之前執行。 此外,如果函數不帶引數或未從安全屏障視窗或表格傳遞任何引數,則不必將其標記為 leakproof 即可在安全條件之前執行。 請參閱 CREATE VIEW 和 Section 39.5。 只有超級使用者才能設定此選項。
CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT
CALLED ON NULL INPUT
(預設值)表示當某些引數為 null 時,將正常呼叫該函數。 然後,函數作者有責任在必要時檢查 null 值並做出適當的回應。
RETURNS NULL ON NULL INPUT
或 STRICT
表示當任何引數為 null 時,該函數始終傳回 null。 如果指定此參數,則在存在 null 引數時不會執行該函數; 相反,會自動假定為 null 結果。
[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER
SECURITY INVOKER
表示該函數將以呼叫它的使用者的權限執行。 這是預設值。 SECURITY DEFINER
指定該函數將以擁有它的使用者的權限執行。 有關如何安全地編寫 SECURITY DEFINER
函數的資訊,請參閱下文。
允許使用關鍵字 EXTERNAL
以符合 SQL 標準,但它是可選的,因為與 SQL 不同,此功能適用於所有函數,而不僅僅是外部函數。
PARALLEL
PARALLEL UNSAFE
表示該函數無法在平行模式下執行; SQL 語句中存在這樣的函數會強制執行循序執行計畫。 這是預設值。 PARALLEL RESTRICTED
表示該函數可以在平行模式下執行,但只能在平行群組組長處理程序中執行。 PARALLEL SAFE
表示該函數可以安全地在平行模式下執行,而沒有限制,包括在平行工作處理程序中。
如果函式會修改任何資料庫狀態、變更交易狀態(除了使用子交易進行錯誤恢復以外)、存取序列(例如,呼叫 currval
),或對設定進行持久性變更,則應將它們標記為平行不安全。如果函式存取暫存表格、用戶端連線狀態、游標、預備語句或系統無法在平行模式下同步的各種後端本機狀態(例如,除了群組領導者以外,不能執行 setseed
,因為另一個處理序所做的變更不會反映在領導者中),則應將它們標記為平行受限。一般來說,如果函式在受限或不安全時被標記為安全,或者如果函式在實際不安全時被標記為受限,則在平行查詢中使用時可能會引發錯誤或產生錯誤的答案。C 語言函式理論上可能會表現出完全未定義的行為(如果標記錯誤),因為系統無法保護自身免受任意 C 程式碼的侵害,但在大多數情況下,結果不會比任何其他函式更糟。如有疑問,應將函式標記為 UNSAFE
,這是預設值。
COST
execution_cost
一個正數,以 cpu_operator_cost 為單位,給出函式的估計執行成本。如果函式傳回一個集合,則這是每個傳回列的成本。如果未指定成本,則 C 語言和內部函式假定為 1 個單位,所有其他語言的函式假定為 100 個單位。較大的值會導致規劃器嘗試避免不必要地多次評估函式。
ROWS
result_rows
一個正數,給出規劃器應預期函式傳回的估計列數。僅當函式宣告為傳回一個集合時,才允許使用此選項。預設假設為 1000 列。
SUPPORT
support_function
用於此函式的規劃器支援函式的名稱(可選擇具有結構描述限定詞)。有關詳細資訊,請參閱第 36.11 節。您必須是超級使用者才能使用此選項。
configuration_parameter
value
SET
子句會導致在輸入函式時將指定的組態參數設定為指定的值,然後在函式退出時還原為其先前的值。SET FROM CURRENT
會將執行 CREATE FUNCTION
時參數的目前值儲存為輸入函式時要套用的值。
如果 SET
子句附加到函式,則在函式內部針對同一變數執行的 SET LOCAL
命令的效果僅限於該函式:組態參數的先前值仍會在函式退出時還原。但是,普通的 SET
命令(沒有 LOCAL
)會覆蓋 SET
子句,就像它會對先前的 SET LOCAL
命令所做的那樣:此類命令的效果將在函式退出後持續存在,除非目前交易回滾。
definition
定義函式的字串常數;含義取決於語言。它可以是內部函式名稱、物件檔案的路徑、SQL 命令或程序語言中的文字。
通常使用貨幣符號引號(請參閱第 4.1.2.4 節)編寫函式定義字串,而不是使用普通的單引號語法會很有幫助。如果沒有貨幣符號引號,則函式定義中的任何單引號或反斜線都必須透過將其加倍來進行跳脫。
obj_file
, link_symbol
當 C 語言原始程式碼中的函式名稱與 SQL 函式的名稱不同時,這種形式的 AS
子句用於可動態載入的 C 語言函式。字串 obj_file
是包含已編譯 C 函式的共享函式庫檔案的名稱,並按照 LOAD
命令進行解釋。字串 link_symbol
是函式的連結符號,也就是 C 語言原始程式碼中的函式名稱。如果省略連結符號,則假定它與正在定義的 SQL 函式的名稱相同。所有函式的 C 名稱必須不同,因此您必須為過載的 C 函式提供不同的 C 名稱(例如,將引數類型用作 C 名稱的一部分)。
當重複的 CREATE FUNCTION
呼叫參照同一個物件檔案時,每個工作階段僅載入該檔案一次。要卸載和重新載入檔案(可能在開發期間),請啟動一個新的工作階段。
sql_body
LANGUAGE SQL
函式的本體。這可以是單個語句
RETURN expression
或一個區塊
BEGIN ATOMICstatement
;statement
; ...statement
; END
這類似於將函式本體的文字寫為字串常數(請參閱上面的definition
),但存在一些差異:這種形式僅適用於 LANGUAGE SQL
,字串常數形式適用於所有語言。這種形式在函式定義時進行剖析,字串常數形式在執行時進行剖析;因此,這種形式不支援多型引數類型和其他在函式定義時無法解析的建構。這種形式會追蹤函式與函式本體中使用的物件之間的依賴關係,因此 DROP ... CASCADE
將正常工作,而使用字串常值形式可能會留下懸置的函式。最後,這種形式與 SQL 標準和其他 SQL 實作更相容。
PostgreSQL 允許函式過載;也就是說,只要它們具有不同的輸入引數類型,就可以對幾個不同的函式使用相同的名稱。無論您是否使用它,此功能在呼叫資料庫中的函式時都需要採取安全預防措施,在這些資料庫中,某些使用者不信任其他使用者;請參閱第 10.3 節。
如果兩個函式具有相同的名稱和輸入引數類型,則認為它們是相同的,忽略任何 OUT
參數。因此,例如,這些宣告衝突
CREATE FUNCTION foo(int) ... CREATE FUNCTION foo(int, out text) ...
具有不同引數類型清單的函式在建立時不會被視為衝突,但如果提供了預設值,則它們可能會在使用中發生衝突。例如,考慮
CREATE FUNCTION foo(int) ... CREATE FUNCTION foo(int, int default 42) ...
呼叫 foo(10)
將會失敗,因為無法明確判斷應該呼叫哪個函式。
完整的SQL類型語法可用於宣告函式的引數和回傳值。但是,括號中的類型修飾符(例如,numeric
類型的精度欄位)會被 CREATE FUNCTION
捨棄。因此,例如 CREATE FUNCTION foo (varchar(10)) ...
與 CREATE FUNCTION foo (varchar) ...
完全相同。
當使用 CREATE OR REPLACE FUNCTION
替換現有函式時,變更參數名稱有一些限制。您不能更改已經分配給任何輸入參數的名稱(雖然您可以為以前沒有名稱的參數添加名稱)。如果有多個輸出參數,您不能更改輸出參數的名稱,因為這會更改描述函式結果的匿名複合類型的欄位名稱。這些限制是為了確保替換函式時,現有函式的呼叫不會停止運作。
如果函式宣告為 STRICT
並具有 VARIADIC
引數,則嚴格性檢查會測試可變參數陣列 整體 是否為非空值。如果陣列具有空值元素,仍然會呼叫該函式。
使用 SQL 函式新增兩個整數
CREATE FUNCTION add(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;
以更符合 SQL 標準的風格編寫的相同函式,使用引數名稱和未引用的主體
CREATE FUNCTION add(a integer, b integer) RETURNS integer LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT RETURN a + b;
在 PL/pgSQL 中遞增一個整數,利用引數名稱
CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$ BEGIN RETURN i + 1; END; $$ LANGUAGE plpgsql;
回傳包含多個輸出參數的記錄
CREATE FUNCTION dup(in int, out f1 int, out f2 text) AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ LANGUAGE SQL; SELECT * FROM dup(42);
您可以使用顯式命名的複合類型,以更冗長的方式執行相同的操作
CREATE TYPE dup_result AS (f1 int, f2 text); CREATE FUNCTION dup(int) RETURNS dup_result AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ LANGUAGE SQL; SELECT * FROM dup(42);
另一種回傳多個欄位的方式是使用 TABLE
函式
CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text) AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$ LANGUAGE SQL; SELECT * FROM dup(42);
但是,TABLE
函式與前面的範例不同,因為它實際上回傳的是 一組 記錄,而不僅僅是一條記錄。
SECURITY DEFINER
函式由於 SECURITY DEFINER
函式是以擁有它的使用者的權限執行的,因此需要小心以確保該函式不會被濫用。為了安全起見,應設定 search_path 以排除任何不受信任的使用者可寫入的綱要。這樣可以防止惡意使用者建立遮蔽該函式預期使用的物件(例如,資料表、函式和運算子)的物件。在這方面特別重要的是臨時資料表綱要,預設情況下會首先搜尋它,並且通常可由任何人寫入。可以透過強制將臨時綱要放在最後搜尋來獲得安全的配置。為此,請將 pg_temp
寫為 search_path
中的最後一個條目。此函式說明了安全用法
CREATE FUNCTION check_password(uname TEXT, pass TEXT) RETURNS BOOLEAN AS $$ DECLARE passed BOOLEAN; BEGIN SELECT (pwd = $2) INTO passed FROM pwds WHERE username = $1; RETURN passed; END; $$ LANGUAGE plpgsql SECURITY DEFINER -- Set a secure search_path: trusted schema(s), then 'pg_temp'. SET search_path = admin, pg_temp;
此函式的目的是存取資料表 admin.pwds
。但是,如果沒有 SET
子句,或者只有提及 admin
的 SET
子句,則可以透過建立名為 pwds
的臨時資料表來顛覆該函式。
如果安全性定義者函式打算建立角色,並且如果它以非超級使用者身分執行,則也應使用 SET
子句將 createrole_self_grant
設定為已知值。
另一個需要記住的重點是,預設情況下,執行權限會授予給新建立函式的 PUBLIC
(有關更多資訊,請參閱第 5.8 節)。通常,您希望將安全性定義者函式的使用限制為僅某些使用者。為此,您必須撤銷預設的 PUBLIC
權限,然後有選擇地授予執行權限。為了避免新函式可供所有人存取的時間窗口,請在單個交易中建立它並設定權限。例如
BEGIN; CREATE FUNCTION check_password(uname TEXT, pass TEXT) ... SECURITY DEFINER; REVOKE ALL ON FUNCTION check_password(uname TEXT, pass TEXT) FROM PUBLIC; GRANT EXECUTE ON FUNCTION check_password(uname TEXT, pass TEXT) TO admins; COMMIT;
CREATE FUNCTION
命令是在 SQL 標準中定義的。PostgreSQL 實作可以以相容的方式使用,但具有許多擴充功能。相反,SQL 標準指定了許多未在 PostgreSQL 中實作的可選功能。
以下是重要的相容性問題
OR REPLACE
是一個 PostgreSQL 擴充功能。
為了與其他一些資料庫系統相容,argmode
可以寫在 argname
之前或之後。但只有第一種方式符合標準。
對於參數預設值,SQL 標準僅指定帶有 DEFAULT
關鍵字的語法。=
的語法用於 T-SQL 和 Firebird。
SETOF
修飾符是一個 PostgreSQL 擴充功能。
只有 SQL
被標準化為一種語言。
除了 CALLED ON NULL INPUT
和 RETURNS NULL ON NULL INPUT
之外的所有其他屬性都未標準化。
對於 LANGUAGE SQL
函式的主體,SQL 標準僅指定 sql_body
形式。
可以以一種既符合標準又可移植到其他實作的方式編寫簡單的 LANGUAGE SQL
函式。使用進階功能、最佳化屬性或其他語言的更複雜函式必然會以重要的方式特定於 PostgreSQL。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表格來報告文件問題。