2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本: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),存在;因此它将 unknown 类型字面量解析为 text 并使用 text = text 运算符。要使用自定义运算符,唯一的方法是显式转换字面量

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

以便根据精确匹配规则立即找到 mytext = text 运算符。如果达到最佳匹配规则,它们会主动区分域类型上的运算符。如果没有,这样的运算符会导致太多不明确运算符错误,因为转换规则始终认为域可以转换为或来自其基本类型,因此域运算符将在所有与基本类型上同名运算符相同的情况下被视为可用。




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

提交更正

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