7.2. 操作符

一次操作符调用的操作数类型是按照下面的过程解析的. 请注意这个过程受被调用操作符的优先级的间接影响. 参阅 Section 1.1.6 获取更多信息.

操作符类型解析

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

    1. 如果搜索路径中找到了多个相同参数类型的操作符,那么只考虑 最早出现在路径中的.但是不同参数类型的操作符将以相同的基础 进行考虑,而不管它们在路径中的位置如何.

  2. 检查一个操作符是否刚好接受输入参数类型. 如果存在一个(在考虑的操作符中,可能只存在一个精确匹配的), 则用之.

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

  3. 寻找最优匹配.

    1. 抛弃那些输入类型不匹配并且也不能强制转换 (使用隐含转换函数)成匹配的候选操作符. unknown 文本在这种情况下被认为是 可疑转换成任何东西.如果只剩下一个候选项,用之; 否则继续下一步.

    2. 遍历所有候选操作符,保留那些输入类型有最多准确匹配的. 如果没有完全准确匹配的操作符,保留所有候选. 如果只有一个,用之,否则继续下一步.

    3. 遍历所有候选操作符,保留那些输入类型有最多准确匹配或者有二进制兼容匹配的. 如果没有完全准确匹配而且也没有二进制兼容匹配的操作符,保留所有候选. 如果只有一个,用之,否则继续下一步.

    4. 遍历所有候选操作符,保留那些需要类型转换时最多位置接受优选类型的. 如果没有接受优选类型的操作符,则保留所有候选. 如果只有一个,用之,否则继续下一步.

    5. 如果任何输入参数是 "unknown",检查剩下的候选操作符对应参数位置的 类型表.如果任何候选操作符接受 "string" 类型,则在那些位置选 "string" 类型(这个假设认为 string 是合适的,因为 unknown 类型文本确实象 string). 否则,如果所有剩下的候选操作符接受相同的类型,选择该类型; 否则抛出一个错误,因为在没有更多线索的条件下不能导出正确的选择. 还要注意是否有哪个候选操作符在选出的类型表中有优选类型. 现在抛弃不接受选定的类型表的候选操作符;然后, 如果任意候选操作符在某个给定的参数位置接受一个优选类型, 则抛弃那些在该参数位置接受非优选类型的候选操作符.

    6. 如果只剩下一个操作符,用之. 如果还有超过一个的候选操作符或是没有候选操作符,则产生一个错误.

例子

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

在分类里只有一个指数操作符,它以 double precision 作为参数.扫描器给下面查询表达式的两个参数赋予 integer 的初始类型:

tgl=> SELECT 2 ^ 3 AS "Exp";
 Exp
-----
   8
(1 row)

分析器对两个参数都做类型转换,查询等效于:

tgl=> SELECT CAST(2 AS double precision) ^ CAST(3 AS double precision) AS "Exp"; Exp
-----
   8
(1 row)

或者

tgl=> SELECT 2.0 ^ 3.0 AS "Exp";
 Exp
-----
   8
(1 row)

注意: 最后的形式最高效,因为不用调用函数做隐含类型转换. 这对小查询没有什么影响,但可能对那些操 作大表的查询的性能产生较大影响.

Example 7-2. 字串连接操作符类型分析

一种类字符串的语法既可以用于字符串也可以用于复杂的扩展类型. 包含不明类型的字串使用可能的候选操作符匹配.

有一个未声明的参数的例子:

tgl=> SELECT text 'abc' || 'def' AS "Text and Unknown";
 Text and Unknown
------------------
 abcdef
(1 row)

本例中分析器寻找一个两个参数都是 text 的操作符.因为有一个这样的操作符,它认为另一个参数的类型是 text

联接未声明类型:

tgl=> SELECT 'abc' || 'def' AS "Unspecified";
 Unspecified
-------------
 abcdef
(1 row)

本例中对类型任何初始提示,因为查询中没有声明任何类型. 因此,分析器查找所有参数可以同时接受字符串类和位串类的候选操作符. 因为在可用时字串类是优选,所以选择该表, 于是字串的"优选类型"text,作为解析未知类型文本的声明类型.

Example 7-3. 绝对值和阶乘类型分析

PostgreSQL 操作符表里面有几条 记录用于前缀操作符 @,所有这些都是为各种数值类型实现 绝对值操作的.其中有一条用于类型 float8, 它是数值表中的优选类型.因此, 在面对非数值输入的时候,PostgreSQL 会使用该类型∶

tgl=> select @ text '-4.5' as "abs";
 abs
-----
 4.5
(1 row)

在这里系统在应用选定的操作符之前执行类一次 text 到 float8 的转换.我们可以验证它是 float8 而不是其它什么类型∶

tgl=> select @ text '-4.5e500' as "abs";
ERROR:  Input '-4.5e500' is out of range for float8

另一方面,前缀操作符 ! (阶乘) 只为整数数据类型定义, 而不是为 float8 定义的.因此,如果我们用 ! 做类似 实验,就有∶

tgl=> select text '44' ! as "factorial";
ERROR:  Unable to identify a postfix operator '!' for type 'text'
        You may need to add parentheses or an explicit cast

这是因为系统无法决定好几个可能的 ! 操作符中 应该用哪个.我们可以用明确地类型转换来帮它∶

tgl=> select cast(text '44' as int8) ! as "factorial";
      factorial
---------------------
 2673996885588443136
(1 row)