From da566c16c0c03f56caa8bd6bfd9452b73ad1a456 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 26 Nov 2003 17:23:52 -0800 Subject: [PATCH 1/9] 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: Introduced Item_equal sql/item.h: Introduced Item_equal. Added traverse method. sql/item_cmpfunc.cc: Introduced Item_equal. Added traverse mehtod. sql/item_cmpfunc.h: Itroduced Item_equal and Item_equal_iterator. sql/item_func.cc: Added traverse method. Introduced Item_equal. sql/item_func.h: Introduced Item_equal. Added traverse method. sql/item_row.cc: Added traverse method. sql/item_row.h: Added traverse method. sql/item_strfunc.h: Added traverse method. sql/opt_range.cc: Used Item_equal in range analysis. sql/opt_sum.cc: Introduced Item_equal. sql/sql_list.h: Added concat and disjoin methods to lists. Fixed remove method for lists. sql/sql_select.cc: Introdiced Item_equal: created Item_equal; used Item_equal objects to generate new paths to access tables. used Item_equal objects to optimize the execution plan chosen by optimizer. sql/sql_select.h: Introduced Item_equal. --- 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 62833fdd15f1924703dbf6edf931df2117fcd716 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 26 Nov 2003 17:57:43 -0800 Subject: [PATCH 2/9] Post-merge after introducing Item_equal sql/sql_list.h: Post-merge --- 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 f6697593d252efc2d4a41897fb503e1598af97ad Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 1 Dec 2003 18:19:31 -0800 Subject: [PATCH 3/9] 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 sql/sql_list.h: Cleanup sql/sql_select.cc: Fixing bugs in Item_equal code: added a parameter in substitute_for_equal_field; modified eliminate_item_equal. mysql-test/t/range.test: Added tests for != predicate. --- 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 fc6cf268b08b5b3140f302972d491dc5d1362728 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Dec 2003 14:33:42 -0800 Subject: [PATCH 4/9] 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: 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 2954cc67a9703eb774f2a6d08764fde5a8ef177a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Dec 2003 16:41:53 -0800 Subject: [PATCH 5/9] The output of explain command for some queries has changed after introducing Item_equal. BitKeeper/etc/ignore: Added mysql-test/r/index_merge_load.result mysql-test/t/index_merge.load to the ignore list --- .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 c88e5213d4f8acbbba33d293a9573042818f0aec Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 18 Feb 2004 22:21:37 -0800 Subject: [PATCH 6/9] Many files: After review fixes for Item_equal. sql/item.cc: After review fixes for Item_equal. sql/item.h: After review fixes for Item_equal. sql/item_cmpfunc.cc: After review fixes for Item_equal. sql/item_cmpfunc.h: After review fixes for Item_equal. sql/item_func.h: After review fixes for Item_equal. sql/item_func.cc: After review fixes for Item_equal. sql/item_row.cc: After review fixes for Item_equal. sql/item_row.h: After review fixes for Item_equal. sql/item_strfunc.h: After review fixes for Item_equal. sql/opt_range.cc: After review fixes for Item_equal. sql/sql_list.h: After review fixes for Item_equal. sql/sql_select.cc: After review fixes for Item_equal. mysql-test/r/select.result: After review fixes for Item_equal. mysql-test/r/subselect.result: 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 fd98ed36c6da045185a6517c6942f893524f363c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 19 Feb 2004 01:12:49 -0800 Subject: [PATCH 7/9] 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 efaf0f0dc54315ebfa7af7afe135737a8beb743a Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 19 Feb 2004 12:04:46 -0800 Subject: [PATCH 8/9] sql_select.cc, opt_range.cc, item.cc: After merge fixes. sql/item.cc: After merge fixes. sql/opt_range.cc: After merge fixes. sql/sql_select.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 b692e9c2154642dc8cf612d2a87cc8f5c047222f Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 19 Feb 2004 17:12:31 -0800 Subject: [PATCH 9/9] sql_select.cc, item_cmpfunc.h: After merge fix for collation. sql/item_cmpfunc.h: After merge fix for collation. sql/sql_select.cc: 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)