From 3938ac543601aee49d3137621293302f2b577640 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 26 Nov 2003 17:23:52 -0800 Subject: [PATCH 01/10] This ChangeSet Introdices Item_equal. The objects of this class represent multiple conjunctive equalities in where conditions: =(f1,f2,...fn) <=> f1=f2 and f2= ... and =fn. The objects are used to generate new possibale paths to access the tables when executing a query. They are also used to optimize the execution plan chosen by the optimizer for the query. --- sql/item.cc | 47 ++- sql/item.h | 26 +- sql/item_cmpfunc.cc | 233 ++++++++++++- sql/item_cmpfunc.h | 63 ++++ sql/item_func.cc | 34 ++ sql/item_func.h | 8 +- sql/item_row.cc | 12 + sql/item_row.h | 1 + sql/item_strfunc.h | 8 + sql/opt_range.cc | 216 ++++++++---- sql/opt_sum.cc | 15 + sql/sql_list.h | 34 +- sql/sql_select.cc | 801 ++++++++++++++++++++++++++++++++++++++++++-- sql/sql_select.h | 2 + 14 files changed, 1394 insertions(+), 106 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 0c5b6450b4a..5091ea87bee 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -299,7 +299,8 @@ bool DTCollation::aggregate(DTCollation &dt) return 0; } -Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name) +Item_field::Item_field(Field *f) + :Item_ident(NullS,f->table_name,f->field_name), item_equal(0) { set_field(f); collation.set(DERIVATION_IMPLICIT); @@ -313,6 +314,7 @@ Item_field::Item_field(THD *thd, Item_field &item) result_field(item.result_field) { collation.set(DERIVATION_IMPLICIT); + item_equal= item.item_equal; } void Item_field::set_field(Field *field_par) @@ -969,6 +971,49 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return 0; } +Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) +{ + Item_equal *item= 0; + while (cond_equal) + { + List_iterator_fast li(cond_equal->current_level); + while ((item= li++)) + { + if (item->contains(field)) + return item; + } + cond_equal= cond_equal->parent_level; + } + return item; +} + +Item *Item_field::equal_fields_propagator(byte *arg) +{ + COND_EQUAL *cond_equal= (COND_EQUAL *) arg; + item_equal= find_item_equal(cond_equal); + Item *item= 0; + if (item_equal) + item= item_equal->get_const(); + if (item) + item->fixed= 0; + else + item= this; + return item; +} + +bool Item_field::replace_equal_field_processor(byte *arg) +{ + if (item_equal) + { + Item_field *subst= item_equal->get_first(); + if (subst && !field->eq(subst->field)) + { + field= subst->field; + return 0; + } + } + return 0; +} void Item::init_make_field(Send_field *tmp_field, enum enum_field_types field_type) diff --git a/sql/item.h b/sql/item.h index 0a0db619e1a..c10f7f7f9f2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -83,6 +83,7 @@ public: }; typedef bool (Item::*Item_processor)(byte *arg); +typedef Item* (Item::*Item_calculator) (byte *arg); class Item { Item(const Item &); /* Prevent use of these */ @@ -201,8 +202,15 @@ public: return (this->*processor)(arg); } + virtual Item* traverse(Item_calculator calculator, byte *arg) + { + return (this->*calculator)(arg); + } + virtual bool remove_dependence_processor(byte * arg) { return 0; } virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; } + virtual Item *equal_fields_propagator(byte * arg) { return this; } + virtual bool replace_equal_field_processor(byte * arg) { return 0; } virtual Item *this_item() { return this; } /* For SPs mostly. */ virtual Item *this_const_item() const { return const_cast(this); } /* For SPs mostly. */ @@ -311,17 +319,21 @@ public: bool remove_dependence_processor(byte * arg); }; +class Item_equal; +class COND_EQUAL; class Item_field :public Item_ident { void set_field(Field *field); public: Field *field,*result_field; + Item_equal *item_equal; // Item_field() {} Item_field(const char *db_par,const char *table_name_par, const char *field_name_par) - :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0) + :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0), + item_equal(0) { collation.set(DERIVATION_IMPLICIT); } // Constructor need to process subselect with temporary tables (see Item) Item_field(THD *thd, Item_field &item); @@ -355,6 +367,9 @@ public: bool get_time(TIME *ltime); bool is_null() { return field->is_null(); } Item *get_tmp_table_item(THD *thd); + Item_equal *find_item_equal(COND_EQUAL *cond_equal); + Item *equal_fields_propagator(byte *arg); + bool replace_equal_field_processor(byte *arg); friend class Item_default_value; friend class Item_insert_value; }; @@ -897,6 +912,15 @@ public: return arg->walk(processor, args) || (this->*processor)(args); } + + Item *traverse(Item_calculator calculator, byte *args) + { + Item *new_item= arg->traverse(calculator, args); + if (!new_item) + return 0; + arg= new_item; + return (this->*calculator)(args); + } }; class Item_insert_value : public Item_field diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1629ed6aa80..76cec7b6615 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -225,7 +225,7 @@ void Item_bool_func2::fix_length_and_dec() } // Make a special case of compare with fields to get nicer DATE comparisons - if (args[0]->type() == FIELD_ITEM) + if (args[0]->type() == FIELD_ITEM && !args[0]->const_item()) { Field *field=((Item_field*) args[0])->field; if (field->store_for_compare()) @@ -238,7 +238,7 @@ void Item_bool_func2::fix_length_and_dec() } } } - if (args[1]->type() == FIELD_ITEM) + if (args[1]->type() == FIELD_ITEM && !args[1]->const_item()) { Field *field=((Item_field*) args[1])->field; if (field->store_for_compare()) @@ -1712,6 +1712,21 @@ bool Item_cond::walk(Item_processor processor, byte *arg) return Item_func::walk(processor, arg); } +Item *Item_cond::traverse(Item_calculator calculator, byte *arg) +{ + List_iterator li(list); + Item *item; + while ((item= li++)) + { + Item *new_item= item->traverse(calculator, arg); + if (!new_item) + return 0; + if (new_item != item) + li.replace(new_item); + } + return Item_func::traverse(calculator, arg); +} + void Item_cond::split_sum_func(Item **ref_pointer_array, List &fields) { List_iterator li(list); @@ -2499,3 +2514,217 @@ Item *Item_cond_or::neg_transformer() /* NOT(a OR b OR ...) -> */ neg_arguments(); return new Item_cond_and(list); } + +Item_equal::Item_equal(Item_field *f1, Item_field *f2) + : Item_bool_func(), const_item(0), eval_item(0), cond_false(0) +{ + const_item_cache= 0; + fields.push_back(f1); + fields.push_back(f2); +} + +Item_equal::Item_equal(Item *c, Item_field *f) + : Item_bool_func(), eval_item(0), cond_false(0) +{ + const_item_cache= 0; + fields.push_back(f); + const_item= c; +} + +Item_equal::Item_equal(Item_equal *item_equal) + : Item_bool_func(), eval_item(0), cond_false(0) +{ + const_item_cache= 0; + List_iterator_fast li(item_equal->fields); + Item_field *item; + while ((item= li++)) + { + fields.push_back(item); + } + const_item= item_equal->const_item; + cond_false= item_equal->cond_false; +} + +void Item_equal::add(Item *c) +{ + if (cond_false) + return; + if (!const_item) + { + const_item= c; + return; + } + Item_func_eq *func= new Item_func_eq(c, const_item); + func->set_cmp_func(); + cond_false = !(func->val_int()); +} + +void Item_equal::add(Item_field *f) +{ + fields.push_back(f); +} + +bool Item_equal::contains(Field *field) +{ + List_iterator_fast it(fields); + Item_field *item; + while ((item= it++)) + { + if (field->eq(item->field)) + return 1; + } + return 0; +} + +void Item_equal::merge(Item_equal *item) +{ + fields.concat(&item->fields); + Item *c= item->const_item; + if (c) + { + /* + The flag cond_false will be set to 1 after this, if + the multiple equality already contains a constant and its + value is not equal to the value of c. + */ + add(const_item); + } + cond_false|= item->cond_false; +} + +void Item_equal::sort(void *table_join_idx) +{ + bool swap; + void **idx= (void **) table_join_idx; + List_iterator it(fields); + do + { + Item_field *item1= it++; + Item_field **ref1= it.ref(); + Item_field *item2; + Item_field **ref2; + + if (!item1) + break; + swap= FALSE; + while ((item2= it++)) + { + ref2= it.ref(); + if (idx[item1->field->table->tablenr] > + idx[item2->field->table->tablenr]) + { + Item_field *item= *ref1; + *ref1= *ref2; + *ref2= item; + swap= TRUE; + } + else + { + item1= item2; + ref1= ref2; + } + } + it.rewind(); + } while (swap); +} + +bool Item_equal::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +{ + List_iterator_fast li(fields); + Item *item; + not_null_tables_cache= used_tables_cache= 0; + const_item_cache= 0; + while ((item=li++)) + { + table_map tmp_table_map; + used_tables_cache|= item->used_tables(); + tmp_table_map= item->not_null_tables(); + not_null_tables_cache|= tmp_table_map; + if (item->maybe_null) + maybe_null=1; + } + fix_length_and_dec(); + fixed= 1; + return 0; +} + +void Item_equal::update_used_tables() +{ + List_iterator_fast li(fields); + Item *item; + not_null_tables_cache= used_tables_cache= 0; + while ((item=li++)) + { + item->update_used_tables(); + used_tables_cache|= item->used_tables(); + const_item_cache&= item->const_item(); + } +} + +longlong Item_equal::val_int() +{ + if (cond_false) + return 0; + List_iterator_fast it(fields); + Item *item= const_item ? const_item : it++; + if ((null_value= item->null_value)) + return 0; + eval_item->store_value(item); + while((item= it++)) + { + if ((null_value= item->null_value) || eval_item->cmp(item)) + return 0; + } + return 1; +} + +void Item_equal::fix_length_and_dec() +{ + Item *item= const_item ? const_item : get_first(); + eval_item= cmp_item::get_comparator(item); + if (item->result_type() == STRING_RESULT) + eval_item->cmp_charset= cmp_collation.collation; +} + +bool Item_equal::walk(Item_processor processor, byte *arg) +{ + List_iterator_fast it(fields); + Item *item; + while ((item= it++)) + if (item->walk(processor, arg)) + return 1; + return Item_func::walk(processor, arg); +} + +Item *Item_equal::traverse(Item_calculator calculator, byte *arg) +{ + List_iterator it(fields); + Item *item; + while ((item= it++)) + { + Item *new_item= item->traverse(calculator, arg); + if (!new_item) + return 0; + if (new_item != item) + it.replace((Item_field *) new_item); + } + return Item_func::traverse(calculator, arg); +} + +void Item_equal::print(String *str) +{ + str->append(func_name()); + str->append('('); + List_iterator_fast it(fields); + Item *item; + if ((item= it++)) + item->print(str); + while ((item= it++)) + { + str->append(','); + str->append(' '); + item->print(str); + } + str->append(')'); +} + diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index dac7a2d43eb..cf14fbc17ea 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -908,13 +908,76 @@ public: void top_level_item() { abort_on_null=1; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, byte *arg); + Item *traverse(Item_calculator calculator, byte *arg); void neg_arguments(); }; +class Item_equal: public Item_bool_func +{ + List fields; /* list of equal field items */ + Item *const_item; /* optional constant item equal to fields items */ + cmp_item *eval_item; + bool cond_false; + DTCollation cmp_collation; +public: + inline Item_equal() + : Item_bool_func(), const_item(0), eval_item(0), cond_false(0) + { const_item_cache=0 ;} + Item_equal(Item_field *f1, Item_field *f2); + Item_equal(Item *c, Item_field *f); + Item_equal(Item_equal *item_equal); + inline Item* get_const() { return const_item; } + void add(Item *c); + void add(Item_field *f); + bool is_false() { return cond_false; } + bool contains(Field *field); + Item_field* get_first() { return fields.head(); } + void merge(Item_equal *item); + enum Functype functype() const { return MULT_EQUAL_FUNC; } + longlong val_int(); + const char *func_name() const { return "multiple equal"; } + optimize_type select_optimize() const { return OPTIMIZE_EQUAL; } + void sort(void *table_join_idx); + friend class Item_equal_iterator; + void fix_length_and_dec(); + bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); + void update_used_tables(); + bool walk(Item_processor processor, byte *arg); + Item *traverse(Item_calculator calculator, byte *arg); + void print(String *str); +}; + +class COND_EQUAL +{ +public: + COND_EQUAL *parent_level; + List current_level; + COND_EQUAL() { parent_level= 0; } +}; + + +class Item_equal_iterator :List_iterator_fast +{ +public: + inline Item_equal_iterator(Item_equal &item_equal) + :List_iterator_fast (item_equal.fields) + {} + inline Item_field* operator++(int) + { + Item_field *item= (*(List_iterator_fast *) this)++; + return item; + } + inline void rewind(void) + { + List_iterator_fast::rewind(); + } +}; + class Item_cond_and :public Item_cond { public: + COND_EQUAL cond_equal; Item_cond_and() :Item_cond() {} Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {} diff --git a/sql/item_func.cc b/sql/item_func.cc index 7e03f8c9c45..4337ee631f4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -242,6 +242,40 @@ bool Item_func::walk (Item_processor processor, byte *argument) return (this->*processor)(argument); } +Item *Item_func::traverse(Item_calculator calculator, byte *argument) +{ + if (arg_count) + { + Item **arg,**arg_end; + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + Item *new_item= (*arg)->traverse(calculator, argument); + if (!new_item) + return 0; + *arg= new_item; + } + } + return (this->*calculator)(argument); +} + +Item *Item_func::equal_fields_propagator(byte *argument) +{ + if (arg_count) + { + Item **arg,**arg_end; + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + if (!(*arg)->fixed) + { + fix_fields(current_thd, 0, 0); + break; + } + } + } + return this; +} + + void Item_func::split_sum_func(Item **ref_pointer_array, List &fields) { Item **arg, **arg_end; diff --git a/sql/item_func.h b/sql/item_func.h index d3cfaf63b5a..f92eea1bc94 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -40,7 +40,8 @@ public: enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, 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, + COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, + BETWEEN, IN_FUNC, MULT_EQUAL_FUNC, INTERVAL_FUNC, ISNOTNULLTEST_FUNC, SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC, SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC, @@ -49,7 +50,8 @@ public: SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, NOT_FUNC, NOT_ALL_FUNC, GUSERVAR_FUNC}; - enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL }; + enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL, + OPTIMIZE_EQUAL }; enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } Item_func(void): @@ -146,6 +148,8 @@ public: bool agg_arg_collations_for_comparison(DTCollation &c, Item **items, uint nitems); bool walk(Item_processor processor, byte *arg); + Item *traverse(Item_calculator calculator, byte *arg); + Item *equal_fields_propagator(byte *arg); }; diff --git a/sql/item_row.cc b/sql/item_row.cc index 89b38c8a753..d7afe9ad5f0 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -140,6 +140,18 @@ bool Item_row::walk(Item_processor processor, byte *arg) return (this->*processor)(arg); } +Item *Item_row::traverse(Item_calculator calculator, byte *arg) +{ + for (uint i= 0; i < arg_count; i++) + { + Item *new_item= items[i]->traverse(calculator, arg); + if (!new_item) + return 0; + items[i]= new_item; + } + return (this->*calculator)(arg); +} + void Item_row::bring_value() { for (uint i= 0; i < arg_count; i++) diff --git a/sql/item_row.h b/sql/item_row.h index a09bd1a2c31..de6c18bf0d9 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -71,6 +71,7 @@ public: void print(String *str); bool walk(Item_processor processor, byte *arg); + Item *traverse(Item_calculator calculator, byte *arg); uint cols() { return arg_count; } Item* el(uint i) { return items[i]; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index a7949511f02..f628160d868 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -427,6 +427,14 @@ public: return item->walk(processor, arg) || Item_str_func::walk(processor, arg); } + Item *traverse(Item_calculator calculator, byte *arg) + { + Item *new_item= item->traverse(calculator, arg); + if (!new_item) + return 0; + item= new_item; + return Item_str_func::traverse(calculator, arg); + } void print(String *str); }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e2761832e65..4626bffc3e2 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -762,11 +762,72 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_RETURN(records ? test(quick) : -1); } +static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, + Field *field, Item *value, + Item_result cmp_type) +{ + SEL_TREE *tree= 0; + DBUG_ENTER("get_func_mm_tree"); + + if (cond_func->functype() == Item_func::NE_FUNC) + { + + tree= get_mm_parts(param, field, Item_func::LT_FUNC, + value, cmp_type); + if (tree) + { + tree= tree_or(param, tree, get_mm_parts(param, field, + Item_func::GT_FUNC, + value, cmp_type)); + } + } + else if (cond_func->functype() == Item_func::BETWEEN) + { + + tree= get_mm_parts(param, field, Item_func::GE_FUNC, + cond_func->arguments()[1],cmp_type); + if (tree) + { + tree= tree_and(param, tree, get_mm_parts(param, field, + Item_func::LE_FUNC, + cond_func->arguments()[2], + cmp_type)); + } + } + else if (cond_func->functype() == Item_func::IN_FUNC) + { + Item_func_in *func=(Item_func_in*) cond_func; + tree= get_mm_parts(param, field, Item_func::EQ_FUNC, + func->arguments()[1], cmp_type); + if (tree) + { + for (uint i =2 ; i < func->argument_count() ; i++) + { + tree= tree_or(param, tree, get_mm_parts(param, field, + Item_func::EQ_FUNC, + func->arguments()[i], + cmp_type)); + } + } + } + else + { + Item_func::Functype func_type= + (value != cond_func->arguments()[0]) ? cond_func->functype() : + ((Item_bool_func2*) cond_func)->rev_functype(); + tree= get_mm_parts(param, field, func_type, value, cmp_type); + } + DBUG_RETURN(tree); +} + /* make a select tree of all keys in condition */ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) { SEL_TREE *tree=0; + SEL_TREE *ftree= 0; + Item_field *field_item= 0; + Item *value; DBUG_ENTER("get_mm_tree"); if (cond->type() == Item::COND_ITEM) @@ -814,9 +875,12 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE)); } - table_map ref_tables=cond->used_tables(); + table_map ref_tables= 0; + table_map param_comp= ~(param->prev_tables | param->read_tables | + param->current_table); if (cond->type() != Item::FUNC_ITEM) { // Should be a field + ref_tables= cond->used_tables(); if ((ref_tables & param->current_table) || (ref_tables & ~(param->prev_tables | param->read_tables))) DBUG_RETURN(0); @@ -828,76 +892,98 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) DBUG_RETURN(0); // Can't be calculated if (cond_func->functype() == Item_func::BETWEEN) - { + { if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) { - Field *field=((Item_field*) (cond_func->arguments()[0]))->field; - Item_result cmp_type=field->cmp_type(); - DBUG_RETURN(tree_and(param, - get_mm_parts(param, field, - Item_func::GE_FUNC, - cond_func->arguments()[1], cmp_type), - get_mm_parts(param, field, - Item_func::LE_FUNC, - cond_func->arguments()[2], cmp_type))); + field_item= (Item_field*) (cond_func->arguments()[0]); + value= NULL; } - DBUG_RETURN(0); + else + DBUG_RETURN(0); } - if (cond_func->functype() == Item_func::IN_FUNC) - { // COND OR + else if (cond_func->functype() == Item_func::IN_FUNC) + { Item_func_in *func=(Item_func_in*) cond_func; if (func->key_item()->type() == Item::FIELD_ITEM) { - Field *field=((Item_field*) (func->key_item()))->field; - Item_result cmp_type=field->cmp_type(); - tree= get_mm_parts(param,field,Item_func::EQ_FUNC, - func->arguments()[1],cmp_type); - if (!tree) - DBUG_RETURN(tree); // Not key field - for (uint i=2 ; i < func->argument_count(); i++) - { - SEL_TREE *new_tree=get_mm_parts(param,field,Item_func::EQ_FUNC, - func->arguments()[i],cmp_type); - tree=tree_or(param,tree,new_tree); - } - DBUG_RETURN(tree); + field_item= (Item_field*) (func->key_item()); + value= NULL; } - DBUG_RETURN(0); // Can't optimize this IN + else + DBUG_RETURN(0); } - - if (ref_tables & ~(param->prev_tables | param->read_tables | - param->current_table)) - DBUG_RETURN(0); // Can't be calculated yet - if (!(ref_tables & param->current_table)) - DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true - - /* check field op const */ - /* btw, ft_func's arguments()[0] isn't FIELD_ITEM. SerG*/ - if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) + else if (cond_func->functype() == Item_func::MULT_EQUAL_FUNC) { - tree= get_mm_parts(param, - ((Item_field*) (cond_func->arguments()[0]))->field, - cond_func->functype(), - cond_func->arg_count > 1 ? cond_func->arguments()[1] : - 0, - ((Item_field*) (cond_func->arguments()[0]))->field-> - cmp_type()); + Item_equal *item_equal= (Item_equal *) cond; + Item_equal_iterator it(*item_equal); + if (!(value= item_equal->get_const())) + value= it++; + while (value) + { + ref_tables= value->used_tables(); + Item_equal_iterator li(*item_equal); + while ((field_item= li++)) + { + if (field_item != value) + { + Field *field= field_item->field; + Item_result cmp_type= field->cmp_type(); + if (!((ref_tables | field->table->map) & param_comp)) + { + tree= get_mm_parts(param, field, Item_func::EQ_FUNC, + value,cmp_type); + ftree= !ftree ? tree : tree_and(param, ftree, tree); + } + } + } + if (item_equal->get_const()) + break; + value= it++; + } + DBUG_RETURN(ftree); } - /* check const op field */ - if (!tree && - cond_func->have_rev_func() && - cond_func->arguments()[1]->type() == Item::FIELD_ITEM) + else if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) { - DBUG_RETURN(get_mm_parts(param, - ((Item_field*) - (cond_func->arguments()[1]))->field, - ((Item_bool_func2*) cond_func)->rev_functype(), - cond_func->arguments()[0], - ((Item_field*) - (cond_func->arguments()[1]))->field->cmp_type() - )); + field_item= (Item_field*) (cond_func->arguments()[0]); + value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0; } - DBUG_RETURN(tree); + else if (cond_func->have_rev_func() && + cond_func->arguments()[1]->type() == Item::FIELD_ITEM) + { + field_item= (Item_field*) (cond_func->arguments()[1]); + value= cond_func->arguments()[0]; + } + else + DBUG_RETURN(0); + + for (uint i= 0; i < cond_func->arg_count; i++) + { + Item *arg= cond_func->arguments()[i]; + if (arg != field_item) + ref_tables|= arg->used_tables(); + } + Field *field= field_item->field; + Item_result cmp_type= field->cmp_type(); + if (!((ref_tables | field->table->map) & param_comp)) + ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type); + Item_equal *item_equal= field_item->item_equal; + if (item_equal) + { + Item_equal_iterator it(*item_equal); + Item_field *item; + while ((item= it++)) + { + Field *f= item->field; + if (field->eq(f)) + continue; + if (!((ref_tables | f->table->map) & param_comp)) + { + tree= get_func_mm_tree(param, cond_func, f, value, cmp_type); + ftree= !ftree ? tree : tree_and(param, ftree, tree); + } + } + } + DBUG_RETURN(ftree); } @@ -905,17 +991,10 @@ static SEL_TREE * get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, Item *value, Item_result cmp_type) { - bool ne_func= FALSE; DBUG_ENTER("get_mm_parts"); if (field->table != param->table) DBUG_RETURN(0); - if (type == Item_func::NE_FUNC) - { - ne_func= TRUE; - type= Item_func::LT_FUNC; - } - KEY_PART *key_part = param->key_parts; KEY_PART *end = param->key_parts_end; SEL_TREE *tree=0; @@ -951,13 +1030,6 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, } } - if (ne_func) - { - SEL_TREE *tree2= get_mm_parts(param, field, Item_func::GT_FUNC, - value, cmp_type); - if (tree2) - tree= tree_or(param,tree,tree2); - } DBUG_RETURN(tree); } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 932aceebdbb..ce650701347 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -352,6 +352,18 @@ static bool simple_pred(Item_func *func_item, Item **args, bool *inv_order) Item *item; *inv_order= 0; switch (func_item->argument_count()) { + case 0: + /* MULT_EQUAL_FUNC */ + { + Item_equal *item_equal= (Item_equal *) func_item; + Item_equal_iterator it(*item_equal); + args[0]= it++; + if (it++) + return 0; + if (!(args[1]= item_equal->get_const())) + return 0; + } + break; case 1: /* field IS NULL */ item= func_item->arguments()[0]; @@ -492,6 +504,9 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, case Item_func::BETWEEN: between= 1; break; + case Item_func::MULT_EQUAL_FUNC: + eq_type= 1; + break; default: return 0; // Can't optimize function } diff --git a/sql/sql_list.h b/sql/sql_list.h index 7200046e6c5..d7a62b7fd7d 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -121,10 +121,12 @@ public: void remove(list_node **prev) { list_node *node=(*prev)->next; - delete *prev; - *prev=node; if (!--elements) last= &first; + else if (last == &(*prev)->next) + last= prev; + delete *prev; + *prev=node; } inline void *pop(void) { @@ -135,6 +137,30 @@ public: last= &first; return tmp->info; } + inline void concat(base_list *list) + { + if (!list->is_empty()) + { + *last= list->first; + last= list->last; + elements+= list->elements; + } + } + inline void disjoin(base_list *list) + { + list_node **prev= &first; + list_node *node= first; + list_node *list_first= list->first; + elements=0; + while (node && node != list_first) + { + prev= &node->next; + node= node->next; + elements++; + } + *prev= *last; + last= prev; + } inline list_node* last_node() { return *last; } inline list_node* first_node() { return first;} inline void *head() { return first->info; } @@ -245,6 +271,8 @@ public: inline T* head() {return (T*) base_list::head(); } inline T** head_ref() {return (T**) base_list::head_ref(); } inline T* pop() {return (T*) base_list::pop(); } + inline void concat(List *list) { return base_list::concat(list); } + inline void disjoin(List *list) { return base_list::disjoin(list); } void delete_elements(void) { list_node *element,*next; @@ -265,6 +293,8 @@ public: inline T* operator++(int) { return (T*) base_list_iterator::next(); } inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); } inline T *replace(List &a) { return (T*) base_list_iterator::replace(a); } + inline void rewind(void) { base_list_iterator::rewind(); } + inline void remove() { base_list_iterator::remove(); } inline void after(T *a) { base_list_iterator::after(a); } inline T** ref(void) { return (T**) base_list_iterator::ref(); } }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index eea5ed72a3a..7c0464bec05 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -72,6 +72,11 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, uint select_options, const char *info, Item *having, Procedure *proc, SELECT_LEX_UNIT *unit); +static Item *flatten_condition(COND *cond); +static COND *build_all_equal_items(COND *cond, + COND_EQUAL *inherited); +static COND* substitute_for_best_equal_field(COND *cond, + void *table_join_idx); static COND *optimize_cond(COND *conds,Item::cond_result *cond_value); static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); @@ -526,6 +531,56 @@ JOIN::optimize() } #endif + /* eliminate NOT operators */ + conds= eliminate_not_funcs(conds); + DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); + + /* Eliminate nested AND/OR in conditions */ + if (conds) + conds= flatten_condition(conds); + { + TABLE_LIST *tables; + for (tables= tables_list; tables; tables= tables->next) + { + if (tables->on_expr) + tables->on_expr= flatten_condition(tables->on_expr); + } + } + + /* + Build all multiple equality predicates and eliminate equality + predicates that can be inferred from these multiple equalities. + For each reference of a field included into a multiple equality + that occurs in a function set a pointer to the multiple equality + predicate. Substitute a constant instead of this field if the + multiple equality contains a constant. + */ + if (conds) + { + conds= build_all_equal_items(conds, NULL); + conds->update_used_tables(); + if (conds->type() == Item::COND_ITEM && + ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) + cond_equal= &((Item_cond_and*) conds)->cond_equal; + else if (conds->type() == Item::FUNC_ITEM && + ((Item_cond*) conds)->functype() == Item_func::MULT_EQUAL_FUNC) + { + cond_equal= new COND_EQUAL; + cond_equal->current_level.push_back((Item_equal *) conds); + } + } + { + TABLE_LIST *tables; + for (tables= tables_list; tables; tables= tables->next) + { + if (tables->on_expr) + { + tables->on_expr= build_all_equal_items(tables->on_expr, cond_equal); + tables->on_expr->update_used_tables(); + } + } + } + conds= optimize_cond(conds,&cond_value); if (thd->net.report_error) { @@ -626,6 +681,30 @@ JOIN::optimize() } mysql_unlock_some_tables(thd, this->table, const_tables); } + /* + Among the equal fields belonging to the same multiple equality + choose the one that is to be retrieved first and substitute + all references to these in where condition for a reference for + the selected field. + */ + if (conds) + { + conds= substitute_for_best_equal_field(conds, map2table); + conds->update_used_tables(); + } + { + TABLE_LIST *tables; + for (tables= tables_list; tables; tables= tables->next) + { + if (tables->on_expr) + { + tables->on_expr= substitute_for_best_equal_field(tables->on_expr, + map2table); + tables->on_expr->update_used_tables(); + map2table[tables->table->tablenr]->on_expr= tables->on_expr; + } + } + } if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ @@ -2202,6 +2281,35 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, } +static void +add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, + Item_field *field_item, + bool eq_func, Item **val, + uint num_values, table_map usable_tables) +{ + Field *field= field_item->field; + add_key_field(key_fields, and_level, field, + eq_func, val, num_values, usable_tables); + Item_equal *item_equal= field_item->item_equal; + if (item_equal) + { + /* + Add to the set of possible key values every substitution of + the field for an equal field included into item_equal + */ + Item_equal_iterator it(*item_equal); + Item_field *item; + while ((item= it++)) + { + if (!field->eq(item->field)) + { + add_key_field(key_fields, and_level, item->field, + eq_func, val, num_values, usable_tables); + } + } + } +} + static void add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, COND *cond, table_map usable_tables) @@ -2247,11 +2355,11 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, // BETWEEN or IN if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) - add_key_field(key_fields,*and_level, - ((Item_field*) (cond_func->key_item()->real_item()))-> - field, 0, - cond_func->arguments()+1, cond_func->argument_count()-1, - usable_tables); + add_key_equal_fields(key_fields,*and_level, + (Item_field*) (cond_func->key_item()->real_item()), + 0, cond_func->arguments()+1, + cond_func->argument_count()-1, + usable_tables); break; case Item_func::OPTIMIZE_OP: { @@ -2261,21 +2369,19 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM && !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT)) { - add_key_field(key_fields,*and_level, - ((Item_field*) (cond_func->arguments()[0])->real_item()) - ->field, - equal_func, - cond_func->arguments()+1, 1, usable_tables); + add_key_equal_fields(key_fields, *and_level, + (Item_field*) (cond_func->arguments()[0])->real_item(), + equal_func, + cond_func->arguments()+1, 1, usable_tables); } if (cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && cond_func->functype() != Item_func::LIKE_FUNC && !(cond_func->arguments()[1]->used_tables() & OUTER_REF_TABLE_BIT)) { - add_key_field(key_fields,*and_level, - ((Item_field*) (cond_func->arguments()[1])->real_item()) - ->field, - equal_func, - cond_func->arguments(),1,usable_tables); + add_key_equal_fields(key_fields,*and_level, + (Item_field*) (cond_func->arguments()[1])->real_item(), + equal_func, + cond_func->arguments(),1,usable_tables); } break; } @@ -2287,15 +2393,55 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, Item *tmp=new Item_null; if (!tmp) // Should never be true return; - add_key_field(key_fields,*and_level, - ((Item_field*) (cond_func->arguments()[0])->real_item()) - ->field, + add_key_equal_fields(key_fields,*and_level, + (Item_field*) (cond_func->arguments()[0])->real_item(), cond_func->functype() == Item_func::ISNULL_FUNC, &tmp, 1, usable_tables); } break; + case Item_func::OPTIMIZE_EQUAL: + Item_equal *item_equal= (Item_equal *) cond; + Item *const_item= item_equal->get_const(); + Item_equal_iterator it(*item_equal); + Item_field *item; + if (const_item) + { + /* + For each field field1 from item_equal consider the equality + field1=const_item as a condition allowing an index access of the table + with field1 by the keys value of field1. + */ + while ((item= it++)) + { + add_key_field(key_fields, *and_level, item->field, + TRUE, &const_item, 1, usable_tables); + } + } + else + { + /* + Consider all pairs of different fields included into item_equal. + For each of them (field1, field1) consider the equality + field1=field2 as a condition allowing an index access of the table + with field1 by the keys value of field2. + */ + Item_equal_iterator fi(*item_equal); + while ((item= fi++)) + { + Field *field= item->field; + while ((item= it++)) + { + if (!field->eq(item->field)) + { + add_key_field(key_fields, *and_level, field, + TRUE, (Item **) &item, 1, usable_tables); + } + } + it.rewind(); + } + } + break; } - return; } /* @@ -2669,21 +2815,33 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, { uint keypart=keyuse->keypart; uint found_part_ref_or_null= KEY_OPTIMIZE_REF_OR_NULL; + bool usable= 0; + table_map best_part_found_ref= 0; + double best_prev_record_reads= DBL_MAX; do { if (!(rest_tables & keyuse->used_tables) && !(found_ref_or_null & keyuse->optimize)) { found_part|=keyuse->keypart_map; - found_ref|= keyuse->used_tables; + double tmp= prev_record_reads(join, + (table_map) (found_ref | keyuse->used_tables)); + if (tmp < best_prev_record_reads) + { + best_part_found_ref= keyuse->used_tables; + best_prev_record_reads= tmp; + } if (rec > keyuse->ref_table_rows) rec= keyuse->ref_table_rows; found_part_ref_or_null&= keyuse->optimize; + usable= 1; } keyuse++; - found_ref_or_null|= found_part_ref_or_null; + found_ref|= best_part_found_ref; } while (keyuse->table == table && keyuse->key == key && keyuse->keypart == keypart); + if (usable) + found_ref_or_null|= found_part_ref_or_null; } while (keyuse->table == table && keyuse->key == key); /* @@ -3394,7 +3552,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) } COND *tmp=make_cond_for_table(cond,used_tables,current_map); - if (!tmp && tab->quick) + if (!tmp && tab->quick && tab->type == JT_ALL) { // Outer join /* Hack to handle the case where we only refer to a table @@ -4089,6 +4247,583 @@ template class List; template class List_iterator; #endif +/* + Eliminate nesting in AND/OR subexpressions od a condition + + SYNOPSIS + flatten_condition() + cond condition where to eliminate nesting + + DESCRIPTION + The function traverse the condition and recursively eliminates + nesting for AND/OR subexpressions: + ... AND (p AND ... r) AND ... => ... AND p AND ... r AND ... + ... OR (p OR ... r) OR ... => ... OR p OR ... r OR ... + + NOTES + Nesting in AND/OR subexpresions inside of NOT/XOR formulas is not + eliminated. + + RETURN + The transformed condition +*/ +static Item *flatten_condition(COND *cond) +{ + if (cond->type() == Item::COND_ITEM) + { + Item_func::Functype functype= ((Item_cond*) cond)->functype(); + if (functype == Item_func::COND_AND_FUNC || + functype == Item_func::COND_OR_FUNC) + { + + List *args= ((Item_cond*) cond)->argument_list(); + List_iterator li(*args); + Item *item; + List list; + while ((item= li++)) + { + item= flatten_condition(item); + if (item->type() == Item::COND_ITEM && + ((Item_func*) item)->functype() == functype) + { + list.concat(((Item_cond*) item)->argument_list()); + li.remove(); + } + } + args->concat(&list); + } + } + return cond; +} + + +/* + Find the multiple equality predicate containing a field + + SYNOPSIS + find_item_equal() + cond_equal multiple equalities to search in + field field to look for + inherited_fl :out set up to TRUE iff multiple equality is found + on upper levels (not on current level of cond_equal) + + DESCRIPTION + The function retrieves the multiple equalities accessed through + the con_equal structure from current level and up looking for + an equality containing field. It stops retrieval as soon as the equality + is found and set up inherited_fl to TRUE if it's found on upper levels. + + RETURN + Item_equal for the found multiple equality predicate if a success; + NULL - otherwise. +*/ + +Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, + bool *inherited_fl) +{ + Item_equal *item= 0; + bool in_upper_level= FALSE; + while (cond_equal) + { + List_iterator_fast li(cond_equal->current_level); + while ((item= li++)) + { + if (item->contains(field)) + goto finish; + } + in_upper_level= TRUE; + cond_equal= cond_equal->parent_level; + } + in_upper_level= FALSE; +finish: + if (inherited_fl) + *inherited_fl= in_upper_level; + return item; +} + +/* + Check whether an item is a simple equality predicate and if so + create/find a multiple equality for this predicate + + SYNOPSIS + check_equality() + item item to check + cond_equal multiple equalities that must hold together with the predicate + + DESCRIPTION + This function first checks whether an item is a simple equality i.e. + the one that equates a field with another field or a constant. + If this is the case the function looks a for a multiple equality + in the lists referenced directly or indirectly by cond_equal. If it + doesn't find any it builds a multiple equality that covers + the predicate, i.e. the predicate can be inferred from it. The built + multiple equality could be obtained in such a way: create a binary + multiple equality equivalent to the predicate, than merge it, if + possible, with one of old multiple equalities. This guarantees that + the set of multiple equalities covering equality predicates will + be minimal. + + NOTES + Now only fields that have the same type defintions (verified by + the Field::eq_def method) are placed to the same multiple equalities. + Because of this some equality predicates are not eliminated and + can be used in constant propagation procedure. + We could weeken the equlity test as soon as at least one of the + equal fields is to be equal to a constant. It would require a + more complicated implementation: we would have to store, in + general case, its own constant for each fields from the multiple + equality. But at the same time it would allow us to get rid + of constant propagation completely: it would be done by the call + to build_all_equal_items. + + IMPLEMENTATION + The implementation does not follow exactly the above rules to + build a new multiple equality for the equality predicate. + If it processes the equality of the form field1=field2, it + looks for multiple equalities me1 containig field1 and me2 containing + field2. If only one of them is found the fuction expand it by + the lacking field. If multiple equalities for both fields are + found they are merged. If both searches fail a new multiple equality + containing just field1 and field2 is added to the existing + multiple equalities. + If the function processes the predicate of the form field1=const, + it looks for a multiple equality containing field1. If found, the + function checks the constant of the multiple equality. If the value + is unknown, it is setup to const. Otherwise the value is compared with + const and the evaluation of the equality predicate is performed. + When expanding/merging equality predicates from the upper levels + the function first copies them for the current level. It looks + acceptable, as this happens rarely. The implementation without + copying would be much more complicated. + + RETURN + TRUE - if the predicate is a simple equality predicate + FALSE - otherwise +*/ + +static bool check_equality(Item *item, COND_EQUAL *cond_equal) +{ + if (item->type() == Item::FUNC_ITEM && + ((Item_func*) item)->functype() == Item_func::EQ_FUNC) + { + Item *left_item= ((Item_func*) item)->arguments()[0]; + Item *right_item= ((Item_func*) item)->arguments()[1]; + if (left_item->type() == Item::FIELD_ITEM && + right_item->type() == Item::FIELD_ITEM) + { + /* The predicate the form field1=field2 is processed */ + + Field *left_field= ((Item_field*) left_item)->field; + Field *right_field= ((Item_field*) right_item)->field; + + if (!left_field->eq_def(right_field)) + return FALSE; + + if (left_field->eq(right_field)) + return TRUE; + + /* Search for multiple equalities containing field1 and/or field2 */ + bool left_copyfl, right_copyfl; + Item_equal *left_item_equal= + find_item_equal(cond_equal, left_field, &left_copyfl); + Item_equal *right_item_equal= + find_item_equal(cond_equal, right_field, &right_copyfl); + + if (left_item_equal && left_item_equal == right_item_equal) + { + /* + The equality predicate is inference of one of the existing + multiple equalities + */ + return TRUE; + } + + /* Copy the found multiple equalities at the current level if needed */ + if (left_copyfl) + { + left_item_equal= new Item_equal(left_item_equal); + cond_equal->current_level.push_back(left_item_equal); + } + if (right_copyfl) + { + right_item_equal= new Item_equal(right_item_equal); + cond_equal->current_level.push_back(right_item_equal); + } + + if (left_item_equal) + { + if (! right_item_equal) + left_item_equal->add((Item_field *) right_item); + else + { + /* Merge two multiple equalities forming a new one */ + left_item_equal->merge(right_item_equal); + /* Remove the merged multiple equality from the list */ + List_iterator li(cond_equal->current_level); + while ((li++) != right_item_equal); + li.remove(); + } + } + else + { + if (right_item_equal) + right_item_equal->add((Item_field *) left_item); + else + { + /* Multiple equalities for neither of the fields has been found */ + Item_equal *item= new Item_equal((Item_field *) left_item, + (Item_field *) right_item); + cond_equal->current_level.push_back(item); + } + } + return TRUE; + } + else + { + /* The predicate of the form field=const/const=field is processed */ + Item *const_item= 0; + Item_field *field_item= 0; + if (left_item->type() == Item::FIELD_ITEM && + right_item->const_item()) + { + field_item= (Item_field*) left_item; + const_item= right_item; + } + else if (right_item->type() == Item::FIELD_ITEM && + left_item->const_item()) + { + field_item= (Item_field*) right_item; + const_item= left_item; + } + if (const_item && + field_item->result_type() == const_item->result_type()) + { + bool copyfl; + Item_equal *item_equal = find_item_equal(cond_equal, + field_item->field, ©fl); + if (copyfl) + { + item_equal= new Item_equal(item_equal); + cond_equal->current_level.push_back(item_equal); + } + if (item_equal) + { + /* + The flag cond_false will be set to 1 after this, if item_equal + already contains a constant and its value is not equal to + the value of const_item. + */ + item_equal->add(const_item); + } + else + { + item_equal= new Item_equal(const_item, field_item); + cond_equal->current_level.push_back(item_equal); + } + return TRUE; + } + } + } + return FALSE; +} + +/* + Replace all equality predicates in a condition by multiple equality items + + SYNOPSIS + build_all_equal_items() + cond condition(expression) where to make replacement + inherited path to all inherited multiple equality items + + DESCRIPTION + At each 'and' level the function detects items for equality predicates + and replaced them by a set of multiple equality items of class Item_equal, + taking into account inherited equalities from upper levels. + If an equality predicate is used not in a conjunction it's just + replaced by a multiple equality predicate. + The functuion also traverse the cond tree and and for each field reference + sets a ponter to the multiple equality item containing the field, if there + is any. If this multiple equality equates fields to a constant the + function replace the field reference by the constant. + + NOTES + Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of + f1=f2, .., fn-1=fn. It substitutes any inference from these + equality predicates that is equivalent to the conjunction. + Thus, =(a1,a2,a3) can substitute for ((a1=a3) AND (a2=a3) AND (a2=a1)) as + it is equivalent to ((a1=a2) AND (a2=a3)). + The function always makes a subsitution of all equality predicates occured + in a conjuction for a minimal set of multiple equality predicates. + This set can be considered as a canonical representation of the + sub-conjunction of the equality predicates. + E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by + (=(t1.a,t2.b,t3.c) AND t2.b>5), not by + (=(t1.a,t2.b) AND =(t1.a,t3.c) AND t2.b>5); + while (t1.a=t2.b AND t2.b>5 AND t3.c,t4.d) is replaced by + (=(t1.a,t2.b) AND =(t3.c=t4.d) AND t2.b>5), + but if additionally =(t4.d,t2.b) is inherited, it + will be replaced by (=(t1.a,t2.b,t3.c,t4.d) AND t2.b>5) + + IMPLEMENTATION + The function performs the substitution in a recursive descent by + the condtion tree, passing to the next AND level a chain of multiple + equality predicates which have been built at the upper levels. + The Item_equal items built at the level are attached to other + non-equality conjucts as a sublist. The pointer to the inherited + multiple equalities is saved in the and condition object (Item_cond_and). + This chain allows us for any field reference occurence easyly to find a + multiple equality that must be held for this occurence. + + RETURN + pointer to the transformed condition +*/ + +static COND *build_all_equal_items(COND *cond, + COND_EQUAL *inherited) +{ + COND_EQUAL cond_equal; + cond_equal.parent_level= inherited; + + if (cond->type() == Item::COND_ITEM) + { + bool and_level= ((Item_cond*) cond)->functype() == + Item_func::COND_AND_FUNC; + List *args= ((Item_cond*) cond)->argument_list(); + + List_iterator li(*args); + Item *item; + + if (and_level) + { + /* + Retrieve all conjucts of this level detecting the equality + that are subject to substitution by multiple equality items and + removing each such predicate from the conjunction after after having + found/created a multiple equality whose inference the predicate is. + */ + while ((item= li++)) + { + if (check_equality(item, &cond_equal)) + li.remove(); + } + + List_iterator_fast it(cond_equal.current_level); + while ((item= it++)) + { + item->fix_fields(current_thd, 0, 0); + } + ((Item_cond_and*)cond)->cond_equal= cond_equal; + inherited= &(((Item_cond_and*)cond)->cond_equal); + } + /* + Make replacement of equality predicates for lower levels + of the condition expression. + */ + List_iterator it(*args); + while((item= it++)) + { + Item *new_item; + if ((new_item = build_all_equal_items(item, inherited))!= item) + { + /* This replacement happens only for standalone equalities */ + it.replace(new_item); + } + } + if (and_level) + args->concat((List *)&cond_equal.current_level); + } + else if (cond->type() == Item::FUNC_ITEM) + { + /* Standalone equalities are handled here */ + Item_equal *item_equal; + if (check_equality(cond, &cond_equal) && + (item_equal= cond_equal.current_level.pop())) + { + item_equal->fix_fields(current_thd, 0, 0); + return item_equal; + } + else + { + cond= cond->traverse(&Item::equal_fields_propagator, + (byte *) inherited); + cond->update_used_tables(); + } + } + return cond; +} + + +/* + Generate minimal set of simple equalities equivalent to a multiple equality + + SYNOPSIS + eliminate_item_equal() + cond condition to add the generated equality to + cond_equal structure to access multiple equality of upper levels + item_equal multiple equality to generate simple equality from + + DESCRIPTION + The function retrieves the fields of the multiple equality item + item_equal and for each field f: + - if item_equal contains const it generates the equality f=const_item; + - otherwise, if f is not the first field, generates the equality + f=item_equal->get_first(). + All generated equality are added to the cond conjunction. + + NOTES + Before generating an equality function checks that it has not + been generated for multiple equalies of the upper levels. + If cond is equal to 0, then not more then one equality is generated + and a pointer to it is returned as the result of the function. + + RETURN + The condition with generated simple equalities or + a pointer to the simple generated equality. +*/ + +static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, + Item_equal *item_equal) +{ + Item *item_const= item_equal->get_const(); + Item_equal_iterator it(*item_equal); + Item *head; + if (item_const) + head= item_const; + else + { + head= item_equal->get_first(); + it++; + } + Item_field *item_field; + Item *new_item= 0; + while ((item_field= it++)) + { + Item_equal *upper= item_field->find_item_equal(cond_equal); + Item_field *item= item_field; + if (upper) + { + if (item_const) + { + if (upper->get_const()) + item= 0; + } + else + { + Item_equal_iterator li(*item_equal); + while ((item= li++) != item_field) + { + if (item->find_item_equal(cond_equal) == upper) + break; + } + } + } + if (item == item_field) + { + if (!cond && new_item) + { + cond= new Item_cond_and(); + ((Item_cond *) cond)->add(new_item); + } + item_field->item_equal= item_equal; + new_item= new Item_func_eq(item_field, head); + ((Item_func_eq *) new_item)->fix_length_and_dec(); + if (cond) + ((Item_cond *) cond)->add(new_item); + } + } + if (!cond) + cond= (COND *) new_item; + return cond; +} + + +/* + Substitute every field reference in a condition by the best equal field + and eliminate all multiplle equality predicates + + SYNOPSIS + substitute_for_best_equal_field() + cond condition to process + table_join_idx index to tables determining field preference + + DESCRIPTION + The function retrieves the cond condition and for each encountered + multiple equality predicate it sorts the field references in it + according to the order of tables specified by the table_join_idx + parameter. Then it eliminates the multiple equality predicate it + replacing it by the conjunction of simple equality predicates + equating every field from the multiple equality to the first + field in it, or to the constant, if there is any. + After this the function retrieves all other conjuncted + predicates substitute every field reference by the field reference + to the first equal field or equal constant if there are any. + + NOTES + At the first glance full sort of fields in multiple equality + seems to be an overkill. Yet it's not the case due to possible + new fields in multiple equality item of lower levels. We want + the order in them to comply with the order of upper levels. + + RETURN + The transformed condition +*/ + +static COND* substitute_for_best_equal_field(COND *cond, + void *table_join_idx) +{ + Item_equal *item_equal; + + if (cond->type() == Item::COND_ITEM) + { + List *cond_list= ((Item_cond*) cond)->argument_list(); + COND_EQUAL *cond_equal= 0; + + bool and_level= ((Item_cond*) cond)->functype() == + Item_func::COND_AND_FUNC; + if (and_level) + { + cond_equal= &((Item_cond_and *) cond)->cond_equal; + cond_list->disjoin((List *) &cond_equal->current_level); + + List_iterator_fast it(cond_equal->current_level); + while((item_equal= it++)) + { + item_equal->sort(table_join_idx); + } + } + + List_iterator li(*cond_list); + Item *item; + while ((item= li++)) + { + Item *new_item =substitute_for_best_equal_field(item, + table_join_idx); + if (new_item != item) + li.replace(new_item); + } + + if (and_level) + { + List_iterator_fast it(cond_equal->current_level); + while((item_equal= it++)) + { + eliminate_item_equal(cond, cond_equal->parent_level, item_equal); + } + } + } + else if (cond->type() == Item::FUNC_ITEM && + ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + { + item_equal= (Item_equal *) cond; + item_equal->sort(table_join_idx); + return eliminate_item_equal(0, 0, item_equal); + } + else + cond->walk(&Item::replace_equal_field_processor, 0); + return cond; +} + + /* change field = field to field = const for each found field = const in the and_level @@ -4318,9 +5053,6 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) return conds; } DBUG_EXECUTE("where",print_where(conds,"original");); - /* eliminate NOT operators */ - conds= eliminate_not_funcs(conds); - DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); /* change field = field to field = const for each found field = const */ propagate_cond_constants((I_List *) 0,conds,conds); /* @@ -4412,7 +5144,8 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } else if (cond->type() == Item::FUNC_ITEM && - ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC) + ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC && + !cond->const_item()) { /* Handles this special case for some ODBC applications: @@ -4463,6 +5196,22 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } } + else if (cond->type() == Item::FUNC_ITEM && + ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + { + /* + The is_false method for an multiple equality item returns 1 + when the conjunction with this item originally contained an + equality that was inconsistent with the multiple equality + predicate or has been inherited from other multiple equality + for which is_false returns 1. + */ + if (((Item_equal *) cond)->is_false()) + { + *cond_value= Item::COND_FALSE; + return (COND*) 0; + } + } else if (cond->const_item()) { *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE; diff --git a/sql/sql_select.h b/sql/sql_select.h index 24854713a0e..ad2adc2ee0f 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -184,6 +184,7 @@ class JOIN :public Sql_alloc ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select COND *conds; // ---"--- Item *conds_history; // store WHERE for explain + COND_EQUAL *cond_equal; TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_selec SQL_SELECT *select; //created in optimisation phase Item **ref_pointer_array; //used pointer reference for this select @@ -243,6 +244,7 @@ class JOIN :public Sql_alloc ref_pointer_array_size= 0; zero_result_cause= 0; optimized= 0; + cond_equal= 0; fields_list= fields; bzero((char*) &keyuse,sizeof(keyuse)); From 1a3a62c3908ccfc78f443f1720453be6d7d87eab Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 26 Nov 2003 17:57:43 -0800 Subject: [PATCH 02/10] Post-merge after introducing Item_equal --- sql/sql_list.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_list.h b/sql/sql_list.h index d7a62b7fd7d..f11951f5c6d 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -283,6 +283,7 @@ public: } empty(); } + inline void concat(List *list) { base_list::concat(list); } }; From 1a9be364dd3a2cd0c4a7ba9cbd25f5f10aac7560 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Mon, 1 Dec 2003 18:19:31 -0800 Subject: [PATCH 03/10] range.test: Added tests for != predicate. sql_select.cc: Fixing bugs in Item_equal code: added a parameter in substitute_for_equal_field; modified eliminate_item_equal. sql_list.h: Cleanup --- mysql-test/t/range.test | 4 ++++ sql/sql_list.h | 5 ++--- sql/sql_select.cc | 20 +++++++++++--------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 75cbb756998..a1d227459fd 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -323,8 +323,12 @@ insert into t2(id, uid, name) select id, uid, name from t1; select count(*) from t1; select count(*) from t2; +analyze table t1,t2; + explain select * from t1, t2 where t1.uid=t2.uid AND t1.uid > 0; +explain select * from t1, t2 where t1.uid=t2.uid AND t2.uid > 0; explain select * from t1, t2 where t1.uid=t2.uid AND t1.uid != 0; +explain select * from t1, t2 where t1.uid=t2.uid AND t2.uid != 0; select * from t1, t2 where t1.uid=t2.uid AND t1.uid > 0; select * from t1, t2 where t1.uid=t2.uid AND t1.uid != 0; diff --git a/sql/sql_list.h b/sql/sql_list.h index f11951f5c6d..b276c68c16a 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -271,8 +271,8 @@ public: inline T* head() {return (T*) base_list::head(); } inline T** head_ref() {return (T**) base_list::head_ref(); } inline T* pop() {return (T*) base_list::pop(); } - inline void concat(List *list) { return base_list::concat(list); } - inline void disjoin(List *list) { return base_list::disjoin(list); } + inline void concat(List *list) { base_list::concat(list); } + inline void disjoin(List *list) { base_list::disjoin(list); } void delete_elements(void) { list_node *element,*next; @@ -283,7 +283,6 @@ public: } empty(); } - inline void concat(List *list) { base_list::concat(list); } }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7687a37f8ca..53aed32d0c6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -77,6 +77,7 @@ static Item *flatten_condition(COND *cond); static COND *build_all_equal_items(COND *cond, COND_EQUAL *inherited); static COND* substitute_for_best_equal_field(COND *cond, + COND_EQUAL *cond_equal, void *table_join_idx); static COND *optimize_cond(COND *conds,Item::cond_result *cond_value); static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); @@ -690,7 +691,7 @@ JOIN::optimize() */ if (conds) { - conds= substitute_for_best_equal_field(conds, map2table); + conds= substitute_for_best_equal_field(conds, cond_equal, map2table); conds->update_used_tables(); } { @@ -700,6 +701,7 @@ JOIN::optimize() if (tables->on_expr) { tables->on_expr= substitute_for_best_equal_field(tables->on_expr, + cond_equal, map2table); tables->on_expr->update_used_tables(); map2table[tables->table->tablenr]->on_expr= tables->on_expr; @@ -4703,11 +4705,8 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, Item_field *item= item_field; if (upper) { - if (item_const) - { - if (upper->get_const()) - item= 0; - } + if (item_const && upper->get_const()) + item= 0; else { Item_equal_iterator li(*item_equal); @@ -4745,6 +4744,7 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, SYNOPSIS substitute_for_best_equal_field() cond condition to process + cond_equal multiple equalities to take into consideration table_join_idx index to tables determining field preference DESCRIPTION @@ -4770,6 +4770,7 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, */ static COND* substitute_for_best_equal_field(COND *cond, + COND_EQUAL *cond_equal, void *table_join_idx) { Item_equal *item_equal; @@ -4777,7 +4778,6 @@ static COND* substitute_for_best_equal_field(COND *cond, if (cond->type() == Item::COND_ITEM) { List *cond_list= ((Item_cond*) cond)->argument_list(); - COND_EQUAL *cond_equal= 0; bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; @@ -4797,7 +4797,7 @@ static COND* substitute_for_best_equal_field(COND *cond, Item *item; while ((item= li++)) { - Item *new_item =substitute_for_best_equal_field(item, + Item *new_item =substitute_for_best_equal_field(item, cond_equal, table_join_idx); if (new_item != item) li.replace(new_item); @@ -4817,7 +4817,9 @@ static COND* substitute_for_best_equal_field(COND *cond, { item_equal= (Item_equal *) cond; item_equal->sort(table_join_idx); - return eliminate_item_equal(0, 0, item_equal); + if (cond_equal && cond_equal->current_level.head() == item_equal) + cond_equal= 0; + return eliminate_item_equal(0, cond_equal, item_equal); } else cond->walk(&Item::replace_equal_field_processor, 0); From 0ba4b082febaf9bb72e2a1f741d6206f92f26a79 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 2 Dec 2003 14:33:42 -0800 Subject: [PATCH 04/10] sql_select.cc: Fixed a problem a with special cases of IS NULL usage caused by some erroneous change made when Item_equal was introduced. --- sql/sql_select.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 748e2c38cf3..55f5b28fc78 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5152,8 +5152,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } else if (cond->type() == Item::FUNC_ITEM && - ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC && - !cond->const_item()) + ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC) { /* Handles this special case for some ODBC applications: @@ -5203,6 +5202,11 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } } + if (cond->const_item()) + { + *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE; + return (COND*) 0; + } } else if (cond->type() == Item::FUNC_ITEM && ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) From 580b8785624da0f560664a8492ac9789aa7531ef Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 2 Dec 2003 16:41:53 -0800 Subject: [PATCH 05/10] The output of explain command for some queries has changed after introducing Item_equal. --- .bzrignore | 2 ++ mysql-test/r/bdb.result | 8 ++++---- mysql-test/r/func_group.result | 2 +- mysql-test/r/func_test.result | 4 ++-- mysql-test/r/heap_btree.result | 4 ++-- mysql-test/r/index_merge.result | 12 ++++++------ mysql-test/r/innodb.result | 8 ++++---- mysql-test/r/join_outer.result | 2 +- mysql-test/r/odbc.result | 2 +- mysql-test/r/range.result | 32 +++++++++++++++++++++----------- mysql-test/r/select.result | 4 ++-- mysql-test/r/subselect.result | 24 ++++++++++++------------ mysql-test/r/user_var.result | 4 ++-- 13 files changed, 60 insertions(+), 48 deletions(-) diff --git a/.bzrignore b/.bzrignore index 5b2789733fc..5cf903dc1a7 100644 --- a/.bzrignore +++ b/.bzrignore @@ -646,3 +646,5 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +mysql-test/r/index_merge_load.result +mysql-test/t/index_merge.load diff --git a/mysql-test/r/bdb.result b/mysql-test/r/bdb.result index f619775aa97..ccbfcc55828 100644 --- a/mysql-test/r/bdb.result +++ b/mysql-test/r/bdb.result @@ -140,13 +140,13 @@ id parent_id level 1015 102 2 explain select level from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const 1 Using where; Using index +1 SIMPLE t1 ref level level 1 const 1 Using index explain select level,id from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const 1 Using where; Using index +1 SIMPLE t1 ref level level 1 const 1 Using index explain select level,id,parent_id from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const 1 Using where +1 SIMPLE t1 ref level level 1 const 1 select level,id from t1 where level=1; level id 1 1002 @@ -625,7 +625,7 @@ id parent_id level 1016 102 2 explain select level from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const 1 Using where; Using index +1 SIMPLE t1 ref level level 1 const 1 Using index select level,id from t1 where level=1; level id 1 1004 diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 7ae3bbb0608..dd49d7e5ba7 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -412,7 +412,7 @@ CHI Los Angeles explain select max(a3) from t1 where a2 is null and a2 = 2; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE select max(a3) from t1 where a2 is null and a2 = 2; max(a3) NULL diff --git a/mysql-test/r/func_test.result b/mysql-test/r/func_test.result index 4951d954abb..1f43886e54a 100644 --- a/mysql-test/r/func_test.result +++ b/mysql-test/r/func_test.result @@ -74,9 +74,9 @@ select * from t1 where 1 xor 1; a explain extended select * from t1 where 1 xor 1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE Warnings: -Note 1003 select high_priority test.t1.a AS `a` from test.t1 where (1 xor 1) +Note 1003 select high_priority test.t1.a AS `a` from test.t1 select - a from t1; - a -1 diff --git a/mysql-test/r/heap_btree.result b/mysql-test/r/heap_btree.result index c42c5e60675..980a7baa00c 100644 --- a/mysql-test/r/heap_btree.result +++ b/mysql-test/r/heap_btree.result @@ -73,7 +73,7 @@ type=heap; insert into t1 values (1,1),(2,2),(1,3),(2,4),(2,5),(2,6); explain select * from t1 where x=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref x x 4 const 1 Using where +1 SIMPLE t1 ref x x 4 const 1 select * from t1 where x=1; x y 1 1 @@ -134,7 +134,7 @@ a b 1 1 explain select * from tx where b=x; id select_type table type possible_keys key key_len ref rows Extra -x SIMPLE tx ref b b x const x Using where +x SIMPLE tx ref b b x const x drop table t1; create table t1 (id int unsigned not null, primary key using BTREE (id)) type=HEAP; insert into t1 values(1); diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 7de111549bb..7393f45368e 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -76,13 +76,13 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t0 ALL i1,i2 NULL NULL NULL 1024 Using where explain select * from t0 where key2 = 45 or key1 is null; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 range i1,i2 i2 4 NULL 1 Using where +1 SIMPLE t0 ref i2 i2 4 const 1 explain select * from t0 where key2=10 or key3=3 or key4 <=> null; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using where explain select * from t0 where key2=10 or key3=3 or key4 is null; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using where +1 SIMPLE t0 index_merge i2,i3 i2,i3 4,4 NULL 2 Using where explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or (key3=10) or (key4 <=> null); id select_type table type possible_keys key key_len ref rows Extra @@ -257,18 +257,18 @@ explain select * from t0,t1 where (t0.key1=t1.key1) and (t0.key1=3 or t0.key2=4) and t1.key1<200; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using where -1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 Using where +1 SIMPLE t0 range i1,i2 i1 4 NULL 179 Using where +1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 explain select * from t0,t1 where (t0.key1=t1.key1) and (t0.key1=3 or t0.key2<4) and t1.key1=2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t0 ref i1,i2 i1 4 const 1 Using where -1 SIMPLE t1 ref i1 i1 4 const 1 Using where +1 SIMPLE t1 ref i1 i1 4 const 1 explain select * from t0,t1 where t0.key1 = 5 and (t1.key1 = t0.key1 or t1.key8 = t0.key1); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 ref i1 i1 4 const 1 Using where +1 SIMPLE t0 ref i1 i1 4 const 1 1 SIMPLE t1 index_merge i1,i8 i1,i8 4,4 NULL 2 Using where explain select * from t0,t1 where t0.key1 < 3 and (t1.key1 = t0.key1 or t1.key8 = t0.key1); diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 0ebf3d50ab8..e19fc862a94 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -141,13 +141,13 @@ id parent_id level 1015 102 2 explain select level from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const # Using where; Using index +1 SIMPLE t1 ref level level 1 const # Using index explain select level,id from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const # Using where; Using index +1 SIMPLE t1 ref level level 1 const # Using index explain select level,id,parent_id from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const # Using where +1 SIMPLE t1 ref level level 1 const # select level,id from t1 where level=1; level id 1 1002 @@ -609,7 +609,7 @@ id parent_id level 1016 102 2 explain select level from t1 where level=1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref level level 1 const # Using where; Using index +1 SIMPLE t1 ref level level 1 const # Using index select level,id from t1 where level=1; level id 1 1004 diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 165f1522378..07c26d737fe 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -634,7 +634,7 @@ insert into t2 values (10,1),(20,2),(30,3); explain select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index NULL PRIMARY 4 NULL 3 Using index -1 SIMPLE t1 eq_ref PRIMARY PRIMARY 2 const 1 Using where; Using index +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 2 const 1 Using index select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30; fooID barID fooID 10 1 NULL diff --git a/mysql-test/r/odbc.result b/mysql-test/r/odbc.result index c0b2ada0053..2d9d39393b1 100644 --- a/mysql-test/r/odbc.result +++ b/mysql-test/r/odbc.result @@ -12,5 +12,5 @@ select * from t1 where a is null; a b explain select * from t1 where b is null; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE drop table t1; diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 0c8f2cb5323..38af6f3fed2 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -220,24 +220,22 @@ insert into t1 (x) values (1),(2),(3),(4),(5),(6),(7),(8),(9); update t1 set y=x; explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 7 and t1.y+0; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref y y 5 const 1 Using where -1 SIMPLE t2 range x x 5 NULL 4 Using where +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 7 and t2.x <= t1.y+0; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref y y 5 const 1 Using where -1 SIMPLE t2 range x x 5 NULL 4 Using where +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 Using where -1 SIMPLE t2 ALL x NULL NULL NULL 9 Range checked for each record (index map: 0x1) +1 SIMPLE t2 range x x 5 NULL 3 Using where explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 Using where -1 SIMPLE t2 ALL x NULL NULL NULL 9 Range checked for each record (index map: 0x1) +1 SIMPLE t2 range x x 5 NULL 3 Using where explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 Using where -1 SIMPLE t2 ALL x NULL NULL NULL 9 Using where +1 SIMPLE t2 range x x 5 NULL 2 Using where explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 Using where @@ -255,12 +253,12 @@ CREATE TABLE t2 (keya int(11) NOT NULL default '0', KEY j1 (keya)); INSERT INTO t2 VALUES (0),(0),(1),(1),(2),(2); explain select * from t1, t2 where (t1.key1 0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range uid_index uid_index 4 NULL 128 Using where 1 SIMPLE t2 ref uid_index uid_index 4 test.t1.uid 38 +explain select * from t1, t2 where t1.uid=t2.uid AND t2.uid > 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range uid_index uid_index 4 NULL 128 Using where +1 SIMPLE t2 ref uid_index uid_index 4 test.t1.uid 38 explain select * from t1, t2 where t1.uid=t2.uid AND t1.uid != 0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range uid_index uid_index 4 NULL 129 Using where 1 SIMPLE t2 ref uid_index uid_index 4 test.t1.uid 38 +explain select * from t1, t2 where t1.uid=t2.uid AND t2.uid != 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range uid_index uid_index 4 NULL 129 Using where +1 SIMPLE t2 ref uid_index uid_index 4 test.t1.uid 38 select * from t1, t2 where t1.uid=t2.uid AND t1.uid > 0; id name uid id name uid 1001 A 1 1001 A 1 diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 8edd0182dec..8bb3a47335a 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -1374,7 +1374,7 @@ id select_type table type possible_keys key key_len ref rows Extra explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 and t4.companynr > 0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where -1 SIMPLE t4 eq_ref PRIMARY PRIMARY 1 test.t2.companynr 1 Using where +1 SIMPLE t4 eq_ref PRIMARY PRIMARY 1 test.t2.companynr 1 explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 or t2.companynr is null; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t4 ALL NULL NULL NULL NULL 12 @@ -1470,7 +1470,7 @@ explain extended select count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where Warnings: -Note 1003 select high_priority count(0) AS `count(*)`,min(test.t2.fld4) AS `min(fld4)`,max(test.t2.fld4) AS `max(fld4)`,sum(test.t2.fld1) AS `sum(fld1)`,avg(test.t2.fld1) AS `avg(fld1)`,std(test.t2.fld1) AS `std(fld1)`,variance(test.t2.fld1) AS `variance(fld1)` from test.t2 where ((test.t2.companynr = 34) and (test.t2.fld4 <> _latin1'')) +Note 1003 select high_priority count(0) AS `count(*)`,min(test.t2.fld4) AS `min(fld4)`,max(test.t2.fld4) AS `max(fld4)`,sum(test.t2.fld1) AS `sum(fld1)`,avg(test.t2.fld1) AS `avg(fld1)`,std(test.t2.fld1) AS `std(fld1)`,variance(test.t2.fld1) AS `variance(fld1)` from test.t2 where ((test.t2.fld4 <> _latin1'') and (test.t2.companynr = 34)) select companynr,count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld1),variance(fld1) from t2 group by companynr limit 3; companynr count(*) min(fld4) max(fld4) sum(fld1) avg(fld1) std(fld1) variance(fld1) 00 82 Anthony windmills 10355753 126289.6707 115550.9757 13352027981.7087 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 0f7d5bb5d43..29cf81883cd 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -309,7 +309,7 @@ id select_type table type possible_keys key key_len ref rows Extra Warnings: Note 1275 Field or reference 't2.a' of SELECT #2 was resolved in SELECT #1 Note 1275 Field or reference 't2.a' of SELECT #3 was resolved in SELECT #1 -Note 1003 select high_priority (select test.t1.a AS `a` from test.t1 where (test.t1.a = test.t2.a) union select test.t5.a AS `a` from test.t5 where (test.t5.a = test.t2.a)) AS `(select a from t1 where t1.a=t2.a union select a from t5 where t5.a=t2.a)`,test.t2.a AS `a` from test.t2 +Note 1003 select high_priority (select test.t1.a AS `a` from test.t1 where (test.t2.a = test.t1.a) union select test.t5.a AS `a` from test.t5 where (test.t2.a = test.t5.a)) AS `(select a from t1 where t1.a=t2.a union select a from t5 where t5.a=t2.a)`,test.t2.a AS `a` from test.t2 select (select a from t1 where t1.a=t2.a union all select a from t5 where t5.a=t2.a), a from t2; ERROR 21000: Subquery returns more than 1 row create table t6 (patient_uq int, clinic_uq int, index i1 (clinic_uq)); @@ -327,7 +327,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 test.t6.clinic_uq 1 Warnings: Note 1275 Field or reference 'clinic_uq' of SELECT #2 was resolved in SELECT #1 -Note 1003 select high_priority test.t6.patient_uq AS `patient_uq`,test.t6.clinic_uq AS `clinic_uq` from test.t6 where exists(select test.t7.uq AS `uq`,test.t7.name AS `name` from test.t7 where (test.t7.uq = test.t6.clinic_uq) limit 1) +Note 1003 select high_priority test.t6.patient_uq AS `patient_uq`,test.t6.clinic_uq AS `clinic_uq` from test.t6 where exists(select test.t7.uq AS `uq`,test.t7.name AS `name` from test.t7 where (test.t6.clinic_uq = test.t7.uq) limit 1) select * from t1 where a= (select a from t2,t4 where t2.b=t4.b); ERROR 23000: Column: 'a' in field list is ambiguous drop table if exists t1,t2,t3; @@ -540,7 +540,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 const PRIMARY,numreponse PRIMARY 7 const,const 1 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away Warnings: -Note 1003 select high_priority test.t1.numreponse AS `numreponse` from test.t1 where ((test.t1.numeropost = _latin1'1') and (test.t1.numreponse = 3)) +Note 1003 select high_priority test.t1.numreponse AS `numreponse` from test.t1 where ((test.t1.numeropost = _latin1'1') and (test.t1.numreponse = (select max(test.t1.numreponse) AS `MAX(numreponse)` from test.t1 where (test.t1.numeropost = _latin1'1')))) drop table t1; CREATE TABLE t1 (a int(1)); INSERT INTO t1 VALUES (1); @@ -899,7 +899,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 Using where; Using index 2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select high_priority test.t1.a AS `a`,(test.t1.a,(select 1 AS `Not_used` from test.t2 join test.t3 where ((test.t3.a = test.t2.a) and (((test.t1.a) = test.t2.a) or isnull(test.t2.a))) having (test.t2.a) limit 1)) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from test.t1 +Note 1003 select high_priority test.t1.a AS `a`,(test.t1.a,(select 1 AS `Not_used` from test.t2 join test.t3 where ((((test.t1.a) = test.t2.a) or isnull(test.t2.a)) and (test.t3.a = test.t2.a)) having (test.t2.a) limit 1)) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from test.t1 drop table t1,t2,t3; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); @@ -1168,9 +1168,9 @@ SELECT 0 IN (SELECT 1 FROM t1 a); EXPLAIN EXTENDED SELECT 0 IN (SELECT 1 FROM t1 a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE Warnings: -Note 1003 select high_priority (0,(select 1 AS `Not_used` from test.t1 a where isnull(1) having (1) limit 1)) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select high_priority (0,(select 1 AS `Not_used` from test.t1 a having (1) limit 1)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) @@ -1178,9 +1178,9 @@ SELECT 0 IN (SELECT 1 FROM t1 a); EXPLAIN EXTENDED SELECT 0 IN (SELECT 1 FROM t1 a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE Warnings: -Note 1003 select high_priority (0,(select 1 AS `Not_used` from test.t1 a where isnull(1) having (1) limit 1)) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select high_priority (0,(select 1 AS `Not_used` from test.t1 a having (1) limit 1)) AS `0 IN (SELECT 1 FROM t1 a)` drop table t1; CREATE TABLE `t1` ( `i` int(11) NOT NULL default '0', @@ -1312,7 +1312,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 func 1 Using where 2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where; Using index Warnings: -Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t1.b = test.t3.a) and ((test.t2.a) = test.t1.a)) limit 1)) +Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where (((test.t2.a) = test.t1.a) and (test.t3.a = test.t1.b)) limit 1)) drop table t1, t2, t3; create table t1 (a int, b int, index a (a,b)); create table t2 (a int, index a (a)); @@ -1348,10 +1348,10 @@ a explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index -2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using index -2 DEPENDENT SUBQUERY t1 ref a a 10 func,test.t3.a 1000 Using where; Using index +2 DEPENDENT SUBQUERY t1 ref a a 5 func 1001 Using where; Using index +2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using where; Using index Warnings: -Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t1.b = test.t3.a) and ((test.t2.a) = test.t1.a)) limit 1)) +Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where (((test.t2.a) = test.t1.a) and (test.t3.a = test.t1.b)) limit 1)) insert into t1 values (3,31); select * from t2 where t2.a in (select a from t1 where t1.b <> 30); a diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index fc3dc4eddcd..fcd06a6d8e7 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -23,7 +23,7 @@ i @vv1:=if(sv1.i,1,0) @vv2:=if(sv2.i,1,0) @vv3:=if(sv3.i,1,0) @vv1+@vv2+@vv3 2 1 0 0 1 explain select * from t1 where i=@vv1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref i i 4 const 1 Using where +1 SIMPLE t1 ref i i 4 const 1 explain select * from t1 where @vv1:=@vv1+1 and i=@vv1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where @@ -32,7 +32,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL i 4 NULL 3 Using where; Using index explain select * from t1 where i=@vv1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref i i 4 const 1 Using where +1 SIMPLE t1 ref i i 4 const 1 drop table t1,t2; set @a=0,@b=0; select @a:=10, @b:=1, @a > @b, @a < @b; From 09fa16424a9d5e35b1b7abbc912b0bdfc105f15f Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 18 Feb 2004 22:21:37 -0800 Subject: [PATCH 06/10] Many files: After review fixes for Item_equal. --- mysql-test/r/select.result | 2 +- mysql-test/r/subselect.result | 8 +- sql/item.cc | 89 +++++++- sql/item.h | 16 +- sql/item_cmpfunc.cc | 131 +++++++++-- sql/item_cmpfunc.h | 95 +++++++- sql/item_func.cc | 45 ++-- sql/item_func.h | 3 +- sql/item_row.cc | 6 +- sql/item_row.h | 2 +- sql/item_strfunc.h | 6 +- sql/opt_range.cc | 168 ++++++++------ sql/sql_list.h | 10 + sql/sql_select.cc | 401 +++++++++++++++++++++------------- 14 files changed, 687 insertions(+), 295 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index d8cd0cf1037..a609cc20225 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -1470,7 +1470,7 @@ explain extended select count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where Warnings: -Note 1003 select high_priority count(0) AS `count(*)`,min(test.t2.fld4) AS `min(fld4)`,max(test.t2.fld4) AS `max(fld4)`,sum(test.t2.fld1) AS `sum(fld1)`,avg(test.t2.fld1) AS `avg(fld1)`,std(test.t2.fld1) AS `std(fld1)`,variance(test.t2.fld1) AS `variance(fld1)` from test.t2 where ((test.t2.fld4 <> _latin1'') and (test.t2.companynr = 34)) +Note 1003 select high_priority count(0) AS `count(*)`,min(test.t2.fld4) AS `min(fld4)`,max(test.t2.fld4) AS `max(fld4)`,sum(test.t2.fld1) AS `sum(fld1)`,avg(test.t2.fld1) AS `avg(fld1)`,std(test.t2.fld1) AS `std(fld1)`,variance(test.t2.fld1) AS `variance(fld1)` from test.t2 where ((test.t2.companynr = 34) and (test.t2.fld4 <> _latin1'')) select companynr,count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld1),variance(fld1) from t2 group by companynr limit 3; companynr count(*) min(fld4) max(fld4) sum(fld1) avg(fld1) std(fld1) variance(fld1) 00 82 Anthony windmills 10355753 126289.6707 115550.9757 13352027981.7087 diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 8607fe6fe2d..3a2de4035eb 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -540,7 +540,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 const PRIMARY,numreponse PRIMARY 7 const,const 1 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away Warnings: -Note 1003 select high_priority test.t1.numreponse AS `numreponse` from test.t1 where ((test.t1.numeropost = _latin1'1') and (test.t1.numreponse = (select max(test.t1.numreponse) AS `MAX(numreponse)` from test.t1 where (test.t1.numeropost = _latin1'1')))) +Note 1003 select high_priority test.t1.numreponse AS `numreponse` from test.t1 where ((test.t1.numreponse = (select max(test.t1.numreponse) AS `MAX(numreponse)` from test.t1 where (test.t1.numeropost = _latin1'1'))) and (test.t1.numeropost = _latin1'1')) drop table t1; CREATE TABLE t1 (a int(1)); INSERT INTO t1 VALUES (1); @@ -899,7 +899,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 Using where; Using index 2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select high_priority test.t1.a AS `a`,(test.t1.a,(select 1 AS `Not_used` from test.t2 join test.t3 where ((((test.t1.a) = test.t2.a) or isnull(test.t2.a)) and (test.t3.a = test.t2.a)) having (test.t2.a) limit 1)) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from test.t1 +Note 1003 select high_priority test.t1.a AS `a`,(test.t1.a,(select 1 AS `Not_used` from test.t2 join test.t3 where ((test.t3.a = test.t2.a) and (((test.t1.a) = test.t2.a) or isnull(test.t2.a))) having (test.t2.a) limit 1)) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from test.t1 drop table t1,t2,t3; create table t1 (a float); select 10.5 IN (SELECT * from t1 LIMIT 1); @@ -1312,7 +1312,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 func 1 Using where 2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where; Using index Warnings: -Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where (((test.t2.a) = test.t1.a) and (test.t3.a = test.t1.b)) limit 1)) +Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t3.a = test.t1.b) and ((test.t2.a) = test.t1.a)) limit 1)) drop table t1, t2, t3; create table t1 (a int, b int, index a (a,b)); create table t2 (a int, index a (a)); @@ -1351,7 +1351,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t1 ref a a 5 func 1001 Using where; Using index 2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using where; Using index Warnings: -Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where (((test.t2.a) = test.t1.a) and (test.t3.a = test.t1.b)) limit 1)) +Note 1003 select high_priority test.t2.a AS `a` from test.t2 where (test.t2.a,(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t3.a = test.t1.b) and ((test.t2.a) = test.t1.a)) limit 1)) insert into t1 values (3,31); select * from t2 where t2.a in (select a from t1 where t1.b <> 30); a diff --git a/sql/item.cc b/sql/item.cc index f9c1843707c..8744bc3574b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -994,6 +994,28 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return 0; } + +/* + Find a field among specified multiple equalities + + SYNOPSIS + find_item_equal() + cond_equal reference to list of multiple equalities where + the field (this object) is to be looked for + + DESCRIPTION + The function first searches the field among multiple equalities + of the current level (in the cond_equal->current_level list). + If it fails, it continues searching in upper levels accessed + through a pointer cond_equal->upper_levels. + The search terminates as soon as a multiple equality containing + the field is found. + + RETURN VALUES + First Item_equal containing the field, if success + 0, otherwise +*/ + Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) { Item_equal *item= 0; @@ -1005,31 +1027,82 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) if (item->contains(field)) return item; } - cond_equal= cond_equal->parent_level; + /* + The field is not found in any of the multiple equalities + of the current level. Look for it in upper levels + */ + cond_equal= cond_equal->upper_levels; } - return item; + return 0; } + +/* + Set a pointer to the multiple equality the field reference belongs to (if any) + + SYNOPSIS + equal_fields_propagator() + arg - reference to list of multiple equalities where + the field (this object) is to be looked for + + DESCRIPTION + The function looks for a multiple equality containing the field item + among those referenced by arg. + In the case such equality exists the function does the following. + If the found multiple equality contains a constant, then the field + reference is substituted for this constant, otherwise it sets a pointer + to the multiple equality in the field item. + + NOTES + This function is supposed to be called as a callback parameter in calls + of the transform method. + + RETURN VALUES + pointer to the replacing constant item, if the field item was substituted + pointer to the field item, otherwise. +*/ + Item *Item_field::equal_fields_propagator(byte *arg) { - COND_EQUAL *cond_equal= (COND_EQUAL *) arg; - item_equal= find_item_equal(cond_equal); + item_equal= find_item_equal((COND_EQUAL *) arg); Item *item= 0; if (item_equal) item= item_equal->get_const(); - if (item) - item->fixed= 0; - else + if (!item) item= this; return item; } + +/* + Set a pointer to the multiple equality the field reference belongs to (if any) + + SYNOPSIS + replace_equal_field_processor() + arg - a dummy parameter, is not used here + + DESCRIPTION + The function replaces a pointer to a field in the Item_field object + by a pointer to another field. + The replacement field is taken from the very beginning of + the item_equal list which the Item_field object refers to (belongs to) + If the Item_field object does not refer any Item_equal object, + nothing is done. + + NOTES + This function is supposed to be called as a callback parameter in calls + of the walk method. + + RETURN VALUES + 0 +*/ + bool Item_field::replace_equal_field_processor(byte *arg) { if (item_equal) { Item_field *subst= item_equal->get_first(); - if (subst && !field->eq(subst->field)) + if (!field->eq(subst->field)) { field= subst->field; return 0; diff --git a/sql/item.h b/sql/item.h index 1783fc6ef48..d4400f1058d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -83,7 +83,7 @@ public: }; typedef bool (Item::*Item_processor)(byte *arg); -typedef Item* (Item::*Item_calculator) (byte *arg); +typedef Item* (Item::*Item_transformer) (byte *arg); class Item { Item(const Item &); /* Prevent use of these */ @@ -212,9 +212,9 @@ public: return (this->*processor)(arg); } - virtual Item* traverse(Item_calculator calculator, byte *arg) + virtual Item* transform(Item_transformer transformer, byte *arg) { - return (this->*calculator)(arg); + return (this->*transformer)(arg); } virtual bool remove_dependence_processor(byte * arg) { return 0; } @@ -949,13 +949,17 @@ public: (this->*processor)(args); } - Item *traverse(Item_calculator calculator, byte *args) + /* + This method like the walk method traverses the item tree, but + at the same time it can replace some nodes in the tree + */ + Item *transform(Item_transformer transformer, byte *args) { - Item *new_item= arg->traverse(calculator, args); + Item *new_item= arg->transform(transformer, args); if (!new_item) return 0; arg= new_item; - return (this->*calculator)(args); + return (this->*transformer)(args); } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f79e59b5e8f..e0bdae769ed 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -225,7 +225,7 @@ void Item_bool_func2::fix_length_and_dec() } // Make a special case of compare with fields to get nicer DATE comparisons - if (args[0]->type() == FIELD_ITEM && !args[0]->const_item()) + if (args[0]->type() == FIELD_ITEM /* && !args[0]->const_item() */ ) { Field *field=((Item_field*) args[0])->field; if (field->store_for_compare()) @@ -238,7 +238,7 @@ void Item_bool_func2::fix_length_and_dec() } } } - if (args[1]->type() == FIELD_ITEM && !args[1]->const_item()) + if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) { Field *field=((Item_field*) args[1])->field; if (field->store_for_compare()) @@ -1739,21 +1739,44 @@ bool Item_cond::walk(Item_processor processor, byte *arg) return Item_func::walk(processor, arg); } -Item *Item_cond::traverse(Item_calculator calculator, byte *arg) + +/* + Transform an Item_cond object with a transformer callback function + + SYNOPSIS + transform() + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg parameter to be passed to the transformer + + DESCRIPTION + The function recursively applies the transform method with the + same transformer to each member item of the codition list. + If the call of the method for a member item returns a new item + the old item is substituted for a new one. + After this the transform method is applied to the root node + of the Item_cond object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_cond::transform(Item_transformer transformer, byte *arg) { List_iterator li(list); Item *item; while ((item= li++)) { - Item *new_item= item->traverse(calculator, arg); + Item *new_item= item->transform(transformer, arg); if (!new_item) return 0; if (new_item != item) li.replace(new_item); } - return Item_func::traverse(calculator, arg); + return Item_func::transform(transformer, arg); } + void Item_cond::split_sum_func(Item **ref_pointer_array, List &fields) { List_iterator li(list); @@ -2591,6 +2614,32 @@ void Item_equal::add(Item_field *f) fields.push_back(f); } +uint Item_equal::members() +{ + uint count= 0; + List_iterator_fast li(fields); + Item_field *item; + while ((item= li++)) + count++; + return count; +} + + +/* + Check whether a field is referred in the multiple equality + + SYNOPSIS + contains() + field field whose occurence is to be checked + + DESCRIPTION + The function checks whether field is occured in the Item_equal object + + RETURN VALUES + 1 if nultiple equality contains a reference to field + 0 otherwise +*/ + bool Item_equal::contains(Field *field) { List_iterator_fast it(fields); @@ -2603,6 +2652,25 @@ bool Item_equal::contains(Field *field) return 0; } + +/* + Join members of another Item_equal object + + SYNOPSIS + merge() + item multiple equality whose members are to be joined + + DESCRIPTION + The function actually merges two multiple equalitis. + After this operation the Item_equal object additionally contains + the field items of another item of the type Item_equal. + If the optional constant items are not equal the cond_false flag is + set to 1. + + RETURN VALUES + none +*/ + void Item_equal::merge(Item_equal *item) { fields.concat(&item->fields); @@ -2619,26 +2687,46 @@ void Item_equal::merge(Item_equal *item) cond_false|= item->cond_false; } -void Item_equal::sort(void *table_join_idx) + +/* + Order field items in multiple equality according to a sorting criteria + + SYNOPSIS + sort() + cmp function to compare field item + arg context extra parameter for the cmp function + + DESCRIPTION + The function perform ordering of the field items in the Item_equal + object according to the criteria determined by the cmp callback parameter. + If cmp(item_field1,item_field2,arg)<0 than item_field1 must be + placed after item_fiel2. + + IMPLEMENTATION + The function sorts field items by the exchange sort algorithm. + The list of field items is looked through and whenever two neighboring + members follow in a wrong order they are swapped. This is performed + again and again until we get all members in a right order. + + RETURN VALUES + None +*/ + +void Item_equal::sort(Item_field_cmpfunc cmp, void *arg) { bool swap; - void **idx= (void **) table_join_idx; List_iterator it(fields); do { Item_field *item1= it++; Item_field **ref1= it.ref(); Item_field *item2; - Item_field **ref2; - if (!item1) - break; swap= FALSE; while ((item2= it++)) { - ref2= it.ref(); - if (idx[item1->field->table->tablenr] > - idx[item2->field->table->tablenr]) + Item_field **ref2= it.ref(); + if (cmp(item1, item2, arg) < 0) { Item_field *item= *ref1; *ref1= *ref2; @@ -2680,6 +2768,8 @@ void Item_equal::update_used_tables() List_iterator_fast li(fields); Item *item; not_null_tables_cache= used_tables_cache= 0; + if ((const_item_cache= cond_false)) + return; while ((item=li++)) { item->update_used_tables(); @@ -2697,7 +2787,7 @@ longlong Item_equal::val_int() if ((null_value= item->null_value)) return 0; eval_item->store_value(item); - while((item= it++)) + while ((item= it++)) { if ((null_value= item->null_value) || eval_item->cmp(item)) return 0; @@ -2723,19 +2813,19 @@ bool Item_equal::walk(Item_processor processor, byte *arg) return Item_func::walk(processor, arg); } -Item *Item_equal::traverse(Item_calculator calculator, byte *arg) +Item *Item_equal::transform(Item_transformer transformer, byte *arg) { List_iterator it(fields); Item *item; while ((item= it++)) { - Item *new_item= item->traverse(calculator, arg); + Item *new_item= item->transform(transformer, arg); if (!new_item) return 0; if (new_item != item) it.replace((Item_field *) new_item); } - return Item_func::traverse(calculator, arg); + return Item_func::transform(transformer, arg); } void Item_equal::print(String *str) @@ -2744,8 +2834,13 @@ void Item_equal::print(String *str) str->append('('); List_iterator_fast it(fields); Item *item; - if ((item= it++)) + if (const_item) + const_item->print(str); + else + { + item= it++; item->print(str); + } while ((item= it++)) { str->append(','); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9d567bbc01b..d7cd0d93588 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -20,6 +20,9 @@ #ifdef __GNUC__ #pragma interface /* gcc class implementation */ #endif +#ifdef __GNUC__ +template class List_iterator_fast; +#endif extern Item_result item_cmp_type(Item_result a,Item_result b); class Item_bool_func2; @@ -27,6 +30,8 @@ class Arg_comparator; typedef int (Arg_comparator::*arg_cmp_func)(); +typedef int (*Item_field_cmpfunc)(Item_field *f1, Item_field *f2, void *arg); + class Arg_comparator: public Sql_alloc { Item **a, **b; @@ -890,6 +895,7 @@ public: :Item_bool_func(), list(nlist), abort_on_null(0) {} ~Item_cond() { list.delete_elements(); } bool add(Item *item) { return list.push_back(item); } + void add_at_head(List *nlist) { list.prepand(nlist); } bool fix_fields(THD *, struct st_table_list *, Item **ref); enum Type type() const { return COND_ITEM; } @@ -902,11 +908,75 @@ public: void top_level_item() { abort_on_null=1; } void copy_andor_arguments(THD *thd, Item_cond *item); bool walk(Item_processor processor, byte *arg); - Item *traverse(Item_calculator calculator, byte *arg); + Item *transform(Item_transformer transformer, byte *arg); void neg_arguments(); }; +/* + The class Item_equal is used to represent conjuctions of equality + predicates of the form field1 = field2, and field=const in where + conditions and on expressions. + + All equality predicates of the form field1=field2 contained in a + conjuction are substituted for a sequence of items of this class. + An item of this class Item_equal(f1,f2,...fk) respresents a + multiple equality f1=f2=...=fk. + + If a conjuction contains predicates f1=f2 and f2=f3, a new item of + this class is created Item_equal(f1,f2,f3) representing the multiple + equality f1=f2=f3 that substitutes the above equality predicates in + the conjuction. + A conjuction of the predicates f2=f1 and f3=f1 and f3=f2 will be + substituted for the item representing the same multiple equality + f1=f2=f3. + An item Item_equal(f1,f2) can appear instead of a conjuction of + f2=f1 and f1=f2, or instead of just the predicate f1=f2. + + An item of the class Item_equal inherites equalities from outer + conjunctive levels. + + Suppose we have a where condition of the following form: + WHERE f1=f2 AND f3=f4 AND f3=f5 AND ... AND (...OR (f1=f3 AND ...)). + In this case: + f1=f2 will be substituted for Item_equal(f1,f2); + f3=f4 and f3=f5 will be substituted for Item_equal(f3,f4,f5); + f1=f3 will be substituted for Item_equal(f1,f2,f3,f4,f5); + + An object of the class Item_equal can contain an optional constant + item c. Thenit represents a multiple equality of the form + c=f1=...=fk. + + Objects of the class Item_equal are used for the following: + + 1. An object Item_equal(t1.f1,...,tk.fk) allows us to consider any + pair of tables ti and tj as joined by an equi-condition. + Thus it provide us with additional access paths from table to table. + + 2. An object Item_equal(t1.f1,...,tk.fk) is applied to deduce new + SARGable predicates: + f1=...=fk AND P(fi) => f1=...=fk AND P(fi) AND P(fj). + It also can give us additional index scans and can allow us to + improve selectivity estimates. + + 3. An object Item_equal(t1.f1,...,tk.fk) is used to optimize the + selected execution plan for the query: if table ti is accessed + before the table tj then in any predicate P in the where condition + the occurence of tj.fj is substituted for ti.fi. This can allow + an evaluation of the predicate at an earlier step. + + When feature 1 is supported they say that join transitive closure + is employed. + When feature 2 is supported they say that search argument transitive + closure is employed. + Both features are usually supported by preprocessing original query and + adding additional predicates. + We do not just add predicates, we rather dynamically replace some + predicates that can not be used to access tables in the investigated + plan for those, obtained by substitution of some fields for equal fields, + that can be used. +*/ + class Item_equal: public Item_bool_func { List fields; /* list of equal field items */ @@ -924,7 +994,7 @@ public: inline Item* get_const() { return const_item; } void add(Item *c); void add(Item_field *f); - bool is_false() { return cond_false; } + uint members(); bool contains(Field *field); Item_field* get_first() { return fields.head(); } void merge(Item_equal *item); @@ -932,22 +1002,29 @@ public: longlong val_int(); const char *func_name() const { return "multiple equal"; } optimize_type select_optimize() const { return OPTIMIZE_EQUAL; } - void sort(void *table_join_idx); + void sort(Item_field_cmpfunc cmp, void *arg); friend class Item_equal_iterator; void fix_length_and_dec(); bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref); void update_used_tables(); bool walk(Item_processor processor, byte *arg); - Item *traverse(Item_calculator calculator, byte *arg); + Item *transform(Item_transformer transformer, byte *arg); void print(String *str); }; class COND_EQUAL { public: - COND_EQUAL *parent_level; - List current_level; - COND_EQUAL() { parent_level= 0; } + uint max_members; /* max number of members the current level + list and all lower level lists */ + COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */ + List current_level; /* list of multiple equalities of + the current and level */ + COND_EQUAL() + { + max_members= 0; + upper_levels= 0; + } }; @@ -971,7 +1048,9 @@ public: class Item_cond_and :public Item_cond { public: - COND_EQUAL cond_equal; + COND_EQUAL cond_equal; /* contains list of Item_equal objects for + the current and level and reference + to multiple equalities of upper and levels */ Item_cond_and() :Item_cond() {} Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {} Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {} diff --git a/sql/item_func.cc b/sql/item_func.cc index 10c22c2b386..14ce7f79f47 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -242,37 +242,42 @@ bool Item_func::walk (Item_processor processor, byte *argument) return (this->*processor)(argument); } -Item *Item_func::traverse(Item_calculator calculator, byte *argument) + +/* + Transform an Item_func object with a transformer callback function + + SYNOPSIS + transform() + transformer the transformer callback function to be applied to the nodes + of the tree of the object + argument parameter to be passed to the transformer + + DESCRIPTION + The function recursively applies the transform method with the + same transformer to each argument the function. + If the call of the method for a member item returns a new item + the old item is substituted for a new one. + After this the transform method is applied to the root node + of the Item_func object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_func::transform(Item_transformer transformer, byte *argument) { if (arg_count) { Item **arg,**arg_end; for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) { - Item *new_item= (*arg)->traverse(calculator, argument); + Item *new_item= (*arg)->transform(transformer, argument); if (!new_item) return 0; *arg= new_item; } } - return (this->*calculator)(argument); -} - -Item *Item_func::equal_fields_propagator(byte *argument) -{ - if (arg_count) - { - Item **arg,**arg_end; - for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) - { - if (!(*arg)->fixed) - { - fix_fields(current_thd, 0, 0); - break; - } - } - } - return this; + return (this->*transformer)(argument); } diff --git a/sql/item_func.h b/sql/item_func.h index 5add4b8a739..1e274237cb8 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -148,8 +148,7 @@ public: bool agg_arg_collations_for_comparison(DTCollation &c, Item **items, uint nitems); bool walk(Item_processor processor, byte *arg); - Item *traverse(Item_calculator calculator, byte *arg); - Item *equal_fields_propagator(byte *arg); + Item *transform(Item_transformer transformer, byte *arg); }; diff --git a/sql/item_row.cc b/sql/item_row.cc index d7afe9ad5f0..7a457c9db7a 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -140,16 +140,16 @@ bool Item_row::walk(Item_processor processor, byte *arg) return (this->*processor)(arg); } -Item *Item_row::traverse(Item_calculator calculator, byte *arg) +Item *Item_row::transform(Item_transformer transformer, byte *arg) { for (uint i= 0; i < arg_count; i++) { - Item *new_item= items[i]->traverse(calculator, arg); + Item *new_item= items[i]->transform(transformer, arg); if (!new_item) return 0; items[i]= new_item; } - return (this->*calculator)(arg); + return (this->*transformer)(arg); } void Item_row::bring_value() diff --git a/sql/item_row.h b/sql/item_row.h index de6c18bf0d9..a550bce4b5a 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -71,7 +71,7 @@ public: void print(String *str); bool walk(Item_processor processor, byte *arg); - Item *traverse(Item_calculator calculator, byte *arg); + Item *transform(Item_transformer transformer, byte *arg); uint cols() { return arg_count; } Item* el(uint i) { return items[i]; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 0bc02ca3dd5..2bc37de86d8 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -427,13 +427,13 @@ public: return item->walk(processor, arg) || Item_str_func::walk(processor, arg); } - Item *traverse(Item_calculator calculator, byte *arg) + Item *transform(Item_transformer transformer, byte *arg) { - Item *new_item= item->traverse(calculator, arg); + Item *new_item= item->transform(transformer, arg); if (!new_item) return 0; item= new_item; - return Item_str_func::traverse(calculator, arg); + return Item_str_func::transform(transformer, arg); } void print(String *str); }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 80d9bf92b45..67603eeeccd 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1494,6 +1494,21 @@ static int get_quick_select_params(SEL_TREE *tree, PARAM *param, } +/* + Build a SEL_TREE for a simple predicate + + SYNOPSIS + get_func_mm_tree() + param PARAM from SQL_SELECT::test_quick_select + cond_func item for the predicate + field field in the predicate + value constant in the predicate + cmp_type compare type for the field + + RETURN + Pointer to thre built tree +*/ + static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, Field *field, Item *value, Item_result cmp_type) @@ -1501,21 +1516,18 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, SEL_TREE *tree= 0; DBUG_ENTER("get_func_mm_tree"); - if (cond_func->functype() == Item_func::NE_FUNC) - { - + switch (cond_func->functype()) { + case Item_func::NE_FUNC: tree= get_mm_parts(param, field, Item_func::LT_FUNC, value, cmp_type); if (tree) { tree= tree_or(param, tree, get_mm_parts(param, field, - Item_func::GT_FUNC, - value, cmp_type)); + Item_func::GT_FUNC, + value, cmp_type)); } - } - else if (cond_func->functype() == Item_func::BETWEEN) - { - + break; + case Item_func::BETWEEN: tree= get_mm_parts(param, field, Item_func::GE_FUNC, cond_func->arguments()[1],cmp_type); if (tree) @@ -1525,30 +1537,42 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, cond_func->arguments()[2], cmp_type)); } - } - else if (cond_func->functype() == Item_func::IN_FUNC) + break; + case Item_func::IN_FUNC: { Item_func_in *func=(Item_func_in*) cond_func; tree= get_mm_parts(param, field, Item_func::EQ_FUNC, func->arguments()[1], cmp_type); if (tree) { - for (uint i =2 ; i < func->argument_count() ; i++) + Item **arg, **end; + for (arg= func->arguments()+2, end= arg+func->argument_count()-2; + arg < end ; arg++) { tree= tree_or(param, tree, get_mm_parts(param, field, Item_func::EQ_FUNC, - func->arguments()[i], + *arg, cmp_type)); } } + break; } - else + default: { + /* + Here the function for the following predicates are processed: + <, <=, =, >=, >, LIKE, IS NULL, IS NOT NULL. + If the predicate is of the form (value op field) it is handled + as the equivalent predicate (field rev_op value), e.g. + 2 <= a is handled as a >= 2. + */ Item_func::Functype func_type= (value != cond_func->arguments()[0]) ? cond_func->functype() : ((Item_bool_func2*) cond_func)->rev_functype(); tree= get_mm_parts(param, field, func_type, value, cmp_type); } + } + DBUG_RETURN(tree); } @@ -1625,71 +1649,71 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); // Can't be calculated - if (cond_func->functype() == Item_func::BETWEEN) - { + switch (cond_func->functype()) { + case Item_func::BETWEEN: + if (cond_func->arguments()[0]->type() != Item::FIELD_ITEM) + DBUG_RETURN(0); + field_item= (Item_field*) (cond_func->arguments()[0]); + value= NULL; + break; + case Item_func::IN_FUNC: + { + Item_func_in *func=(Item_func_in*) cond_func; + if (func->key_item()->type() != Item::FIELD_ITEM) + DBUG_RETURN(0); + field_item= (Item_field*) (func->key_item()); + value= NULL; + break; + } + case Item_func::MULT_EQUAL_FUNC: + { + Item_equal *item_equal= (Item_equal *) cond; + if (!(value= item_equal->get_const())) + DBUG_RETURN(0); + Item_equal_iterator it(*item_equal); + ref_tables= value->used_tables(); + while ((field_item= it++)) + { + Field *field= field_item->field; + Item_result cmp_type= field->cmp_type(); + if (!((ref_tables | field->table->map) & param_comp)) + { + tree= get_mm_parts(param, field, Item_func::EQ_FUNC, + value,cmp_type); + ftree= !ftree ? tree : tree_and(param, ftree, tree); + } + } + + DBUG_RETURN(ftree); + } + default: if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) { field_item= (Item_field*) (cond_func->arguments()[0]); - value= NULL; + value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0; } - else - DBUG_RETURN(0); - } - else if (cond_func->functype() == Item_func::IN_FUNC) - { - Item_func_in *func=(Item_func_in*) cond_func; - if (func->key_item()->type() == Item::FIELD_ITEM) - { - field_item= (Item_field*) (func->key_item()); - value= NULL; - } - else - DBUG_RETURN(0); - } - else if (cond_func->functype() == Item_func::MULT_EQUAL_FUNC) - { - Item_equal *item_equal= (Item_equal *) cond; - Item_equal_iterator it(*item_equal); - if (!(value= item_equal->get_const())) - value= it++; - while (value) - { - ref_tables= value->used_tables(); - Item_equal_iterator li(*item_equal); - while ((field_item= li++)) - { - if (field_item != value) - { - Field *field= field_item->field; - Item_result cmp_type= field->cmp_type(); - if (!((ref_tables | field->table->map) & param_comp)) - { - tree= get_mm_parts(param, field, Item_func::EQ_FUNC, - value,cmp_type); - ftree= !ftree ? tree : tree_and(param, ftree, tree); - } - } - } - if (item_equal->get_const()) - break; - value= it++; - } - DBUG_RETURN(ftree); - } - else if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) - { - field_item= (Item_field*) (cond_func->arguments()[0]); - value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0; - } - else if (cond_func->have_rev_func() && + else if (cond_func->have_rev_func() && cond_func->arguments()[1]->type() == Item::FIELD_ITEM) - { - field_item= (Item_field*) (cond_func->arguments()[1]); - value= cond_func->arguments()[0]; + { + field_item= (Item_field*) (cond_func->arguments()[1]); + value= cond_func->arguments()[0]; + } + else + DBUG_RETURN(0); } - else - DBUG_RETURN(0); + /* + If the where condition contains a predicate (ti.field op const), + then not only SELL_TREE for this predicate is built, but + the trees for the results of substitution of ti.field for + each tj.field belonging to the same multiple equality as ti.field + are built as well. + E.g. for WHERE t1.a=t2.a AND t2.a > 10 + a SEL_TREE for t2.a > 10 will be built for quick select from t2 + and + a SEL_TREE for t1.a > 10 will be built for quick select from t1. + */ + for (uint i= 0; i < cond_func->arg_count; i++) { Item *arg= cond_func->arguments()[i]; diff --git a/sql/sql_list.h b/sql/sql_list.h index bac4a2a8655..5318237b786 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -161,6 +161,15 @@ public: *prev= *last; last= prev; } + inline void prepand(base_list *list) + { + if (!list->is_empty()) + { + *list->last= first; + first= list->first; + elements+= list->elements; + } + } inline list_node* last_node() { return *last; } inline list_node* first_node() { return first;} inline void *head() { return first->info; } @@ -273,6 +282,7 @@ public: inline T* pop() {return (T*) base_list::pop(); } inline void concat(List *list) { base_list::concat(list); } inline void disjoin(List *list) { base_list::disjoin(list); } + inline void prepand(List *list) { base_list::prepand(list); } void delete_elements(void) { list_node *element,*next; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4736cca1d41..ba911f47490 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -45,6 +45,7 @@ static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, JOIN_TAB *join_tab, uint tables, COND *conds, + COND_EQUAL *cond_equal, table_map table_map, SELECT_LEX *select_lex); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key); @@ -73,7 +74,6 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, uint select_options, const char *info, Item *having, Procedure *proc, SELECT_LEX_UNIT *unit); -static Item *flatten_condition(COND *cond); static COND *build_all_equal_items(COND *cond, COND_EQUAL *inherited); static COND* substitute_for_best_equal_field(COND *cond, @@ -530,19 +530,15 @@ JOIN::optimize() } #endif - /* eliminate NOT operators */ + /* Eliminate NOT operators */ conds= eliminate_not_funcs(conds); DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); - - /* Eliminate nested AND/OR in conditions */ - if (conds) - conds= flatten_condition(conds); { TABLE_LIST *tables; for (tables= tables_list; tables; tables= tables->next) { if (tables->on_expr) - tables->on_expr= flatten_condition(tables->on_expr); + tables->on_expr= eliminate_not_funcs(tables->on_expr); } } @@ -1868,7 +1864,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, if (conds || outer_join) if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables, - conds, ~outer_join, join->select_lex)) + conds, join->cond_equal, + ~outer_join, join->select_lex)) DBUG_RETURN(1); /* Read tables with 0 or 1 rows (system tables) */ @@ -2298,6 +2295,27 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, } +/* + Add possible keys to array of possible keys originated from a simple predicate + + SYNPOSIS + add_key_equal_field() + key_fields Pointer to add key, if usable + and_level And level, to be stored in KEY_FIELD + field Field used in comparision + eq_func True if we used =, <=> or IS NULL + value Value used for comparison with field + Is NULL for BETWEEN and IN + usable_tables Tables which can be used for key optimization + + NOTES + If field items f1 and f2 belong to the same multiple equality and + a key is added for f1, the the same key is added for f2. + + RETURN + *key_fields is incremented if we stored a key in the array +*/ + static void add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, Item_field *field_item, @@ -2601,15 +2619,19 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, - uint tables, COND *cond, table_map normal_tables, - SELECT_LEX *select_lex) + uint tables, COND *cond, COND_EQUAL *cond_equal, + table_map normal_tables, SELECT_LEX *select_lex) { uint and_level,i,found_eq_constant; KEY_FIELD *key_fields, *end, *field; + uint m= 1; + + if (cond_equal && cond_equal->max_members) + m= cond_equal->max_members; if (!(key_fields=(KEY_FIELD*) thd->alloc(sizeof(key_fields[0])* - (thd->lex->current_select->cond_count+1)*2))) + (thd->lex->current_select->cond_count+1)*2*m))) return TRUE; /* purecov: inspected */ and_level= 0; field= end= key_fields; @@ -2838,8 +2860,6 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, do { uint keypart=keyuse->keypart; - uint found_part_ref_or_null= KEY_OPTIMIZE_REF_OR_NULL; - bool usable= 0; table_map best_part_found_ref= 0; double best_prev_record_reads= DBL_MAX; do @@ -2848,8 +2868,9 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, !(found_ref_or_null & keyuse->optimize)) { found_part|=keyuse->keypart_map; - double tmp= prev_record_reads(join, - (table_map) (found_ref | keyuse->used_tables)); + double tmp= prev_record_reads(join, + (found_ref | + keyuse->used_tables)); if (tmp < best_prev_record_reads) { best_part_found_ref= keyuse->used_tables; @@ -2857,15 +2878,17 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, } if (rec > keyuse->ref_table_rows) rec= keyuse->ref_table_rows; - found_part_ref_or_null&= keyuse->optimize; - usable= 1; + /* + If there is one 'key_column IS NULL' expression, we can + use this ref_or_null optimsation of this field + */ + found_ref_or_null|= (keyuse->optimize & + KEY_OPTIMIZE_REF_OR_NULL); } keyuse++; - found_ref|= best_part_found_ref; } while (keyuse->table == table && keyuse->key == key && keyuse->keypart == keypart); - if (usable) - found_ref_or_null|= found_part_ref_or_null; + found_ref|= best_part_found_ref; } while (keyuse->table == table && keyuse->key == key); /* @@ -4274,55 +4297,6 @@ template class List; template class List_iterator; #endif -/* - Eliminate nesting in AND/OR subexpressions od a condition - - SYNOPSIS - flatten_condition() - cond condition where to eliminate nesting - - DESCRIPTION - The function traverse the condition and recursively eliminates - nesting for AND/OR subexpressions: - ... AND (p AND ... r) AND ... => ... AND p AND ... r AND ... - ... OR (p OR ... r) OR ... => ... OR p OR ... r OR ... - - NOTES - Nesting in AND/OR subexpresions inside of NOT/XOR formulas is not - eliminated. - - RETURN - The transformed condition -*/ -static Item *flatten_condition(COND *cond) -{ - if (cond->type() == Item::COND_ITEM) - { - Item_func::Functype functype= ((Item_cond*) cond)->functype(); - if (functype == Item_func::COND_AND_FUNC || - functype == Item_func::COND_OR_FUNC) - { - - List *args= ((Item_cond*) cond)->argument_list(); - List_iterator li(*args); - Item *item; - List list; - while ((item= li++)) - { - item= flatten_condition(item); - if (item->type() == Item::COND_ITEM && - ((Item_func*) item)->functype() == functype) - { - list.concat(((Item_cond*) item)->argument_list()); - li.remove(); - } - } - args->concat(&list); - } - } - return cond; -} - /* Find the multiple equality predicate containing a field @@ -4359,14 +4333,14 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, goto finish; } in_upper_level= TRUE; - cond_equal= cond_equal->parent_level; + cond_equal= cond_equal->upper_levels; } in_upper_level= FALSE; finish: - if (inherited_fl) - *inherited_fl= in_upper_level; + *inherited_fl= in_upper_level; return item; } + /* Check whether an item is a simple equality predicate and if so @@ -4379,22 +4353,40 @@ finish: DESCRIPTION This function first checks whether an item is a simple equality i.e. - the one that equates a field with another field or a constant. + the one that equates a field with another field or a constant + (item=constant_item or item=field_item). If this is the case the function looks a for a multiple equality - in the lists referenced directly or indirectly by cond_equal. If it - doesn't find any it builds a multiple equality that covers - the predicate, i.e. the predicate can be inferred from it. The built - multiple equality could be obtained in such a way: create a binary - multiple equality equivalent to the predicate, than merge it, if - possible, with one of old multiple equalities. This guarantees that - the set of multiple equalities covering equality predicates will + in the lists referenced directly or indirectly by cond_equal inferring + the given simple equality. If it doesn't find any, it builds a multiple + equality that covers the predicate, i.e. the predicate can be inferred + from it. + The built multiple equality could be obtained in such a way: + create a binary multiple equality equivalent to the predicate, then + merge it, if possible, with one of old multiple equalities. + This guarantees that the set of multiple equalities covering equality + predicates will be minimal. + EXAMPLE + For the where condition + WHERE a=b AND b=c AND + (b=2 OR f=e) + the check_equality will be called for the following equality + predicates a=b, b=c, b=2 and f=e. + For a=b it will be called with *cond_equal=(0,[]) and will transform + *cond_equal into (0,[Item_equal(a,b)]). + For b=c it will be called with *cond_equal=(0,[Item_equal(a,b)]) + and will transform *cond_equal into CE=(0,[Item_equal(a,b,c)]). + For b=2 it will be called with *cond_equal=(ptr(CE),[]) + and will transform *cond_equal into (ptr(CE,[Item_equal(2,a,b,c)]). + For f=e it will be called with *cond_equal=(ptr(CE), []) + and will transform *cond_equal into (ptr(CE,[Item_equal(f,e)]). + NOTES Now only fields that have the same type defintions (verified by the Field::eq_def method) are placed to the same multiple equalities. Because of this some equality predicates are not eliminated and - can be used in constant propagation procedure. + can be used in the constant propagation procedure. We could weeken the equlity test as soon as at least one of the equal fields is to be equal to a constant. It would require a more complicated implementation: we would have to store, in @@ -4408,7 +4400,7 @@ finish: build a new multiple equality for the equality predicate. If it processes the equality of the form field1=field2, it looks for multiple equalities me1 containig field1 and me2 containing - field2. If only one of them is found the fuction expand it by + field2. If only one of them is found the fuction expands it with the lacking field. If multiple equalities for both fields are found they are merged. If both searches fail a new multiple equality containing just field1 and field2 is added to the existing @@ -4446,7 +4438,7 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) if (!left_field->eq_def(right_field)) return FALSE; - if (left_field->eq(right_field)) + if (left_field->eq(right_field)) /* f = f */ return TRUE; /* Search for multiple equalities containing field1 and/or field2 */ @@ -4460,7 +4452,8 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) { /* The equality predicate is inference of one of the existing - multiple equalities + multiple equalities, i.e the condition is already covered + by upper level equalities */ return TRUE; } @@ -4468,17 +4461,20 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) /* Copy the found multiple equalities at the current level if needed */ if (left_copyfl) { + /* left_item_equal of an upper level contains left_item */ left_item_equal= new Item_equal(left_item_equal); cond_equal->current_level.push_back(left_item_equal); } if (right_copyfl) { + /* right_item_equal of an upper level contains right_item */ right_item_equal= new Item_equal(right_item_equal); cond_equal->current_level.push_back(right_item_equal); } if (left_item_equal) { + /* left item was found in the current or one of the upper levels */ if (! right_item_equal) left_item_equal->add((Item_field *) right_item); else @@ -4493,11 +4489,12 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) } else { + /* left item was not found neither the current nor in upper levels */ if (right_item_equal) right_item_equal->add((Item_field *) left_item); else { - /* Multiple equalities for neither of the fields has been found */ + /* None of the fields was found in multiple equalities */ Item_equal *item= new Item_equal((Item_field *) left_item, (Item_field *) right_item); cond_equal->current_level.push_back(item); @@ -4505,7 +4502,7 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) } return TRUE; } - else + { /* The predicate of the form field=const/const=field is processed */ Item *const_item= 0; @@ -4568,10 +4565,17 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) taking into account inherited equalities from upper levels. If an equality predicate is used not in a conjunction it's just replaced by a multiple equality predicate. - The functuion also traverse the cond tree and and for each field reference - sets a ponter to the multiple equality item containing the field, if there + For each 'and' level the function set a pointer to the inherited + multiple equalities in the cond_equal field of the associated + object of the type Item_cond_and. + The function also traverses the cond tree and and for each field reference + sets a pointer to the multiple equality item containing the field, if there is any. If this multiple equality equates fields to a constant the - function replace the field reference by the constant. + function replace the field reference by the constant. + The function also determines the maximum number of members in + equality lists of each Item_cond_and object assigning it to + cond_equal->max_members of this object and updating accordingly + the upper levels COND_EQUAL structures. NOTES Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of @@ -4579,14 +4583,14 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) equality predicates that is equivalent to the conjunction. Thus, =(a1,a2,a3) can substitute for ((a1=a3) AND (a2=a3) AND (a2=a1)) as it is equivalent to ((a1=a2) AND (a2=a3)). - The function always makes a subsitution of all equality predicates occured + The function always makes a substitution of all equality predicates occured in a conjuction for a minimal set of multiple equality predicates. This set can be considered as a canonical representation of the sub-conjunction of the equality predicates. E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by (=(t1.a,t2.b,t3.c) AND t2.b>5), not by (=(t1.a,t2.b) AND =(t1.a,t3.c) AND t2.b>5); - while (t1.a=t2.b AND t2.b>5 AND t3.c,t4.d) is replaced by + while (t1.a=t2.b AND t2.b>5 AND t3.c=t4.d) is replaced by (=(t1.a,t2.b) AND =(t3.c=t4.d) AND t2.b>5), but if additionally =(t4.d,t2.b) is inherited, it will be replaced by (=(t1.a,t2.b,t3.c,t4.d) AND t2.b>5) @@ -4600,6 +4604,13 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) multiple equalities is saved in the and condition object (Item_cond_and). This chain allows us for any field reference occurence easyly to find a multiple equality that must be held for this occurence. + For each AND level we do the following: + - scan it for all equality predicate (=) items + - join them into disjoint Item_equal() groups + - process the included OR conditions recursively to do the same for + lower AND levels. + We need to do things in this order as lower AND levels need to know about + all possible Item_equal objects in upper levels. RETURN pointer to the transformed condition @@ -4608,8 +4619,10 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) static COND *build_all_equal_items(COND *cond, COND_EQUAL *inherited) { + Item_equal *item_equal; + uint members; COND_EQUAL cond_equal; - cond_equal.parent_level= inherited; + cond_equal.upper_levels= inherited; if (cond->type() == Item::COND_ITEM) { @@ -4625,7 +4638,7 @@ static COND *build_all_equal_items(COND *cond, /* Retrieve all conjucts of this level detecting the equality that are subject to substitution by multiple equality items and - removing each such predicate from the conjunction after after having + removing each such predicate from the conjunction after having found/created a multiple equality whose inference the predicate is. */ while ((item= li++)) @@ -4635,10 +4648,25 @@ static COND *build_all_equal_items(COND *cond, } List_iterator_fast it(cond_equal.current_level); - while ((item= it++)) + while ((item_equal= it++)) { - item->fix_fields(current_thd, 0, 0); - } + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); + members= item_equal->members(); + if (cond_equal.max_members < members) + cond_equal.max_members= members; + } + members= cond_equal.max_members; + if (inherited && inherited->max_members < members) + { + do + { + inherited->max_members= members; + inherited= inherited->upper_levels; + } + while (inherited); + } + ((Item_cond_and*)cond)->cond_equal= cond_equal; inherited= &(((Item_cond_and*)cond)->cond_equal); } @@ -4646,14 +4674,14 @@ static COND *build_all_equal_items(COND *cond, Make replacement of equality predicates for lower levels of the condition expression. */ - List_iterator it(*args); - while((item= it++)) + li.rewind(); + while((item= li++)) { Item *new_item; if ((new_item = build_all_equal_items(item, inherited))!= item) { /* This replacement happens only for standalone equalities */ - it.replace(new_item); + li.replace(new_item); } } if (and_level) @@ -4661,24 +4689,78 @@ static COND *build_all_equal_items(COND *cond, } else if (cond->type() == Item::FUNC_ITEM) { - /* Standalone equalities are handled here */ - Item_equal *item_equal; + /* + If an equality predicate forms the whole and level, + we call it standalone equality and it's processed here. + E.g. in the following where condition + WHERE a=5 AND (b=5 or a=c) + (b=5) and (a=c) are standalone equalities. + In general we can't leave alone standalone eqalities: + for WHERE a=b AND c=d AND (b=c OR d=5) + b=c is replaced by =(a,b,c,d). + */ if (check_equality(cond, &cond_equal) && (item_equal= cond_equal.current_level.pop())) { - item_equal->fix_fields(current_thd, 0, 0); + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); return item_equal; } - else - { - cond= cond->traverse(&Item::equal_fields_propagator, - (byte *) inherited); - cond->update_used_tables(); - } + /* + For each field reference in cond, not from equalitym predicates, + set a pointer to the multiple equality if belongs to (if there is any) + */ + cond= cond->transform(&Item::equal_fields_propagator, + (byte *) inherited); + cond->update_used_tables(); } return cond; } +/* + Compare field items by table order in the execution plan + + SYNOPSIS + compare_fields_by_table_order() + field1 first field item to compare + field2 second field item to compare + table_join_idx index to tables determining table order + + DESCRIPTION + field1 considered as better than field2 if the table containing + field1 is accessed earlier than the table containing field2. + The function finds out what of two fields is better according + this criteria. + + RETURN + 1, if field1 is better than field2 + -1, if field2 is better than field1 + 0, otherwise +*/ + +static int compare_fields_by_table_order(Item_field *field1, + Item_field *field2, + void *table_join_idx) +{ + int cmp= 0; + bool outer_ref= 0; + if (field2->used_tables() & OUTER_REF_TABLE_BIT) + { + outer_ref= 1; + cmp= -1; + } + if (field2->used_tables() & OUTER_REF_TABLE_BIT) + { + outer_ref= 1; + cmp++; + } + if (outer_ref) + return cmp; + JOIN_TAB **idx= (JOIN_TAB **) table_join_idx; + cmp= idx[field2->field->table->tablenr]-idx[field1->field->table->tablenr]; + return cmp < 0 ? -1 : (cmp ? 1 : 0); +} + /* Generate minimal set of simple equalities equivalent to a multiple equality @@ -4686,7 +4768,7 @@ static COND *build_all_equal_items(COND *cond, SYNOPSIS eliminate_item_equal() cond condition to add the generated equality to - cond_equal structure to access multiple equality of upper levels + upper_levels structure to access multiple equality of upper levels item_equal multiple equality to generate simple equality from DESCRIPTION @@ -4700,20 +4782,37 @@ static COND *build_all_equal_items(COND *cond, NOTES Before generating an equality function checks that it has not been generated for multiple equalies of the upper levels. + E.g. for the following where condition + WHERE a=5 AND ((a=b AND b=c) OR c>4) + the upper level AND condition will contain =(5,a), + while the lower level AND condition will contain =(5,a,b,c). + When splitting =(5,a,b,c) into a separate equality predicates + we should omit 5=a, as we have it already in the upper level. + The following where condition gives us a more complicated case: + WHERE t1.a=t2.b AND t3.c=t4.d AND (t2.b=t3.c OR t4.e>5 ...) AND ... + Given the tables are accessed in the order t1->t2->t3->t4 for + the selected query execution plan the lower level multiple + equality =(t1.a,t2.b,t3.c,t4.d) formally should be converted to + t1.a=t2.b AND t1.a=t3.c AND t1.a=t4.d. But t1.a=t2.a will be + generated for the upper level. Also t3.c=t4.d will be generated there. + So only t1.a=t3.c should be left in the lower level. If cond is equal to 0, then not more then one equality is generated and a pointer to it is returned as the result of the function. RETURN The condition with generated simple equalities or - a pointer to the simple generated equality. + a pointer to the simple generated equality, if success. + 0, otherwise. */ -static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, +static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, Item_equal *item_equal) { + List eq_list; + Item_func_eq *eq_item= 0; Item *item_const= item_equal->get_const(); Item_equal_iterator it(*item_equal); - Item *head; + Item *head; if (item_const) head= item_const; else @@ -4722,10 +4821,9 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, it++; } Item_field *item_field; - Item *new_item= 0; while ((item_field= it++)) { - Item_equal *upper= item_field->find_item_equal(cond_equal); + Item_equal *upper= item_field->find_item_equal(upper_levels); Item_field *item= item_field; if (upper) { @@ -4736,27 +4834,31 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal, Item_equal_iterator li(*item_equal); while ((item= li++) != item_field) { - if (item->find_item_equal(cond_equal) == upper) + if (item->find_item_equal(upper_levels) == upper) break; } } } if (item == item_field) { - if (!cond && new_item) - { - cond= new Item_cond_and(); - ((Item_cond *) cond)->add(new_item); - } - item_field->item_equal= item_equal; - new_item= new Item_func_eq(item_field, head); - ((Item_func_eq *) new_item)->fix_length_and_dec(); - if (cond) - ((Item_cond *) cond)->add(new_item); - } + if (eq_item) + eq_list.push_back(eq_item); + eq_item= new Item_func_eq(item_field, head); + if (!eq_item) + return 0; + eq_item->set_cmp_func(); + } } + + if (!cond && !eq_list.head()) + return eq_item; + + eq_list.push_back(eq_item); if (!cond) - cond= (COND *) new_item; + cond= new Item_cond_and(eq_list); + else + ((Item_cond *) cond)->add_at_head(&eq_list); + return cond; } @@ -4811,9 +4913,9 @@ static COND* substitute_for_best_equal_field(COND *cond, cond_list->disjoin((List *) &cond_equal->current_level); List_iterator_fast it(cond_equal->current_level); - while((item_equal= it++)) + while ((item_equal= it++)) { - item_equal->sort(table_join_idx); + item_equal->sort(&compare_fields_by_table_order, table_join_idx); } } @@ -4830,9 +4932,9 @@ static COND* substitute_for_best_equal_field(COND *cond, if (and_level) { List_iterator_fast it(cond_equal->current_level); - while((item_equal= it++)) + while ((item_equal= it++)) { - eliminate_item_equal(cond, cond_equal->parent_level, item_equal); + eliminate_item_equal(cond, cond_equal->upper_levels, item_equal); } } } @@ -4840,7 +4942,7 @@ static COND* substitute_for_best_equal_field(COND *cond, ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) { item_equal= (Item_equal *) cond; - item_equal->sort(table_join_idx); + item_equal->sort(&compare_fields_by_table_order, table_join_idx); if (cond_equal && cond_equal->current_level.head() == item_equal) cond_equal= 0; return eliminate_item_equal(0, cond_equal, item_equal); @@ -5050,7 +5152,23 @@ COND *eliminate_not_funcs(COND *cond) { Item *new_item= eliminate_not_funcs(item); if (item != new_item) - VOID(li.replace(new_item)); /* replace item with a new condition */ + { + /* + Replace item with a new condition. + Remove unnecessary and/or level + that might appear after the replacement. + */ + if (new_item->type() == Item::COND_ITEM && + ((Item_cond*) new_item)->functype() == + ((Item_cond*) cond)->functype()) + { + List *list= ((Item_cond*) new_item)->argument_list(); + li.replace(*list); + list->empty(); + } + else + li.replace(new_item); + } } } else if (cond->type() == Item::FUNC_ITEM && /* 'NOT' operation? */ @@ -5228,22 +5346,6 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) return (COND*) 0; } } - else if (cond->type() == Item::FUNC_ITEM && - ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) - { - /* - The is_false method for an multiple equality item returns 1 - when the conjunction with this item originally contained an - equality that was inconsistent with the multiple equality - predicate or has been inherited from other multiple equality - for which is_false returns 1. - */ - if (((Item_equal *) cond)->is_false()) - { - *cond_value= Item::COND_FALSE; - return (COND*) 0; - } - } else if (cond->const_item()) { *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE; @@ -8357,6 +8459,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) } if (!(cache->field=(CACHE_FIELD*) sql_alloc(sizeof(CACHE_FIELD)*(cache->fields+table_count*2)+(blobs+1)* + sizeof(CACHE_FIELD*)))) { my_free((gptr) cache->buff,MYF(0)); /* purecov: inspected */ From d24a2296bb267f234b74f3aae4dc02817a7a3bbd Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 19 Feb 2004 01:12:49 -0800 Subject: [PATCH 07/10] Manual merge --- sql/item.h | 89 ++++++++++++++++-------- sql/opt_range.cc | 104 +++++++++++++++++++--------- sql/sql_select.cc | 169 +++++++++++++++++++++++++--------------------- 3 files changed, 224 insertions(+), 138 deletions(-) diff --git a/sql/item.h b/sql/item.h index d4400f1058d..035f491b349 100644 --- a/sql/item.h +++ b/sql/item.h @@ -126,8 +126,8 @@ public: top AND/OR ctructure of WHERE clause to protect it of optimisation changes in prepared statements */ - Item(THD *thd, Item &item); - virtual ~Item() { name=0; cleanup(); } /*lint -e1509 */ + Item(THD *thd, Item *item); + virtual ~Item() { name=0; } /*lint -e1509 */ void set_name(const char *str,uint length, CHARSET_INFO *cs); void init_make_field(Send_field *tmp_field,enum enum_field_types type); virtual void cleanup() { fixed=0; } @@ -206,6 +206,7 @@ public: virtual Item *get_tmp_table_item(THD *thd) { return copy_or_same(thd); } CHARSET_INFO *default_charset() const; + virtual CHARSET_INFO *compare_collation() { return NULL; } virtual bool walk(Item_processor processor, byte *arg) { @@ -239,6 +240,11 @@ public: /* Used in sql_select.cc:eliminate_not_funcs() */ virtual Item *neg_transformer() { return NULL; } + void delete_self() + { + cleanup(); + delete this; + } }; @@ -304,7 +310,10 @@ public: inline void make_field(Send_field *field) { - this_item()->make_field(field); + Item *it= this_item(); + + it->set_name(m_name.str, m_name.length, system_charset_info); + it->make_field(field); } inline Item_result result_type() const @@ -326,6 +335,11 @@ public: { str->append(m_name.str, m_name.length); } + + inline bool send(Protocol *protocol, String *str) + { + return this_item()->send(protocol, str); + } }; @@ -339,7 +353,7 @@ public: st_select_lex *depended_from; Item_ident(const char *db_name_par,const char *table_name_par, const char *field_name_par); - Item_ident(THD *thd, Item_ident &item); + Item_ident(THD *thd, Item_ident *item); const char *full_name() const; bool remove_dependence_processor(byte * arg); @@ -362,7 +376,7 @@ public: item_equal(0) { collation.set(DERIVATION_IMPLICIT); } // Constructor need to process subselect with temporary tables (see Item) - Item_field(THD *thd, Item_field &item); + Item_field(THD *thd, Item_field *item); Item_field(Field *field); enum Type type() const { return FIELD_ITEM; } bool eq(const Item *item, bool binary_cmp) const; @@ -393,6 +407,7 @@ public: bool get_time(TIME *ltime); bool is_null() { return field->is_null(); } Item *get_tmp_table_item(THD *thd); + void cleanup(); Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item *equal_fields_propagator(byte *arg); bool replace_equal_field_processor(byte *arg); @@ -583,7 +598,7 @@ public: CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) { collation.set(cs, dv); - str_value.set(str,length,cs); + str_value.set_or_copy_aligned(str,length,cs); /* We have to have a different max_length than 'length' here to ensure that we get the right length if we do use the item @@ -599,12 +614,11 @@ public: CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) { collation.set(cs, dv); - str_value.set(str,length,cs); + str_value.set_or_copy_aligned(str,length,cs); max_length= str_value.numchars()*cs->mbmaxlen; set_name(name_par,0,cs); decimals=NOT_FIXED_DEC; } - ~Item_string() {} enum Type type() const { return STRING_ITEM; } double val() { @@ -671,7 +685,6 @@ class Item_varbinary :public Item { public: Item_varbinary(const char *str,uint str_length); - ~Item_varbinary() {} enum Type type() const { return VARBIN_ITEM; } double val() { return (double) Item_varbinary::val_int(); } longlong val_int(); @@ -688,8 +701,8 @@ public: Field *result_field; /* Save result here */ Item_result_field() :result_field(0) {} // Constructor used for Item_sum/Item_cond_and/or (see Item comment) - Item_result_field(THD *thd, Item_result_field &item): - Item(thd, item), result_field(item.result_field) + Item_result_field(THD *thd, Item_result_field *item): + Item(thd, item), result_field(item->result_field) {} ~Item_result_field() {} /* Required with gcc 2.95 */ Field *get_tmp_table_field() { return result_field; } @@ -708,20 +721,25 @@ public: class Item_ref :public Item_ident { public: - Field *result_field; /* Save result here */ + Field *result_field; /* Save result here */ Item **ref; - Item_ref(const char *db_par, const char *table_name_par, - const char *field_name_par) - :Item_ident(db_par,table_name_par,field_name_par),ref(0) {} - Item_ref(Item **item, const char *table_name_par, const char *field_name_par) - :Item_ident(NullS,table_name_par,field_name_par),ref(item) {} + Item **hook_ptr; /* These two to restore */ + Item *orig_item; /* things in 'cleanup()' */ + Item_ref(Item **hook, Item *original,const char *db_par, + const char *table_name_par, const char *field_name_par) + :Item_ident(db_par,table_name_par,field_name_par),ref(0), hook_ptr(hook), + orig_item(original) {} + Item_ref(Item **item, Item **hook, + const char *table_name_par, const char *field_name_par) + :Item_ident(NullS,table_name_par,field_name_par), + ref(item), hook_ptr(hook), orig_item(hook ? *hook:0) {} // Constructor need to process subselect with temporary tables (see Item) - Item_ref(THD *thd, Item_ref &item) - :Item_ident(thd, item), ref(item.ref) {} + Item_ref(THD *thd, Item_ref *item, Item **hook) + :Item_ident(thd, item), ref(item->ref), + hook_ptr(hook), orig_item(hook ? *hook : 0) {} enum Type type() const { return REF_ITEM; } bool eq(const Item *item, bool binary_cmp) const { return ref && (*ref)->eq(item, binary_cmp); } - ~Item_ref() { if (ref && (*ref) && (*ref) != this) delete *ref; } double val() { double tmp=(*ref)->val_result(); @@ -766,6 +784,7 @@ public: } Item *real_item() { return *ref; } void print(String *str); + void cleanup(); }; class Item_in_subselect; @@ -776,7 +795,7 @@ protected: public: Item_ref_null_helper(Item_in_subselect* master, Item **item, const char *table_name_par, const char *field_name_par): - Item_ref(item, table_name_par, field_name_par), owner(master) {} + Item_ref(item, NULL, table_name_par, field_name_par), owner(master) {} double val(); longlong val_int(); String* val_str(String* s); @@ -840,7 +859,6 @@ public: name=item->name; cached_field_type= item->field_type(); } - ~Item_copy_string() { delete item; } enum Type type() const { return COPY_STR_ITEM; } enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return cached_field_type; } @@ -997,13 +1015,15 @@ public: void set_used_tables(table_map map) { used_table_map= map; } virtual bool allocate(uint i) { return 0; }; - virtual bool setup(Item *item) { example= item; return 0; }; - virtual void store(Item *)= 0; - void set_len_n_dec(uint32 max_len, uint8 dec) + virtual bool setup(Item *item) { - max_length= max_len; - decimals= dec; - } + example= item; + max_length= item->max_length; + decimals= item->decimals; + collation.set(item->collation); + return 0; + }; + virtual void store(Item *)= 0; enum Type type() const { return CACHE_ITEM; } static Item_cache* get_cache(Item_result type); table_map used_tables() const { return used_table_map; } @@ -1028,7 +1048,7 @@ class Item_cache_real: public Item_cache double value; public: Item_cache_real(): Item_cache() {} - + void store(Item *item); double val() { return value; } longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5)); } @@ -1101,6 +1121,11 @@ public: bool check_cols(uint c); bool null_inside(); void bring_value(); + void cleanup() + { + Item_cache::cleanup(); + values= 0; + } }; @@ -1111,6 +1136,7 @@ class Item_type_holder: public Item { protected: Item_result item_type; + Item_result orig_type; Field *field_example; public: Item_type_holder(THD*, Item*); @@ -1122,6 +1148,11 @@ public: String *val_str(String*); bool join_types(THD *thd, Item *); Field *example() { return field_example; } + void cleanup() + { + Item::cleanup(); + item_type= orig_type; + } }; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 67603eeeccd..6bcec4fef17 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -301,10 +301,11 @@ typedef struct st_qsel_param { uint imerge_cost_buff_size; /* size of the buffer */ } PARAM; -static SEL_TREE * get_mm_parts(PARAM *param,Field *field, +static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field, Item_func::Functype type,Item *value, Item_result cmp_type); -static SEL_ARG *get_mm_leaf(PARAM *param,Field *field,KEY_PART *key_part, +static SEL_ARG *get_mm_leaf(PARAM *param,COND *cond_func,Field *field, + KEY_PART *key_part, Item_func::Functype type,Item *value); static SEL_TREE *get_mm_tree(PARAM *param,COND *cond); static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree); @@ -612,14 +613,25 @@ SQL_SELECT::SQL_SELECT() :quick(0),cond(0),free_cond(0) } -SQL_SELECT::~SQL_SELECT() +void SQL_SELECT::cleanup() { delete quick; + quick= 0; if (free_cond) + { + free_cond=0; delete cond; + cond= 0; + } close_cached_file(&file); } + +SQL_SELECT::~SQL_SELECT() +{ + cleanup(); +} + #undef index // Fix for Unixware 7 QUICK_SELECT_I::QUICK_SELECT_I() @@ -1746,7 +1758,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) static SEL_TREE * -get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, +get_mm_parts(PARAM *param, COND *cond_func, Field *field, + Item_func::Functype type, Item *value, Item_result cmp_type) { DBUG_ENTER("get_mm_parts"); @@ -1768,7 +1781,8 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, DBUG_RETURN(0); // OOM if (!value || !(value->used_tables() & ~param->read_tables)) { - sel_arg=get_mm_leaf(param,key_part->field,key_part,type,value); + sel_arg=get_mm_leaf(param,cond_func, + key_part->field,key_part,type,value); if (!sel_arg) continue; if (sel_arg->type == SEL_ARG::IMPOSSIBLE) @@ -1794,7 +1808,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, static SEL_ARG * -get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, +get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, Item_func::Functype type,Item *value) { uint maybe_null=(uint) field->real_maybe_null(), copies; @@ -1803,6 +1817,32 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, char *str, *str2; DBUG_ENTER("get_mm_leaf"); + if (!value) // IS NULL or IS NOT NULL + { + if (field->table->outer_join) // Can't use a key on this + DBUG_RETURN(0); + if (!maybe_null) // Not null field + DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0); + if (!(tree=new SEL_ARG(field,is_null_string,is_null_string))) + DBUG_RETURN(0); // out of memory + if (type == Item_func::ISNOTNULL_FUNC) + { + tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */ + tree->max_flag=NO_MAX_RANGE; + } + DBUG_RETURN(tree); + } + + /* + We can't use an index when comparing strings of + different collations + */ + if (field->result_type() == STRING_RESULT && + value->result_type() == STRING_RESULT && + key_part->image_type == Field::itRAW && + ((Field_str*)field)->charset() != conf_func->compare_collation()) + DBUG_RETURN(0); + if (type == Item_func::LIKE_FUNC) { bool like_error; @@ -1866,22 +1906,6 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, DBUG_RETURN(new SEL_ARG(field,min_str,max_str)); } - if (!value) // IS NULL or IS NOT NULL - { - if (field->table->outer_join) // Can't use a key on this - DBUG_RETURN(0); - if (!maybe_null) // Not null field - DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0); - if (!(tree=new SEL_ARG(field,is_null_string,is_null_string))) - DBUG_RETURN(0); // out of memory - if (type == Item_func::ISNOTNULL_FUNC) - { - tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */ - tree->max_flag=NO_MAX_RANGE; - } - DBUG_RETURN(tree); - } - if (!field->optimize_range(param->real_keynr[key_part->key]) && type != Item_func::EQ_FUNC && type != Item_func::EQUAL_FUNC) @@ -1895,7 +1919,7 @@ get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, value->result_type() != STRING_RESULT && field->cmp_type() != value->result_type()) DBUG_RETURN(0); - + if (value->save_in_field(field, 1) > 0) { /* This happens when we try to insert a NULL field in a not null column */ @@ -3097,6 +3121,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) param->table->quick_rows[key]=records; param->table->quick_key_parts[key]=param->max_key_part+1; } + DBUG_PRINT("exit", ("Records: %lu", (ulong) records)); DBUG_RETURN(records); } @@ -3440,8 +3465,30 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, key_part->part_length+=HA_KEY_BLOB_LENGTH; key_part->null_bit= key_info->key_part[part].null_bit; } - if (!insert_dynamic(&quick->ranges,(gptr)&range)) - return quick; + if (insert_dynamic(&quick->ranges,(gptr)&range)) + goto err; + + /* + Add a NULL range if REF_OR_NULL optimization is used. + For example: + if we have "WHERE A=2 OR A IS NULL" we created the (A=2) range above + and have ref->null_ref_key set. Will create a new NULL range here. + */ + if (ref->null_ref_key) + { + QUICK_RANGE *null_range; + + *ref->null_ref_key= 1; // Set null byte then create a range + if (!(null_range= new QUICK_RANGE(ref->key_buff, ref->key_length, + ref->key_buff, ref->key_length, + EQ_RANGE))) + goto err; + *ref->null_ref_key= 0; // Clear null byte + if (insert_dynamic(&quick->ranges,(gptr)&null_range)) + goto err; + } + + return quick; err: delete quick; @@ -3584,12 +3631,7 @@ int QUICK_RANGE_SELECT::get_next() int result; if (range) { // Already read through key -/* result=((range->flag & EQ_RANGE) ? - file->index_next_same(record, (byte*) range->min_key, - range->min_length) : - file->index_next(record)); -*/ - result=((range->flag & (EQ_RANGE | GEOM_FLAG) ) ? + result=((range->flag & (EQ_RANGE | GEOM_FLAG)) ? file->index_next_same(record, (byte*) range->min_key, range->min_length) : file->index_next(record)); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ba911f47490..6f9353eefd4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24,8 +24,6 @@ #include "mysql_priv.h" #include "sql_select.h" -#include "opt_ft.h" - #include #include #include @@ -89,7 +87,7 @@ static int do_select(JOIN *join,List *fields,TABLE *tmp_table, Procedure *proc); static int sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records); static int sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records); -static int flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last); +static int flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last); static int end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); static int end_send_group(JOIN *join, JOIN_TAB *join_tab,bool end_of_records); static int end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); @@ -182,7 +180,6 @@ int handle_select(THD *thd, LEX *lex, select_result *result) register SELECT_LEX *select_lex = &lex->select_lex; DBUG_ENTER("handle_select"); - fix_tables_pointers(lex->all_selects_list); if (select_lex->next_select()) res=mysql_union(thd, lex, result, &lex->unit); else @@ -309,7 +306,7 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check that all tables, fields, conds and order are ok */ - if (setup_tables(tables_list) || + if (setup_tables(tables_list, 0) || setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1, @@ -939,8 +936,10 @@ JOIN::optimize() as in other cases the join is done before the sort. */ if (const_tables != tables && - (order || group_list) && join_tab[const_tables].type != JT_ALL && + (order || group_list) && + join_tab[const_tables].type != JT_ALL && join_tab[const_tables].type != JT_FT && + join_tab[const_tables].type != JT_REF_OR_NULL && (order && simple_order || group_list && simple_group)) { if (add_ref_to_table_cond(thd,&join_tab[const_tables])) @@ -1094,7 +1093,7 @@ JOIN::reinit() if (unit->select_limit_cnt == HA_POS_ERROR) select_lex->options&= ~OPTION_FOUND_ROWS; - if (setup_tables(tables_list)) + if (setup_tables(tables_list, 1)) DBUG_RETURN(1); /* Reset of sum functions */ @@ -1590,12 +1589,7 @@ JOIN::cleanup() JOIN_TAB *tab, *end; for (tab= join_tab, end= tab+tables ; tab != end ; tab++) { - delete tab->select; - delete tab->quick; - tab->select=0; - tab->quick=0; - x_free(tab->cache.buff); - tab->cache.buff= 0; + tab->cleanup(); } } tmp_join->tmp_join= 0; @@ -1659,13 +1653,14 @@ mysql_select(THD *thd, Item ***rref_pointer_array, goto err; } } - free_join= 0; } + free_join= 0; join->select_options= select_options; } else { - join= new JOIN(thd, fields, select_options, result); + if (!(join= new JOIN(thd, fields, select_options, result))) + DBUG_RETURN(-1); thd->proc_info="init"; thd->used_tables=0; // Updated by setup_fields if (join->prepare(rref_pointer_array, tables, wild_num, @@ -1726,8 +1721,8 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select, { select->head=table; table->reginfo.impossible_range=0; - if ((error=select->test_quick_select(thd, *(key_map *)keys,(table_map) 0,limit)) - == 1) + if ((error=select->test_quick_select(thd, *(key_map *)keys,(table_map) 0, + limit)) == 1) DBUG_RETURN(select->quick->records); if (error == -1) { @@ -2210,7 +2205,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, */ static void -add_key_field(KEY_FIELD **key_fields,uint and_level, +add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, Field *field,bool eq_func,Item **value, uint num_values, table_map usable_tables) { @@ -2277,6 +2272,17 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, (*value)->result_type() != STRING_RESULT && field->cmp_type() != (*value)->result_type()) return; + + /* + We can't use indexes if the effective collation + of the operation differ from the field collation. + */ + if (field->result_type() == STRING_RESULT && + (*value)->result_type() == STRING_RESULT && + field->cmp_type() == STRING_RESULT && + ((Field_str*)field)->charset() != cond->compare_collation()) + return; + } } DBUG_ASSERT(num_values == 1); @@ -3441,7 +3447,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, { /* Must read with repeat */ j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF; - j->null_ref_key= null_ref_key; + j->ref.null_ref_key= null_ref_key; } else if (ref_key == j->ref.key_copy) { @@ -3920,6 +3926,41 @@ bool error_if_full_join(JOIN *join) } +/* + cleanup JOIN_TAB + + SYNOPSIS + JOIN_TAB::cleanup() +*/ + +void JOIN_TAB::cleanup() +{ + delete select; + select= 0; + delete quick; + quick= 0; + x_free(cache.buff); + cache.buff= 0; + if (table) + { + if (table->key_read) + { + table->key_read= 0; + table->file->extra(HA_EXTRA_NO_KEYREAD); + } + /* Don't free index if we are using read_record */ + if (!read_record.table) + table->file->index_end(); + /* + We need to reset this for next select + (Tested in part_of_refkey) + */ + table->reginfo.join_tab= 0; + } + end_read_record(&read_record); +} + + /* Free resources of given join @@ -3954,11 +3995,6 @@ JOIN::join_free(bool full) { if (tab->table) { - if (tab->table->key_read) - { - tab->table->key_read= 0; - tab->table->file->extra(HA_EXTRA_NO_KEYREAD); - } /* Don't free index if we are using read_record */ if (!tab->read_record.table) tab->table->file->index_end(); @@ -3969,29 +4005,7 @@ JOIN::join_free(bool full) { for (tab= join_tab, end= tab+tables; tab != end; tab++) { - delete tab->select; - delete tab->quick; - tab->select=0; - tab->quick=0; - x_free(tab->cache.buff); - tab->cache.buff= 0; - if (tab->table) - { - if (tab->table->key_read) - { - tab->table->key_read= 0; - tab->table->file->extra(HA_EXTRA_NO_KEYREAD); - } - /* Don't free index if we are using read_record */ - if (!tab->read_record.table) - tab->table->file->index_end(); - /* - We need to reset this for next select - (Tested in part_of_refkey) - */ - tab->table->reginfo.join_tab= 0; - } - end_read_record(&tab->read_record); + tab->cleanup(); } table= 0; } @@ -5237,17 +5251,9 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) { Item *new_item=remove_eq_conds(item,&tmp_cond_value); if (!new_item) - { -#ifdef DELETE_ITEMS - delete item; // This may be shared -#endif li.remove(); - } else if (item != new_item) { -#ifdef DELETE_ITEMS - delete item; // This may be shared -#endif VOID(li.replace(new_item)); should_fix_fields=1; } @@ -6604,14 +6610,14 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) static int -flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last) +flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last) { int error; READ_RECORD *info; if (!join_tab->cache.records) return 0; /* Nothing to do */ - if (skipp_last) + if (skip_last) (void) store_record_in_cache(&join_tab->cache); // Must save this for later if (join_tab->use_quick == 2) { @@ -6645,21 +6651,21 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last) } SQL_SELECT *select=join_tab->select; if (!error && (!join_tab->cache.select || - !join_tab->cache.select->skipp_record())) + !join_tab->cache.select->skip_record())) { uint i; reset_cache(&join_tab->cache); - for (i=(join_tab->cache.records- (skipp_last ? 1 : 0)) ; i-- > 0 ;) + for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;) { read_cached_record(join_tab); - if (!select || !select->skipp_record()) + if (!select || !select->skip_record()) if ((error=(join_tab->next_select)(join,join_tab+1,0)) < 0) return error; /* purecov: inspected */ } } } while (!(error=info->read_record(info))); - if (skipp_last) + if (skip_last) read_cached_record(join_tab); // Restore current record reset_cache(&join_tab->cache); join_tab->cache.records=0; join_tab->cache.ptr_record= (uint) ~0; @@ -7066,12 +7072,12 @@ join_read_always_key_or_null(JOIN_TAB *tab) int res; /* First read according to key which is NOT NULL */ - *tab->null_ref_key=0; + *tab->ref.null_ref_key= 0; // Clear null byte if ((res= join_read_always_key(tab)) >= 0) return res; /* Then read key with null value */ - *tab->null_ref_key= 1; + *tab->ref.null_ref_key= 1; // Set null byte return safe_index_read(tab); } @@ -7085,10 +7091,10 @@ join_read_next_same_or_null(READ_RECORD *info) JOIN_TAB *tab= info->table->reginfo.join_tab; /* Test if we have already done a read after null key */ - if (*tab->null_ref_key) + if (*tab->ref.null_ref_key) return -1; // All keys read - *tab->null_ref_key= 1; // Read null key - return safe_index_read(tab); + *tab->ref.null_ref_key= 1; // Set null byte + return safe_index_read(tab); // then read null keys } @@ -8061,8 +8067,9 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, For impossible ranges (like when doing a lookup on NULL on a NOT NULL field, quick will contain an empty record set. */ - if (!(select->quick=get_ft_or_quick_select_for_ref(tab->join->thd, - table, tab))) + if (!(select->quick= tab->type == JT_FT ? + new FT_SELECT(thd, table, tab->ref.key) : + get_quick_select_for_ref(thd, table, &tab->ref))) goto err; } } @@ -8070,9 +8077,12 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, table->file->info(HA_STATUS_VARIABLE); // Get record count table->sort.found_records=filesort(thd, table,sortorder, length, select, filesort_limit, &examined_rows); - tab->records=table->sort.found_records; // For SQL_CALC_ROWS - delete select; // filesort did select - tab->select=0; + tab->records= table->sort.found_records; // For SQL_CALC_ROWS + if (select) + { + select->cleanup(); // filesort did select + tab->select= 0; + } tab->select_cond=0; tab->type=JT_ALL; // Read with normal read_record tab->read_first_record= join_init_read_record; @@ -9155,7 +9165,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, if (pos->type() == Item::FIELD_ITEM) { Item_field *item; - if (!(item= new Item_field(thd, *((Item_field*) pos)))) + if (!(item= new Item_field(thd, ((Item_field*) pos)))) goto err; pos= item; if (item->field->flags & BLOB_FLAG) @@ -10056,20 +10066,23 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) sl; sl= sl->next_select()) { + // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only + uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN); + res= mysql_explain_select(thd, sl, (((&thd->lex->select_lex)==sl)? - ((thd->lex->all_selects_list != sl)?"PRIMARY": - "SIMPLE"): + ((thd->lex->all_selects_list != sl) ? + primary_key_name : "SIMPLE"): ((sl == first)? ((sl->linkage == DERIVED_TABLE_TYPE) ? "DERIVED": - ((sl->uncacheable & UNCACHEABLE_DEPENDENT)? + ((uncacheable & UNCACHEABLE_DEPENDENT) ? "DEPENDENT SUBQUERY": - (sl->uncacheable?"UNCACHEABLE SUBQUERY": + (uncacheable?"UNCACHEABLE SUBQUERY": "SUBQUERY"))): - ((sl->uncacheable & UNCACHEABLE_DEPENDENT)? + ((uncacheable & UNCACHEABLE_DEPENDENT) ? "DEPENDENT UNION": - sl->uncacheable?"UNCACHEABLE UNION": + uncacheable?"UNCACHEABLE UNION": "UNION"))), result); if (res) From e720c2ce699ddce6bbee71e9599cc55856c7583d Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 19 Feb 2004 12:04:46 -0800 Subject: [PATCH 08/10] sql_select.cc, opt_range.cc, item.cc: After merge fixes. --- sql/item.cc | 3 ++- sql/opt_range.cc | 16 ++++++++-------- sql/sql_select.cc | 26 ++++++++++++++------------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 7143c8d2f12..e5ffba43042 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -333,7 +333,7 @@ Item_field::Item_field(THD *thd, Item_field *item) result_field(item->result_field) { collation.set(DERIVATION_IMPLICIT); - item_equal= item.item_equal; + item_equal= item->item_equal; } void Item_field::set_field(Field *field_par) @@ -1002,6 +1002,7 @@ void Item_field::cleanup() { Item_ident::cleanup(); field= result_field= 0; +} /* Find a field among specified multiple equalities diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 6bcec4fef17..cdd4e0f4bf5 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1530,21 +1530,21 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, switch (cond_func->functype()) { case Item_func::NE_FUNC: - tree= get_mm_parts(param, field, Item_func::LT_FUNC, + tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, value, cmp_type); if (tree) { - tree= tree_or(param, tree, get_mm_parts(param, field, + tree= tree_or(param, tree, get_mm_parts(param, cond_func, field, Item_func::GT_FUNC, value, cmp_type)); } break; case Item_func::BETWEEN: - tree= get_mm_parts(param, field, Item_func::GE_FUNC, + tree= get_mm_parts(param, cond_func, field, Item_func::GE_FUNC, cond_func->arguments()[1],cmp_type); if (tree) { - tree= tree_and(param, tree, get_mm_parts(param, field, + tree= tree_and(param, tree, get_mm_parts(param, cond_func, field, Item_func::LE_FUNC, cond_func->arguments()[2], cmp_type)); @@ -1553,7 +1553,7 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, case Item_func::IN_FUNC: { Item_func_in *func=(Item_func_in*) cond_func; - tree= get_mm_parts(param, field, Item_func::EQ_FUNC, + tree= get_mm_parts(param, cond_func, field, Item_func::EQ_FUNC, func->arguments()[1], cmp_type); if (tree) { @@ -1561,7 +1561,7 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, for (arg= func->arguments()+2, end= arg+func->argument_count()-2; arg < end ; arg++) { - tree= tree_or(param, tree, get_mm_parts(param, field, + tree= tree_or(param, tree, get_mm_parts(param, cond_func, field, Item_func::EQ_FUNC, *arg, cmp_type)); @@ -1581,7 +1581,7 @@ static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, Item_func::Functype func_type= (value != cond_func->arguments()[0]) ? cond_func->functype() : ((Item_bool_func2*) cond_func)->rev_functype(); - tree= get_mm_parts(param, field, func_type, value, cmp_type); + tree= get_mm_parts(param, cond_func, field, func_type, value, cmp_type); } } @@ -1690,7 +1690,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) Item_result cmp_type= field->cmp_type(); if (!((ref_tables | field->table->map) & param_comp)) { - tree= get_mm_parts(param, field, Item_func::EQ_FUNC, + tree= get_mm_parts(param, cond, field, Item_func::EQ_FUNC, value,cmp_type); ftree= !ftree ? tree : tree_and(param, ftree, tree); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6f9353eefd4..5b5cd44d277 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2190,6 +2190,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, add_key_field() key_fields Pointer to add key, if usable and_level And level, to be stored in KEY_FIELD + cond Condition predicate field Field used in comparision eq_func True if we used =, <=> or IS NULL value Value used for comparison with field @@ -2205,8 +2206,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, */ static void -add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, - Field *field,bool eq_func,Item **value, uint num_values, +add_key_field(KEY_FIELD **key_fields, uint and_level, COND *cond, + Field *field, bool eq_func, Item **value, uint num_values, table_map usable_tables) { uint exists_optimize= 0; @@ -2305,9 +2306,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, Add possible keys to array of possible keys originated from a simple predicate SYNPOSIS - add_key_equal_field() + add_key_equal_fields() key_fields Pointer to add key, if usable and_level And level, to be stored in KEY_FIELD + cond Condition predicate field Field used in comparision eq_func True if we used =, <=> or IS NULL value Value used for comparison with field @@ -2324,12 +2326,12 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, COND *cond, static void add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, - Item_field *field_item, + COND *cond, Item_field *field_item, bool eq_func, Item **val, uint num_values, table_map usable_tables) { Field *field= field_item->field; - add_key_field(key_fields, and_level, field, + add_key_field(key_fields, and_level, cond, field, eq_func, val, num_values, usable_tables); Item_equal *item_equal= field_item->item_equal; if (item_equal) @@ -2344,7 +2346,7 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, { if (!field->eq(item->field)) { - add_key_field(key_fields, and_level, item->field, + add_key_field(key_fields, and_level, cond, item->field, eq_func, val, num_values, usable_tables); } } @@ -2396,7 +2398,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, // BETWEEN or IN if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) - add_key_equal_fields(key_fields,*and_level, + add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->key_item()->real_item()), 0, cond_func->arguments()+1, cond_func->argument_count()-1, @@ -2410,7 +2412,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM && !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT)) { - add_key_equal_fields(key_fields, *and_level, + add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[0])->real_item(), equal_func, cond_func->arguments()+1, 1, usable_tables); @@ -2419,7 +2421,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, cond_func->functype() != Item_func::LIKE_FUNC && !(cond_func->arguments()[1]->used_tables() & OUTER_REF_TABLE_BIT)) { - add_key_equal_fields(key_fields,*and_level, + add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[1])->real_item(), equal_func, cond_func->arguments(),1,usable_tables); @@ -2434,7 +2436,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, Item *tmp=new Item_null; if (!tmp) // Should never be true return; - add_key_equal_fields(key_fields,*and_level, + add_key_equal_fields(key_fields, *and_level, cond_func, (Item_field*) (cond_func->arguments()[0])->real_item(), cond_func->functype() == Item_func::ISNULL_FUNC, &tmp, 1, usable_tables); @@ -2454,7 +2456,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, */ while ((item= it++)) { - add_key_field(key_fields, *and_level, item->field, + add_key_field(key_fields, *and_level, cond, item->field, TRUE, &const_item, 1, usable_tables); } } @@ -2474,7 +2476,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, { if (!field->eq(item->field)) { - add_key_field(key_fields, *and_level, field, + add_key_field(key_fields, *and_level, cond, field, TRUE, (Item **) &item, 1, usable_tables); } } From bae2554367994e624e9fdab3ac31fd20bee271f4 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 19 Feb 2004 17:12:31 -0800 Subject: [PATCH 09/10] sql_select.cc, item_cmpfunc.h: After merge fix for collation. --- sql/item_cmpfunc.h | 2 ++ sql/sql_select.cc | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 39b4e1802e1..b90489ffeea 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1020,6 +1020,8 @@ public: bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); void print(String *str); + CHARSET_INFO *compare_collation() + { return fields.head()->collation.collation; } }; class COND_EQUAL diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5b5cd44d277..36205effe71 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4539,6 +4539,12 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) field_item->result_type() == const_item->result_type()) { bool copyfl; + + if (field_item->result_type() == STRING_RESULT && + ((Field_str *) field_item)->charset() != + ((Item_cond *) item)->compare_collation()) + return FALSE; + Item_equal *item_equal = find_item_equal(cond_equal, field_item->field, ©fl); if (copyfl) From 4c8e3917186ad416effa97bce402b9bb55a9f7b5 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Tue, 19 Oct 2004 14:12:55 -0700 Subject: [PATCH 10/10] table.h, sql_select.h: Added the code processing on expressions for applying multiple equalities. sql_select.cc: Post-merge fixes for Item_equal patch. Added the code processing on expressions for applying multiple equalities. Many files: Post-merge fixes for Item_equal patch. item_cmpfunc.cc: Post-merge fixes for Item_equal patch. Fixed a problem when an equality field=const cannot be applied to the predicate P(field,c) for constant propagation as a conversion of field is needed. item.h, item.cc: Fixed a problem when an equality field=const cannot be applied to the predicate P(field,c) for constant propagation as a conversion of field is needed. --- mysql-test/r/func_test.result | 2 +- mysql-test/r/index_merge.result | 6 +- mysql-test/r/join_nested.result | 28 +-- mysql-test/r/range.result | 4 +- mysql-test/r/subselect.result | 14 +- sql/item.cc | 101 +++++++++- sql/item.h | 4 +- sql/item_cmpfunc.cc | 11 +- sql/opt_range.cc | 57 ++++++ sql/sql_select.cc | 344 ++++++++++++++++++++------------ sql/sql_select.h | 3 +- sql/table.h | 2 + 12 files changed, 416 insertions(+), 160 deletions(-) diff --git a/mysql-test/r/func_test.result b/mysql-test/r/func_test.result index a3a4f892975..4d3a570d1c4 100644 --- a/mysql-test/r/func_test.result +++ b/mysql-test/r/func_test.result @@ -79,7 +79,7 @@ explain extended select * from t1 where 1 xor 1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (1 xor 1) +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` select - a from t1; - a -1 diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index 4055256f5ea..6b41992a733 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -82,7 +82,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where explain select * from t0 where key2=10 or key3=3 or key4 is null; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i2,i3 i2,i3 4,4 NULL 2 Using where +1 SIMPLE t0 index_merge i2,i3 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or (key3=10) or (key4 <=> null); id select_type table type possible_keys key key_len ref rows Extra @@ -257,8 +257,8 @@ explain select * from t0,t1 where (t0.key1=t1.key1) and (t0.key1=3 or t0.key2=4) and t1.key1<200; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where -1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 Using where +1 SIMPLE t0 range i1,i2 i1 4 NULL 179 Using where +1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 explain select * from t0,t1 where (t0.key1=t1.key1) and (t0.key1=3 or t0.key2<4) and t1.key1=2; diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result index 09c3e67ae70..6eff8676d9d 100644 --- a/mysql-test/r/join_nested.result +++ b/mysql-test/r/join_nested.result @@ -76,7 +76,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t4 ALL NULL NULL NULL NULL 2 Warnings: -Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` = 1) or isnull(`test`.`t3`.`c`)) +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where ((`test`.`t3`.`a` = 1) or isnull(`test`.`t3`.`c`)) SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b FROM t2 LEFT JOIN @@ -153,7 +153,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t4 ALL NULL NULL NULL NULL 2 1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Warnings: -Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where ((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b FROM t2 LEFT JOIN @@ -183,7 +183,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t4 ALL NULL NULL NULL NULL 2 1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where (((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) and ((`test`.`t5`.`a` < 3) or isnull(`test`.`t5`.`c`))) +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t4`.`b` = `test`.`t2`.`b`)) where (((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) and ((`test`.`t5`.`a` < 3) or isnull(`test`.`t5`.`c`))) SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b FROM t2 LEFT JOIN @@ -233,7 +233,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t6 ALL NULL NULL NULL NULL 3 1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Warnings: -Note 1003 select `test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10))) where 1 +Note 1003 select `test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t7`.`b`) and (`test`.`t6`.`b` < 10))) where 1 SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b FROM t6, t7 @@ -562,7 +562,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t6 ALL NULL NULL NULL NULL 3 1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`))) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) where ((`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`))) SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b, t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b FROM t0,t1 @@ -660,7 +660,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1)) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`))) SELECT t9.a,t9.b FROM t9; a b @@ -858,7 +858,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 1 SIMPLE t4 ALL NULL NULL NULL NULL 2 Warnings: -Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t1` join `test`.`t3` join `test`.`t2` left join `test`.`t4` on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where (`test`.`t1`.`a` <= 2) +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t1` join `test`.`t3` join `test`.`t2` left join `test`.`t4` on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) where (`test`.`t1`.`a` <= 2) CREATE INDEX idx_b ON t2(b); EXPLAIN EXTENDED SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b @@ -872,7 +872,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref idx_b idx_b 5 test.t3.b 2 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Warnings: -Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t3` join `test`.`t4` left join (`test`.`t1` join `test`.`t2`) on(((`test`.`t3`.`a` = 1) and (`test`.`t3`.`b` = `test`.`t2`.`b`) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where 1 +Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t3` join `test`.`t4` left join (`test`.`t1` join `test`.`t2`) on(((`test`.`t4`.`b` = `test`.`t3`.`b`) and (`test`.`t2`.`b` = `test`.`t3`.`b`) and (`test`.`t3`.`a` = 1))) where 1 SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b FROM t3,t4 LEFT JOIN @@ -935,7 +935,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1)) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`))) CREATE INDEX idx_b ON t4(b); CREATE INDEX idx_b ON t5(b); EXPLAIN EXTENDED @@ -986,7 +986,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1)) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`))) CREATE INDEX idx_b ON t8(b); EXPLAIN EXTENDED SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b, @@ -1033,10 +1033,10 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where 1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where -1 SIMPLE t8 ref idx_b idx_b 5 test.t7.b 2 Using where +1 SIMPLE t8 ref idx_b idx_b 5 test.t5.b 2 Using where 1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1)) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`))) CREATE INDEX idx_b ON t1(b); CREATE INDEX idx_a ON t0(a); EXPLAIN EXTENDED @@ -1084,10 +1084,10 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where 1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where 1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where -1 SIMPLE t8 ref idx_b idx_b 5 test.t7.b 2 Using where +1 SIMPLE t8 ref idx_b idx_b 5 test.t5.b 2 Using where 1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1)) +Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t4`.`b` = `test`.`t2`.`b`) and (`test`.`t3`.`a` = 1))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t8`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t7`.`b` = `test`.`t5`.`b`) and (`test`.`t6`.`b` >= 2)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t5`.`b` = `test`.`t0`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t9`.`a` = 1) and (`test`.`t1`.`b` = `test`.`t0`.`b`) and (`test`.`t0`.`a` = 1) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t4`.`b` = `test`.`t3`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t9`.`b` = `test`.`t8`.`b`) or isnull(`test`.`t8`.`c`))) SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b, t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b FROM t0,t1 diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index add6d8f320d..7dd62296f4a 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -254,12 +254,12 @@ INSERT INTO t2 VALUES (0),(0),(1),(1),(2),(2); explain select * from t1, t2 where (t1.key1 (`test`.`t2`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and ((`test`.`t2`.`a`) = `test`.`t1`.`a`)))) +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a`,(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t1`.`b`) and ((`test`.`t2`.`a`) = `test`.`t1`.`a`)))) drop table t1, t2, t3; create table t1 (a int, b int, index a (a,b)); create table t2 (a int, index a (a)); @@ -1675,10 +1675,10 @@ Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `tes explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY tt ALL NULL NULL NULL NULL 12 Using where -2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 7 Using where; Using index +2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 Using where; Using index Warnings: Note 1276 Field or reference 'tt.id' of SELECT #2 was resolved in SELECT #1 -Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and ((`test`.`t1`.`id` = `test`.`tt`.`id`) or isnull(`test`.`t1`.`id`))) having (`test`.`t1`.`id` is not null))) +Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and (`test`.`tt`.`id` = `test`.`t1`.`id`)) having (`test`.`t1`.`id` is not null))) insert into t1 (id, text) values (1000, 'text1000'), (1001, 'text1001'); create table t2 (id int not null, text varchar(20) not null default '', primary key (id)); insert into t2 (id, text) values (1, 'text1'), (2, 'text2'), (3, 'text3'), (4, 'text4'), (5, 'text5'), (6, 'text6'), (7, 'text7'), (8, 'text8'), (9, 'text9'), (10, 'text10'), (11, 'text1'), (12, 'text2'), (13, 'text3'), (14, 'text4'), (15, 'text5'), (16, 'text6'), (17, 'text7'), (18, 'text8'), (19, 'text9'), (20, 'text10'),(21, 'text1'), (22, 'text2'), (23, 'text3'), (24, 'text4'), (25, 'text5'), (26, 'text6'), (27, 'text7'), (28, 'text8'), (29, 'text9'), (30, 'text10'), (31, 'text1'), (32, 'text2'), (33, 'text3'), (34, 'text4'), (35, 'text5'), (36, 'text6'), (37, 'text7'), (38, 'text8'), (39, 'text9'), (40, 'text10'), (41, 'text1'), (42, 'text2'), (43, 'text3'), (44, 'text4'), (45, 'text5'), (46, 'text6'), (47, 'text7'), (48, 'text8'), (49, 'text9'), (50, 'text10'); @@ -1704,7 +1704,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE b eq_ref PRIMARY PRIMARY 4 test.a.id 2 1 SIMPLE c eq_ref PRIMARY PRIMARY 4 func 1 Using where Warnings: -Note 1003 select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`a`.`id` = `test`.`b`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`) +Note 1003 select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`b`.`id` = `test`.`a`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`) drop table t1,t2; create table t1 (a int); insert into t1 values (1); @@ -1832,7 +1832,7 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where Warnings: Note 1276 Field or reference 'up.a' of SELECT #2 was resolved in SELECT #1 -Note 1003 select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`up`.`a`)) +Note 1003 select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`up`.`a` = `test`.`t1`.`a`)) drop table t1; CREATE TABLE t1 (t1_a int); INSERT INTO t1 VALUES (1); diff --git a/sql/item.cc b/sql/item.cc index a17336ea634..c99c0a9ba6d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -408,7 +408,7 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion) Item_field::Item_field(Field *f) :Item_ident(NullS, f->table_name, f->field_name), - item_equal(0), + item_equal(0), no_const_subst(0), have_privileges(0), any_privileges(0) { set_field(f); @@ -419,7 +419,7 @@ Item_field::Item_field(Field *f) Item_field::Item_field(THD *thd, Field *f) :Item_ident(NullS, thd->strdup(f->table_name), thd->strdup(f->field_name)), - item_equal(0), + item_equal(0), no_const_subst(0), have_privileges(0), any_privileges(0) { set_field(f); @@ -432,11 +432,12 @@ Item_field::Item_field(THD *thd, Item_field *item) :Item_ident(thd, item), field(item->field), result_field(item->result_field), + item_equal(item->item_equal), + no_const_subst(item->no_const_subst), have_privileges(item->have_privileges), any_privileges(item->any_privileges) { collation.set(DERIVATION_IMPLICIT); - item_equal= item->item_equal; } void Item_field::set_field(Field *field_par) @@ -1607,7 +1608,101 @@ void Item_field::cleanup() First Item_equal containing the field, if success 0, otherwise */ +Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) +{ + Item_equal *item= 0; + while (cond_equal) + { + List_iterator_fast li(cond_equal->current_level); + while ((item= li++)) + { + if (item->contains(field)) + return item; + } + /* + The field is not found in any of the multiple equalities + of the current level. Look for it in upper levels + */ + cond_equal= cond_equal->upper_levels; + } + return 0; +} + +/* + Set a pointer to the multiple equality the field reference belongs to (if any) + + SYNOPSIS + equal_fields_propagator() + arg - reference to list of multiple equalities where + the field (this object) is to be looked for + + DESCRIPTION + The function looks for a multiple equality containing the field item + among those referenced by arg. + In the case such equality exists the function does the following. + If the found multiple equality contains a constant, then the field + reference is substituted for this constant, otherwise it sets a pointer + to the multiple equality in the field item. + + NOTES + This function is supposed to be called as a callback parameter in calls + of the transform method. + + RETURN VALUES + pointer to the replacing constant item, if the field item was substituted + pointer to the field item, otherwise. +*/ + +Item *Item_field::equal_fields_propagator(byte *arg) +{ + if (no_const_subst) + return this; + item_equal= find_item_equal((COND_EQUAL *) arg); + Item *item= 0; + if (item_equal) + item= item_equal->get_const(); + if (!item) + item= this; + return item; +} + + +/* + Set a pointer to the multiple equality the field reference belongs to (if any) + + SYNOPSIS + replace_equal_field_processor() + arg - a dummy parameter, is not used here + + DESCRIPTION + The function replaces a pointer to a field in the Item_field object + by a pointer to another field. + The replacement field is taken from the very beginning of + the item_equal list which the Item_field object refers to (belongs to) + If the Item_field object does not refer any Item_equal object, + nothing is done. + + NOTES + This function is supposed to be called as a callback parameter in calls + of the walk method. + + RETURN VALUES + 0 +*/ + +bool Item_field::replace_equal_field_processor(byte *arg) +{ + if (item_equal) + { + Item_field *subst= item_equal->get_first(); + if (!field->eq(subst->field)) + { + field= subst->field; + return 0; + } + } + return 0; } void Item::init_make_field(Send_field *tmp_field, diff --git a/sql/item.h b/sql/item.h index 9826bcb4a5a..c8e274b7205 100644 --- a/sql/item.h +++ b/sql/item.h @@ -457,6 +457,8 @@ protected: void set_field(Field *field); public: Field *field,*result_field; + Item_equal *item_equal; + bool no_const_subst; /* if any_privileges set to TRUE then here real effective privileges will be stored @@ -468,7 +470,7 @@ public: Item_field(const char *db_par,const char *table_name_par, const char *field_name_par) :Item_ident(db_par,table_name_par,field_name_par), - field(0), result_field(0), item_equal(0),} + field(0), result_field(0), item_equal(0), no_const_subst(0), have_privileges(0), any_privileges(0) { collation.set(DERIVATION_IMPLICIT); } // Constructor need to process subselect with temporary tables (see Item) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1f2e744f714..66af96f671f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -231,6 +231,8 @@ void Item_bool_func2::fix_length_and_dec() conv->collation.set(args[weak]->collation.derivation); conv->fix_fields(thd, 0, &conv); } + if (args[weak]->type() == FIELD_ITEM) + ((Item_field *)args[weak])->no_const_subst= 1; args[weak]= conv ? conv : args[weak]; } } @@ -1956,7 +1958,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) char buff[sizeof(char*)]; // Max local vars in function #endif not_null_tables_cache= used_tables_cache= 0; - const_item_cache= 0; + const_item_cache= 1; /* and_table_cache is the value that Item_cond_or() returns for not_null_tables() @@ -1987,7 +1989,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) 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(); + const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; if (item->maybe_null) maybe_null=1; @@ -2051,7 +2053,7 @@ void Item_cond::split_sum_func(Item **ref_pointer_array, List &fields) List_iterator li(list); Item *item; used_tables_cache=0; - const_item_cache=0; + const_item_cache=1; while ((item=li++)) { if (item->with_sum_func && item->type() != SUM_FUNC_ITEM) @@ -2088,7 +2090,7 @@ void Item_cond::update_used_tables() { item->update_used_tables(); used_tables_cache|= item->used_tables(); - const_item_cache&= item->const_item(); + const_item_cache&= item->const_item(); } } @@ -2934,6 +2936,7 @@ void Item_equal::add(Item *c) } Item_func_eq *func= new Item_func_eq(c, const_item); func->set_cmp_func(); + func->quick_fix_field(); cond_false = !(func->val_int()); } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 502a8d51b07..fe34ac7235e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3339,6 +3339,63 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) } DBUG_RETURN(ftree); + } + default: + if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM) + { + field_item= (Item_field*) (cond_func->arguments()[0]); + value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0; + } + else if (cond_func->have_rev_func() && + cond_func->arguments()[1]->type() == Item::FIELD_ITEM) + { + field_item= (Item_field*) (cond_func->arguments()[1]); + value= cond_func->arguments()[0]; + } + else + DBUG_RETURN(0); + } + + /* + If the where condition contains a predicate (ti.field op const), + then not only SELL_TREE for this predicate is built, but + the trees for the results of substitution of ti.field for + each tj.field belonging to the same multiple equality as ti.field + are built as well. + E.g. for WHERE t1.a=t2.a AND t2.a > 10 + a SEL_TREE for t2.a > 10 will be built for quick select from t2 + and + a SEL_TREE for t1.a > 10 will be built for quick select from t1. + */ + + for (uint i= 0; i < cond_func->arg_count; i++) + { + Item *arg= cond_func->arguments()[i]; + if (arg != field_item) + ref_tables|= arg->used_tables(); + } + Field *field= field_item->field; + Item_result cmp_type= field->cmp_type(); + if (!((ref_tables | field->table->map) & param_comp)) + ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type); + Item_equal *item_equal= field_item->item_equal; + if (item_equal) + { + Item_equal_iterator it(*item_equal); + Item_field *item; + while ((item= it++)) + { + Field *f= item->field; + if (field->eq(f)) + continue; + if (!((ref_tables | f->table->map) & param_comp)) + { + tree= get_func_mm_tree(param, cond_func, f, value, cmp_type); + ftree= !ftree ? tree : tree_and(param, ftree, tree); + } + } + } + DBUG_RETURN(ftree); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b3355f6c57c..05df457da1b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -91,8 +91,10 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, uint select_options, const char *info, Item *having, Procedure *proc, SELECT_LEX_UNIT *unit); -static COND *build_all_equal_items(COND *cond, - COND_EQUAL *inherited); +static COND *build_equal_items(COND *cond, + COND_EQUAL *inherited, + List *join_list, + COND_EQUAL **cond_equal_ref); static COND* substitute_for_best_equal_field(COND *cond, COND_EQUAL *cond_equal, void *table_join_idx); @@ -530,17 +532,29 @@ JOIN::optimize() } } #endif - - /* Eliminate NOT operators */ - conds= eliminate_not_funcs(conds); - DBUG_EXECUTE("where", print_where(conds, "after negation elimination");); + SELECT_LEX *sel= thd->lex->current_select; + if (sel->first_cond_optimization) { - TABLE_LIST *tables; - for (tables= tables_list; tables; tables= tables->next) - { - if (tables->on_expr) - tables->on_expr= eliminate_not_funcs(tables->on_expr); - } + /* + The following code will allocate the new items in a permanent + MEMROOT for prepared statements and stored procedures. + */ + + Item_arena *arena= thd->current_arena, backup; + if (arena->is_conventional()) + arena= 0; // For easier test + else + thd->set_n_backup_item_arena(arena, &backup); + + sel->first_cond_optimization= 0; + + /* Convert all outer joins to inner joins if possible */ + conds= simplify_joins(this, join_list, conds, TRUE); + + sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; + + if (arena) + thd->restore_backup_item_arena(arena, &backup); } /* @@ -550,32 +564,8 @@ JOIN::optimize() that occurs in a function set a pointer to the multiple equality predicate. Substitute a constant instead of this field if the multiple equality contains a constant. - */ - if (conds) - { - conds= build_all_equal_items(conds, NULL); - conds->update_used_tables(); - if (conds->type() == Item::COND_ITEM && - ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) - cond_equal= &((Item_cond_and*) conds)->cond_equal; - else if (conds->type() == Item::FUNC_ITEM && - ((Item_cond*) conds)->functype() == Item_func::MULT_EQUAL_FUNC) - { - cond_equal= new COND_EQUAL; - cond_equal->current_level.push_back((Item_equal *) conds); - } - } - { - TABLE_LIST *tables; - for (tables= tables_list; tables; tables= tables->next) - { - if (tables->on_expr) - { - tables->on_expr= build_all_equal_items(tables->on_expr, cond_equal); - tables->on_expr->update_used_tables(); - } - } - } + */ + conds= build_equal_items(conds, NULL, join_list, &cond_equal); conds= optimize_cond(this, conds,&cond_value); if (thd->net.report_error) @@ -669,31 +659,6 @@ JOIN::optimize() if (const_tables && !thd->locked_tables && !(select_options & SELECT_NO_UNLOCK)) mysql_unlock_some_tables(thd, table, const_tables); - /* - Among the equal fields belonging to the same multiple equality - choose the one that is to be retrieved first and substitute - all references to these in where condition for a reference for - the selected field. - */ - if (conds) - { - conds= substitute_for_best_equal_field(conds, cond_equal, map2table); - conds->update_used_tables(); - } - { - TABLE_LIST *tables; - for (tables= tables_list; tables; tables= tables->next) - { - if (tables->on_expr) - { - tables->on_expr= substitute_for_best_equal_field(tables->on_expr, - cond_equal, - map2table); - tables->on_expr->update_used_tables(); - map2table[tables->table->tablenr]->on_expr= tables->on_expr; - } - } - } if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ @@ -710,6 +675,32 @@ JOIN::optimize() make_outerjoin_info(this); + /* + Among the equal fields belonging to the same multiple equality + choose the one that is to be retrieved first and substitute + all references to these in where condition for a reference for + the selected field. + */ + if (conds) + { + conds= substitute_for_best_equal_field(conds, cond_equal, map2table); + conds->update_used_tables(); + } + /* + Permorm the the optimization on fields evaluation mentioned above + for all on expressions. + */ + for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++) + { + if (*tab->on_expr_ref) + { + *tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref, + tab->cond_equal, + map2table); + (*tab->on_expr_ref)->update_used_tables(); + } + } + if (make_join_select(this, select, conds)) { zero_result_cause= @@ -1545,8 +1536,6 @@ JOIN::exec() /* table->keyuse is set in the case there was an original WHERE clause on the table that was optimized away. - table->on_expr tells us that it was a LEFT JOIN and there will be - at least one row generated from the table. */ if (curr_table->select_cond || (curr_table->keyuse && !curr_table->first_inner)) @@ -1646,6 +1635,7 @@ JOIN::cleanup() tmp_table_param.copy_field=0; DBUG_RETURN(tmp_join->cleanup()); } + cond_equal= 0; lock=0; // It's faster to unlock later join_free(1); @@ -1794,7 +1784,7 @@ Cursor::fetch(ulong num_rows) { THD *thd= join->thd; JOIN_TAB *join_tab= join->join_tab + join->const_tables;; - COND *on_expr= join_tab->on_expr; + COND *on_expr= *join_tab->on_expr_ref; COND *select_cond= join_tab->select_cond; READ_RECORD *info= &join_tab->read_record; @@ -2156,7 +2146,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->dependent= tables->dep_tables; s->key_dependent= 0; - if ((s->on_expr=tables->on_expr)) + s->on_expr_ref= &tables->on_expr; + if (*s->on_expr_ref) { /* s is the only inner table of an outer join */ if (!table->file->records) @@ -2378,7 +2369,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, SQL_SELECT *select; select= make_select(s->table, found_const_table_map, found_const_table_map, - s->on_expr ? s->on_expr : conds, + *s->on_expr_ref ? *s->on_expr_ref : conds, &error); records= get_quick_record_count(join->thd, select, s->table, &s->const_keys, join->row_limit); @@ -2396,7 +2387,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, join->const_table_map|= s->table->map; set_position(join,const_count++,s,(KEYUSE*) 0); s->type= JT_CONST; - if (s->on_expr) + if (*s->on_expr_ref) { /* Generate empty row */ s->info= "Impossible ON condition"; @@ -2774,16 +2765,26 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, case Item_func::OPTIMIZE_NONE: break; case Item_func::OPTIMIZE_KEY: - // BETWEEN, IN, NOT + { + // BETWEEN, IN, NE if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) - add_key_field(key_fields,*and_level,cond_func, - ((Item_field*)(cond_func->key_item()->real_item()))->field, - cond_func->argument_count() == 2 && - cond_func->functype() == Item_func::IN_FUNC, - cond_func->arguments()+1, cond_func->argument_count()-1, - usable_tables); + { + Item **values= cond_func->arguments()+1; + if (cond_func->functype() == Item_func::NE_FUNC && + cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && + !(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT)) + values--; + add_key_equal_fields(key_fields, *and_level, cond_func, + (Item_field*) (cond_func->key_item()->real_item()), + cond_func->argument_count() == 2 && + cond_func->functype() == Item_func::IN_FUNC, + values, + cond_func->argument_count()-1, + usable_tables); + } break; + } case Item_func::OPTIMIZE_OP: { bool equal_func=(cond_func->functype() == Item_func::EQ_FUNC || @@ -3048,9 +3049,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, for inner tables in outer joins these keys will be taken into account as well. */ - if (join_tab[i].on_expr) + if (*join_tab[i].on_expr_ref) { - add_key_fields(join_tab,&end,&and_level,join_tab[i].on_expr, + add_key_fields(join_tab,&end,&and_level,*join_tab[i].on_expr_ref, join_tab[i].table->map); } else @@ -4654,7 +4655,7 @@ get_best_combination(JOIN *join) form=join->table[tablenr]=j->table; used_tables|= form->map; form->reginfo.join_tab=j; - if (!j->on_expr) + if (!*j->on_expr_ref) form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN if (j->type == JT_CONST) continue; // Handled in make_join_stat.. @@ -4909,7 +4910,7 @@ make_simple_join(JOIN *join,TABLE *tmp_table) join_tab->type= JT_ALL; /* Map through all records */ join_tab->keys.init(~0); /* test everything in quick */ join_tab->info=0; - join_tab->on_expr=0; + join_tab->on_expr_ref=0; join_tab->last_inner= 0; join_tab->first_unmatched= 0; join_tab->ref.key = -1; @@ -4978,7 +4979,7 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) first inner table of the embedding outer join operation, if there is any, through the field t0->first_upper. The on expression for the outer join operation is attached to the - corresponding first inner table through the field t0->on_expr. + corresponding first inner table through the field t0->on_expr_ref. Here ti are structures of the JOIN_TAB type. EXAMPLE @@ -4992,8 +4993,8 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab) is selected, the following references will be set; t4->last_inner=[t4], t4->first_inner=[t4], t4->first_upper=[t2] t2->last_inner=[t4], t2->first_inner=t3->first_inner=[t2], - on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to t2->on_expr, - while t3.a=t4.a will be attached to t4->on_expr. + on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to + *t2->on_expr_ref, while t3.a=t4.a will be attached to *t4->on_expr_ref. NOTES The function assumes that the simplification procedure has been @@ -5020,7 +5021,8 @@ make_outerjoin_info(JOIN *join) is in the query above.) */ tab->last_inner= tab->first_inner= tab; - tab->on_expr= tbl->on_expr; + tab->on_expr_ref= &tbl->on_expr; + tab->cond_equal= tbl->cond_equal; if (embedding) tab->first_upper= embedding->nested_join->first_nested; } @@ -5034,7 +5036,8 @@ make_outerjoin_info(JOIN *join) Save reference to it in the nested join structure. */ nested_join->first_nested= tab; - tab->on_expr= embedding->on_expr; + tab->on_expr_ref= &embedding->on_expr; + tab->cond_equal= tbl->cond_equal; if (embedding->embedding) tab->first_upper= embedding->embedding->nested_join->first_nested; } @@ -5069,10 +5072,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) for (JOIN_TAB *tab= join->join_tab+join->const_tables; tab < join->join_tab+join->tables ; tab++) { - if (tab->on_expr) + if (*tab->on_expr_ref) { JOIN_TAB *cond_tab= tab->first_inner; - COND *tmp= make_cond_for_table(tab->on_expr, + COND *tmp= make_cond_for_table(*tab->on_expr_ref, join->const_table_map, (table_map) 0); if (!tmp) @@ -5210,7 +5213,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { /* Join with outer join condition */ COND *orig_cond=sel->cond; - sel->cond= and_conds(sel->cond, tab->on_expr); + sel->cond= and_conds(sel->cond, *tab->on_expr_ref); if (sel->cond && !sel->cond->fixed) sel->cond->fix_fields(join->thd, 0, &sel->cond); if (sel->test_quick_select(join->thd, tab->keys, @@ -5225,7 +5228,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) we have to check isn't it only "impossible ON" instead */ sel->cond=orig_cond; - if (!tab->on_expr || + if (!*tab->on_expr_ref || sel->test_quick_select(join->thd, tab->keys, used_tables & ~ current_map, (join->select_options & @@ -5290,7 +5293,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) Table tab is the last inner table of an outer join. An on expression is always attached to it. */ - COND *on_expr= first_inner_tab->on_expr; + COND *on_expr= *first_inner_tab->on_expr_ref; table_map used_tables= join->const_table_map | OUTER_REF_TABLE_BIT | RAND_TABLE_BIT; @@ -5804,7 +5807,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) table_map not_const_tables= ~join->const_table_map; table_map ref; prev_ptr= &first_order; - *simple_order= join->join_tab[join->const_tables].on_expr ? 0 : 1; + *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1; /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */ @@ -6024,7 +6027,7 @@ finish: general case, its own constant for each fields from the multiple equality. But at the same time it would allow us to get rid of constant propagation completely: it would be done by the call - to build_all_equal_items. + to build_equal_items_for_cond. IMPLEMENTATION The implementation does not follow exactly the above rules to @@ -6156,7 +6159,7 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) bool copyfl; if (field_item->result_type() == STRING_RESULT && - ((Field_str *) field_item)->charset() != + ((Field_str *) field_item->field)->charset() != ((Item_cond *) item)->compare_collation()) return FALSE; @@ -6192,7 +6195,7 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) Replace all equality predicates in a condition by multiple equality items SYNOPSIS - build_all_equal_items() + build_equal_items_for_cond() cond condition(expression) where to make replacement inherited path to all inherited multiple equality items @@ -6253,7 +6256,7 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) pointer to the transformed condition */ -static COND *build_all_equal_items(COND *cond, +static COND *build_equal_items_for_cond(COND *cond, COND_EQUAL *inherited) { Item_equal *item_equal; @@ -6315,7 +6318,7 @@ static COND *build_all_equal_items(COND *cond, while((item= li++)) { Item *new_item; - if ((new_item = build_all_equal_items(item, inherited))!= item) + if ((new_item = build_equal_items_for_cond(item, inherited))!= item) { /* This replacement happens only for standalone equalities */ li.replace(new_item); @@ -6351,6 +6354,113 @@ static COND *build_all_equal_items(COND *cond, (byte *) inherited); cond->update_used_tables(); } + return cond; +} + +/* + Build multiple equalities for a condition and all on expressions that + inherit these multiple equalities + + SYNOPSIS + build_equal_items() + cond condition to build the multiple equalities for + inherited path to all inherited multiple equality items + join_list list of join tables to which the condition refers to + cond_equal_ref :out pointer to the structure to place built equalities in + + DESCRIPTION + The function first applies the build_equal_items_for_cond function + to build all multiple equalities for condition cond utilizing equalities + referred through the parameter inherited. The extended set of + equalities is returned in the structure referred by the cond_equal_ref + parameter. After this the function calls itself recursively for + all on expressions whose direct references can be found in join_list + and who inherit directly the multiple equalities just having built. + + NOTES + The on expression used in an outer join operation inherits all equalities + from the on expression of the embedding join, if there is any, or + otherwise - from the where condition. + This fact is not obvious, but presumably can be proved. + Consider the following query: + SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t2.a=t4.a + WHERE t1.a=t2.a; + If the on expression in the query inherits =(t1.a,t2.a), then we + can build the multiple equality =(t1.a,t2.a,t3.a,t4.a) that infers + the equality t3.a=t4.a. Although the on expression + t1.a=t3.a AND t2.a=t4.a AND t3.a=t4.a is not equivalent to the one + in the query the latter can be replaced by the former: the new query + will return the same result set as the original one. + + Interesting that multiple equality =(t1.a,t2.a,t3.a,t4.a) allows us + to use t1.a=t3.a AND t3.a=t4.a under the on condition: + SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a + WHERE t1.a=t2.a + This query equivalent to: + SELECT * FROM (t1 LEFT JOIN (t3,t4) ON t1.a=t3.a AND t3.a=t4.a),t2 + WHERE t1.a=t2.a + Similarly the original query can be rewritten to the query: + SELECT * FROM (t1,t2) LEFT JOIN (t3,t4) ON t2.a=t4.a AND t3.a=t4.a + WHERE t1.a=t2.a + that is equivalent to: + SELECT * FROM (t2 LEFT JOIN (t3,t4)ON t2.a=t4.a AND t3.a=t4.a), t1 + WHERE t1.a=t2.a + Thus, applying equalities from the where condition we basically + can get more freedom in performing join operations. + Althogh we don't use this property now, it probably makes sense to use + it in the future. + + RETURN + pointer to the transformed condition containing multiple equalities +*/ + +static COND *build_equal_items(COND *cond, + COND_EQUAL *inherited, + List *join_list, + COND_EQUAL **cond_equal_ref) +{ + COND_EQUAL *cond_equal= 0; + + if (cond) + { + cond= build_equal_items_for_cond(cond, inherited); + cond->update_used_tables(); + if (cond->type() == Item::COND_ITEM && + ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) + cond_equal= &((Item_cond_and*) cond)->cond_equal; + else if (cond->type() == Item::FUNC_ITEM && + ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + { + cond_equal= new COND_EQUAL; + cond_equal->current_level.push_back((Item_equal *) cond); + } + } + if (cond_equal) + { + cond_equal->upper_levels= inherited; + inherited= cond_equal; + } + *cond_equal_ref= cond_equal; + + if (join_list) + { + TABLE_LIST *table; + List_iterator li(*join_list); + + while ((table= li++)) + { + if (table->on_expr) + { + List *join_list= table->nested_join ? + &table->nested_join->join_list : NULL; + table->on_expr= build_equal_items(table->on_expr, + inherited, + join_list, + &table->cond_equal); + } + } + } + return cond; } @@ -6447,6 +6557,11 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, { List eq_list; Item_func_eq *eq_item= 0; + if (((Item *) item_equal)->const_item() && !item_equal->val_int()) + { + cond= new Item_int((char*) "FALSE",0,1); + return cond; + } Item *item_const= item_equal->get_const(); Item_equal_iterator it(*item_equal); Item *head; @@ -6484,6 +6599,7 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, if (!eq_item) return 0; eq_item->set_cmp_func(); + eq_item->quick_fix_field(); } } @@ -6495,6 +6611,9 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, cond= new Item_cond_and(eq_list); else ((Item_cond *) cond)->add_at_head(&eq_list); + + cond->quick_fix_field(); + cond->update_used_tables(); return cond; } @@ -6589,7 +6708,6 @@ static COND* substitute_for_best_equal_field(COND *cond, return cond; } - /* change field = field to field = const for each found field = const in the and_level @@ -6998,38 +7116,15 @@ simplify_joins(JOIN *join, List *join_list, COND *conds, bool top) } } DBUG_RETURN(conds); +} static COND * optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) { THD *thd= join->thd; - SELECT_LEX *select= thd->lex->current_select;} + SELECT_LEX *select= thd->lex->current_select; DBUG_ENTER("optimize_cond"); - if (select->first_cond_optimization) - { - /* - The following code will allocate the new items in a permanent - MEMROOT for prepared statements and stored procedures. - */ - - Item_arena *arena= thd->current_arena, backup; - if (arena->is_conventional()) - arena= 0; // For easier test - else - thd->set_n_backup_item_arena(arena, &backup); - - select->first_cond_optimization= 0; - - /* Convert all outer joins to inner joins if possible */ - conds= simplify_joins(join, join->join_list, conds, TRUE); - - select->prep_where= conds ? conds->copy_andor_structure(thd) : 0; - - if (arena) - thd->restore_backup_item_arena(arena, &backup); - } - if (!conds) { *cond_value= Item::COND_TRUE; @@ -7047,6 +7142,7 @@ optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value) DBUG_EXECUTE("where",print_where(conds,"after const change");); conds= remove_eq_conds(thd, conds, cond_value) ; DBUG_EXECUTE("info",print_where(conds,"after remove");); + } DBUG_RETURN(conds); } @@ -8871,9 +8967,9 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) table->file->extra(HA_EXTRA_NO_KEYREAD); } } - if (tab->on_expr && !table->null_row) + if (*tab->on_expr_ref && !table->null_row) { - if ((table->null_row= test(tab->on_expr->val_int() == 0))) + if ((table->null_row= test((*tab->on_expr_ref)->val_int() == 0))) mark_as_null_row(table); } if (!table->null_row) diff --git a/sql/sql_select.h b/sql/sql_select.h index c9cfeb70225..18664624a77 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -91,7 +91,8 @@ typedef struct st_join_table { SQL_SELECT *select; COND *select_cond; QUICK_SELECT_I *quick; - Item *on_expr; /* associated on expression */ + Item **on_expr_ref; /* pointer to the associated on expression */ + COND_EQUAL *cond_equal; /* multiple equalities for the on expression */ st_join_table *first_inner; /* first inner table for including outerjoin */ bool found; /* true after all matches or null complement */ bool not_null_compl;/* true before null complement is added */ diff --git a/sql/table.h b/sql/table.h index 35f6c6803fb..605cd516d9c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -21,6 +21,7 @@ class Item; /* Needed by ORDER */ class GRANT_TABLE; class st_select_lex_unit; class st_select_lex; +class COND_EQUAL; /* Order clause list element */ @@ -209,6 +210,7 @@ typedef struct st_table_list char *db, *alias, *real_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ + COND_EQUAL *cond_equal; /* Used with outer join */ struct st_table_list *natural_join; /* natural join on this table*/ /* ... join ... USE INDEX ... IGNORE INDEX */ List *use_index, *ignore_index;