revision-id: da88e1ec12b0ba39552bf54367c1bb3b89eac4a8 (mariadb-10.3.26-78-gda88e1e) parent(s): 59eda73eff1a22ac0373d818bc802c05e82b5449 author: Igor Babaev committer: Igor Babaev timestamp: 2021-02-10 23:38:52 -0800 message: MDEV-24840 Crash caused by query with IN subquery containing union of two table value costructors This bug affected queries with a [NOT] IN/ANY/ALL subquery whose top level unit contained several table value constructors. The problem appeared because the code of the function Item_subselect::fix_fields() that was responsible for wrapping table value constructors encountered at the top level unit of a [NOT] IN/ANY/ALL subquery did not take into account that the chain of the select objects comprising the unit were not immutable. Approved by Oleksandr Byelkin <sanja@mariadb.com> --- mysql-test/main/table_value_constr.result | 68 +++++++++++++++++++++++++++++++ mysql-test/main/table_value_constr.test | 33 +++++++++++++++ sql/item_subselect.cc | 6 ++- sql/item_subselect.h | 2 +- sql/sql_tvc.cc | 11 ++--- 5 files changed, 113 insertions(+), 7 deletions(-) diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result index 69c75dd..d7b3286 100644 --- a/mysql-test/main/table_value_constr.result +++ b/mysql-test/main/table_value_constr.result @@ -2674,4 +2674,72 @@ values (1,2); values ((select min(a), max(b) from t1)); ERROR 21000: Operand should contain 1 column(s) drop table t1; +# +# MDEV-24840: union of TVCs in IN subquery +# +create table t1 (a int) engine=myisam; +insert into t1 values (3), (7), (1); +select a from t1 where a in (values (7) union values (8)); +a +7 +explain extended select a from t1 where a in (values (7) union values (8)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +4 DEPENDENT SUBQUERY <derived2> ref key0 key0 4 func 2 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +5 DEPENDENT UNION <derived3> ref key0 key0 4 func 2 100.00 +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#4 */ select `tvc_0`.`7` from (values (7)) `tvc_0` where <cache>(`test`.`t1`.`a`) = `tvc_0`.`7` union /* select#5 */ select `tvc_0`.`8` from (values (8)) `tvc_0` where <cache>(`test`.`t1`.`a`) = `tvc_0`.`8`))) +prepare stmt from "select a from t1 where a in (values (7) union values (8))"; +execute stmt; +a +7 +execute stmt; +a +7 +deallocate prepare stmt; +select a from t1 where a not in (values (7) union values (8)); +a +3 +1 +explain extended select a from t1 where a not in (values (7) union values (8)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +4 DEPENDENT SUBQUERY <derived2> ALL NULL NULL NULL NULL 2 100.00 Using where +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +5 DEPENDENT UNION <derived3> ALL NULL NULL NULL NULL 2 100.00 Using where +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where !<expr_cache><`test`.`t1`.`a`>(<in_optimizer>(`test`.`t1`.`a`,<exists>(/* select#4 */ select `tvc_0`.`7` from (values (7)) `tvc_0` where trigcond(<cache>(`test`.`t1`.`a`) = `tvc_0`.`7`) union /* select#5 */ select `tvc_0`.`8` from (values (8)) `tvc_0` where trigcond(<cache>(`test`.`t1`.`a`) = `tvc_0`.`8`)))) +select a from t1 where a < all(values (7) union values (8)); +a +3 +1 +explain extended select a from t1 where a < all(values (7) union values (8)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +5 UNION <derived3> ALL NULL NULL NULL NULL 2 100.00 +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <not>(<in_optimizer>(`test`.`t1`.`a`,<min>(/* select#4 */ select `tvc_0`.`7` from (values (7)) `tvc_0` union /* select#5 */ select `tvc_0`.`8` from (values (8)) `tvc_0`) <= <cache>(`test`.`t1`.`a`))) +select a from t1 where a >= any(values (7) union values (8)); +a +7 +explain extended select a from t1 where a >= any(values (7) union values (8)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +4 SUBQUERY <derived2> ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +5 UNION <derived3> ALL NULL NULL NULL NULL 2 100.00 +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <nop>(<in_optimizer>(`test`.`t1`.`a`,<min>(/* select#4 */ select `tvc_0`.`7` from (values (7)) `tvc_0` union /* select#5 */ select `tvc_0`.`8` from (values (8)) `tvc_0`) <= <cache>(`test`.`t1`.`a`))) +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 e8697be..bac85ff 100644 --- a/mysql-test/main/table_value_constr.test +++ b/mysql-test/main/table_value_constr.test @@ -1401,4 +1401,37 @@ values ((select min(a), max(b) from t1)); drop table t1; +--echo # +--echo # MDEV-24840: union of TVCs in IN subquery +--echo # + +create table t1 (a int) engine=myisam; +insert into t1 values (3), (7), (1); + +let $q= +select a from t1 where a in (values (7) union values (8)); +eval $q; +eval explain extended $q; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +let $q= +select a from t1 where a not in (values (7) union values (8)); +eval $q; +eval explain extended $q; + +let $q= +select a from t1 where a < all(values (7) union values (8)); +eval $q; +eval explain extended $q; + +let $q= +select a from t1 where a >= any(values (7) union values (8)); +eval $q; +eval explain extended $q; + +drop table t1; + --echo End of 10.3 tests diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d882918..6a30012 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -269,7 +269,11 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) { if (sl->tvc) { - wrap_tvc_into_select(thd, sl); + if (!(sl= wrap_tvc_into_select(thd, sl))) + { + res= TRUE; + goto end; + } } } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index fdc39f1..4e0a963 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -271,7 +271,7 @@ class Item_subselect :public Item_result_field, Item* build_clone(THD *thd) { return 0; } Item* get_copy(THD *thd) { return 0; } - bool wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl); + st_select_lex *wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl); friend class select_result_interceptor; friend class Item_in_optimizer; diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 1f91539..78607b6 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -779,11 +779,12 @@ st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl) SELECT * FROM (VALUES (v1), ... (vn)) tvc_x and replaces the subselect with the result of the transformation. - @retval false if successfull - true otherwise + @retval wrapping select if successful + 0 otherwise */ -bool Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) +st_select_lex * +Item_subselect::wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl) { LEX *lex= thd->lex; /* SELECT_LEX object where the transformation is performed */ @@ -794,12 +795,12 @@ bool 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 false; + return wrapper_sl; } else { lex->current_select= parent_select; - return true; + return 0; } }