Skip to content

Commit b000d69

Browse files
committed
MDEV-23221: A subquery causes crash
* Fix the crash: IN-to-EXISTS rewrite causes an error (and so JOIN::optimize() fails with an error, too), don't call update_used_tables(). Terminate the query execution instead. * Fix the cause of the error in the IN-to-EXISTS rewrite: don't do the rewrite if doing it will cause an error of this kind: This version of MariaDB doesn't yet support 'SUBQUERY in ROW in left expression of IN/ALL/ANY' * Fix another issue exposed by this testcase: JOIN::setup_subquery_caches() may be invoked before any select has saved its query plan, and will crash because none of the SELECTs has called create_explain_query_if_not_exists() to create the Explain Data Structure for this SELECT. TODO: When merging this to 10.2, remove the poorly-placed call to create_explain_query_if_not_exists made by fix for M_D_E_V-16153
1 parent 4b97f14 commit b000d69

File tree

5 files changed

+139
-4
lines changed

5 files changed

+139
-4
lines changed

mysql-test/r/subselect_exists2in.result

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,4 +972,74 @@ id
972972
DROP PROCEDURE p1;
973973
DROP TABLE t1;
974974
# End of 10.0 tests
975+
#
976+
# MDEV-23221: A subquery causes crash
977+
#
978+
create table t1 (
979+
location_code varchar(10),
980+
country_id varchar(10)
981+
);
982+
insert into t1 values ('HKG', 'HK');
983+
insert into t1 values ('NYC', 'US');
984+
insert into t1 values ('LAX', 'US');
985+
create table t2 (
986+
container_id varchar(10),
987+
cntr_activity_type varchar(10),
988+
cntr_dest varchar(10)
989+
);
990+
insert into t2 values ('AAAA1111', 'VSL', 'NYC');
991+
insert into t2 values ('AAAA1111', 'CUV', 'NYC');
992+
insert into t2 values ('BBBB2222', 'VSL', 'LAX');
993+
insert into t2 values ('BBBB2222', 'XYZ', 'LAX');
994+
# Must not crash or return an error:
995+
select
996+
(select country_id from t1 where location_code = cl1.cntr_dest) as dest_cntry,
997+
(select
998+
max(container_id)
999+
from t2 as cl2
1000+
where
1001+
cl2.container_id = cl1.container_id and
1002+
cl2.cntr_activity_type = 'CUV' and
1003+
exists (select location_code
1004+
from t1
1005+
where
1006+
location_code = cl2.cntr_dest and
1007+
country_id = dest_cntry)
1008+
) as CUV
1009+
from
1010+
t2 cl1;
1011+
dest_cntry CUV
1012+
US AAAA1111
1013+
US AAAA1111
1014+
US NULL
1015+
US NULL
1016+
prepare s from "select
1017+
(select country_id from t1 where location_code = cl1.cntr_dest) as dest_cntry,
1018+
(select
1019+
max(container_id)
1020+
from t2 as cl2
1021+
where
1022+
cl2.container_id = cl1.container_id and
1023+
cl2.cntr_activity_type = 'CUV' and
1024+
exists (select location_code
1025+
from t1
1026+
where
1027+
location_code = cl2.cntr_dest and
1028+
country_id = dest_cntry)
1029+
) as CUV
1030+
from
1031+
t2 cl1";
1032+
execute s;
1033+
dest_cntry CUV
1034+
US AAAA1111
1035+
US AAAA1111
1036+
US NULL
1037+
US NULL
1038+
execute s;
1039+
dest_cntry CUV
1040+
US AAAA1111
1041+
US AAAA1111
1042+
US NULL
1043+
US NULL
1044+
drop table t1,t2;
9751045
set optimizer_switch=default;

mysql-test/t/subselect_exists2in.test

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,5 +828,53 @@ DROP TABLE t1;
828828

829829
--echo # End of 10.0 tests
830830

831+
--echo #
832+
--echo # MDEV-23221: A subquery causes crash
833+
--echo #
834+
create table t1 (
835+
location_code varchar(10),
836+
country_id varchar(10)
837+
);
838+
insert into t1 values ('HKG', 'HK');
839+
insert into t1 values ('NYC', 'US');
840+
insert into t1 values ('LAX', 'US');
841+
842+
create table t2 (
843+
container_id varchar(10),
844+
cntr_activity_type varchar(10),
845+
cntr_dest varchar(10)
846+
);
847+
insert into t2 values ('AAAA1111', 'VSL', 'NYC');
848+
insert into t2 values ('AAAA1111', 'CUV', 'NYC');
849+
insert into t2 values ('BBBB2222', 'VSL', 'LAX');
850+
insert into t2 values ('BBBB2222', 'XYZ', 'LAX');
851+
852+
let $query=
853+
select
854+
(select country_id from t1 where location_code = cl1.cntr_dest) as dest_cntry,
855+
(select
856+
max(container_id)
857+
from t2 as cl2
858+
where
859+
cl2.container_id = cl1.container_id and
860+
cl2.cntr_activity_type = 'CUV' and
861+
exists (select location_code
862+
from t1
863+
where
864+
location_code = cl2.cntr_dest and
865+
country_id = dest_cntry)
866+
) as CUV
867+
from
868+
t2 cl1;
869+
870+
--echo # Must not crash or return an error:
871+
eval $query;
872+
873+
eval prepare s from "$query";
874+
execute s;
875+
execute s;
876+
877+
drop table t1,t2;
878+
831879
#restore defaults
832880
set optimizer_switch=default;

sql/item_subselect.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,13 +2727,16 @@ bool Item_exists_subselect::select_prepare_to_be_in()
27272727
Check if 'func' is an equality in form "inner_table.column = outer_expr"
27282728
27292729
@param func Expression to check
2730+
@param allow_subselect If true, the outer_expr part can have a subquery
2731+
If false, it cannot.
27302732
@param local_field OUT Return "inner_table.column" here
27312733
@param outer_expr OUT Return outer_expr here
27322734
27332735
@return true - 'func' is an Equality.
27342736
*/
27352737

27362738
static bool check_equality_for_exist2in(Item_func *func,
2739+
bool allow_subselect,
27372740
Item_ident **local_field,
27382741
Item **outer_exp)
27392742
{
@@ -2744,7 +2747,8 @@ static bool check_equality_for_exist2in(Item_func *func,
27442747
args= func->arguments();
27452748
if (args[0]->real_type() == Item::FIELD_ITEM &&
27462749
args[0]->all_used_tables() != OUTER_REF_TABLE_BIT &&
2747-
args[1]->all_used_tables() == OUTER_REF_TABLE_BIT)
2750+
args[1]->all_used_tables() == OUTER_REF_TABLE_BIT &&
2751+
(allow_subselect || !args[1]->has_subquery()))
27482752
{
27492753
/* It is Item_field or Item_direct_view_ref) */
27502754
DBUG_ASSERT(args[0]->type() == Item::FIELD_ITEM ||
@@ -2755,7 +2759,8 @@ static bool check_equality_for_exist2in(Item_func *func,
27552759
}
27562760
else if (args[1]->real_type() == Item::FIELD_ITEM &&
27572761
args[1]->all_used_tables() != OUTER_REF_TABLE_BIT &&
2758-
args[0]->all_used_tables() == OUTER_REF_TABLE_BIT)
2762+
args[0]->all_used_tables() == OUTER_REF_TABLE_BIT &&
2763+
(allow_subselect || !args[0]->has_subquery()))
27592764
{
27602765
/* It is Item_field or Item_direct_view_ref) */
27612766
DBUG_ASSERT(args[1]->type() == Item::FIELD_ITEM ||
@@ -2784,6 +2789,13 @@ typedef struct st_eq_field_outer
27842789
27852790
outer1=inner_tbl1.col1 AND ... AND outer2=inner_tbl1.col2 AND remainder_cond
27862791
2792+
if there is just one outer_expr=inner_expr pair, then outer_expr can have a
2793+
subselect in it. If there are many such pairs, then none of outer_expr can
2794+
have a subselect in it. If we allow this, the query will fail with an error:
2795+
2796+
This version of MariaDB doesn't yet support 'SUBQUERY in ROW in left
2797+
expression of IN/ALL/ANY'
2798+
27872799
@param conds Condition to be checked
27882800
@parm result Array to collect EQ_FIELD_OUTER elements describing
27892801
inner-vs-outer equalities the function has found.
@@ -2801,14 +2813,17 @@ static bool find_inner_outer_equalities(Item **conds,
28012813
{
28022814
List_iterator<Item> li(*((Item_cond*)*conds)->argument_list());
28032815
Item *item;
2816+
bool allow_subselect= true;
28042817
while ((item= li++))
28052818
{
28062819
if (item->type() == Item::FUNC_ITEM &&
28072820
check_equality_for_exist2in((Item_func *)item,
2821+
allow_subselect,
28082822
&element.local_field,
28092823
&element.outer_exp))
28102824
{
28112825
found= TRUE;
2826+
allow_subselect= false;
28122827
element.eq_ref= li.ref();
28132828
if (result.append(element))
28142829
goto alloc_err;
@@ -2817,6 +2832,7 @@ static bool find_inner_outer_equalities(Item **conds,
28172832
}
28182833
else if ((*conds)->type() == Item::FUNC_ITEM &&
28192834
check_equality_for_exist2in((Item_func *)*conds,
2835+
true,
28202836
&element.local_field,
28212837
&element.outer_exp))
28222838
{

sql/sql_lex.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3802,7 +3802,8 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
38023802
sl->options|= SELECT_DESCRIBE;
38033803
inner_join->select_options|= SELECT_DESCRIBE;
38043804
}
3805-
res= inner_join->optimize();
3805+
if ((res= inner_join->optimize()))
3806+
return TRUE;
38063807
if (!inner_join->cleaned)
38073808
sl->update_used_tables();
38083809
sl->update_correlated_cache();

sql/sql_select.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1055,11 +1055,11 @@ int JOIN::optimize()
10551055
if (optimization_state != JOIN::NOT_OPTIMIZED)
10561056
return FALSE;
10571057
optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS;
1058+
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
10581059

10591060
int res= optimize_inner();
10601061
if (!res && have_query_plan != QEP_DELETED)
10611062
{
1062-
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
10631063
have_query_plan= QEP_AVAILABLE;
10641064
save_explain_data(thd->lex->explain, false /* can overwrite */,
10651065
need_tmp,

0 commit comments

Comments
 (0)