更新时间:2024-11-27 GMT+08:00

操作符

操作符类型解析

  1. 从系统表PG_OPERATOR中选出要考虑的操作符。如果可以找到一个参数类型以及参数个数都一致的操作符,那么这个操作符就是最终使用的操作符。如果找到了多个备选的操作符,那么将从中选择一个最合适的。
  2. 寻找最优匹配。
    1. 丢弃输入类型不匹配以及无法隐式转换成匹配的候选操作符。unknown文本在这种情况下可以转换成任何类型。如果只剩下一个候选操作符,则使用,否则继续下一步。

    2. 查看所有候选操作符,并保留输入类型最匹配的操作符。此时,域被看作和其基本类型相同。如果没有完全匹配的操作符,则保留所有候选。如果只剩下一个候选操作符,则使用,否则继续下一步。

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

    4. 如果有任何输入参数是unknown类型,请检查其余候选操作符对应参数位置的类型范畴。在每一个能够接受string类型范畴的位置使用string类型(这种偏向字符串的做法合理,因为unknown文本跟字符串相似)。另外,如果所有剩下的候选操作符都接受相同的类型范畴,则选择该类型范畴,否则会报错(因为在没有更多线索的条件下无法作出正确的选择)。现在丢弃不接受选定类型范畴的候选操作符。此外,如果有任意候选操作符接受该范畴中的首选类型,则丢弃该参数接受非首选类型的候选操作符。如果没有一个操作符能被保留,则保留所有候选。如果只剩下一个候选操作符,则使用,否则继续下一步。

    5. 如果同时有unknown和已知参数,并且所有已知参数都是相同的类型,那么假设unknown参数也属于该类型,并检查哪些候选操作符在unknown参数位置接受该类型。如果只有一个操作符符合,则使用。否则,报错。

示例

示例1:阶乘操作符类型解析。在系统表中里只有一个阶乘操作符(后缀!),它以bigint作为参数。扫描器给下面查询表达式的参数赋予bigint的初始类型:

1
SELECT 40 ! AS "40 factorial";
图1 返回结果

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

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

示例2:字符串连接操作符类型分析。一种字符串风格的语法既可以用于字符串也可以用于复杂的扩展类型。未声明类型的字符串将被所有可能的候选操作符匹配。有一个未声明的参数的例子:

1
SELECT text 'abc' || 'def' AS "text and unknown";
图2 返回结果

本例中分析器寻找两个参数都是text的操作符。确实有这样的操作符,两个参数都是text类型。

下面是连接两个未声明类型的值:

SELECT 'abc' || 'def' AS "unspecified";
图3 返回结果

因为查询中没有声明任何类型,所以本例中对类型没有任何初始提示。因此,分析器查找所有候选操作符,发现既存在接受字符串类型范畴的操作符也存在接受位串类型范畴的操作符。因为字符串类型范畴是首选,所以选择字符串类型范畴的首选类型text作为解析未知类型文本的声明类型。

示例3:绝对值和取反操作符类型分析。GaussDB(DWS)操作符表里面有几条记录对应于前缀操作符@,它们都用于为各种数值类型实现绝对值操作。其中之一用于float8类型,它是数值类型范畴中的首选类型。因此,在面对unknown输入的时候, GaussDB(DWS)会使用该类型:

1
SELECT @ '-4.5' AS "abs";
图4 返回结果

此处,系统在应用选定的操作符之前隐式的转换unknown类型的文字为float8类型。

示例4:数组包含操作符类型分析。以操作符两侧带有一个已知类型和一个未知类型的情况为例:

1
SELECT array[1,2] <@ '{1,2,3}' as "is subset";
图5 返回结果

GaussDB(DWS)的pg_operator表中有多条记录对应于中缀操作符<@,但是只有“数组包含(anyarray <@ anyarray) ”和“范围包含(anyelement <@ anyrange)”可在左侧接受一个整数数组。因为多态伪类型(参阅伪类型)不被认为是首选操作符类型,因此解析器无法在此基础上解决歧义。不过,寻找最优匹配的最后一条解析规则又指出,假定未知类型的文字和另一个已知的输入类型相同,也就是只有两个运算符之一可以匹配,所以选择数组包含。(如果选择了范围包含,就会报错,因为字符串格式不正确不能作为范围。)