diff options
author | unknown <monty@mashka.mysql.fi> | 2003-06-26 05:38:19 +0300 |
---|---|---|
committer | unknown <monty@mashka.mysql.fi> | 2003-06-26 05:38:19 +0300 |
commit | a3beaaa3af7af26aabadda6836618d32d6c90e51 (patch) | |
tree | af26f54d96f10ded8e53e3634bc9beecf75bd15e /sql | |
parent | 3d5f6a8867c848459191ba320b573bd832e51d5a (diff) | |
download | mariadb-git-a3beaaa3af7af26aabadda6836618d32d6c90e51.tar.gz |
LEFT JOIN optimization: Change LEFT JOIN to normal join if possible
mysql-test/r/select.result:
Added test for LEFT JOIN optimization
mysql-test/t/select.test:
Added test for LEFT JOIN optimization
sql/item.h:
LEFT JOIN optimization
sql/item_cmpfunc.cc:
LEFT JOIN optimization
sql/item_cmpfunc.h:
LEFT JOIN optimization
sql/item_func.cc:
LEFT JOIN optimization
sql/item_func.h:
LEFT JOIN optimization
sql/item_strfunc.cc:
LEFT JOIN optimization
sql/sql_base.cc:
Heart of LEFT JOIN optimization
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.h | 17 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 41 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 18 | ||||
-rw-r--r-- | sql/item_func.cc | 14 | ||||
-rw-r--r-- | sql/item_func.h | 9 | ||||
-rw-r--r-- | sql/item_strfunc.cc | 17 | ||||
-rw-r--r-- | sql/sql_base.cc | 16 |
7 files changed, 101 insertions, 31 deletions
diff --git a/sql/item.h b/sql/item.h index 09d428509d0..1631bf76135 100644 --- a/sql/item.h +++ b/sql/item.h @@ -71,7 +71,24 @@ public: virtual double val_result() { return val(); } virtual longlong val_int_result() { return val_int(); } virtual String *str_result(String* tmp) { return val_str(tmp); } + /* bit map of tables used by item */ virtual table_map used_tables() const { return (table_map) 0L; } + /* + Return table map of tables that can't be NULL tables (tables that are + used in a context where if they would contain a NULL row generated + by a LEFT or RIGHT join, the item would not be true). + This expression is used on WHERE item to determinate if a LEFT JOIN can be + converted to a normal join. + Generally this function should return used_tables() if the function + would return null if any of the arguments are null + As this is only used in the beginning of optimization, the value don't + have to be updated in update_used_tables() + */ + virtual table_map not_null_tables() const { return used_tables(); } + /* + Returns true if this is a simple constant item like an integer, not + a constant expression + */ virtual bool basic_const_item() const { return 0; } virtual Item *new_item() { return 0; } /* Only for const items */ virtual cond_result eq_cmp_result() const { return COND_OK; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3344f2bc01d..770fe3c0232 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -292,10 +292,12 @@ void Item_func_interval::fix_length_and_dec() } } maybe_null=0; max_length=2; - used_tables_cache|=item->used_tables(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); with_sum_func= with_sum_func || item->with_sum_func; } + void Item_func_interval::split_sum_func(List<Item> &fields) { if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) @@ -1073,8 +1075,9 @@ void Item_func_in::fix_length_and_dec() } maybe_null= item->maybe_null; max_length=2; - used_tables_cache|=item->used_tables(); - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); + const_item_cache&= item->const_item(); } @@ -1174,14 +1177,21 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables) char buff[sizeof(char*)]; // Max local vars in function used_tables_cache=0; const_item_cache=0; + /* + and_table_cache is the value that Item_cond_or() returns for + not_null_tables() + */ + and_tables_cache= ~(table_map) 0; if (thd && check_stack_overrun(thd,buff)) return 0; // Fatal error flag is set! while ((item=li++)) { + table_map tmp_table_map; while (item->type() == Item::COND_ITEM && ((Item_cond*) item)->functype() == functype()) { // Identical function + li.replace(((Item_cond*) item)->list); ((Item_cond*) item)->list.empty(); #ifdef DELETE_ITEMS @@ -1193,9 +1203,12 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables) item->top_level_item(); if (item->fix_fields(thd,tables)) return 1; /* purecov: inspected */ - used_tables_cache|=item->used_tables(); - with_sum_func= with_sum_func || item->with_sum_func; - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + tmp_table_map= item->not_null_tables(); + not_null_tables_cache|= tmp_table_map; + and_tables_cache&= tmp_table_map; + const_item_cache&= item->const_item(); + with_sum_func= with_sum_func || item->with_sum_func; if (item->maybe_null) maybe_null=1; } @@ -1234,17 +1247,19 @@ Item_cond::used_tables() const return used_tables_cache; } + void Item_cond::update_used_tables() { - used_tables_cache=0; - const_item_cache=1; List_iterator_fast<Item> li(list); Item *item; + + used_tables_cache=0; + const_item_cache=1; while ((item=li++)) { item->update_used_tables(); - used_tables_cache|=item->used_tables(); - const_item_cache&= item->const_item(); + used_tables_cache|= item->used_tables(); + const_item_cache&= item->const_item(); } } @@ -1348,12 +1363,16 @@ Item *and_expressions(Item *a, Item *b, Item **org_item) { Item_cond *res; if ((res= new Item_cond_and(a, (Item*) b))) + { res->used_tables_cache= a->used_tables() | b->used_tables(); + res->not_null_tables_cache= a->not_null_tables() | b->not_null_tables(); + } return res; } if (((Item_cond_and*) a)->add((Item*) b)) return 0; ((Item_cond_and*) a)->used_tables_cache|= b->used_tables(); + ((Item_cond_and*) a)->not_null_tables_cache|= b->not_null_tables(); return a; } @@ -1489,6 +1508,8 @@ Item_func_regex::fix_fields(THD *thd,TABLE_LIST *tables) max_length=1; decimals=0; binary=args[0]->binary || args[1]->binary; used_tables_cache=args[0]->used_tables() | args[1]->used_tables(); + not_null_tables_cache= (args[0]->not_null_tables() | + args[1]->not_null_tables()); const_item_cache=args[0]->const_item() && args[1]->const_item(); if (!regex_compiled && args[1]->const_item()) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index f7ade97940c..536ac9dc3d4 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -204,7 +204,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "ifnull"; } - unsigned int size_of() { return sizeof(*this);} + table_map not_null_tables() const { return 0; } }; @@ -224,7 +224,7 @@ public: } void fix_length_and_dec(); const char *func_name() const { return "if"; } - unsigned int size_of() { return sizeof(*this);} + table_map not_null_tables() const { return 0; } }; @@ -239,7 +239,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "nullif"; } - unsigned int size_of() { return sizeof(*this);} + table_map not_null_tables() const { return 0; } }; @@ -254,9 +254,10 @@ public: void fix_length_and_dec(); enum Item_result result_type () const { return cached_result_type; } const char *func_name() const { return "coalesce"; } - unsigned int size_of() { return sizeof(*this);} + table_map not_null_tables() const { return 0; } }; + class Item_func_case :public Item_func { Item * first_expr, *else_expr; @@ -270,6 +271,7 @@ public: String *val_str(String *); void fix_length_and_dec(); void update_used_tables(); + table_map not_null_tables() const { return 0; } enum Item_result result_type () const { return cached_result_type; } const char *func_name() const { return "case"; } void print(String *str); @@ -479,10 +481,12 @@ public: } } } + table_map not_null_tables() const { return 0; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } unsigned int size_of() { return sizeof(*this);} }; + class Item_func_isnotnull :public Item_bool_func { public: @@ -495,9 +499,10 @@ public: } const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } - unsigned int size_of() { return sizeof(*this);} + table_map not_null_tables() const { return 0; } }; + class Item_func_like :public Item_bool_func2 { char escape; @@ -572,6 +577,8 @@ class Item_cond :public Item_bool_func protected: List<Item> list; bool abort_on_null; + table_map and_tables_cache; + public: /* Item_cond() is only used to create top level items */ Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; } @@ -611,6 +618,7 @@ public: enum Functype functype() const { return COND_OR_FUNC; } longlong val_int(); const char *func_name() const { return "or"; } + table_map not_null_tables() const { return and_tables_cache; } }; diff --git a/sql/item_func.cc b/sql/item_func.cc index e847b203006..23bec0c3c81 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -61,7 +61,7 @@ Item_func::fix_fields(THD *thd,TABLE_LIST *tables) Item **arg,**arg_end; char buff[STACK_BUFF_ALLOC]; // Max argument in function binary=0; - used_tables_cache=0; + used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; if (thd && check_stack_overrun(thd,buff)) @@ -78,8 +78,9 @@ Item_func::fix_fields(THD *thd,TABLE_LIST *tables) if (item->binary) binary=1; with_sum_func= with_sum_func || item->with_sum_func; - used_tables_cache|=item->used_tables(); - const_item_cache&= item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache|= item->not_null_tables(); + const_item_cache&= item->const_item(); } } fix_length_and_dec(); @@ -122,6 +123,13 @@ table_map Item_func::used_tables() const return used_tables_cache; } + +table_map Item_func::not_null_tables() const +{ + return not_null_tables_cache; +} + + void Item_func::print(String *str) { str->append(func_name()); diff --git a/sql/item_func.h b/sql/item_func.h index 68e5335dc7e..8a4cace0b87 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -34,7 +34,7 @@ protected: Item **args,*tmp_arg[2]; public: uint arg_count; - table_map used_tables_cache; + table_map used_tables_cache, not_null_tables_cache; bool const_item_cache; enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, @@ -97,6 +97,7 @@ public: bool fix_fields(THD *,struct st_table_list *); void make_field(Send_field *field); table_map used_tables() const; + table_map not_null_tables() const; void update_used_tables(); bool eq(const Item *item, bool binary_cmp) const; virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; } @@ -588,7 +589,8 @@ public: void split_sum_func(List<Item> &fields); void update_used_tables() { - item->update_used_tables() ; Item_func::update_used_tables(); + item->update_used_tables(); + Item_func::update_used_tables(); used_tables_cache|= item->used_tables(); const_item_cache&= item->const_item(); } @@ -597,6 +599,7 @@ public: { maybe_null=0; max_length=3; used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; } @@ -736,6 +739,7 @@ public: return res; } Item_result result_type () const { return udf.result_type(); } + table_map not_null_tables() const { return 0; } unsigned int size_of() { return sizeof(*this);} }; @@ -969,6 +973,7 @@ public: } enum Functype functype() const { return FT_FUNC; } void update_used_tables() {} + table_map not_null_tables() const { return 0; } bool fix_fields(THD *thd,struct st_table_list *tlist); bool eq(const Item *, bool binary_cmp) const; longlong val_int() { return val()!=0.0; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 208be1ecd7f..20496a47671 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -606,9 +606,10 @@ void Item_func_concat_ws::fix_length_and_dec() max_length=MAX_BLOB_WIDTH; maybe_null=1; } - used_tables_cache|=separator->used_tables(); - const_item_cache&=separator->const_item(); - with_sum_func= with_sum_func || separator->with_sum_func; + used_tables_cache|= separator->used_tables(); + not_null_tables_cache&= separator->not_null_tables(); + const_item_cache&= separator->const_item(); + with_sum_func= with_sum_func || separator->with_sum_func; } void Item_func_concat_ws::update_used_tables() @@ -1509,8 +1510,9 @@ void Item_func_elt::fix_length_and_dec() } maybe_null=1; // NULL if wrong first arg with_sum_func= with_sum_func || item->with_sum_func; - used_tables_cache|=item->used_tables(); - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); + const_item_cache&= item->const_item(); } @@ -1591,8 +1593,9 @@ void Item_func_make_set::fix_length_and_dec() max_length=arg_count-1; for (uint i=1 ; i < arg_count ; i++) max_length+=args[i]->max_length; - used_tables_cache|=item->used_tables(); - const_item_cache&=item->const_item(); + used_tables_cache|= item->used_tables(); + not_null_tables_cache&= item->not_null_tables(); + const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 3bfd5e14d43..43718e5d93b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1903,11 +1903,11 @@ bool setup_tables(TABLE_LIST *tables) table->used_fields=0; table->const_table=0; - table->outer_join=table->null_row=0; + table->null_row=0; table->status=STATUS_NO_RECORD; table->keys_in_use_for_query= table->keys_in_use; table->used_keys= table->keys_for_keyread; - table->maybe_null=test(table->outer_join=table_list->outer_join); + table->maybe_null=test(table->outer_join= table_list->outer_join); table->tablenr=tablenr; table->map= (table_map) 1 << tablenr; table->force_index= table_list->force_index; @@ -2027,6 +2027,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) { + table_map not_null_tables= 0; DBUG_ENTER("setup_conds"); thd->set_query_id=1; thd->cond_count=0; @@ -2036,6 +2037,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) thd->where="where clause"; if ((*conds)->fix_fields(thd,tables)) DBUG_RETURN(1); + not_null_tables= (*conds)->not_null_tables(); } /* Check if we are using outer joins */ @@ -2049,9 +2051,15 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) DBUG_RETURN(1); thd->cond_count++; - /* If it's a normal join, add the ON/USING expression to the WHERE */ - if (!table->outer_join) + /* + If it's a normal join or a LEFT JOIN which can be optimized away + add the ON/USING expression to the WHERE + */ + if (!table->outer_join || + ((table->table->map & not_null_tables) && + !(specialflag & SPECIAL_NO_NEW_FUNC))) { + table->outer_join= 0; if (!(*conds=and_conds(*conds, table->on_expr))) DBUG_RETURN(1); table->on_expr=0; |