diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result index c0c67707231c4..db1bc190feff4 100644 --- a/mysql-test/main/subselect4.result +++ b/mysql-test/main/subselect4.result @@ -3373,4 +3373,50 @@ CREATE TABLE t(c INT); SELECT (SELECT 0 GROUP BY c HAVING (SELECT c)) FROM t GROUP BY c; (SELECT 0 GROUP BY c HAVING (SELECT c)) DROP TABLE t; +# +# MDEV-35168 subselects with outer references to derived tables may be incorrectly evaluated as constant +# +create table t1 (a int); +insert into t1 (a) values (5); +select 1 as one from +( +t1 as t2 left join +( +select 0 as c1 +from t1 +) as dt1 +on dt1.c1 = t2.a +) +where exists +( +select 1 from t1 +where dt1.c1 <> t1.a +); +one +create view v1 as ( select 0 as c1 from t1 ); +select 1 as one from +( +t1 left join v1 +on v1.c1 = t1.a +) +where exists +( +select 1 from t1 +where v1.c1 <> t1.a +); +one +with cte1 as ( select 0 as c1 from t1 ) +select 1 as one from +( +t1 left join cte1 +on cte1.c1 = t1.a +) +where exists +( +select 1 from t1 +where cte1.c1 <> t1.a +); +one +drop view v1; +drop table t1; # End of 10.11 tests diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test index 083da1647b27c..e0e28698346ae 100644 --- a/mysql-test/main/subselect4.test +++ b/mysql-test/main/subselect4.test @@ -2695,4 +2695,54 @@ CREATE TABLE t(c INT); SELECT (SELECT 0 GROUP BY c HAVING (SELECT c)) FROM t GROUP BY c; DROP TABLE t; +--echo # +--echo # MDEV-35168 subselects with outer references to derived tables may be incorrectly evaluated as constant +--echo # + +create table t1 (a int); +insert into t1 (a) values (5); + +select 1 as one from + ( + t1 as t2 left join + ( + select 0 as c1 + from t1 + ) as dt1 + on dt1.c1 = t2.a + ) + where exists + ( + select 1 from t1 + where dt1.c1 <> t1.a + ); + +create view v1 as ( select 0 as c1 from t1 ); + +select 1 as one from + ( + t1 left join v1 + on v1.c1 = t1.a + ) + where exists + ( + select 1 from t1 + where v1.c1 <> t1.a + ); + +with cte1 as ( select 0 as c1 from t1 ) +select 1 as one from + ( + t1 left join cte1 + on cte1.c1 = t1.a + ) + where exists + ( + select 1 from t1 + where cte1.c1 <> t1.a + ); + +drop view v1; +drop table t1; + --echo # End of 10.11 tests diff --git a/sql/item.cc b/sql/item.cc index 0f7aff680f7de..217d649f0d5c4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3274,6 +3274,21 @@ bool Item_field::enumerate_field_refs_processor(void *arg) return FALSE; } + +bool Item_direct_view_ref::enumerate_table_refs_processor(void *arg) +{ + Field_fixer *ff= (Field_fixer*)arg; + st_select_lex *cmp= ff->new_parent->get_merged_into(); + + // our table is resolved in the select of interest + if (null_ref_table && + null_ref_table != NO_NULL_TABLE && + null_ref_table->pos_in_table_list->select_lex == cmp) + ff->used_tables |= null_ref_table->map; + return FALSE; +} + + bool Item_field::update_table_bitmaps_processor(void *arg) { update_table_bitmaps(); diff --git a/sql/item.h b/sql/item.h index 76bcf4f073af4..988067dbc942e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2303,6 +2303,7 @@ class Item :public Value_source, virtual bool update_table_bitmaps_processor(void *arg) { return 0; } virtual bool enumerate_field_refs_processor(void *arg) { return 0; } + virtual bool enumerate_table_refs_processor(void *arg) { return 0; } virtual bool mark_as_eliminated_processor(void *arg) { return 0; } virtual bool eliminate_subselect_processor(void *arg) { return 0; } virtual bool view_used_tables_processor(void *arg) { return 0; } @@ -6342,6 +6343,7 @@ class Item_direct_view_ref :public Item_direct_ref set_null_ref_table(); } + bool enumerate_table_refs_processor(void *arg) override; bool fix_fields(THD *, Item **) override; bool eq(const Item *item, const Eq_config &config) const override; Item *get_tmp_table_item(THD *thd) override @@ -8351,4 +8353,24 @@ inline void TABLE::use_all_stored_columns() bitmap_clear_bit(read_set, (*vf)->field_index); } + +class Field_fixer: public Field_enumerator +{ +public: + table_map used_tables; /* Collect used_tables here */ + st_select_lex *new_parent; /* Select we're in */ + void visit_field(Item_field *item) override + { + //for (TABLE_LIST *tbl= new_parent->leaf_tables; tbl; tbl= tbl->next_local) + //{ + // if (tbl->table == field->table) + // { + used_tables|= item->field->table->map; + // return; + // } + //} + //used_tables |= OUTER_REF_TABLE_BIT; + } +}; + #endif /* SQL_ITEM_INCLUDED */ diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 0378aad8537da..1a29421fd0aae 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -469,26 +469,6 @@ void Item_subselect::fix_after_pullout(st_select_lex *new_parent, } -class Field_fixer: public Field_enumerator -{ -public: - table_map used_tables; /* Collect used_tables here */ - st_select_lex *new_parent; /* Select we're in */ - void visit_field(Item_field *item) override - { - //for (TABLE_LIST *tbl= new_parent->leaf_tables; tbl; tbl= tbl->next_local) - //{ - // if (tbl->table == field->table) - // { - used_tables|= item->field->table->map; - // return; - // } - //} - //used_tables |= OUTER_REF_TABLE_BIT; - } -}; - - /* Recalculate used_tables_cache */ @@ -538,6 +518,14 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, fixer.used_tables= 0; fixer.new_parent= new_parent; upper->item->walk(&Item::enumerate_field_refs_processor, 0, &fixer); + /* + An reference (that can be null) might refer to a constant item + (that isn't and can't be null). + We need to update the table map to include tables that might be + null to avoid this subselect being marked as constant. + This information is in the Item_direct_view_ref wrapper. + */ + upper->item->walk(&Item::enumerate_table_refs_processor, 0, &fixer); used_tables_cache |= fixer.used_tables; upper->item->walk(&Item::update_table_bitmaps_processor, FALSE, NULL); /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 700cd18ae2b5c..7b49b02745780 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1714,6 +1714,16 @@ class st_select_lex: public st_select_lex_node TABLE_LIST *find_table(THD *thd, const LEX_CSTRING *db_name, const LEX_CSTRING *table_name); + + st_select_lex *get_merged_into() + { + st_select_lex *ret= this; + + while(ret->merged_into) + ret= ret->merged_into; + + return ret; + } }; typedef class st_select_lex SELECT_LEX;