From 9fde2bbacf79557aa7f0a959a77aa4f6226dc8d9 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 23 Jul 2021 06:42:50 -0700 Subject: [PATCH] MDEV-25484 Crash when parsing query using derived table containing TVC This patch fixes parsing problems concerning derived tables that use table value constructors (TVC) with LIMIT and ORDER BY clauses of the form ((VALUES ... LIMIT ...) ORDER BY ...) as dt The fix has to be applied only to 10.3 as 10.4 that employs a different grammar rules has no such problems. The test cases should be merged upstream. Approved by Oleksandr Byelkin --- mysql-test/main/table_value_constr.result | 38 +++++++++++++++++++++++ mysql-test/main/table_value_constr.test | 22 +++++++++++++ sql/sql_lex.cc | 9 +++++- sql/sql_yacc.yy | 12 ++++--- 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result index ff6d19a8e0e..1d1cd054556 100644 --- a/mysql-test/main/table_value_constr.result +++ b/mysql-test/main/table_value_constr.result @@ -3062,4 +3062,42 @@ a 2 3 drop table t1; +# +# MDEV-25484: Derived table using TVC with LIMIT and ORDER BY +# +create table t1 (a int); +insert into t1 values (3), (7), (1); +select * from ( (select * from t1 limit 2) order by 1 desc) as dt; +a +7 +3 +(values (3), (7), (1) limit 2) order by 1 desc; +3 +7 +3 +select * from ( (values (3), (7), (1) limit 2) order by 1 desc) as dt; +3 +7 +3 +select * from ( select * from t1 order by 1 limit 2 ) as dt; +a +1 +3 +values (3),(7),(1) order by 1 limit 2; +3 +1 +3 +select * from ( values (3),(7),(1) order by 1 limit 2 ) as dt; +3 +1 +3 +values (3),(7),(1) union values (2),(4) order by 1 limit 2; +3 +1 +2 +select * from (values (3),(7),(1) union values (2),(4) order by 1 limit 2) as dt; +3 +1 +2 +drop table t1; End of 10.3 tests diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test index 3e976f88ed5..d13962579cc 100644 --- a/mysql-test/main/table_value_constr.test +++ b/mysql-test/main/table_value_constr.test @@ -1628,4 +1628,26 @@ select * from t1; drop table t1; + +--echo # +--echo # MDEV-25484: Derived table using TVC with LIMIT and ORDER BY +--echo # + +create table t1 (a int); +insert into t1 values (3), (7), (1); + +select * from ( (select * from t1 limit 2) order by 1 desc) as dt; +(values (3), (7), (1) limit 2) order by 1 desc; +select * from ( (values (3), (7), (1) limit 2) order by 1 desc) as dt; + + +select * from ( select * from t1 order by 1 limit 2 ) as dt; +values (3),(7),(1) order by 1 limit 2; +select * from ( values (3),(7),(1) order by 1 limit 2 ) as dt; + +values (3),(7),(1) union values (2),(4) order by 1 limit 2; +select * from (values (3),(7),(1) union values (2),(4) order by 1 limit 2) as dt; + +drop table t1; + --echo End of 10.3 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2a337b0f842..8de645a7197 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -8396,8 +8396,15 @@ bool LEX::tvc_finalize_derived() thd->parse_error(); return true; } + if (unlikely(!(current_select->tvc= + new (thd->mem_root) + table_value_constr(many_values, + current_select, + current_select->options)))) + return true; + restore_values_list_state(); current_select->linkage= DERIVED_TABLE_TYPE; - return tvc_finalize(); + return false; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b915667a239..7d13dd8b0c8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -12855,10 +12855,13 @@ order_clause: created yet. */ SELECT_LEX *first_sl= unit->first_select(); - if (unlikely(!unit->is_unit_op() && - (first_sl->order_list.elements || - first_sl->select_limit) && + if (unlikely(!first_sl->next_select() && first_sl->tvc && unit->add_fake_select_lex(thd))) + MYSQL_YYABORT; + else if (unlikely(!unit->is_unit_op() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(thd))) MYSQL_YYABORT; } if (sel->master_unit()->is_unit_op() && !sel->braces) @@ -12907,7 +12910,8 @@ limit_clause_init: LIMIT { SELECT_LEX *sel= Select; - if (sel->master_unit()->is_unit_op() && !sel->braces) + if (sel->master_unit()->is_unit_op() && !sel->braces && + sel->master_unit()->fake_select_lex) { /* Move LIMIT that belongs to UNION to fake_select_lex */ Lex->current_select= sel->master_unit()->fake_select_lex;