支持的版本:当前 (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),而存在;因此它将 unknown 类型的字面量解析为 text,并使用 text = text 操作符。要使自定义操作符被使用,唯一的方法是显式地强制转换该字面量

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

这样,根据精确匹配规则,就可以立即找到 mytext = text 操作符。如果达到最佳匹配规则,它们会主动排斥域类型上的操作符。如果它们不这样做,这样的操作符会产生太多歧义操作符失败,因为强制转换规则始终认为一个域可以强制转换为或从其基本类型强制转换,因此域操作符将被认为可以在与基本类型上类似名称的操作符相同的所有情况下使用。




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

提交更正

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