Skip to content

Commit

Permalink
MDEV-32320: Server crashes at TABLE::add_tmp_key
Browse files Browse the repository at this point in the history
The code inside Item_subselect::fix_fields() could fail to check
that left expression had an Item_row, like this:

  (('x', 1.0) ,1) IN (SELECT 'x', 1.23 FROM ... UNION ...)

In order to hit the failure, the first SELECT of the subquery had
to be a degenerate no-tables select. In this case, execution will
not enter into Item_in_subselect::create_row_in_to_exists_cond()
and will not check if left_expr is composed of scalars.

But the subquery is a UNION so as a whole it is not degenerate.
We try to create an expression cache for the subquery.
We create a temp.table from left_expr columns. No field is created
for the Item_row. Then, we crash when trying to add an index over a
non-existent field.

Fixed by moving the left_expr cardinality check to a point in
check_and_do_in_subquery_rewrites() which gets executed for all
cases.
It's better to make the check early so we don't have to care about
subquery rewrite code hitting Item_row in left_expr.
  • Loading branch information
spetrunia committed Oct 15, 2023
1 parent d594f1e commit c886689
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
30 changes: 30 additions & 0 deletions mysql-test/main/subselect4.result
Original file line number Diff line number Diff line change
Expand Up @@ -3183,4 +3183,34 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 100
drop table t0, t1, t2;
#
# MDEV-32320: Server crashes at TABLE::add_tmp_key
#
select
( ( 'x' , 1.000000 ) , 1 )
IN
(SELECT
'x' , 'x'
WHERE ( 'x' )
UNION
SELECT 1 , 'x'
HAVING 1 != 1
) as T;
ERROR 21000: Operand should contain 2 column(s)
SELECT
EXISTS (
WITH x ( x ) AS ( SELECT 1 )
SELECT NULL
WHERE ( 1 , 1 ) =
(SELECT
1 , ( ( x , 1.000000 ) , 1 )
IN
(SELECT 'x' , 'x' WHERE ( ( 'x' ) )
UNION
SELECT 1 , x HAVING 1 != 1
)
FROM x
)
);
ERROR 21000: Operand should contain 2 column(s)
# End of 10.4 tests
34 changes: 34 additions & 0 deletions mysql-test/main/subselect4.test
Original file line number Diff line number Diff line change
Expand Up @@ -2596,4 +2596,38 @@ select * from t1 where t1.a in (select t2.a from t2 order by t2.b);

drop table t0, t1, t2;

--echo #
--echo # MDEV-32320: Server crashes at TABLE::add_tmp_key
--echo #

--error ER_OPERAND_COLUMNS
select
( ( 'x' , 1.000000 ) , 1 )
IN
(SELECT
'x' , 'x'
WHERE ( 'x' )
UNION
SELECT 1 , 'x'
HAVING 1 != 1
) as T;

--error ER_OPERAND_COLUMNS
SELECT
EXISTS (
WITH x ( x ) AS ( SELECT 1 )
SELECT NULL
WHERE ( 1 , 1 ) =
(SELECT
1 , ( ( x , 1.000000 ) , 1 )
IN
(SELECT 'x' , 'x' WHERE ( ( 'x' ) )
UNION
SELECT 1 , x HAVING 1 != 1
)
FROM x
)
);


--echo # End of 10.4 tests
7 changes: 0 additions & 7 deletions sql/item_subselect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2492,10 +2492,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(true);

Item *item_eq=
new (thd->mem_root)
Item_func_eq(thd, new (thd->mem_root)
Expand Down Expand Up @@ -2562,9 +2558,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(select_lex->ref_pointer_array[i]->type() == REF_ITEM &&
((Item_ref*)(select_lex->ref_pointer_array[i]))->ref_type() ==
Item_ref::OUTER_REF));
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->element_index(i)->cols()))
DBUG_RETURN(true);
item= new (thd->mem_root)
Item_func_eq(thd,
new (thd->mem_root)
Expand Down
8 changes: 8 additions & 0 deletions sql/opt_subselect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,14 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
my_error(ER_OPERAND_COLUMNS, MYF(0), in_subs->left_expr->cols());
DBUG_RETURN(-1);
}

uint cols_num= in_subs->left_expr->cols();
for (uint i= 0; i < cols_num; i++)
{
if (select_lex->ref_pointer_array[i]->
check_cols(in_subs->left_expr->element_index(i)->cols()))
DBUG_RETURN(-1);
}
}

DBUG_PRINT("info", ("Checking if subq can be converted to semi-join"));
Expand Down

0 comments on commit c886689

Please sign in to comment.