操作符

下面讲解了如何在一次操作符调用中确定所使用的究竟是哪个操作符。请注意这个过程间接受被调用操作符的优先级影响。详阅 操作符优先级

操作符类型解析

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

    1. 如果搜索路径中找到了多个相同参数类型的操作符,那么只考虑最早出现在路径中的那一个。 但是不同参数类型的操作符将被平等看待,而不管它们在路径中的位置如何。
  2. 查找精确接受输入参数类型的操作符。如果找到一个(在一组被考虑的操作符中, 可能只存在一个精确匹配的),则用之。

    1. 如果一个双目操作符调用中的一个参数是unknown类型, 则在本次检查中假设其与另一个参数类型相同。包括两个unknown 输入的调用或一个一元带有unknown输入的操作符,将绝不会在此处找到匹配。
  3. 查找最佳匹配。

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

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

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

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

    1. 如果只有一个操作符符合,那么使用它。否则,产生一个错误。

下面是一些例子。

例.阶乘操作符类型解析

在系统表中里只有一个阶乘操作符,它以double precision类型作为参数。扫描器给下面查询表达式的参数赋予integer的初始类型:

SELECT 2 ^ 3 AS "exp";

 exp
-----
   8
(1 row)

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

SELECT CAST(2 AS double precision) ^ CAST(3 AS double precision) AS "exp";

例.字符串连接操作符类型分析

一种字符串风格的语法既可以用于字符串也可以用于复杂的扩展类型。 未声明类型的字符串将被所有可能的候选操作符匹配。

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

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 作为解析未知类型文本的声明类型。

例.绝对值和取反操作符类型分析

OushuDB 操作符表里面有几条记录对应于前缀操作符@, 它们都用于为各种数值类型实现绝对值操作。其中之一用于float8类型, 它是数值类型范畴中的首选类型。因此,在面对非数值输入的时候,OushuDB 会使用该类型:

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

此处,系统在应用选定的操作符之前隐式的转换text类型为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 may need to add explicit type casts.

这是因为系统无法决定几个可能的~操作符中究竟应该使用哪一个。 我们可以用明确地类型转换来帮它:

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

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