暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

万里数据库GreatDB支持Oracle SQL语法兼容

原创 Dbb 2024-05-16
733

Oracle兼容的整体思路

GreatDB的Oracle的兼容性处理的优先原则如下:

  1. GreatDB/MySQL风格的SQL和存储过程可以直接MYSQL默认模式下工作
  2. 与GreatDB/MySQL风格的SQL和存储过程不冲突的Oracle兼容功能也可以在MYSQL默认模式下直接工作
  3. 与GreatDB/MySQL风格的SQL和存储过程存在语法或语义冲突的功能,需要用户显式设定工作模式

GreatDB对Oracle的兼容性主要通过以下方式实现:

  • 原生支持:Oracle、MySQL等关系型数据库都支持或部分支持通用的SQL标准,对于部分简单语句,无需调整即可兼容

  • 兼容扩展:GreatDB对于部分Oracle独有的函数和语法,在SQL引擎层实现对MySQL的扩展,如:MERGE INTO, CONNECT BY,该类型兼容无需设定,直接使用即可

  • 兼容模式:在GreatDB中开启:SET sql_mode='ORACLE'; 可以在当前会话中开启Oracle兼容模式,该设定解决Oracle语法与MySQL语法存在语法或语义上的冲突时,需要用户选择Oracle兼容模式

数据类型的映射

数据类型映射属于:扩展兼容(无需设定sql_mode),实现方式为:简单别名,即:在解析阶段将关键词进行替换,如果使用CLOB创建的表,在系统内会被转换成LONGTEXT。

具体实现的映射包括:

Oracle类型GreatDB类型兼容性
CLOBLONGTEXT简单别名
NUMBERDECIMAL简单别名
VARCHAR2VARCHAR简单别名
PLS_INTEGERINT简单别名

Oracle SQL语法兼容

GreatDB实现的扩展兼容(无需设定sql_mode), 包括:

  • DATETIME INTERVAL
  • EXECUTE IMMEDIATE
  • EXPLAIN PLAN FOR
  • INSERT ALL INTO
  • MERGE INTO
  • CONNECT BY [START WITH] | START WITH CONNECT BY
  • ORACLE HINT
  • ROWNUM
  • PRIOR
  • CONNECT_BY_ROOT
  • LEVEL
  • CONNECT_BY_ISLEAF
  • CONNECT_BY_ISCYCLE
  • SEQUENCE
  • 子查询无别名
  • (+) 外连接
  • 空串''与null等价开关
  • KEEP FIRST/LAST
  • MINUS
  • SELECT...OFFSET...FETCH
  • SELECT...FOR UPDATE OF COLUMNS
  • ...

GreatDB实现的兼容模式(需设定sql_mode),包括:

  • DATETIME运算(加减)
  • 带双引号的存储过程创建
  • MATERIALIZED VIEW
  • DELETE语句支持不带FROM
  • ORDER BY兼容
  • RATIO_TO_REPORT
  • SELECT...FOR UPDATE WAIT N
  • UPDATE SET 多字段更新
  • SQLCODE_SQLERRM_FUNCTION
  • 全局临时表
  • ...

Oracle函数兼容

GreatDB实现的扩展兼容(无需设定sql_mode),包括:

  • ADD_MONTHS
  • CHR
  • DECODE
  • DUMP
  • SYS_GUID
  • INITCAP
  • LENGTHB
  • LIST_AGG
  • MONTHS_BETWEEN
  • NVL
  • NVL2
  • REGEXP_COUNT
  • REPLACE
  • SUBSTRB
  • SYSTIMESTAMP
  • TO_CHAR
  • TO_DATE
  • TO_NUMBER
  • TO_TIMESTAMP
  • TRANSLATE
  • TRUNC
  • VSIZE
  • SYS_CONNECT_BY_PATH
  • ...

GreatDB实现的兼容模式(需设定sql_mode=oracle),包括:

  • INSTR
  • LPAD/RPAD
  • TRIM/LTRIM/RTRIM
  • SUBSTR
  • LENGTH
  • SYSDATE
  • ...

NOTE: 以上函数在设定sql_mode后,行为与Oracle会更加接近;反之则保持GreatDB/MySQL的原生行为

存储过程的兼容

GreatDB实现的扩展兼容(无需设定sql_mode),包括:

  • CREATE OR REPLACE
  • ...

GreatDB实现的兼容模式(需设定sql_mode=oracle),包括:

存储过程/函数基础结构兼容

  • CURSOR游标

  • REF_CURSOR

  • EXIT/EXIT WHEN

  • FOR LOOP

  • FORALL LOOP

  • SELECT BULK_INTO

  • VAR_TYPE

  • TRIGGER

  • TYPE IS RECORD

  • TYPE IS TABLE

  • 异常处理 EXCEPTION HANDLER

  • IF .. ELSIF支持

  • WHILE...LOOP... END LOOP

  • 存储过程/函数支持默认参数(DEFAULT)

  • 存储过程支持使用RETURN

  • 匿名存储块

  • 命名标记法传递参数

  • PACKAGE

  • ...

NOTE: PACKAGE是ORACLE专有的功能,内部的存储过程和函数使用Oracle兼容的语法。

示例:

Oracle版本 存储过程:

CREATE OR REPLACE EDITIONABLE FUNCTION f0(delta INT DEFAULT 0) RETURN TIMESTAMP AS
    cnt INT := 10;
BEGIN
    RETURN SYSDATE + delta*cnt;
END;

SELECT f0(2) FROM DUAL ;
SELECT f0() FROM DUAL ;

GreatDB 原生版本 存储过程:

CREATE OR REPLACE FUNCTION f1(delta INT) RETURNS TIMESTAMP
BEGIN
    DECLARE cnt INT DEFAULT 10;
    SET delta = cnt * delta;
    RETURN DATE_ADD(SYSDATE(), INTERVAL delta DAY);
END;

SELECT f0(2) FROM DUAL ;

GreatDB 兼容模式 版本存储过程:

SET sql_mode='ORACLE';

CREATE OR REPLACE FUNCTION f0(delta INT DEFAULT 0) RETURN TIMESTAMP AS
    cnt INT := 10;
BEGIN
    RETURN SYSDATE + delta*cnt;
END;

SELECT f0(2) FROM DUAL ;
SELECT f0() FROM DUAL ;

常见问题

引入兼容模式或者说 SQL_MODE='ORACLE' 对版本平滑升级有什么影响?

  • 5.0.6中存在部分要求兼容模式的函数,在5.0.7以及后续版本中要求开启sql_mode='ORACLE'才能使用,但在5.0.7中开启兼容mode后,存储过程要按照oracle的格式进行调整会,因此会导致不兼容,无法平滑升级。该部分不兼容只能通过手动重建方式进行调整。如果存储过程完全按照GreatDB原生方式或者按照兼容模式方式书写,则都可以平滑升级。

  • oracle_mode是一个组合mode,存在添加的新的行为限制,为了兼容历史版本的行为,需要配合 shrink_sql_mode 进行组合

能否不显式使用 SQL_MODE='ORACLE' 来触发兼容模式?

  • 在GreatDB配置档中添加SQL_MODE='ORACLE'将导致系统功能异常,原因是:系统所依赖的基础存储过程并没有被执行兼容改造
  • 在 GreatDB 初始化完成后,添加在GreatDB配置档中添加SQL_MODE='ORACLE' 并重启系统,默认新建连接使用oracle mode ( MGR方式的集群不确定是否可用)
  • 在 GreatDB 启动的数据库中进行全局修改 set global sql_mode=oracle,这样默认新建连接就是使用 oracle 模式, 缺点每次重启都需要重新设置一次,集群这样配置不确定生效
  • 在特定场景下,可以将SQL_MODE='ORACLE'在会话配置中固化,如:在JDBC连接串中添加:session_variables=sql_mode=ORACLE,这样连接中SQL就无需显式设定兼容模式

能否在存储过程内部切换兼容模式?

在GreatDB中兼容模式或者说sql_mode是存储过程的固有属性,决定了该存储过程如何被解析和运行,在存储过程中修改sql_mode 可以改变当前的兼容模式,但当该存储过程运行结束返回后,上层上下文当中的兼容模式会被还原(与存储过程运行前一致)

举例说明:

SET SQL_MODE=DEFAULT;

DROP PROCEDURE mode_test;

DELIMITER $ ;
CREATE PROCEDURE mode_test() BEGIN
 SELECT LENGTH('测试'), @@SQL_MODE;
 SET SQL_MODE='ORACLE';
 SELECT LENGTH('测试'), @@SQL_MODE;
END ;
$

DELIMITER ; $

SET SQL_MODE=DEFAULT;
SELECT 'before test 1', LENGTH('测试'),@@SQL_MODE;
CALL mode_test();
SELECT 'after test 1', LENGTH('测试'),@@SQL_MODE;

该存储过程运行结果如下:

> SET SQL_MODE=DEFAULT;

-- 执行测试前调用LENGTH,输出按照GreatDB默认行为,显示字符字节长度=6
> SELECT 'before test 1', LENGTH('测试'),@@SQL_MODE;
| before test 1 | LENGTH('测试')   | @@SQL_MODE | 
| before test 1 |                6 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO NO_ENGINE_SUBSTITUTION |
1 row in set (0.00 sec)

> CALL mode_test();
-- 存储过程中,前调用LENGTH,输出按照GreatDB默认行为,显示字符字节长度=6
| LENGTH('测试')   | @@SQL_MODE  |
|                6 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
1 row in set (0.00 sec)


-- 存储过程中,设定Oracle兼容模式,调用LENGTH,输出按照Oracle兼容行为,显示字符字节长度=2  
| LENGTH('测试')   | @@SQL_MODE                                                     |
|                2 | PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,SYSDATE_IS_NOW |
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

> SELECT 'after test 1', LENGTH('测试'),@@SQL_MODE;
-- 返回存储过程后,调用LENGTH,输出按照GreatDB默认行为,显示字符字节长度=6
| before test 1 |                6 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO NO_ENGINE_SUBSTITUTION |
1 row in set (0.00 sec)

能否在触发器和事件中使用?

在GreatDB中定义触发器前设定兼容模式或者说sql_mode将影响触发器内SQL或函数的兼容风格,但由于GreatDB的触发器和事件兼容功能尚未发布,因此触发器和事件本身的语法还是使用GreatDB/MySQL原生的方式

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论