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
系列函式符合穩定條件,因為它們的值在事務中不會改變。
VOLATILE
表示函式值即使在單個表掃描中也可能發生變化,因此無法進行最佳化。在這種意義上,相對較少的資料庫函式是易變的;一些例子是 random()
、currval()
、timeofday()
。但請注意,任何有副作用的函式都必須歸類為易變的,即使其結果相當可預測,以防止呼叫被最佳化掉;例如 setval()
。
更多詳細資訊請參閱 第 36.7 節。
LEAKPROOF
LEAKPROOF
表示函式沒有副作用。除了透過返回值外,它不透露有關其引數的任何資訊。例如,一個函式在某些引數值下丟擲錯誤訊息而不是其他引數值,或者在任何錯誤訊息中包含引數值,則不是 leakproof 的。這會影響系統如何執行對使用 security_barrier
選項建立的檢視或啟用行級安全策略的表執行查詢。系統將首先強制執行來自安全策略和安全屏障檢視的條件,然後再執行使用者提供的來自查詢本身的、包含非 leakproof 函式的條件,以防止資料意外洩露。被標記為 leakproof 的函式和運算子被假定為可信的,並且可能在安全策略和安全屏障檢視中的條件之前執行。此外,不帶引數或未從安全屏障檢視或表中傳遞任何引數的函式不需要被標記為 leakproof 即可在安全條件之前執行。請參閱 CREATE VIEW 和 第 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
)或對設定進行了永續性更改,則應將函式標記為 parallel unsafe。如果它們訪問臨時表、客戶端連線狀態、遊標、預編譯語句或後端本地的雜項狀態(系統無法在並行模式下同步,例如 setseed
只能由組長執行,因為其他程序的更改不會反映在領導者中),則應將其標記為 parallel restricted。總的來說,如果一個函式被標記為 safe,但實際上是 restricted 或 unsafe,或者被標記為 restricted,但實際上是 unsafe,那麼在並行查詢中使用它時,可能會導致錯誤或產生錯誤的結果。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
AS 子句的這種形式用於動態可載入的 C 語言函式,當 C 語言原始碼中的函式名與 SQL 函式名不同時。字串 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
引數,則 strictness 檢查會測試該變長陣列整體是否為非 NULL。如果陣列包含 NULL 元素,函式仍將被呼叫。
使用 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
子句,或者 SET
子句只提到 admin
,那麼函式可能會被建立名為 pwds
的臨時表所顛覆。
如果 security definer 函式打算建立角色,並且它以非超級使用者的身份執行,那麼 createrole_self_grant
也應該使用 SET
子句設定為一個已知值。
需要注意的另一點是,預設情況下,新建立函式的執行許可權授予給 PUBLIC
(有關更多資訊,請參閱 第 5.8 節)。通常您希望限制 security definer 函式的使用僅限於某些使用者。要做到這一點,您必須撤銷預設的 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。
如果您在文件中看到任何不正確、與您對特定功能的使用經驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。