Commit 1396c2d2 authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#17: Table elimination

- Better comments
- Switch from "type" enum and switch to virtual functions for member funcs.
parent 307a6ba5
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
Table elimination is redone on every PS re-execution. Table elimination is redone on every PS re-execution.
TABLE ELIMINATION ALGORITHM TABLE ELIMINATION ALGORITHM FOR ONE OUTER JOIN
As said above, we can remove inner side of an outer join if it is As said above, we can remove inner side of an outer join if it is
...@@ -101,14 +101,14 @@ ...@@ -101,14 +101,14 @@
each value is either bound (i.e. functionally dependent) or not. each value is either bound (i.e. functionally dependent) or not.
Module nodes: Module nodes:
- Nodes representing tblX.colY=expr equalities. Equality node has - Modules representing tblX.colY=expr equalities. Equality module has
= incoming edges from columns used in expr = incoming edges from columns used in expr
= outgoing edge to tblX.colY column. = outgoing edge to tblX.colY column.
- Nodes representing unique keys. Unique key has - Nodes representing unique keys. Unique key has
= incoming edges from key component value nodes = incoming edges from key component value modules
= outgoing edge to key's table node = outgoing edge to key's table module
- Inner side of outer join node. Outer join node has - Inner side of outer join module. Outer join module has
= incoming edges from table value nodes = incoming edges from table value modules
= No outgoing edges. Once we reach it, we know we can eliminate the = No outgoing edges. Once we reach it, we know we can eliminate the
outer join. outer join.
A module may depend on multiple values, and hence its primary attribute is A module may depend on multiple values, and hence its primary attribute is
...@@ -116,10 +116,13 @@ ...@@ -116,10 +116,13 @@
The algorithm starts with equality nodes that don't have any incoming edges The algorithm starts with equality nodes that don't have any incoming edges
(their expressions are either constant or depend only on tables that are (their expressions are either constant or depend only on tables that are
outside of any outer joins) and proceeds to traverse dependency->dependant outside of the outer join in question) and performns a breadth-first
edges until we've other traversed everything (TODO rephrase elaborate), or traversal. If we reach the outer join nest node, it means outer join is
we've reached the point where all outer join modules have zero unsatisfied functionally-dependant and can be eliminated. Otherwise it cannot.
dependencies.
HANDLING MULTIPLE NESTED OUTER JOINS
(TODO : explanations why 'local bottom up is sufficient')
*/ */
class Value_dep; class Value_dep;
...@@ -143,13 +146,9 @@ class Table_elimination; ...@@ -143,13 +146,9 @@ class Table_elimination;
class Value_dep : public Sql_alloc class Value_dep : public Sql_alloc
{ {
public: public:
enum { Value_dep(): bound(FALSE), next(NULL) {}
VALUE_FIELD, virtual void now_bound(Table_elimination *te, Module_dep **bound_modules)=0;
VALUE_TABLE, virtual ~Value_dep() {} /* only to shut up compiler warnings */
} type; /* Type of the object */
Value_dep(): bound(FALSE), next(NULL)
{}
bool bound; bool bound;
Value_dep *next; Value_dep *next;
...@@ -166,22 +165,28 @@ class Field_value : public Value_dep ...@@ -166,22 +165,28 @@ class Field_value : public Value_dep
public: public:
Field_value(Table_value *table_arg, Field *field_arg) : Field_value(Table_value *table_arg, Field *field_arg) :
table(table_arg), field(field_arg) table(table_arg), field(field_arg)
{ {}
type= Value_dep::VALUE_FIELD;
}
Table_value *table; /* Table this field is from */ Table_value *table; /* Table this field is from */
Field *field; Field *field;
/* /*
Field_deps that belong to one table form a linked list. list members are Field_deps that belong to one table form a linked list, ordered by
ordered by field_index field_index
*/ */
Field_value *next_table_field; Field_value *next_table_field;
uint bitmap_offset; /* Offset of our part of the bitmap */ uint bitmap_offset; /* Offset of our part of the bitmap */
/*
Field became known. Check out
- unique keys we belong to
- expressions that depend on us.
*/
void now_bound(Table_elimination *te, Module_dep **bound_modules);
void signal_from_field_to_exprs(Table_elimination* te,
Module_dep **bound_modules);
}; };
/* /*
A table value. There is one Table_value object for every table that can A table value. There is one Table_value object for every table that can
potentially be eliminated. potentially be eliminated.
...@@ -192,14 +197,13 @@ class Table_value : public Value_dep ...@@ -192,14 +197,13 @@ class Table_value : public Value_dep
{ {
public: public:
Table_value(TABLE *table_arg) : Table_value(TABLE *table_arg) :
table(table_arg), fields(NULL), keys(NULL), outer_join_dep(NULL) table(table_arg), fields(NULL), keys(NULL)
{ {}
type= Value_dep::VALUE_TABLE;
}
TABLE *table; TABLE *table;
Field_value *fields; /* Ordered list of fields that belong to this table */ Field_value *fields; /* Ordered list of fields that belong to this table */
Key_module *keys; /* Ordered list of Unique keys in this table */ Key_module *keys; /* Ordered list of Unique keys in this table */
Outer_join_module *outer_join_dep; /* Innermost eliminable outer join we're in */ //Outer_join_module *outer_join_dep;
void now_bound(Table_elimination *te, Module_dep **bound_modules);
}; };
...@@ -211,12 +215,11 @@ class Module_dep : public Sql_alloc ...@@ -211,12 +215,11 @@ class Module_dep : public Sql_alloc
{ {
public: public:
enum { enum {
MODULE_EXPRESSION,
MODULE_MULTI_EQUALITY,
MODULE_UNIQUE_KEY,
MODULE_OUTER_JOIN MODULE_OUTER_JOIN
} type; /* Type of the object */ } type; /* Type of the object */
virtual bool now_bound(Table_elimination *te, Value_dep **bound_modules)=0;
virtual ~Module_dep(){}
/* /*
Used to make a linked list of elements that became bound and thus can Used to make a linked list of elements that became bound and thus can
make elements that depend on them bound, too. make elements that depend on them bound, too.
...@@ -240,6 +243,7 @@ public: ...@@ -240,6 +243,7 @@ public:
/* Used during condition analysis only, similar to KEYUSE::level */ /* Used during condition analysis only, similar to KEYUSE::level */
uint level; uint level;
bool now_bound(Table_elimination *te, Value_dep **bound_values);
}; };
...@@ -254,17 +258,16 @@ public: ...@@ -254,17 +258,16 @@ public:
Key_module(Table_value *table_arg, uint keyno_arg, uint n_parts_arg) : Key_module(Table_value *table_arg, uint keyno_arg, uint n_parts_arg) :
table(table_arg), keyno(keyno_arg), next_table_key(NULL) table(table_arg), keyno(keyno_arg), next_table_key(NULL)
{ {
type= Module_dep::MODULE_UNIQUE_KEY;
unknown_args= n_parts_arg; unknown_args= n_parts_arg;
} }
Table_value *table; /* Table this key is from */ Table_value *table; /* Table this key is from */
uint keyno; uint keyno;
/* Unique keys form a linked list, ordered by keyno */ /* Unique keys form a linked list, ordered by keyno */
Key_module *next_table_key; Key_module *next_table_key;
bool now_bound(Table_elimination *te, Value_dep **bound_values);
}; };
/* /*
An outer join nest that is subject to elimination An outer join nest that is subject to elimination
- it depends on all tables inside it - it depends on all tables inside it
...@@ -273,18 +276,11 @@ public: ...@@ -273,18 +276,11 @@ public:
class Outer_join_module: public Module_dep class Outer_join_module: public Module_dep
{ {
public: public:
Outer_join_module(//TABLE_LIST *table_list_arg, Outer_join_module(uint n_children)
uint n_children)
// table_list(table_list_arg)
{ {
type= Module_dep::MODULE_OUTER_JOIN;
unknown_args= n_children; unknown_args= n_children;
} }
/* bool now_bound(Table_elimination *te, Value_dep **bound_values);
Outer join we're representing. This can be a join nest or one table that
is outer join'ed.
*/
// TABLE_LIST *table_list;
}; };
...@@ -307,6 +303,8 @@ public: ...@@ -307,6 +303,8 @@ public:
/* tablenr -> Table_value* mapping. */ /* tablenr -> Table_value* mapping. */
Table_value *table_deps[MAX_KEY]; Table_value *table_deps[MAX_KEY];
Outer_join_module *outer_join_dep;
/* Bitmap of how expressions depend on bits */ /* Bitmap of how expressions depend on bits */
MY_BITMAP expr_deps; MY_BITMAP expr_deps;
}; };
...@@ -319,8 +317,12 @@ eliminate_tables_for_list(Table_elimination *te, ...@@ -319,8 +317,12 @@ eliminate_tables_for_list(Table_elimination *te,
table_map tables_in_list, table_map tables_in_list,
Item *on_expr, Item *on_expr,
table_map tables_used_elsewhere); table_map tables_used_elsewhere);
static bool static
check_func_dependency(Table_elimination *te, table_map tables, Item* cond); bool check_func_dependency(Table_elimination *te,
table_map dep_tables,
List_iterator<TABLE_LIST> *it,
TABLE_LIST *oj_tbl,
Item* cond);
static static
bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps, bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps,
...@@ -332,16 +334,12 @@ bool add_eq_dep(Table_elimination *te, Equality_module **eq_dep, ...@@ -332,16 +334,12 @@ bool add_eq_dep(Table_elimination *te, Equality_module **eq_dep,
Item_func *cond, Item *left, Item *right, Item_func *cond, Item *left, Item *right,
table_map usable_tables); table_map usable_tables);
static static
Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fields, Equality_module *merge_func_deps(Equality_module *start,
Equality_module *end, uint and_level); Equality_module *new_fields,
Equality_module *end, uint and_level);
static Table_value *get_table_value(Table_elimination *te, TABLE *table); static Table_value *get_table_value(Table_elimination *te, TABLE *table);
static Field_value *get_field_value(Table_elimination *te, Field *field); static Field_value *get_field_value(Table_elimination *te, Field *field);
static Outer_join_module *get_outer_join_dep(Table_elimination *te,
//TABLE_LIST *outer_join,
table_map deps_map);
static
bool run_elimination_wave(Table_elimination *te, Module_dep *bound_modules);
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl); static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl);
...@@ -560,7 +558,7 @@ bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps, ...@@ -560,7 +558,7 @@ bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps,
static static
Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fields, Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fields,
Equality_module *end, uint and_level) Equality_module *end, uint and_level)
{ {
if (start == new_fields) if (start == new_fields)
return start; // Impossible or return start; // Impossible or
...@@ -684,7 +682,6 @@ bool add_eq_dep(Table_elimination *te, Equality_module **eq_mod, ...@@ -684,7 +682,6 @@ bool add_eq_dep(Table_elimination *te, Equality_module **eq_mod,
} }
} }
(*eq_mod)->type= Module_dep::MODULE_EXPRESSION; //psergey-todo;
if (!((*eq_mod)->field= get_field_value(te, field))) if (!((*eq_mod)->field= get_field_value(te, field)))
return TRUE; return TRUE;
(*eq_mod)->expression= right; (*eq_mod)->expression= right;
...@@ -763,6 +760,7 @@ static Field_value *get_field_value(Table_elimination *te, Field *field) ...@@ -763,6 +760,7 @@ static Field_value *get_field_value(Table_elimination *te, Field *field)
created before the parents. created before the parents.
*/ */
#if 0
static static
Outer_join_module *get_outer_join_dep(Table_elimination *te, Outer_join_module *get_outer_join_dep(Table_elimination *te,
// TABLE_LIST *outer_join, // TABLE_LIST *outer_join,
...@@ -806,6 +804,7 @@ Outer_join_module *get_outer_join_dep(Table_elimination *te, ...@@ -806,6 +804,7 @@ Outer_join_module *get_outer_join_dep(Table_elimination *te,
} }
return oj_dep; return oj_dep;
} }
#endif
/* /*
...@@ -879,7 +878,7 @@ bool setup_equality_deps(Table_elimination *te, Module_dep **bound_deps_list) ...@@ -879,7 +878,7 @@ bool setup_equality_deps(Table_elimination *te, Module_dep **bound_deps_list)
uint offset= 0; uint offset= 0;
for (Table_value **tbl_dep=te->table_deps; for (Table_value **tbl_dep=te->table_deps;
tbl_dep < te->table_deps + MAX_TABLES; tbl_dep < te->table_deps + MAX_TABLES;
tbl_dep++) // psergey-todo: TODO change to Table_map_iterator tbl_dep++) // psergey-todo: Wipe this out altogether
{ {
if (*tbl_dep) if (*tbl_dep)
{ {
...@@ -1090,7 +1089,8 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list, ...@@ -1090,7 +1089,8 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list,
{ {
/* This is "... LEFT JOIN tbl ON cond" */ /* This is "... LEFT JOIN tbl ON cond" */
if (!(tbl->table->map & outside_used_tables) && if (!(tbl->table->map & outside_used_tables) &&
check_func_dependency(te, tbl->table->map, tbl->on_expr)) check_func_dependency(te, tbl->table->map, NULL, tbl,
tbl->on_expr))
{ {
mark_as_eliminated(te->join, tbl); mark_as_eliminated(te->join, tbl);
} }
...@@ -1108,8 +1108,9 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list, ...@@ -1108,8 +1108,9 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list,
/* Try eliminating the nest we're called for */ /* Try eliminating the nest we're called for */
if (all_eliminated && on_expr && !(list_tables & tables_used_elsewhere)) if (all_eliminated && on_expr && !(list_tables & tables_used_elsewhere))
{ {
it.rewind();
return check_func_dependency(te, list_tables & ~te->join->eliminated_tables, return check_func_dependency(te, list_tables & ~te->join->eliminated_tables,
on_expr); &it, NULL, on_expr);
} }
return FALSE; /* not eliminated */ return FALSE; /* not eliminated */
} }
...@@ -1134,31 +1135,130 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list, ...@@ -1134,31 +1135,130 @@ eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list,
*/ */
static static
bool check_func_dependency(Table_elimination *te, table_map tables, Item* cond) bool check_func_dependency(Table_elimination *te,
table_map dep_tables,
List_iterator<TABLE_LIST> *it,
TABLE_LIST *oj_tbl,
Item* cond)
{ {
uint and_level=0; uint and_level=0;
Equality_module* eq_dep= te->equality_deps; Equality_module* eq_dep= te->equality_deps;
if (build_eq_deps_for_cond(te, &eq_dep, &and_level, cond, tables))
return TRUE;
te->n_equality_deps= eq_dep - te->equality_deps;
Module_dep *bound_modules; Module_dep *bound_modules;
if (!get_outer_join_dep(te, tables) &&
!setup_equality_deps(te, &bound_modules) && bzero(te->table_deps, sizeof(te->table_deps));
run_elimination_wave(te, bound_modules))
if (oj_tbl)
{ {
return TRUE; /* eliminated */ if (!get_table_value(te, oj_tbl->table))
return FALSE;
} }
return FALSE; else
{
TABLE_LIST *tbl;
while ((tbl= (*it)++))
{
if (tbl->table && (tbl->table->map & dep_tables))
{
if (!get_table_value(te, tbl->table))
return FALSE;
}
}
}
/* Extract equalities from the ON expression */
if (build_eq_deps_for_cond(te, &eq_dep, &and_level, cond, dep_tables) ||
eq_dep == te->equality_deps)
return FALSE;
te->n_equality_deps= eq_dep - te->equality_deps;
/* Create objects for running wave algorithm */
if (!(te->outer_join_dep= new Outer_join_module(my_count_bits(dep_tables))) ||
setup_equality_deps(te, &bound_modules))
{
return FALSE; /* OOM, default to non-dependent */
}
Value_dep *bound_values= NULL;
while (bound_modules)
{
for (;bound_modules; bound_modules= bound_modules->next)
{
if (bound_modules->now_bound(te, &bound_values))
return TRUE; /* Dependent! */
}
for (;bound_values; bound_values=bound_values->next)
bound_values->now_bound(te, &bound_modules);
}
return FALSE; /* Not dependent */
} }
static /*
void signal_from_field_to_exprs(Table_elimination* te, Field_value *field_dep, Table is known means that
Module_dep **bound_modules) - one more element in outer join nest is known
- all its fields are known
*/
void Table_value::now_bound(Table_elimination *te,
Module_dep **bound_modules)
{
DBUG_PRINT("info", ("table %s is now bound", table->alias));
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(te, bound_modules);
}
}
if (te->outer_join_dep->unknown_args &&
!--te->outer_join_dep->unknown_args)
{
/* Mark as bound and add to the list */
te->outer_join_dep->next= *bound_modules;
*bound_modules= te->outer_join_dep;
}
}
void Field_value::now_bound(Table_elimination *te,
Module_dep **bound_modules)
{
DBUG_PRINT("info", ("field %s.%s is now bound", field->table->alias,
field->field_name));
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(te, bound_modules);
}
/*
Walk through expressions that depend on this field and 'notify' them
that this field is no longer unknown.
*/
void Field_value::signal_from_field_to_exprs(Table_elimination* te,
Module_dep **bound_modules)
{ {
for (uint i=0; i < te->n_equality_deps; i++) for (uint i=0; i < te->n_equality_deps; i++)
{ {
if (bitmap_is_set(&te->expr_deps, field_dep->bitmap_offset + i) && if (bitmap_is_set(&te->expr_deps, bitmap_offset + i) &&
te->equality_deps[i].unknown_args && te->equality_deps[i].unknown_args &&
!--te->equality_deps[i].unknown_args) !--te->equality_deps[i].unknown_args)
{ {
...@@ -1171,131 +1271,37 @@ void signal_from_field_to_exprs(Table_elimination* te, Field_value *field_dep, ...@@ -1171,131 +1271,37 @@ void signal_from_field_to_exprs(Table_elimination* te, Field_value *field_dep,
} }
/* bool Outer_join_module::now_bound(Table_elimination *te,
Run the wave. Value_dep **bound_values)
All Func_dep-derived objects are divided into three classes: {
- Those that have bound=FALSE DBUG_PRINT("info", ("Outer join eliminated"));
- Those that have bound=TRUE return TRUE; /* Signal to finish the process */
- Those that have bound=TRUE and are in the list.. }
*/
static
bool run_elimination_wave(Table_elimination *te, Module_dep *bound_modules) bool Equality_module::now_bound(Table_elimination *te,
Value_dep **bound_values)
{ {
Value_dep *bound_values= NULL; /* For field=expr and we got to know the expr, so we know the field */
while (bound_modules) if (!field->bound)
{ {
for (;bound_modules; bound_modules= bound_modules->next) /* Mark as bound and add to the list */
{ field->bound= TRUE;
switch (bound_modules->type) field->next= *bound_values;
{ *bound_values= field;
case Module_dep::MODULE_EXPRESSION: }
{ return FALSE;
/* It's a field=expr and we got to know the expr, so we know the field */ }
Equality_module *eq_dep= (Equality_module*)bound_modules;
if (!eq_dep->field->bound)
{
/* Mark as bound and add to the list */
eq_dep->field->bound= TRUE;
eq_dep->field->next= bound_values;
bound_values= eq_dep->field;
}
break;
}
case Module_dep::MODULE_UNIQUE_KEY:
{
/* Unique key is known means the table is known */
Table_value *table_dep=((Key_module*)bound_modules)->table;
if (!table_dep->bound)
{
/* Mark as bound and add to the list */
table_dep->bound= TRUE;
table_dep->next= bound_values;
bound_values= table_dep;
}
break;
}
case Module_dep::MODULE_OUTER_JOIN:
{
DBUG_PRINT("info", ("Outer join eliminated"));
return TRUE;
break;
}
case Module_dep::MODULE_MULTI_EQUALITY:
default:
DBUG_ASSERT(0);
}
}
for (;bound_values; bound_values=bound_values->next)
{
switch (bound_values->type)
{
case Value_dep::VALUE_FIELD:
{
/*
Field became known. Check out
- unique keys we belong to
- expressions that depend on us.
*/
Field_value *field_dep= (Field_value*)bound_values;
DBUG_PRINT("info", ("field %s.%s is now bound",
field_dep->field->table->alias,
field_dep->field->field_name));
for (Key_module *key_dep= field_dep->table->keys; key_dep;
key_dep= key_dep->next_table_key)
{
if (field_dep->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(te, field_dep, &bound_modules);
break;
}
case Value_dep::VALUE_TABLE:
{
Table_value *table_dep=(Table_value*)bound_values;
DBUG_PRINT("info", ("table %s is now bound",
table_dep->table->alias));
/*
Table is known means that
- one more element in outer join nest is known
- all its fields are known
*/
Outer_join_module *outer_join_dep= table_dep->outer_join_dep;
if (outer_join_dep->unknown_args &&
!--outer_join_dep->unknown_args)
{
/* Mark as bound and add to the list */
outer_join_dep->next= bound_modules;
bound_modules= outer_join_dep;
break;
}
for (Field_value *field_dep= table_dep->fields; field_dep; /* Unique key is known means its table is known */
field_dep= field_dep->next_table_field) bool Key_module::now_bound(Table_elimination *te, Value_dep **bound_values)
{ {
if (!field_dep->bound) if (!table->bound)
{ {
/* Mark as bound and add to the list */ /* Mark as bound and add to the list */
field_dep->bound= TRUE; table->bound= TRUE;
signal_from_field_to_exprs(te, field_dep, &bound_modules); table->next= *bound_values;
} *bound_values= table;
}
break;
}
default:
DBUG_ASSERT(0);
}
}
} }
return FALSE; return FALSE;
} }
...@@ -1304,6 +1310,7 @@ bool run_elimination_wave(Table_elimination *te, Module_dep *bound_modules) ...@@ -1304,6 +1310,7 @@ bool run_elimination_wave(Table_elimination *te, Module_dep *bound_modules)
/* /*
Mark one table or the whole join nest as eliminated. Mark one table or the whole join nest as eliminated.
*/ */
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl) static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
{ {
TABLE *table; TABLE *table;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment