2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

CREATE FUNCTION

CREATE FUNCTION — 定義一個新函式

概要

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'
    | 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

引數的模式:INOUTINOUTVARIADIC。如果省略,預設為 IN。只有 OUT 引數可以跟在 VARIADIC 引數之後。此外,OUTINOUT 引數不能與 RETURNS TABLE 語法一起使用。

argname

引數的名稱。某些語言(包括 SQL 和 PL/pgSQL)允許您在函式體中使用名稱。對於其他語言,輸入引數的名稱僅作為額外的文件,就函式本身而言;但您可以在呼叫函式時使用輸入引數名稱來提高可讀性(參見 第 4.3 節)。無論如何,輸出引數的名稱很重要,因為它定義了結果行型別中的列名。(如果您為輸出引數省略了名稱,系統將選擇一個預設列名。)

argtype

函式引數的資料型別(可選擇模式限定),如果有的話。引數型別可以是基本型別、複合型別或域型別,或者可以引用表列的型別。

根據實現語言,也可能允許指定偽型別,例如 cstring。偽型別表示實際引數型別要麼不完全指定,要麼不在普通 SQL 資料型別集合之外。

透過寫入 table_name.column_name%TYPE 來引用列的型別。使用此功能有時可以幫助函式獨立於表定義的變化。

default_expr

如果未指定引數,則用作預設值的表示式。該表示式必須可強制轉換為引數的引數型別。只有輸入(包括 INOUT)引數可以有預設值。具有預設值的引數後面的所有輸入引數也必須具有預設值。

rettype

返回資料型別(可選擇模式限定)。返回型別可以是基本型別、複合型別或域型別,或者可以引用表列的型別。根據實現語言,也可能允許指定偽型別,例如 cstring。如果函式不應返回值,則將 void 指定為返回型別。

當存在 OUTINOUT 引數時,可以省略 RETURNS 子句。如果存在,它必須與輸出引數所隱含的結果型別一致:如果是多個輸出引數,則為 RECORD,或者與單個輸出引數的型別相同。

SETOF 修飾符表示函式將返回一個項集,而不是單個項。

透過寫入 table_name.column_name%TYPE 來引用列的型別。

column_name

RETURNS TABLE 語法中輸出列的名稱。這實際上是宣告一個命名的 OUT 引數的另一種方式,除了 RETURNS TABLE 也隱含了 RETURNS SETOF

column_type

RETURNS TABLE 語法中輸出列的資料型別。

lang_name

函式實現的語言名稱。它可以是 sqlcinternal,或者是使用者定義的儲存過程語言的名稱,例如 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 INPUTSTRICT 表示當任何引數為 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 命令一樣:此類命令的效果將在函式退出後持續存在,除非當前事務被回滾。

有關允許的引數名稱和值的更多資訊,請參閱 SET第 19 章

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 ATOMIC
  statement;
  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 INPUTRETURNS NULL ON NULL INPUT 外,所有其他屬性均未標準化。

  • 對於 LANGUAGE SQL 函式的主體,SQL 標準僅指定 sql_body 形式。

簡單的 LANGUAGE SQL 函式可以以一種既符合標準又可移植到其他實現的方式編寫。使用高階功能、最佳化屬性或其他語言的更復雜的函式必然會在很大程度上特定於 PostgreSQL。

提交更正

如果您在文件中看到任何不正確、與您對特定功能的使用經驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。