编组
AntDB基于Oracle兼容下的DECODE函数适配
News-2022-09-12
亚信安慧科技
在去'O'大背景下,为了减轻用户去'O'成本以及加快去'O'速度,很多国产数据库厂商都会在数据库产品上做Oracle兼容性适配。AntDB作为一款成熟、稳定的国产数据库,自然也兼容Oracle,并且兼容程度高达95%以上。本文将从Oracle的DECODE函数着手,分析一下AntDB的适配方案。

01

Oracle的DECODE函数简介


  • 语法
图1-1 Oracle的DECODE函数语法


  • 功能
DECODE将expr依次与search比较,如果expr与某个search相等,则返回对应的result,如果没有找到相等的search则返回default。如果没有指定default,则返回NULL。


函数含义相当于:


图1-2 Oracle的DECODE函数语义

  • 示例
示例如下

1-3  Oracle的DECODE函数使用例


02

AntDB适配方案


Oracle的DECODE函数功能和AntDB的CASE表达式的功能非常相似,而AntDB也确实利用CASE表达式适配了DECODE函数的功能。


AntDB的CASE表达式

  • 语法1
2-1 AntDB的CASE表达式语法1

  • 功能1
CASE子句可以用于任何表达式可以出现的地方。每一个condition是一个返回boolean结果的表达式。如果结果为真,那么CASE表达式的结果就是符合条件的result,并且剩下的CASE表达式不会被处理。如果条件的结果不为真,那么以相同方式搜寻任何随后的WHEN子句。如果没有WHEN condition为真,那么CASE表达式的结果就是ELSE子句里的result。如果省略了ELSE子句而且没有条件为真,则CASE表达式结果为空。


  • 示例1

2-2 AntDB的CASE表达式使用例1

  • 语法2


2-3 AntDB的CASE表达式语法2

  • 功能2

第一个expression会被计算,然后与所有在WHEN子句中的每一个value对比,直到找到一个相等的。如果没有找到匹配的,则返回在ELSE子句中的result(或者空值)。这类似于 C 里的switch语句。

  • 示例2

2-4 AntDB的CASE表达式使用例2

AntDB利用CASE表达式的语法2,对DECODE函数进行了适配。


03

DECODE函数适配概要


3.1 语法分析阶段

AntDB利用CASE表达式的语法2,对DECODE函数进行了适配,在语法分析时将DECODE函数转换成了CASE表达式。


转换前:


3-1 语法转换前的DECODE语法

转换后:


3-2 语法转换后CASE表达式语法


3.2 语义解析阶段


AntDB在对CASE表达式的语义解析阶段,添加了对DECODE函数的一些细节处理。


主要添加了以下2个处理:
1. DECODE函数的expr参数与search参数比较时,选择合适的比较类型
AntDB的CASE表达式选择了expr参数的类型作为与各个search参数比较时使用的类型。
为了与ORACLE的行为一致,AntDB在适配DECODE函数时,选择了第一个search参数的类型作为比较时使用的类型。
2. search参数为空字符串时作为NULL处理
这个是ORACLE的另一个兼容性适配,这里不做详细说明。


04

DECODE函数适配详细


4.1 语法分析阶段

语法分析时,将DECODE函数转成CASE表达式。详细请参考以下代码。


代码文件:src/backend/parser/ora_gram.y


4-1 语法规则中的适配

在函数的语法规则里追加了对decode函数的特殊处理(参考上述代码中的第一个if块):


  • 对decode函数的参数个数进行检查
由于decode函数的最后一个参数可以省略,则参数个数至少为3。少于3个参数则报语法错误。


  • 通过reparsedecodefunc函数将decode函数转成CaseExpr
在说明reparsedecodefunc函数的转换处理之前,先了解下CASE表达式相关的结构体。

CASE表达式结构体CaseExpr

4-2 CaseExpr结构体

CaseExpr结构体成员的说明如下。


- casetype
CASE表达式结果类型,在语义解析时才能知道,语法分析时赋值为InvalidOid。
- casecollid
排序规则的OID,语法层不做处理。
- arg
CASE关键字后面的参数,即DECODE函数的第一个参数expr。
- args
WHEN语句列表。
每个WHEN语句包含对应的search和result参数。
WHEN语句对应的结构体参考如下。

4-3 CaseWhen结构体

CaseWhen结构体的成员说明如下。


  • expr
条件表达式。
CASE关键字后的参数(expr)与WHEN关键字后的参数(search)是否相等的表达式。
  • result
WHEN语句中search对应的result。
  • location
token的位置信息
- defresult
ELSE关键字后参数,即CASE表达式的默认结果。
- isdecode
标记是否是ORACLE的DECODE函数。
- location
token的位置信息

reparse_decode_func函数的转换处理

先看下该函数的代码:


4-4 reparse_decode_func函数实现

reparsedecodefunc()函数的参数args是DECODE函数的参数列表,该函数的主要处理逻辑如下:


1. 新作成一个CaseExpr节点


2. 利用DECODE函数的参数列表,设置CaseExpr的主要成员casetype
语法层赋值为InvalidOid。
  • isdecode
只有ORACLE的DECODE函数才会进reparsedecodefunc(),因此设置为true。
  • arg
设置为DECODE函数的第一个参数expr。
  • args
1) 将DECODE函数的每一对search/result参数作成CaseWhen节点设置CaseWhen主要成员:
  • expr
设置为DECODE函数的search参数。
注意:
语法层并没有作成DECODE函数的第一个参数与search参数的等号表达式,后面的语义解析阶段会做这件事。
search参数为NULL时,利用DECODE函数的第一个参数作成IS_NULL表达式,并赋值给CaseWhen的expr成员。
  • result
设置为search参数对应的result参数。
2) 将CaseWhen节点追加到args列表
defresult
设置为DECODE函数的最后一个参数。


3. 返回CaseExpr节点


4.2 语义解析阶段


AntDB的CASE表达式是通过transformCaseExpr()函数进行解析的。语法分析时ORACLE的DECODE函数被转换成了CASE表达式,因此DECODE函数的语义解析也将通过transformCaseExpr()函数进行解析。

transformCaseExpr函数的主要处理流程(红色部分为DECODE函数适配追加)请参考图4-5。

4-5  语义解析阶段的处理流程

如图所示,语义解析时,为了与ORACLE的行为一致,AntDB为DECODE函数做了一些细节上的处理。


05

DECODE函数适配详细


经过AntDB适配后的DECODE函数,演示结果如下


5-1 适配后的结果演示



06

总结


Oracle功能适配时,利用AntDB既有的功能,可能起到事半功倍的效果。当然,熟悉和理解AntDB既有的功能,也是必不可少的技能。
DECODE函数的适配只是Oracle兼容性开发的冰山一角,AntDB在Oracle兼容上有着深厚的积累和独特的优势,想要去'O'的同学,可以试试AntDB。