Commit 54ed3939 authored by Igor Babaev's avatar Igor Babaev

MDEV-31657 Crash on query using CTE with the same name as a base table

If a query contained a CTE whose name coincided with the name of one of
the base tables used in the specification of the CTE and the query had at
least two references to this CTE in the specifications of other CTEs then
processing of the query led to unlimited recursion that ultimately caused
a crash of the server.

Any secondary non-recursive reference to a CTE requires creation of a copy
of the CTE specification. All the references to CTEs in this copy must be
resolved. If the specification contains a reference to a base table whose
name coincides with the name of then CTE then it should be ensured that
this reference in no way can be resolved against the name of the CTE.
parent 96130b18
......@@ -2624,4 +2624,49 @@ a
1
1
DROP TABLE t1;
#
# MDEV-31657: CTE with the same name as base table used twice
# in another CTE
#
create table t (a int);
insert into t values (3), (7), (1);
with
t as (select * from t),
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
select * from cte;
t1a t2a
3 3
7 7
1 1
create table s (a int);
insert into s values (1), (4), (7);
with
t as (select * from t),
s as (select a-1 as a from s),
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
union
select t.a+1, s.a+1 from t, s where t.a=s.a+1)
select * from cte;
ta sa
3 3
2 1
8 7
with
t as (select * from t),
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
union
select t.a+1, s.a+1 from t, s where t.a=s.a),
s as (select a+10 as a from s)
select * from cte;
ta sa
1 1
7 7
2 2
8 8
drop table t,s;
with
t as (select * from t),
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
select * from cte;
ERROR 42S02: Table 'test.t' doesn't exist
# End of 10.4 tests
......@@ -1967,4 +1967,50 @@ SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-31657: CTE with the same name as base table used twice
--echo # in another CTE
--echo #
create table t (a int);
insert into t values (3), (7), (1);
let $q1=
with
t as (select * from t),
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
select * from cte;
eval $q1;
create table s (a int);
insert into s values (1), (4), (7);
let $q2=
with
t as (select * from t),
s as (select a-1 as a from s),
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
union
select t.a+1, s.a+1 from t, s where t.a=s.a+1)
select * from cte;
eval $q2;
let $q3=
with
t as (select * from t),
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
union
select t.a+1, s.a+1 from t, s where t.a=s.a),
s as (select a+10 as a from s)
select * from cte;
eval $q3;
drop table t,s;
--ERROR ER_NO_SUCH_TABLE
eval $q1;
--echo # End of 10.4 tests
......@@ -8137,11 +8137,6 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
INSERT_ACL : SELECT_ACL);
}
if (tl->with || !tl->db.str ||
(tl->select_lex &&
(tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
continue;
const ACL_internal_table_access *access=
get_cached_table_access(&t_ref->grant.m_internal,
t_ref->get_db_name(),
......
......@@ -105,6 +105,7 @@ bool LEX::check_dependencies_in_with_clauses()
@param tables Points to the beginning of the sub-chain
@param tables_last Points to the address with the sub-chain barrier
@param excl_spec Ignore the definition with this spec
@details
The method resolves tables references to CTE from the chain of
......@@ -146,7 +147,8 @@ bool LEX::check_dependencies_in_with_clauses()
*/
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
TABLE_LIST **tables_last)
TABLE_LIST **tables_last,
st_select_lex_unit *excl_spec)
{
With_element *with_elem= 0;
......@@ -155,7 +157,8 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
if (tbl->derived)
continue;
if (!tbl->db.str && !tbl->with)
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
excl_spec);
if (!tbl->with) // no CTE matches table reference tbl
{
if (only_cte_resolution)
......@@ -243,7 +246,7 @@ LEX::check_cte_dependencies_and_resolve_references()
return true;
if (!with_cte_resolution)
return false;
if (resolve_references_to_cte(query_tables, query_tables_last))
if (resolve_references_to_cte(query_tables, query_tables_last, NULL))
return true;
return false;
}
......@@ -387,6 +390,7 @@ bool With_element::check_dependencies_in_spec()
@param table The reference to the table that is looked for
@param barrier The barrier with element for the search
@param excl_spec Ignore the definition with this spec
@details
The function looks through the elements of this with clause trying to find
......@@ -400,12 +404,15 @@ bool With_element::check_dependencies_in_spec()
*/
With_element *With_clause::find_table_def(TABLE_LIST *table,
With_element *barrier)
With_element *barrier,
st_select_lex_unit *excl_spec)
{
for (With_element *with_elem= with_list.first;
with_elem != barrier;
with_elem= with_elem->next)
{
if (excl_spec && with_elem->spec == excl_spec)
continue;
if (my_strcasecmp(system_charset_info, with_elem->get_name_str(),
table->table_name.str) == 0 &&
!table->is_fqtn)
......@@ -465,7 +472,7 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
top_unit->with_element &&
top_unit->with_element->get_owner() == with_clause)
barrier= top_unit->with_element;
found= with_clause->find_table_def(tbl, barrier);
found= with_clause->find_table_def(tbl, barrier, NULL);
if (found)
break;
}
......@@ -520,10 +527,11 @@ void With_element::check_dependencies_in_select(st_select_lex *sl,
{
With_clause *with_clause= sl->master_unit()->with_clause;
if (with_clause)
tbl->with= with_clause->find_table_def(tbl, NULL);
tbl->with= with_clause->find_table_def(tbl, NULL, NULL);
if (!tbl->with)
tbl->with= owner->find_table_def(tbl,
owner->with_recursive ? NULL : this);
owner->with_recursive ? NULL : this,
NULL);
}
if (!tbl->with)
tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
......@@ -1098,7 +1106,8 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex,
*/
lex->only_cte_resolution= old_lex->only_cte_resolution;
if (lex->resolve_references_to_cte(lex->query_tables,
lex->query_tables_last))
lex->query_tables_last,
spec))
{
res= NULL;
goto err;
......@@ -1264,6 +1273,7 @@ bool With_element::is_anchor(st_select_lex *sel)
Search for the definition of the given table referred in this select node
@param table reference to the table whose definition is searched for
@param excl_spec ignore the definition with this spec
@details
The method looks for the definition of the table whose reference is occurred
......@@ -1276,7 +1286,8 @@ bool With_element::is_anchor(st_select_lex *sel)
NULL - otherwise
*/
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table,
st_select_lex_unit *excl_spec)
{
With_element *found= NULL;
With_clause *containing_with_clause= NULL;
......@@ -1293,7 +1304,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
With_clause *attached_with_clause= sl->get_with_clause();
if (attached_with_clause &&
attached_with_clause != containing_with_clause &&
(found= attached_with_clause->find_table_def(table, NULL)))
(found= attached_with_clause->find_table_def(table, NULL, excl_spec)))
break;
master_unit= sl->master_unit();
outer_sl= master_unit->outer_select();
......@@ -1303,7 +1314,8 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
containing_with_clause= with_elem->get_owner();
With_element *barrier= containing_with_clause->with_recursive ?
NULL : with_elem;
if ((found= containing_with_clause->find_table_def(table, barrier)))
if ((found= containing_with_clause->find_table_def(table, barrier,
excl_spec)))
break;
if (outer_sl && !outer_sl->get_with_element())
break;
......
......@@ -322,7 +322,8 @@ class With_element : public Sql_alloc
friend
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
TABLE_LIST **tables_last);
TABLE_LIST **tables_last,
st_select_lex_unit *excl_spec);
};
const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8;
......@@ -422,7 +423,8 @@ class With_clause : public Sql_alloc
void move_anchors_ahead();
With_element *find_table_def(TABLE_LIST *table, With_element *barrier);
With_element *find_table_def(TABLE_LIST *table, With_element *barrier,
st_select_lex_unit *excl_spec);
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
......
......@@ -1531,7 +1531,8 @@ class st_select_lex: public st_select_lex_node
master_unit()->cloned_from->with_element :
master_unit()->with_element;
}
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
With_element *find_table_def_in_with_clauses(TABLE_LIST *table,
st_select_lex_unit * excl_spec);
bool check_unrestricted_recursive(bool only_standard_compliant);
bool check_subqueries_with_recursive_references();
void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
......@@ -4708,7 +4709,8 @@ struct LEX: public Query_tables_list
bool check_dependencies_in_with_clauses();
bool check_cte_dependencies_and_resolve_references();
bool resolve_references_to_cte(TABLE_LIST *tables,
TABLE_LIST **tables_last);
TABLE_LIST **tables_last,
st_select_lex_unit *excl_spec);
};
......
......@@ -295,7 +295,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
{
if (!tbl->with && tbl->select_lex)
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
NULL);
/*
Ensure that we have some privileges on this table, more strict check
will be done on column level after preparation,
......
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