1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-19956 Queries with subqueries containing UNION are not parsed

Shift-Reduce conflicts prevented parsing some queries with subqueries that
used set operations when the subqueries occurred in expressions or in IN
predicands.
The grammar rules for query expression were transformed in order to avoid
these conflicts. New grammar rules employ an idea taken from MySQL 8.0.
This commit is contained in:
Igor Babaev
2019-09-20 09:03:38 -07:00
parent e3da362c03
commit b44171428a
21 changed files with 7212 additions and 593 deletions

View File

@ -1444,7 +1444,7 @@ int Lex_input_stream::lex_token(YYSTYPE *yylval, THD *thd)
return LEFT_PAREN_LIKE;
if (token == WITH)
return LEFT_PAREN_WITH;
if (token != left_paren && token != SELECT_SYM)
if (token != left_paren && token != SELECT_SYM && token != VALUES)
return LEFT_PAREN_ALT;
else
return left_paren;
@ -5339,10 +5339,9 @@ LEX::create_unit(SELECT_LEX *first_sel)
SELECT_LEX_UNIT *unit;
DBUG_ENTER("LEX::create_unit");
if (first_sel->master_unit())
DBUG_RETURN(first_sel->master_unit());
unit = first_sel->master_unit();
if (!(unit= alloc_unit()))
if (!unit && !(unit= alloc_unit()))
DBUG_RETURN(NULL);
unit->register_select_chain(first_sel);
@ -9001,7 +9000,8 @@ bool LEX::insert_select_hack(SELECT_LEX *sel)
builtin_select.link_prev= NULL; // indicator of removal
}
set_main_unit(sel->master_unit());
if (set_main_unit(sel->master_unit()))
return true;
DBUG_ASSERT(builtin_select.table_list.elements == 1);
TABLE_LIST *insert_table= builtin_select.table_list.first;
@ -9045,9 +9045,10 @@ bool LEX::insert_select_hack(SELECT_LEX *sel)
}
/*
/**
Create an Item_singlerow_subselect for a query expression.
*/
Item *LEX::create_item_query_expression(THD *thd,
st_select_lex_unit *unit)
{
@ -9062,118 +9063,17 @@ Item *LEX::create_item_query_expression(THD *thd,
SELECT_LEX *curr_sel= select_stack_head();
DBUG_ASSERT(current_select == curr_sel);
if (!curr_sel)
{
curr_sel= &builtin_select;
curr_sel->register_unit(unit, &curr_sel->context);
curr_sel->add_statistics(unit);
curr_sel->register_unit(unit, &curr_sel->context);
curr_sel->add_statistics(unit);
}
return new (thd->mem_root)
Item_singlerow_subselect(thd, unit->first_select());
}
/**
Process unit parsed in brackets
*/
bool LEX::parsed_unit_in_brackets(SELECT_LEX_UNIT *unit)
{
SELECT_LEX *first_in_nest= unit->pre_last_parse->next_select()->first_nested;
if (first_in_nest->first_nested != first_in_nest)
{
/* There is a priority jump starting from first_in_nest */
if (create_priority_nest(first_in_nest) == NULL)
return true;
unit->fix_distinct();
}
push_select(unit->fake_select_lex);
return false;
}
/**
Process tail of unit parsed in brackets
*/
SELECT_LEX *LEX::parsed_unit_in_brackets_tail(SELECT_LEX_UNIT *unit,
Lex_order_limit_lock * l)
{
pop_select();
if (l)
{
(l)->set_to(unit->fake_select_lex);
}
return unit->first_select();
}
/**
Process select parsed in brackets
*/
SELECT_LEX *LEX::parsed_select(SELECT_LEX *sel, Lex_order_limit_lock * l)
{
pop_select();
if (l)
{
if (sel->next_select())
{
SELECT_LEX_UNIT *unit= sel->master_unit();
if (!unit)
unit= create_unit(sel);
if (!unit)
return NULL;
if (!unit->fake_select_lex->is_set_query_expr_tail)
l->set_to(unit->fake_select_lex);
else
{
if (!l->order_list && !unit->fake_select_lex->explicit_limit)
{
sel= unit->fake_select_lex;
l->order_list= &sel->order_list;
}
else
sel= wrap_unit_into_derived(unit);
if (!sel)
return NULL;
l->set_to(sel);
}
}
else if (!sel->is_set_query_expr_tail)
{
l->set_to(sel);
}
else
{
if (!l->order_list && !sel->explicit_limit)
l->order_list= &sel->order_list;
else
{
SELECT_LEX_UNIT *unit= create_unit(sel);
if (!unit)
return NULL;
sel= wrap_unit_into_derived(unit);
}
if (!sel)
return NULL;
l->set_to(sel);
}
}
return sel;
}
/**
Process select parsed in brackets
*/
SELECT_LEX *LEX::parsed_select_in_brackets(SELECT_LEX *sel,
Lex_order_limit_lock * l)
{
sel->braces= TRUE;
return parsed_select(sel, l);
}
SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2,
enum sub_select_type unit_type,
bool distinct)
@ -9204,6 +9104,7 @@ SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2,
if (res == NULL)
return NULL;
res->pre_last_parse= sel1;
push_select(res->fake_select_lex);
return res;
}
@ -9216,12 +9117,6 @@ SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit,
SELECT_LEX *sel1;
if (!s2->next_select())
sel1= s2;
else
{
sel1= wrap_unit_into_derived(s2->master_unit());
if (!sel1)
return NULL;
}
SELECT_LEX *last= unit->pre_last_parse->next_select();
int cmp= oracle? 0 : cmp_unit_op(unit_type, last->get_linkage());
@ -9253,41 +9148,73 @@ SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit,
return unit;
}
/**
Process parsed select in body
Add primary expression as the next term in a given query expression body
pruducing a new query expression body
*/
SELECT_LEX_UNIT *LEX::parsed_body_select(SELECT_LEX *sel,
Lex_order_limit_lock * l)
SELECT_LEX_UNIT *
LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
SELECT_LEX *sel,
enum sub_select_type unit_type,
bool distinct,
bool oracle)
{
if (sel->braces && l && l->lock.defined_lock)
SELECT_LEX *sel2= sel;
if (sel->master_unit() && sel->master_unit()->first_select()->next_select())
{
my_error(ER_WRONG_USAGE, MYF(0), "lock options",
"SELECT in brackets");
return NULL;
}
if (!(sel= parsed_select(sel, l)))
return NULL;
SELECT_LEX_UNIT *res= create_unit(sel);
if (res && sel->tvc && sel->order_list.elements)
{
if (res->add_fake_select_lex(thd))
sel2= wrap_unit_into_derived(sel->master_unit());
if (!sel2)
return NULL;
SELECT_LEX *fake= res->fake_select_lex;
fake->order_list= sel->order_list;
fake->explicit_limit= sel->explicit_limit;
fake->select_limit= sel->select_limit;
fake->offset_limit= sel->offset_limit;
}
return res;
SELECT_LEX *sel1= unit->first_select();
if (!sel1->next_select())
unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct);
else
unit= parsed_select_expr_cont(unit, sel2, unit_type, distinct, oracle);
return unit;
}
/**
Process parsed unit in body
Add query primary to a parenthesized query primary
pruducing a new query expression body
*/
bool LEX::parsed_body_unit(SELECT_LEX_UNIT *unit)
SELECT_LEX_UNIT *
LEX::add_primary_to_query_expression_body_ext_parens(
SELECT_LEX_UNIT *unit,
SELECT_LEX *sel,
enum sub_select_type unit_type,
bool distinct)
{
SELECT_LEX *sel1= unit->first_select();
if (unit->first_select()->next_select())
{
sel1= wrap_unit_into_derived(unit);
if (!sel1)
return NULL;
if (!create_unit(sel1))
return NULL;
}
SELECT_LEX *sel2= sel;
if (sel->master_unit() && sel->master_unit()->first_select()->next_select())
{
sel2= wrap_unit_into_derived(sel->master_unit());
if (!sel2)
return NULL;
}
unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct);
return unit;
}
/**
Process multi-operand query expression body
*/
bool LEX::parsed_multi_operand_query_expression_body(SELECT_LEX_UNIT *unit)
{
SELECT_LEX *first_in_nest=
unit->pre_last_parse->next_select()->first_nested;
@ -9298,27 +9225,60 @@ bool LEX::parsed_body_unit(SELECT_LEX_UNIT *unit)
return true;
unit->fix_distinct();
}
push_select(unit->fake_select_lex);
return false;
}
/**
Process parsed tail of unit in body
TODO: make processing for double tail case
/**
Add non-empty tail to a query expression body
*/
SELECT_LEX_UNIT *LEX::parsed_body_unit_tail(SELECT_LEX_UNIT *unit,
Lex_order_limit_lock * l)
SELECT_LEX_UNIT *LEX::add_tail_to_query_expression_body(SELECT_LEX_UNIT *unit,
Lex_order_limit_lock *l)
{
DBUG_ASSERT(l != NULL);
pop_select();
if (l)
{
(l)->set_to(unit->fake_select_lex);
}
SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex :
unit->first_select();
l->set_to(sel);
return unit;
}
/**
Add non-empty tail to a parenthesized query primary
*/
SELECT_LEX_UNIT *
LEX::add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit,
Lex_order_limit_lock *l)
{
SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex :
unit->first_select();
DBUG_ASSERT(l != NULL);
pop_select();
if (sel->is_set_query_expr_tail)
{
if (!l->order_list && !sel->explicit_limit)
l->order_list= &sel->order_list;
else
{
if (!unit)
return NULL;
sel= wrap_unit_into_derived(unit);
if (!sel)
return NULL;
if (!create_unit(sel))
return NULL;
}
}
l->set_to(sel);
return sel->master_unit();
}
/**
Process subselect parsing
*/
@ -9345,7 +9305,6 @@ SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit)
}
/**
Process INSERT-like select
*/
@ -9400,40 +9359,8 @@ SELECT_LEX *LEX::parsed_TVC_end()
}
TABLE_LIST *LEX::parsed_derived_select(SELECT_LEX *sel, int for_system_time,
LEX_CSTRING *alias)
{
TABLE_LIST *res;
derived_tables|= DERIVED_SUBQUERY;
sel->set_linkage(DERIVED_TABLE_TYPE);
sel->braces= FALSE;
// Add the subtree of subquery to the current SELECT_LEX
SELECT_LEX *curr_sel= select_stack_head();
DBUG_ASSERT(current_select == curr_sel);
SELECT_LEX_UNIT *unit= sel->master_unit();
if (!unit)
{
unit= create_unit(sel);
if (!unit)
return NULL;
}
curr_sel->register_unit(unit, &curr_sel->context);
curr_sel->add_statistics(unit);
Table_ident *ti= new (thd->mem_root) Table_ident(unit);
if (ti == NULL)
return NULL;
if (!(res= curr_sel->add_table_to_list(thd, ti, alias, 0,
TL_READ, MDL_SHARED_READ)))
return NULL;
if (for_system_time)
{
res->vers_conditions= vers_conditions;
}
return res;
}
TABLE_LIST *LEX::parsed_derived_unit(SELECT_LEX_UNIT *unit,
TABLE_LIST *LEX::parsed_derived_table(SELECT_LEX_UNIT *unit,
int for_system_time,
LEX_CSTRING *alias)
{
@ -9444,8 +9371,6 @@ TABLE_LIST *LEX::parsed_derived_unit(SELECT_LEX_UNIT *unit,
// Add the subtree of subquery to the current SELECT_LEX
SELECT_LEX *curr_sel= select_stack_head();
DBUG_ASSERT(current_select == curr_sel);
curr_sel->register_unit(unit, &curr_sel->context);
curr_sel->add_statistics(unit);
Table_ident *ti= new (thd->mem_root) Table_ident(unit);
if (ti == NULL)
@ -9463,7 +9388,8 @@ TABLE_LIST *LEX::parsed_derived_unit(SELECT_LEX_UNIT *unit,
bool LEX::parsed_create_view(SELECT_LEX_UNIT *unit, int check)
{
SQL_I_List<TABLE_LIST> *save= &first_select_lex()->table_list;
set_main_unit(unit);
if (set_main_unit(unit))
return true;
if (check_main_unit_semantics())
return true;
first_select_lex()->table_list.push_front(save);
@ -9486,7 +9412,8 @@ bool LEX::select_finalize(st_select_lex_unit *expr)
sql_command= SQLCOM_SELECT;
selects_allow_into= TRUE;
selects_allow_procedure= TRUE;
set_main_unit(expr);
if (set_main_unit(expr))
return true;
return check_main_unit_semantics();
}
@ -9497,6 +9424,7 @@ bool LEX::select_finalize(st_select_lex_unit *expr, Lex_select_lock l)
select_finalize(expr);
}
/*
"IN" and "EXISTS" subselect can appear in two statement types:
@ -9529,7 +9457,6 @@ void LEX::relink_hack(st_select_lex *select_lex)
}
bool SELECT_LEX_UNIT::set_lock_to_the_last_select(Lex_select_lock l)
{
if (l.defined_lock)