
# 操作符
#### 操作符类型解析
1. 从系统表pg_operator中选出要考虑的操作符。如果可以找到一个参数类型以及参数个数都一致的操作符，那么这个操作符就是最终使用的操作符。如果找到了多个备选的操作符，将从中选择一个最合适的。
2. 寻找最优匹配。
   1. 抛弃那些输入类型不匹配并且也不能隐式转换成匹配的候选操作符。unknown文本在这种情况下可以转换成任何东西。如果只剩下一个候选项，则用之，否则继续下一步。
   
   2. 遍历所有候选操作符，保留那些输入类型匹配最准确的。域类型看做和域类型的基本类型相同。如果没有一个操作符能被保留，则保留所有候选。如果只剩下一个候选项，则用之，否则继续下一步。
   
   3. 遍历所有候选操作符，保留那些需要类型转换时接受（属于输入数据类型的类型范畴的）首选类型位置最多的操作符。如果没有接受首选类型的操作符，则保留所有候选。如果只剩下一个候选项，则用之，否则继续下一步。
   
   4. 如果有任何输入参数是unknown类型，检查剩余的候选操作符对应参数位置的类型范畴。在每一个能够接受字符串类型范畴的位置使用string类型（这种对字符串的偏爱是合适的，因为unknown文本确实像字符串）。如果所有剩下的候选操作符都接受相同的类型范畴，则选择该类型范畴，否则抛出一个错误（因为在没有更多线索的条件下无法作出正确的选择）。现在抛弃不接受选定的类型范畴的候选操作符，如果任意候选操作符在某个给定的参数位置接受一个首选类型，则抛弃那些在该参数位置接受非首选类型的候选操作符。如果没有一个操作符能被保留，则保留所有候选。如果只剩下一个候选项，则用之，否则继续下一步。
   
   5. 如果同时有unknown和已知类型的参数，并且所有已知类型的参数都是相同的类型，那么假设unknown参数也是那种类型，并检查哪一个候选操作符在unknown参数位置接受该类型。如果只有一个操作符符合，那么使用它。否则产生一个错误。
      ![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/caution_3.0-zh-cn.png)
      在找到一个操作符后，如果输入的参数类型和操作符的参数类型不一致，可能会发生隐式类型转换，转换后可能发生不可预知的行为。如果隐式转换后行为有问题，可以通过显式类型转换规避此问题。例如，定长类型bpchar转换为变长类型text后，会消除字符串行尾空格，如果再和其它字符串比较时可能会发生错误行为。
      
    
 
#### 示例
示例1：阶乘操作符类型解析。在系统表中里只有一个阶乘操作符（后缀!），它以bigint作为参数。扫描器给下面查询表达式的参数赋予bigint的初始类型：
```
gaussdb=# SELECT 40 ! AS "40 factorial";
                   40 factorial
--------------------------------------------------
 815915283247897734345611269596115894272000000000
(1 row)
```
分析器对参数做类型转换，查询等效于：
```
gaussdb=# SELECT CAST(40 AS bigint) ! AS "40 factorial";
```
示例2：字符串连接操作符类型分析。一种字符串风格的语法既可以用于字符串也可以用于复杂的扩展类型。未声明类型的字符串将被所有可能的候选操作符匹配。有一个未声明的参数的例子：
```
gaussdb=# SELECT text 'abc' || 'def' AS "text and unknown";
 text and unknown
------------------
 abcdef
(1 row)
```
本例中分析器寻找两个参数都是text的操作符。确实有这样的操作符，两个参数都是text类型。
下面是连接两个未声明类型的值：
```
gaussdb=# SELECT 'abc' || 'def' AS "unspecified";
 unspecified
-------------
 abcdef
(1 row)
```
![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
因为查询中没有声明任何类型，所以本例中对类型没有任何初始提示。因此，分析器查找所有候选操作符，发现既存在接受字符串类型范畴的操作符也存在接受位串类型范畴的操作符。因为字符串类型范畴是首选，所以选择字符串类型范畴的首选类型text作为解析未知类型文本的声明类型。
示例3：绝对值和取反操作符类型分析。GaussDB操作符表里面有几条记录对应于前缀操作符@，它们都用于为各种数值类型实现绝对值操作。其中之一用于float8类型，它是数值类型范畴中的首选类型。因此，在面对unknown输入的时候， GaussDB会使用该类型：
```
gaussdb=# SELECT @ '-4.5' AS "abs";
 abs
-----
 4.5
(1 row)
```
系统在应用选定的操作符之前隐式的转换unknown类型的文字为float8类型。
示例4：数组包含操作符类型分析。这里是解决一个操作符带有一个已知和一个未知类型输入的例子：
```
gaussdb=# SELECT array[1,2] <@ '{1,2,3}' as "is subset";
 is subset
-----------
 t
(1 row)
```
![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
GaussDB操作符表有几条记录对应于中缀操作符\<@，但是只有两个可以在左侧接受一个整数数组的操作符是数组包含(anyarray \<@ anyarray) 和范围包含(anyelement \<@ anyrange)的。因为没有多态的伪类型(参阅[伪类型](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/gaussdb-12-0339.html#ZH-CN_TOPIC_0000002559105783))是首选的，所以解析器不能解决这个基础上的歧义。然而最后一个解析规则告诉用户，假设未知类型的文字是和另外一个输入相同的类型，那就是整数数组。现在只有两个操作符中的一个可以匹配，所以选择数组包含。（如果用户选择了范围包含，用户将得到一个错误，因为字符串没有正确的格式成为范围的文字。）
