diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 257a36e94c5..0a3385edb3b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -52,6 +52,524 @@ const LEX_CSTRING empty_clex_str= {"", 0}; const LEX_CSTRING star_clex_str= {"*", 1}; const LEX_CSTRING param_clex_str= {"?", 1}; + +/** + Helper action for a case expression statement (the expr in 'CASE expr'). + This helper is used for 'searched' cases only. + @param lex the parser lex context + @param expr the parsed expression + @return 0 on success +*/ + +int LEX::case_stmt_action_expr(Item* expr) +{ + int case_expr_id= spcont->register_case_expr(); + sp_instr_set_case_expr *i; + + if (spcont->push_case_expr_id(case_expr_id)) + return 1; + + i= new (thd->mem_root) + sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr, + this); + + sphead->add_cont_backpatch(i); + return sphead->add_instr(i); +} + +/** + Helper action for a case when condition. + This helper is used for both 'simple' and 'searched' cases. + @param lex the parser lex context + @param when the parsed expression for the WHEN clause + @param simple true for simple cases, false for searched cases +*/ + +int LEX::case_stmt_action_when(Item *when, bool simple) +{ + uint ip= sphead->instructions(); + sp_instr_jump_if_not *i; + Item_case_expr *var; + Item *expr; + + if (simple) + { + var= new (thd->mem_root) + Item_case_expr(thd, spcont->get_current_case_expr_id()); + +#ifdef DBUG_ASSERT_EXISTS + if (var) + { + var->m_sp= sphead; + } +#endif + + expr= new (thd->mem_root) Item_func_eq(thd, var, when); + i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this); + } + else + i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this); + + /* + BACKPATCH: Registering forward jump from + "case_stmt_action_when" to "case_stmt_action_then" + (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) + */ + + return + !MY_TEST(i) || + sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) || + sphead->add_cont_backpatch(i) || + sphead->add_instr(i); +} + +/** + Helper action for a case then statements. + This helper is used for both 'simple' and 'searched' cases. + @param lex the parser lex context +*/ + +int LEX::case_stmt_action_then() +{ + uint ip= sphead->instructions(); + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont); + if (!MY_TEST(i) || sphead->add_instr(i)) + return 1; + + /* + BACKPATCH: Resolving forward jump from + "case_stmt_action_when" to "case_stmt_action_then" + (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) + */ + + sphead->backpatch(spcont->pop_label()); + + /* + BACKPATCH: Registering forward jump from + "case_stmt_action_then" to after END CASE + (jump from instruction 4 to 12, 7 to 12 ... in the example) + */ + + return sphead->push_backpatch(thd, i, spcont->last_label()); +} + + +/** + Helper action for a SET statement. + Used to push a system variable into the assignment list. + + @param tmp the system variable with base name + @param var_type the scope of the variable + @param val the value being assigned to the variable + + @return TRUE if error, FALSE otherwise. +*/ + +bool +LEX::set_system_variable(enum enum_var_type var_type, + sys_var *sysvar, const Lex_ident_sys_st *base_name, + Item *val) +{ + set_var *setvar; + + /* No AUTOCOMMIT from a stored function or trigger. */ + if (spcont && sysvar == Sys_autocommit_ptr) + sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + + if (val && val->type() == Item::FIELD_ITEM && + ((Item_field*)val)->table_name.str) + { + my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str); + return TRUE; + } + + if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar, + base_name, val))) + return TRUE; + + return var_list.push_back(setvar, thd->mem_root); +} + + +/** + Helper action for a SET statement. + Used to SET a field of NEW row. + + @param name the field name + @param val the value being assigned to the row + + @return TRUE if error, FALSE otherwise. +*/ + +bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val) +{ + Item_trigger_field *trg_fld; + sp_instr_set_trigger_field *sp_fld; + + /* QQ: Shouldn't this be field's default value ? */ + if (! val) + val= new (thd->mem_root) Item_null(thd); + + DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE && + (trg_chistics.event == TRG_EVENT_INSERT || + trg_chistics.event == TRG_EVENT_UPDATE)); + + trg_fld= new (thd->mem_root) + Item_trigger_field(thd, current_context(), + Item_trigger_field::NEW_ROW, + *name, UPDATE_ACL, FALSE); + + if (unlikely(trg_fld == NULL)) + return TRUE; + + sp_fld= new (thd->mem_root) + sp_instr_set_trigger_field(sphead->instructions(), + spcont, trg_fld, val, this); + + if (unlikely(sp_fld == NULL)) + return TRUE; + + /* + Let us add this item to list of all Item_trigger_field + objects in trigger. + */ + trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); + + return sphead->add_instr(sp_fld); +} + + +/** + Create an object to represent a SP variable in the Item-hierarchy. + + @param name The SP variable name. + @param spvar The SP variable (optional). + @param start_in_q Start position of the SP variable name in the query. + @param end_in_q End position of the SP variable name in the query. + + @remark If spvar is not specified, the name is used to search for the + variable in the parse-time context. If the variable does not + exist, a error is set and NULL is returned to the caller. + + @return An Item_splocal object representing the SP variable, or NULL on error. +*/ +Item_splocal* +LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar) +{ + const Sp_rcontext_handler *rh; + Item_splocal *item; + const char *start_in_q= cname->pos(); + const char *end_in_q= cname->end(); + uint pos_in_q, len_in_q; + Lex_ident_sys name(thd, cname); + + if (name.is_null()) + return NULL; // EOM + + /* If necessary, look for the variable. */ + if (spcont && !spvar) + spvar= find_variable(&name, &rh); + + if (!spvar) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str); + return NULL; + } + + DBUG_ASSERT(spcont && spvar); + + /* Position and length of the SP variable name in the query. */ + pos_in_q= (uint)(start_in_q - sphead->m_tmp_query); + len_in_q= (uint)(end_in_q - start_in_q); + + item= new (thd->mem_root) + Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(), + pos_in_q, len_in_q); + +#ifdef DBUG_ASSERT_EXISTS + if (item) + item->m_sp= sphead; +#endif + + return item; +} + + +/** + Helper to resolve the SQL:2003 Syntax exception 1) in . + See SQL:2003, Part 2, section 8.4 , Note 184, page 383. + This function returns the proper item for the SQL expression + left [NOT] IN ( expr ) + @param thd the current thread + @param left the in predicand + @param equal true for IN predicates, false for NOT IN predicates + @param expr first and only expression of the in value list + @return an expression representing the IN predicate. +*/ +Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, + Item *expr) +{ + /* + Relevant references for this issue: + - SQL:2003, Part 2, section 8.4 , page 383, + - SQL:2003, Part 2, section 7.2 , page 296, + - SQL:2003, Part 2, section 6.3 , page 174, + - SQL:2003, Part 2, section 7.15 , page 370, + - SQL:2003 Feature F561, "Full value expressions". + + The exception in SQL:2003 Note 184 means: + Item_singlerow_subselect, which corresponds to a , + should be re-interpreted as an Item_in_subselect, which corresponds + to a when used inside an . + + Our reading of Note 184 is reccursive, so that all: + - IN (( )) + - IN ((( ))) + - IN '('^N ')'^N + - etc + should be interpreted as a
, no matter how deep in the + expression the is. + */ + + Item *result; + + DBUG_ENTER("handle_sql2003_note184_exception"); + + if (expr->type() == Item::SUBSELECT_ITEM) + { + Item_subselect *expr2 = (Item_subselect*) expr; + + if (expr2->substype() == Item_subselect::SINGLEROW_SUBS) + { + Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2; + st_select_lex *subselect; + + /* + Implement the mandated change, by altering the semantic tree: + left IN Item_singlerow_subselect(subselect) + is modified to + left IN (subselect) + which is represented as + Item_in_subselect(left, subselect) + */ + subselect= expr3->invalidate_and_restore_select_lex(); + result= new (thd->mem_root) Item_in_subselect(thd, left, subselect); + + if (! equal) + result = negate_expression(thd, result); + + DBUG_RETURN(result); + } + } + + if (equal) + result= new (thd->mem_root) Item_func_eq(thd, left, expr); + else + result= new (thd->mem_root) Item_func_ne(thd, left, expr); + + DBUG_RETURN(result); +} + +/** + 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 pos The position in the raw SQL buffer +*/ + + +bool sp_create_assignment_lex(THD *thd, const char *pos) +{ + if (thd->lex->sphead) + { + sp_lex_local *new_lex; + if (!(new_lex= new (thd->mem_root) sp_lex_set_var(thd, thd->lex)) || + new_lex->main_select_push()) + return true; + new_lex->sphead->m_tmp_query= pos; + return thd->lex->sphead->reset_lex(thd, new_lex); + } + return false; +} + + +/** + 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 + @param need_set_keyword - if a SET statement "SET a=10", + or a direct assignment overwise "a:=10" + @return false if success, true otherwise. +*/ + +bool sp_create_assignment_instr(THD *thd, bool no_lookahead, + bool need_set_keyword) +{ + LEX *lex= thd->lex; + + if (lex->sphead) + { + if (!lex->var_list.is_empty()) + { + /* + - Every variable assignment from the same SET command, e.g.: + SET @var1=expr1, @var2=expr2; + produce each own sp_create_assignment_instr() call + lex->var_list.elements is 1 in this case. + - This query: + SET TRANSACTION READ ONLY, ISOLATION LEVEL SERIALIZABLE; + in translated to: + SET tx_read_only=1, tx_isolation=ISO_SERIALIZABLE; + but produces a single sp_create_assignment_instr() call + which includes the query fragment covering both options. + */ + DBUG_ASSERT(lex->var_list.elements >= 1 && lex->var_list.elements <= 2); + /* + sql_mode=ORACLE's direct assignment of a global variable + is not possible by the grammar. + */ + DBUG_ASSERT(lex->option_type != OPT_GLOBAL || need_set_keyword); + /* + We have assignment to user or system variable or + option setting, so we should construct sp_instr_stmt + for it. + */ + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + + /* + Extract the query statement from the tokenizer. The + end is either lip->ptr, if there was no lookahead, + lip->tok_end otherwise. + */ + static const LEX_CSTRING setlc= { STRING_WITH_LEN("SET ") }; + static const LEX_CSTRING setgl= { STRING_WITH_LEN("SET GLOBAL ") }; + const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end(); + Lex_cstring qbuf(lex->sphead->m_tmp_query, qend); + if (lex->new_sp_instr_stmt(thd, + lex->option_type == OPT_GLOBAL ? setgl : + need_set_keyword ? setlc : + null_clex_str, + qbuf)) + return true; + } + lex->pop_select(); + if (lex->check_main_unit_semantics()) + { + /* + "lex" can be referrenced by: + - sp_instr_set SET a= expr; + - sp_instr_set_row_field SET r.a= expr; + - sp_instr_stmt (just generated above) SET @a= expr; + In this case, "lex" is fully owned by sp_instr_xxx and it will + be deleted by the destructor ~sp_instr_xxx(). + So we should remove "lex" from the stack sp_head::m_lex, + to avoid double free. + Note, in case "lex" is not owned by any sp_instr_xxx, + it's also safe to remove it from the stack right now. + So we can remove it unconditionally, without testing lex->sp_lex_in_use. + */ + lex->sphead->restore_lex(thd); + 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; +} + + +void LEX::add_key_to_list(LEX_CSTRING *field_name, + enum Key::Keytype type, bool check_exists) +{ + Key *key; + MEM_ROOT *mem_root= thd->mem_root; + key= new (mem_root) + Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false, + DDL_options(check_exists ? + DDL_options::OPT_IF_NOT_EXISTS : + DDL_options::OPT_NONE)); + key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0), + mem_root); + alter_info.key_list.push_back(key, mem_root); +} + + +bool LEX::add_alter_list(const char *name, Virtual_column_info *expr, + bool exists) +{ + MEM_ROOT *mem_root= thd->mem_root; + Alter_column *ac= new (mem_root) Alter_column(name, expr, exists); + if (unlikely(ac == NULL)) + return true; + alter_info.alter_list.push_back(ac, mem_root); + alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + return false; +} + + +void LEX::init_last_field(Column_definition *field, + const LEX_CSTRING *field_name, + const CHARSET_INFO *cs) +{ + last_field= field; + + field->field_name= *field_name; + + /* reset LEX fields that are used in Create_field::set_and_check() */ + charset= cs; +} + + +bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) +{ + /* + if charset is NULL - we're parsing a field declaration. + we cannot call find_bin_collation for a field here, because actual + field charset is determined in get_sql_field_charset() much later. + so we only set a flag. + */ + if (!charset) + { + charset= cs; + last_field->flags|= bin ? BINCMP_FLAG : 0; + return false; + } + + charset= bin ? find_bin_collation(cs ? cs : charset) + : cs ? cs : charset; + return charset == NULL; +} + + +Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) +{ + Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); + if (unlikely(!v)) + return 0; + v->expr= expr; + v->utf8= 0; /* connection charset */ + return v; +} + + + /** @note The order of the elements of this array must correspond to the order of elements in enum_binlog_stmt_unsafe. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 05e68f3b752..5d56c885189 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -175,506 +175,6 @@ void turn_parser_debug_on() #endif -/** - Helper action for a case expression statement (the expr in 'CASE expr'). - This helper is used for 'searched' cases only. - @param lex the parser lex context - @param expr the parsed expression - @return 0 on success -*/ - -int LEX::case_stmt_action_expr(Item* expr) -{ - int case_expr_id= spcont->register_case_expr(); - sp_instr_set_case_expr *i; - - if (spcont->push_case_expr_id(case_expr_id)) - return 1; - - i= new (thd->mem_root) - sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr, - this); - - sphead->add_cont_backpatch(i); - return sphead->add_instr(i); -} - -/** - Helper action for a case when condition. - This helper is used for both 'simple' and 'searched' cases. - @param lex the parser lex context - @param when the parsed expression for the WHEN clause - @param simple true for simple cases, false for searched cases -*/ - -int LEX::case_stmt_action_when(Item *when, bool simple) -{ - uint ip= sphead->instructions(); - sp_instr_jump_if_not *i; - Item_case_expr *var; - Item *expr; - - if (simple) - { - var= new (thd->mem_root) - Item_case_expr(thd, spcont->get_current_case_expr_id()); - -#ifdef DBUG_ASSERT_EXISTS - if (var) - { - var->m_sp= sphead; - } -#endif - - expr= new (thd->mem_root) Item_func_eq(thd, var, when); - i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this); - } - else - i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this); - - /* - BACKPATCH: Registering forward jump from - "case_stmt_action_when" to "case_stmt_action_then" - (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) - */ - - return - !MY_TEST(i) || - sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) || - sphead->add_cont_backpatch(i) || - sphead->add_instr(i); -} - -/** - Helper action for a case then statements. - This helper is used for both 'simple' and 'searched' cases. - @param lex the parser lex context -*/ - -int LEX::case_stmt_action_then() -{ - uint ip= sphead->instructions(); - sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont); - if (!MY_TEST(i) || sphead->add_instr(i)) - return 1; - - /* - BACKPATCH: Resolving forward jump from - "case_stmt_action_when" to "case_stmt_action_then" - (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) - */ - - sphead->backpatch(spcont->pop_label()); - - /* - BACKPATCH: Registering forward jump from - "case_stmt_action_then" to after END CASE - (jump from instruction 4 to 12, 7 to 12 ... in the example) - */ - - return sphead->push_backpatch(thd, i, spcont->last_label()); -} - - -/** - Helper action for a SET statement. - Used to push a system variable into the assignment list. - - @param tmp the system variable with base name - @param var_type the scope of the variable - @param val the value being assigned to the variable - - @return TRUE if error, FALSE otherwise. -*/ - -bool -LEX::set_system_variable(enum enum_var_type var_type, - sys_var *sysvar, const Lex_ident_sys_st *base_name, - Item *val) -{ - set_var *setvar; - - /* No AUTOCOMMIT from a stored function or trigger. */ - if (spcont && sysvar == Sys_autocommit_ptr) - sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; - - if (val && val->type() == Item::FIELD_ITEM && - ((Item_field*)val)->table_name.str) - { - my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str); - return TRUE; - } - - if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar, - base_name, val))) - return TRUE; - - return var_list.push_back(setvar, thd->mem_root); -} - - -/** - Helper action for a SET statement. - Used to SET a field of NEW row. - - @param name the field name - @param val the value being assigned to the row - - @return TRUE if error, FALSE otherwise. -*/ - -bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val) -{ - Item_trigger_field *trg_fld; - sp_instr_set_trigger_field *sp_fld; - - /* QQ: Shouldn't this be field's default value ? */ - if (! val) - val= new (thd->mem_root) Item_null(thd); - - DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE && - (trg_chistics.event == TRG_EVENT_INSERT || - trg_chistics.event == TRG_EVENT_UPDATE)); - - trg_fld= new (thd->mem_root) - Item_trigger_field(thd, current_context(), - Item_trigger_field::NEW_ROW, - *name, UPDATE_ACL, FALSE); - - if (unlikely(trg_fld == NULL)) - return TRUE; - - sp_fld= new (thd->mem_root) - sp_instr_set_trigger_field(sphead->instructions(), - spcont, trg_fld, val, this); - - if (unlikely(sp_fld == NULL)) - return TRUE; - - /* - Let us add this item to list of all Item_trigger_field - objects in trigger. - */ - trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); - - return sphead->add_instr(sp_fld); -} - - -/** - Create an object to represent a SP variable in the Item-hierarchy. - - @param name The SP variable name. - @param spvar The SP variable (optional). - @param start_in_q Start position of the SP variable name in the query. - @param end_in_q End position of the SP variable name in the query. - - @remark If spvar is not specified, the name is used to search for the - variable in the parse-time context. If the variable does not - exist, a error is set and NULL is returned to the caller. - - @return An Item_splocal object representing the SP variable, or NULL on error. -*/ -Item_splocal* -LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar) -{ - const Sp_rcontext_handler *rh; - Item_splocal *item; - const char *start_in_q= cname->pos(); - const char *end_in_q= cname->end(); - uint pos_in_q, len_in_q; - Lex_ident_sys name(thd, cname); - - if (name.is_null()) - return NULL; // EOM - - /* If necessary, look for the variable. */ - if (spcont && !spvar) - spvar= find_variable(&name, &rh); - - if (!spvar) - { - my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str); - return NULL; - } - - DBUG_ASSERT(spcont && spvar); - - /* Position and length of the SP variable name in the query. */ - pos_in_q= (uint)(start_in_q - sphead->m_tmp_query); - len_in_q= (uint)(end_in_q - start_in_q); - - item= new (thd->mem_root) - Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(), - pos_in_q, len_in_q); - -#ifdef DBUG_ASSERT_EXISTS - if (item) - item->m_sp= sphead; -#endif - - return item; -} - -/** - Helper to resolve the SQL:2003 Syntax exception 1) in . - See SQL:2003, Part 2, section 8.4 , Note 184, page 383. - This function returns the proper item for the SQL expression - left [NOT] IN ( expr ) - @param thd the current thread - @param left the in predicand - @param equal true for IN predicates, false for NOT IN predicates - @param expr first and only expression of the in value list - @return an expression representing the IN predicate. -*/ -Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, - Item *expr) -{ - /* - Relevant references for this issue: - - SQL:2003, Part 2, section 8.4 , page 383, - - SQL:2003, Part 2, section 7.2 , page 296, - - SQL:2003, Part 2, section 6.3 , page 174, - - SQL:2003, Part 2, section 7.15 , page 370, - - SQL:2003 Feature F561, "Full value expressions". - - The exception in SQL:2003 Note 184 means: - Item_singlerow_subselect, which corresponds to a , - should be re-interpreted as an Item_in_subselect, which corresponds - to a
when used inside an . - - Our reading of Note 184 is reccursive, so that all: - - IN (( )) - - IN ((( ))) - - IN '('^N ')'^N - - etc - should be interpreted as a
, no matter how deep in the - expression the is. - */ - - Item *result; - - DBUG_ENTER("handle_sql2003_note184_exception"); - - if (expr->type() == Item::SUBSELECT_ITEM) - { - Item_subselect *expr2 = (Item_subselect*) expr; - - if (expr2->substype() == Item_subselect::SINGLEROW_SUBS) - { - Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2; - st_select_lex *subselect; - - /* - Implement the mandated change, by altering the semantic tree: - left IN Item_singlerow_subselect(subselect) - is modified to - left IN (subselect) - which is represented as - Item_in_subselect(left, subselect) - */ - subselect= expr3->invalidate_and_restore_select_lex(); - result= new (thd->mem_root) Item_in_subselect(thd, left, subselect); - - if (! equal) - result = negate_expression(thd, result); - - DBUG_RETURN(result); - } - } - - if (equal) - result= new (thd->mem_root) Item_func_eq(thd, left, expr); - else - result= new (thd->mem_root) Item_func_ne(thd, left, expr); - - DBUG_RETURN(result); -} - -/** - 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 pos The position in the raw SQL buffer -*/ - - -bool sp_create_assignment_lex(THD *thd, const char *pos) -{ - if (thd->lex->sphead) - { - sp_lex_local *new_lex; - if (!(new_lex= new (thd->mem_root) sp_lex_set_var(thd, thd->lex)) || - new_lex->main_select_push()) - return true; - new_lex->sphead->m_tmp_query= pos; - return thd->lex->sphead->reset_lex(thd, new_lex); - } - return false; -} - - -/** - 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 - @param need_set_keyword - if a SET statement "SET a=10", - or a direct assignment overwise "a:=10" - @return false if success, true otherwise. -*/ - -bool sp_create_assignment_instr(THD *thd, bool no_lookahead, - bool need_set_keyword) -{ - LEX *lex= thd->lex; - - if (lex->sphead) - { - if (!lex->var_list.is_empty()) - { - /* - - Every variable assignment from the same SET command, e.g.: - SET @var1=expr1, @var2=expr2; - produce each own sp_create_assignment_instr() call - lex->var_list.elements is 1 in this case. - - This query: - SET TRANSACTION READ ONLY, ISOLATION LEVEL SERIALIZABLE; - in translated to: - SET tx_read_only=1, tx_isolation=ISO_SERIALIZABLE; - but produces a single sp_create_assignment_instr() call - which includes the query fragment covering both options. - */ - DBUG_ASSERT(lex->var_list.elements >= 1 && lex->var_list.elements <= 2); - /* - sql_mode=ORACLE's direct assignment of a global variable - is not possible by the grammar. - */ - DBUG_ASSERT(Lex->option_type != OPT_GLOBAL || need_set_keyword); - /* - We have assignment to user or system variable or - option setting, so we should construct sp_instr_stmt - for it. - */ - Lex_input_stream *lip= &thd->m_parser_state->m_lip; - - /* - Extract the query statement from the tokenizer. The - end is either lip->ptr, if there was no lookahead, - lip->tok_end otherwise. - */ - static const LEX_CSTRING setlc= { STRING_WITH_LEN("SET ") }; - static const LEX_CSTRING setgl= { STRING_WITH_LEN("SET GLOBAL ") }; - const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end(); - Lex_cstring qbuf(lex->sphead->m_tmp_query, qend); - if (lex->new_sp_instr_stmt(thd, - Lex->option_type == OPT_GLOBAL ? setgl : - need_set_keyword ? setlc : - null_clex_str, - qbuf)) - return true; - } - lex->pop_select(); - if (lex->check_main_unit_semantics()) - { - /* - "lex" can be referrenced by: - - sp_instr_set SET a= expr; - - sp_instr_set_row_field SET r.a= expr; - - sp_instr_stmt (just generated above) SET @a= expr; - In this case, "lex" is fully owned by sp_instr_xxx and it will - be deleted by the destructor ~sp_instr_xxx(). - So we should remove "lex" from the stack sp_head::m_lex, - to avoid double free. - Note, in case "lex" is not owned by any sp_instr_xxx, - it's also safe to remove it from the stack right now. - So we can remove it unconditionally, without testing lex->sp_lex_in_use. - */ - lex->sphead->restore_lex(thd); - 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; -} - -void LEX::add_key_to_list(LEX_CSTRING *field_name, - enum Key::Keytype type, bool check_exists) -{ - Key *key; - MEM_ROOT *mem_root= thd->mem_root; - key= new (mem_root) - Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false, - DDL_options(check_exists ? - DDL_options::OPT_IF_NOT_EXISTS : - DDL_options::OPT_NONE)); - key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0), - mem_root); - alter_info.key_list.push_back(key, mem_root); -} - -bool LEX::add_alter_list(const char *name, Virtual_column_info *expr, - bool exists) -{ - MEM_ROOT *mem_root= thd->mem_root; - Alter_column *ac= new (mem_root) Alter_column(name, expr, exists); - if (unlikely(ac == NULL)) - return true; - alter_info.alter_list.push_back(ac, mem_root); - alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; - return false; -} - -void LEX::init_last_field(Column_definition *field, - const LEX_CSTRING *field_name, - const CHARSET_INFO *cs) -{ - last_field= field; - - field->field_name= *field_name; - - /* reset LEX fields that are used in Create_field::set_and_check() */ - charset= cs; -} - - -bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) -{ - /* - if charset is NULL - we're parsing a field declaration. - we cannot call find_bin_collation for a field here, because actual - field charset is determined in get_sql_field_charset() much later. - so we only set a flag. - */ - if (!charset) - { - charset= cs; - last_field->flags|= bin ? BINCMP_FLAG : 0; - return false; - } - - charset= bin ? find_bin_collation(cs ? cs : charset) - : cs ? cs : charset; - return charset == NULL; -} - #define bincmp_collation(X,Y) \ do \ { \ @@ -682,18 +182,6 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) MYSQL_YYABORT; \ } while(0) - -Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) -{ - Virtual_column_info *v= new (thd->mem_root) Virtual_column_info(); - if (unlikely(!v)) - return 0; - v->expr= expr; - v->utf8= 0; /* connection charset */ - return v; -} - - %} %union { int num; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 20312ab7e37..4ea4fd464fb 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -152,8 +152,6 @@ void ORAerror(THD *thd, const char *s) } - - #define bincmp_collation(X,Y) \ do \ { \