10.2. 操作符

下面讲解的过程解释了如何在一次操作符调用中确定所使用的究竟是哪个操作符。 请注意这个过程受被调用操作符的优先级影响。参阅Section 4.1.6获取更多信息

操作符类型解析

  1. 从系统表pg_operator中选出要考虑的操作符。如果使用了一个不带修饰的操作符 名(常见的状况),那么认为该操作符是那些在当前搜索路径中名字和参数个数都正确的 操作符(参阅 Section 5.7.3)。 如果给出一个带修饰的操作符名,那么只考虑指定模式中的操作符。

    1. 如果搜索路径中找到了多个相同参数类型的操作符,那么只考虑最早出现在路径中的那一个。 但是不同参数类型的操作符将被平等看待,而不管它们在路径中的位置如何。

  2. 查找精确接受输入参数类型的操作符。如果找到一个(在一组被考虑的操作符中,可能只存在一个精确匹配的), 则用之。

    1. 如果一个双目操作符调用中的一个参数是unknown类型,则在本次检查中假设其与另一个参数类型相同。 其它涉及unknown的情况绝不会在此处找到匹配。

  3. 寻找最优匹配。

    1. 抛弃那些输入类型不匹配并且也不能隐式转换成匹配的候选操作符。unknown文本在这种情况下 可以转换成任何东西。如果只剩下一个候选项,则用之,否则继续下一步。

    2. 遍历所有候选操作符,保留那些输入类型匹配最准确的。此时,域被看作和他们的基本类型相同。如果没有一个操作符能被保留,则保留所有候选。 如果只剩下一个候选项,则用之,否则继续下一步。

    3. 遍历所有候选操作符,保留那些需要类型转换时接受(属于输入数据类型的类型范畴的)首选类型位置 最多的操作符。如果没有接受首选类型的操作符,则保留所有候选。如果只剩下一个候选项,则用之, 否则继续下一步。

    4. 如果有任何输入参数是 unknown类型,检查剩余的候选操作符对应参数位置的类型范畴。 在每一个能够接受字符串类型范畴的位置使用string类型(这种对字符串的偏爱是合适的, 因为unknown文本确实像字符串)。另外,如果所有剩下的候选操作符都接受相同的类型 范畴,则选择该类型范畴,否则抛出一个错误(因为在没有更多线索的条件下无法作出正确 的选择)。现在抛弃不接受选定的类型范畴的候选操作符,然后,如果任意候选操作符在 某个给定的参数位置接受一个首选类型, 则抛弃那些在该参数位置接受非首选类型的候选操作符。

    5. 如果只剩下一个操作符,则用之。如果还有多个候选操作符或者没有候选操作符,则产生一个错误。

下面是一些例子。

Example 10-1. 指数操作符类型解析

只有一个在标准目录里被指定阶乘的操作符,它接受一个参数类型bigint。 扫描器为在查询表达式中的参数指定一个初始类型integer

SELECT 40 ! AS "40 factorial";

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

因此,解析器在操作数上做了一个类型转换,该查询相当于:

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

Example 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作为解决未知文字的指定类型。

Example 10-3. 绝对值与否定操作符类型解析

PostgreSQL操作符目录中有几个对于前缀操作符@的条目, 这些都现实了针对各种数字数据类型的绝对值操作符。 其中有一条为float8类型,它是在数字类型中首选的类型。 因此,PostgreSQL将在遇到一个未知类型的输入时使用它:

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

在这里,系统在接受选取操作符之前已经隐含地解决了将未知类型文字作为float8类型。 我们可以确认我们使用的是float8而不是别的类型:

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

ERROR:  "-4.5e500" 超出了双精度类型范围

另一方面,前缀符~只在整数数据类型的时候被指定,并不是为float8。 因此,如果我们尝试一个使用~的类似操作,我们可以这样:

SELECT ~ '20' AS "negation";

ERROR:  操作符不唯一: ~ "未知"
HINT: 不能选择一个最佳的候选操作符。你或许需要添加显式类型。

此情况的发生是由于系统不能决定在几个可能的~符号中哪个是指定的。 我们可以帮助它做出一个明确的选择:

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

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