forked from StoneAtom/StoneDB
1023 lines
27 KiB
C++
1023 lines
27 KiB
C++
/* Copyright (c) 2013, 2021, Oracle and/or its affiliates.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "parse_tree_nodes.h"
|
|
|
|
#include "sp_instr.h" // sp_instr_set
|
|
#include "sql_delete.h" // Sql_cmd_delete_multi, Sql_cmd_delete
|
|
#include "sql_insert.h" // Sql_cmd_insert...
|
|
|
|
|
|
bool PT_group::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
SELECT_LEX *select= pc->select;
|
|
select->parsing_place= CTX_GROUP_BY;
|
|
|
|
if (group_list->contextualize(pc))
|
|
return true;
|
|
assert(select == pc->select);
|
|
|
|
select->group_list= group_list->value;
|
|
|
|
// Ensure we're resetting parsing place of the right select
|
|
assert(select->parsing_place == CTX_GROUP_BY);
|
|
select->parsing_place= CTX_NONE;
|
|
|
|
switch (olap) {
|
|
case UNSPECIFIED_OLAP_TYPE:
|
|
break;
|
|
case CUBE_TYPE:
|
|
if (select->linkage == GLOBAL_OPTIONS_TYPE)
|
|
{
|
|
my_error(ER_WRONG_USAGE, MYF(0), "WITH CUBE",
|
|
"global union parameters");
|
|
return true;
|
|
}
|
|
select->olap= CUBE_TYPE;
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE");
|
|
return true;
|
|
case ROLLUP_TYPE:
|
|
if (select->linkage == GLOBAL_OPTIONS_TYPE)
|
|
{
|
|
my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
|
|
"global union parameters");
|
|
return true;
|
|
}
|
|
if (select->is_distinct())
|
|
{
|
|
// DISTINCT+ROLLUP does not work
|
|
my_error(ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", "DISTINCT");
|
|
return true;
|
|
}
|
|
select->olap= ROLLUP_TYPE;
|
|
break;
|
|
default:
|
|
assert(!"unexpected OLAP type!");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_order::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
THD *thd= pc->thd;
|
|
LEX *lex= thd->lex;
|
|
SELECT_LEX_UNIT * const unit= pc->select->master_unit();
|
|
const bool braces= pc->select->braces;
|
|
|
|
if (pc->select->linkage != GLOBAL_OPTIONS_TYPE &&
|
|
pc->select->olap != UNSPECIFIED_OLAP_TYPE &&
|
|
(pc->select->linkage != UNION_TYPE || braces))
|
|
{
|
|
my_error(ER_WRONG_USAGE, MYF(0),
|
|
"CUBE/ROLLUP", "ORDER BY");
|
|
return true;
|
|
}
|
|
if (lex->sql_command != SQLCOM_ALTER_TABLE && !unit->fake_select_lex)
|
|
{
|
|
/*
|
|
A query of the of the form (SELECT ...) ORDER BY order_list is
|
|
executed in the same way as the query
|
|
SELECT ... ORDER BY order_list
|
|
unless the SELECT construct contains ORDER BY or LIMIT clauses.
|
|
Otherwise we create a fake SELECT_LEX if it has not been created
|
|
yet.
|
|
*/
|
|
SELECT_LEX *first_sl= unit->first_select();
|
|
if (!unit->is_union() &&
|
|
(first_sl->order_list.elements || first_sl->select_limit))
|
|
{
|
|
if (unit->add_fake_select_lex(lex->thd))
|
|
return true;
|
|
pc->select= unit->fake_select_lex;
|
|
}
|
|
}
|
|
|
|
bool context_is_pushed= false;
|
|
if (pc->select->parsing_place == CTX_NONE)
|
|
{
|
|
if (unit->is_union() && !braces)
|
|
{
|
|
/*
|
|
At this point we don't know yet whether this is the last
|
|
select in union or not, but we move ORDER BY to
|
|
fake_select_lex anyway. If there would be one more select
|
|
in union mysql_new_select will correctly throw error.
|
|
*/
|
|
pc->select= unit->fake_select_lex;
|
|
lex->push_context(&pc->select->context);
|
|
context_is_pushed= true;
|
|
}
|
|
/*
|
|
To preserve correct markup for the case
|
|
SELECT group_concat(... ORDER BY (subquery))
|
|
we do not change parsing_place if it's not NONE.
|
|
*/
|
|
pc->select->parsing_place= CTX_ORDER_BY;
|
|
}
|
|
|
|
if (order_list->contextualize(pc))
|
|
return true;
|
|
|
|
if (context_is_pushed)
|
|
lex->pop_context();
|
|
|
|
pc->select->order_list= order_list->value;
|
|
|
|
// Reset parsing place only for ORDER BY
|
|
if (pc->select->parsing_place == CTX_ORDER_BY)
|
|
pc->select->parsing_place= CTX_NONE;
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_table_factor_select_sym::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX * const lex= pc->thd->lex;
|
|
SELECT_LEX *const outer_select= pc->select;
|
|
|
|
if (!outer_select->embedding || outer_select->end_nested_join(pc->thd))
|
|
{
|
|
/* we are not in parentheses */
|
|
error(pc, pos);
|
|
return true;
|
|
}
|
|
TABLE_LIST *embedding= outer_select->embedding;
|
|
const bool is_deeply_nested= embedding &&
|
|
!embedding->nested_join->join_list.elements;
|
|
|
|
lex->derived_tables|= DERIVED_SUBQUERY;
|
|
if (!lex->expr_allows_subselect ||
|
|
lex->sql_command == (int)SQLCOM_PURGE)
|
|
{
|
|
error(pc, pos);
|
|
return true;
|
|
}
|
|
|
|
outer_select->parsing_place= CTX_DERIVED;
|
|
if (outer_select->linkage == GLOBAL_OPTIONS_TYPE)
|
|
return true; // TODO: error(pc, pos)?
|
|
|
|
SELECT_LEX * const child= lex->new_query(pc->select);
|
|
if (child == NULL)
|
|
return true;
|
|
|
|
// Note that this current select is different from the one above
|
|
pc->select= child;
|
|
pc->select->linkage= DERIVED_TABLE_TYPE;
|
|
pc->select->parsing_place= CTX_SELECT_LIST;
|
|
outer_select->parsing_place= CTX_NONE;
|
|
|
|
Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;
|
|
|
|
if (select_options.query_spec_options & SELECT_HIGH_PRIORITY)
|
|
{
|
|
yyps->m_lock_type= TL_READ_HIGH_PRIORITY;
|
|
yyps->m_mdl_type= MDL_SHARED_READ;
|
|
}
|
|
if (select_options.save_to(pc))
|
|
return true;
|
|
|
|
if (select_item_list->contextualize(pc))
|
|
return true;
|
|
assert(child == pc->select);
|
|
|
|
// Ensure we're resetting parsing place of the right select
|
|
assert(child->parsing_place == CTX_SELECT_LIST);
|
|
child->parsing_place= CTX_NONE;
|
|
|
|
if (table_expression->contextualize(pc))
|
|
return true;
|
|
|
|
if (is_deeply_nested)
|
|
{
|
|
if (child->set_braces(1))
|
|
{
|
|
error(pc, pos);
|
|
return true;
|
|
}
|
|
}
|
|
if (outer_select->init_nested_join(pc->thd))
|
|
return true;
|
|
|
|
/* incomplete derived tables return NULL, we must be
|
|
nested in select_derived rule to be here. */
|
|
value= NULL;
|
|
|
|
if (opt_hint_list != NULL && opt_hint_list->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_table_factor_parenthesis::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
SELECT_LEX * const outer_select= pc->select;
|
|
|
|
if (select_derived_union->contextualize(pc))
|
|
return true;
|
|
|
|
/*
|
|
Use outer_select instead of pc->select as derived table has
|
|
altered value of pc->select.
|
|
*/
|
|
if (!(select_derived_union->value || opt_table_alias) &&
|
|
outer_select->embedding &&
|
|
!outer_select->embedding->nested_join->join_list.elements)
|
|
{
|
|
/*
|
|
we have a derived table (select_derived_union->value == NULL)
|
|
but no alias,
|
|
Since we are nested in further parentheses so we
|
|
can pass NULL to the outer level parentheses
|
|
Permits parsing of "((((select ...))) as xyz)"
|
|
*/
|
|
value= 0;
|
|
}
|
|
else if (!select_derived_union->value)
|
|
{
|
|
/*
|
|
Handle case of derived table, alias may be NULL if there
|
|
are no outer parentheses, add_table_to_list() will throw
|
|
error in this case
|
|
*/
|
|
SELECT_LEX_UNIT *unit= pc->select->master_unit();
|
|
pc->select= outer_select;
|
|
Table_ident *ti= new Table_ident(unit);
|
|
if (ti == NULL)
|
|
return true;
|
|
|
|
value= pc->select->add_table_to_list(pc->thd,
|
|
ti, opt_table_alias, 0,
|
|
TL_READ, MDL_SHARED_READ);
|
|
if (value == NULL)
|
|
return true;
|
|
pc->select->add_joined_table(value);
|
|
pc->thd->lex->pop_context();
|
|
}
|
|
else if (opt_table_alias != NULL)
|
|
{
|
|
/*
|
|
Tables with or without joins within parentheses cannot
|
|
have aliases, and we ruled out derived tables above.
|
|
*/
|
|
error(pc, alias_pos);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
/* nested join: FROM (t1 JOIN t2 ...) */
|
|
value= select_derived_union->value;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_internal_variable_name_2d::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
THD *thd= pc->thd;
|
|
LEX *lex= thd->lex;
|
|
sp_head *sp= lex->sphead;
|
|
|
|
if (check_reserved_words(&ident1))
|
|
{
|
|
error(pc, pos);
|
|
return true;
|
|
}
|
|
|
|
if (sp && sp->m_type == SP_TYPE_TRIGGER &&
|
|
(!my_strcasecmp(system_charset_info, ident1.str, "NEW") ||
|
|
!my_strcasecmp(system_charset_info, ident1.str, "OLD")))
|
|
{
|
|
if (ident1.str[0]=='O' || ident1.str[0]=='o')
|
|
{
|
|
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
|
|
return true;
|
|
}
|
|
if (sp->m_trg_chistics.event == TRG_EVENT_DELETE)
|
|
{
|
|
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0),
|
|
"NEW", "on DELETE");
|
|
return true;
|
|
}
|
|
if (sp->m_trg_chistics.action_time == TRG_ACTION_AFTER)
|
|
{
|
|
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ");
|
|
return true;
|
|
}
|
|
/* This special combination will denote field of NEW row */
|
|
value.var= trg_new_row_fake_var;
|
|
value.base_name= ident2;
|
|
}
|
|
else
|
|
{
|
|
sys_var *tmp=find_sys_var(thd, ident2.str, ident2.length);
|
|
if (!tmp)
|
|
return true;
|
|
if (!tmp->is_struct())
|
|
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), ident2.str);
|
|
value.var= tmp;
|
|
value.base_name= ident1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_option_value_no_option_type_internal::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc) || name->contextualize(pc))
|
|
return true;
|
|
|
|
THD *thd= pc->thd;
|
|
LEX *lex= thd->lex;
|
|
sp_head *sp= lex->sphead;
|
|
|
|
if (sp)
|
|
sp->m_parser_data.push_expr_start_ptr(expr_pos.raw.start);
|
|
|
|
if (opt_expr != NULL && opt_expr->itemize(pc, &opt_expr))
|
|
return true;
|
|
|
|
const char *expr_start_ptr= NULL;
|
|
|
|
if (sp)
|
|
expr_start_ptr= sp->m_parser_data.pop_expr_start_ptr();
|
|
|
|
if (name->value.var == trg_new_row_fake_var)
|
|
{
|
|
assert(sp);
|
|
assert(expr_start_ptr);
|
|
|
|
/* We are parsing trigger and this is a trigger NEW-field. */
|
|
|
|
LEX_STRING expr_query= EMPTY_STR;
|
|
|
|
if (!opt_expr)
|
|
{
|
|
// This is: SET NEW.x = DEFAULT
|
|
// DEFAULT clause is not supported in triggers.
|
|
|
|
error(pc, expr_pos);
|
|
return true;
|
|
}
|
|
else if (lex->is_metadata_used())
|
|
{
|
|
expr_query= make_string(thd, expr_start_ptr, expr_pos.raw.end);
|
|
|
|
if (!expr_query.str)
|
|
return true;
|
|
}
|
|
|
|
if (set_trigger_new_row(pc, name->value.base_name, opt_expr, expr_query))
|
|
return true;
|
|
}
|
|
else if (name->value.var)
|
|
{
|
|
/* We're not parsing SP and this is a system variable. */
|
|
|
|
if (set_system_variable(thd, &name->value, lex->option_type, opt_expr))
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
assert(sp);
|
|
assert(expr_start_ptr);
|
|
|
|
/* We're parsing SP and this is an SP-variable. */
|
|
|
|
sp_pcontext *pctx= lex->get_sp_current_parsing_ctx();
|
|
sp_variable *spv= pctx->find_variable(name->value.base_name, false);
|
|
|
|
LEX_STRING expr_query= EMPTY_STR;
|
|
|
|
if (!opt_expr)
|
|
{
|
|
/*
|
|
This is: SET x = DEFAULT, where x is a SP-variable.
|
|
This is not supported.
|
|
*/
|
|
|
|
error(pc, expr_pos);
|
|
return true;
|
|
}
|
|
else if (lex->is_metadata_used())
|
|
{
|
|
expr_query= make_string(thd, expr_start_ptr, expr_pos.raw.end);
|
|
|
|
if (!expr_query.str)
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
NOTE: every SET-expression has its own LEX-object, even if it is
|
|
a multiple SET-statement, like:
|
|
|
|
SET spv1 = expr1, spv2 = expr2, ...
|
|
|
|
Every SET-expression has its own sp_instr_set. Thus, the
|
|
instruction owns the LEX-object, i.e. the instruction is
|
|
responsible for destruction of the LEX-object.
|
|
*/
|
|
|
|
sp_instr_set *i=
|
|
new sp_instr_set(sp->instructions(), lex,
|
|
spv->offset, opt_expr, expr_query,
|
|
true); // The instruction owns its lex.
|
|
|
|
if (!i || sp->add_instr(thd, i))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_option_value_no_option_type_password::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
THD *thd= pc->thd;
|
|
LEX *lex= thd->lex;
|
|
sp_head *sp= lex->sphead;
|
|
sp_pcontext *pctx= lex->get_sp_current_parsing_ctx();
|
|
LEX_STRING pw= { C_STRING_WITH_LEN("password") };
|
|
|
|
if (pctx && pctx->find_variable(pw, false))
|
|
{
|
|
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
|
|
return true;
|
|
}
|
|
|
|
LEX_USER *user= (LEX_USER*) thd->alloc(sizeof(LEX_USER));
|
|
|
|
if (!user)
|
|
return true;
|
|
|
|
LEX_CSTRING sctx_user= thd->security_context()->user();
|
|
user->user.str= (char *) sctx_user.str;
|
|
user->user.length= sctx_user.length;
|
|
|
|
LEX_CSTRING sctx_priv_host= thd->security_context()->priv_host();
|
|
assert(sctx_priv_host.str);
|
|
user->host.str= (char *) sctx_priv_host.str;
|
|
user->host.length= sctx_priv_host.length;
|
|
|
|
set_var_password *var= new set_var_password(user,
|
|
const_cast<char *>(password));
|
|
if (var == NULL)
|
|
return true;
|
|
|
|
lex->var_list.push_back(var);
|
|
lex->autocommit= true;
|
|
lex->is_set_password_sql= true;
|
|
|
|
if (sp)
|
|
sp->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
|
|
|
|
if (sp_create_assignment_instr(pc->thd, expr_pos.raw.end))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Given a table in the source list, find a correspondent table in the
|
|
table references list.
|
|
|
|
@param src Source table to match.
|
|
@param ref Table references list.
|
|
|
|
@remark The source table list (tables listed before the FROM clause
|
|
or tables listed in the FROM clause before the USING clause) may
|
|
contain table names or aliases that must match unambiguously one,
|
|
and only one, table in the target table list (table references list,
|
|
after FROM/USING clause).
|
|
|
|
@return Matching table, NULL otherwise.
|
|
*/
|
|
|
|
static TABLE_LIST *multi_delete_table_match(TABLE_LIST *tbl, TABLE_LIST *tables)
|
|
{
|
|
TABLE_LIST *match= NULL;
|
|
DBUG_ENTER("multi_delete_table_match");
|
|
|
|
for (TABLE_LIST *elem= tables; elem; elem= elem->next_local)
|
|
{
|
|
int cmp;
|
|
|
|
if (tbl->is_fqtn && elem->is_alias)
|
|
continue; /* no match */
|
|
if (tbl->is_fqtn && elem->is_fqtn)
|
|
cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
|
|
strcmp(tbl->db, elem->db);
|
|
else if (elem->is_alias)
|
|
cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias);
|
|
else
|
|
cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
|
|
strcmp(tbl->db, elem->db);
|
|
|
|
if (cmp)
|
|
continue;
|
|
|
|
if (match)
|
|
{
|
|
my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias);
|
|
DBUG_RETURN(NULL);
|
|
}
|
|
|
|
match= elem;
|
|
}
|
|
|
|
if (!match)
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE");
|
|
|
|
DBUG_RETURN(match);
|
|
}
|
|
|
|
|
|
/**
|
|
Link tables in auxilary table list of multi-delete with corresponding
|
|
elements in main table list, and set proper locks for them.
|
|
|
|
@param pc Parse context
|
|
|
|
@retval
|
|
FALSE success
|
|
@retval
|
|
TRUE error
|
|
*/
|
|
|
|
static bool multi_delete_set_locks_and_link_aux_tables(Parse_context *pc)
|
|
{
|
|
LEX * const lex= pc->thd->lex;
|
|
TABLE_LIST *tables= pc->select->table_list.first;
|
|
TABLE_LIST *target_tbl;
|
|
DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
|
|
|
|
for (target_tbl= lex->auxiliary_table_list.first;
|
|
target_tbl; target_tbl= target_tbl->next_local)
|
|
{
|
|
/* All tables in aux_tables must be found in FROM PART */
|
|
TABLE_LIST *walk= multi_delete_table_match(target_tbl, tables);
|
|
if (!walk)
|
|
DBUG_RETURN(TRUE);
|
|
if (!walk->is_derived())
|
|
{
|
|
target_tbl->table_name= walk->table_name;
|
|
target_tbl->table_name_length= walk->table_name_length;
|
|
}
|
|
walk->updating= target_tbl->updating;
|
|
walk->lock_type= target_tbl->lock_type;
|
|
/* We can assume that tables to be deleted from are locked for write. */
|
|
assert(walk->lock_type >= TL_WRITE_ALLOW_WRITE);
|
|
walk->mdl_request.set_type(mdl_type_for_dml(walk->lock_type));
|
|
target_tbl->correspondent_table= walk; // Remember corresponding table
|
|
}
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
|
|
bool PT_delete::add_table(Parse_context *pc, Table_ident *table)
|
|
{
|
|
const ulong table_opts= is_multitable() ? TL_OPTION_UPDATING | TL_OPTION_ALIAS
|
|
: TL_OPTION_UPDATING;
|
|
const thr_lock_type lock_type=
|
|
(opt_delete_options & DELETE_LOW_PRIORITY) ? TL_WRITE_LOW_PRIORITY
|
|
: TL_WRITE_DEFAULT;
|
|
const enum_mdl_type mdl_type=
|
|
(opt_delete_options & DELETE_LOW_PRIORITY) ? MDL_SHARED_WRITE_LOW_PRIO
|
|
: MDL_SHARED_WRITE;
|
|
return !pc->select->add_table_to_list(pc->thd, table, NULL, table_opts,
|
|
lock_type, mdl_type, NULL,
|
|
opt_use_partition);
|
|
}
|
|
|
|
bool PT_delete::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX * const lex= pc->thd->lex;
|
|
|
|
lex->sql_command= is_multitable() ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
|
|
lex->set_ignore(MY_TEST(opt_delete_options & DELETE_IGNORE));
|
|
lex->select_lex->init_order();
|
|
if (opt_delete_options & DELETE_QUICK)
|
|
pc->select->add_base_options(OPTION_QUICK);
|
|
|
|
if (is_multitable())
|
|
{
|
|
for (Table_ident **i= table_list.begin(); i != table_list.end(); ++i)
|
|
{
|
|
if (add_table(pc, *i))
|
|
return true;
|
|
}
|
|
}
|
|
else if (add_table(pc, table_ident))
|
|
return true;
|
|
|
|
if (is_multitable())
|
|
mysql_init_multi_delete(lex);
|
|
else
|
|
pc->select->top_join_list.push_back(pc->select->get_table_list());
|
|
|
|
Yacc_state * const yyps= &pc->thd->m_parser_state->m_yacc;
|
|
yyps->m_lock_type= TL_READ_DEFAULT;
|
|
yyps->m_mdl_type= MDL_SHARED_READ;
|
|
|
|
if (is_multitable() && join_table_list->contextualize(pc))
|
|
return true;
|
|
|
|
if (opt_where_clause != NULL &&
|
|
opt_where_clause->itemize(pc, &opt_where_clause))
|
|
return true;
|
|
pc->select->set_where_cond(opt_where_clause);
|
|
|
|
if (opt_order_clause != NULL && opt_order_clause->contextualize(pc))
|
|
return true;
|
|
|
|
assert(pc->select->select_limit == NULL);
|
|
if (opt_delete_limit_clause != NULL)
|
|
{
|
|
if (opt_delete_limit_clause->itemize(pc, &opt_delete_limit_clause))
|
|
return true;
|
|
pc->select->select_limit= opt_delete_limit_clause;
|
|
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
|
|
pc->select->explicit_limit= 1;
|
|
}
|
|
|
|
if (is_multitable() && multi_delete_set_locks_and_link_aux_tables(pc))
|
|
return true;
|
|
|
|
if (opt_hints != NULL && opt_hints->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
Sql_cmd *PT_delete::make_cmd(THD *thd)
|
|
{
|
|
Parse_context pc(thd, thd->lex->current_select());
|
|
if (contextualize(&pc))
|
|
return NULL;
|
|
if (is_multitable())
|
|
return new (thd->mem_root) Sql_cmd_delete_multi;
|
|
else
|
|
return new (thd->mem_root) Sql_cmd_delete;
|
|
}
|
|
|
|
|
|
bool PT_update::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX *lex= pc->thd->lex;
|
|
lex->sql_command= SQLCOM_UPDATE;
|
|
lex->duplicates= DUP_ERROR;
|
|
|
|
lex->set_ignore(opt_ignore);
|
|
if (join_table_list->contextualize(pc))
|
|
return true;
|
|
pc->select->parsing_place= CTX_UPDATE_VALUE_LIST;
|
|
|
|
if (column_list->contextualize(pc) ||
|
|
value_list->contextualize(pc))
|
|
{
|
|
return true;
|
|
}
|
|
pc->select->item_list= column_list->value;
|
|
|
|
// Ensure we're resetting parsing context of the right select
|
|
assert(pc->select->parsing_place == CTX_UPDATE_VALUE_LIST);
|
|
pc->select->parsing_place= CTX_NONE;
|
|
if (lex->select_lex->table_list.elements > 1)
|
|
lex->sql_command= SQLCOM_UPDATE_MULTI;
|
|
else if (lex->select_lex->get_table_list()->is_derived())
|
|
{
|
|
/* it is single table update and it is update of derived table */
|
|
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
|
|
lex->select_lex->get_table_list()->alias, "UPDATE");
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
In case of multi-update setting write lock for all tables may
|
|
be too pessimistic. We will decrease lock level if possible in
|
|
mysql_multi_update().
|
|
*/
|
|
pc->select->set_lock_for_tables(opt_low_priority);
|
|
|
|
if (opt_where_clause != NULL &&
|
|
opt_where_clause->itemize(pc, &opt_where_clause))
|
|
{
|
|
return true;
|
|
}
|
|
pc->select->set_where_cond(opt_where_clause);
|
|
|
|
if (opt_order_clause != NULL && opt_order_clause->contextualize(pc))
|
|
return true;
|
|
|
|
assert(pc->select->select_limit == NULL);
|
|
if (opt_limit_clause != NULL)
|
|
{
|
|
if (opt_limit_clause->itemize(pc, &opt_limit_clause))
|
|
return true;
|
|
pc->select->select_limit= opt_limit_clause;
|
|
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
|
|
pc->select->explicit_limit= 1;
|
|
}
|
|
|
|
if (opt_hints != NULL && opt_hints->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
Sql_cmd *PT_update::make_cmd(THD *thd)
|
|
{
|
|
Parse_context pc(thd, thd->lex->current_select());
|
|
if (contextualize(&pc))
|
|
return NULL;
|
|
sql_cmd.update_value_list= value_list->value;
|
|
sql_cmd.sql_command= thd->lex->sql_command;
|
|
|
|
return &sql_cmd;
|
|
}
|
|
|
|
|
|
bool PT_create_select::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX *lex= pc->thd->lex;
|
|
if (lex->sql_command == SQLCOM_INSERT)
|
|
lex->sql_command= SQLCOM_INSERT_SELECT;
|
|
else if (lex->sql_command == SQLCOM_REPLACE)
|
|
lex->sql_command= SQLCOM_REPLACE_SELECT;
|
|
/*
|
|
The following work only with the local list, the global list
|
|
is created correctly in this case
|
|
*/
|
|
assert(pc->select == lex->current_select());
|
|
SQL_I_List<TABLE_LIST> save_list;
|
|
pc->select->table_list.save_and_clear(&save_list);
|
|
pc->select->parsing_place= CTX_SELECT_LIST;
|
|
|
|
if (options.query_spec_options & SELECT_HIGH_PRIORITY)
|
|
{
|
|
Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;
|
|
yyps->m_lock_type= TL_READ_HIGH_PRIORITY;
|
|
yyps->m_mdl_type= MDL_SHARED_READ;
|
|
}
|
|
if (options.save_to(pc))
|
|
return true;
|
|
|
|
if (item_list->contextualize(pc))
|
|
return true;
|
|
|
|
// Ensure we're resetting parsing context of the right select
|
|
assert(pc->select->parsing_place == CTX_SELECT_LIST);
|
|
pc->select->parsing_place= CTX_NONE;
|
|
|
|
if (table_expression->contextualize(pc))
|
|
return true;
|
|
/*
|
|
The following work only with the local list, the global list
|
|
is created correctly in this case
|
|
*/
|
|
pc->select->table_list.push_front(&save_list);
|
|
|
|
if (opt_hints != NULL && opt_hints->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_insert_values_list::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
List_iterator<List_item> it1(many_values);
|
|
List<Item> *item_list;
|
|
while ((item_list= it1++))
|
|
{
|
|
List_iterator<Item> it2(*item_list);
|
|
Item *item;
|
|
while ((item= it2++))
|
|
{
|
|
if (item->itemize(pc, &item))
|
|
return true;
|
|
it2.replace(item);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_insert_query_expression::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc) || create_select->contextualize(pc))
|
|
return true;
|
|
|
|
pc->select->set_braces(braces);
|
|
|
|
if (opt_union != NULL && opt_union->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PT_insert::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX *lex= pc->thd->lex;
|
|
if (is_replace)
|
|
{
|
|
lex->sql_command = SQLCOM_REPLACE;
|
|
lex->duplicates= DUP_REPLACE;
|
|
}
|
|
else
|
|
{
|
|
lex->sql_command= SQLCOM_INSERT;
|
|
lex->duplicates= DUP_ERROR;
|
|
lex->set_ignore(ignore);
|
|
}
|
|
|
|
Yacc_state *yyps= &pc->thd->m_parser_state->m_yacc;
|
|
if (!pc->select->add_table_to_list(pc->thd, table_ident, NULL,
|
|
TL_OPTION_UPDATING,
|
|
yyps->m_lock_type,
|
|
yyps->m_mdl_type,
|
|
NULL,
|
|
opt_use_partition))
|
|
{
|
|
return true;
|
|
}
|
|
pc->select->set_lock_for_tables(lock_option);
|
|
|
|
assert(lex->current_select() == lex->select_lex);
|
|
|
|
if (column_list->contextualize(pc))
|
|
return true;
|
|
|
|
if (has_select())
|
|
{
|
|
if (insert_query_expression->contextualize(pc))
|
|
return true;
|
|
lex->bulk_insert_row_cnt= 0;
|
|
}
|
|
else
|
|
{
|
|
if (row_value_list->contextualize(pc))
|
|
return true;
|
|
lex->bulk_insert_row_cnt= row_value_list->get_many_values().elements;
|
|
}
|
|
|
|
|
|
if (opt_on_duplicate_column_list != NULL)
|
|
{
|
|
assert(!is_replace);
|
|
assert(opt_on_duplicate_value_list != NULL &&
|
|
opt_on_duplicate_value_list->elements() ==
|
|
opt_on_duplicate_column_list->elements());
|
|
|
|
lex->duplicates= DUP_UPDATE;
|
|
TABLE_LIST *first_table= lex->select_lex->table_list.first;
|
|
/* Fix lock for ON DUPLICATE KEY UPDATE */
|
|
if (first_table->lock_type == TL_WRITE_CONCURRENT_DEFAULT)
|
|
first_table->lock_type= TL_WRITE_DEFAULT;
|
|
|
|
pc->select->parsing_place= CTX_UPDATE_VALUE_LIST;
|
|
|
|
if (opt_on_duplicate_column_list->contextualize(pc) ||
|
|
opt_on_duplicate_value_list->contextualize(pc))
|
|
return true;
|
|
|
|
// Ensure we're resetting parsing context of the right select
|
|
assert(pc->select->parsing_place == CTX_UPDATE_VALUE_LIST);
|
|
pc->select->parsing_place= CTX_NONE;
|
|
}
|
|
|
|
if (opt_hints != NULL && opt_hints->contextualize(pc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
Sql_cmd *PT_insert::make_cmd(THD *thd)
|
|
{
|
|
Parse_context pc(thd, thd->lex->current_select());
|
|
if (contextualize(&pc))
|
|
return NULL;
|
|
|
|
Sql_cmd_insert_base *sql_cmd;
|
|
if (has_select())
|
|
sql_cmd= new (thd->mem_root) Sql_cmd_insert_select(is_replace,
|
|
thd->lex->duplicates);
|
|
else
|
|
sql_cmd= new (thd->mem_root) Sql_cmd_insert(is_replace, thd->lex->duplicates);
|
|
if (sql_cmd == NULL)
|
|
return NULL;
|
|
|
|
if (!has_select())
|
|
sql_cmd->insert_many_values= row_value_list->get_many_values();
|
|
|
|
sql_cmd->insert_field_list= column_list->value;
|
|
if (opt_on_duplicate_column_list != NULL)
|
|
{
|
|
assert(!is_replace);
|
|
sql_cmd->insert_update_list= opt_on_duplicate_column_list->value;
|
|
sql_cmd->insert_value_list= opt_on_duplicate_value_list->value;
|
|
}
|
|
|
|
return sql_cmd;
|
|
}
|
|
|
|
|
|
/**
|
|
@brief
|
|
make_cmd for PT_alter_instance.
|
|
Contextualize parse tree node and return sql_cmd handle.
|
|
|
|
@params thd [in] Thread handle
|
|
|
|
@returns
|
|
sql_cmd Success
|
|
NULL Failure
|
|
*/
|
|
|
|
Sql_cmd *PT_alter_instance::make_cmd(THD *thd)
|
|
{
|
|
Parse_context pc(thd, thd->lex->current_select());
|
|
if (contextualize(&pc))
|
|
return NULL;
|
|
return &sql_cmd;
|
|
}
|
|
|
|
/**
|
|
@brief
|
|
Prepare parse tree node and set required information
|
|
|
|
@params pc [in] Parser context
|
|
|
|
@returns
|
|
false Success
|
|
true Error
|
|
*/
|
|
|
|
bool PT_alter_instance::contextualize(Parse_context *pc)
|
|
{
|
|
if (super::contextualize(pc))
|
|
return true;
|
|
|
|
LEX *lex= pc->thd->lex;
|
|
lex->no_write_to_binlog= false;
|
|
return false;
|
|
}
|