MDEV-21023 Move LEX methods and related functions from sql_yacc.yy to sql_lex.cc
This commit is contained in:
parent
68ed3a81f2
commit
e26d049197
518
sql/sql_lex.cc
518
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 <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.
|
||||
|
512
sql/sql_yacc.yy
512
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 <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 \
|
||||
{ \
|
||||
|
Loading…
x
Reference in New Issue
Block a user