Commit e26d0491 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-21023 Move LEX methods and related functions from sql_yacc.yy to sql_lex.cc

parent 68ed3a81
......@@ -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 <in predicate>.
See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
This function returns the proper item for the SQL expression
<code>left [NOT] IN ( expr )</code>
@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 <in predicate>, page 383,
- SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- SQL:2003 Feature F561, "Full value expressions".
The exception in SQL:2003 Note 184 means:
Item_singlerow_subselect, which corresponds to a <scalar subquery>,
should be re-interpreted as an Item_in_subselect, which corresponds
to a <table subquery> when used inside an <in predicate>.
Our reading of Note 184 is reccursive, so that all:
- IN (( <subquery> ))
- IN ((( <subquery> )))
- IN '('^N <subquery> ')'^N
- etc
should be interpreted as a <table subquery>, no matter how deep in the
expression the <subquery> 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.
......
......@@ -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 <in predicate>.
See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
This function returns the proper item for the SQL expression
<code>left [NOT] IN ( expr )</code>
@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 <in predicate>, page 383,
- SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- SQL:2003 Feature F561, "Full value expressions".
The exception in SQL:2003 Note 184 means:
Item_singlerow_subselect, which corresponds to a <scalar subquery>,
should be re-interpreted as an Item_in_subselect, which corresponds
to a <table subquery> when used inside an <in predicate>.
Our reading of Note 184 is reccursive, so that all:
- IN (( <subquery> ))
- IN ((( <subquery> )))
- IN '('^N <subquery> ')'^N
- etc
should be interpreted as a <table subquery>, no matter how deep in the
expression the <subquery> 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;
......
......@@ -152,8 +152,6 @@ void ORAerror(THD *thd, const char *s)
}
#define bincmp_collation(X,Y) \
do \
{ \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment