diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 47 | ||||
-rw-r--r-- | sql/item.h | 26 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 233 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 63 | ||||
-rw-r--r-- | sql/item_func.cc | 34 | ||||
-rw-r--r-- | sql/item_func.h | 8 | ||||
-rw-r--r-- | sql/item_row.cc | 12 | ||||
-rw-r--r-- | sql/item_row.h | 1 | ||||
-rw-r--r-- | sql/item_strfunc.h | 8 | ||||
-rw-r--r-- | sql/opt_range.cc | 222 | ||||
-rw-r--r-- | sql/opt_sum.cc | 15 | ||||
-rw-r--r-- | sql/sql_list.h | 34 | ||||
-rw-r--r-- | sql/sql_select.cc | 801 | ||||
-rw-r--r-- | sql/sql_select.h | 2 |
14 files changed, 1397 insertions, 109 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<Item_equal> 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<Item*>(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<Item> 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<Item> &fields) { List_iterator<Item> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_field> 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<Item_equal> current_level; + COND_EQUAL() { parent_level= 0; } +}; + + +class Item_equal_iterator :List_iterator_fast<Item_field> +{ +public: + inline Item_equal_iterator(Item_equal &item_equal) + :List_iterator_fast<Item_field> (item_equal.fields) + {} + inline Item_field* operator++(int) + { + Item_field *item= (*(List_iterator_fast<Item_field> *) this)++; + return item; + } + inline void rewind(void) + { + List_iterator_fast<Item_field>::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<Item> &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++) + 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++)) { - 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); + 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); + } + } } - DBUG_RETURN(tree); - } - DBUG_RETURN(0); // Can't optimize this IN - } - - 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) - { - 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()); - } - /* check const op field */ - if (!tree && - cond_func->have_rev_func() && - cond_func->arguments()[1]->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() - )); + if (item_equal->get_const()) + break; + value= it++; + } + DBUG_RETURN(ftree); } - DBUG_RETURN(tree); + 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() && + 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<T> *list) { return base_list::concat(list); } + inline void disjoin(List<T> *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<T> &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 */ @@ -2203,6 +2282,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<Item_func_match>; template class List_iterator<Item_func_match>; #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<Item> *args= ((Item_cond*) cond)->argument_list(); + List_iterator<Item> li(*args); + Item *item; + List<Item> 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<Item_equal> 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<Item_equal> 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<Item> *args= ((Item_cond*) cond)->argument_list(); + + List_iterator<Item> 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<Item_equal> 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<Item> 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<Item> *)&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<Item> *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<Item> *) &cond_equal->current_level); + + List_iterator_fast<Item_equal> it(cond_equal->current_level); + while((item_equal= it++)) + { + item_equal->sort(table_join_idx); + } + } + + List_iterator<Item> 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<Item_equal> 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<COND_CMP> *) 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)); |