10.3. Functions

使用以下过程确定函数调用引用的特定函数。

功能类型解析

  • pg_proc系统目录中选择要考虑的功能。如果使用了非模式限定的函数名称,则考虑的函数是在当前搜索路径中可见的具有匹配名称和参数计数的那些函数(请参见Section 5.8.3)。如果给出了合格的函数名称,则仅考虑指定架构中的函数。

  • 如果搜索路径找到具有相同参数类型的多个函数,则仅考虑路径中最早出现的一个函数。无论搜索路径位置如何,都将在同等基础上考虑不同自变量类型的函数。

    • 如果使用VARIADIC数组参数声明了一个函数,并且该调用未使用VARIADIC关键字,则将该函数视为将数组参数替换为一次或多次出现的其元素类型,以匹配该调用。进行此类扩展后,该函数可能具有与某些非变量函数相同的有效参数类型。在那种情况下,将使用搜索路径中较早出现的功能,或者如果两个功能处于同一模式中,则首选非可变功能。

当通过限定名称[9]调用在架构中发现的可变参数函数时,这会造成安全隐患,该函数允许不受信任的用户创建对象。恶意用户可以控制并执行任意 SQL 函数,就像执行它们一样。替换带有VARIADIC关键字的呼叫,可以绕过此危险。填充VARIADIC "any"参数的调用通常没有包含VARIADIC关键字的等效公式。为了安全地发出这些调用,函数的架构必须仅允许受信任的用户创建对象。

  • 具有参数默认值的函数被认为与忽略零个或多个默认参数位置的任何调用匹配。如果一个以上的函数与一个呼叫相匹配,则使用最早出现在搜索路径中的一个。如果在同一模式中有两个或多个这样的函数,并且它们的参数类型在非默认位置(如果它们具有不同的默认参数集,则可能是相同的),那么系统将无法确定首选哪个,等等。如果找不到与该调用的更好匹配,则会导致“歧义函数调用”错误。

通过合格名称[9]调用架构中允许不信任用户创建对象的任何函数时,这会造成可用性危害。恶意用户可以使用现有功能的名称创建功能,复制该功能的参数并附加具有默认值的新颖参数。这排除了对原始函数的新调用。为了避免这种危险,请将函数放置在仅允许受信任用户创建对象的架构中。

  • 检查是否有一个函数完全接受 Importing 参数类型。如果存在(考虑的一组功能中只有一个完全匹配),请使用它。如果通过限定名称[9]调用在架构中发现的允许不受信任的用户创建对象的函数,则缺少完全匹配会带来安全隐患。在这种情况下,强制转换参数以强制完全匹配。 (涉及unknown的案例在此步骤将永远找不到匹配项.)

  • 如果找不到完全匹配的内容,请查看该函数调用是否似乎是特殊的类型转换请求。如果函数调用只有一个参数并且函数名称与某些数据类型的(内部)名称相同,则会发生这种情况。此外,函数参数必须是未知类型的 Literals,或者是对指定数据类型可以二进制强制转换的类型,或者可以通过应用该类型的 I/O 函数转换为命名数据类型的类型(即是,转换是与标准字符串类型之一进行转换或从标准字符串类型之一进行转换.当满足这些条件时,函数调用将被视为CAST规范的形式.[10]

  • 寻找最佳搭配。

  • 丢弃 Importing 类型不匹配且无法转换(使用隐式转换)匹配的候选函数。为此,假定unknownLiterals 可转换为任何东西。如果只剩下一名候选人,请使用它;否则 continue 下一步。

    • 如果任何 Importing 参数属于域类型,则在所有后续步骤中都将其视为域的基本类型。这样可以确保域的行为像其基本类型一样,以实现模糊函数解析。

    • 遍历所有候选者,并保留与 Importing 类型最匹配的候选者。如果没有完全匹配的候选人,则保留所有候选人。如果只剩下一名候选人,请使用它;否则 continue 下一步。

    • 遍历所有候选项,并将那些接受首选类型(Importing 数据类型的类别)的候选项保留在需要进行类型转换的大多数位置。如果没有候选人接受首选类型,则保留所有候选人。如果只剩下一名候选人,请使用它;否则 continue 下一步。

    • 如果任何 Importing 自变量是unknown,请检查其余自变量在这些自变量位置接受的类型类别。如果有候选人接受该类别,请在每个位置选择string类别。 (这种偏向字符串的做法是适当的,因为未知类型的 Literals 看起来像字符串.)否则,如果所有其他候选都接受相同的类型类别,则选择该类别;否则,如果所有其他候选都接受相同的类型类别,则选择该类别。否则失败,因为没有更多线索就无法推断出正确的选择。现在,丢弃不接受所选类型类别的候选对象。此外,如果任何候选者接受该类别中的首选类型,则丢弃该参数接受非首选类型的候选者。如果没有任何候选人在这些测试中幸存下来,请保留所有候选人。如果只剩下一名候选人,请使用它;否则 continue 下一步。

    • 如果同时具有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. 可变参数功能

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. 子字符串函数类型解析

有多个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);

Note

解析器从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)

[9]使用非模式限定的名称不会引起危险,因为包含允许不信任用户创建对象的架构的搜索路径不是安全模式使用模式

[10]此步骤的原因是在没有实际的强制转换功能的情况下支持函数样式的强制转换规范。如果存在强制转换功能,则通常按其输出类型命名,因此无需特殊情况。有关其他 Comment,请参见CREATE CAST