最近给项目做支持,由于函数类型问题,加了几条函数定义。
用户使用函数场景是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不能的原因。