From 7e34954b5ea44b2ce74f1f66eb80bbdad2e61d68 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Thu, 17 Jul 2003 19:39:31 +0300 Subject: [PATCH] new optimisation for ref_null (SCRUM) (WL#818) --- mysql-test/r/subselect.result | 15 +++++++- mysql-test/t/subselect.test | 6 +++- sql/item_cmpfunc.h | 1 + sql/item_func.h | 2 +- sql/item_subselect.cc | 22 ++++++++++-- sql/item_subselect.h | 8 +++-- sql/sql_select.cc | 67 ++++++++++++++++++++++------------- sql/sql_select.h | 1 + 8 files changed, 90 insertions(+), 32 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index a5eb79005ca..e941c115b2c 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -828,8 +828,21 @@ a t1.a in (select t2.a from t2) explain SELECT t1.a, t1.a in (select t2.a from t2) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 Using index +2 DEPENDENT SUBQUERY t2 index_in a a 5 const 2 Using where; Using index +CREATE TABLE t3 (a int(11) default '0'); +INSERT INTO t3 VALUES (1),(2),(3); +SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; +a t1.a in (select t2.a from t2,t3 where t3.a=t2.a) +1 1 +2 1 +3 1 +4 0 +explain SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 Using index 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 const 2 Using where; Using index -drop table t1,t2; +2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where +drop table t1,t2,t3; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); ERROR 42000: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 5a00bf42451..9ceed286063 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -469,7 +469,11 @@ INSERT INTO t1 VALUES (1),(2),(3),(4); INSERT INTO t2 VALUES (1),(2),(3); SELECT t1.a, t1.a in (select t2.a from t2) FROM t1; explain SELECT t1.a, t1.a in (select t2.a from t2) FROM t1; -drop table t1,t2; +CREATE TABLE t3 (a int(11) default '0'); +INSERT INTO t3 VALUES (1),(2),(3); +SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; +explain SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1; +drop table t1,t2,t3; #LIMIT is not supported now create table t1 (a float); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 03775bbf375..d0748269c77 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -669,6 +669,7 @@ public: Item_is_not_null_test(Item_in_subselect* ow, Item *a) :Item_func_isnull(a), owner(ow) {} + enum Functype functype() const { return ISNOTNULLTEST_FUNC; } longlong val_int(); const char *func_name() const { return "is_not_null_test"; } void update_used_tables(); diff --git a/sql/item_func.h b/sql/item_func.h index 30a499c2a52..65f80b26021 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -41,7 +41,7 @@ public: GE_FUNC,GT_FUNC,FT_FUNC, LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, BETWEEN, IN_FUNC, - INTERVAL_FUNC, + INTERVAL_FUNC, ISNOTNULLTEST_FUNC, SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC, SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC, SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC, diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 894173f4575..3d2a88d29e5 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -999,8 +999,15 @@ int subselect_indexin_engine::exec() DBUG_ENTER("subselect_indexin_engine::exec"); int error; TABLE *table= tab->table; + ((Item_in_subselect *) item)->value= 0; - if ((tab->ref.key_err= (*tab->ref.key_copy)->copy())) + if (check_null) + { + *tab->null_ref_key= 0; + ((Item_in_subselect *) item)->was_null= 0; + } + + if ((*tab->ref.key_copy) && (tab->ref.key_err= (*tab->ref.key_copy)->copy())) { table->status= STATUS_NOT_FOUND; error= -1; @@ -1022,12 +1029,21 @@ int subselect_indexin_engine::exec() { if (!cond || cond->val_int()) { - ((Item_in_subselect *) item)->value= 1; + if (check_null && *tab->null_ref_key) + ((Item_in_subselect *) item)->was_null= 1; + else + ((Item_in_subselect *) item)->value= 1; goto finish; } } else - goto finish; + { + if (!check_null || *tab->null_ref_key) + goto finish; + *tab->null_ref_key= 1; + if (safe_index_read(tab)) + goto finish; + } error= table->file->index_next_same(table->record[0], tab->ref.key_buff, tab->ref.key_length); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 5749220629f..e2738102ebd 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -228,6 +228,7 @@ public: friend class Item_asterisk_remover; friend class Item_ref_null_helper; friend class Item_is_not_null_test; + friend class subselect_indexin_engine; }; /* ALL/ANY/SOME subselect */ @@ -337,10 +338,13 @@ public: class subselect_indexin_engine: public subselect_simplein_engine { + bool check_null; public: subselect_indexin_engine(THD *thd, st_join_table *tab_arg, - Item_subselect *subs, Item *where) - :subselect_simplein_engine(thd, tab_arg, subs, where) + Item_subselect *subs, Item *where, + bool chk_null) + :subselect_simplein_engine(thd, tab_arg, subs, where), + check_null(chk_null) {} int exec(); }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dca5f2019fe..3c5c53422ae 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -456,7 +456,7 @@ err: bool JOIN::test_in_subselect(Item **where) { if (conds->type() == Item::FUNC_ITEM && - ((class Item_func *)this->conds)->functype() == Item_func::EQ_FUNC && + ((Item_func *)this->conds)->functype() == Item_func::EQ_FUNC && ((Item_func *)conds)->arguments()[0]->type() == Item::REF_ITEM && ((Item_func *)conds)->arguments()[1]->type() == Item::FIELD_ITEM) { @@ -763,38 +763,57 @@ JOIN::optimize() /* is this simple IN subquery? */ - if (!group_list && !order && !having && + if (!group_list && !order && unit->item && unit->item->substype() == Item_subselect::IN_SUBS && tables == 1 && conds && !unit->first_select()->next_select()) { - Item *where= 0; - if (join_tab[0].type == JT_EQ_REF) + if (!having) { - if (test_in_subselect(&where)) + Item *where= 0; + if (join_tab[0].type == JT_EQ_REF) { - join_tab[0].type= JT_SIMPLE_IN; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new subselect_simplein_engine(thd, join_tab, - unit->item, - where))); + if (test_in_subselect(&where)) + { + join_tab[0].type= JT_SIMPLE_IN; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new subselect_simplein_engine(thd, + join_tab, + unit->item, + where))); + } } - } - else if (join_tab[0].type == JT_REF) + else if (join_tab[0].type == JT_REF) + { + if (test_in_subselect(&where)) + { + join_tab[0].type= JT_INDEX_IN; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new subselect_indexin_engine(thd, + join_tab, + unit->item, + where, + 0))); + } + } + } else if (join_tab[0].type == JT_REF_OR_NULL && + having->type() == Item::FUNC_ITEM && + ((Item_func *) having)->functype() == + Item_func::ISNOTNULLTEST_FUNC) { - if (test_in_subselect(&where)) - { - join_tab[0].type= JT_INDEX_IN; - error= 0; - DBUG_RETURN(unit->item-> - change_engine(new subselect_indexin_engine(thd, join_tab, - unit->item, - where))); - } + join_tab[0].type= JT_INDEX_IN; + error= 0; + DBUG_RETURN(unit->item-> + change_engine(new subselect_indexin_engine(thd, + join_tab, + unit->item, + conds, + 1))); } + } - /* Need to tell Innobase that to play it safe, it should fetch all columns of the tables: this is because MySQL may build row @@ -5445,7 +5464,7 @@ int report_error(TABLE *table, int error) } -static int safe_index_read(JOIN_TAB *tab) +int safe_index_read(JOIN_TAB *tab) { int error; TABLE *table= tab->table; diff --git a/sql/sql_select.h b/sql/sql_select.h index e7e22b0107d..7b5aa49b9aa 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -411,3 +411,4 @@ bool cp_buffer_from_ref(TABLE_REF *ref); bool error_if_full_join(JOIN *join); void relink_tables(SELECT_LEX *select_lex); int report_error(TABLE *table, int error); +int safe_index_read(JOIN_TAB *tab);