透過以下過程確定函式呼叫所引用的特定函式。
函式型別解析
從 pg_proc
系統目錄中選擇要考慮的函式。如果使用了非模式限定的函式名,則考慮的函式是具有匹配名稱和引數數量且在當前搜尋路徑中可見的函式(參見 第 5.10.3 節)。如果給出了限定的函式名,則只考慮指定模式中的函式。
如果在搜尋路徑中找到多個具有相同引數型別的函式,則只考慮在路徑中最早出現的那個。不同引數型別的函式被同等對待,無論搜尋路徑中的位置如何。
如果一個函式聲明瞭一個 VARIADIC
陣列引數,而呼叫未使用 VARIADIC
關鍵字,那麼該函式將被視為陣列引數被其元素型別的一個或多個例項所替換,以匹配呼叫。展開後,該函式可能具有與非 variadic 函式相同的有效引數型別。在這種情況下,將使用搜索路徑中較早出現的函式,或者如果兩個函式在同一個模式中,則優先選擇非 variadic 函式。
這在使用限定名稱 [10] 呼叫位於允許不受信任使用者建立物件的模式中的 variadic 函式時,會產生安全風險。惡意使用者可以接管並執行任意 SQL 函式,就好像您執行了它們一樣。請替換為帶有 VARIADIC
關鍵字的呼叫,這樣可以規避此風險。填充 VARIADIC "any"
引數的呼叫通常沒有包含 VARIADIC
關鍵字的等效形式。要安全地發出這些呼叫,函式的模式必須只允許受信任的使用者建立物件。
具有引數預設值的函式被認為匹配任何省略了零個或多個可預設引數位置的呼叫。如果多個此類函式匹配呼叫,則使用搜索路徑中最早出現的那個。如果同一模式中有兩個或更多此類函式在非預設引數位置具有相同的引數型別(如果它們具有不同的可預設引數集,這是可能的),系統將無法確定偏好哪個,因此如果找不到更好的匹配項,將導致 “模糊函式呼叫” 錯誤。
當透過限定名稱[10]呼叫位於允許不受信任使用者建立物件的模式中的任何函式時,這會造成可用性風險。惡意使用者可以建立一個與現有函式同名的函式,複製該函式的引數並附加具有預設值的非常規引數。這會阻止對原始函式的後續呼叫。為了防止這種風險,請將函式放在只允許受信任使用者建立物件的模式中。
檢查是否存在接受完全相同的輸入引數型別的函式。如果存在(考慮的函式集中只能有一個精確匹配),則使用它。缺少精確匹配會在使用限定名稱[10]呼叫位於允許不受信任使用者建立物件的模式中的函式時,造成安全風險。在這種情況下,請強制轉換引數以強制精確匹配。(涉及 unknown
的情況在此步驟中永遠找不到匹配項。)
如果沒有找到精確匹配,請檢視函式呼叫是否似乎是特殊的型別轉換請求。當函式呼叫只有一個引數,並且函式名稱與某個資料型別的(內部)名稱相同,並且函式引數是未指定型別的字面量,或者是一個可以與命名資料型別進行二進位制轉換的型別,或者可以透過應用該型別的 I/O 函式轉換為命名資料型別的型別(即,轉換是與標準字串型別之一進行或來自標準字串型別之一)時,就會發生這種情況。當滿足這些條件時,函式呼叫將被視為一種 CAST
規範。[11]
尋找最佳匹配。
丟棄那些輸入型別不匹配且無法轉換(使用隱式轉換)以匹配的候選函式。unknown
字面量在此目的下被假定為可以轉換為任何型別。如果只剩一個候選者,則使用它;否則繼續下一步。
如果任何輸入引數是域型別,則在所有後續步驟中將其視為域的基本型別。這確保域對於模糊函式解析目的就像它們的基型別一樣。
遍歷所有候選者,並保留那些在輸入型別上具有最多精確匹配的候選者。如果沒有精確匹配,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。
遍歷所有候選者,並保留那些在需要型別轉換的位置接受首選型別(即輸入資料型別的型別類別)的候選者。如果沒有候選者接受首選型別,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。
如果任何輸入引數是 unknown
,則檢查剩餘候選者在這些引數位置接受的型別類別。在每個位置,如果任何候選者接受該類別,則選擇 string
類別。(偏向字串是合適的,因為未指定型別的字面量看起來像字串。)否則,如果所有剩餘候選者都接受相同的型別類別,則選擇該類別;否則失敗,因為無法推斷出正確的選擇而無需更多線索。現在,丟棄不接受所選型別類別的候選者。此外,如果任何候選者在該類別中接受首選型別,則丟棄那些接受非首選型別的候選者。如果這些測試後沒有候選者存活,則保留所有候選者。如果只剩下一個候選者,則使用它;否則繼續下一步。
如果同時存在 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
函式,其中一個接受 text
和 integer
型別。如果使用型別未指定的字串常量呼叫,系統將選擇接受 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
目錄中得知 text
和 varchar
是二進位制相容的,這意味著可以在不進行任何物理轉換的情況下將一個傳遞給接受另一個的函式。因此,在這種情況下,實際上沒有插入型別轉換呼叫。
並且,如果函式使用型別為 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)
如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。