最近給項目做支持,由于函數類型問題,加了幾條函數定義。
用戶使用函數場景是func('string', 'string')。當時給用戶添加了一條函數定義:func(text, text)。后來由于和其他函數沖突改成了func(varchar, varchar)。varchar和text同樣都是字符串類型,而且用的相同的結構體,我的感知是效果是相同的,然后在使用過程中還是發生錯誤:
ERROR: function func(unknown, unknown) is not unique
LINE 1: select func('****','****');^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
這里的問題是,使用varchar,unknown不會默認轉為varchar。而當參數類型是text時,就會默認轉為text,然后在進行查找,這很奇怪了。開始定位原因,發現在執行函數func_select_candidate,會有不同。func_select_candidate代碼非常長,這里就不貼出來了,大家可以自行看下,我在這里簡單對代碼進行介紹:
/* * 函數正式由于存在unknown type,所以無法準確定位到目標函數,* 所以這個函數主要用來處理帶有unknown type的函數查詢*/
func_select_candidate()
{
/* Step 1 */
處理有一個或多個unknown type的函數,盡可能匹配到相同類型的函數,
如果候選者只有一個則返回,否則進入下一步;
如果沒有候選者,則返回NULL;
/* Step 2 */
如果還是有多個候選函數,那么通過查找最優先類型函數(typispreferred為true的類型),
如果候選者只有一個則返回,否則進入下一步;
如果沒有候選者,則返回NULL;
/* Step 3 */
如果還是有多個候選函數,那么嘗試將unknown type嘗試轉換為最優先類型函數;
如果候選者只有一個則返回,否則進入下一步;
如果沒有候選者,則返回NULL;
/* Step 4 */
如果上一步確認可以轉到最優先類型,則嘗試進行使用最優先類型進行匹配函數;
如果候選者只有一個則返回,否則進入下一步;
如果沒有候選者,則返回NULL;
/* Step 5 */
如果還是沒有找到最有函數,并且unknown type參數個數少于總的參數個數,
則嘗試將所有的unknown type轉換為已知的類型接收,并且查看是否可以將已知類型轉換為函數類型,
一旦找到即返回,否則返回NULL;
}
這里的typispreferred和typcategory引起了我的注意,可以查看文檔:
typcategory:typcategory
is an arbitrary classification of data types that is used by the parser to determine which implicit casts should be“preferred”. SeeTable 51.64.
typispreferred:True if the type is a preferred cast target within itstypcategory
查看系統表:
postgres=# select oid,typname,typcategory,typispreferred from pg_type where oid=1043 or oid = 25 or oid = 705; oid | typname | typcategory | typispreferred
------+---------+-------------+----------------25 | text | S | t705 | unknown | X | f1043 | varchar | S | f
(3 rows)
text是typcategory為‘S’的preferred類型。
我定義的func(text, text),進入了func_select_candidate的part3和4,完成了匹配,而如果是func(varchar, varchar)由于不滿足part3、4、5,直接返回NULL,返回無法找到最優函數的錯誤。這也就是類型使用text能夠完成函數識別而varchar不能的原因。