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

10.3. 函式 #

透過以下過程確定函式呼叫所引用的特定函式。

函式型別解析

  1. pg_proc 系統目錄中選擇要考慮的函式。如果使用了非模式限定的函式名,則考慮的函式是具有匹配名稱和引數數量且在當前搜尋路徑中可見的函式(參見 第 5.10.3 節)。如果給出了限定的函式名,則只考慮指定模式中的函式。

    1. 如果在搜尋路徑中找到多個具有相同引數型別的函式,則只考慮在路徑中最早出現的那個。不同引數型別的函式被同等對待,無論搜尋路徑中的位置如何。

    2. 如果一個函式聲明瞭一個 VARIADIC 陣列引數,而呼叫未使用 VARIADIC 關鍵字,那麼該函式將被視為陣列引數被其元素型別的一個或多個例項所替換,以匹配呼叫。展開後,該函式可能具有與非 variadic 函式相同的有效引數型別。在這種情況下,將使用搜索路徑中較早出現的函式,或者如果兩個函式在同一個模式中,則優先選擇非 variadic 函式。

      這在使用限定名稱 [10] 呼叫位於允許不受信任使用者建立物件的模式中的 variadic 函式時,會產生安全風險。惡意使用者可以接管並執行任意 SQL 函式,就好像您執行了它們一樣。請替換為帶有 VARIADIC 關鍵字的呼叫,這樣可以規避此風險。填充 VARIADIC "any" 引數的呼叫通常沒有包含 VARIADIC 關鍵字的等效形式。要安全地發出這些呼叫,函式的模式必須只允許受信任的使用者建立物件。

    3. 具有引數預設值的函式被認為匹配任何省略了零個或多個可預設引數位置的呼叫。如果多個此類函式匹配呼叫,則使用搜索路徑中最早出現的那個。如果同一模式中有兩個或更多此類函式在非預設引數位置具有相同的引數型別(如果它們具有不同的可預設引數集,這是可能的),系統將無法確定偏好哪個,因此如果找不到更好的匹配項,將導致 模糊函式呼叫 錯誤。

      當透過限定名稱[10]呼叫位於允許不受信任使用者建立物件的模式中的任何函式時,這會造成可用性風險。惡意使用者可以建立一個與現有函式同名的函式,複製該函式的引數並附加具有預設值的非常規引數。這會阻止對原始函式的後續呼叫。為了防止這種風險,請將函式放在只允許受信任使用者建立物件的模式中。

  2. 檢查是否存在接受完全相同的輸入引數型別的函式。如果存在(考慮的函式集中只能有一個精確匹配),則使用它。缺少精確匹配會在使用限定名稱[10]呼叫位於允許不受信任使用者建立物件的模式中的函式時,造成安全風險。在這種情況下,請強制轉換引數以強制精確匹配。(涉及 unknown 的情況在此步驟中永遠找不到匹配項。)

  3. 如果沒有找到精確匹配,請檢視函式呼叫是否似乎是特殊的型別轉換請求。當函式呼叫只有一個引數,並且函式名稱與某個資料型別的(內部)名稱相同,並且函式引數是未指定型別的字面量,或者是一個可以與命名資料型別進行二進位制轉換的型別,或者可以透過應用該型別的 I/O 函式轉換為命名資料型別的型別(即,轉換是與標準字串型別之一進行或來自標準字串型別之一)時,就會發生這種情況。當滿足這些條件時,函式呼叫將被視為一種 CAST 規範。[11]

  4. 尋找最佳匹配。

    1. 丟棄那些輸入型別不匹配且無法轉換(使用隱式轉換)以匹配的候選函式。unknown 字面量在此目的下被假定為可以轉換為任何型別。如果只剩一個候選者,則使用它;否則繼續下一步。

    2. 如果任何輸入引數是域型別,則在所有後續步驟中將其視為域的基本型別。這確保域對於模糊函式解析目的就像它們的基型別一樣。

    3. 遍歷所有候選者,並保留那些在輸入型別上具有最多精確匹配的候選者。如果沒有精確匹配,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。

    4. 遍歷所有候選者,並保留那些在需要型別轉換的位置接受首選型別(即輸入資料型別的型別類別)的候選者。如果沒有候選者接受首選型別,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。

    5. 如果任何輸入引數是 unknown,則檢查剩餘候選者在這些引數位置接受的型別類別。在每個位置,如果任何候選者接受該類別,則選擇 string 類別。(偏向字串是合適的,因為未指定型別的字面量看起來像字串。)否則,如果所有剩餘候選者都接受相同的型別類別,則選擇該類別;否則失敗,因為無法推斷出正確的選擇而無需更多線索。現在,丟棄不接受所選型別類別的候選者。此外,如果任何候選者在該類別中接受首選型別,則丟棄那些接受非首選型別的候選者。如果這些測試後沒有候選者存活,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。

    6. 如果同時存在 unknown 和已知型別的引數,並且所有已知型別的引數都具有相同的型別,則假定 unknown 引數也具有該型別,並檢查哪些候選者可以在 unknown 引數位置接受該型別。如果只有一個候選者透過此測試,則使用它。否則,失敗。

請注意,對於運算子和函式型別解析,“最佳匹配”規則是相同的。以下是一些示例。

示例 10.6. 四捨五入函式引數型別解析

只有一個接受兩個引數的 round 函式;它接受型別為 numeric 的第一個引數和型別為 integer 的第二個引數。因此,以下查詢會自動將型別為 integer 的第一個引數轉換為 numeric

SELECT round(4, 4);

 round
--------
 4.0000
(1 row)

該查詢實際上由解析器轉換為

SELECT round(CAST (4 AS numeric), 4);

由於帶小數點的數字常量最初被分配 numeric 型別,因此以下查詢將不需要型別轉換,因此可能效率稍高

SELECT round(4.0, 4);

示例 10.7. Variadic 函式解析

CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int
  LANGUAGE sql AS 'SELECT 1';
CREATE FUNCTION

此函式接受但不需要 VARIADIC 關鍵字。它能容忍整數和數字引數

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);
 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                1 |                1 |                1
(1 row)

但是,第一個和第二個呼叫將優先選擇更具體的函式(如果可用)

CREATE FUNCTION public.variadic_example(numeric) RETURNS int
  LANGUAGE sql AS 'SELECT 2';
CREATE FUNCTION

CREATE FUNCTION public.variadic_example(int) RETURNS int
  LANGUAGE sql AS 'SELECT 3';
CREATE FUNCTION

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);
 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                3 |                2 |                1
(1 row)

給定預設配置且僅存在第一個函式,則第一個和第二個呼叫是不安全的。任何使用者都可以透過建立第二個或第三個函式來攔截它們。透過精確匹配引數型別並使用 VARIADIC 關鍵字,第三個呼叫是安全的。


示例 10.8. Substring 函式型別解析

有幾個 substr 函式,其中一個接受 textinteger 型別。如果使用型別未指定的字串常量呼叫,系統將選擇接受 string(即 text 型別)的首選類別引數的候選函式。

SELECT substr('1234', 3);

 substr
--------
     34
(1 row)

如果字串被宣告為 varchar 型別,就像它來自表一樣,那麼解析器將嘗試將其轉換為 text

SELECT substr(varchar '1234', 3);

 substr
--------
     34
(1 row)

這由解析器轉換為實際上成為

SELECT substr(CAST (varchar '1234' AS text), 3);

注意

解析器從 pg_cast 目錄中得知 textvarchar 是二進位制相容的,這意味著可以在不進行任何物理轉換的情況下將一個傳遞給接受另一個的函式。因此,在這種情況下,實際上沒有插入型別轉換呼叫。

並且,如果函式使用型別為 integer 的引數呼叫,解析器將嘗試將其轉換為 text

SELECT substr(1234, 3);
ERROR:  function substr(integer, integer) does not exist
HINT:  No function matches the given name and argument types. You might need
to add explicit type casts.

這不起作用,因為 integer 沒有隱式轉換為 text。然而,顯式轉換將起作用

SELECT substr(CAST (1234 AS text), 3);

 substr
--------
     34
(1 row)



[10] 對於非模式限定名稱,不會出現此風險,因為包含允許不受信任使用者建立物件的模式的搜尋路徑不是 安全的模式使用模式

[11] 此步驟的原因是為了在沒有實際轉換函式的情況下支援函式樣式轉換規範。如果存在轉換函式,它通常以其輸出型別命名,因此無需特殊處理。有關其他註釋,請參閱 CREATE CAST

提交更正

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