04 Mar '21
revision-id: 027dcbe0b48eca2e59e2ef137f8f031083546f96 (mariadb-10.3.26-100-g027dcbe)
parent(s): 0f81ca6a0bb21fbba4bca93a7555f7c8e6357b47
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-03 22:48:39 -0800
message:
MDEV-22786 Crashes with nested table value constructors
The bug caused crashes of the server when processing queries with nested
table value constructors (TVC) . It happened because the grammar rules to
parse TVC used the same global lists for both nested TVC and nesting TVC.
As a result invalid select trees were constructed for queries with nested
TVC and this led to crashes at the prepare stage.
This patch provides its own lists structures for each TVC nest level.
Besides the patch fixes a bug in the function wrap_tvc() that missed
inheritance of the SELECT_LEX::exclude_from_table_unique_test for
selects that wrapped TVCs. This inheritance is critical for specifications
of derived tables that employ nested TVCs.
Approved by dmitry.shulga(a)mariadb.com
---
mysql-test/main/table_value_constr.result | 175 ++++++++++++++++++++++++++++++
mysql-test/main/table_value_constr.test | 106 ++++++++++++++++++
sql/sql_lex.cc | 45 +++++++-
sql/sql_lex.h | 12 +-
sql/sql_tvc.cc | 2 +
sql/sql_yacc.yy | 3 +-
sql/sql_yacc_ora.yy | 3 +-
7 files changed, 333 insertions(+), 13 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index d2965ab..ff6d19a 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2887,4 +2887,179 @@ drop table t1,t2,t3;
select sum((values(1)));
sum((values(1)))
1
+#
+# MDEV-22786: Nested table values constructors
+#
+values ((values (2)));
+(values (2))
+2
+values ((values (2)), (5), (select 4));
+(values (2)) 5 (select 4)
+2 5 4
+values ((7), (values (2)), (5), (select 4));
+7 (values (2)) 5 (select 4)
+7 2 5 4
+values ((values (2))) union values ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((select 4)), ((values (3)));
+(values (2))
+2
+4
+3
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+(values (4)) (values (5))
+4 5
+1 7
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+(values (4)) (select 5)
+4 5
+1 7
+values ((select 2)) union values ((values (3)));
+(select 2)
+2
+3
+values ((values (2))) union values((select 3));
+(values (2))
+2
+3
+values ((values (2))) union all values ((values (2)));
+(values (2))
+2
+2
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+2 8
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+4 5
+2 8
+values ((values (1) union values (1)));
+(values (1) union values (1))
+1
+values ((values (1) union values (1) union values (1)));
+(values (1) union values (1) union values (1))
+1
+values ((values ((values (4)))));
+(values ((values (4))))
+4
+values ((values ((select 5))));
+(values ((select 5)))
+5
+values ((select (values (4))), (values ((values(5)))));
+(select (values (4))) (values ((values(5))))
+4 5
+values ((select (values (4))), (values ((select 5))));
+(select (values (4))) (values ((select 5)))
+4 5
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+(select (values (4))) (values ((values(5))))
+4 5
+4 7
+values ((values (2))), ((values ((values (4)))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((select 4))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+4
+select * from (values ((values (2)))) dt;
+(values (2))
+2
+select * from (values ((values (2)), (5), (select 4))) dt;
+(values (2)) 5 (select 4)
+2 5 4
+select * from (values ((values (2))) union values ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+(values (2))
+2
+4
+3
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+values ((values ((select a from t1 where a=7))));
+(values ((select a from t1 where a=7)))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select a from t1 where a=7)))
+7
+NULL
+values ((values ((select a from t1 where a in ((values (7)))))));
+(values ((select a from t1 where a in ((values (7))))))
+7
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+(values ((select a from t1 where a in ((values (7), (8))))))
+7
+values ((values
+((select a from t1 where a in (values (7) union values (8))))));
+(values
+((select a from t1 where a in (values (7) union values (8)))))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((select (values(2)) from t1 where a<7));
+ERROR 21000: Subquery returns more than 1 row
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+(values ((select a from t1 where a=7)))
+7
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+a
+3
+7
+1
+2
+3
+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 88d0ac2..3e976f8 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1522,4 +1522,110 @@ drop table t1,t2,t3;
select sum((values(1)));
+--echo #
+--echo # MDEV-22786: Nested table values constructors
+--echo #
+
+values ((values (2)));
+
+values ((values (2)), (5), (select 4));
+
+values ((7), (values (2)), (5), (select 4));
+
+values ((values (2))) union values ((values (3)));
+
+values ((values (2))), ((values (3)));
+
+values ((values (2))), ((select 4)), ((values (3)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+
+values ((select 2)) union values ((values (3)));
+
+values ((values (2))) union values((select 3));
+
+values ((values (2))) union all values ((values (2)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (1) union values (1)));
+
+values ((values (1) union values (1) union values (1)));
+
+values ((values ((values (4)))));
+
+values ((values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))));
+
+values ((select (values (4))), (values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+
+values ((values (2))), ((values ((values (4)))));
+
+values ((values (2))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+
+select * from (values ((values (2)))) dt;
+
+select * from (values ((values (2)), (5), (select 4))) dt;
+
+select * from (values ((values (2))) union values ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+values ((values ((select a from t1 where a=7))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a in ((values (7)))))));
+
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+
+values ((values
+ ((select a from t1 where a in (values (7) union values (8))))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+--error ER_SUBQUERY_NO_1_ROW
+values ((select (values(2)) from t1 where a<7));
+
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+
+drop table t1;
+
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 70d795c..c2bc838 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2421,6 +2421,8 @@ void st_select_lex::init_select()
with_dep= 0;
join= 0;
lock_type= TL_READ_DEFAULT;
+ save_many_values.empty();
+ save_insert_list= 0;
tvc= 0;
in_funcs.empty();
curr_tvc_name= 0;
@@ -8302,16 +8304,52 @@ bool LEX::last_field_generated_always_as_row_end()
}
+void LEX::save_values_list_state()
+{
+ current_select->save_many_values= many_values;
+ current_select->save_insert_list= insert_list;
+}
+
+
+void LEX::restore_values_list_state()
+{
+ many_values= current_select->save_many_values;
+ insert_list= current_select->save_insert_list;
+}
+
+
+void LEX::tvc_start()
+{
+ if (current_select == &select_lex)
+ mysql_init_select(this);
+ else
+ save_values_list_state();
+ many_values.empty();
+ insert_list= 0;
+}
+
+
+bool LEX::tvc_start_derived()
+{
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(this, 1, NULL)))
+ return true;
+ save_values_list_state();
+ many_values.empty();
+ insert_list= 0;
+ return false;
+}
+
+
bool LEX::tvc_finalize()
{
- mysql_init_select(this);
if (unlikely(!(current_select->tvc=
new (thd->mem_root)
table_value_constr(many_values,
current_select,
current_select->options))))
return true;
- many_values.empty();
+ restore_values_list_state();
if (!current_select->master_unit()->fake_select_lex)
current_select->master_unit()->add_fake_select_lex(thd);
return false;
@@ -8326,9 +8364,6 @@ bool LEX::tvc_finalize_derived()
thd->parse_error();
return true;
}
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(this, 1, NULL)))
- return true;
current_select->linkage= DERIVED_TABLE_TYPE;
return tvc_finalize();
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 979e212..474f317 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1176,6 +1176,8 @@ class st_select_lex: public st_select_lex_node
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
+ List<List_item> save_many_values;
+ List<Item> *save_insert_list;
table_value_constr *tvc;
bool in_tvc;
@@ -4046,12 +4048,10 @@ struct LEX: public Query_tables_list
return false;
}
- void tvc_start()
- {
- field_list.empty();
- many_values.empty();
- insert_list= 0;
- }
+ void save_values_list_state();
+ void restore_values_list_state();
+ void tvc_start();
+ bool tvc_start_derived();
bool tvc_finalize();
bool tvc_finalize_derived();
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index cb056b0..96c5223 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -673,6 +673,8 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->nest_level= tvc_sl->nest_level;
wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->exclude_from_table_unique_test=
+ tvc_sl->exclude_from_table_unique_test;
lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 88f12e9..b26e2dd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -12334,7 +12334,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index eaeaf2e..4af034d 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -12272,7 +12272,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
1
0
[Commits] ed3a627: MDEV-21104 Wrong result (extra rows and wrong values) with incremental BNLH
by IgorBabaev 04 Mar '21
by IgorBabaev 04 Mar '21
04 Mar '21
revision-id: ed3a627f612e08b99a2f2d7a0ad91966b11fc504 (mariadb-10.2.31-769-ged3a627)
parent(s): 259e5243faa88370bbb890342326a324fb648f7d
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-03 19:33:04 -0800
message:
MDEV-21104 Wrong result (extra rows and wrong values) with incremental BNLH
This bug could affect multi-way join queries with embedded outer joins that
contained a conjunctive IS NULL predicate over a non-nullable column from
inner table of an outer join. The predicate could occur in WHERE condition
or in ON condition. Due to this bug a wrong result set could be returned by
the query. The bug manifested itself only when join buffers were employed
for join operations.
The problem appeared because
- a bug in the function JOIN_CACHE::get_match_flag_by_pos that not always
returned proper match flags for embedding outer joins stored together
with table rows put a join buffer.
- bug in the function JOIN_CACHE::join_matching_records that not always
correctly determined that a row from the buffer could be skipped due
to applied 'not_exists' optimization.
The patch introduces a new function that finds the match flag for a record
from join buffer specifying the the buffer where this flag has to be found.
The function is called JOIN_CACHE::get_match_flag_by_pos_from_join_buffer().
Now this function rather than JOIN_CACHE::get_match_flag_by_pos() is used
in JOIN_CACHE::skip_if_matched() to check whether a record from the join
buffer must be ignored when extending the record by null complements.
Also the code of the function JOIN_CACHE::skip_if_not_needed_match() has
been changed. The function checks whether a record from the join buffer
still may produce some useful extensions.
Also some clarifying comments has been added.
Approved by monty(a)mariadb.com.
---
mysql-test/r/join_cache.result | 53 +++++++++++++++++++++++
mysql-test/t/join_cache.test | 36 ++++++++++++++++
sql/sql_join_cache.cc | 95 +++++++++++++++++++++++++++++++++++++-----
sql/sql_join_cache.h | 11 ++++-
4 files changed, 184 insertions(+), 11 deletions(-)
diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result
index e41c79a..87c4079 100644
--- a/mysql-test/r/join_cache.result
+++ b/mysql-test/r/join_cache.result
@@ -6054,4 +6054,57 @@ select f2 from t2,t1 where f2 = 0;
f2
drop table t1, t2;
set join_buffer_size=@save_join_buffer_size;
+#
+# MDEV-21104: BNLH used for multi-join query with embedded outer join
+# and possible 'not exists' optimization
+#
+set join_cache_level=4;
+CREATE TABLE t1 (a int) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (b int, c int) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (1,2),(2,4);
+CREATE TABLE t3 (d int, KEY(d)) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (1),(2);
+CREATE TABLE t4 (e int primary key) ENGINE=MyISAM;
+INSERT INTO t4 VALUES (1),(2);
+ANALYZE TABLE t1,t2,t3,t4;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+test.t3 analyze status OK
+test.t4 analyze status OK
+SELECT * FROM t2 LEFT JOIN t3 ON c = d;
+b c d
+1 2 2
+2 4 NULL
+SELECT * FROM (t2 LEFT JOIN t3 ON c = d ) JOIN t4;
+b c d e
+1 2 2 1
+2 4 NULL 1
+1 2 2 2
+2 4 NULL 2
+EXPLAIN SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2
+1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 hash_index d #hash#d:d 5:5 test.t2.c 2 Using where; Using index; Using join buffer (incremental, BNLH join)
+1 SIMPLE t4 hash_index PRIMARY #hash#PRIMARY:PRIMARY 4:4 test.t2.b 2 Using index; Using join buffer (incremental, BNLH join)
+SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e;
+a b c d e
+1 1 2 2 1
+2 1 2 2 1
+1 2 4 NULL 2
+2 2 4 NULL 2
+EXPLAIN SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e
+WHERE e IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2
+1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 hash_index d #hash#d:d 5:5 test.t2.c 2 Using where; Using index; Using join buffer (incremental, BNLH join)
+1 SIMPLE t4 hash_index PRIMARY #hash#PRIMARY:PRIMARY 4:4 test.t2.b 2 Using where; Using index; Not exists; Using join buffer (incremental, BNLH join)
+SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e
+WHERE e IS NULL;
+a b c d e
+DROP TABLE t1,t2,t3,t4;
+set join_cache_level=@save_join_cache_level;
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test
index 9576d59..15cd1e9 100644
--- a/mysql-test/t/join_cache.test
+++ b/mysql-test/t/join_cache.test
@@ -4014,5 +4014,41 @@ select f2 from t2,t1 where f2 = 0;
drop table t1, t2;
set join_buffer_size=@save_join_buffer_size;
+
+--echo #
+--echo # MDEV-21104: BNLH used for multi-join query with embedded outer join
+--echo # and possible 'not exists' optimization
+--echo #
+
+set join_cache_level=4;
+
+CREATE TABLE t1 (a int) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (b int, c int) ENGINE=MyISAM;
+INSERT INTO t2 VALUES (1,2),(2,4);
+CREATE TABLE t3 (d int, KEY(d)) ENGINE=MyISAM;
+INSERT INTO t3 VALUES (1),(2);
+CREATE TABLE t4 (e int primary key) ENGINE=MyISAM;
+INSERT INTO t4 VALUES (1),(2);
+ANALYZE TABLE t1,t2,t3,t4;
+
+SELECT * FROM t2 LEFT JOIN t3 ON c = d;
+SELECT * FROM (t2 LEFT JOIN t3 ON c = d ) JOIN t4;
+
+let $q1=
+SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e;
+eval EXPLAIN $q1;
+eval $q1;
+
+let $q2=
+SELECT * FROM t1 LEFT JOIN ( ( t2 LEFT JOIN t3 ON c = d ) JOIN t4 ) ON b = e
+ WHERE e IS NULL;
+eval EXPLAIN $q2;
+eval $q2;
+
+DROP TABLE t1,t2,t3,t4;
+
+set join_cache_level=@save_join_cache_level;
+
# The following command must be the last one in the file
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 1dfc938..6437740 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -1649,7 +1649,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr)
}
-/*
+/*
Get the match flag from the referenced record: the default implementation
SYNOPSIS
@@ -1661,6 +1661,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr)
get the match flag for the record pointed by the reference at the position
rec_ptr. If the match flag is placed in one of the previous buffers the
function first reaches the linked record fields in this buffer.
+ The function returns the value of the first encountered match flag.
RETURN VALUE
match flag for the record at the position rec_ptr
@@ -1685,6 +1686,46 @@ enum JOIN_CACHE::Match_flag JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr)
/*
+ Get the match flag for the referenced record from specified join buffer
+
+ SYNOPSIS
+ get_match_flag_by_pos_from_join_buffer()
+ rec_ptr position of the first field of the record in the join buffer
+ tab join table with join buffer where to look for the match flag
+
+ DESCRIPTION
+ This default implementation of the get_match_flag_by_pos_from_join_buffer
+ method gets the match flag for the record pointed by the reference at the
+ position rec_ptr from the join buffer attached to the join table tab.
+
+ RETURN VALUE
+ match flag for the record at the position rec_ptr from the join
+ buffer attached to the table tab.
+*/
+
+enum JOIN_CACHE::Match_flag
+JOIN_CACHE::get_match_flag_by_pos_from_join_buffer(uchar *rec_ptr,
+ JOIN_TAB *tab)
+{
+ DBUG_ASSERT(tab->cache && tab->cache->with_match_flag);
+ Match_flag match_fl= MATCH_NOT_FOUND;
+ JOIN_CACHE *prev_cache= 0;
+ for (JOIN_CACHE *cache= this; cache; cache= prev_cache)
+ {
+ if (cache->join_tab == tab)
+ {
+ match_fl= (enum Match_flag) rec_ptr[0];
+ return match_fl;
+ }
+ if ((prev_cache= cache->prev_cache))
+ rec_ptr= prev_cache->get_rec_ref(rec_ptr);
+ }
+ DBUG_ASSERT(0);
+ return match_fl;
+}
+
+
+/*
Calculate the increment of the auxiliary buffer for a record write
SYNOPSIS
@@ -1954,6 +1995,10 @@ bool JOIN_CACHE::read_referenced_field(CACHE_FIELD *copy,
If the record is skipped the value of 'pos' is set to point to the position
right after the record.
+ NOTE
+ Currently this function is called only when generating null complemented
+ records for outer joins (=> only when join_tab->first_unmatched != NULL).
+
RETURN VALUE
TRUE the match flag is set to MATCH_FOUND and the record has been skipped
FALSE otherwise
@@ -1966,7 +2011,9 @@ bool JOIN_CACHE::skip_if_matched()
if (prev_cache)
offset+= prev_cache->get_size_of_rec_offset();
/* Check whether the match flag is MATCH_FOUND */
- if (get_match_flag_by_pos(pos+offset) == MATCH_FOUND)
+ if (get_match_flag_by_pos_from_join_buffer(pos+offset,
+ join_tab->first_unmatched) ==
+ MATCH_FOUND)
{
pos+= size_of_rec_len + get_rec_length(pos);
return TRUE;
@@ -1983,13 +2030,23 @@ bool JOIN_CACHE::skip_if_matched()
DESCRIPTION
This default implementation of the virtual function skip_if_not_needed_match
- skips the next record from the join buffer if its match flag is not
- MATCH_NOT_FOUND, and, either its value is MATCH_FOUND and join_tab is the
- first inner table of an inner join, or, its value is MATCH_IMPOSSIBLE
- and join_tab is the first inner table of an outer join.
+ skips the next record from the join when generating join extensions
+ for the records in the join buffer depending on the value of the match flag.
+ - In the case of a semi-nest the match flag may be in two states
+ {MATCH_NOT_FOUND, MATCH_FOUND}. The record is skipped if the flag is set
+ to MATCH_FOUND.
+ - In the case of a outer join nest when not_exists optimization is applied
+ the match may be in three states {MATCH_NOT_FOUND, MATCH_IMPOSSIBLE,
+ MATCH_FOUND. The record is skipped if the flag is set to MATCH_FOUND or
+ to MATCH_IMPOSSIBLE.
+
If the record is skipped the value of 'pos' is set to point to the position
right after the record.
+ NOTE
+ Currently the function is called only when generating non-null complemented
+ extensions for records in the join buffer.
+
RETURN VALUE
TRUE the record has to be skipped
FALSE otherwise
@@ -2000,11 +2057,19 @@ bool JOIN_CACHE::skip_if_not_needed_match()
DBUG_ASSERT(with_length);
enum Match_flag match_fl;
uint offset= size_of_rec_len;
+ bool skip= FALSE;
if (prev_cache)
offset+= prev_cache->get_size_of_rec_offset();
- if ((match_fl= get_match_flag_by_pos(pos+offset)) != MATCH_NOT_FOUND &&
- (join_tab->check_only_first_match() == (match_fl == MATCH_FOUND)) )
+ if (!join_tab->check_only_first_match())
+ return FALSE;
+
+ match_fl= get_match_flag_by_pos(pos+offset);
+ skip= join_tab->first_sj_inner_tab ?
+ match_fl == MATCH_FOUND : // the case of semi-join
+ match_fl != MATCH_NOT_FOUND; // the case of outer-join
+
+ if (skip)
{
pos+= size_of_rec_len + get_rec_length(pos);
return TRUE;
@@ -2104,7 +2169,14 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last)
goto finish;
}
join_tab->not_null_compl= FALSE;
- /* Prepare for generation of null complementing extensions */
+ /*
+ Prepare for generation of null complementing extensions.
+ For all inner tables of the outer join operation for which
+ regular matches have been just found the field first_unmatched
+ is set to point the the first inner table. After all null
+ complement rows are generated for this outer join this field
+ is set back to NULL.
+ */
for (tab= join_tab->first_inner; tab <= join_tab->last_inner; tab++)
tab->first_unmatched= join_tab->first_inner;
}
@@ -2221,7 +2293,10 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
int error;
enum_nested_loop_state rc= NESTED_LOOP_OK;
join_tab->table->null_row= 0;
- bool check_only_first_match= join_tab->check_only_first_match();
+ bool check_only_first_match=
+ join_tab->check_only_first_match() &&
+ (!join_tab->first_inner || // semi-join case
+ join_tab->first_inner == join_tab->first_unmatched); // outer join case
bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join();
DBUG_ENTER("JOIN_CACHE::join_matching_records");
diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h
index 1cbc6ac..4eaa66b 100644
--- a/sql/sql_join_cache.h
+++ b/sql/sql_join_cache.h
@@ -206,7 +206,9 @@ class JOIN_CACHE :public Sql_alloc
/*
This flag indicates that records written into the join buffer contain
- a match flag field. The flag must be set by the init method.
+ a match flag field. The flag must be set by the init method.
+ Currently any implementation of the virtial init method calls
+ the function JOIN_CACHE::calc_record_fields() to set this flag.
*/
bool with_match_flag;
/*
@@ -646,6 +648,13 @@ class JOIN_CACHE :public Sql_alloc
/* Shall return the value of the match flag for the positioned record */
virtual enum Match_flag get_match_flag_by_pos(uchar *rec_ptr);
+ /*
+ Shall return the value of the match flag for the positioned record
+ from the join buffer attached to the specified table.
+ */
+ virtual enum Match_flag
+ get_match_flag_by_pos_from_join_buffer(uchar *rec_ptr, JOIN_TAB *tab);
+
/* Shall return the position of the current record */
virtual uchar *get_curr_rec() { return curr_rec_pos; }
1
0
04 Mar '21
revision-id: e51dfd2521afd083404331959ef75784a614ddcb (mariadb-10.3.26-100-ge51dfd2)
parent(s): 0f81ca6a0bb21fbba4bca93a7555f7c8e6357b47
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-03 15:00:38 -0800
message:
MDEV-22786 Crashes with nested table value constructors
The bug caused crashes of the server when processing queries with nested
table value constructors (TVC) . It happened because the grammar rules to
parse TVC used the same global lists for both nested TVC and nesting TVC.
As a result invalid select trees were constructed for queries with nested
TVC and this led to crashes at the prepare stage.
This patch provides its own lists structures for each TVC nest level.
Besides the patch fixes a bug in the function wrap_tvc() that missed
inheritance of the SELECT_LEX::exclude_from_table_unique_test for
selects that wrapped TVCs. This inheritance is critical for specifications
of derived tables that employ nested TVCs.
---
mysql-test/main/table_value_constr.result | 175 ++++++++++++++++++++++++++++++
mysql-test/main/table_value_constr.test | 106 ++++++++++++++++++
sql/sql_lex.cc | 45 +++++++-
sql/sql_lex.h | 12 +-
sql/sql_tvc.cc | 2 +
sql/sql_yacc.yy | 3 +-
sql/sql_yacc_ora.yy | 3 +-
7 files changed, 333 insertions(+), 13 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index d2965ab..ff6d19a 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2887,4 +2887,179 @@ drop table t1,t2,t3;
select sum((values(1)));
sum((values(1)))
1
+#
+# MDEV-22786: Nested table values constructors
+#
+values ((values (2)));
+(values (2))
+2
+values ((values (2)), (5), (select 4));
+(values (2)) 5 (select 4)
+2 5 4
+values ((7), (values (2)), (5), (select 4));
+7 (values (2)) 5 (select 4)
+7 2 5 4
+values ((values (2))) union values ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((select 4)), ((values (3)));
+(values (2))
+2
+4
+3
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+(values (4)) (values (5))
+4 5
+1 7
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+(values (4)) (select 5)
+4 5
+1 7
+values ((select 2)) union values ((values (3)));
+(select 2)
+2
+3
+values ((values (2))) union values((select 3));
+(values (2))
+2
+3
+values ((values (2))) union all values ((values (2)));
+(values (2))
+2
+2
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+2 8
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+4 5
+2 8
+values ((values (1) union values (1)));
+(values (1) union values (1))
+1
+values ((values (1) union values (1) union values (1)));
+(values (1) union values (1) union values (1))
+1
+values ((values ((values (4)))));
+(values ((values (4))))
+4
+values ((values ((select 5))));
+(values ((select 5)))
+5
+values ((select (values (4))), (values ((values(5)))));
+(select (values (4))) (values ((values(5))))
+4 5
+values ((select (values (4))), (values ((select 5))));
+(select (values (4))) (values ((select 5)))
+4 5
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+(select (values (4))) (values ((values(5))))
+4 5
+4 7
+values ((values (2))), ((values ((values (4)))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((select 4))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+4
+select * from (values ((values (2)))) dt;
+(values (2))
+2
+select * from (values ((values (2)), (5), (select 4))) dt;
+(values (2)) 5 (select 4)
+2 5 4
+select * from (values ((values (2))) union values ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+(values (2))
+2
+4
+3
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+values ((values ((select a from t1 where a=7))));
+(values ((select a from t1 where a=7)))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select a from t1 where a=7)))
+7
+NULL
+values ((values ((select a from t1 where a in ((values (7)))))));
+(values ((select a from t1 where a in ((values (7))))))
+7
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+(values ((select a from t1 where a in ((values (7), (8))))))
+7
+values ((values
+((select a from t1 where a in (values (7) union values (8))))));
+(values
+((select a from t1 where a in (values (7) union values (8)))))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((select (values(2)) from t1 where a<7));
+ERROR 21000: Subquery returns more than 1 row
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+(values ((select a from t1 where a=7)))
+7
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+a
+3
+7
+1
+2
+3
+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 88d0ac2..3e976f8 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1522,4 +1522,110 @@ drop table t1,t2,t3;
select sum((values(1)));
+--echo #
+--echo # MDEV-22786: Nested table values constructors
+--echo #
+
+values ((values (2)));
+
+values ((values (2)), (5), (select 4));
+
+values ((7), (values (2)), (5), (select 4));
+
+values ((values (2))) union values ((values (3)));
+
+values ((values (2))), ((values (3)));
+
+values ((values (2))), ((select 4)), ((values (3)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+
+values ((select 2)) union values ((values (3)));
+
+values ((values (2))) union values((select 3));
+
+values ((values (2))) union all values ((values (2)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (1) union values (1)));
+
+values ((values (1) union values (1) union values (1)));
+
+values ((values ((values (4)))));
+
+values ((values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))));
+
+values ((select (values (4))), (values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+
+values ((values (2))), ((values ((values (4)))));
+
+values ((values (2))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+
+select * from (values ((values (2)))) dt;
+
+select * from (values ((values (2)), (5), (select 4))) dt;
+
+select * from (values ((values (2))) union values ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+values ((values ((select a from t1 where a=7))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a in ((values (7)))))));
+
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+
+values ((values
+ ((select a from t1 where a in (values (7) union values (8))))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+--error ER_SUBQUERY_NO_1_ROW
+values ((select (values(2)) from t1 where a<7));
+
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+
+drop table t1;
+
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 70d795c..c2bc838 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2421,6 +2421,8 @@ void st_select_lex::init_select()
with_dep= 0;
join= 0;
lock_type= TL_READ_DEFAULT;
+ save_many_values.empty();
+ save_insert_list= 0;
tvc= 0;
in_funcs.empty();
curr_tvc_name= 0;
@@ -8302,16 +8304,52 @@ bool LEX::last_field_generated_always_as_row_end()
}
+void LEX::save_values_list_state()
+{
+ current_select->save_many_values= many_values;
+ current_select->save_insert_list= insert_list;
+}
+
+
+void LEX::restore_values_list_state()
+{
+ many_values= current_select->save_many_values;
+ insert_list= current_select->save_insert_list;
+}
+
+
+void LEX::tvc_start()
+{
+ if (current_select == &select_lex)
+ mysql_init_select(this);
+ else
+ save_values_list_state();
+ many_values.empty();
+ insert_list= 0;
+}
+
+
+bool LEX::tvc_start_derived()
+{
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(this, 1, NULL)))
+ return true;
+ save_values_list_state();
+ many_values.empty();
+ insert_list= 0;
+ return false;
+}
+
+
bool LEX::tvc_finalize()
{
- mysql_init_select(this);
if (unlikely(!(current_select->tvc=
new (thd->mem_root)
table_value_constr(many_values,
current_select,
current_select->options))))
return true;
- many_values.empty();
+ restore_values_list_state();
if (!current_select->master_unit()->fake_select_lex)
current_select->master_unit()->add_fake_select_lex(thd);
return false;
@@ -8326,9 +8364,6 @@ bool LEX::tvc_finalize_derived()
thd->parse_error();
return true;
}
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(this, 1, NULL)))
- return true;
current_select->linkage= DERIVED_TABLE_TYPE;
return tvc_finalize();
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 979e212..474f317 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1176,6 +1176,8 @@ class st_select_lex: public st_select_lex_node
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
+ List<List_item> save_many_values;
+ List<Item> *save_insert_list;
table_value_constr *tvc;
bool in_tvc;
@@ -4046,12 +4048,10 @@ struct LEX: public Query_tables_list
return false;
}
- void tvc_start()
- {
- field_list.empty();
- many_values.empty();
- insert_list= 0;
- }
+ void save_values_list_state();
+ void restore_values_list_state();
+ void tvc_start();
+ bool tvc_start_derived();
bool tvc_finalize();
bool tvc_finalize_derived();
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index cb056b0..96c5223 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -673,6 +673,8 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->nest_level= tvc_sl->nest_level;
wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->exclude_from_table_unique_test=
+ tvc_sl->exclude_from_table_unique_test;
lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 88f12e9..b26e2dd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -12334,7 +12334,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index eaeaf2e..4af034d 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -12272,7 +12272,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
1
0
03 Mar '21
revision-id: d6fd177a5f0d0984da4aec46de04bbc84bce00a2 (mariadb-10.3.26-101-gd6fd177)
parent(s): 11c4e9be19916d3dc4f77647aa99781ddacc88d7
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-03 09:16:15 -0800
message:
MDEV-22786 Crashes with nested table value constructors
The bug caused crashes of the server when processing queries with nested
table value constructors (TVC) . It happened because the grammar rules to
parse TVC used the same global lists for both nested TVC and nesting TVC.
As a result invalid select trees were constructed for queries with nested
TVC and this led to crashes at the prepare stage.
This patch provides its own lists structures for each TVC nest level.
Besides the patch fixes a bug in the function wrap_tvc() that missed
inheritance of the SELECT_LEX::exclude_from_table_unique_test for
selects that wrapped TVCs. This inheritance is critical for specifications
of derived tables that employ nested TVCs.
---
mysql-test/main/table_value_constr.result | 8 ++++++++
mysql-test/main/table_value_constr.test | 3 +++
sql/sql_lex.cc | 2 --
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index 0d18df1..ff6d19a 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -3053,5 +3053,13 @@ select * from (values ((values ((select a from t1 where a=7))))) dt;
select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
(values ((select (values(2)) from t1 where a=8)))
NULL
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+a
+3
+7
+1
+2
+3
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 ec147e6..3e976f8 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1623,6 +1623,9 @@ select * from (values ((values ((select a from t1 where a=7))))) dt;
select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+insert into t1(a) values ((values (2))), ((values (3)));
+select * from t1;
+
drop table t1;
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 495b27c..c2bc838 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -8324,7 +8324,6 @@ void LEX::tvc_start()
mysql_init_select(this);
else
save_values_list_state();
- field_list.empty();
many_values.empty();
insert_list= 0;
}
@@ -8336,7 +8335,6 @@ bool LEX::tvc_start_derived()
unlikely(mysql_new_select(this, 1, NULL)))
return true;
save_values_list_state();
- field_list.empty();
many_values.empty();
insert_list= 0;
return false;
1
0
02 Mar '21
revision-id: 11c4e9be19916d3dc4f77647aa99781ddacc88d7 (mariadb-10.3.26-100-g11c4e9b)
parent(s): 0f81ca6a0bb21fbba4bca93a7555f7c8e6357b47
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-01 20:23:16 -0800
message:
MDEV-22786 Crashes with nested table value constructors
The bug caused crashes of the server when processing queries with nested
table value constructors (TVC) . It happened because the grammar rules to
parse TVC used the same global lists for both nested TVC and nesting TVC.
As a result invalid select trees were constructed for queries with nested
TVC anf this led to crashes at the prepare stage.
This patch provides its own lists structures for each TVC nest level.
Besides the patch fixes a bug in the function wrap_tvc() that missed
inheritance of the SELECT_LEX::exclude_from_table_unique_test for
selects that wrapped TVCs. This inheritance is critical for specifications
of derived tables that employ nested TVCs.
---
mysql-test/main/table_value_constr.result | 167 ++++++++++++++++++++++++++++++
mysql-test/main/table_value_constr.test | 103 ++++++++++++++++++
sql/sql_lex.cc | 47 ++++++++-
sql/sql_lex.h | 12 +--
sql/sql_tvc.cc | 2 +
sql/sql_yacc.yy | 3 +-
sql/sql_yacc_ora.yy | 3 +-
7 files changed, 324 insertions(+), 13 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index d2965ab..0d18df1 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2887,4 +2887,171 @@ drop table t1,t2,t3;
select sum((values(1)));
sum((values(1)))
1
+#
+# MDEV-22786: Nested table values constructors
+#
+values ((values (2)));
+(values (2))
+2
+values ((values (2)), (5), (select 4));
+(values (2)) 5 (select 4)
+2 5 4
+values ((7), (values (2)), (5), (select 4));
+7 (values (2)) 5 (select 4)
+7 2 5 4
+values ((values (2))) union values ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((select 4)), ((values (3)));
+(values (2))
+2
+4
+3
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+(values (4)) (values (5))
+4 5
+1 7
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+(values (4)) (select 5)
+4 5
+1 7
+values ((select 2)) union values ((values (3)));
+(select 2)
+2
+3
+values ((values (2))) union values((select 3));
+(values (2))
+2
+3
+values ((values (2))) union all values ((values (2)));
+(values (2))
+2
+2
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+2 8
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+4 5
+2 8
+values ((values (1) union values (1)));
+(values (1) union values (1))
+1
+values ((values (1) union values (1) union values (1)));
+(values (1) union values (1) union values (1))
+1
+values ((values ((values (4)))));
+(values ((values (4))))
+4
+values ((values ((select 5))));
+(values ((select 5)))
+5
+values ((select (values (4))), (values ((values(5)))));
+(select (values (4))) (values ((values(5))))
+4 5
+values ((select (values (4))), (values ((select 5))));
+(select (values (4))) (values ((select 5)))
+4 5
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+(select (values (4))) (values ((values(5))))
+4 5
+4 7
+values ((values (2))), ((values ((values (4)))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((select 4))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+4
+select * from (values ((values (2)))) dt;
+(values (2))
+2
+select * from (values ((values (2)), (5), (select 4))) dt;
+(values (2)) 5 (select 4)
+2 5 4
+select * from (values ((values (2))) union values ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((values (3)))) dt;
+(values (2))
+2
+3
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+(values (2))
+2
+4
+3
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+values ((values ((select a from t1 where a=7))));
+(values ((select a from t1 where a=7)))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select a from t1 where a=7)))
+7
+NULL
+values ((values ((select a from t1 where a in ((values (7)))))));
+(values ((select a from t1 where a in ((values (7))))))
+7
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+(values ((select a from t1 where a in ((values (7), (8))))))
+7
+values ((values
+((select a from t1 where a in (values (7) union values (8))))));
+(values
+((select a from t1 where a in (values (7) union values (8)))))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((select (values(2)) from t1 where a<7));
+ERROR 21000: Subquery returns more than 1 row
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+(values ((select a from t1 where a=7)))
+7
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+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 88d0ac2..ec147e6 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1522,4 +1522,107 @@ drop table t1,t2,t3;
select sum((values(1)));
+--echo #
+--echo # MDEV-22786: Nested table values constructors
+--echo #
+
+values ((values (2)));
+
+values ((values (2)), (5), (select 4));
+
+values ((7), (values (2)), (5), (select 4));
+
+values ((values (2))) union values ((values (3)));
+
+values ((values (2))), ((values (3)));
+
+values ((values (2))), ((select 4)), ((values (3)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+
+values ((select 2)) union values ((values (3)));
+
+values ((values (2))) union values((select 3));
+
+values ((values (2))) union all values ((values (2)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (1) union values (1)));
+
+values ((values (1) union values (1) union values (1)));
+
+values ((values ((values (4)))));
+
+values ((values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))));
+
+values ((select (values (4))), (values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+
+values ((values (2))), ((values ((values (4)))));
+
+values ((values (2))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+
+select * from (values ((values (2)))) dt;
+
+select * from (values ((values (2)), (5), (select 4))) dt;
+
+select * from (values ((values (2))) union values ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((values (3)))) dt;
+
+select * from (values ((values (2))), ((select 4)), ((values (3)))) dt;
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+values ((values ((select a from t1 where a=7))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a in ((values (7)))))));
+
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+
+values ((values
+ ((select a from t1 where a in (values (7) union values (8))))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+--error ER_SUBQUERY_NO_1_ROW
+values ((select (values(2)) from t1 where a<7));
+
+select * from (values ((values ((select a from t1 where a=7))))) dt;
+
+select * from (values ((values ((select (values(2)) from t1 where a=8))))) dt;
+
+drop table t1;
+
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 70d795c..495b27c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2421,6 +2421,8 @@ void st_select_lex::init_select()
with_dep= 0;
join= 0;
lock_type= TL_READ_DEFAULT;
+ save_many_values.empty();
+ save_insert_list= 0;
tvc= 0;
in_funcs.empty();
curr_tvc_name= 0;
@@ -8302,16 +8304,54 @@ bool LEX::last_field_generated_always_as_row_end()
}
+void LEX::save_values_list_state()
+{
+ current_select->save_many_values= many_values;
+ current_select->save_insert_list= insert_list;
+}
+
+
+void LEX::restore_values_list_state()
+{
+ many_values= current_select->save_many_values;
+ insert_list= current_select->save_insert_list;
+}
+
+
+void LEX::tvc_start()
+{
+ if (current_select == &select_lex)
+ mysql_init_select(this);
+ else
+ save_values_list_state();
+ field_list.empty();
+ many_values.empty();
+ insert_list= 0;
+}
+
+
+bool LEX::tvc_start_derived()
+{
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(this, 1, NULL)))
+ return true;
+ save_values_list_state();
+ field_list.empty();
+ many_values.empty();
+ insert_list= 0;
+ return false;
+}
+
+
bool LEX::tvc_finalize()
{
- mysql_init_select(this);
if (unlikely(!(current_select->tvc=
new (thd->mem_root)
table_value_constr(many_values,
current_select,
current_select->options))))
return true;
- many_values.empty();
+ restore_values_list_state();
if (!current_select->master_unit()->fake_select_lex)
current_select->master_unit()->add_fake_select_lex(thd);
return false;
@@ -8326,9 +8366,6 @@ bool LEX::tvc_finalize_derived()
thd->parse_error();
return true;
}
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(this, 1, NULL)))
- return true;
current_select->linkage= DERIVED_TABLE_TYPE;
return tvc_finalize();
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 979e212..474f317 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1176,6 +1176,8 @@ class st_select_lex: public st_select_lex_node
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
+ List<List_item> save_many_values;
+ List<Item> *save_insert_list;
table_value_constr *tvc;
bool in_tvc;
@@ -4046,12 +4048,10 @@ struct LEX: public Query_tables_list
return false;
}
- void tvc_start()
- {
- field_list.empty();
- many_values.empty();
- insert_list= 0;
- }
+ void save_values_list_state();
+ void restore_values_list_state();
+ void tvc_start();
+ bool tvc_start_derived();
bool tvc_finalize();
bool tvc_finalize_derived();
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index cb056b0..96c5223 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -673,6 +673,8 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->nest_level= tvc_sl->nest_level;
wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->linkage= tvc_sl->linkage;
+ wrapper_sl->exclude_from_table_unique_test=
+ tvc_sl->exclude_from_table_unique_test;
lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 88f12e9..b26e2dd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -12334,7 +12334,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index eaeaf2e..4af034d 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -12272,7 +12272,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
1
0
[Commits] 273cc52: MDEV-24919 Crash with subselect formed by table value constructor and
by IgorBabaev 01 Mar '21
by IgorBabaev 01 Mar '21
01 Mar '21
revision-id: 273cc529a8063206e2c432a7b5633001fe08a10f (mariadb-10.3.26-93-g273cc52)
parent(s): 25ecf8ed4b4cbca69a9fa09c27bbd4e5c83fafe3
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-03-01 09:40:33 -0800
message:
MDEV-24919 Crash with subselect formed by table value constructor and
used in set function
If a subselect is formed by a table value constructor (TVC) then the
following transformation is applied at the prepare stage:
VALUES (v1), ... (vn) => SELECT * FROM (VALUES (v1), ... (vn)) tvc_x.
The transformation is performed by the function wrap_tvc() that resets
THD::LEX::current select to the top level select of the result of the
transformation. After the call of wrap_tvc() in the function
Item_subselect::wrap_tvc_into_select() the field THD::LEX::current must be
reset to the same select as before the call. It was not done. As a result
if the subselect formed by a TVC was an argument of a set function then
an assertion was hit in the function Item_sum::check_sum_func().
Approved by Oleksandr Byelkin <sanja(a)mariadb.com>
---
mysql-test/main/table_value_constr.result | 6 ++++++
mysql-test/main/table_value_constr.test | 6 ++++++
sql/sql_tvc.cc | 15 +++++----------
3 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index 603f21a..d2965ab 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2881,4 +2881,10 @@ NULL
deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+#
+# MDEV-24919: subselect formed by TVC and used in set function
+#
+select sum((values(1)));
+sum((values(1)))
+1
End of 10.3 tests
diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test
index 2246a19..88d0ac2 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1516,4 +1516,10 @@ deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+--echo #
+--echo # MDEV-24919: subselect formed by TVC and used in set function
+--echo #
+
+select sum((values(1)));
+
--echo End of 10.3 tests
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 0a771b5..cb056b0 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -648,7 +648,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
st_select_lex *parent_select)
{
LEX *lex= thd->lex;
- select_result *save_result= thd->lex->result;
+ select_result *save_result= lex->result;
uint8 save_derived_tables= lex->derived_tables;
thd->lex->result= NULL;
@@ -729,13 +729,13 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
if (arena)
thd->restore_active_arena(arena, &backup);
- thd->lex->result= save_result;
+ lex->result= save_result;
return wrapper_sl;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
- thd->lex->result= save_result;
+ lex->result= save_result;
lex->derived_tables= save_derived_tables;
return 0;
}
@@ -819,14 +819,9 @@ Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl)
{
if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
((subselect_single_select_engine *) engine)->change_select(wrapper_sl);
- lex->current_select= wrapper_sl;
- return wrapper_sl;
- }
- else
- {
- lex->current_select= parent_select;
- return 0;
}
+ lex->current_select= parent_select;
+ return wrapper_sl;
}
1
0
[Commits] 6e85b78: MDEV-24919 Crash with subselect formed by table value constructor and
by IgorBabaev 27 Feb '21
by IgorBabaev 27 Feb '21
27 Feb '21
revision-id: 6e85b78fe65bd7fe15689d4a532399676f62902d (mariadb-10.3.26-93-g6e85b78)
parent(s): 25ecf8ed4b4cbca69a9fa09c27bbd4e5c83fafe3
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-02-26 16:55:45 -0800
message:
MDEV-24919 Crash with subselect formed by table value constructor and
used in set function
If a subselect is formed by a table value constructor (TVC) then the
following transformation is applied at the prepare stage:
VALUES (v1), ... (vn) => SELECT * FROM (VALUES (v1), ... (vn)) tvc_x.
The transformation is performed by the function wrap_tvc() that resets
THD::LEX::current select to the top level select of the result of the
transformation. After the call of wrap_tvc() in the function
Item_subselect::wrap_tvc_into_select() the field THD::LEX::current must be
reset to the same select as before the call. It was not done. As a result
if the subselect formed by a TVC was an argument of a set function then
an assertion was hit in the function Item_sum::check_sum_func().
---
mysql-test/main/table_value_constr.result | 6 ++++++
mysql-test/main/table_value_constr.test | 6 ++++++
sql/sql_tvc.cc | 15 +++++----------
3 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index 603f21a..d2965ab 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2881,4 +2881,10 @@ NULL
deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+#
+# MDEV-24919: subselect formed by TVC and used in set function
+#
+select sum((values(1)));
+sum((values(1)))
+1
End of 10.3 tests
diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test
index 2246a19..88d0ac2 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1516,4 +1516,10 @@ deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+--echo #
+--echo # MDEV-24919: subselect formed by TVC and used in set function
+--echo #
+
+select sum((values(1)));
+
--echo End of 10.3 tests
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 0a771b5..cb056b0 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -648,7 +648,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
st_select_lex *parent_select)
{
LEX *lex= thd->lex;
- select_result *save_result= thd->lex->result;
+ select_result *save_result= lex->result;
uint8 save_derived_tables= lex->derived_tables;
thd->lex->result= NULL;
@@ -729,13 +729,13 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
if (arena)
thd->restore_active_arena(arena, &backup);
- thd->lex->result= save_result;
+ lex->result= save_result;
return wrapper_sl;
err:
if (arena)
thd->restore_active_arena(arena, &backup);
- thd->lex->result= save_result;
+ lex->result= save_result;
lex->derived_tables= save_derived_tables;
return 0;
}
@@ -819,14 +819,9 @@ Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl)
{
if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
((subselect_single_select_engine *) engine)->change_select(wrapper_sl);
- lex->current_select= wrapper_sl;
- return wrapper_sl;
- }
- else
- {
- lex->current_select= parent_select;
- return 0;
}
+ lex->current_select= parent_select;
+ return wrapper_sl;
}
1
0
26 Feb '21
revision-id: a6c2892003775b903180b648e872381455dfae33 (mariadb-10.3.26-88-ga6c2892)
parent(s): bf6484e7bb4af3a3bc60289d86e4bde813f4e0c0
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-02-25 23:11:03 -0800
message:
MDEV-22786 Crashes with nested table value constructors
The bug caused crashes of the server when processing queries with nested
table value constructors (TVC) . It happened because the grammar rules to
parse TVC used the same global lists for both nested TVC and nesting TVC.
This patch provides its own lists structures for each TVC nest level.
---
mysql-test/main/table_value_constr.result | 138 ++++++++++++++++++++++++++++++
mysql-test/main/table_value_constr.test | 87 +++++++++++++++++++
sql/sql_lex.cc | 47 ++++++++--
sql/sql_lex.h | 12 +--
sql/sql_yacc.yy | 3 +-
sql/sql_yacc_ora.yy | 3 +-
6 files changed, 277 insertions(+), 13 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index 603f21a..0a165aa 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -2881,4 +2881,142 @@ NULL
deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+#
+# MDEV-22786: Nested table values constructors
+#
+values ((values (2)));
+(values (2))
+2
+values ((values (2)), (5), (select 4));
+(values (2)) 5 (select 4)
+2 5 4
+values ((7), (values (2)), (5), (select 4));
+7 (values (2)) 5 (select 4)
+7 2 5 4
+values ((values (2))) union values ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((values (3)));
+(values (2))
+2
+3
+values ((values (2))), ((select 4)), ((values (3)));
+(values (2))
+2
+4
+3
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+(values (4)) (values (5))
+4 5
+1 7
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+(values (4)) (select 5)
+4 5
+1 7
+values ((select 2)) union values ((values (3)));
+(select 2)
+2
+3
+values ((values (2))) union values((select 3));
+(values (2))
+2
+3
+values ((values (2))) union all values ((values (2)));
+(values (2))
+2
+2
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+2 8
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+(values (4)) (values (5))
+4 5
+1 7
+4 5
+2 8
+values ((values (1) union values (1)));
+(values (1) union values (1))
+1
+values ((values (1) union values (1) union values (1)));
+(values (1) union values (1) union values (1))
+1
+values ((values ((values (4)))));
+(values ((values (4))))
+4
+values ((values ((select 5))));
+(values ((select 5)))
+5
+values ((select (values (4))), (values ((values(5)))));
+(select (values (4))) (values ((values(5))))
+4 5
+values ((select (values (4))), (values ((select 5))));
+(select (values (4))) (values ((select 5)))
+4 5
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+(select (values (4))) (values ((values(5))))
+4 5
+4 7
+values ((values (2))), ((values ((values (4)))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((select 4))));
+(values (2))
+2
+4
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+(values (2))
+2
+4
+8
+4
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+values ((values ((select a from t1 where a=7))));
+(values ((select a from t1 where a=7)))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select a from t1 where a=7)))
+7
+NULL
+values ((values ((select a from t1 where a in ((values (7)))))));
+(values ((select a from t1 where a in ((values (7))))))
+7
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+(values ((select a from t1 where a in ((values (7), (8))))))
+7
+values ((values
+((select a from t1 where a in (values (7) union values (8))))));
+(values
+((select a from t1 where a in (values (7) union values (8)))))
+7
+values ((values ((select (values(2)) from t1 where a=8))));
+(values ((select (values(2)) from t1 where a=8)))
+NULL
+values ((select (values(2)) from t1 where a<7));
+ERROR 21000: Subquery returns more than 1 row
+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 2246a19..3e87ac8 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1516,4 +1516,91 @@ deallocate prepare stmt;
drop view v1;
drop table t1,t2,t3;
+--echo #
+--echo # MDEV-22786: Nested table values constructors
+--echo #
+
+values ((values (2)));
+
+values ((values (2)), (5), (select 4));
+
+values ((7), (values (2)), (5), (select 4));
+
+values ((values (2))) union values ((values (3)));
+
+values ((values (2))), ((values (3)));
+
+values ((values (2))), ((select 4)), ((values (3)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)));
+
+values ((values (4)), (select 5)), ((select 1), (values (7)));
+
+values ((select 2)) union values ((values (3)));
+
+values ((values (2))) union values((select 3));
+
+values ((values (2))) union all values ((values (2)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (4)), (values (5))), ((values (1)), (values (7)))
+union all
+values ((values (4)), (select 5)), ((select 2), (values (8)));
+
+values ((values (1) union values (1)));
+
+values ((values (1) union values (1) union values (1)));
+
+values ((values ((values (4)))));
+
+values ((values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))));
+
+values ((select (values (4))), (values ((select 5))));
+
+values ((select (values (4))), (values ((values(5)))))
+union
+values ((select (values (4))), (values ((select 7))));
+
+values ((values (2))), ((values ((values (4)))));
+
+values ((values (2))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union
+values ((values (8))), ((values ((select 4))));
+
+values ((values (2))), ((values ((values (4)))))
+union all
+values ((values (8))), ((values ((select 4))));
+
+create table t1 (a int);
+insert into t1 values (3), (7), (1);
+
+values ((values ((select a from t1 where a=7))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a=7))))
+union
+values ((values ((select (values(2)) from t1 where a=8))));
+
+values ((values ((select a from t1 where a in ((values (7)))))));
+
+values ((values ((select a from t1 where a in ((values (7), (8)))))));
+
+values ((values
+ ((select a from t1 where a in (values (7) union values (8))))));
+
+values ((values ((select (values(2)) from t1 where a=8))));
+
+--error ER_SUBQUERY_NO_1_ROW
+values ((select (values(2)) from t1 where a<7));
+
+drop table t1;
+
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 70d795c..495b27c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2421,6 +2421,8 @@ void st_select_lex::init_select()
with_dep= 0;
join= 0;
lock_type= TL_READ_DEFAULT;
+ save_many_values.empty();
+ save_insert_list= 0;
tvc= 0;
in_funcs.empty();
curr_tvc_name= 0;
@@ -8302,16 +8304,54 @@ bool LEX::last_field_generated_always_as_row_end()
}
+void LEX::save_values_list_state()
+{
+ current_select->save_many_values= many_values;
+ current_select->save_insert_list= insert_list;
+}
+
+
+void LEX::restore_values_list_state()
+{
+ many_values= current_select->save_many_values;
+ insert_list= current_select->save_insert_list;
+}
+
+
+void LEX::tvc_start()
+{
+ if (current_select == &select_lex)
+ mysql_init_select(this);
+ else
+ save_values_list_state();
+ field_list.empty();
+ many_values.empty();
+ insert_list= 0;
+}
+
+
+bool LEX::tvc_start_derived()
+{
+ if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
+ unlikely(mysql_new_select(this, 1, NULL)))
+ return true;
+ save_values_list_state();
+ field_list.empty();
+ many_values.empty();
+ insert_list= 0;
+ return false;
+}
+
+
bool LEX::tvc_finalize()
{
- mysql_init_select(this);
if (unlikely(!(current_select->tvc=
new (thd->mem_root)
table_value_constr(many_values,
current_select,
current_select->options))))
return true;
- many_values.empty();
+ restore_values_list_state();
if (!current_select->master_unit()->fake_select_lex)
current_select->master_unit()->add_fake_select_lex(thd);
return false;
@@ -8326,9 +8366,6 @@ bool LEX::tvc_finalize_derived()
thd->parse_error();
return true;
}
- if (current_select->linkage == GLOBAL_OPTIONS_TYPE ||
- unlikely(mysql_new_select(this, 1, NULL)))
- return true;
current_select->linkage= DERIVED_TABLE_TYPE;
return tvc_finalize();
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 979e212..474f317 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1176,6 +1176,8 @@ class st_select_lex: public st_select_lex_node
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
+ List<List_item> save_many_values;
+ List<Item> *save_insert_list;
table_value_constr *tvc;
bool in_tvc;
@@ -4046,12 +4048,10 @@ struct LEX: public Query_tables_list
return false;
}
- void tvc_start()
- {
- field_list.empty();
- many_values.empty();
- insert_list= 0;
- }
+ void save_values_list_state();
+ void restore_values_list_state();
+ void tvc_start();
+ bool tvc_start_derived();
bool tvc_finalize();
bool tvc_finalize_derived();
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 88f12e9..b26e2dd 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -12334,7 +12334,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index eaeaf2e..4af034d 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -12272,7 +12272,8 @@ derived_query_specification:
derived_table_value_constructor:
VALUES
{
- Lex->tvc_start();
+ if (Lex->tvc_start_derived())
+ MYSQL_YYABORT;
}
values_list
{
1
0
[Commits] bf6484e: MDEV-24910 Crash with SELECT that uses table value constructor as a subselect
by IgorBabaev 24 Feb '21
by IgorBabaev 24 Feb '21
24 Feb '21
revision-id: bf6484e7bb4af3a3bc60289d86e4bde813f4e0c0 (mariadb-10.3.26-87-gbf6484e)
parent(s): 13f0e1e1392b1e275d55a7d37b3dac946d908bb0
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-02-24 13:51:47 -0800
message:
MDEV-24910 Crash with SELECT that uses table value constructor as a subselect
This bug caused crashes of the server when processing queries with table
value constructors (TVC) that contained subqueries and were used itself as
subselects. For such TVCs the following transformation is applied at the
prepare stage:
VALUES (v1), ... (vn) => SELECT * FROM (VALUES (v1), ... (vn)) tvc_x.
This transformation allows to reduce the problem of evaluation of TVCs used
as subselects to the problem of evaluation of regular subselects.
The transformation is implemented in the wrap_tvc(). The code the function
to mimic the behaviour of the parser when processing the result of the
transformation. However this imitation was not free of some flaws. First
the function called the method exclude() that completely destroyed the
select tree structures below the transformed TVC. Second the function
used the procedure mysql_new_select to create st_select_lex nodes for
both wrapping select of the transformation and TVC. This also led to
constructing of invalid select tree structures.
The patch actually re-engineers the code of wrap_tvc().
Approved by Oleksandr Byelkin <sanja(a)mariadb.com>
---
mysql-test/main/table_value_constr.result | 110 ++++++++++++++++++++-
mysql-test/main/table_value_constr.test | 57 +++++++++++
.../compat/oracle/r/table_value_constr.result | 4 +-
sql/sql_lex.cc | 23 +++++
sql/sql_lex.h | 1 +
sql/sql_tvc.cc | 67 ++++++++-----
6 files changed, 231 insertions(+), 31 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index e112aca..603f21a 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -748,7 +748,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1)) `tvc_0`) where 1
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1)) `tvc_0`) where 1
explain extended select * from t1
where a in (select * from (values (1)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
@@ -983,7 +983,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1),(2)) `tvc_0`) where 1
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1),(2)) `tvc_0`) where 1
explain extended select * from t1
where a = any (select * from (values (1),(2)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
@@ -2775,4 +2775,110 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
drop table t1;
+#
+# MDEV-24910: TVC containing subquery used as a subselect
+#
+create table t1 (a int) engine=myisam;
+insert into t1 values (3), (7), (1);
+create table t2 (b int) engine=myisam;
+insert into t2 values (1), (2);
+select (values ((select 2))) from t2;
+(values ((select 2)))
+2
+2
+explain select (values ((select 2))) from t2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1249 Select 3 was reduced during optimization
+prepare stmt from "select (values ((select 2))) from t2";
+execute stmt;
+(values ((select 2)))
+2
+2
+execute stmt;
+(values ((select 2)))
+2
+2
+deallocate prepare stmt;
+select (values ((select * from t1 where a > 10))) from t2;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+explain select (values ((select * from t1 where a > 10))) from t2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+prepare stmt from "select (values ((select * from t1 where a > 10))) from t2";
+execute stmt;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+execute stmt;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+deallocate prepare stmt;
+create table t3 (a int);
+insert into t3 values
+(3), (7), (7), (1), (3), (9), (7), (9), (8), (7), (8);
+create view v1 as select count(a) as c from t3 group by a;
+select
+(values ((select * from t3 where a in (select * from v1))));
+(values ((select * from t3 where a in (select * from v1))))
+1
+explain select
+(values ((select * from t3 where a in (select * from v1))));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
+6 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t3 ALL NULL NULL NULL NULL 11
+3 SUBQUERY <subquery4> eq_ref distinct_key distinct_key 8 func 1 Using where
+4 MATERIALIZED <derived5> ALL NULL NULL NULL NULL 11
+5 DERIVED t3 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort
+prepare stmt from "select
+(values ((select * from t3 where a in (select * from v1))))";
+execute stmt;
+(values ((select * from t3 where a in (select * from v1))))
+1
+execute stmt;
+(values ((select * from t3 where a in (select * from v1))))
+1
+deallocate prepare stmt;
+select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))));
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+explain select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
+6 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t3 ALL NULL NULL NULL NULL 11 Using where
+3 SUBQUERY <subquery4> eq_ref distinct_key distinct_key 8 func 1 Using where
+4 MATERIALIZED <derived5> ALL NULL NULL NULL NULL 11
+5 DERIVED t3 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort
+prepare stmt from "select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))";
+execute stmt;
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+execute stmt;
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+deallocate prepare stmt;
+drop view v1;
+drop table t1,t2,t3;
End of 10.3 tests
diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test
index 0a78fd9..2246a19 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1459,4 +1459,61 @@ eval explain $q3;
drop table t1;
+--echo #
+--echo # MDEV-24910: TVC containing subquery used as a subselect
+--echo #
+
+create table t1 (a int) engine=myisam;
+insert into t1 values (3), (7), (1);
+create table t2 (b int) engine=myisam;
+insert into t2 values (1), (2);
+
+let $q1=
+select (values ((select 2))) from t2;
+eval $q1;
+eval explain $q1;
+eval prepare stmt from "$q1";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q2=
+select (values ((select * from t1 where a > 10))) from t2;
+eval $q2;
+eval explain $q2;
+eval prepare stmt from "$q2";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+create table t3 (a int);
+insert into t3 values
+ (3), (7), (7), (1), (3), (9), (7), (9), (8), (7), (8);
+
+create view v1 as select count(a) as c from t3 group by a;
+
+let $q3=
+select
+(values ((select * from t3 where a in (select * from v1))));
+eval $q3;
+eval explain $q3;
+eval prepare stmt from "$q3";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q4=
+select
+(values ((select * from t3
+ where a > 10 and a in (select * from v1))));
+eval $q4;
+eval explain $q4;
+eval prepare stmt from "$q4";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+drop view v1;
+drop table t1,t2,t3;
+
--echo End of 10.3 tests
diff --git a/mysql-test/suite/compat/oracle/r/table_value_constr.result b/mysql-test/suite/compat/oracle/r/table_value_constr.result
index d4f8e28..5393295 100644
--- a/mysql-test/suite/compat/oracle/r/table_value_constr.result
+++ b/mysql-test/suite/compat/oracle/r/table_value_constr.result
@@ -746,7 +746,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1)) "tvc_0") where 1
+Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1)) "tvc_0") where 1
explain extended select * from t1
where a in (select * from (values (1)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
@@ -981,7 +981,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1),(2)) "tvc_0") where 1
+Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1),(2)) "tvc_0") where 1
explain extended select * from t1
where a = any (select * from (values (1),(2)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 9766a28..70d795c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2464,9 +2464,32 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg)
{
slave= slave_arg;
slave_arg->master= this;
+ slave->prev= &master->slave;
+ slave->next= 0;
}
}
+/*
+ @brief
+ Substitute this node in select tree for a newly creates node
+
+ @param subst the node to substitute for
+
+ @details
+ The function substitute this node in the select tree for a newly
+ created node subst. This node is just removed from the tree but all
+ its link fields and the attached sub-tree remain untouched.
+*/
+
+void st_select_lex_node::substitute_in_tree(st_select_lex_node *subst)
+{
+ if ((subst->next= next))
+ next->prev= &subst->next;
+ subst->prev= prev;
+ (*prev)= subst;
+ subst->master= master;
+}
+
/*
include on level down (but do not link)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 77b4e15..979e212 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -711,6 +711,7 @@ class st_select_lex_node {
void include_global(st_select_lex_node **plink);
void exclude();
void exclude_from_tree();
+ void substitute_in_tree(st_select_lex_node *subst);
void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
void move_node(st_select_lex_node *where_to_move)
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 0a5f668..0a771b5 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -654,44 +654,61 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ Item *item;
+ SELECT_LEX *wrapper_sl;
+ SELECT_LEX_UNIT *derived_unit;
+
/*
- Create SELECT_LEX of the select used in the result of transformation
+ Create SELECT_LEX wrapper_sl of the select used in the result
+ of the transformation
*/
- lex->current_select= tvc_sl;
- if (mysql_new_select(lex, 0, NULL))
+ if (!(wrapper_sl= new (thd->mem_root) SELECT_LEX()))
goto err;
- mysql_init_select(lex);
- /* Create item list as '*' for the subquery SQ */
- Item *item;
- SELECT_LEX *wrapper_sl;
- wrapper_sl= lex->current_select;
+ wrapper_sl->select_number= ++thd->lex->stmt_lex->current_select_number;
+ wrapper_sl->parent_lex= lex; /* Used in init_query. */
+ wrapper_sl->init_query();
+ wrapper_sl->init_select();
+
+ wrapper_sl->nest_level= tvc_sl->nest_level;
+ wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->linkage= tvc_sl->linkage;
- wrapper_sl->parsing_place= SELECT_LIST;
+
+ lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
NULL, NULL, &star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(wrapper_sl->with_wild)++;
-
- /* Exclude SELECT with TVC */
- tvc_sl->exclude();
+
+ /* Include the newly created select into the global list of selects */
+ wrapper_sl->include_global((st_select_lex_node**)&lex->all_selects_list);
+
+ /* Substitute select node used of TVC for the newly created select */
+ tvc_sl->substitute_in_tree(wrapper_sl);
+
/*
- Create derived table DT that will wrap TVC in the result of transformation
+ Create a unit for the substituted select used for TVC and attach it
+ to the the wrapper select wrapper_sl as the only unit. The created
+ unit is the unit for the derived table tvc_x of the transformation.
*/
- SELECT_LEX *tvc_select; // select for tvc
- SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
- if (mysql_new_select(lex, 1, tvc_sl))
+ if (!(derived_unit= new (thd->mem_root) SELECT_LEX_UNIT()))
goto err;
- tvc_select= lex->current_select;
- derived_unit= tvc_select->master_unit();
- tvc_select->linkage= DERIVED_TABLE_TYPE;
+ derived_unit->init_query();
+ derived_unit->thd= thd;
+ derived_unit->include_down(wrapper_sl);
- lex->current_select= wrapper_sl;
+ /*
+ Attach the select used of TVC as the only slave to the unit for
+ the derived table tvc_x of the transformation
+ */
+ derived_unit->add_slave(tvc_sl);
+ tvc_sl->linkage= DERIVED_TABLE_TYPE;
/*
- Create the name of the wrapping derived table and
- add it to the FROM list of the wrapper
- */
+ Generate the name of the derived table created for TVC and
+ add it to the FROM list of the wrapping select
+ */
Table_ident *ti;
LEX_CSTRING alias;
TABLE_LIST *derived_tab;
@@ -710,10 +727,6 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
lex->derived_tables|= DERIVED_SUBQUERY;
- wrapper_sl->where= 0;
- wrapper_sl->set_braces(false);
- derived_unit->set_with_clause(0);
-
if (arena)
thd->restore_active_arena(arena, &backup);
thd->lex->result= save_result;
1
0
[Commits] 47c85a4: MDEV-24910 Crash with SELECT that uses table value constructor as a subselect
by IgorBabaev 24 Feb '21
by IgorBabaev 24 Feb '21
24 Feb '21
revision-id: 47c85a4d0120cc1ef9f28ba5824a07dd683fca80 (mariadb-10.3.26-85-g47c85a4)
parent(s): 640f42311a72fa82bf7117c2791fc47ceb420361
author: Igor Babaev
committer: Igor Babaev
timestamp: 2021-02-23 22:33:06 -0800
message:
MDEV-24910 Crash with SELECT that uses table value constructor as a subselect
This bug caused crashes of the server when processing queries with table
value constructors (TVC) that contained subqueries and were used itself as
subselects. For such TVCs the following transformation is applied at the
prepare stage:
VALUES (v1), ... (vn) => SELECT * FROM (VALUES (v1), ... (vn)) tvc_x.
This transformation allows to reduce the problem of evaluation of TVCs used
as subselects to the problem of evaluation of regular subselects.
The transformation is implemented in the wrap_tvc(). The code the function
to mimic the behaviour of the parser when processing the result of the
transformation. However this imitation was not free of some flaws. First
the function called the method exclude() that completely destroyed the
select tree structures below the transformed TVC. Second the function
used the procedure mysql_new_select to create st_select_lex nodes for
both wrapping select of the transformation and TVC. This also led to
constructing of invalid select tree structures.
The patch actually re-engineers the code of wrap_tvc().
---
mysql-test/main/table_value_constr.result | 110 +++++++++++++++++++++++++++++-
mysql-test/main/table_value_constr.test | 57 ++++++++++++++++
sql/sql_lex.cc | 23 +++++++
sql/sql_lex.h | 1 +
sql/sql_tvc.cc | 67 ++++++++++--------
5 files changed, 229 insertions(+), 29 deletions(-)
diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result
index e112aca..603f21a 100644
--- a/mysql-test/main/table_value_constr.result
+++ b/mysql-test/main/table_value_constr.result
@@ -748,7 +748,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1)) `tvc_0`) where 1
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1)) `tvc_0`) where 1
explain extended select * from t1
where a in (select * from (values (1)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
@@ -983,7 +983,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 2 100.00
2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1),(2)) `tvc_0`) where 1
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join ((values (1),(2)) `tvc_0`) where 1
explain extended select * from t1
where a = any (select * from (values (1),(2)) as tvc_0);
id select_type table type possible_keys key key_len ref rows filtered Extra
@@ -2775,4 +2775,110 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
drop table t1;
+#
+# MDEV-24910: TVC containing subquery used as a subselect
+#
+create table t1 (a int) engine=myisam;
+insert into t1 values (3), (7), (1);
+create table t2 (b int) engine=myisam;
+insert into t2 values (1), (2);
+select (values ((select 2))) from t2;
+(values ((select 2)))
+2
+2
+explain select (values ((select 2))) from t2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1249 Select 3 was reduced during optimization
+prepare stmt from "select (values ((select 2))) from t2";
+execute stmt;
+(values ((select 2)))
+2
+2
+execute stmt;
+(values ((select 2)))
+2
+2
+deallocate prepare stmt;
+select (values ((select * from t1 where a > 10))) from t2;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+explain select (values ((select * from t1 where a > 10))) from t2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+prepare stmt from "select (values ((select * from t1 where a > 10))) from t2";
+execute stmt;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+execute stmt;
+(values ((select * from t1 where a > 10)))
+NULL
+NULL
+deallocate prepare stmt;
+create table t3 (a int);
+insert into t3 values
+(3), (7), (7), (1), (3), (9), (7), (9), (8), (7), (8);
+create view v1 as select count(a) as c from t3 group by a;
+select
+(values ((select * from t3 where a in (select * from v1))));
+(values ((select * from t3 where a in (select * from v1))))
+1
+explain select
+(values ((select * from t3 where a in (select * from v1))));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
+6 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t3 ALL NULL NULL NULL NULL 11
+3 SUBQUERY <subquery4> eq_ref distinct_key distinct_key 8 func 1 Using where
+4 MATERIALIZED <derived5> ALL NULL NULL NULL NULL 11
+5 DERIVED t3 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort
+prepare stmt from "select
+(values ((select * from t3 where a in (select * from v1))))";
+execute stmt;
+(values ((select * from t3 where a in (select * from v1))))
+1
+execute stmt;
+(values ((select * from t3 where a in (select * from v1))))
+1
+deallocate prepare stmt;
+select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))));
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+explain select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
+6 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used
+3 SUBQUERY t3 ALL NULL NULL NULL NULL 11 Using where
+3 SUBQUERY <subquery4> eq_ref distinct_key distinct_key 8 func 1 Using where
+4 MATERIALIZED <derived5> ALL NULL NULL NULL NULL 11
+5 DERIVED t3 ALL NULL NULL NULL NULL 11 Using temporary; Using filesort
+prepare stmt from "select
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))";
+execute stmt;
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+execute stmt;
+(values ((select * from t3
+where a > 10 and a in (select * from v1))))
+NULL
+deallocate prepare stmt;
+drop view v1;
+drop table t1,t2,t3;
End of 10.3 tests
diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test
index 0a78fd9..2246a19 100644
--- a/mysql-test/main/table_value_constr.test
+++ b/mysql-test/main/table_value_constr.test
@@ -1459,4 +1459,61 @@ eval explain $q3;
drop table t1;
+--echo #
+--echo # MDEV-24910: TVC containing subquery used as a subselect
+--echo #
+
+create table t1 (a int) engine=myisam;
+insert into t1 values (3), (7), (1);
+create table t2 (b int) engine=myisam;
+insert into t2 values (1), (2);
+
+let $q1=
+select (values ((select 2))) from t2;
+eval $q1;
+eval explain $q1;
+eval prepare stmt from "$q1";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q2=
+select (values ((select * from t1 where a > 10))) from t2;
+eval $q2;
+eval explain $q2;
+eval prepare stmt from "$q2";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+create table t3 (a int);
+insert into t3 values
+ (3), (7), (7), (1), (3), (9), (7), (9), (8), (7), (8);
+
+create view v1 as select count(a) as c from t3 group by a;
+
+let $q3=
+select
+(values ((select * from t3 where a in (select * from v1))));
+eval $q3;
+eval explain $q3;
+eval prepare stmt from "$q3";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q4=
+select
+(values ((select * from t3
+ where a > 10 and a in (select * from v1))));
+eval $q4;
+eval explain $q4;
+eval prepare stmt from "$q4";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+drop view v1;
+drop table t1,t2,t3;
+
--echo End of 10.3 tests
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 9766a28..70d795c 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2464,9 +2464,32 @@ void st_select_lex_node::add_slave(st_select_lex_node *slave_arg)
{
slave= slave_arg;
slave_arg->master= this;
+ slave->prev= &master->slave;
+ slave->next= 0;
}
}
+/*
+ @brief
+ Substitute this node in select tree for a newly creates node
+
+ @param subst the node to substitute for
+
+ @details
+ The function substitute this node in the select tree for a newly
+ created node subst. This node is just removed from the tree but all
+ its link fields and the attached sub-tree remain untouched.
+*/
+
+void st_select_lex_node::substitute_in_tree(st_select_lex_node *subst)
+{
+ if ((subst->next= next))
+ next->prev= &subst->next;
+ subst->prev= prev;
+ (*prev)= subst;
+ subst->master= master;
+}
+
/*
include on level down (but do not link)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 77b4e15..979e212 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -711,6 +711,7 @@ class st_select_lex_node {
void include_global(st_select_lex_node **plink);
void exclude();
void exclude_from_tree();
+ void substitute_in_tree(st_select_lex_node *subst);
void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; }
void move_node(st_select_lex_node *where_to_move)
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 0a5f668..0a771b5 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -654,44 +654,61 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ Item *item;
+ SELECT_LEX *wrapper_sl;
+ SELECT_LEX_UNIT *derived_unit;
+
/*
- Create SELECT_LEX of the select used in the result of transformation
+ Create SELECT_LEX wrapper_sl of the select used in the result
+ of the transformation
*/
- lex->current_select= tvc_sl;
- if (mysql_new_select(lex, 0, NULL))
+ if (!(wrapper_sl= new (thd->mem_root) SELECT_LEX()))
goto err;
- mysql_init_select(lex);
- /* Create item list as '*' for the subquery SQ */
- Item *item;
- SELECT_LEX *wrapper_sl;
- wrapper_sl= lex->current_select;
+ wrapper_sl->select_number= ++thd->lex->stmt_lex->current_select_number;
+ wrapper_sl->parent_lex= lex; /* Used in init_query. */
+ wrapper_sl->init_query();
+ wrapper_sl->init_select();
+
+ wrapper_sl->nest_level= tvc_sl->nest_level;
+ wrapper_sl->parsing_place= tvc_sl->parsing_place;
wrapper_sl->linkage= tvc_sl->linkage;
- wrapper_sl->parsing_place= SELECT_LIST;
+
+ lex->current_select= wrapper_sl;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
NULL, NULL, &star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(wrapper_sl->with_wild)++;
-
- /* Exclude SELECT with TVC */
- tvc_sl->exclude();
+
+ /* Include the newly created select into the global list of selects */
+ wrapper_sl->include_global((st_select_lex_node**)&lex->all_selects_list);
+
+ /* Substitute select node used of TVC for the newly created select */
+ tvc_sl->substitute_in_tree(wrapper_sl);
+
/*
- Create derived table DT that will wrap TVC in the result of transformation
+ Create a unit for the substituted select used for TVC and attach it
+ to the the wrapper select wrapper_sl as the only unit. The created
+ unit is the unit for the derived table tvc_x of the transformation.
*/
- SELECT_LEX *tvc_select; // select for tvc
- SELECT_LEX_UNIT *derived_unit; // unit for tvc_select
- if (mysql_new_select(lex, 1, tvc_sl))
+ if (!(derived_unit= new (thd->mem_root) SELECT_LEX_UNIT()))
goto err;
- tvc_select= lex->current_select;
- derived_unit= tvc_select->master_unit();
- tvc_select->linkage= DERIVED_TABLE_TYPE;
+ derived_unit->init_query();
+ derived_unit->thd= thd;
+ derived_unit->include_down(wrapper_sl);
- lex->current_select= wrapper_sl;
+ /*
+ Attach the select used of TVC as the only slave to the unit for
+ the derived table tvc_x of the transformation
+ */
+ derived_unit->add_slave(tvc_sl);
+ tvc_sl->linkage= DERIVED_TABLE_TYPE;
/*
- Create the name of the wrapping derived table and
- add it to the FROM list of the wrapper
- */
+ Generate the name of the derived table created for TVC and
+ add it to the FROM list of the wrapping select
+ */
Table_ident *ti;
LEX_CSTRING alias;
TABLE_LIST *derived_tab;
@@ -710,10 +727,6 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->table_list.first->derived_type= DTYPE_TABLE | DTYPE_MATERIALIZE;
lex->derived_tables|= DERIVED_SUBQUERY;
- wrapper_sl->where= 0;
- wrapper_sl->set_braces(false);
- derived_unit->set_with_clause(0);
-
if (arena)
thd->restore_active_arena(arena, &backup);
thd->lex->result= save_result;
1
0