CREATE FUNCTION
用于定義一個新函數。
簡介
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
特權。
語法
CREATE [ OR REPLACE ] FUNCTION
name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
[ RETURNS rettype
| RETURNS TABLE ( column_name column_type [, ...] ) ]
{ LANGUAGE lang_name
| TRANSFORM { FOR TYPE type_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 }
| COST execution_cost
| ROWS result_rows
| SUPPORT support_function
| SET configuration_parameter { TO value | = value | FROM CURRENT }
| AS 'definition'
| AS 'obj_file', 'link_symbol'
} ...
參數
name
要創建的函數的名稱(可以被模式限定)。
argmode
一個參數的模式:IN
、OUT
、INOUT
或者VARIADIC
。如果省略,默認為IN
。只有OUT
參數能跟在一個VARIADIC
參數后面。還有,OUT
和INOUT
參數不能和RETURNS TABLE
符號一起使用。
argname
一個參數的名稱。一些語言(包括 SQL 和 PL/pgSQL)讓你在函數體中使用該名稱。對于其他語言,一個輸入參數的名字只是額外的文字(就該函數本身所關心的來說)。但是你可以在調用一個函數時使用輸入參數名來提高可讀性。在任何情況下,輸出參數的名稱是有意義的,因為它定義了結果行類型中的列名(如果忽略一個輸出參數的名稱,系統將選擇一個默認的列名)。
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
用以實現該函數的語言的名稱??梢允?code data-tag="code" class="code">sql、c
、internal
或者一個用戶定義的過程語言的名稱,例如plpgsql
。不推薦用單引號包圍該名稱,并且要求區分大小寫。
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()
。
LEAKPROOF
LEAKPROOF
表示該函數沒有副作用。它不會泄露有關其參數的信息(除了通過返回值)。例如,一個只對某些參數值拋出錯誤消息而對另外一些卻不拋出錯誤的函數不是防泄漏的,一個把參數值包括在任何錯誤消息中的函數也不是防泄漏的。這會影響系統如何執行在使用security_barrier
選項創建的視圖或者開啟了行級安全性的表上執行查詢。對于包含有非防泄漏函數的查詢,系統將在任何來自查詢本身的用戶提供條件之前強制來自安全策略或者安全屏障的條件,防止無意中的數據暴露。被標記為防泄漏的函數和操作符被假定是可信的,并且可以在安全性策略和安全性屏障視圖的條件之前被執行。此外,沒有參數的函數或者不從安全屏障視圖或表傳遞任何參數的函數不一定要被標記為防泄漏的。
CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT
CALLED ON NULL INPUT
(默認)表示在某些參數為空值時應正常調用該函數。如果有必要,函數的作者應該負責檢查空值并且做出適當的相應。
RETURNS NULL ON NULL INPUT
或STRICT
表示只要其任意參數為空值,該函數就會返回空值。如果指定了這個參數,當有空值參數時該函數不會被執行,而是自動返回一個空值結果。
[
EXTERNAL
] SECURITY INVOKER
[
EXTERNAL
] SECURITY DEFINER
SECURITY INVOKER
表示要用調用該函數的用戶的特權來執行它。這是默認值。SECURITY DEFINER
指定要用擁有該函數的用戶的特權來執行該函數。
為了符合 SQL,允許使用關鍵詞EXTERNAL
。但是它是可選的,因為與 SQL 中不同,這個特性適用于所有函數而不僅是那些外部函數。
PARALLEL
PARALLEL UNSAFE
表示該函數不能在并行模式中運行并且 SQL 語句中存在一個這樣的函數會強制使用順序執行計劃。這是默認選項。PARALLEL RESTRICTED
表示該函數能在并行模式中運行,但是其執行被限制在并行組的領導者中。PARALLEL SAFE
表示該函數對于在并行模式中運行是安全的并且不受限制。
如果函數修改任何數據庫狀態、會使用子事務之類的方式改變事務、訪問序列或者對設置(如setval
)做出持久性的更改,它們就應該被標記為并行不安全。如果它們訪問臨時表、客戶端連接狀態、游標、預備語句或者系統無法在并行模式中同步的本地后端狀態(例如setseed
只能在組領導者中執行,因為另一個進程所作的更改不會在領導者中被反映出來),它們應該被標為并行受限。通常,如果一個函數是受限的或者不安全的卻被標成了安全,或者它本來是不安全的卻被標成了受限,在并行查詢中執行時它可能會拋出錯誤或者產生錯誤的答案。如果被錯誤的標記, C 語言函數理論上可能展現出完全無法定義的行為,因為系統沒有辦法保護自己不受任意的 C 代碼影響,但是在大部分情況下其結果也不會比任何其他函數差到哪里去。如果有疑問,函數應該被標為UNSAFE
,這也是默認值。
COST
execution_cost
一個給出該函數的估計執行代價的正數,單位是 cpu_operator_cost。如果該函數返回一個集合,這就是每個被返回行的代價。如果沒有指定代價,對 C 語言和內部函數會指定為 1 個單位,對其他語言的函數則會指定為 100 單位。更大的值會導致規劃器嘗試避免對該函數的不必要的過多計算。
ROWS
result_rows
一個正數,它給出規劃器期望該函數返回的行數估計。只有當該函數被聲明為返回一個集合時才允許這個參數。默認假設為 1000 行。
SUPPORT
support_function
用于此函數的planner support function的名稱(可選的模式限定)。你必須是超級用戶才能使用此選項。
configuration_parameter
value
SET
子句導致進入該函數時指定配置參數將被設置為指定值。并且在該函數退出時恢復到該參數之前的值。SET FROM CURRENT
會把CREATE FUNCTION
被執行時該參數的當前值保存為進入該函數時將被應用的值。
如果一個SET
子句被附加到一個函數,那么在該函數內為同一個變量執行的SET LOCAL
命令會被限制于該函數:在函數退出時該配置參數之前的值仍會被恢復。不過,一個普通的SET
命令(沒有LOCAL
)會覆蓋SET
子句,更像一個之前的SET LOCAL
命令所做的那樣:這種命令的效果在函數退出后將會持續,除非當前事務被回滾。
definition
一個定義該函數的字符串常量,其含義取決于語言。它可以是一個內部函數名、一個對象文件的路徑、一個 SQL 命令或者用一種過程語言編寫的文本。
obj_file
,
link_symbol
當 C 語言源代碼中該函數的名稱與 SQL 函數的名稱不同時,這種形式的AS
子句被用于動態可載入 C 語言函數。字符串 obj_file
是包含編譯好的 C 函數的動態庫文件的名稱,它會由 LOAD 命令解析。字符串 link_symbol
是該函數的鏈接符號,也就是該函數在 C 語言源代碼中的名稱。如果省略鏈接符號,它將被假定為要定義的 SQL 函數的名稱。所有函數的 C 名稱都必須不同,因此必須為重載的 C 函數給出不同的 C 名稱(例如把參數類型作為 C 名稱的一部分)。
在重復調用引用同一對象文件的CREATE FUNCTION
時,對每個會話該文件只會被載入一次。要卸載并且重新裝載該文件(可能是在開發期間),需要開始一個新會話。
重載
PolarDB允許函數重載,也就是說同一個名稱可以被用于多個不同的函數,只要它們具有可區分的輸入參數類型。
如果兩個函數具有相同的名稱和輸入參數類型,它們被認為相同(不考慮任何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 類型語法用于聲明一個函數的參數和返回值。不過,CREATE FUNCTION
會拋棄帶圓括號的類型修飾符(例如類型numeric
的精度域)。例如CREATE FUNCTION foo (varchar(10)) ...
和CREATE FUNCTION foo (varchar) ...
完全一樣。
在用CREATE OR REPLACE FUNCTION
替換一個現有函數時,對于更改參數名是有限制的。不能更改已經分配給任何輸入參數的名稱(不過可以給之前沒有名稱的參數增加名稱)。如果有多于一個輸出參數,不能更改輸出參數的名稱,因為可能會改變描述函數結果的匿名組合類型的列名。這些限制是為了確保函數被替換時,已有的對該函數的調用不會停止工作。
如果一個被聲明為STRICT
的函數帶有一個VARIADIC
參數,會嚴格檢查該可變數組作為一個整體是否為非空。如果該數組有空值元素,該函數仍將被調用。
示例
這里是一些小例子,它們可以幫你了解函數創建。
CREATE FUNCTION add(integer, integer) RETURNS integer
AS 'select $1 + $2;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
在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
-- 設置一個安全的 search_path:受信的模式,然后是 'pg_temp'。
SET search_path = admin, pg_temp;
這個函數的目的是為了訪問表admin.pwds
。但是如果沒有SET
子句或者帶有SET
子句卻只提到admin
,該函數會變成創建一個名為pwds
的臨時表。
默認情況下,會為新創建的函數給PUBLIC
授予執行特權。你常常會希望把安全定義器函數的使用限制在某些用戶中。要這樣做,你必須收回默認的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;