10.2. Operators

使用以下过程确定由运算符表达式引用的特定运算符。请注意,此过程受所涉及运算符的优先级间接影响,因为这将确定将哪些子表达式作为哪个运算符的 Importing。有关更多信息,请参见Section 4.1.6

操作员类型分辨率

以下是一些示例。

例 10.1. 析因运算符类型解析

在标准目录中仅定义了一个阶乘运算符(后缀!),并且它接受类型为bigint的参数。扫描程序将初始类型integer分配给此查询表达式中的参数:

SELECT 40 ! AS "40 factorial";

                   40 factorial
--------------------------------------------------
 815915283247897734345611269596115894272000000000
(1 row)

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

SELECT CAST(40 AS bigint) ! AS "40 factorial";

示例 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)

在这种情况下,由于没有在查询中指定任何类型,因此没有使用哪种类型的初始提示。因此,解析器将查找所有候选运算符,并发现存在同时接受字符串类别和位字符串类别 Importing 的候选者。由于字符串类别在可用时是首选的,因此将选择该类别,然后将字符串的首选类型text用作特定类型,以将未知类型的 Literals 解析为。

例 10.3. 绝对值和负运算符类型解析

PostgreSQL 运算符目录中有几个前缀运算符@的条目,所有条目都对各种数字数据类型实现绝对值运算。这些条目之一用于类型float8,这是数字类别中的首选类型。因此,当遇到unknownImporting 时,PostgreSQL 将使用该条目:

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

在这里,系统在应用所选的运算符之前已将未知类型的 Literals 隐式解析为类型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. 数组包含运算符类型解析

这是使用一个已知 Importing 和一个未知 Importing 来解析运算符的另一个示例:

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

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

PostgreSQL 运算符目录中有几个用于 infix 运算符<@的条目,但是可能在左侧接受整数数组的仅有两个条目是数组包含(anyarray <@ anyarray)和范围包含(anyelement <@ anyrange)。由于这些多态伪类型(请参见Section 8.20)都不被认为是首选,因此解析器无法在此基础上解决歧义。但是,Step 3.f告诉它假定未知类型 Literals 与其他 Importing(即整数数组)具有相同的类型。现在,只有两个运算符之一可以匹配,因此选择了数组包含。 (如果选择了范围内包含,我们将得到一个错误,因为字符串的格式不正确,不能作为范围 Literals.)

实施例 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运算符(Step 2.a),而没有。然后它将考虑域的基本类型text,并查看是否存在text = text运算符(Step 2.b);因此它将unknown类型的 Literals 解析为text并使用text = text运算符。获取要使用的自定义运算符的唯一方法是显式转换 Literals:

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

以便根据完全匹配规则立即找到mytext = text运算符。如果达到最佳匹配规则,则它们会积极区分域类型上的运算符。否则,这样的运算符会造成太多的模棱两可的运算符故障,因为强制转换规则始终将域视为可从其基本类型或从其基本类型强制转换的域,因此在与域类型相同的所有情况下,域运算符都将被视为可用基本类型上名称相似的运算符。


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

上一章 首页 下一章