diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-08-08 16:42:01 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:39 +0400 |
commit | 892af78085482bb304947e7856bfd1b8def37c83 (patch) | |
tree | 54fd3b2ca25fa90c264d56b10785c326e6aaa876 /sql | |
parent | 7e10e38825f5cf02048a3cc69aea9d440e3140ba (diff) | |
download | mariadb-git-892af78085482bb304947e7856bfd1b8def37c83.tar.gz |
MDEV-10411 Providing compatibility for basic PL/SQL constructs
Part6: assignment operator
var:= 10;
Diffstat (limited to 'sql')
-rw-r--r-- | sql/set_var.cc | 11 | ||||
-rw-r--r-- | sql/set_var.h | 1 | ||||
-rw-r--r-- | sql/sql_lex.cc | 96 | ||||
-rw-r--r-- | sql/sql_lex.h | 7 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 89 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 247 |
6 files changed, 259 insertions, 192 deletions
diff --git a/sql/set_var.cc b/sql/set_var.cc index 07395e3e708..a5b80e34993 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -686,6 +686,17 @@ sys_var *intern_find_sys_var(const char *str, uint length) } +bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) +{ + tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length); + + if (tmp->var != NULL) + tmp->base_name= null_lex_str; + + return thd->is_error(); +} + + /** Execute update of all variables. diff --git a/sql/set_var.h b/sql/set_var.h index 97dc3b5ba51..ddd6a225eb8 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -391,6 +391,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type); int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond); sys_var *find_sys_var(THD *thd, const char *str, size_t length=0); +bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp); int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free); #define SYSVAR_AUTOSIZE(VAR,VAL) \ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d28504431e2..4be04cbde8d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5090,6 +5090,102 @@ bool LEX::sp_param_fill_definition(sp_variable *spvar) } +void LEX::set_stmt_init() +{ + sql_command= SQLCOM_SET_OPTION; + mysql_init_select(this); + option_type= OPT_SESSION; + autocommit= 0; +}; + + +bool LEX::init_internal_variable(struct sys_var_with_base *variable, + LEX_STRING name) +{ + sp_variable *spv; + + /* Best effort lookup for system variable. */ + if (!spcont || !(spv = spcont->find_variable(name, false))) + { + struct sys_var_with_base tmp= {NULL, name}; + + /* Not an SP local variable */ + if (find_sys_var_null_base(thd, &tmp)) + return true; + + *variable= tmp; + return false; + } + + /* + Possibly an SP local variable (or a shadowed sysvar). + Will depend on the context of the SET statement. + */ + variable->var= NULL; + variable->base_name= name; + return false; +} + + +bool LEX::init_internal_variable(struct sys_var_with_base *variable, + LEX_STRING dbname, LEX_STRING name) +{ + if (check_reserved_words(&dbname)) + { + thd->parse_error(); + return true; + } + if (sphead && sphead->m_type == TYPE_ENUM_TRIGGER && + (!my_strcasecmp(system_charset_info, dbname.str, "NEW") || + !my_strcasecmp(system_charset_info, dbname.str, "OLD"))) + { + if (dbname.str[0]=='O' || dbname.str[0]=='o') + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""); + return true; + } + if (trg_chistics.event == TRG_EVENT_DELETE) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); + return true; + } + if (trg_chistics.action_time == TRG_ACTION_AFTER) + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "); + return true; + } + /* This special combination will denote field of NEW row */ + variable->var= trg_new_row_fake_var; + variable->base_name= name; + return false; + } + + sys_var *tmp= find_sys_var(thd, name.str, name.length); + if (!tmp) + return true; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name.str); + variable->var= tmp; + variable->base_name= dbname; + return false; +} + + +bool LEX::init_default_internal_variable(struct sys_var_with_base *variable, + LEX_STRING name) +{ + sys_var *tmp= find_sys_var(thd, name.str, name.length); + if (!tmp) + return true; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name.str); + variable->var= tmp; + variable->base_name.str= (char*) "default"; + variable->base_name.length= 7; + return false; +} + + #ifdef MYSQL_SERVER uint binlog_unsafe_map[256]; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0b433867974..a298bf33968 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3064,6 +3064,13 @@ public: bool set_trigger_new_row(LEX_STRING *name, Item *val); bool set_system_variable(struct sys_var_with_base *tmp, enum enum_var_type var_type, Item *val); + void set_stmt_init(); + bool init_internal_variable(struct sys_var_with_base *variable, + LEX_STRING name); + bool init_internal_variable(struct sys_var_with_base *variable, + LEX_STRING dbname, LEX_STRING name); + bool init_default_internal_variable(struct sys_var_with_base *variable, + LEX_STRING name); bool set_local_variable(sp_variable *spv, Item *val); Item_splocal *create_item_for_sp_var(LEX_STRING name, sp_variable *spvar, const char *start_in_q, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9eb5669a60f..4c08c0fbc43 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -335,17 +335,6 @@ int LEX::case_stmt_action_then() return sphead->push_backpatch(thd, i, spcont->last_label()); } -static bool -find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) -{ - tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length); - - if (tmp->var != NULL) - tmp->base_name= null_lex_str; - - return thd->is_error(); -} - /** Helper action for a SET statement. @@ -14880,22 +14869,15 @@ set: SET { LEX *lex=Lex; - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; + lex->set_stmt_init(); lex->var_list.empty(); - lex->autocommit= 0; sp_create_assignment_lex(thd, yychar == YYEMPTY); } start_option_value_list {} | SET STATEMENT_SYM { - LEX *lex= Lex; - mysql_init_select(lex); - lex->option_type= OPT_SESSION; - lex->sql_command= SQLCOM_SET_OPTION; - lex->autocommit= 0; + Lex->set_stmt_init(); } set_stmt_option_value_following_option_type_list { @@ -15193,77 +15175,18 @@ option_value_no_option_type: internal_variable_name: ident { - sp_pcontext *spc= thd->lex->spcont; - sp_variable *spv; - - /* Best effort lookup for system variable. */ - if (!spc || !(spv = spc->find_variable($1, false))) - { - struct sys_var_with_base tmp= {NULL, $1}; - - /* Not an SP local variable */ - if (find_sys_var_null_base(thd, &tmp)) - MYSQL_YYABORT; - - $$= tmp; - } - else - { - /* - Possibly an SP local variable (or a shadowed sysvar). - Will depend on the context of the SET statement. - */ - $$.var= NULL; - $$.base_name= $1; - } + if (Lex->init_internal_variable(&$$, $1)) + MYSQL_YYABORT; } | ident '.' ident { - LEX *lex= Lex; - if (check_reserved_words(&$1)) - { - thd->parse_error(); + if (Lex->init_internal_variable(&$$, $1, $3)) MYSQL_YYABORT; - } - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) - { - if ($1.str[0]=='O' || $1.str[0]=='o') - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "")); - if (lex->trg_chistics.event == TRG_EVENT_DELETE) - { - my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), - "NEW", "on DELETE"); - MYSQL_YYABORT; - } - if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ")); - /* This special combination will denote field of NEW row */ - $$.var= trg_new_row_fake_var; - $$.base_name= $3; - } - else - { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) - MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name= $1; - } } | DEFAULT '.' ident { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) + if (Lex->init_default_internal_variable(&$$, $3)) MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name.str= (char*) "default"; - $$.base_name.length= 7; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 081a26f2c05..3766a650fc0 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -215,18 +215,6 @@ static bool push_sp_empty_label(THD *thd) } -static bool -find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) -{ - tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length); - - if (tmp->var != NULL) - tmp->base_name= null_lex_str; - - return thd->is_error(); -} - - #define bincmp_collation(X,Y) \ do \ { \ @@ -335,10 +323,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 102 shift/reduce conflicts. + Currently there are 104 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 102 +%expect 104 /* Comments for TOKENS. @@ -1059,7 +1047,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty opt_constraint constraint opt_ident - label_declaration_oracle + label_declaration_oracle ident_directly_assignable %type <lex_string_with_metadata> TEXT_STRING @@ -1217,6 +1205,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <Lex_length_and_dec> precision opt_precision float_options %type <symbol> keyword keyword_sp + keyword_directly_assignable + keyword_directly_not_assignable %type <lex_user> user grant_user grant_role user_or_role current_role admin_option_for_role user_maybe_role @@ -1234,6 +1224,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); UNDERSCORE_CHARSET %type <variable> internal_variable_name + internal_variable_name_directly_assignable %type <select_lex> subselect get_select_lex get_select_lex_derived @@ -1320,6 +1311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); vcol_opt_attribute_list vcol_attribute opt_serial_attribute opt_serial_attribute_list serial_attribute explainable_command + set_assign END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1507,6 +1499,7 @@ statement: | savepoint | select | set + | set_assign | signal_stmt | show | shutdown @@ -13788,6 +13781,26 @@ ident: } ; + +ident_directly_assignable: + IDENT_sys { $$=$1; } + | keyword_directly_assignable + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + | keyword_sp + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + + label_ident: IDENT_sys { $$=$1; } | keyword_sp @@ -13876,27 +13889,30 @@ user: user_maybe_role /* Keyword that we allow for identifiers (except SP labels) */ keyword: keyword_sp {} - | ASCII_SYM {} + | keyword_directly_assignable {} + | keyword_directly_not_assignable {} + ; + + +/* + Keywords that we allow in Oracle-style direct assignments: + xxx := 10; +*/ +keyword_directly_assignable: + ASCII_SYM {} | BACKUP_SYM {} - | BEGIN_SYM {} | BINLOG_SYM {} | BYTE_SYM {} | CACHE_SYM {} - | CHARSET {} | CHECKSUM_SYM {} | CHECKPOINT_SYM {} - | CLOSE_SYM {} | COLUMN_ADD_SYM {} | COLUMN_CHECK_SYM {} | COLUMN_CREATE_SYM {} | COLUMN_DELETE_SYM {} | COLUMN_GET_SYM {} - | COMMENT_SYM {} | COMMIT_SYM {} - | CONTAINS_SYM {} | DEALLOCATE_SYM {} - | DO_SYM {} - | END {} | EXAMINED_SYM {} | EXCLUDE_SYM {} | EXECUTE_SYM {} @@ -13905,13 +13921,9 @@ keyword: | FOLLOWING_SYM {} | FORMAT_SYM {} | GET_SYM {} - | HANDLER_SYM {} | HELP_SYM {} | HOST_SYM {} | INSTALL_SYM {} - | LANGUAGE_SYM {} - | NO_SYM {} - | OPEN_SYM {} | OPTION {} | OPTIONS_SYM {} | OTHERS_SYM {} @@ -13922,11 +13934,9 @@ keyword: | PRECEDING_SYM {} | PREPARE_SYM {} | REMOVE_SYM {} - | REPAIR {} | RESET_SYM {} | RESTORE_SYM {} | ROLLBACK_SYM {} - | SAVEPOINT_SYM {} | SECURITY_SYM {} | SERVER_SYM {} | SHUTDOWN {} @@ -13939,7 +13949,6 @@ keyword: | STOP_SYM {} | STORED_SYM {} | TIES_SYM {} - | TRUNCATE_SYM {} | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} @@ -13949,6 +13958,51 @@ keyword: ; /* + Keywords that are allowed as identifiers (e.g. table, column names), + but: + - not allowed as SP label names + - not allowed as variable names in Oracle-style assignments: + xxx := 10; + + If we allowed these variables in assignments, there would be conflicts + with SP characteristics, or verb clauses, or compound statements, e.g.: + CREATE PROCEDURE p1 LANGUAGE ... + would be either: + CREATE PROCEDURE p1 LANGUAGE SQL BEGIN END; + or + CREATE PROCEDURE p1 LANGUAGE:=10; + + Note, these variables can still be assigned using quoted identifiers: + `do`:= 10; + "do":= 10; (when ANSI_QUOTES) + or using a SET statement: + SET do= 10; + + Note, some of these keywords are reserved keywords in Oracle. + In case if heavy grammar conflicts are found in the future, + we'll possibly need to make them reserved for sql_mode=ORACLE. + + TODO: Allow these variables as SP lables when sql_mode=ORACLE. + TODO: Allow assigning of "SP characteristics" marked variables + inside compound blocks. +*/ +keyword_directly_not_assignable: + CONTAINS_SYM { /* SP characteristic */ } + | LANGUAGE_SYM { /* SP characteristic */ } + | NO_SYM { /* SP characteristic */ } + | CHARSET { /* SET CHARSET utf8; */ } + | DO_SYM { /* Verb clause */ } + | REPAIR { /* Verb clause */ } + | HANDLER_SYM { /* Verb clause */ } + | CLOSE_SYM { /* Verb clause. Reserved in Oracle */ } + | OPEN_SYM { /* Verb clause. Reserved in Oracle */ } + | SAVEPOINT_SYM { /* Verb clause. Reserved in Oracle */ } + | TRUNCATE_SYM { /* Verb clause. Reserved in Oracle */ } + | BEGIN_SYM { /* Compound. Reserved in Oracle */ } + | END { /* Compound. Reserved in Oracle */ } + ; + +/* * Keywords that we allow for labels in SPs. * Anything that's the beginning of a statement or characteristics * must be in keyword above, otherwise we get (harmful) shift/reduce @@ -14271,7 +14325,7 @@ keyword_sp: | X509_SYM {} | XML_SYM {} | YEAR_SYM {} - | VIA_SYM {} + | VIA_SYM {} ; /* @@ -14285,22 +14339,15 @@ set: SET { LEX *lex=Lex; - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; + lex->set_stmt_init(); lex->var_list.empty(); - lex->autocommit= 0; sp_create_assignment_lex(thd, yychar == YYEMPTY); } start_option_value_list {} | SET STATEMENT_SYM { - LEX *lex= Lex; - mysql_init_select(lex); - lex->option_type= OPT_SESSION; - lex->sql_command= SQLCOM_SET_OPTION; - lex->autocommit= 0; + Lex->set_stmt_init(); } set_stmt_option_value_following_option_type_list { @@ -14314,6 +14361,28 @@ set: {} ; +set_assign: + internal_variable_name_directly_assignable SET_VAR + { + LEX *lex=Lex; + lex->set_stmt_init(); + lex->var_list.empty(); + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + set_expr_or_default + { + sp_pcontext *spc= Lex->spcont; + sp_variable *spv= spc->find_variable($1.base_name, false); + + /* It is a local variable. */ + if (Lex->set_local_variable(spv, $4)) + MYSQL_YYABORT; + + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; + set_stmt_option_value_following_option_type_list: /* Only system variables can be used here. If this condition is changed @@ -14598,77 +14667,37 @@ option_value_no_option_type: internal_variable_name: ident { - sp_pcontext *spc= thd->lex->spcont; - sp_variable *spv; - - /* Best effort lookup for system variable. */ - if (!spc || !(spv = spc->find_variable($1, false))) - { - struct sys_var_with_base tmp= {NULL, $1}; + if (Lex->init_internal_variable(&$$, $1)) + MYSQL_YYABORT; + } + | ident '.' ident + { + if (Lex->init_internal_variable(&$$, $1, $3)) + MYSQL_YYABORT; + } + | DEFAULT '.' ident + { + if (Lex->init_default_internal_variable(&$$, $3)) + MYSQL_YYABORT; + } + ; - /* Not an SP local variable */ - if (find_sys_var_null_base(thd, &tmp)) - MYSQL_YYABORT; - $$= tmp; - } - else - { - /* - Possibly an SP local variable (or a shadowed sysvar). - Will depend on the context of the SET statement. - */ - $$.var= NULL; - $$.base_name= $1; - } +internal_variable_name_directly_assignable: + ident_directly_assignable + { + if (Lex->init_internal_variable(&$$, $1)) + MYSQL_YYABORT; } - | ident '.' ident + | ident_directly_assignable '.' ident { - LEX *lex= Lex; - if (check_reserved_words(&$1)) - { - thd->parse_error(); + if (Lex->init_internal_variable(&$$, $1, $3)) MYSQL_YYABORT; - } - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) - { - if ($1.str[0]=='O' || $1.str[0]=='o') - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "")); - if (lex->trg_chistics.event == TRG_EVENT_DELETE) - { - my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), - "NEW", "on DELETE"); - MYSQL_YYABORT; - } - if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ")); - /* This special combination will denote field of NEW row */ - $$.var= trg_new_row_fake_var; - $$.base_name= $3; - } - else - { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) - MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name= $1; - } } | DEFAULT '.' ident { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) + if (Lex->init_default_internal_variable(&$$, $3)) MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name.str= (char*) "default"; - $$.base_name.length= 7; } ; @@ -15485,11 +15514,6 @@ opt_release: | NO_SYM RELEASE_SYM { $$= TVL_NO; } ; -opt_savepoint: - /* empty */ {} - | SAVEPOINT_SYM {} - ; - commit: COMMIT_SYM opt_work opt_chain opt_release { @@ -15512,13 +15536,18 @@ rollback: lex->tx_chain= $3; lex->tx_release= $4; } - | ROLLBACK_SYM opt_work - TO_SYM opt_savepoint ident + | ROLLBACK_SYM opt_work TO_SYM SAVEPOINT_SYM ident { LEX *lex=Lex; lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; lex->ident= $5; } + | ROLLBACK_SYM opt_work TO_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $4; + } ; savepoint: |