CREATE FUNCTION
CREATE FUNCTION
用于定義一個新函數(shù)。
簡介
CREATE FUNCTION
定義一個新函數(shù)。CREATE OR REPLACE FUNCTION
將創(chuàng)建一個新函數(shù)或者替換一個現(xiàn)有的函數(shù)。要定義一個函數(shù),用戶必須具有該語言上的USAGE
特權(quán)。
如果包括了一個模式名,那么該函數(shù)會被創(chuàng)建在指定的模式中。否則,它會被創(chuàng)建在當(dāng)前模式中。新函數(shù)的名稱不能匹配同一個模式中具有相同輸入?yún)?shù)類型的任何現(xiàn)有函數(shù)或過程。不過,不同參數(shù)類型的函數(shù)和過程能夠共享一個名字(這被稱作重載)。
要替換一個現(xiàn)有函數(shù)的當(dāng)前定義,可以使用CREATE OR REPLACE FUNCTION
。但不能用這種方式更改函數(shù)的名稱或者參數(shù)類型(如果嘗試這樣做,實際上就會創(chuàng)建一個新的不同的函數(shù))。還有,CREATE OR REPLACE FUNCTION
將不會讓你更改一個現(xiàn)有函數(shù)的返回類型。要這樣做,你必須先刪除再重建該函數(shù)(在使用OUT
參數(shù)時,這意味著除了刪除函數(shù)之外無法更改任何OUT
參數(shù)的類型)。
當(dāng)CREATE OR REPLACE FUNCTION
被用來替換一個現(xiàn)有的函數(shù),該函數(shù)的擁有權(quán)和權(quán)限不會改變。所有其他的函數(shù)屬性會按照該命令中所指定的或者隱含的來賦值。必須擁有(包括成為擁有角色的成員)該函數(shù)才能替換它。
如果你刪除并且重建一個函數(shù),新函數(shù)將和舊的不一樣,你將必須刪掉引用舊函數(shù)的現(xiàn)有規(guī)則、視圖、觸發(fā)器等。使用CREATE OR REPLACE FUNCTION
更改一個函數(shù)定義不會破壞引用該函數(shù)的對象。還有,ALTER FUNCTION
可以被用來更改一個現(xiàn)有函數(shù)的大部分輔助屬性。
創(chuàng)建該函數(shù)的用戶將成為該函數(shù)的擁有者。
要創(chuàng)建一個函數(shù),你必須擁有參數(shù)類型和返回類型上的USAGE
特權(quán)。
語法
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'
} ...
參數(shù)
name
要創(chuàng)建的函數(shù)的名稱(可以被模式限定)。
argmode
一個參數(shù)的模式:IN
、OUT
、INOUT
或者VARIADIC
。如果省略,默認(rèn)為IN
。只有OUT
參數(shù)能跟在一個VARIADIC
參數(shù)后面。還有,OUT
和INOUT
參數(shù)不能和RETURNS TABLE
符號一起使用。
argname
一個參數(shù)的名稱。一些語言(包括 SQL 和 PL/pgSQL)讓你在函數(shù)體中使用該名稱。對于其他語言,一個輸入?yún)?shù)的名字只是額外的文字(就該函數(shù)本身所關(guān)心的來說)。但是你可以在調(diào)用一個函數(shù)時使用輸入?yún)?shù)名來提高可讀性。在任何情況下,輸出參數(shù)的名稱是有意義的,因為它定義了結(jié)果行類型中的列名(如果忽略一個輸出參數(shù)的名稱,系統(tǒng)將選擇一個默認(rèn)的列名)。
argtype
該函數(shù)參數(shù)(如果有)的數(shù)據(jù)類型(可以是模式限定的)。參數(shù)類型可以是基本類型、組合類型或者域類型,或者可以引用一個表列的類型。
根據(jù)實現(xiàn)語言,也可以允許指定cstring
之類的“偽類型”。偽類型表示實際參數(shù)類型沒有被完整指定或者不屬于普通 SQL 數(shù)據(jù)類型集合。
可以寫 table_name
.
column_name
%TYPE
來引用一列的類型。使用這種特性有時可以幫助創(chuàng)建一個不受表定義更改影響的函數(shù)。
default_expr
如果參數(shù)沒有被指定值時要用作默認(rèn)值的表達(dá)式。該表達(dá)式必須能被強(qiáng)制為該參數(shù)的參數(shù)類型。只有輸入(包括INOUT
)參數(shù)可以具有默認(rèn)值。所有跟隨在一個具有默認(rèn)值的參數(shù)之后的輸入?yún)?shù)也必須有默認(rèn)值。
rettype
返回數(shù)據(jù)類型(可能被模式限定)。返回類型可以是一種基本類型、組合類型或者域類型,也可以引用一個表列的類型。根據(jù)實現(xiàn)語言,也可以允許指定cstring
之類的“偽類型”。如果該函數(shù)不會返回一個值,可以指定返回類型為void
。
當(dāng)有OUT
或者INOUT
參數(shù)時,可以省略RETURNS
子句。如果存在,該子句必須和輸出參數(shù)所表示的結(jié)果類型一致:如果有多個輸出參數(shù),則為RECORD
,否則與單個輸出參數(shù)的類型相同。
SETOF
修飾符表示該函數(shù)將返回一個項的集合而不是一個單一項。
可以寫 table_name
.
column_name
%TYPE
來引用一列的類型。
column_name
RETURNS TABLE
語法中一個輸出列的名稱。這實際上是另一種聲明OUT
參數(shù)的方法,不過RETURNS TABLE
也隱含了RETURNS SETOF
。
column_type
RETURNS TABLE
語法中的輸出列的數(shù)據(jù)類型。
lang_name
用以實現(xiàn)該函數(shù)的語言的名稱。可以是sql
、c
、internal
或者一個用戶定義的過程語言的名稱,例如plpgsql
。不推薦用單引號包圍該名稱,并且要求區(qū)分大小寫。
TRANSFORM {{ FOR TYPE
type_name
} [, ... ] }
一個由轉(zhuǎn)換構(gòu)成的列表,對該函數(shù)的調(diào)用適用于它們。轉(zhuǎn)換在 SQL 類型和語言相關(guān)的數(shù)據(jù)類型之間進(jìn)行變換,詳見 CREATE TRANSFORM。過程語言實現(xiàn)通常把有關(guān)內(nèi)建類型的知識硬編碼在代碼中,因此那些不需要列舉在這里。如果一種過程語言實現(xiàn)不知道如何處理一種類型并且沒有轉(zhuǎn)換被提供,它將回退到一種默認(rèn)的行為來轉(zhuǎn)換數(shù)據(jù)類型,但是這取決于具體實現(xiàn)。
WINDOW
WINDOW
表示該函數(shù)是一個窗口函數(shù)而不是一個普通函數(shù)。當(dāng)前只用于用 C 編寫的函數(shù)。在替換一個現(xiàn)有函數(shù)定義時,不能更改WINDOW
屬性。
IMMUTABLE
STABLE
VOLATILE
這些屬性告知查詢優(yōu)化器該函數(shù)的行為。最多只能指定其中一個。如果這些都不出現(xiàn),則會默認(rèn)為VOLATILE
。
IMMUTABLE
表示該函數(shù)不能修改數(shù)據(jù)庫并且對于給定的參數(shù)值總是會返回相同的值。也就是說,它不會做數(shù)據(jù)庫查找或者使用沒有在其參數(shù)列表中直接出現(xiàn)的信息。如果給定合格選項,任何用全常量參數(shù)對該函數(shù)的額調(diào)用可以立刻用該函數(shù)值替換。
STABLE
表示該函數(shù)不能修改數(shù)據(jù)庫,并且對于相同的參數(shù)值,它在一次表掃描中將返回相同的結(jié)果。但是這種結(jié)果在不同的 SQL 語句執(zhí)行期間可能會變化。對于那些結(jié)果依賴于數(shù)據(jù)庫查找、參數(shù)變量(例如當(dāng)前時區(qū))等的函數(shù)來說,這是合適的(對希望查詢被當(dāng)前命令修改的行的AFTER
觸發(fā)器不適合)。還要注意current_timestamp
函數(shù)族適合被標(biāo)記為穩(wěn)定,因為它們的值在一個事務(wù)內(nèi)不會改變。
VOLATILE
表示該函數(shù)的值在一次表掃描中都有可能改變,因此不能做優(yōu)化。在這種意義上,相對較少的數(shù)據(jù)庫函數(shù)是不穩(wěn)定的,一些例子是random()
、currval()
、timeofday()
。但是注意任何有副作用的函數(shù)都必須被分類為不穩(wěn)定的,即便其結(jié)果是可以預(yù)測的,這是為了調(diào)用被優(yōu)化掉。一個例子是setval()
。
LEAKPROOF
LEAKPROOF
表示該函數(shù)沒有副作用。它不會泄露有關(guān)其參數(shù)的信息(除了通過返回值)。例如,一個只對某些參數(shù)值拋出錯誤消息而對另外一些卻不拋出錯誤的函數(shù)不是防泄漏的,一個把參數(shù)值包括在任何錯誤消息中的函數(shù)也不是防泄漏的。這會影響系統(tǒng)如何執(zhí)行在使用security_barrier
選項創(chuàng)建的視圖或者開啟了行級安全性的表上執(zhí)行查詢。對于包含有非防泄漏函數(shù)的查詢,系統(tǒng)將在任何來自查詢本身的用戶提供條件之前強(qiáng)制來自安全策略或者安全屏障的條件,防止無意中的數(shù)據(jù)暴露。被標(biāo)記為防泄漏的函數(shù)和操作符被假定是可信的,并且可以在安全性策略和安全性屏障視圖的條件之前被執(zhí)行。此外,沒有參數(shù)的函數(shù)或者不從安全屏障視圖或表傳遞任何參數(shù)的函數(shù)不一定要被標(biāo)記為防泄漏的。
CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT
CALLED ON NULL INPUT
(默認(rèn))表示在某些參數(shù)為空值時應(yīng)正常調(diào)用該函數(shù)。如果有必要,函數(shù)的作者應(yīng)該負(fù)責(zé)檢查空值并且做出適當(dāng)?shù)南鄳?yīng)。
RETURNS NULL ON NULL INPUT
或STRICT
表示只要其任意參數(shù)為空值,該函數(shù)就會返回空值。如果指定了這個參數(shù),當(dāng)有空值參數(shù)時該函數(shù)不會被執(zhí)行,而是自動返回一個空值結(jié)果。
[
EXTERNAL
] SECURITY INVOKER
[
EXTERNAL
] SECURITY DEFINER
SECURITY INVOKER
表示要用調(diào)用該函數(shù)的用戶的特權(quán)來執(zhí)行它。這是默認(rèn)值。SECURITY DEFINER
指定要用擁有該函數(shù)的用戶的特權(quán)來執(zhí)行該函數(shù)。
為了符合 SQL,允許使用關(guān)鍵詞EXTERNAL
。但是它是可選的,因為與 SQL 中不同,這個特性適用于所有函數(shù)而不僅是那些外部函數(shù)。
PARALLEL
PARALLEL UNSAFE
表示該函數(shù)不能在并行模式中運行并且 SQL 語句中存在一個這樣的函數(shù)會強(qiáng)制使用順序執(zhí)行計劃。這是默認(rèn)選項。PARALLEL RESTRICTED
表示該函數(shù)能在并行模式中運行,但是其執(zhí)行被限制在并行組的領(lǐng)導(dǎo)者中。PARALLEL SAFE
表示該函數(shù)對于在并行模式中運行是安全的并且不受限制。
如果函數(shù)修改任何數(shù)據(jù)庫狀態(tài)、會使用子事務(wù)之類的方式改變事務(wù)、訪問序列或者對設(shè)置(如setval
)做出持久性的更改,它們就應(yīng)該被標(biāo)記為并行不安全。如果它們訪問臨時表、客戶端連接狀態(tài)、游標(biāo)、預(yù)備語句或者系統(tǒng)無法在并行模式中同步的本地后端狀態(tài)(例如setseed
只能在組領(lǐng)導(dǎo)者中執(zhí)行,因為另一個進(jìn)程所作的更改不會在領(lǐng)導(dǎo)者中被反映出來),它們應(yīng)該被標(biāo)為并行受限。通常,如果一個函數(shù)是受限的或者不安全的卻被標(biāo)成了安全,或者它本來是不安全的卻被標(biāo)成了受限,在并行查詢中執(zhí)行時它可能會拋出錯誤或者產(chǎn)生錯誤的答案。如果被錯誤的標(biāo)記, C 語言函數(shù)理論上可能展現(xiàn)出完全無法定義的行為,因為系統(tǒng)沒有辦法保護(hù)自己不受任意的 C 代碼影響,但是在大部分情況下其結(jié)果也不會比任何其他函數(shù)差到哪里去。如果有疑問,函數(shù)應(yīng)該被標(biāo)為UNSAFE
,這也是默認(rèn)值。
COST
execution_cost
一個給出該函數(shù)的估計執(zhí)行代價的正數(shù),單位是 cpu_operator_cost。如果該函數(shù)返回一個集合,這就是每個被返回行的代價。如果沒有指定代價,對 C 語言和內(nèi)部函數(shù)會指定為 1 個單位,對其他語言的函數(shù)則會指定為 100 單位。更大的值會導(dǎo)致規(guī)劃器嘗試避免對該函數(shù)的不必要的過多計算。
ROWS
result_rows
一個正數(shù),它給出規(guī)劃器期望該函數(shù)返回的行數(shù)估計。只有當(dāng)該函數(shù)被聲明為返回一個集合時才允許這個參數(shù)。默認(rèn)假設(shè)為 1000 行。
SUPPORT
support_function
用于此函數(shù)的planner support function的名稱(可選的模式限定)。你必須是超級用戶才能使用此選項。
configuration_parameter
value
SET
子句導(dǎo)致進(jìn)入該函數(shù)時指定配置參數(shù)將被設(shè)置為指定值。并且在該函數(shù)退出時恢復(fù)到該參數(shù)之前的值。SET FROM CURRENT
會把CREATE FUNCTION
被執(zhí)行時該參數(shù)的當(dāng)前值保存為進(jìn)入該函數(shù)時將被應(yīng)用的值。
如果一個SET
子句被附加到一個函數(shù),那么在該函數(shù)內(nèi)為同一個變量執(zhí)行的SET LOCAL
命令會被限制于該函數(shù):在函數(shù)退出時該配置參數(shù)之前的值仍會被恢復(fù)。不過,一個普通的SET
命令(沒有LOCAL
)會覆蓋SET
子句,更像一個之前的SET LOCAL
命令所做的那樣:這種命令的效果在函數(shù)退出后將會持續(xù),除非當(dāng)前事務(wù)被回滾。
definition
一個定義該函數(shù)的字符串常量,其含義取決于語言。它可以是一個內(nèi)部函數(shù)名、一個對象文件的路徑、一個 SQL 命令或者用一種過程語言編寫的文本。
obj_file
,
link_symbol
當(dāng) C 語言源代碼中該函數(shù)的名稱與 SQL 函數(shù)的名稱不同時,這種形式的AS
子句被用于動態(tài)可載入 C 語言函數(shù)。字符串 obj_file
是包含編譯好的 C 函數(shù)的動態(tài)庫文件的名稱,它會由 LOAD 命令解析。字符串 link_symbol
是該函數(shù)的鏈接符號,也就是該函數(shù)在 C 語言源代碼中的名稱。如果省略鏈接符號,它將被假定為要定義的 SQL 函數(shù)的名稱。所有函數(shù)的 C 名稱都必須不同,因此必須為重載的 C 函數(shù)給出不同的 C 名稱(例如把參數(shù)類型作為 C 名稱的一部分)。
在重復(fù)調(diào)用引用同一對象文件的CREATE FUNCTION
時,對每個會話該文件只會被載入一次。要卸載并且重新裝載該文件(可能是在開發(fā)期間),需要開始一個新會話。
重載
PolarDB允許函數(shù)重載,也就是說同一個名稱可以被用于多個不同的函數(shù),只要它們具有可區(qū)分的輸入?yún)?shù)類型。
如果兩個函數(shù)具有相同的名稱和輸入?yún)?shù)類型,它們被認(rèn)為相同(不考慮任何OUT
參數(shù))。因此這些聲明會沖突:
CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, out text) ...
具有不同參數(shù)類型列表的函數(shù)在創(chuàng)建時將不會被認(rèn)為是沖突的,但是如果默認(rèn)值被提供,在使用時它們有可能會沖突。例如,考慮
CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, int default 42) ...
調(diào)用foo(10)
將會失敗,因為在要決定應(yīng)該調(diào)用哪個函數(shù)時會有歧義。
說明
允許把完整的 SQL 類型語法用于聲明一個函數(shù)的參數(shù)和返回值。不過,CREATE FUNCTION
會拋棄帶圓括號的類型修飾符(例如類型numeric
的精度域)。例如CREATE FUNCTION foo (varchar(10)) ...
和CREATE FUNCTION foo (varchar) ...
完全一樣。
在用CREATE OR REPLACE FUNCTION
替換一個現(xiàn)有函數(shù)時,對于更改參數(shù)名是有限制的。不能更改已經(jīng)分配給任何輸入?yún)?shù)的名稱(不過可以給之前沒有名稱的參數(shù)增加名稱)。如果有多于一個輸出參數(shù),不能更改輸出參數(shù)的名稱,因為可能會改變描述函數(shù)結(jié)果的匿名組合類型的列名。這些限制是為了確保函數(shù)被替換時,已有的對該函數(shù)的調(diào)用不會停止工作。
如果一個被聲明為STRICT
的函數(shù)帶有一個VARIADIC
參數(shù),會嚴(yán)格檢查該可變數(shù)組作為一個整體是否為非空。如果該數(shù)組有空值元素,該函數(shù)仍將被調(diào)用。
示例
這里是一些小例子,它們可以幫你了解函數(shù)創(chuàng)建。
CREATE FUNCTION add(integer, integer) RETURNS integer
AS 'select $1 + $2;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
在PL/pgSQL中,使用一個參數(shù)名稱增加一個整數(shù):
CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$
BEGIN
RETURN i + 1;
END;
$$ LANGUAGE plpgsql;
返回一個包含多個輸出參數(shù)的記錄:
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);
你可以用更復(fù)雜的方式(用一個顯式命名的組合類型)來做同樣的事情:
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
函數(shù):
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
函數(shù)與之前的例子不同,因為它實際返回了一個記錄集合而不只是一個記錄。
安全地編寫 SECURITY DEFINER函數(shù)
因為一個SECURITY DEFINER
函數(shù)會被以創(chuàng)建它的用戶的特權(quán)來執(zhí)行,需要小心地確保該函數(shù)不會被誤用。為了安全,search_path 應(yīng)該被設(shè)置為排除任何不可信用戶可寫的模式。這可以阻止惡意用戶創(chuàng)建對象(例如表、函數(shù)以及操作符)來掩飾該函數(shù)所要用到的對象。在這方面特別重要的是臨時表模式,默認(rèn)情況下它會第一個被搜索并且通常對任何用戶都是可寫的。可以通過強(qiáng)制最后搜索臨時模式來得到一種安全的布局。要這樣做,把pg_temp
寫成search_path
中的最后一項。這個函數(shù)展示了安全的用法:
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
-- 設(shè)置一個安全的 search_path:受信的模式,然后是 'pg_temp'。
SET search_path = admin, pg_temp;
這個函數(shù)的目的是為了訪問表admin.pwds
。但是如果沒有SET
子句或者帶有SET
子句卻只提到admin
,該函數(shù)會變成創(chuàng)建一個名為pwds
的臨時表。
默認(rèn)情況下,會為新創(chuàng)建的函數(shù)給PUBLIC
授予執(zhí)行特權(quán)。你常常會希望把安全定義器函數(shù)的使用限制在某些用戶中。要這樣做,你必須收回默認(rèn)的PUBLIC
特權(quán),然后選擇性地授予執(zhí)行特權(quán)。為了避免出現(xiàn)新函數(shù)能被所有人訪問的時間窗口,應(yīng)在一個事務(wù)中創(chuàng)建它并且設(shè)置特權(quán)。例如:
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;