diff options
Diffstat (limited to 'sql/sql_yacc.yy')
-rw-r--r-- | sql/sql_yacc.yy | 472 |
1 files changed, 326 insertions, 146 deletions
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e2263e21e8f..7b7f766bbd1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -730,6 +730,116 @@ static bool add_create_index (LEX *lex, Key::Keytype type, return FALSE; } + +/** + Create a separate LEX for each assignment if in SP. + + If we are in SP we want have own LEX for each assignment. + This is mostly because it is hard for several sp_instr_set + and sp_instr_set_trigger instructions share one LEX. + (Well, it is theoretically possible but adds some extra + overhead on preparation for execution stage and IMO less + robust). + + QQ: May be we should simply prohibit group assignments in SP? + + @see sp_create_assignment_instr + + @param thd Thread context + @param no_lookahead True if the parser has no lookahead +*/ + +static void sp_create_assignment_lex(THD *thd, bool no_lookahead) +{ + LEX *lex= thd->lex; + + if (lex->sphead) + { + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + LEX *old_lex= lex; + lex->sphead->reset_lex(thd); + lex= thd->lex; + + /* Set new LEX as if we at start of set rule. */ + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->var_list.empty(); + lex->one_shot_set= 0; + lex->autocommit= 0; + /* get_ptr() is only correct with no lookahead. */ + DBUG_ASSERT(no_lookahead); + lex->sphead->m_tmp_query= lip->get_ptr(); + /* Inherit from outer lex. */ + lex->option_type= old_lex->option_type; + } +} + + +/** + Create a SP instruction for a SET assignment. + + @see sp_create_assignment_lex + + @param thd Thread context + @param no_lookahead True if the parser has no lookahead + + @return false if success, true otherwise. +*/ + +static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) +{ + LEX *lex= thd->lex; + + if (lex->sphead) + { + sp_head *sp= lex->sphead; + + if (!lex->var_list.is_empty()) + { + /* + We have assignment to user or system variable or + option setting, so we should construct sp_instr_stmt + for it. + */ + LEX_STRING qbuff; + sp_instr_stmt *i; + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + + if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, + lex))) + return true; + + /* + Extract the query statement from the tokenizer. The + end is either lip->ptr, if there was no lookahead, + lip->tok_end otherwise. + */ + if (no_lookahead) + qbuff.length= lip->get_ptr() - sp->m_tmp_query; + else + qbuff.length= lip->get_tok_end() - sp->m_tmp_query; + + if (!(qbuff.str= (char*) alloc_root(thd->mem_root, + qbuff.length + 5))) + return true; + + strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query, + qbuff.length); + qbuff.length+= 4; + i->m_query= qbuff; + if (sp->add_instr(i)) + return true; + } + enum_var_type inner_option_type= lex->option_type; + if (lex->sphead->restore_lex(thd)) + return true; + /* Copy option_type to outer lex in case it has changed. */ + thd->lex->option_type= inner_option_type; + } + return false; +} + + %} %union { int num; @@ -788,10 +898,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 174 shift/reduce conflicts. + Currently there are 171 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 174 +%expect 171 /* Comments for TOKENS. @@ -1172,6 +1282,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ON /* SQL-2003-R */ %token ONE_SHOT_SYM %token ONE_SYM +%token ONLY_SYM /* SQL-2003-R */ %token ONLINE_SYM %token OPEN_SYM /* SQL-2003-R */ %token OPTIMIZE @@ -1475,14 +1586,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); table_option opt_if_not_exists opt_no_write_to_binlog opt_temporary all_or_any opt_distinct opt_ignore_leaves fulltext_options spatial_type union_option - start_transaction_opts field_def - union_opt select_derived_init option_type2 + field_def + union_opt select_derived_init transaction_access_mode_types opt_natural_language_mode opt_query_expansion opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type opt_time_precision kill_type kill_option int_num +/* + Bit field of MYSQL_START_TRANS_OPT_* flags. +*/ +%type <num> opt_start_transaction_option_list +%type <num> start_transaction_option_list +%type <num> start_transaction_option + %type <m_yes_no_unk> opt_chain opt_release @@ -1610,7 +1728,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ref_list opt_match_clause opt_on_update_delete use opt_delete_options opt_delete_option varchar nchar nvarchar opt_outer table_list table_name table_alias_ref_list table_alias_ref - opt_option opt_place + opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges grant_ident grant_list grant_option object_privilege object_privilege_list user_list rename_list @@ -7100,20 +7218,56 @@ slave: ; start: - START_SYM TRANSACTION_SYM start_transaction_opts + START_SYM TRANSACTION_SYM opt_start_transaction_option_list { LEX *lex= Lex; lex->sql_command= SQLCOM_BEGIN; + /* READ ONLY and READ WRITE are mutually exclusive. */ + if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) && + ($3 & MYSQL_START_TRANS_OPT_READ_ONLY)) + { + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } lex->start_transaction_opt= $3; } ; -start_transaction_opts: - /*empty*/ { $$ = 0; } - | WITH CONSISTENT_SYM SNAPSHOT_SYM +opt_start_transaction_option_list: + /* empty */ + { + $$= 0; + } + | start_transaction_option_list + { + $$= $1; + } + ; + +start_transaction_option_list: + start_transaction_option + { + $$= $1; + } + | start_transaction_option_list ',' start_transaction_option + { + $$= $1 | $3; + } + ; + +start_transaction_option: + WITH CONSISTENT_SYM SNAPSHOT_SYM { $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; } + | READ_SYM ONLY_SYM + { + $$= MYSQL_START_TRANS_OPT_READ_ONLY; + } + | READ_SYM WRITE_SYM + { + $$= MYSQL_START_TRANS_OPT_READ_WRITE; + } ; slave_thread_opts: @@ -13148,6 +13302,7 @@ keyword_sp: | ONE_SHOT_SYM {} | ONE_SYM {} | ONLINE_SYM {} + | ONLY_SYM {} | PACK_KEYS_SYM {} | PAGE_SYM {} | PARTIAL {} @@ -13271,8 +13426,15 @@ keyword_sp: /* Option functions */ +/* + SQLCOM_SET_OPTION statement. + + Note that to avoid shift/reduce conflicts, we have separate rules for the + first option listed in the statement. +*/ + set: - SET opt_option + SET { LEX *lex=Lex; lex->sql_command= SQLCOM_SET_OPTION; @@ -13281,115 +13443,96 @@ set: lex->var_list.empty(); lex->one_shot_set= 0; lex->autocommit= 0; + sp_create_assignment_lex(YYTHD, yychar == YYEMPTY); } - option_value_list + start_option_value_list {} ; -opt_option: - /* empty */ {} - | OPTION {} - ; -option_value_list: - option_type_value - | option_value_list ',' option_type_value - ; - -option_type_value: +// Start of option value list +start_option_value_list: + option_value_no_option_type { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= YYLIP; - - if (lex->sphead) - { - /* - If we are in SP we want have own LEX for each assignment. - This is mostly because it is hard for several sp_instr_set - and sp_instr_set_trigger instructions share one LEX. - (Well, it is theoretically possible but adds some extra - overhead on preparation for execution stage and IMO less - robust). - - QQ: May be we should simply prohibit group assignments in SP? - */ - lex->sphead->reset_lex(thd); - lex= thd->lex; - - /* Set new LEX as if we at start of set rule. */ - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; - lex->var_list.empty(); - lex->one_shot_set= 0; - lex->autocommit= 0; - lex->sphead->m_tmp_query= lip->get_tok_start(); - } + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; } - ext_option_value + option_value_list_continued + | TRANSACTION_SYM { - THD *thd= YYTHD; - LEX *lex= thd->lex; - Lex_input_stream *lip= YYLIP; - - if (lex->sphead) - { - sp_head *sp= lex->sphead; + Lex->option_type= OPT_DEFAULT; + } + transaction_characteristics + { + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_type + { + Lex->option_type= $1; + } + start_option_value_list_following_option_type + ; - if (!lex->var_list.is_empty()) - { - /* - We have assignment to user or system variable or - option setting, so we should construct sp_instr_stmt - for it. - */ - LEX_STRING qbuff; - sp_instr_stmt *i; - if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont, - lex))) - MYSQL_YYABORT; +// Start of option value list, option_type was given +start_option_value_list_following_option_type: + option_value_following_option_type + { + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + option_value_list_continued + | TRANSACTION_SYM transaction_characteristics + { + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; - /* - Extract the query statement from the tokenizer. The - end is either lip->ptr, if there was no lookahead, - lip->tok_end otherwise. - */ - if (yychar == YYEMPTY) - qbuff.length= lip->get_ptr() - sp->m_tmp_query; - else - qbuff.length= lip->get_tok_end() - sp->m_tmp_query; +// Remainder of the option value list after first option value. +option_value_list_continued: + /* empty */ + | ',' option_value_list + ; - if (!(qbuff.str= (char*) alloc_root(thd->mem_root, - qbuff.length + 5))) - MYSQL_YYABORT; +// Repeating list of option values after first option value. +option_value_list: + { + sp_create_assignment_lex(YYTHD, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_value_list ',' + { + sp_create_assignment_lex(YYTHD, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; - strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query, - qbuff.length); - qbuff.length+= 4; - i->m_query= qbuff; - if (sp->add_instr(i)) - MYSQL_YYABORT; - } - if (lex->sphead->restore_lex(thd)) - MYSQL_YYABORT; - } +// Wrapper around option values following the first option value in the stmt. +option_value: + option_type + { + Lex->option_type= $1; } + option_value_following_option_type + | option_value_no_option_type ; option_type: - option_type2 {} - | GLOBAL_SYM { $$=OPT_GLOBAL; } + GLOBAL_SYM { $$=OPT_GLOBAL; } | LOCAL_SYM { $$=OPT_SESSION; } | SESSION_SYM { $$=OPT_SESSION; } ; -option_type2: - /* empty */ { $$= OPT_DEFAULT; } - | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; } - ; - opt_var_type: /* empty */ { $$=OPT_SESSION; } | GLOBAL_SYM { $$=OPT_GLOBAL; } @@ -13404,74 +13547,62 @@ opt_var_ident_type: | SESSION_SYM '.' { $$=OPT_SESSION; } ; -ext_option_value: - sys_option_value - | option_type2 option_value +// Option values with preceeding option_type. +option_value_following_option_type: + internal_variable_name equal set_expr_or_default + { + THD *thd= YYTHD; + LEX *lex= Lex; + + if ($1.var && $1.var != trg_new_row_fake_var) + { + /* It is a system variable. */ + if (set_system_variable(thd, &$1, lex->option_type, $3)) + MYSQL_YYABORT; + } + else + { + /* + Not in trigger assigning value to new row, + and option_type preceeding local variable is illegal. + */ + my_parse_error(ER(ER_SYNTAX_ERROR)); + MYSQL_YYABORT; + } + } ; -sys_option_value: - option_type internal_variable_name equal set_expr_or_default +// Option values without preceeding option_type. +option_value_no_option_type: + internal_variable_name equal set_expr_or_default { THD *thd= YYTHD; LEX *lex= Lex; - LEX_STRING *name= &$2.base_name; + LEX_STRING *name= &$1.base_name; - if ($2.var == trg_new_row_fake_var) + if ($1.var == trg_new_row_fake_var) { /* We are in trigger and assigning value to field of new row */ - if ($1) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - if (set_trigger_new_row(YYTHD, name, $4)) + if (set_trigger_new_row(YYTHD, name, $3)) MYSQL_YYABORT; } - else if ($2.var) + else if ($1.var) { - if ($1) - lex->option_type= $1; - /* It is a system variable. */ - if (set_system_variable(thd, &$2, lex->option_type, $4)) + if (set_system_variable(thd, &$1, lex->option_type, $3)) MYSQL_YYABORT; } else { sp_pcontext *spc= lex->spcont; - sp_variable_t *spv= spc->find_variable(name); - - if ($1) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } + sp_variable *spv= spc->find_variable(name, false); /* It is a local variable. */ - if (set_local_variable(thd, spv, $4)) + if (set_local_variable(thd, spv, $3)) MYSQL_YYABORT; } } - | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types - { - THD *thd= YYTHD; - LEX *lex=Lex; - lex->option_type= $1; - Item *item= new (thd->mem_root) Item_int((int32) $5); - if (item == NULL) - MYSQL_YYABORT; - set_var *var= new set_var(lex->option_type, - find_sys_var(thd, "tx_isolation"), - &null_lex_str, - item); - if (var == NULL) - MYSQL_YYABORT; - lex->var_list.push_back(var); - } - ; - -option_value: - '@' ident_or_text equal expr + | '@' ident_or_text equal expr { Item_func_set_user_var *item; item= new (YYTHD->mem_root) Item_func_set_user_var($2, $4); @@ -13517,7 +13648,7 @@ option_value: names.str= (char *)"names"; names.length= 5; - if (spc && spc->find_variable(&names)) + if (spc && spc->find_variable(&names, false)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else my_parse_error(ER(ER_SYNTAX_ERROR)); @@ -13553,7 +13684,7 @@ option_value: pw.str= (char *)"password"; pw.length= 8; - if (spc && spc->find_variable(&pw)) + if (spc && spc->find_variable(&pw, false)) { my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str); MYSQL_YYABORT; @@ -13562,6 +13693,7 @@ option_value: MYSQL_YYABORT; user->host=null_lex_str; user->user.str=thd->security_ctx->user; + user->user.length= strlen(thd->security_ctx->user); set_var_password *var= new set_var_password(user, $3); if (var == NULL) MYSQL_YYABORT; @@ -13666,6 +13798,54 @@ internal_variable_name: } ; +transaction_characteristics: + transaction_access_mode + | isolation_level + | transaction_access_mode ',' isolation_level + | isolation_level ',' transaction_access_mode + ; + +transaction_access_mode: + transaction_access_mode_types + { + THD *thd= YYTHD; + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int((int32) $1); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= new set_var(lex->option_type, + find_sys_var(thd, "tx_read_only"), + &null_lex_str, + item); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var); + } + ; + +isolation_level: + ISOLATION LEVEL_SYM isolation_types + { + THD *thd= YYTHD; + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int((int32) $3); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= new set_var(lex->option_type, + find_sys_var(thd, "tx_isolation"), + &null_lex_str, + item); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var); + } + ; + +transaction_access_mode_types: + READ_SYM ONLY_SYM { $$= true; } + | READ_SYM WRITE_SYM { $$= false; } + ; + isolation_types: READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } |