支援的版本:目前 (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 關鍵字,則該函式會被視為將陣列參數替換為一個或多個其元素型別的出現,以符合呼叫的需求。在此擴充之後,函式可能具有與某些非可變函式相同的有效引數型別。在這種情況下,使用搜尋路徑中較早出現的函式,或者如果兩個函式位於相同的結構描述中,則首選非可變函式。

      當透過限定名稱[10]呼叫在允許不受信任的使用者建立物件的結構描述中找到的可變函式時,會產生安全性風險。惡意使用者可以取得控制權並執行任意 SQL 函式,就像您執行它們一樣。替換帶有 VARIADIC 關鍵字的呼叫,這會繞過此風險。填充 VARIADIC "any" 參數的呼叫通常沒有包含 VARIADIC 關鍵字的等效公式。為了安全地發出這些呼叫,函式的結構描述必須僅允許受信任的使用者建立物件。

    3. 對於參數具有預設值的函式,會被視為符合省略零個或多個可預設參數位置的任何呼叫。如果多個此類函式符合呼叫,則使用搜尋路徑中最早出現的函式。如果在同一結構描述中有兩個或多個此類函式在非預設位置具有相同的參數型別(如果它們具有不同的可預設參數集,則這是可能的),系統將無法確定首選哪個函式,因此,如果找不到更好的呼叫相符項,將導致不明確的函式呼叫錯誤。

      當透過限定名稱[10]呼叫在允許不受信任的使用者建立物件的結構描述中找到的任何函式時,會產生可用性風險。惡意使用者可以使用現有函式的名稱建立函式,複製該函式的參數並附加具有預設值的新參數。這會阻止對原始函式的新呼叫。為了避免這種風險,請將函式放置在僅允許受信任的使用者建立物件的結構描述中。

  2. 檢查是否存在接受完全輸入引數型別的函式。如果存在一個(在所考慮的函式集中只能有一個完全相符項),則使用它。當透過限定名稱[10]呼叫在允許不受信任的使用者建立物件的結構描述中找到的函式時,缺少完全相符項會產生安全性風險。在這種情況下,強制轉換引數以強制完全相符。(涉及 unknown 的情況永遠不會在此步驟中找到相符項。)

  3. 如果找不到完全相符項,請查看函式呼叫是否顯示為特殊型別轉換請求。如果函式呼叫只有一個引數,且函式名稱與某個資料型別的(內部)名稱相同,就會發生這種情況。此外,函式引數必須是未知型別的文字,或可與命名資料型別進行二進位相容的型別,或可透過套用該型別的 I/O 函式轉換為命名資料型別的型別(也就是說,轉換是轉換為或從標準字串型別之一轉換而來)。當滿足這些條件時,函式呼叫將被視為 CAST 規格的一種形式。[11]

  4. 尋找最佳相符項。

    1. 捨棄輸入型別不相符且無法轉換(使用隱含轉換)以符合的候選函式。為了此目的,unknown 文字被假定為可轉換為任何內容。如果只剩下一個候選函式,則使用它;否則繼續下一步。

    2. 如果任何輸入引數是網域型別,則在所有後續步驟中將其視為網域的基本型別。這可確保網域在不明確函式解析方面表現得像它們的基本型別一樣。

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

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

    5. 如果有任何輸入參數為 unknown,則檢查剩餘候選對象在這些參數位置所接受的類型類別。在每個位置,如果任何候選對象接受 string 類別,則選擇 string 類別。(這種對字串的偏好是適當的,因為 unknown 類型的文字看起來像字串。)否則,如果所有剩餘的候選對象都接受相同的類型類別,則選擇該類別;否則失敗,因為在沒有更多線索的情況下無法推斷出正確的選擇。現在,丟棄不接受所選類型類別的候選對象。此外,如果任何候選對象在該類別中接受首選類型,則丟棄接受非首選類型的候選對象。如果沒有任何候選對象通過這些測試,則保留所有候選對象。如果只剩下一個候選對象,則使用它;否則,繼續下一步。

    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. 變長參數函數解析

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

此函數接受(但非必要)VARIADIC 關鍵字。它容許 integer 和 numeric 參數

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] 非 schema 限定名稱不會發生此風險,因為包含允許不受信任的使用者建立物件之 schema 的搜尋路徑不是 安全 schema 使用模式

[11] 執行此步驟的原因是為了支援在沒有實際轉換函數的情況下使用函數樣式的轉換規範。 如果有轉換函數,通常會以其輸出類型命名,因此無需特殊處理。 有關其他評論,請參閱CREATE CAST

提交更正

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