2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 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 关键字,那么该函数将被视为数组参数被其元素类型的一个或多个实例所替换,以匹配调用。展开后,该函数可能具有与非 variadic 函数相同的有效参数类型。在这种情况下,将使用搜索路径中较早出现的函数,或者如果两个函数在同一个模式中,则优先选择非 variadic 函数。

      这在使用限定名称 [10] 调用位于允许不受信任用户创建对象的模式中的 variadic 函数时,会产生安全风险。恶意用户可以接管并执行任意 SQL 函数,就好像您执行了它们一样。请替换为带有 VARIADIC 关键字的调用,这样可以规避此风险。填充 VARIADIC "any" 参数的调用通常没有包含 VARIADIC 关键字的等效形式。要安全地发出这些调用,函数的模式必须只允许受信任的用户创建对象。

    3. 具有参数默认值的函数被认为匹配任何省略了零个或多个可默认参数位置的调用。如果多个此类函数匹配调用,则使用搜索路径中最早出现的那个。如果同一模式中有两个或更多此类函数在非默认参数位置具有相同的参数类型(如果它们具有不同的可默认参数集,这是可能的),系统将无法确定偏好哪个,因此如果找不到更好的匹配项,将导致 模糊函数调用 错误。

      当通过限定名称[10]调用位于允许不受信任用户创建对象的模式中的任何函数时,这会造成可用性风险。恶意用户可以创建一个与现有函数同名的函数,复制该函数的参数并附加具有默认值的非常规参数。这会阻止对原始函数的后续调用。为了防止这种风险,请将函数放在只允许受信任用户创建对象的模式中。

  2. 检查是否存在接受完全相同的输入参数类型的函数。如果存在(考虑的函数集中只能有一个精确匹配),则使用它。缺少精确匹配会在使用限定名称[10]调用位于允许不受信任用户创建对象的模式中的函数时,造成安全风险。在这种情况下,请强制转换参数以强制精确匹配。(涉及 unknown 的情况在此步骤中永远找不到匹配项。)

  3. 如果没有找到精确匹配,请查看函数调用是否似乎是特殊的类型转换请求。当函数调用只有一个参数,并且函数名称与某个数据类型的(内部)名称相同,并且函数参数是未指定类型的字面量,或者是一个可以与命名数据类型进行二进制转换的类型,或者可以通过应用该类型的 I/O 函数转换为命名数据类型的类型(即,转换是与标准字符串类型之一进行或来自标准字符串类型之一)时,就会发生这种情况。当满足这些条件时,函数调用将被视为一种 CAST 规范。[11]

  4. 寻找最佳匹配。

    1. 丢弃那些输入类型不匹配且无法转换(使用隐式转换)以匹配的候选函数。unknown 字面量在此目的下被假定为可以转换为任何类型。如果只剩一个候选者,则使用它;否则继续下一步。

    2. 如果任何输入参数是域类型,则在所有后续步骤中将其视为域的基本类型。这确保域对于模糊函数解析目的就像它们的基类型一样。

    3. 遍历所有候选者,并保留那些在输入类型上具有最多精确匹配的候选者。如果没有精确匹配,则保留所有候选者。如果只剩下一个候选者,则使用它;否则继续下一步。

    4. 遍历所有候选者,并保留那些在需要类型转换的位置接受首选类型(即输入数据类型的类型类别)的候选者。如果没有候选者接受首选类型,则保留所有候选者。如果只剩下一个候选者,则使用它;否则继续下一步。

    5. 如果任何输入参数是 unknown,则检查剩余候选者在这些参数位置接受的类型类别。在每个位置,如果任何候选者接受该类别,则选择 string 类别。(偏向字符串是合适的,因为未指定类型的字面量看起来像字符串。)否则,如果所有剩余候选者都接受相同的类型类别,则选择该类别;否则失败,因为无法推断出正确的选择而无需更多线索。现在,丢弃不接受所选类型类别的候选者。此外,如果任何候选者在该类别中接受首选类型,则丢弃那些接受非首选类型的候选者。如果这些测试后没有候选者存活,则保留所有候选者。如果只剩下一个候选者,则使用它;否则继续下一步。

    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. 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 函数,其中一个接受 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] 对于非模式限定名称,不会出现此风险,因为包含允许不受信任用户创建对象的模式的搜索路径不是 安全的模式使用模式

[11] 此步骤的原因是为了在没有实际转换函数的情况下支持函数样式转换规范。如果存在转换函数,它通常以其输出类型命名,因此无需特殊处理。有关其他注释,请参阅 CREATE CAST

提交更正

如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。