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语言子集。
-
非兼容功能
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.cc
、yy_oracle.hh
和yy_mariadb.cc
、yy_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的产生式规则有变化
兼容功能的意思是,内核代码有所更改或增加
-
数据类型兼容
除存储过程外,MariaDB 10.3 还新增了
VARCHAR2
、NUMBER
、DATE
、RAW
、CLOG
、BLOB
的数据类型兼容。上述数据类型与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 其他兼容
-
词法解析的兼容:sql_lex.cc。mariadb 与 mysql 一样 词法解析是自己实现的,这里有所修改,改动不多
-
内置函数的创建
-
构造event_sql的语法上区分oracle语法
-
存储过程错误处理