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.2. 运算符 #

通过以下过程确定运算符表达式引用的特定运算符。请注意,此过程受涉及运算符的优先级的间接影响,因为优先级将决定哪些子表达式被视为哪些运算符的输入。有关更多信息,请参见第 4.1.6 节

运算符类型解析

  1. pg_operator 系统目录中选择要考虑的运算符。如果使用了非模式限定的运算符名称(通常情况),则考虑的运算符是名称和参数计数匹配且在当前搜索路径中可见的运算符(请参见第 5.10.3 节)。如果给出了模式限定的运算符名称,则只考虑指定模式中的运算符。

    1. 如果在搜索路径中找到多个具有相同参数类型的运算符,则只考虑路径中最早出现的那个。不同参数类型的运算符被平等对待,无论其在搜索路径中的位置如何。

  2. 检查是否存在接受与输入参数类型完全匹配的运算符。如果存在(考虑的运算符集中最多只能有一个精确匹配),则使用它。缺少精确匹配在调用时(通过限定名称[9](不典型),任何在允许不受信任用户创建对象的模式中找到的运算符)会产生安全风险。在这种情况下,请强制转换参数以强制执行精确匹配。

    1. 如果二元运算符调用中的一个参数是 unknown 类型,则在此检查中假定它与另一个参数相同。涉及两个 unknown 输入的调用,或带有 unknown 输入的前缀运算符,在此步骤中将永远找不到匹配项。

    2. 如果二元运算符调用中的一个参数是 unknown 类型,而另一个参数是域类型,则接下来检查是否存在一个运算符在两侧都精确接受域的基础类型;如果存在,则使用它。

  3. 查找最佳匹配。

    1. 丢弃候选运算符,这些运算符的输入类型不匹配且无法通过(使用隐式转换)进行转换以匹配。为此,假定 unknown 字面量可以转换为任何类型。如果只剩下一个候选项,则使用它;否则,继续下一步。

    2. 如果任何输入参数是域类型,则在后续所有步骤中将其视为域的基础类型。这可确保域在歧义运算符解析方面像其基础类型一样。

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

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

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

    6. 如果存在 unknown 和已知类型参数,并且所有已知类型参数具有相同的类型,则假定 unknown 参数也具有该类型,并检查哪些候选项可以在 unknown 参数位置上接受该类型。如果恰好有一个候选项通过此测试,则使用它。否则,失败。

以下是一些示例。

示例 10.1. 平方根运算符类型解析

标准目录中只有一个平方根运算符(前缀 |/),它接受 double precision 类型的参数。扫描器在此查询表达式中为参数分配了 integer 的初始类型。

SELECT |/ 40 AS "square root of 40";
 square root of 40
-------------------
 6.324555320336759
(1 row)

因此,解析器对操作数进行类型转换,查询等效于:

SELECT |/ CAST(40 AS double precision) AS "square root of 40";

示例 10.2. 字符串连接运算符类型解析

使用类似字符串的语法来处理字符串类型和处理复杂扩展类型。未指定类型的字符串会与可能的运算符候选项匹配。

一个具有一个未指定参数的示例

SELECT text 'abc' || 'def' AS "text and unknown";

 text and unknown
------------------
 abcdef
(1 row)

在这种情况下,解析器会查看是否存在一个运算符在两个参数上都接受 text。因为存在,所以它假定第二个参数应解释为 text 类型。

这是两个未指定类型值的连接

SELECT 'abc' || 'def' AS "unspecified";

 unspecified
-------------
 abcdef
(1 row)

在这种情况下,没有关于使用哪种类型的初始提示,因为查询中未指定任何类型。因此,解析器会查找所有候选运算符,发现存在接受字符串类别和位字符串类别输入的候选项。由于字符串类别是首选的(如果可用),因此选择该类别,然后使用字符串的首选类型 text 作为解析未知类型字面量的具体类型。


示例 10.3. 绝对值和求反运算符类型解析

PostgreSQL 运算符目录有几个前缀运算符 @ 的条目,它们都实现了各种数值数据类型的绝对值操作。其中一个条目是为 float8 类型设计的,它是数值类别中的首选类型。因此,当面对 unknown 输入时,PostgreSQL 将使用该条目。

SELECT @ '-4.5' AS "abs";
 abs
-----
 4.5
(1 row)

在这里,系统在应用选定的运算符之前,已隐式地将未知类型的字面量解析为 float8 类型。我们可以验证使用的是 float8 而不是其他类型。

SELECT @ '-4.5e500' AS "abs";

ERROR:  "-4.5e500" is out of range for type double precision

另一方面,前缀运算符 ~(按位求反)仅为整数数据类型定义,而不是为 float8 定义。因此,如果我们尝试使用 ~ 进行类似的尝试,则会得到:

SELECT ~ '20' AS "negation";

ERROR:  operator is not unique: ~ "unknown"
HINT:  Could not choose a best candidate operator. You might need to add
explicit type casts.

发生这种情况是因为系统无法确定应优先使用哪个可能的 ~ 运算符。我们可以通过显式强制转换来帮助它。

SELECT ~ CAST('20' AS int8) AS "negation";

 negation
----------
      -21
(1 row)

示例 10.4. 数组包含运算符类型解析

这是另一种解析具有一个已知和一个未知输入的运算符的示例。

SELECT array[1,2] <@ '{1,2,3}' as "is subset";

 is subset
-----------
 t
(1 row)

PostgreSQL 运算符目录有几个中缀运算符 <@ 的条目,但只有两个可能接受左侧的整数数组:数组包含(anyarray <@ anyarray)和范围包含(anyelement <@ anyrange)。由于这些多态伪类型(参见第 8.21 节)都不是首选类型,因此解析器无法在此基础上解析歧义。但是,步骤 3.f 告诉它假定未知类型的字面量与另一个输入(即整数数组)具有相同的类型。现在只有一个运算符可以匹配,因此选择数组包含。(如果选择了范围包含,则会收到错误,因为字符串的格式不正确,无法作为范围字面量。)


示例 10.5. 域类型上的自定义运算符

用户有时会尝试声明仅应用于域类型的运算符。这是可能的,但不如看起来有用,因为运算符解析规则旨在选择应用于域基础类型的运算符。例如,考虑以下:

CREATE DOMAIN mytext AS text CHECK(...);
CREATE FUNCTION mytext_eq_text (mytext, text) RETURNS boolean AS ...;
CREATE OPERATOR = (procedure=mytext_eq_text, leftarg=mytext, rightarg=text);
CREATE TABLE mytable (val mytext);

SELECT * FROM mytable WHERE val = 'foo';

此查询不会使用自定义运算符。解析器将首先检查是否存在 mytext = mytext 运算符(步骤 2.a),但不存在;然后它将考虑域的基础类型 text,并检查是否存在 text = text 运算符(步骤 2.b),存在;因此,它将未知类型的字面量解析为 text,并使用 text = text 运算符。要使用自定义运算符的唯一方法是显式强制转换字面量。

SELECT * FROM mytable WHERE val = text 'foo';

以便根据精确匹配规则立即找到 mytext = text 运算符。如果达到最佳匹配规则,则它们会主动歧视域类型上的运算符。如果它们不这样做,这样的运算符将导致过多的歧义运算符失败,因为强制转换规则始终将域视为可强制转换为其基础类型或从其基础类型强制转换,因此域运算符将在与基础类型上同名运算符相同的情况下被考虑使用。




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

提交更正

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