Skip to content

Commit 54ed393

Browse files
committed
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.
1 parent 96130b1 commit 54ed393

File tree

7 files changed

+124
-21
lines changed

7 files changed

+124
-21
lines changed

mysql-test/main/cte_nonrecursive.result

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2624,4 +2624,49 @@ a
26242624
1
26252625
1
26262626
DROP TABLE t1;
2627+
#
2628+
# MDEV-31657: CTE with the same name as base table used twice
2629+
# in another CTE
2630+
#
2631+
create table t (a int);
2632+
insert into t values (3), (7), (1);
2633+
with
2634+
t as (select * from t),
2635+
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
2636+
select * from cte;
2637+
t1a t2a
2638+
3 3
2639+
7 7
2640+
1 1
2641+
create table s (a int);
2642+
insert into s values (1), (4), (7);
2643+
with
2644+
t as (select * from t),
2645+
s as (select a-1 as a from s),
2646+
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
2647+
union
2648+
select t.a+1, s.a+1 from t, s where t.a=s.a+1)
2649+
select * from cte;
2650+
ta sa
2651+
3 3
2652+
2 1
2653+
8 7
2654+
with
2655+
t as (select * from t),
2656+
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
2657+
union
2658+
select t.a+1, s.a+1 from t, s where t.a=s.a),
2659+
s as (select a+10 as a from s)
2660+
select * from cte;
2661+
ta sa
2662+
1 1
2663+
7 7
2664+
2 2
2665+
8 8
2666+
drop table t,s;
2667+
with
2668+
t as (select * from t),
2669+
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
2670+
select * from cte;
2671+
ERROR 42S02: Table 'test.t' doesn't exist
26272672
# End of 10.4 tests

mysql-test/main/cte_nonrecursive.test

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,4 +1967,50 @@ SELECT * FROM t1;
19671967

19681968
DROP TABLE t1;
19691969

1970+
--echo #
1971+
--echo # MDEV-31657: CTE with the same name as base table used twice
1972+
--echo # in another CTE
1973+
--echo #
1974+
1975+
create table t (a int);
1976+
insert into t values (3), (7), (1);
1977+
1978+
let $q1=
1979+
with
1980+
t as (select * from t),
1981+
cte as (select t1.a as t1a, t2.a as t2a from t as t1, t as t2 where t1.a=t2.a)
1982+
select * from cte;
1983+
1984+
eval $q1;
1985+
1986+
create table s (a int);
1987+
insert into s values (1), (4), (7);
1988+
1989+
let $q2=
1990+
with
1991+
t as (select * from t),
1992+
s as (select a-1 as a from s),
1993+
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
1994+
union
1995+
select t.a+1, s.a+1 from t, s where t.a=s.a+1)
1996+
select * from cte;
1997+
1998+
eval $q2;
1999+
2000+
let $q3=
2001+
with
2002+
t as (select * from t),
2003+
cte as (select t.a as ta, s.a as sa from t, s where t.a=s.a
2004+
union
2005+
select t.a+1, s.a+1 from t, s where t.a=s.a),
2006+
s as (select a+10 as a from s)
2007+
select * from cte;
2008+
2009+
eval $q3;
2010+
2011+
drop table t,s;
2012+
2013+
--ERROR ER_NO_SUCH_TABLE
2014+
eval $q1;
2015+
19702016
--echo # End of 10.4 tests

sql/sql_acl.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8137,11 +8137,6 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
81378137
INSERT_ACL : SELECT_ACL);
81388138
}
81398139

8140-
if (tl->with || !tl->db.str ||
8141-
(tl->select_lex &&
8142-
(tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
8143-
continue;
8144-
81458140
const ACL_internal_table_access *access=
81468141
get_cached_table_access(&t_ref->grant.m_internal,
81478142
t_ref->get_db_name(),

sql/sql_cte.cc

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ bool LEX::check_dependencies_in_with_clauses()
105105
106106
@param tables Points to the beginning of the sub-chain
107107
@param tables_last Points to the address with the sub-chain barrier
108+
@param excl_spec Ignore the definition with this spec
108109
109110
@details
110111
The method resolves tables references to CTE from the chain of
@@ -146,7 +147,8 @@ bool LEX::check_dependencies_in_with_clauses()
146147
*/
147148

148149
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
149-
TABLE_LIST **tables_last)
150+
TABLE_LIST **tables_last,
151+
st_select_lex_unit *excl_spec)
150152
{
151153
With_element *with_elem= 0;
152154

@@ -155,7 +157,8 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
155157
if (tbl->derived)
156158
continue;
157159
if (!tbl->db.str && !tbl->with)
158-
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
160+
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
161+
excl_spec);
159162
if (!tbl->with) // no CTE matches table reference tbl
160163
{
161164
if (only_cte_resolution)
@@ -243,7 +246,7 @@ LEX::check_cte_dependencies_and_resolve_references()
243246
return true;
244247
if (!with_cte_resolution)
245248
return false;
246-
if (resolve_references_to_cte(query_tables, query_tables_last))
249+
if (resolve_references_to_cte(query_tables, query_tables_last, NULL))
247250
return true;
248251
return false;
249252
}
@@ -387,6 +390,7 @@ bool With_element::check_dependencies_in_spec()
387390
388391
@param table The reference to the table that is looked for
389392
@param barrier The barrier with element for the search
393+
@param excl_spec Ignore the definition with this spec
390394
391395
@details
392396
The function looks through the elements of this with clause trying to find
@@ -400,12 +404,15 @@ bool With_element::check_dependencies_in_spec()
400404
*/
401405

402406
With_element *With_clause::find_table_def(TABLE_LIST *table,
403-
With_element *barrier)
407+
With_element *barrier,
408+
st_select_lex_unit *excl_spec)
404409
{
405410
for (With_element *with_elem= with_list.first;
406411
with_elem != barrier;
407412
with_elem= with_elem->next)
408413
{
414+
if (excl_spec && with_elem->spec == excl_spec)
415+
continue;
409416
if (my_strcasecmp(system_charset_info, with_elem->get_name_str(),
410417
table->table_name.str) == 0 &&
411418
!table->is_fqtn)
@@ -465,7 +472,7 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
465472
top_unit->with_element &&
466473
top_unit->with_element->get_owner() == with_clause)
467474
barrier= top_unit->with_element;
468-
found= with_clause->find_table_def(tbl, barrier);
475+
found= with_clause->find_table_def(tbl, barrier, NULL);
469476
if (found)
470477
break;
471478
}
@@ -520,10 +527,11 @@ void With_element::check_dependencies_in_select(st_select_lex *sl,
520527
{
521528
With_clause *with_clause= sl->master_unit()->with_clause;
522529
if (with_clause)
523-
tbl->with= with_clause->find_table_def(tbl, NULL);
530+
tbl->with= with_clause->find_table_def(tbl, NULL, NULL);
524531
if (!tbl->with)
525532
tbl->with= owner->find_table_def(tbl,
526-
owner->with_recursive ? NULL : this);
533+
owner->with_recursive ? NULL : this,
534+
NULL);
527535
}
528536
if (!tbl->with)
529537
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,
10981106
*/
10991107
lex->only_cte_resolution= old_lex->only_cte_resolution;
11001108
if (lex->resolve_references_to_cte(lex->query_tables,
1101-
lex->query_tables_last))
1109+
lex->query_tables_last,
1110+
spec))
11021111
{
11031112
res= NULL;
11041113
goto err;
@@ -1264,6 +1273,7 @@ bool With_element::is_anchor(st_select_lex *sel)
12641273
Search for the definition of the given table referred in this select node
12651274
12661275
@param table reference to the table whose definition is searched for
1276+
@param excl_spec ignore the definition with this spec
12671277
12681278
@details
12691279
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)
12761286
NULL - otherwise
12771287
*/
12781288

1279-
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
1289+
With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table,
1290+
st_select_lex_unit *excl_spec)
12801291
{
12811292
With_element *found= NULL;
12821293
With_clause *containing_with_clause= NULL;
@@ -1293,7 +1304,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
12931304
With_clause *attached_with_clause= sl->get_with_clause();
12941305
if (attached_with_clause &&
12951306
attached_with_clause != containing_with_clause &&
1296-
(found= attached_with_clause->find_table_def(table, NULL)))
1307+
(found= attached_with_clause->find_table_def(table, NULL, excl_spec)))
12971308
break;
12981309
master_unit= sl->master_unit();
12991310
outer_sl= master_unit->outer_select();
@@ -1303,7 +1314,8 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table)
13031314
containing_with_clause= with_elem->get_owner();
13041315
With_element *barrier= containing_with_clause->with_recursive ?
13051316
NULL : with_elem;
1306-
if ((found= containing_with_clause->find_table_def(table, barrier)))
1317+
if ((found= containing_with_clause->find_table_def(table, barrier,
1318+
excl_spec)))
13071319
break;
13081320
if (outer_sl && !outer_sl->get_with_element())
13091321
break;

sql/sql_cte.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ class With_element : public Sql_alloc
322322

323323
friend
324324
bool LEX::resolve_references_to_cte(TABLE_LIST *tables,
325-
TABLE_LIST **tables_last);
325+
TABLE_LIST **tables_last,
326+
st_select_lex_unit *excl_spec);
326327
};
327328

328329
const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8;
@@ -422,7 +423,8 @@ class With_clause : public Sql_alloc
422423

423424
void move_anchors_ahead();
424425

425-
With_element *find_table_def(TABLE_LIST *table, With_element *barrier);
426+
With_element *find_table_def(TABLE_LIST *table, With_element *barrier,
427+
st_select_lex_unit *excl_spec);
426428

427429
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
428430

sql/sql_lex.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,8 @@ class st_select_lex: public st_select_lex_node
15311531
master_unit()->cloned_from->with_element :
15321532
master_unit()->with_element;
15331533
}
1534-
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
1534+
With_element *find_table_def_in_with_clauses(TABLE_LIST *table,
1535+
st_select_lex_unit * excl_spec);
15351536
bool check_unrestricted_recursive(bool only_standard_compliant);
15361537
bool check_subqueries_with_recursive_references();
15371538
void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
@@ -4708,7 +4709,8 @@ struct LEX: public Query_tables_list
47084709
bool check_dependencies_in_with_clauses();
47094710
bool check_cte_dependencies_and_resolve_references();
47104711
bool resolve_references_to_cte(TABLE_LIST *tables,
4711-
TABLE_LIST **tables_last);
4712+
TABLE_LIST **tables_last,
4713+
st_select_lex_unit *excl_spec);
47124714
};
47134715

47144716

sql/sql_view.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
295295
for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
296296
{
297297
if (!tbl->with && tbl->select_lex)
298-
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl);
298+
tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl,
299+
NULL);
299300
/*
300301
Ensure that we have some privileges on this table, more strict check
301302
will be done on column level after preparation,

0 commit comments

Comments
 (0)