searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

MariaDB sql_mode 兼容 oracle 实现调研

2023-03-31 01:28:48
86
0

sql_mode 兼容 oracle 功能

在 MariaDB 10.3 之前,MariaDB 不支持 Oracle 的 PL/SQL 语言,设置 SQL_MODE=ORACLE 后,仅仅类似于设置一下模式的别名:
SET SQL_MODE='PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER';
从 MariaDB 10.3 开始,提供了 PL/SQL 语言子集,设置 SQL_MODE=ORACLE 类似于设置为:
SET SQL_MODE='PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS, NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT';
具体来说,此时MarriaDB主要提供的是存储过程的PL/SQL语言子集。
  1. 非兼容功能

Mariadb 兼容模式下并非100%兼容oracle的语法,以下网站介绍了迁移过程的语法差异,以及列举了需转换或者不支持的内容:https://sqlines.com/oracle-to-mariadb-compatibility

sql_mode 兼容 Oracle 实现原理

多语法解析器实现

MariaDB 通过以sql/sql_yacc.yy为模板先生成yy_oracle.yy文件与yy_mariadb.yy文件:
// sql/CMakeLists.txt:73 (mariadb 10.11)
ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy 
                                                                  ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy 
                       COMMAND ${CMAKE_COMMAND} "-DVAL1=ORACLE" "-DVAL2=MARIADB" "-DOUT1=${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy" "-DOUT2=${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy" "-DIN=${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy" "-DBISON_VERSION=${BISON_VERSION}" -P ${CMAKE_CURRENT_SOURCE_DIR}/gen_yy_files.cmake 
                       COMMENT "Building yy_mariadb.yy and yy_oracle.yy from sql_yacc.yy" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy ${CMAKE_CURRENT_SOURCE_DIR}/gen_yy_files.cmake )
然后使用Bison分别生成yy_oracle.ccyy_oracle.hhyy_mariadb.ccyy_mariadb.hh文件。
// sql/CMakeLists.txt:381 (mariadb 10.11) 
BISON_TARGET(gen_mariadb_cc_hh ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.cc COMPILE_FLAGS "-p MYSQL -S ${CMAKE_CURRENT_BINARY_DIR}/myskel.m4") 
BISON_TARGET(gen_oracle_cc_hh ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.cc COMPILE_FLAGS "-p ORA -S ${CMAKE_CURRENT_BINARY_DIR}/myskel.m4")
yy_oracle 与 yy_mariadb 分别定义了各自的parse函数:
// yy_mariadb.hh
int MYSQLparse (THD *thd);
 
// yy_oracle.hh
int ORAparse (THD *thd);
mariadb在解析SQL时,根据sql_mode 是否设置了ORACLE决定采用哪一个parser:
// sql_parse.cc:10350
bool parse_sql(THD *thd, Parser_state *parser_state, Object_creation_ctx *creation_ctx, bool do_pfs_digest) { 
    // ... 
    /* Parse the query. */ 
    bool mysql_parse_status= thd->variables.sql_mode & MODE_ORACLE ? ORAparse(thd) : MYSQLparse(thd); 
    // ... 
}​

语法规则差异内容

  • 新增语法
%type(非终结符)
相应功能
实现方式
%type <NONE> set_assign
存储过程,赋值
兼容语法
%type <spvar_mode> sp_opt_inout
存储过程,参数模式
兼容语法
%type <NONE> sp_labelable_stmt
存储过程,可标签化语句
兼容语法
%type <simple_string> remember_end_opt
存储过程相关
兼容语法
%type <lex_str> opt_package_routine_end_name
包相关
兼容功能
%type <lex_str> label_declaration_oracle
标签
兼容语法,兼容功能
%type <lex_str> labels_declaration_oracle
标签
兼容语法,兼容功能
%type <kwd> keyword_directly_assignable
关键字
兼容语法
%type <ident_sys> ident_directly_assignable
直接复制语法
兼容语法
%type <ident_cli> ident_cli_directly_assignable
直接复制语法
兼容语法
%type <spname> opt_sp_name
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_body_list
存储过程相关
兼容语法、兼容功能
%type <spblock> opt_sp_decl_body_list
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_non_handler
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_non_handler_list
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_handler
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_handler_list
存储过程相关
兼容语法、兼容功能
sp_opt_inout
存储过程相关
兼容语法
sp_proc_stmts1_implicit_block
存储过程相关
兼容语法
opt_exception_clause、exception_handlers、exception_handler
异常处理
兼容语法、兼容功能
sp_no_param
存储过程相关
兼容语法
opt_sp_parenthesized_fdparam_list
存储过程相关
兼容语法
opt_sp_parenthesized_pdparam_list
存储过程相关
兼容语法
sp_tail_is
存储过程相关
兼容语法
sp_instr_addr
存储过程相关
兼容语法
sp_body
存储过程相关
兼容语法、兼容功能
create_package_chistic
包相关
兼容功能
%type <spblock> opt_sp_decl_handler_list
存储过程相关
兼容语法、兼容功能
%type <spblock> package_implementation_routine_definition
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_item_declaration
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section_list1
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section_list2
包相关
兼容语法、兼容功能
%type <spblock_handlers> sp_block_statements_and_exceptions
存储过程相关
兼容语法、兼容功能
%type <spblock_handlers> package_implementation_executable_section
包相关
兼容语法、兼容功能
%type <sp_instr_addr> sp_instr_addr
存储过程相关
兼容语法
%type <num> opt_exception_clause exception_handlers
存储过程相关
兼容语法、兼容功能
%type <lex> remember_lex
包相关
兼容语法、兼容功能
%type <lex> package_routine_lex
包相关
兼容语法、兼容功能
%type <lex> package_specification_function
包相关
兼容语法、兼容功能
%type <lex> package_specification_procedure
包相关
兼容语法、兼容功能
注:
兼容语法的意思是,仅仅yy的产生式规则有变化
兼容功能的意思是,内核代码有所更改或增加
  1. 数据类型兼容

除存储过程外,MariaDB 10.3 还新增了 VARCHAR2NUMBERDATERAWCLOGBLOB的数据类型兼容。上述数据类型与MariaDB原数据类型是同义词,关系如下:
  • VARCHAR2 -> VARCHAR
  • NUMBER -> DECIMAL
  • DATE -> DATETIME
  • RAW -> VARBINARY
  • CLOB -> LONGTEXT
  • BLOB -> LONGBLOB
另外MariaDB 没有支持BFILE
这些数据类型不需要设置 sql_mode=ORACLE,在查找关键词时会做转换:
// sql_lex.cc
int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
                                   uint len, bool function)
{
  const char *tok= m_tok_start;

  SYMBOL *symbol= get_hash_symbol(tok, len, function);
  if (symbol)
  {
    kwd->set_keyword(tok, len);
    DBUG_ASSERT(tok >= get_buf());
    DBUG_ASSERT(tok < get_end_of_query());

    if (m_thd->variables.sql_mode & MODE_ORACLE)
    {
      switch (symbol->tok) {
      case BEGIN_MARIADB_SYM:          return BEGIN_ORACLE_SYM;
      case BLOB_MARIADB_SYM:           return BLOB_ORACLE_SYM;
      case BODY_MARIADB_SYM:           return BODY_ORACLE_SYM;
      case CLOB_MARIADB_SYM:           return CLOB_ORACLE_SYM;
      case CONTINUE_MARIADB_SYM:       return CONTINUE_ORACLE_SYM;
      case DECLARE_MARIADB_SYM:        return DECLARE_ORACLE_SYM;
      case DECODE_MARIADB_SYM:         return DECODE_ORACLE_SYM;
      case ELSEIF_MARIADB_SYM:         return ELSEIF_ORACLE_SYM;
      case ELSIF_MARIADB_SYM:          return ELSIF_ORACLE_SYM;
      case EXCEPTION_MARIADB_SYM:      return EXCEPTION_ORACLE_SYM;
      case EXIT_MARIADB_SYM:           return EXIT_ORACLE_SYM;
      case GOTO_MARIADB_SYM:           return GOTO_ORACLE_SYM;
      case MINUS_ORACLE_SYM:           return EXCEPT_SYM;
      case NUMBER_MARIADB_SYM:         return NUMBER_ORACLE_SYM;
      case OTHERS_MARIADB_SYM:         return OTHERS_ORACLE_SYM;
      case PACKAGE_MARIADB_SYM:        return PACKAGE_ORACLE_SYM;
      case RAISE_MARIADB_SYM:          return RAISE_ORACLE_SYM;
      case RAW_MARIADB_SYM:            return RAW_ORACLE_SYM;
      case RETURN_MARIADB_SYM:         return RETURN_ORACLE_SYM;
      case ROWTYPE_MARIADB_SYM:        return ROWTYPE_ORACLE_SYM;
      case VARCHAR2_MARIADB_SYM:       return VARCHAR2_ORACLE_SYM;
      }
    }

    if ((symbol->tok == NOT_SYM) &&
        (m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
      return NOT2_SYM;
    if ((symbol->tok == OR2_SYM) &&
        (m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
    {
      return (m_thd->variables.sql_mode & MODE_ORACLE) ?
             ORACLE_CONCAT_SYM : MYSQL_CONCAT_SYM;
    }

    return symbol->tok;
  }
  return 0;
}​
2.4 其他兼容
  1. 词法解析的兼容:sql_lex.cc。mariadb 与 mysql 一样 词法解析是自己实现的,这里有所修改,改动不多
  2. 内置函数的创建
  3. 构造event_sql的语法上区分oracle语法
  4. 存储过程错误处理
0条评论
作者已关闭评论
lcken
3文章数
1粉丝数
lcken
3 文章 | 1 粉丝
lcken
3文章数
1粉丝数
lcken
3 文章 | 1 粉丝
原创

MariaDB sql_mode 兼容 oracle 实现调研

2023-03-31 01:28:48
86
0

sql_mode 兼容 oracle 功能

在 MariaDB 10.3 之前,MariaDB 不支持 Oracle 的 PL/SQL 语言,设置 SQL_MODE=ORACLE 后,仅仅类似于设置一下模式的别名:
SET SQL_MODE='PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER';
从 MariaDB 10.3 开始,提供了 PL/SQL 语言子集,设置 SQL_MODE=ORACLE 类似于设置为:
SET SQL_MODE='PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS, NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER,SIMULTANEOUS_ASSIGNMENT';
具体来说,此时MarriaDB主要提供的是存储过程的PL/SQL语言子集。
  1. 非兼容功能

Mariadb 兼容模式下并非100%兼容oracle的语法,以下网站介绍了迁移过程的语法差异,以及列举了需转换或者不支持的内容:https://sqlines.com/oracle-to-mariadb-compatibility

sql_mode 兼容 Oracle 实现原理

多语法解析器实现

MariaDB 通过以sql/sql_yacc.yy为模板先生成yy_oracle.yy文件与yy_mariadb.yy文件:
// sql/CMakeLists.txt:73 (mariadb 10.11)
ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy 
                                                                  ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy 
                       COMMAND ${CMAKE_COMMAND} "-DVAL1=ORACLE" "-DVAL2=MARIADB" "-DOUT1=${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy" "-DOUT2=${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy" "-DIN=${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy" "-DBISON_VERSION=${BISON_VERSION}" -P ${CMAKE_CURRENT_SOURCE_DIR}/gen_yy_files.cmake 
                       COMMENT "Building yy_mariadb.yy and yy_oracle.yy from sql_yacc.yy" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy ${CMAKE_CURRENT_SOURCE_DIR}/gen_yy_files.cmake )
然后使用Bison分别生成yy_oracle.ccyy_oracle.hhyy_mariadb.ccyy_mariadb.hh文件。
// sql/CMakeLists.txt:381 (mariadb 10.11) 
BISON_TARGET(gen_mariadb_cc_hh ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.yy ${CMAKE_CURRENT_BINARY_DIR}/yy_mariadb.cc COMPILE_FLAGS "-p MYSQL -S ${CMAKE_CURRENT_BINARY_DIR}/myskel.m4") 
BISON_TARGET(gen_oracle_cc_hh ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.yy ${CMAKE_CURRENT_BINARY_DIR}/yy_oracle.cc COMPILE_FLAGS "-p ORA -S ${CMAKE_CURRENT_BINARY_DIR}/myskel.m4")
yy_oracle 与 yy_mariadb 分别定义了各自的parse函数:
// yy_mariadb.hh
int MYSQLparse (THD *thd);
 
// yy_oracle.hh
int ORAparse (THD *thd);
mariadb在解析SQL时,根据sql_mode 是否设置了ORACLE决定采用哪一个parser:
// sql_parse.cc:10350
bool parse_sql(THD *thd, Parser_state *parser_state, Object_creation_ctx *creation_ctx, bool do_pfs_digest) { 
    // ... 
    /* Parse the query. */ 
    bool mysql_parse_status= thd->variables.sql_mode & MODE_ORACLE ? ORAparse(thd) : MYSQLparse(thd); 
    // ... 
}​

语法规则差异内容

  • 新增语法
%type(非终结符)
相应功能
实现方式
%type <NONE> set_assign
存储过程,赋值
兼容语法
%type <spvar_mode> sp_opt_inout
存储过程,参数模式
兼容语法
%type <NONE> sp_labelable_stmt
存储过程,可标签化语句
兼容语法
%type <simple_string> remember_end_opt
存储过程相关
兼容语法
%type <lex_str> opt_package_routine_end_name
包相关
兼容功能
%type <lex_str> label_declaration_oracle
标签
兼容语法,兼容功能
%type <lex_str> labels_declaration_oracle
标签
兼容语法,兼容功能
%type <kwd> keyword_directly_assignable
关键字
兼容语法
%type <ident_sys> ident_directly_assignable
直接复制语法
兼容语法
%type <ident_cli> ident_cli_directly_assignable
直接复制语法
兼容语法
%type <spname> opt_sp_name
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_body_list
存储过程相关
兼容语法、兼容功能
%type <spblock> opt_sp_decl_body_list
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_non_handler
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_non_handler_list
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_handler
存储过程相关
兼容语法、兼容功能
%type <spblock> sp_decl_handler_list
存储过程相关
兼容语法、兼容功能
sp_opt_inout
存储过程相关
兼容语法
sp_proc_stmts1_implicit_block
存储过程相关
兼容语法
opt_exception_clause、exception_handlers、exception_handler
异常处理
兼容语法、兼容功能
sp_no_param
存储过程相关
兼容语法
opt_sp_parenthesized_fdparam_list
存储过程相关
兼容语法
opt_sp_parenthesized_pdparam_list
存储过程相关
兼容语法
sp_tail_is
存储过程相关
兼容语法
sp_instr_addr
存储过程相关
兼容语法
sp_body
存储过程相关
兼容语法、兼容功能
create_package_chistic
包相关
兼容功能
%type <spblock> opt_sp_decl_handler_list
存储过程相关
兼容语法、兼容功能
%type <spblock> package_implementation_routine_definition
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_item_declaration
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section_list1
包相关
兼容语法、兼容功能
%type <spblock> package_implementation_declare_section_list2
包相关
兼容语法、兼容功能
%type <spblock_handlers> sp_block_statements_and_exceptions
存储过程相关
兼容语法、兼容功能
%type <spblock_handlers> package_implementation_executable_section
包相关
兼容语法、兼容功能
%type <sp_instr_addr> sp_instr_addr
存储过程相关
兼容语法
%type <num> opt_exception_clause exception_handlers
存储过程相关
兼容语法、兼容功能
%type <lex> remember_lex
包相关
兼容语法、兼容功能
%type <lex> package_routine_lex
包相关
兼容语法、兼容功能
%type <lex> package_specification_function
包相关
兼容语法、兼容功能
%type <lex> package_specification_procedure
包相关
兼容语法、兼容功能
注:
兼容语法的意思是,仅仅yy的产生式规则有变化
兼容功能的意思是,内核代码有所更改或增加
  1. 数据类型兼容

除存储过程外,MariaDB 10.3 还新增了 VARCHAR2NUMBERDATERAWCLOGBLOB的数据类型兼容。上述数据类型与MariaDB原数据类型是同义词,关系如下:
  • VARCHAR2 -> VARCHAR
  • NUMBER -> DECIMAL
  • DATE -> DATETIME
  • RAW -> VARBINARY
  • CLOB -> LONGTEXT
  • BLOB -> LONGBLOB
另外MariaDB 没有支持BFILE
这些数据类型不需要设置 sql_mode=ORACLE,在查找关键词时会做转换:
// sql_lex.cc
int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd,
                                   uint len, bool function)
{
  const char *tok= m_tok_start;

  SYMBOL *symbol= get_hash_symbol(tok, len, function);
  if (symbol)
  {
    kwd->set_keyword(tok, len);
    DBUG_ASSERT(tok >= get_buf());
    DBUG_ASSERT(tok < get_end_of_query());

    if (m_thd->variables.sql_mode & MODE_ORACLE)
    {
      switch (symbol->tok) {
      case BEGIN_MARIADB_SYM:          return BEGIN_ORACLE_SYM;
      case BLOB_MARIADB_SYM:           return BLOB_ORACLE_SYM;
      case BODY_MARIADB_SYM:           return BODY_ORACLE_SYM;
      case CLOB_MARIADB_SYM:           return CLOB_ORACLE_SYM;
      case CONTINUE_MARIADB_SYM:       return CONTINUE_ORACLE_SYM;
      case DECLARE_MARIADB_SYM:        return DECLARE_ORACLE_SYM;
      case DECODE_MARIADB_SYM:         return DECODE_ORACLE_SYM;
      case ELSEIF_MARIADB_SYM:         return ELSEIF_ORACLE_SYM;
      case ELSIF_MARIADB_SYM:          return ELSIF_ORACLE_SYM;
      case EXCEPTION_MARIADB_SYM:      return EXCEPTION_ORACLE_SYM;
      case EXIT_MARIADB_SYM:           return EXIT_ORACLE_SYM;
      case GOTO_MARIADB_SYM:           return GOTO_ORACLE_SYM;
      case MINUS_ORACLE_SYM:           return EXCEPT_SYM;
      case NUMBER_MARIADB_SYM:         return NUMBER_ORACLE_SYM;
      case OTHERS_MARIADB_SYM:         return OTHERS_ORACLE_SYM;
      case PACKAGE_MARIADB_SYM:        return PACKAGE_ORACLE_SYM;
      case RAISE_MARIADB_SYM:          return RAISE_ORACLE_SYM;
      case RAW_MARIADB_SYM:            return RAW_ORACLE_SYM;
      case RETURN_MARIADB_SYM:         return RETURN_ORACLE_SYM;
      case ROWTYPE_MARIADB_SYM:        return ROWTYPE_ORACLE_SYM;
      case VARCHAR2_MARIADB_SYM:       return VARCHAR2_ORACLE_SYM;
      }
    }

    if ((symbol->tok == NOT_SYM) &&
        (m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE))
      return NOT2_SYM;
    if ((symbol->tok == OR2_SYM) &&
        (m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT))
    {
      return (m_thd->variables.sql_mode & MODE_ORACLE) ?
             ORACLE_CONCAT_SYM : MYSQL_CONCAT_SYM;
    }

    return symbol->tok;
  }
  return 0;
}​
2.4 其他兼容
  1. 词法解析的兼容:sql_lex.cc。mariadb 与 mysql 一样 词法解析是自己实现的,这里有所修改,改动不多
  2. 内置函数的创建
  3. 构造event_sql的语法上区分oracle语法
  4. 存储过程错误处理
文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0