diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2009-09-01 00:02:09 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2009-09-01 00:02:09 +0400 |
commit | 3c3d091939581a257ac9a2fe914037090635c350 (patch) | |
tree | 03d5085bbb778a68766cec13706559c049ffb6bf | |
parent | c0e89ee4bb867c380e239aed3d307d577cabb8aa (diff) | |
download | mariadb-git-3c3d091939581a257ac9a2fe914037090635c350.tar.gz |
MWL#17: Table-elimination
- Addressing review feedback, generation 4.
include/my_global.h:
Make ALIGN_PTR's action correspond to that of ALIGN_SIZE
sql/item.cc:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
sql/item.h:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
sql/item_cmpfunc.cc:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
sql/item_subselect.cc:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
sql/item_subselect.h:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
sql/opt_table_elimination.cc:
MWL#17: Table-elimination
- Addressing review feedback, generation 4: abstract everything in case
we would need to change it for something else in the future.
sql/sql_list.h:
MWL#17: Table-elimination
- Introduce exchange_sort(List<T> ...) template function
sql/sql_select.cc:
MWL#17: Table-elimination
- Review feedback: function renames, better comments
-rw-r--r-- | include/my_global.h | 3 | ||||
-rw-r--r-- | sql/item.cc | 4 | ||||
-rw-r--r-- | sql/item.h | 27 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 28 | ||||
-rw-r--r-- | sql/item_subselect.cc | 4 | ||||
-rw-r--r-- | sql/item_subselect.h | 2 | ||||
-rw-r--r-- | sql/opt_table_elimination.cc | 1776 | ||||
-rw-r--r-- | sql/sql_list.h | 37 | ||||
-rw-r--r-- | sql/sql_select.cc | 43 |
9 files changed, 1129 insertions, 795 deletions
diff --git a/include/my_global.h b/include/my_global.h index b350ece971b..6b06b84eb24 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -950,8 +950,7 @@ typedef long long my_ptrdiff_t; #define MY_ALIGN(A,L) (((A) + (L) - 1) & ~((L) - 1)) #define ALIGN_SIZE(A) MY_ALIGN((A),sizeof(double)) /* Size to make adressable obj. */ -#define ALIGN_PTR(A, t) ((t*) MY_ALIGN((A),sizeof(t))) - /* Offset of field f in structure t */ +#define ALIGN_PTR(A, t) ((t*) MY_ALIGN((A), sizeof(double))) #define OFFSET(t, f) ((size_t)(char *)&((t *)0)->f) #define ADD_TO_PTR(ptr,size,type) (type) ((uchar*) (ptr)+size) #define PTR_BYTE_DIFF(A,B) (my_ptrdiff_t) ((uchar*) (A) - (uchar*) (B)) diff --git a/sql/item.cc b/sql/item.cc index 9cf369a2670..ed352d7b366 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1916,10 +1916,10 @@ void Item_field::reset_field(Field *f) } -bool Item_field::check_column_usage_processor(uchar *arg) +bool Item_field::enumerate_field_refs_processor(uchar *arg) { Field_enumerator *fe= (Field_enumerator*)arg; - fe->see_field(field); + fe->visit_field(field); return FALSE; } diff --git a/sql/item.h b/sql/item.h index 205c72ede9c..d680c9cf4c6 100644 --- a/sql/item.h +++ b/sql/item.h @@ -734,7 +734,7 @@ public: /* Bitmap of tables used by item (note: if you need to check dependencies on individual columns, check out - check_column_usage_processor) + class Field_enumerator) */ virtual table_map used_tables() const { return (table_map) 0L; } /* @@ -892,7 +892,7 @@ public: virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; } virtual bool is_expensive_processor(uchar *arg) { return 0; } virtual bool register_field_in_read_map(uchar *arg) { return 0; } - virtual bool check_column_usage_processor(uchar *arg) { return 0; } + virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; } virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } /* Check if a partition function is allowed @@ -1018,14 +1018,29 @@ public: }; -/* Data for Item::check_column_usage_processor */ +/* + Class to be used to enumerate all field references in an item tree. + Suggested usage: + + class My_enumerator : public Field_enumerator + { + virtual void visit_field() { ... your actions ...} + } + + My_enumerator enumerator; + item->walk(Item::enumerate_field_refs_processor, ...,(uchar*)&enumerator); + + This is similar to Visitor pattern. +*/ + class Field_enumerator { public: - virtual void see_field(Field *field)= 0; - virtual ~Field_enumerator() {}; /* Shut up compiler warning */ + virtual void visit_field(Field *field)= 0; + virtual ~Field_enumerator() {}; /* purecov: inspected */ }; + class sp_head; @@ -1491,7 +1506,7 @@ public: bool find_item_in_field_list_processor(uchar *arg); bool register_field_in_read_map(uchar *arg); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} - bool check_column_usage_processor(uchar *arg); + bool enumerate_field_refs_processor(uchar *arg); void cleanup(); bool result_as_longlong() { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bb391187fb9..b2e7be5ef09 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5168,33 +5168,7 @@ void Item_equal::merge(Item_equal *item) void Item_equal::sort(Item_field_cmpfunc cmp, void *arg) { - bool swap; - List_iterator<Item_field> it(fields); - do - { - Item_field *item1= it++; - Item_field **ref1= it.ref(); - Item_field *item2; - - swap= FALSE; - while ((item2= it++)) - { - Item_field **ref2= it.ref(); - if (cmp(item1, item2, arg) < 0) - { - Item_field *item= *ref1; - *ref1= *ref2; - *ref2= item; - swap= TRUE; - } - else - { - item1= item2; - ref1= ref2; - } - } - it.rewind(); - } while (swap); + exchange_sort<Item_field>(&fields, cmp, arg); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 45b540f5009..22792a5b8b9 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -215,13 +215,13 @@ err: } -bool Item_subselect::check_column_usage_processor(uchar *arg) +bool Item_subselect::enumerate_field_refs_processor(uchar *arg) { List_iterator<Item> it(refers_to); Item *item; while ((item= it++)) { - if (item->walk(&Item::check_column_usage_processor,FALSE, arg)) + if (item->walk(&Item::enumerate_field_refs_processor, FALSE, arg)) return TRUE; } return FALSE; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index dc1703c8f34..19d58c65259 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -135,7 +135,7 @@ public: enum_parsing_place place() { return parsing_place; } bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool mark_as_eliminated_processor(uchar *arg); - bool check_column_usage_processor(uchar *arg); + bool enumerate_field_refs_processor(uchar *arg); /** Get the SELECT_LEX structure associated with this Item. diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index 6c517fa7842..832a6358b65 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -94,7 +94,8 @@ tblX.columnY= expr - where expr is functionally-depdendent. + where expr is functionally depdendent. expr is functionally dependent when + all columns that it refers to are functionally dependent. These relationships are modeled as a bipartite directed graph that has dependencies as edges and two kinds of nodes: @@ -124,7 +125,7 @@ (their expressions are either constant or depend only on tables that are outside of the outer join in question) and performns a breadth-first traversal. If we reach the outer join nest node, it means outer join is - functionally-dependant and can be eliminated. Otherwise it cannot be. + functionally dependent and can be eliminated. Otherwise it cannot be. HANDLING MULTIPLE NESTED OUTER JOINS ==================================== @@ -174,33 +175,41 @@ table t2. */ -class Value_dep; - class Field_value; - class Table_value; +class Dep_value; + class Dep_value_field; + class Dep_value_table; -class Module_dep; - class Equality_module; - class Outer_join_module; - class Key_module; +class Dep_module; + class Dep_module_expr; + class Dep_module_goal; + class Dep_module_key; -class Func_dep_analyzer; +class Dep_analysis_context; /* - A value, something that can be bound or not bound. Also, values can be linked - in a list. + A value, something that can be bound or not bound. One can also iterate over + unbound modules that depend on this value */ -class Value_dep : public Sql_alloc +class Dep_value : public Sql_alloc { public: - Value_dep(): bound(FALSE), next(NULL) {} - virtual void now_bound(Func_dep_analyzer *fda, Module_dep **bound_modules)=0; - virtual ~Value_dep(){} /* purecov: inspected */ /* stop compiler warnings */ - + Dep_value(): bound(FALSE) {} + virtual ~Dep_value(){} /* purecov: inspected */ /* stop compiler warnings */ + + bool is_bound() { return bound; } + void make_bound() { bound= TRUE; } + + /* Iteration over unbound modules that depend on this value */ + typedef char *Iterator; + virtual Iterator init_unbound_modules_iter(char *buf)=0; + virtual Dep_module* get_next_unbound_module(Dep_analysis_context *dac, + Iterator iter) = 0; + static const size_t iterator_size; +protected: bool bound; - Value_dep *next; }; @@ -209,70 +218,136 @@ public: - the field depends on its table and equalities - expressions that use the field are its dependencies */ -class Field_value : public Value_dep + +class Dep_value_field : public Dep_value { public: - Field_value(Table_value *table_arg, Field *field_arg) : + Dep_value_field(Dep_value_table *table_arg, Field *field_arg) : table(table_arg), field(field_arg) {} - Table_value *table; /* Table this field is from */ - Field *field; + Dep_value_table *table; /* Table this field is from */ + Field *field; /* Field this object is representing */ + + /* Iteration over unbound modules that are our dependencies */ + virtual Iterator init_unbound_modules_iter(char *buf); + virtual Dep_module* get_next_unbound_module(Dep_analysis_context *dac, Iterator iter); + void make_unbound_modules_iter_skip_keys(Iterator iter); + + static const size_t iterator_size; +private: /* Field_deps that belong to one table form a linked list, ordered by field_index */ - Field_value *next_table_field; + Dep_value_field *next_table_field; + /* - Offset to bits in Func_dep_analyzer::expr_deps + Offset to bits in Dep_analysis_context::expr_deps (see comment to that + member for semantics of the bits). */ uint bitmap_offset; - - void now_bound(Func_dep_analyzer *fda, Module_dep **bound_modules); - void signal_from_field_to_exprs(Func_dep_analyzer* fda, - Module_dep **bound_modules); + + class Module_iter + { + public: + /* if not null, return this and advance */ + Dep_module_key *key_dep; + /* Otherwise, this and advance */ + uint equality_no; + }; + friend class Dep_analysis_context; + friend class Field_dependency_recorder; + friend class Dep_value_table; }; +const size_t Dep_value_field::iterator_size= + ALIGN_SIZE(sizeof(Dep_value_field::Module_iter)); + /* - A table value. There is one Table_value object for every table that can + A table value. There is one Dep_value_table object for every table that can potentially be eliminated. Dependencies: - table depends on any of its unique keys - has its fields and embedding outer join as dependency */ -class Table_value : public Value_dep + +class Dep_value_table : public Dep_value { public: - Table_value(TABLE *table_arg) : + Dep_value_table(TABLE *table_arg) : table(table_arg), fields(NULL), keys(NULL) {} TABLE *table; - Field_value *fields; /* Ordered list of fields that belong to this table */ - Key_module *keys; /* Ordered list of Unique keys in this table */ - void now_bound(Func_dep_analyzer *fda, Module_dep **bound_modules); + Dep_value_field *fields; /* Ordered list of fields that belong to this table */ + Dep_module_key *keys; /* Ordered list of Unique keys in this table */ + + /* Iteration over unbound modules that are our dependencies */ + Iterator init_unbound_modules_iter(char *buf); + Dep_module* get_next_unbound_module(Dep_analysis_context *dac, Iterator iter); + + static const size_t iterator_size; +private: + class Module_iter + { + public: + /* Space for field iterator */ + char buf[Dep_value_field::iterator_size]; + /* !NULL <=> iterating over dependenant modules of this field */ + Dep_value_field *field_dep; + bool returned_goal; + }; }; +const size_t Dep_value_table::iterator_size= + ALIGN_SIZE(sizeof(Dep_value_table::Module_iter)); + +const size_t Dep_value::iterator_size= + max(Dep_value_table::iterator_size, Dep_value_field::iterator_size); + + /* A 'module'. Module has unsatisfied dependencies, number of whose is stored in - unknown_args. Modules also can be linked together in a list. + unbound_args. Modules also can be linked together in a list. */ -class Module_dep : public Sql_alloc +class Dep_module : public Sql_alloc { public: - virtual bool now_bound(Func_dep_analyzer *fda, Value_dep **bound_modules)=0; - virtual ~Module_dep(){} /* purecov: inspected */ /* stop compiler warnings */ + virtual ~Dep_module(){} /* purecov: inspected */ /* stop compiler warnings */ + + /* Mark as bound. Currently is non-virtual and does nothing */ + void make_bound() {}; + /* - Used to make a linked list of elements that became bound and thus can - make elements that depend on them bound, too. + The final module will return TRUE here. When we see that TRUE was returned, + that will mean that functional dependency check succeeded. */ - Module_dep *next; - uint unknown_args; + virtual bool is_final () { return FALSE; } - Module_dep() : next(NULL), unknown_args(0) {} + /* + Increment number of bound arguments. this is expected to change + is_applicable() from false to true after sufficient set of arguments is + bound. + */ + void touch() { unbound_args--; } + bool is_applicable() { return !test(unbound_args); } + + /* Iteration over values that */ + typedef char *Iterator; + virtual Iterator init_unbound_values_iter(char *buf)=0; + virtual Dep_value* get_next_unbound_value(Dep_analysis_context *dac, Iterator iter)=0; + static const size_t iterator_size; +protected: + uint unbound_args; + + Dep_module() : unbound_args(0) {} + /* to bump unbound_args when constructing depedendencies */ + friend class Field_dependency_recorder; + friend class Dep_analysis_context; }; @@ -283,92 +358,139 @@ public: - tbl1.col1=tbl2.col2=... multi-equality. */ -class Equality_module : public Module_dep +class Dep_module_expr : public Dep_module { public: - Field_value *field; - Item *expression; - - List<Field_value> *mult_equal_fields; + Dep_value_field *field; + Item *expr; + List<Dep_value_field> *mult_equal_fields; /* Used during condition analysis only, similar to KEYUSE::level */ uint level; - bool now_bound(Func_dep_analyzer *fda, Value_dep **bound_values); + + Iterator init_unbound_values_iter(char *buf); + Dep_value* get_next_unbound_value(Dep_analysis_context *dac, Iterator iter); + static const size_t iterator_size; +private: + class Value_iter + { + public: + Dep_value_field *field; + List_iterator<Dep_value_field> it; + }; }; +const size_t Dep_module_expr::iterator_size= + ALIGN_SIZE(sizeof(Dep_module_expr::Value_iter)); + /* - A Unique key. + A Unique key - Unique key depends on all of its components - Key's table is its dependency */ -class Key_module: public Module_dep + +class Dep_module_key: public Dep_module { public: - Key_module(Table_value *table_arg, uint keyno_arg, uint n_parts_arg) : + Dep_module_key(Dep_value_table *table_arg, uint keyno_arg, uint n_parts_arg) : table(table_arg), keyno(keyno_arg), next_table_key(NULL) { - unknown_args= n_parts_arg; + unbound_args= n_parts_arg; } - Table_value *table; /* Table this key is from */ - uint keyno; + Dep_value_table *table; /* Table this key is from */ + uint keyno; /* The index we're representing */ /* Unique keys form a linked list, ordered by keyno */ - Key_module *next_table_key; - bool now_bound(Func_dep_analyzer *fda, Value_dep **bound_values); + Dep_module_key *next_table_key; + + Iterator init_unbound_values_iter(char *buf); + Dep_value* get_next_unbound_value(Dep_analysis_context *dac, Iterator iter); + static const size_t iterator_size; +private: + class Value_iter + { + public: + Dep_value_table *table; + }; }; +const size_t Dep_module_key::iterator_size= + ALIGN_SIZE(sizeof(Dep_module_key::Value_iter)); + +const size_t Dep_module::iterator_size= + max(Dep_module_expr::iterator_size, Dep_module_key::iterator_size); + /* - An outer join nest that is subject to elimination - - it depends on all tables inside it - - has its parent outer join as dependency + A module that represents outer join that we're trying to eliminate. If we + manage to declare this module to be bound, then outer join can be eliminated. */ -class Outer_join_module: public Module_dep + +class Dep_module_goal: public Dep_module { public: - Outer_join_module(uint n_children) + Dep_module_goal(uint n_children) { - unknown_args= n_children; + unbound_args= n_children; + } + bool is_final() { return TRUE; } + /* + This is the goal module, so the running wave algorithm should terminate + once it sees that this module is applicable and should never try to apply + it, hence no use for unbound value iterator implementation. + */ + Iterator init_unbound_values_iter(char *buf) + { + DBUG_ASSERT(0); + return NULL; + } + Dep_value* get_next_unbound_value(Dep_analysis_context *dac, Iterator iter) + { + DBUG_ASSERT(0); + return NULL; } - bool now_bound(Func_dep_analyzer *fda, Value_dep **bound_values); }; /* Functional dependency analyzer context */ -class Func_dep_analyzer +class Dep_analysis_context { public: - Func_dep_analyzer(JOIN *join_arg) : join(join_arg) - { - bzero(table_deps, sizeof(table_deps)); - } - - JOIN *join; /* Join we're working on */ + bool setup_equality_modules_deps(List<Dep_module> *bound_modules); + bool run_wave(List<Dep_module> *new_bound_modules); /* Tables that we're looking at eliminating */ table_map usable_tables; /* Array of equality dependencies */ - Equality_module *equality_mods; + Dep_module_expr *equality_mods; uint n_equality_mods; /* Number of elements in the array */ uint n_equality_mods_alloced; - /* tablenr -> Table_value* mapping. */ - Table_value *table_deps[MAX_KEY]; + /* tablenr -> Dep_value_table* mapping. */ + Dep_value_table *table_deps[MAX_KEY]; /* Element for the outer join we're attempting to eliminate */ - Outer_join_module *outer_join_dep; + Dep_module_goal *outer_join_dep; /* - Bitmap of how expressions depend on bits. Given a Field_value object, + Bitmap of how expressions depend on bits. Given a Dep_value_field object, one can check bitmap_is_set(expr_deps, field_val->bitmap_offset + expr_no) to see if expression equality_mods[expr_no] depends on the given field. */ MY_BITMAP expr_deps; + + Dep_value_table *create_table_value(TABLE *table); + Dep_value_field *get_field_value(Field *field); + +#ifndef DBUG_OFF + void dbug_print_deps(); +#endif }; + void eliminate_tables(JOIN *join); static bool @@ -383,78 +505,632 @@ bool check_func_dependency(JOIN *join, List_iterator<TABLE_LIST> *it, TABLE_LIST *oj_tbl, Item* cond); - static -void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, - uint *and_level, Item *cond); +void build_eq_mods_for_cond(Dep_analysis_context *dac, + Dep_module_expr **eq_mod, uint *and_level, + Item *cond); static -void add_eq_mod(Func_dep_analyzer *fda, Equality_module **eq_mod, - uint and_level, - Item_func *cond, Item *left, Item *right); +void check_equality(Dep_analysis_context *dac, Dep_module_expr **eq_mod, + uint and_level, Item_func *cond, Item *left, Item *right); static -Equality_module *merge_func_deps(Equality_module *start, - Equality_module *new_fields, - Equality_module *end, uint and_level); - -static Table_value *get_table_value(Func_dep_analyzer *fda, TABLE *table); -static Field_value *get_field_value(Func_dep_analyzer *fda, Field *field); +Dep_module_expr *merge_eq_mods(Dep_module_expr *start, + Dep_module_expr *new_fields, + Dep_module_expr *end, uint and_level); static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl); +static +void add_module_expr(Dep_analysis_context *dac, Dep_module_expr **eq_mod, + uint and_level, Dep_value_field *field_val, Item *right, + List<Dep_value_field>* mult_equal_fields); + + +/*****************************************************************************/ + +/* + Perform table elimination -static void add_eq_mod2(Func_dep_analyzer *fda, Equality_module **eq_mod, - uint and_level, Field_value *field_val, Item *right, - List<Field_value>* mult_equal_fields); + SYNOPSIS + eliminate_tables() + join Join to work on + + DESCRIPTION + This is the entry point for table elimination. Grep for MODULE INTERFACE + section in this file for calling convention. + The idea behind table elimination is that if we have an outer join: + + SELECT * FROM t1 LEFT JOIN + (t2 JOIN t3) ON t3.primary_key=t1.col AND + t4.primary_key=t2.col + such that + + 1. columns of the inner tables are not used anywhere ouside the outer + join (not in WHERE, not in GROUP/ORDER BY clause, not in select list + etc etc), and + 2. inner side of the outer join is guaranteed to produce at most one + record combination for each record combination of outer tables. + + then the inner side of the outer join can be removed from the query. + This is because it will always produce one matching record (either a + real match or a NULL-complemented record combination), and since there + are no references to columns of the inner tables anywhere, it doesn't + matter which record combination it was. + + This function primary handles checking #1. It collects a bitmap of + tables that are not used in select list/GROUP BY/ORDER BY/HAVING/etc and + thus can possibly be eliminated. + + After this, if #1 is met, the function calls eliminate_tables_for_list() + that checks #2. + + SIDE EFFECTS + See the OVERVIEW section at the top of this file. + +*/ + +void eliminate_tables(JOIN *join) +{ + THD* thd= join->thd; + Item *item; + table_map used_tables; + DBUG_ENTER("eliminate_tables"); + + DBUG_ASSERT(join->eliminated_tables == 0); + + /* If there are no outer joins, we have nothing to eliminate: */ + if (!join->outer_join) + DBUG_VOID_RETURN; #ifndef DBUG_OFF -static void dbug_print_deps(Func_dep_analyzer *fda); -#endif + if (!optimizer_flag(thd, OPTIMIZER_SWITCH_TABLE_ELIMINATION)) + DBUG_VOID_RETURN; /* purecov: inspected */ +#endif + + /* Find the tables that are referred to from WHERE/HAVING */ + used_tables= (join->conds? join->conds->used_tables() : 0) | + (join->having? join->having->used_tables() : 0); + + /* Add tables referred to from the select list */ + List_iterator<Item> it(join->fields_list); + while ((item= it++)) + used_tables |= item->used_tables(); + + /* Add tables referred to from ORDER BY and GROUP BY lists */ + ORDER *all_lists[]= { join->order, join->group_list}; + for (int i=0; i < 2; i++) + { + for (ORDER *cur_list= all_lists[i]; cur_list; cur_list= cur_list->next) + used_tables |= (*(cur_list->item))->used_tables(); + } + + if (join->select_lex == &thd->lex->select_lex) + { + + /* Multi-table UPDATE: don't eliminate tables referred from SET statement */ + if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) + { + /* Multi-table UPDATE and DELETE: don't eliminate the tables we modify: */ + used_tables |= thd->table_map_for_update; + List_iterator<Item> it2(thd->lex->value_list); + while ((item= it2++)) + used_tables |= item->used_tables(); + } + + if (thd->lex->sql_command == SQLCOM_DELETE_MULTI) + { + TABLE_LIST *tbl; + for (tbl= (TABLE_LIST*)thd->lex->auxiliary_table_list.first; + tbl; tbl= tbl->next_local) + { + used_tables |= tbl->table->map; + } + } + } + + table_map all_tables= join->all_tables_map(); + if (all_tables & ~used_tables) + { + /* There are some tables that we probably could eliminate. Try it. */ + eliminate_tables_for_list(join, join->join_list, all_tables, NULL, + used_tables); + } + DBUG_VOID_RETURN; +} + + +/* + Perform table elimination in a given join list + + SYNOPSIS + eliminate_tables_for_list() + join The join we're working on + join_list Join list to eliminate tables from (and if + on_expr !=NULL, then try eliminating join_list + itself) + list_tables Bitmap of tables embedded in the join_list. + on_expr ON expression, if the join list is the inner side + of an outer join. + NULL means it's not an outer join but rather a + top-level join list. + tables_used_elsewhere Bitmap of tables that are referred to from + somewhere outside of the join list (e.g. + select list, HAVING, other ON expressions, etc). + + DESCRIPTION + Perform table elimination in a given join list. + + RETURN + TRUE The entire join list eliminated + FALSE Join list wasn't eliminated (but some of its child outer joins + possibly were) +*/ + +static bool +eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list, + table_map list_tables, Item *on_expr, + table_map tables_used_elsewhere) +{ + TABLE_LIST *tbl; + List_iterator<TABLE_LIST> it(*join_list); + table_map tables_used_on_left= 0; + bool all_eliminated= TRUE; + + while ((tbl= it++)) + { + if (tbl->on_expr) + { + table_map outside_used_tables= tables_used_elsewhere | + tables_used_on_left; + if (tbl->nested_join) + { + /* This is "... LEFT JOIN (join_nest) ON cond" */ + if (eliminate_tables_for_list(join, + &tbl->nested_join->join_list, + tbl->nested_join->used_tables, + tbl->on_expr, + outside_used_tables)) + { + mark_as_eliminated(join, tbl); + } + else + all_eliminated= FALSE; + } + else + { + /* This is "... LEFT JOIN tbl ON cond" */ + if (!(tbl->table->map & outside_used_tables) && + check_func_dependency(join, tbl->table->map, NULL, tbl, + tbl->on_expr)) + { + mark_as_eliminated(join, tbl); + } + else + all_eliminated= FALSE; + } + tables_used_on_left |= tbl->on_expr->used_tables(); + } + else + { + DBUG_ASSERT(!tbl->nested_join); + } + } + + /* Try eliminating the nest we're called for */ + if (all_eliminated && on_expr && !(list_tables & tables_used_elsewhere)) + { + it.rewind(); + return check_func_dependency(join, list_tables & ~join->eliminated_tables, + &it, NULL, on_expr); + } + return FALSE; /* not eliminated */ +} + + +/* + Check if given condition makes given set of tables functionally dependent + + SYNOPSIS + check_func_dependency() + join Join we're procesing + dep_tables Tables that we check to be functionally dependent (on + everything else) + it Iterator that enumerates these tables, or NULL if we're + checking one single table and it is specified in oj_tbl + parameter. + oj_tbl NULL, or one single table that we're checking + cond Condition to use to prove functional dependency + + DESCRIPTION + Check if we can use given condition to infer that the set of given tables + is functionally dependent on everything else. + + RETURN + TRUE - Yes, functionally dependent + FALSE - No, or error +*/ + +static +bool check_func_dependency(JOIN *join, + table_map dep_tables, + List_iterator<TABLE_LIST> *it, + TABLE_LIST *oj_tbl, + Item* cond) +{ + Dep_analysis_context dac; + + /* + Pre-alloc some Dep_module_expr structures. We don't need this to be + guaranteed upper bound. + */ + dac.n_equality_mods_alloced= + join->thd->lex->current_select->max_equal_elems + + (join->thd->lex->current_select->cond_count+1)*2 + + join->thd->lex->current_select->between_count; + + bzero(dac.table_deps, sizeof(dac.table_deps)); + if (!(dac.equality_mods= new Dep_module_expr[dac.n_equality_mods_alloced])) + return FALSE; /* purecov: inspected */ + + Dep_module_expr* last_eq_mod= dac.equality_mods; + + /* Create Dep_value_table objects for all tables we're trying to eliminate */ + if (oj_tbl) + { + if (!dac.create_table_value(oj_tbl->table)) + return FALSE; /* purecov: inspected */ + } + else + { + TABLE_LIST *tbl; + while ((tbl= (*it)++)) + { + if (tbl->table && (tbl->table->map & dep_tables)) + { + if (!dac.create_table_value(tbl->table)) + return FALSE; /* purecov: inspected */ + } + } + } + dac.usable_tables= dep_tables; + + /* + Analyze the the ON expression and create Dep_module_expr objects and + Dep_value_field objects for the used fields. + */ + uint and_level=0; + build_eq_mods_for_cond(&dac, &last_eq_mod, &and_level, cond); + if (!(dac.n_equality_mods= last_eq_mod - dac.equality_mods)) + return FALSE; /* No useful conditions */ + + List<Dep_module> bound_modules; + + if (!(dac.outer_join_dep= new Dep_module_goal(my_count_bits(dep_tables))) || + dac.setup_equality_modules_deps(&bound_modules)) + { + return FALSE; /* OOM, default to non-dependent */ /* purecov: inspected */ + } + + DBUG_EXECUTE("test", dac.dbug_print_deps(); ); + + return dac.run_wave(&bound_modules); +} + + +/* + Running wave functional dependency check algorithm + + SYNOPSIS + Dep_analysis_context::run_wave() + new_bound_modules List of bound modules to start the running wave from. + The list is destroyed during execution + + DESCRIPTION + This function uses running wave algorithm to check if the join nest is + functionally-dependent. + We start from provided list of bound modules, and then run the wave across + dependency edges, trying the reach the Dep_module_goal module. If we manage + to reach it, then the join nest is functionally-dependent, otherwise it is + not. + + RETURN + TRUE Yes, functionally dependent + FALSE No. +*/ + +bool Dep_analysis_context::run_wave(List<Dep_module> *new_bound_modules) +{ + List<Dep_value> new_bound_values; + + Dep_value *value; + Dep_module *module; + + while (!new_bound_modules->is_empty()) + { + /* + The "wave" is in new_bound_modules list. Iterate over values that can be + reached from these modules but are not yet bound, and collect the next + wave generation in new_bound_values list. + */ + List_iterator<Dep_module> modules_it(*new_bound_modules); + while ((module= modules_it++)) + { + char iter_buf[Dep_module::iterator_size]; + Dep_module::Iterator iter; + iter= module->init_unbound_values_iter(iter_buf); + while ((value= module->get_next_unbound_value(this, iter))) + { + value->make_bound(); + new_bound_values.push_back(value); + } + } + new_bound_modules->empty(); + + /* + Now walk over list of values we've just found to be bound and check which + unbound modules can be reached from them. If there are some modules that + became bound, collect them in new_bound_modules list. + */ + List_iterator<Dep_value> value_it(new_bound_values); + while ((value= value_it++)) + { + char iter_buf[Dep_value::iterator_size]; + Dep_value::Iterator iter; + iter= value->init_unbound_modules_iter(iter_buf); + while ((module= value->get_next_unbound_module(this, iter))) + { + module->touch(); + if (!module->is_applicable()) + continue; + if (module->is_final()) + return TRUE; /* Functionally dependent */ + module->make_bound(); + new_bound_modules->push_back(module); + } + } + new_bound_values.empty(); + } + return FALSE; +} + + +/* + This is used to analyze expressions in "tbl.col=expr" dependencies so + that we can figure out which fields the expression depends on. +*/ + +class Field_dependency_recorder : public Field_enumerator +{ +public: + Field_dependency_recorder(Dep_analysis_context *ctx_arg): ctx(ctx_arg) + {} + + void visit_field(Field *field) + { + Dep_value_table *tbl_dep; + if ((tbl_dep= ctx->table_deps[field->table->tablenr])) + { + for (Dep_value_field *field_dep= tbl_dep->fields; field_dep; + field_dep= field_dep->next_table_field) + { + if (field->field_index == field_dep->field->field_index) + { + uint offs= field_dep->bitmap_offset + expr_offset; + if (!bitmap_is_set(&ctx->expr_deps, offs)) + ctx->equality_mods[expr_offset].unbound_args++; + bitmap_set_bit(&ctx->expr_deps, offs); + return; + } + } + /* + We got here if didn't find this field. It's not a part of + a unique key, and/or there is no field=expr element for it. + Bump the dependency anyway, this will signal that this dependency + cannot be satisfied. + */ + ctx->equality_mods[expr_offset].unbound_args++; + } + else + visited_other_tables= TRUE; + } + + Dep_analysis_context *ctx; + /* Offset of the expression we're processing in the dependency bitmap */ + uint expr_offset; + + bool visited_other_tables; +}; + + + + +/* + Setup inbound dependency relationships for tbl.col=expr equalities + + SYNOPSIS + setup_equality_modules_deps() + bound_deps_list Put here modules that were found not to depend on + any non-bound columns. + + DESCRIPTION + Setup inbound dependency relationships for tbl.col=expr equalities: + - allocate a bitmap where we store such dependencies + - for each "tbl.col=expr" equality, analyze the expr part and find out + which fields it refers to and set appropriate dependencies. + + RETURN + FALSE OK + TRUE Out of memory +*/ + +bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module> + *bound_modules) +{ + DBUG_ENTER("setup_equality_modules_deps"); + + /* + Count Dep_value_field objects and assign each of them a unique + bitmap_offset value. + */ + uint offset= 0; + for (Dep_value_table **tbl_dep= table_deps; + tbl_dep < table_deps + MAX_TABLES; + tbl_dep++) + { + if (*tbl_dep) + { + for (Dep_value_field *field_dep= (*tbl_dep)->fields; + field_dep; + field_dep= field_dep->next_table_field) + { + field_dep->bitmap_offset= offset; + offset += n_equality_mods; + } + } + } + + void *buf; + if (!(buf= current_thd->alloc(bitmap_buffer_size(offset))) || + bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE)) + { + DBUG_RETURN(TRUE); /* purecov: inspected */ + } + bitmap_clear_all(&expr_deps); + + /* + Analyze all "field=expr" dependencies, and have expr_deps encode + dependencies of expressions from fields. + + Also collect a linked list of equalities that are bound. + */ + Field_dependency_recorder deps_recorder(this); + for (Dep_module_expr *eq_mod= equality_mods; + eq_mod < equality_mods + n_equality_mods; + eq_mod++) + { + deps_recorder.expr_offset= eq_mod - equality_mods; + deps_recorder.visited_other_tables= FALSE; + eq_mod->unbound_args= 0; + + if (eq_mod->field) + { + /* Regular tbl.col=expr(tblX1.col1, tblY1.col2, ...) */ + eq_mod->expr->walk(&Item::enumerate_field_refs_processor, FALSE, + (uchar*)&deps_recorder); + } + else + { + /* It's a multi-equality */ + eq_mod->unbound_args= !test(eq_mod->expr); + List_iterator<Dep_value_field> it(*eq_mod->mult_equal_fields); + Dep_value_field* field_val; + while ((field_val= it++)) + { + uint offs= field_val->bitmap_offset + eq_mod - equality_mods; + bitmap_set_bit(&expr_deps, offs); + } + } + + if (!eq_mod->unbound_args) + bound_modules->push_back(eq_mod); + } + + DBUG_RETURN(FALSE); +} -/*******************************************************************************************/ /* - Produce Eq_dep elements for given condition. + Ordering that we're using whenever we need to maintain a no-duplicates list + of field value objects. +*/ + +static +int compare_field_values(Dep_value_field *a, Dep_value_field *b, void *unused) +{ + uint a_ratio= a->field->table->tablenr*MAX_FIELDS + + a->field->field_index; + + uint b_ratio= b->field->table->tablenr*MAX_FIELDS + + b->field->field_index; + return (a_ratio < b_ratio)? -1 : ((a_ratio == b_ratio)? 0 : 1); +} + + +/* + Produce Dep_module_expr elements for given condition. SYNOPSIS build_eq_mods_for_cond() - fda Table elimination context - eq_mod INOUT Put produced equality conditions here - and_level INOUT AND-level (like in add_key_fields) - cond Condition to process + ctx Table elimination context + eq_mod INOUT Put produced equality conditions here + and_level INOUT AND-level (like in add_key_fields) + cond Condition to process DESCRIPTION + Analyze the given condition and produce an array of Dep_module_expr + dependencies from it. The idea of analysis is as follows: + There are useful equalities that have form + + eliminable_tbl.field = expr (denote as useful_equality) + + The condition is composed of useful equalities and other conditions that + are combined together with AND and OR operators. We process the condition + in recursive fashion according to these basic rules: + + useful_equality1 AND useful_equality2 -> make array of two + Dep_module_expr objects + + useful_equality AND other_cond -> discard other_cond + + useful_equality OR other_cond -> discard everything + + useful_equality1 OR useful_equality2 -> check if both sides of OR are the + same equality. If yes, that's the + result, otherwise discard + everything. + + The rules are used to map the condition into an array Dep_module_expr + elements. The array will specify functional dependencies that logically + follow from the condition. + + SEE ALSO This function is modeled after add_key_fields() */ static -void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, +void build_eq_mods_for_cond(Dep_analysis_context *ctx, + Dep_module_expr **eq_mod, uint *and_level, Item *cond) { if (cond->type() == Item_func::COND_ITEM) { List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); - Equality_module *org_key_fields= *eq_mod; + uint orig_offset= *eq_mod - ctx->equality_mods; /* AND/OR */ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { Item *item; while ((item=li++)) - build_eq_mods_for_cond(fda, eq_mod, and_level, item); - for (; org_key_fields != *eq_mod ; org_key_fields++) - org_key_fields->level= *and_level; + build_eq_mods_for_cond(ctx, eq_mod, and_level, item); + + for (Dep_module_expr *mod_exp= ctx->equality_mods + orig_offset; + mod_exp != *eq_mod ; mod_exp++) + { + mod_exp->level= *and_level; + } } else { Item *item; (*and_level)++; - build_eq_mods_for_cond(fda, eq_mod, and_level, li++); + build_eq_mods_for_cond(ctx, eq_mod, and_level, li++); while ((item=li++)) { - Equality_module *start_key_fields= *eq_mod; + Dep_module_expr *start_key_fields= *eq_mod; (*and_level)++; - build_eq_mods_for_cond(fda, eq_mod, and_level, item); - *eq_mod= merge_func_deps(org_key_fields, start_key_fields, *eq_mod, - ++(*and_level)); + build_eq_mods_for_cond(ctx, eq_mod, and_level, item); + *eq_mod= merge_eq_mods(ctx->equality_mods + orig_offset, + start_key_fields, *eq_mod, + ++(*and_level)); } } return; @@ -474,23 +1150,23 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, (fld= args[0]->real_item())->type() == Item::FIELD_ITEM && args[1]->eq(args[2], ((Item_field*)fld)->field->binary())) { - add_eq_mod(fda, eq_mod, *and_level, cond_func, args[0], args[1]); - add_eq_mod(fda, eq_mod, *and_level, cond_func, args[1], args[0]); + check_equality(ctx, eq_mod, *and_level, cond_func, args[0], args[1]); + check_equality(ctx, eq_mod, *and_level, cond_func, args[1], args[0]); } break; } case Item_func::EQ_FUNC: case Item_func::EQUAL_FUNC: { - add_eq_mod(fda, eq_mod, *and_level, cond_func, args[0], args[1]); - add_eq_mod(fda, eq_mod, *and_level, cond_func, args[1], args[0]); + check_equality(ctx, eq_mod, *and_level, cond_func, args[0], args[1]); + check_equality(ctx, eq_mod, *and_level, cond_func, args[1], args[0]); break; } case Item_func::ISNULL_FUNC: { Item *tmp=new Item_null; if (tmp) - add_eq_mod(fda, eq_mod, *and_level, cond_func, args[0], tmp); + check_equality(ctx, eq_mod, *and_level, cond_func, args[0], tmp); break; } case Item_func::MULT_EQUAL_FUNC: @@ -501,15 +1177,15 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, tbl1.field1 = tbl2.field2 = tbl3.field3 [= const_expr] multiple-equality. Do two things: - - Collect an ordered List<Field_value> of tblX.colY where tblX is one - of those that we're trying to eliminate. - - rembember if there was a const_expr or tblY.colZ that we can consider - bound. - Store all collected information in a Equality_module object. + - Collect List<Dep_value_field> of tblX.colY where tblX is one of the + tables we're trying to eliminate. + - rembember if there was a bound value, either const_expr or tblY.colZ + swher tblY is not a table that we're trying to eliminate. + Store all collected information in a Dep_module_expr object. */ Item_equal *item_equal= (Item_equal*)cond; - List<Field_value> *fvl; - if (!(fvl= new List<Field_value>)) + List<Dep_value_field> *fvl; + if (!(fvl= new List<Dep_value_field>)) break; /* purecov: inspected */ Item_equal_iterator it(*item_equal); @@ -517,31 +1193,11 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, Item *bound_item= item_equal->get_const(); while ((item= it++)) { - if ((item->used_tables() & fda->usable_tables)) + if ((item->used_tables() & ctx->usable_tables)) { - Field_value *field_val; - if ((field_val= get_field_value(fda, item->field))) - { - List_iterator<Field_value> it2(*fvl); - Field_value *other_f; - uint field_val_ratio= field_val->field->table->tablenr*MAX_FIELDS + - field_val->field->field_index; - bool added= FALSE; - while ((other_f= it2++)) - { - uint other_f_ratio= other_f->field->table->tablenr*MAX_FIELDS + - other_f->field->field_index; - if (other_f_ratio > field_val_ratio) - { - *(it2.ref())= field_val; - it2.after(other_f); - added= TRUE; - break; - } - } - if (!added) - fvl->push_back(field_val); - } + Dep_value_field *field_val; + if ((field_val= ctx->get_field_value(item->field))) + fvl->push_back(field_val); } else { @@ -549,7 +1205,8 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, bound_item= item; } } - add_eq_mod2(fda, eq_mod, *and_level, NULL, bound_item, fvl); + exchange_sort<Dep_value_field>(fvl, compare_field_values, NULL); + add_module_expr(ctx, eq_mod, *and_level, NULL, bound_item, fvl); break; } default: @@ -559,23 +1216,23 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, /* - Perform an OR operation on two (adjacent) Equality_module arrays. + Perform an OR operation on two (adjacent) Dep_module_expr arrays. SYNOPSIS - merge_func_deps() + merge_eq_mods() start Start of left OR-part new_fields Start of right OR-part end End of right OR-part - and_level AND-level. + and_level AND-level (like in add_key_fields) DESCRIPTION - This function is invoked for two adjacent arrays of Equality_module elements: + This function is invoked for two adjacent arrays of Dep_module_expr elements: $LEFT_PART $RIGHT_PART +-----------------------+-----------------------+ start new_fields end - The goal is to produce an array which would correspnd to the combined + The goal is to produce an array which would correspond to the combined $LEFT_PART OR $RIGHT_PART @@ -604,19 +1261,20 @@ void build_eq_mods_for_cond(Func_dep_analyzer *fda, Equality_module **eq_mod, */ static -Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fields, - Equality_module *end, uint and_level) +Dep_module_expr *merge_eq_mods(Dep_module_expr *start, + Dep_module_expr *new_fields, + Dep_module_expr *end, uint and_level) { if (start == new_fields) return start; /* (nothing) OR (...) -> (nothing) */ if (new_fields == end) return start; /* (...) OR (nothing) -> (nothing) */ - Equality_module *first_free=new_fields; + Dep_module_expr *first_free= new_fields; for (; new_fields != end ; new_fields++) { - for (Equality_module *old=start ; old != first_free ; old++) + for (Dep_module_expr *old=start ; old != first_free ; old++) { if (old->field == new_fields->field) { @@ -626,7 +1284,7 @@ Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fi OR-ing two multiple equalities. We must compute an intersection of used fields, and check the constants according to these rules: - a=b=c=d OR a=c=e=f -> a=c (compute intersection) + a=b=c=d OR a=c=e=f -> a=c (compute intersection) a=const1 OR a=b -> (nothing) a=const1 OR a=const1 -> a=const1 a=const1 OR a=const2 -> (nothing) @@ -639,28 +1297,28 @@ Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fi equalities are guaranteed to be disjoint, so we'll only get one hit. */ - if (old->expression && new_fields->expression && - old->expression->eq_by_collation(new_fields->expression, - old->mult_equal_fields->head()->field->binary(), - old->mult_equal_fields->head()->field->charset())) + Field *eq_field= old->mult_equal_fields->head()->field; + if (old->expr && new_fields->expr && + old->expr->eq_by_collation(new_fields->expr, eq_field->binary(), + eq_field->charset())) { /* Ok, keep */ } else { /* no single constant/bound item. */ - old->expression= NULL; + old->expr= NULL; } - List <Field_value> *fv; - if (!(fv= new List<Field_value>)) + List <Dep_value_field> *fv; + if (!(fv= new List<Dep_value_field>)) break; /* purecov: inspected */ - List_iterator<Field_value> it1(*old->mult_equal_fields); - List_iterator<Field_value> it2(*new_fields->mult_equal_fields); - Field_value *lfield= it1++; - Field_value *rfield= it2++; - // Merge + List_iterator<Dep_value_field> it1(*old->mult_equal_fields); + List_iterator<Dep_value_field> it2(*new_fields->mult_equal_fields); + Dep_value_field *lfield= it1++; + Dep_value_field *rfield= it2++; + /* Intersect two ordered lists */ while (lfield && rfield) { if (lfield == rfield) @@ -671,38 +1329,34 @@ Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fi } else { - uint left_ratio= lfield->field->table->tablenr*MAX_FIELDS + - lfield->field->field_index; - uint right_ratio= rfield->field->table->tablenr*MAX_FIELDS + - rfield->field->field_index; - if (left_ratio < right_ratio) - lfield=it1++; + if (compare_field_values(lfield, rfield, NULL) < 0) + lfield= it1++; else - rfield=it2++; + rfield= it2++; } } - if (fv->elements + test(old->expression) > 1) + if (fv->elements + test(old->expr) > 1) { old->mult_equal_fields= fv; old->level= and_level; } } - else if (!new_fields->expression->const_item()) + else if (!new_fields->expr->const_item()) { /* If the value matches, we can use the key reference. If not, we keep it until we have examined all new values */ - if (old->expression->eq(new_fields->expression, - old->field->field->binary())) + if (old->expr->eq(new_fields->expr, + old->field->field->binary())) { old->level= and_level; } } - else if (old->expression->eq_by_collation(new_fields->expression, - old->field->field->binary(), - old->field->field->charset())) + else if (old->expr->eq_by_collation(new_fields->expr, + old->field->field->binary(), + old->field->field->charset())) { old->level= and_level; } @@ -722,7 +1376,7 @@ Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fi Ok, the results are within the [start, first_free) range, and the useful elements have level==and_level. Now, remove all unusable elements: */ - for (Equality_module *old=start ; old != first_free ;) + for (Dep_module_expr *old=start ; old != first_free ;) { if (old->level != and_level) { // Not used in all levels @@ -738,34 +1392,34 @@ Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fi /* - Add an Equality_module element for left=right condition + Add an Dep_module_expr element for left=right condition SYNOPSIS - add_eq_mod() + check_equality() fda Table elimination context - eq_mod INOUT Store created Equality_module here and increment ptr if + eq_mod INOUT Store created Dep_module_expr here and increment ptr if you do so - and_level AND-level () + and_level AND-level (like in add_key_fields) cond Condition we've inferred the left=right equality from. left Left expression right Right expression - usable_tables Create Equality_module only if Left_expression's table + usable_tables Create Dep_module_expr only if Left_expression's table belongs to this set. DESCRIPTION - Check if the passed equality means that 'left' expr is functionally dependent on - the 'right', and if yes, create an Equality_module object for it. - - RETURN - FALSE OK - TRUE Out of memory + Check if the passed left=right equality is such that + - 'left' is an Item_field referring to a field in a table we're checking + to be functionally depdendent, + - the equality allows to conclude that 'left' expression is functionally + dependent on the 'right', + and if so, create an Dep_module_expr object. */ static -void add_eq_mod(Func_dep_analyzer *fda, Equality_module **eq_mod, - uint and_level, Item_func *cond, Item *left, Item *right) +void check_equality(Dep_analysis_context *ctx, Dep_module_expr **eq_mod, + uint and_level, Item_func *cond, Item *left, Item *right) { - if ((left->used_tables() & fda->usable_tables) && + if ((left->used_tables() & ctx->usable_tables) && !(right->used_tables() & RAND_TABLE_BIT) && left->real_item()->type() == Item::FIELD_ITEM) { @@ -780,7 +1434,7 @@ void add_eq_mod(Func_dep_analyzer *fda, Equality_module **eq_mod, else { /* - We can't assume there's a functional dependency if the effective + We can't assume there's a functional dependency if the effective collation of the operation differ from the field collation. */ if (field->cmp_type() == STRING_RESULT && @@ -788,81 +1442,118 @@ void add_eq_mod(Func_dep_analyzer *fda, Equality_module **eq_mod, return; } } - Field_value *field_val; - if ((field_val= get_field_value(fda, field))) - add_eq_mod2(fda, eq_mod, and_level, field_val, right, NULL); + Dep_value_field *field_val; + if ((field_val= ctx->get_field_value(field))) + add_module_expr(ctx, eq_mod, and_level, field_val, right, NULL); } } -/* Just add eq_mod w/o any checks */ -static void add_eq_mod2(Func_dep_analyzer *fda, Equality_module **eq_mod, - uint and_level, Field_value *field_val, Item *right, - List<Field_value>* mult_equal_fields) +/* + Add a Dep_module_expr object with the specified parameters. + + DESCRIPTION + Add a Dep_module_expr object with the specified parameters. Re-allocate + the ctx->equality_mods array if it has no space left. +*/ + +static +void add_module_expr(Dep_analysis_context *ctx, Dep_module_expr **eq_mod, + uint and_level, Dep_value_field *field_val, + Item *right, List<Dep_value_field>* mult_equal_fields) { - if (*eq_mod == fda->equality_mods + fda->n_equality_mods_alloced) + if (*eq_mod == ctx->equality_mods + ctx->n_equality_mods_alloced) { /* We've filled the entire equality_mods array. Replace it with a bigger one. We do it somewhat inefficiently but it doesn't matter. */ /* purecov: begin inspected */ - Equality_module *new_arr; - if (!(new_arr= new Equality_module[fda->n_equality_mods_alloced *2])) + Dep_module_expr *new_arr; + if (!(new_arr= new Dep_module_expr[ctx->n_equality_mods_alloced *2])) return; - fda->n_equality_mods_alloced *= 2; - for (int i= 0; i < *eq_mod - fda->equality_mods; i++) - new_arr[i]= fda->equality_mods[i]; + ctx->n_equality_mods_alloced *= 2; + for (int i= 0; i < *eq_mod - ctx->equality_mods; i++) + new_arr[i]= ctx->equality_mods[i]; - fda->equality_mods= new_arr; - *eq_mod= new_arr + (*eq_mod - fda->equality_mods); + ctx->equality_mods= new_arr; + *eq_mod= new_arr + (*eq_mod - ctx->equality_mods); /* purecov: end */ } (*eq_mod)->field= field_val; - (*eq_mod)->expression= right; + (*eq_mod)->expr= right; (*eq_mod)->level= and_level; (*eq_mod)->mult_equal_fields= mult_equal_fields; (*eq_mod)++; } + /* - Get a Table_value object for the given table, creating it if necessary. + Create a Dep_value_table object for the given table + + SYNOPSIS + Dep_analysis_context::create_table_value() + table Table to create object for + + DESCRIPTION + Create a Dep_value_table object for the given table. Also create + Dep_module_key objects for all unique keys in the table. + + RETURN + Created table value object + NULL if out of memory */ -static Table_value *get_table_value(Func_dep_analyzer *fda, TABLE *table) +Dep_value_table *Dep_analysis_context::create_table_value(TABLE *table) { - Table_value *tbl_dep; - if (!(tbl_dep= new Table_value(table))) + Dep_value_table *tbl_dep; + if (!(tbl_dep= new Dep_value_table(table))) return NULL; /* purecov: inspected */ - Key_module **key_list= &(tbl_dep->keys); + Dep_module_key **key_list= &(tbl_dep->keys); /* Add dependencies for unique keys */ for (uint i=0; i < table->s->keys; i++) { KEY *key= table->key_info + i; if ((key->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME) { - Key_module *key_dep= new Key_module(tbl_dep, i, key->key_parts); + Dep_module_key *key_dep; + if (!(key_dep= new Dep_module_key(tbl_dep, i, key->key_parts))) + return NULL; *key_list= key_dep; key_list= &(key_dep->next_table_key); } } - return fda->table_deps[table->tablenr]= tbl_dep; + return table_deps[table->tablenr]= tbl_dep; } /* - Get a Field_value object for the given field, creating it if necessary + Get a Dep_value_field object for the given field, creating it if necessary + + SYNOPSIS + Dep_analysis_context::get_field_value() + field Field to create object for + + DESCRIPTION + Get a Dep_value_field object for the given field. First, we search for it + in the list of Dep_value_field objects we have already created. If we don't + find it, we create a new Dep_value_field and put it into the list of field + objects we have for the table. + + RETURN + Created field value object + NULL if out of memory */ -static Field_value *get_field_value(Func_dep_analyzer *fda, Field *field) +Dep_value_field *Dep_analysis_context::get_field_value(Field *field) { TABLE *table= field->table; - Table_value *tbl_dep= fda->table_deps[table->tablenr]; + Dep_value_table *tbl_dep= table_deps[table->tablenr]; /* Try finding the field in field list */ - Field_value **pfield= &(tbl_dep->fields); + Dep_value_field **pfield= &(tbl_dep->fields); while (*pfield && (*pfield)->field->field_index < field->field_index) { pfield= &((*pfield)->next_table_field); @@ -871,7 +1562,7 @@ static Field_value *get_field_value(Func_dep_analyzer *fda, Field *field) return *pfield; /* Create the field and insert it in the list */ - Field_value *new_field= new Field_value(tbl_dep, field); + Dep_value_field *new_field= new Dep_value_field(tbl_dep, field); new_field->next_table_field= *pfield; *pfield= new_field; @@ -879,575 +1570,163 @@ static Field_value *get_field_value(Func_dep_analyzer *fda, Field *field) } -/* - This is used to analyze expressions in "tbl.col=expr" dependencies so - that we can figure out which fields the expression depends on. +/* + Iteration over unbound modules that are our dependencies. + for those we have: + - dependendencies of our fields + - outer join we're in */ - -class Field_dependency_recorder : public Field_enumerator +char *Dep_value_table::init_unbound_modules_iter(char *buf) { -public: - Field_dependency_recorder(Func_dep_analyzer *te_arg): fda(te_arg) - {} - - void see_field(Field *field) + Module_iter *iter= ALIGN_PTR(my_ptrdiff_t(buf), Module_iter); + iter->field_dep= fields; + if (fields) { - Table_value *tbl_dep; - if ((tbl_dep= fda->table_deps[field->table->tablenr])) - { - for (Field_value *field_dep= tbl_dep->fields; field_dep; - field_dep= field_dep->next_table_field) - { - if (field->field_index == field_dep->field->field_index) - { - uint offs= field_dep->bitmap_offset + expr_offset; - if (!bitmap_is_set(&fda->expr_deps, offs)) - fda->equality_mods[expr_offset].unknown_args++; - bitmap_set_bit(&fda->expr_deps, offs); - return; - } - } - /* - We got here if didn't find this field. It's not a part of - a unique key, and/or there is no field=expr element for it. - Bump the dependency anyway, this will signal that this dependency - cannot be satisfied. - */ - fda->equality_mods[expr_offset].unknown_args++; - } - else - saw_other_tbl= TRUE; + fields->init_unbound_modules_iter(iter->buf); + fields->make_unbound_modules_iter_skip_keys(iter->buf); } - - Func_dep_analyzer *fda; - /* Offset of the expression we're processing in the dependency bitmap */ - uint expr_offset; - - bool saw_other_tbl; -}; - - -/* - Setup inbound dependency relationships for tbl.col=expr equalities - - SYNOPSIS - setup_equality_modules_deps() - fda Table elimination context - bound_deps_list OUT Start of linked list of elements that were found to - be bound (caller will use this to see if that - allows to declare further elements bound) - DESCRIPTION - Setup inbound dependency relationships for tbl.col=expr equalities: - - allocate a bitmap where we store such dependencies - - for each "tbl.col=expr" equality, analyze the expr part and find out - which fields it refers to and set appropriate dependencies. - - RETURN - FALSE OK - TRUE Out of memory -*/ - -static -bool setup_equality_modules_deps(Func_dep_analyzer *fda, - Module_dep **bound_deps_list) -{ - DBUG_ENTER("setup_equality_modules_deps"); - - /* - Count Field_value objects and assign each of them a unique bitmap_offset - value. - */ - uint offset= 0; - for (Table_value **tbl_dep=fda->table_deps; - tbl_dep < fda->table_deps + MAX_TABLES; - tbl_dep++) - { - if (*tbl_dep) - { - for (Field_value *field_dep= (*tbl_dep)->fields; - field_dep; - field_dep= field_dep->next_table_field) - { - field_dep->bitmap_offset= offset; - offset += fda->n_equality_mods; - } - } - } - - void *buf; - if (!(buf= current_thd->alloc(bitmap_buffer_size(offset))) || - bitmap_init(&fda->expr_deps, (my_bitmap_map*)buf, offset, FALSE)) - { - DBUG_RETURN(TRUE); /* purecov: inspected */ - } - bitmap_clear_all(&fda->expr_deps); - - /* - Analyze all "field=expr" dependencies, and have fda->expr_deps encode - dependencies of expressions from fields. - - Also collect a linked list of equalities that are bound. - */ - Module_dep *bound_dep= NULL; - Field_dependency_recorder deps_recorder(fda); - for (Equality_module *eq_mod= fda->equality_mods; - eq_mod < fda->equality_mods + fda->n_equality_mods; - eq_mod++) - { - deps_recorder.expr_offset= eq_mod - fda->equality_mods; - deps_recorder.saw_other_tbl= FALSE; - eq_mod->unknown_args= 0; - - if (eq_mod->field) - { - /* Regular tbl.col=expr(tblX1.col1, tblY1.col2, ...) */ - eq_mod->expression->walk(&Item::check_column_usage_processor, FALSE, - (uchar*)&deps_recorder); - } - else - { - /* It's a multi-equality */ - eq_mod->unknown_args= !test(eq_mod->expression); - List_iterator<Field_value> it(*eq_mod->mult_equal_fields); - Field_value* field_val; - while ((field_val= it++)) - { - uint offs= field_val->bitmap_offset + eq_mod - fda->equality_mods; - bitmap_set_bit(&fda->expr_deps, offs); - } - } - - if (!eq_mod->unknown_args) - { - eq_mod->next= bound_dep; - bound_dep= eq_mod; - } - } - *bound_deps_list= bound_dep; - - DBUG_RETURN(FALSE); + iter->returned_goal= FALSE; + return (char*)iter; } -/* - Perform table elimination - - SYNOPSIS - eliminate_tables() - join Join to work on - - DESCRIPTION - This is the entry point for table elimination. Grep for MODULE INTERFACE - section in this file for calling convention. - - The idea behind table elimination is that if we have an outer join: - - SELECT * FROM t1 LEFT JOIN - (t2 JOIN t3) ON t3.primary_key=t1.col AND - t4.primary_key=t2.col - such that - - 1. columns of the inner tables are not used anywhere ouside the outer - join (not in WHERE, not in GROUP/ORDER BY clause, not in select list - etc etc), and - 2. inner side of the outer join is guaranteed to produce at most one - record combination for each record combination of outer tables. - - then the inner side of the outer join can be removed from the query. - This is because it will always produce one matching record (either a - real match or a NULL-complemented record combination), and since there - are no references to columns of the inner tables anywhere, it doesn't - matter which record combination it was. - - This function primary handles checking #1. It collects a bitmap of - tables that are not used in select list/GROUP BY/ORDER BY/HAVING/etc and - thus can possibly be eliminated. - - SIDE EFFECTS - See the OVERVIEW section at the top of this file. - -*/ - -void eliminate_tables(JOIN *join) +Dep_module* +Dep_value_table::get_next_unbound_module(Dep_analysis_context *dac, + char *iter) { - THD* thd= join->thd; - Item *item; - table_map used_tables; - DBUG_ENTER("eliminate_tables"); - - DBUG_ASSERT(join->eliminated_tables == 0); - - /* If there are no outer joins, we have nothing to eliminate: */ - if (!join->outer_join) - DBUG_VOID_RETURN; - -#ifndef DBUG_OFF - if (!optimizer_flag(thd, OPTIMIZER_SWITCH_TABLE_ELIMINATION)) - DBUG_VOID_RETURN; /* purecov: inspected */ -#endif - - /* Find the tables that are referred to from WHERE/HAVING */ - used_tables= (join->conds? join->conds->used_tables() : 0) | - (join->having? join->having->used_tables() : 0); - - /* Add tables referred to from the select list */ - List_iterator<Item> it(join->fields_list); - while ((item= it++)) - used_tables |= item->used_tables(); - - /* Add tables referred to from ORDER BY and GROUP BY lists */ - ORDER *all_lists[]= { join->order, join->group_list}; - for (int i=0; i < 2; i++) + Module_iter *di= (Module_iter*)iter; + while (di->field_dep) { - for (ORDER *cur_list= all_lists[i]; cur_list; cur_list= cur_list->next) - used_tables |= (*(cur_list->item))->used_tables(); - } - - if (join->select_lex == &thd->lex->select_lex) - { - - /* Multi-table UPDATE: don't eliminate tables referred from SET statement */ - if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) + Dep_module *res; + if ((res= di->field_dep->get_next_unbound_module(dac, di->buf))) + return res; + if ((di->field_dep= di->field_dep->next_table_field)) { - /* Multi-table UPDATE and DELETE: don't eliminate the tables we modify: */ - used_tables |= thd->table_map_for_update; - List_iterator<Item> it2(thd->lex->value_list); - while ((item= it2++)) - used_tables |= item->used_tables(); - } - - if (thd->lex->sql_command == SQLCOM_DELETE_MULTI) - { - TABLE_LIST *tbl; - for (tbl= (TABLE_LIST*)thd->lex->auxiliary_table_list.first; - tbl; tbl= tbl->next_local) - { - used_tables |= tbl->table->map; - } + char *field_iter= ((Module_iter*)iter)->buf; + di->field_dep->init_unbound_modules_iter(field_iter); + di->field_dep->make_unbound_modules_iter_skip_keys(field_iter); } } - table_map all_tables= join->all_tables_map(); - if (all_tables & ~used_tables) + if (!di->returned_goal) { - /* There are some tables that we probably could eliminate. Try it. */ - eliminate_tables_for_list(join, join->join_list, all_tables, NULL, - used_tables); + di->returned_goal= TRUE; + return dac->outer_join_dep; } - DBUG_VOID_RETURN; + return NULL; } -/* - Perform table elimination in a given join list - - SYNOPSIS - eliminate_tables_for_list() - fda Table elimination context - join_list Join list to work on - list_tables Bitmap of tables embedded in the join_list. - on_expr ON expression, if the join list is the inner side - of an outer join. - NULL means it's not an outer join but rather a - top-level join list. - tables_used_elsewhere Bitmap of tables that are referred to from - somewhere outside of the join list (e.g. - select list, HAVING, other ON expressions, etc). - - DESCRIPTION - Perform table elimination in a given join list. - - RETURN - TRUE The entire join list eliminated - FALSE Join list wasn't eliminated (but some of its possibly were) -*/ - -static bool -eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list, - table_map list_tables, Item *on_expr, - table_map tables_used_elsewhere) +char *Dep_module_expr::init_unbound_values_iter(char *buf) { - TABLE_LIST *tbl; - List_iterator<TABLE_LIST> it(*join_list); - table_map tables_used_on_left= 0; - bool all_eliminated= TRUE; - - while ((tbl= it++)) - { - if (tbl->on_expr) - { - table_map outside_used_tables= tables_used_elsewhere | - tables_used_on_left; - if (tbl->nested_join) - { - /* This is "... LEFT JOIN (join_nest) ON cond" */ - if (eliminate_tables_for_list(join, - &tbl->nested_join->join_list, - tbl->nested_join->used_tables, - tbl->on_expr, - outside_used_tables)) - { - mark_as_eliminated(join, tbl); - } - else - all_eliminated= FALSE; - } - else - { - /* This is "... LEFT JOIN tbl ON cond" */ - if (!(tbl->table->map & outside_used_tables) && - check_func_dependency(join, tbl->table->map, NULL, tbl, - tbl->on_expr)) - { - mark_as_eliminated(join, tbl); - } - else - all_eliminated= FALSE; - } - tables_used_on_left |= tbl->on_expr->used_tables(); - } - else - { - DBUG_ASSERT(!tbl->nested_join); - } - } - - /* Try eliminating the nest we're called for */ - if (all_eliminated && on_expr && !(list_tables & tables_used_elsewhere)) + Value_iter *iter= ALIGN_PTR(my_ptrdiff_t(buf), Value_iter); + iter->field= field; + if (!field) { - it.rewind(); - return check_func_dependency(join, list_tables & ~join->eliminated_tables, - &it, NULL, on_expr); + new (&iter->it) List_iterator<Dep_value_field>(*mult_equal_fields); } - return FALSE; /* not eliminated */ + return (char*)iter; } -/* - Check if given condition makes given set of tables functionally-dependent - - SYNOPSIS - check_func_dependency() - fda Table elimination context - tables Set of tables we want to be functionally dependent - cond Condition to use - - DESCRIPTION - Check if we can use given condition to infer that the set of given tables - is functionally-dependent on everything else. - - RETURN - TRUE - Yes, functionally dependent - FALSE - No, or error -*/ - -static -bool check_func_dependency(JOIN *join, - table_map dep_tables, - List_iterator<TABLE_LIST> *it, - TABLE_LIST *oj_tbl, - Item* cond) +Dep_value* Dep_module_expr::get_next_unbound_value(Dep_analysis_context *dac, + char *buf) { - Module_dep *bound_modules; - - Func_dep_analyzer fda(join); - - /* - Pre-alloc some Equality_module structures. We don't need this to be - guaranteed upper bound. - */ - fda.n_equality_mods_alloced= - join->thd->lex->current_select->max_equal_elems + - (join->thd->lex->current_select->cond_count+1)*2 + - join->thd->lex->current_select->between_count; - - if (!(fda.equality_mods= new Equality_module[fda.n_equality_mods_alloced])) - return FALSE; /* purecov: inspected */ - - Equality_module* last_eq_mod= fda.equality_mods; - - /* Create Table_value objects for all tables we're trying to eliminate */ - if (oj_tbl) + Dep_value *res; + if (field) { - if (!get_table_value(&fda, oj_tbl->table)) - return FALSE; /* purecov: inspected */ + res= ((Value_iter*)buf)->field; + ((Value_iter*)buf)->field= NULL; + return (!res || res->is_bound())? NULL : res; } else { - TABLE_LIST *tbl; - while ((tbl= (*it)++)) + while ((res= ((Value_iter*)buf)->it++)) { - if (tbl->table && (tbl->table->map & dep_tables)) - { - if (!get_table_value(&fda, tbl->table)) - return FALSE; /* purecov: inspected */ - } + if (!res->is_bound()) + return res; } + return NULL; } - - fda.usable_tables= dep_tables; - /* - Analyze the the ON expression and create Equality_module objects and - Field_value objects for the used fields. - */ - uint and_level=0; - build_eq_mods_for_cond(&fda, &last_eq_mod, &and_level, cond); - if (!(fda.n_equality_mods= last_eq_mod - fda.equality_mods)) - return FALSE; /* No useful conditions */ - - if (!(fda.outer_join_dep= new Outer_join_module(my_count_bits(dep_tables))) || - setup_equality_modules_deps(&fda, &bound_modules)) - { - return FALSE; /* OOM, default to non-dependent */ /* purecov: inspected */ - } - - DBUG_EXECUTE("test", dbug_print_deps(&fda); ); - - /* The forward running wave algorithm: */ - Value_dep *bound_values= NULL; - while (bound_modules) - { - for (;bound_modules; bound_modules= bound_modules->next) - { - if (bound_modules->now_bound(&fda, &bound_values)) - return TRUE; /* Dependent */ - } - for (;bound_values; bound_values=bound_values->next) - bound_values->now_bound(&fda, &bound_modules); - } - return FALSE; /* Not dependent */ } -void Table_value::now_bound(Func_dep_analyzer *fda, - Module_dep **bound_modules) +char *Dep_module_key::init_unbound_values_iter(char *buf) { - DBUG_PRINT("info", ("table %s is now bound", table->alias)); - - /* Signal to all fields that they are now bound */ - for (Field_value *field_dep= fields; field_dep; - field_dep= field_dep->next_table_field) - { - if (!field_dep->bound) - { - /* Mark as bound and add to the list */ - field_dep->bound= TRUE; - field_dep->signal_from_field_to_exprs(fda, bound_modules); - } - } - - /* Signal to outer join that one more table is known */ - if (fda->outer_join_dep->unknown_args && - !--fda->outer_join_dep->unknown_args) - { - /* Mark as bound and add to the list */ - fda->outer_join_dep->next= *bound_modules; - *bound_modules= fda->outer_join_dep; - } + Value_iter *iter= ALIGN_PTR(my_ptrdiff_t(buf), Value_iter); + iter->table= table; + return (char*)iter; } -void Field_value::now_bound(Func_dep_analyzer *fda, - Module_dep **bound_modules) +Dep_value* Dep_module_key::get_next_unbound_value(Dep_analysis_context *dac, + Dep_module::Iterator iter) { - DBUG_PRINT("info", ("field %s.%s is now bound", field->table->alias, - field->field_name)); - - /* Signal to unique keys and expressions that use this field*/ - for (Key_module *key_dep= table->keys; key_dep; - key_dep= key_dep->next_table_key) - { - if (field->part_of_key.is_set(key_dep->keyno) && - key_dep->unknown_args && !--key_dep->unknown_args) - { - DBUG_PRINT("info", ("key %s.%s is now bound", - key_dep->table->table->alias, - key_dep->table->table->key_info[key_dep->keyno].name)); - /* Mark as bound and add to the list */ - key_dep->next= *bound_modules; - *bound_modules= key_dep; - } - } - signal_from_field_to_exprs(fda, bound_modules); + Dep_value* res= ((Value_iter*)iter)->table; + ((Value_iter*)iter)->table= NULL; + return res; } -/* - Walk through expressions that depend on this field and notify them - that this field is now known. -*/ -void Field_value::signal_from_field_to_exprs(Func_dep_analyzer* fda, - Module_dep **bound_modules) +Dep_value::Iterator Dep_value_field::init_unbound_modules_iter(char *buf) { - for (uint i=0; i < fda->n_equality_mods; i++) - { - if (bitmap_is_set(&fda->expr_deps, bitmap_offset + i) && - fda->equality_mods[i].unknown_args && - !--fda->equality_mods[i].unknown_args) - { - /* Mark as bound and add to the list */ - Equality_module* eq_mod= &fda->equality_mods[i]; - eq_mod->next= *bound_modules; - *bound_modules= eq_mod; - } - } + Module_iter *iter= ALIGN_PTR(my_ptrdiff_t(buf), Module_iter); + iter->key_dep= table->keys; + iter->equality_no= 0; + return (char*)iter; } -bool Outer_join_module::now_bound(Func_dep_analyzer *fda, - Value_dep **bound_values) +void Dep_value_field::make_unbound_modules_iter_skip_keys(Dep_value::Iterator iter) { - DBUG_PRINT("info", ("Outer join eliminated")); - return TRUE; /* Signal out that the search is finished */ + ((Module_iter*)iter)->key_dep= NULL; } -bool Equality_module::now_bound(Func_dep_analyzer *fda, - Value_dep **bound_values) +Dep_module* Dep_value_field::get_next_unbound_module(Dep_analysis_context *dac, + Dep_value::Iterator iter) { - if (mult_equal_fields) + Module_iter *di= (Module_iter*)iter; + Dep_module_key *key_dep= di->key_dep; + + /* + First, enumerate all unique keys that are + - not yet applicable + - have this field as a part of them + */ + while (key_dep && (key_dep->is_applicable() || + !field->part_of_key.is_set(key_dep->keyno))) { - /* It's a=b=c=... multiple equality. Mark all equality members as known. */ - List_iterator<Field_value> it(*mult_equal_fields); - Field_value *fv; - while ((fv= it++)) - { - if (!fv->bound) - { - /* Mark as bound and add to the list */ - fv->bound= TRUE; - fv->next= *bound_values; - *bound_values= fv; - } - } + key_dep= key_dep->next_table_key; } - else + + if (key_dep) { - /* It's a fieldX=exprY equality. Mark exprY as known */ - if (!field->bound) - { - /* Mark as bound and add to the list */ - field->bound= TRUE; - field->next= *bound_values; - *bound_values= field; - } + di->key_dep= key_dep->next_table_key; + return key_dep; } - return FALSE; -} - - -/* Unique key is known means its table is known */ - -bool Key_module::now_bound(Func_dep_analyzer *fda, Value_dep **bound_values) -{ - if (!table->bound) + else + di->key_dep= NULL; + + /* + Then walk through [multi]equalities and find those that + - depend on this field + - and are not bound yet. + */ + uint eq_no= di->equality_no; + while (eq_no < dac->n_equality_mods && + (!bitmap_is_set(&dac->expr_deps, bitmap_offset + eq_no) || + dac->equality_mods[eq_no].is_applicable())) { - /* Mark as bound and add to the list */ - table->bound= TRUE; - table->next= *bound_values; - *bound_values= table; + eq_no++; } - return FALSE; + + if (eq_no < dac->n_equality_mods) + { + di->equality_no= eq_no+1; + return &dac->equality_mods[eq_no]; + } + return NULL; } @@ -1490,8 +1769,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl) #ifndef DBUG_OFF /* purecov: begin inspected */ -static -void dbug_print_deps(Func_dep_analyzer *fda) +void Dep_analysis_context::dbug_print_deps() { DBUG_ENTER("dbug_print_deps"); DBUG_LOCK_FILE; @@ -1499,17 +1777,17 @@ void dbug_print_deps(Func_dep_analyzer *fda) fprintf(DBUG_FILE,"deps {\n"); /* Start with printing equalities */ - for (Equality_module *eq_mod= fda->equality_mods; - eq_mod != fda->equality_mods + fda->n_equality_mods; eq_mod++) + for (Dep_module_expr *eq_mod= equality_mods; + eq_mod != equality_mods + n_equality_mods; eq_mod++) { char buf[128]; String str(buf, sizeof(buf), &my_charset_bin); str.length(0); - eq_mod->expression->print(&str, QT_ORDINARY); + eq_mod->expr->print(&str, QT_ORDINARY); if (eq_mod->field) { fprintf(DBUG_FILE, " equality%d: %s -> %s.%s\n", - eq_mod - fda->equality_mods, + eq_mod - equality_mods, str.c_ptr(), eq_mod->field->table->table->alias, eq_mod->field->field->field_name); @@ -1517,7 +1795,7 @@ void dbug_print_deps(Func_dep_analyzer *fda) else { fprintf(DBUG_FILE, " equality%d: multi-equality", - eq_mod - fda->equality_mods); + eq_mod - equality_mods); } } fprintf(DBUG_FILE,"\n"); @@ -1525,21 +1803,21 @@ void dbug_print_deps(Func_dep_analyzer *fda) /* Then tables and their fields */ for (uint i=0; i < MAX_TABLES; i++) { - Table_value *table_dep; - if ((table_dep= fda->table_deps[i])) + Dep_value_table *table_dep; + if ((table_dep= table_deps[i])) { /* Print table */ fprintf(DBUG_FILE, " table %s\n", table_dep->table->alias); /* Print fields */ - for (Field_value *field_dep= table_dep->fields; field_dep; + for (Dep_value_field *field_dep= table_dep->fields; field_dep; field_dep= field_dep->next_table_field) { fprintf(DBUG_FILE, " field %s.%s ->", table_dep->table->alias, field_dep->field->field_name); uint ofs= field_dep->bitmap_offset; - for (uint bit= ofs; bit < ofs + fda->n_equality_mods; bit++) + for (uint bit= ofs; bit < ofs + n_equality_mods; bit++) { - if (bitmap_is_set(&fda->expr_deps, bit)) + if (bitmap_is_set(&expr_deps, bit)) fprintf(DBUG_FILE, " equality%d ", bit - ofs); } fprintf(DBUG_FILE, "\n"); diff --git a/sql/sql_list.h b/sql/sql_list.h index 6edf9a00b87..eb580120c1b 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -443,6 +443,43 @@ public: /* + Exchange sort algorithm for List<T>. +*/ +template <class T> +inline void exchange_sort(List<T> *list_to_sort, + int (*sort_func)(T *a, T *b, void *arg), void *arg) +{ + bool swap; + List_iterator<T> it(*list_to_sort); + do + { + T *item1= it++; + T **ref1= it.ref(); + T *item2; + + swap= FALSE; + while ((item2= it++)) + { + T **ref2= it.ref(); + if (sort_func(item1, item2, arg) < 0) + { + T *item= *ref1; + *ref1= *ref2; + *ref2= item; + swap= TRUE; + } + else + { + item1= item2; + ref1= ref2; + } + } + it.rewind(); + } while (swap); +} + + +/* A simple intrusive list which automaticly removes element from list on delete (for THD element) */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9362594acbe..a0313bbb17c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2991,22 +2991,33 @@ typedef struct key_field_t { elements that would correspond to "$LEFT_PART OR $RIGHT_PART". The rules for combining elements are as follows: + (keyfieldA1 AND keyfieldA2 AND ...) OR (keyfieldB1 AND keyfieldB2 AND ...)= - AND_ij (keyfieldA_i OR keyfieldB_j) + + = AND_ij (keyfieldA_i OR keyfieldB_j) We discard all (keyfieldA_i OR keyfieldB_j) that refer to different fields. For those referring to the same field, the logic is as follows: - t.keycol= + t.keycol=expr1 OR t.keycol=expr2 -> (since expr1 and expr2 are different + we can't produce a single equality, + so produce nothing) + + t.keycol=expr1 OR t.keycol=expr1 -> t.keycol=expr1 + + t.keycol=expr1 OR t.keycol IS NULL -> t.keycol=expr1, and also set + KEY_OPTIMIZE_REF_OR_NULL flag - To be able to do 'ref_or_null' we merge a comparison of a column - and 'column IS NULL' to one test. This is useful for sub select queries - that are internally transformed to something like:. + The last one is for ref_or_null access. We have handling for this special + because it's needed for evaluating IN subqueries that are internally + transformed into @code - SELECT * FROM t1 WHERE t1.key=outer_ref_field or t1.key IS NULL + EXISTS(SELECT * FROM t1 WHERE t1.key=outer_ref_field or t1.key IS NULL) @endcode + See add_key_fields() for discussion of what is and_level. + KEY_FIELD::null_rejecting is processed as follows: @n result has null_rejecting=true if it is set for both ORed references. for example: @@ -3346,6 +3357,26 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level, } } + +/* + In this and other functions, and_level is a number that is ever-growing + and is different for the contents of every AND or OR clause. For example, + when processing clause + + (a AND b AND c) OR (x AND y) + + we'll have + * KEY_FIELD elements for (a AND b AND c) are assigned and_level=1 + * KEY_FIELD elements for (x AND y) are assigned and_level=2 + * OR operation is performed, and whatever elements are left after it are + assigned and_level=3. + + The primary reason for having and_level attribute is the OR operation which + uses and_level to mark KEY_FIELDs that should get into the result of the OR + operation + +*/ + static void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, COND *cond, table_map usable_tables, |