pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
函数调用所引用的具体函数按照下述过程确定。
函数类型解析
从系统目录pg_proc中选取要考虑的函数。如果使用的是未带模式限定的函数名,则考虑当前搜索路径中可见且名称和参数个数匹配的函数(见第 5.7.3 节)。如果给出了限定名,则只考虑指定模式中的函数。
如果搜索路径中找到多个参数类型完全相同的函数,只考虑路径中最早出现的那一个。参数类型不同的函数则不受搜索路径位置影响,处于同等地位。
如果函数声明了一个VARIADIC数组参数,而调用时没有使用VARIADIC关键字,则把数组参数视为一个或多个其元素类型的参数,个数按匹配该调用的需要确定。扩展之后,该函数的有效参数类型可能与某个非variadic函数完全相同。在这种情况下,使用搜索路径中较早出现的函数;如果两个函数位于同一模式,则优先选择非variadic的那个。
带有参数默认值的函数,被认为可以匹配任何省略了零个或多个可使用默认值的参数位置的调用。如果不止一个这样的函数能匹配某个调用,则使用搜索路径中最早出现的那一个。如果同一模式中存在两个或更多这样的函数,并且它们在未使用默认值的参数位置上的参数类型相同(如果它们具有不同的带有默认值的参数集合,这种情况是可能的),系统就无法判断该偏好哪一个;如果找不到更好的匹配,就会报“有歧义的函数调用”错误。
检查是否存在一个恰好接受输入参数类型的函数。如果存在(在被考虑的函数集合中至多只有一个精确匹配),就使用它。(涉及unknown的情况在这一步永远找不到匹配。)
如果没有找到精确匹配,就检查该函数调用是否看起来像一种特殊的类型转换请求。这种情况发生在:函数调用只有一个参数,且函数名与某个数据类型的(内部)名称相同。此外,函数参数必须是unknown类型字面量,或者是一种可以二进制强制转换为该命名数据类型的类型,或者是一种可以通过应用其 I/O 函数转换为该命名数据类型的类型(也就是说,该转换要么转到某种标准字符串类型,要么从某种标准字符串类型转来)。满足这些条件时,该函数调用会被当作一种CAST形式。 [7]
寻找最佳匹配。
丢弃那些输入类型不匹配,且也无法通过隐式类型转换实现匹配的候选函数。为此,假定unknown字面量可以转换为任何类型。如果只剩一个候选函数,就使用它;否则继续下一步。
如果任何输入参数是域类型,则在后续所有步骤中都把它当作该域的基础类型。这样可以确保在解决函数歧义时,域的行为与其基础类型一致。
遍历所有候选函数,保留那些在输入类型上精确匹配数最多的候选项。如果没有任何候选项存在精确匹配,则保留全部候选项。如果只剩一个候选函数,就使用它;否则继续下一步。
遍历所有候选函数,在需要类型转换的位置上,保留接受首选类型(属于输入数据类型的类型分类)的位置数最多的候选项。如果没有候选项接受首选类型,则保留全部候选项。如果只剩一个候选函数,就使用它;否则继续下一步。
如果有任何输入参数是unknown,就检查剩余候选函数在这些参数位置上接受哪些类型分类。在每个位置上,如果有任何候选项接受string分类,就选择该分类。(这种偏向字符串的做法是合适的,因为unknown类型的字面量看起来像字符串。)否则,如果所有剩余候选项都接受同一种类型分类,就选择该分类;否则失败,因为在缺少更多线索的情况下无法推断出正确选择。然后丢弃不接受所选类型分类的候选项。此外,如果有任何候选项接受该分类中的首选类型,就丢弃在该参数上接受非首选类型的候选项。
如果只剩一个候选函数,就使用它。如果没有候选函数或者不止一个候选函数剩余,则失败。
注意,操作符类型解析和函数类型解析的“最佳匹配”规则是完全相同的。下面是一些示例。
例 10.5. round 函数参数类型解析
只有一个接受两个参数的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.6. substr 函数类型解析
存在多个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)
[7] 之所以有这一步,是为了在不存在实际类型转换函数的情况下仍支持函数风格的类型转换写法。如果存在类型转换函数,按惯例它会以其输出类型命名,因此无需为此设置特殊情况。更多说明见CREATE CAST。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。