Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions mysql-test/main/subselect_extra.result
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,26 @@ a b
DROP VIEW v2;
DROP TABLE t1,t2;
set optimizer_switch= @tmp_subselect_extra_derived;
#
# MDEV-35168: Potential Bug in Database Handling of NULL Values in EXISTS Clause
#
create table t0 (vkey int);
insert into t0 (vkey) values (5);
select 1 as c0
from
((select
0 as c_0
from
t0
) as subq_2
right outer join t0 as ref_6
on (subq_2.c_0 = ref_6.vkey ))
where exists (
select
1
from
t0
where (subq_2.c_0 <> t0.vkey));
c0
drop table t0;
# End of 10.11 tests
24 changes: 24 additions & 0 deletions mysql-test/main/subselect_extra.test
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,27 @@ DROP VIEW v2;
DROP TABLE t1,t2;

set optimizer_switch= @tmp_subselect_extra_derived;

--echo #
--echo # MDEV-35168: Potential Bug in Database Handling of NULL Values in EXISTS Clause
--echo #
create table t0 (vkey int);
insert into t0 (vkey) values (5);
select 1 as c0
from
((select
0 as c_0
from
t0
) as subq_2
right outer join t0 as ref_6
on (subq_2.c_0 = ref_6.vkey ))
where exists (
select
1
from
t0
where (subq_2.c_0 <> t0.vkey));
drop table t0;

--echo # End of 10.11 tests
23 changes: 23 additions & 0 deletions mysql-test/main/subselect_extra_no_semijoin.result
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,29 @@ a b
DROP VIEW v2;
DROP TABLE t1,t2;
set optimizer_switch= @tmp_subselect_extra_derived;
#
# MDEV-35168: Potential Bug in Database Handling of NULL Values in EXISTS Clause
#
create table t0 (vkey int);
insert into t0 (vkey) values (5);
select 1 as c0
from
((select
0 as c_0
from
t0
) as subq_2
right outer join t0 as ref_6
on (subq_2.c_0 = ref_6.vkey ))
where exists (
select
1
from
t0
where (subq_2.c_0 <> t0.vkey));
c0
drop table t0;
# End of 10.11 tests
set optimizer_switch= @subselect_extra_no_sj_tmp;
set @optimizer_switch_for_subselect_extra_test=null;
#
Expand Down
1 change: 1 addition & 0 deletions sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2455,6 +2455,7 @@ class Item :public Value_source,
*/
virtual bool check_index_dependence(void *arg) { return 0; }
virtual bool check_sequence_privileges(void *arg) { return 0; }
virtual bool where_exists_processor(void *arg) { return 0; }
/*============== End of Item processor list ======================*/

/*
Expand Down
6 changes: 6 additions & 0 deletions sql/item_subselect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3505,6 +3505,12 @@ bool Item_exists_subselect::fix_fields(THD *thd, Item **ref)
}


bool Item_exists_subselect::where_exists_processor(void *arg)
{
return walk(&Item::enumerate_field_refs_processor, true, arg);
}


bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
uint outer_cols_num;
Expand Down
2 changes: 2 additions & 0 deletions sql/item_subselect.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ class Item_exists_subselect :public Item_subselect
void under_not(Item_func_not *upper) override { upper_not= upper; };

void set_exists_transformed() { exists_transformed= TRUE; }
bool where_exists_processor(void *arg) override;

friend class select_exists_subselect;
friend class subselect_uniquesubquery_engine;
Expand Down Expand Up @@ -783,6 +784,7 @@ class Item_in_subselect :public Item_exists_subselect
void init_subq_materialization_tracker(THD *thd);
Subq_materialization_tracker *get_materialization_tracker() const
{ return materialization_tracker; }
bool where_exists_processor(void *arg) override { return 0; }

friend class Item_ref_null_helper;
friend class Item_is_not_null_test;
Expand Down
93 changes: 92 additions & 1 deletion sql/table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9827,6 +9827,92 @@ static inline bool derived_table_optimization_done(TABLE_LIST *table)
}


/*
Queries of the form
SELECT ... FROM (SELECT constant AS alias_N FROM t0) dt ... WHERE EXISTS
(SELECT ... WHERE (dt.alias_N ...));
must force derived table dt to be materialized, or the WHERE EXISTS will
not filter rows correctly. If we allow derived table dt to be merged,
then references to dt.alias_N are replaced with their constant values
directly, so a WHERE EXISTS subquery will attempt to filter rows from the
outer query based on those constant values rather than the columns'
values computed during outer query evaluation.

This can't be done later, during DT_MERGE, because by that point the WHERE
EXISTS subquery has already had its WHERE clause updated with the field
from the merged query and it's impossible to detect that the merge should
be prevented by that time. Doing this here prevents merging from occurring
in any case.
*/
static bool where_exists_depends_on_mergeable_derived(TABLE_LIST *derived,
SELECT_LEX *select_lex)
{
if (!derived->on_expr || !select_lex->where)
return false;

/*
The WhereExistsVisitor visits the fields of the WHERE clause within a
subquery of an outer WHERE EXISTS clause. For each field found, it
checks to see if the same field is referenced in the derived table and
if so, blocks derived table merging.
*/
class WhereExistsVisitor : public Field_enumerator
{
struct DerivedTableVisitor : public Field_enumerator
{
WhereExistsVisitor *outer{nullptr};
Item_field *where_exists_field{nullptr};

void visit_field(Item_field *derived_table_field) override
{
if (outer->block_merging || !derived_table_field->field_name)
return;
outer->block_merging=
(derived_table_field->field_name.streq(where_exists_field->field_name) &&
derived_table_field->table_name.streq(where_exists_field->table_name));
}

public:
DerivedTableVisitor(WhereExistsVisitor *wev, Item_field *field)
: outer(wev)
, where_exists_field(field)
{
DBUG_ASSERT(outer);
DBUG_ASSERT(field);
}
};

Item *dt_expr{nullptr};

void visit_field(Item_field *where_exists_field) override
{
if (!dt_expr || !where_exists_field->field_name)
return;
DerivedTableVisitor dt_visitor(this, where_exists_field);
dt_expr->walk(&Item::enumerate_field_refs_processor,
true, &dt_visitor);
}

public:
bool block_merging{false};

WhereExistsVisitor(Item *derived_expr)
: dt_expr(derived_expr)
{
DBUG_ASSERT(dt_expr);
}
};

// Visit each field in the WHERE clause of the subquery in the WHERE EXISTS
// and check to see if any field references a constant field from the given
// derived table of the outer query.
WhereExistsVisitor visitor(derived->on_expr);
select_lex->where->walk(&Item::where_exists_processor,
true, &visitor);
return visitor.block_merging;
}


/**
@brief
Initialize this derived table/view
Expand Down Expand Up @@ -9886,8 +9972,13 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)

if (!derived_table_optimization_done(this))
{
const bool force_materialization=
where_exists_depends_on_mergeable_derived(this,
select_lex);

/* A subquery might be forced to be materialized due to a side-effect. */
if (!is_materialized_derived() && unit->can_be_merged() &&
if (!force_materialization && !is_materialized_derived() &&
unit->can_be_merged() &&
/*
Following is special case of
SELECT * FROM (<limited-select>) WHERE ROWNUM() <= nnn
Expand Down