2009-06-25 12:05:53 +02:00
|
|
|
/**
|
|
|
|
@file
|
|
|
|
|
|
|
|
@brief
|
|
|
|
Table Elimination Module
|
|
|
|
|
|
|
|
@defgroup Table_Elimination Table Elimination Module
|
|
|
|
@{
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_PRAGMA_IMPLEMENTATION
|
|
|
|
#pragma implementation // gcc: Class implementation
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
2009-08-13 00:34:21 +02:00
|
|
|
#include "my_bit.h"
|
2009-06-25 12:05:53 +02:00
|
|
|
#include "sql_select.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
OVERVIEW
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-08-16 16:35:47 +02:00
|
|
|
This file contains table elimination module. The idea behind table
|
|
|
|
elimination is as follows: suppose we have a left join
|
|
|
|
|
|
|
|
SELECT * FROM t1 LEFT JOIN
|
|
|
|
(t2 JOIN t3) ON t3.primary_key=t1.col AND
|
|
|
|
t4.primary_key=t2.col
|
|
|
|
such that
|
|
|
|
* 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),
|
|
|
|
* inner side of the outer join is guaranteed to produce at most one matching
|
|
|
|
record combination for each record combination of outer tables.
|
|
|
|
|
|
|
|
then the inner side of the outer join can be removed from the query, as it
|
|
|
|
will always produce only one record combination (either real or
|
|
|
|
null-complemented one) and we don't care about what that record combination
|
|
|
|
is.
|
|
|
|
|
|
|
|
MODULE INTERFACE
|
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
The module has one entry point - eliminate_tables() function, which one
|
2009-08-13 21:10:53 +02:00
|
|
|
needs to call (once) at some point before the join optimization.
|
2009-06-25 22:07:29 +02:00
|
|
|
eliminate_tables() operates over the JOIN structures. Logically, it
|
|
|
|
removes the right sides of outer join nests. Physically, it changes the
|
|
|
|
following members:
|
|
|
|
|
|
|
|
* Eliminated tables are marked as constant and moved to the front of the
|
|
|
|
join order.
|
|
|
|
|
2009-08-13 21:10:53 +02:00
|
|
|
* In addition to this, they are recorded in JOIN::eliminated_tables bitmap.
|
2009-06-25 22:07:29 +02:00
|
|
|
|
|
|
|
* Items that became disused because they were in the ON expression of an
|
|
|
|
eliminated outer join are notified by means of the Item tree walk which
|
|
|
|
calls Item::mark_as_eliminated_processor for every item
|
2009-08-13 00:34:21 +02:00
|
|
|
- At the moment the only Item that cares whether it was eliminated is
|
|
|
|
Item_subselect with its Item_subselect::eliminated flag which is used
|
|
|
|
by EXPLAIN code to check if the subquery should be shown in EXPLAIN.
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-08-13 21:10:53 +02:00
|
|
|
Table elimination is redone on every PS re-execution.
|
2009-08-16 16:35:47 +02:00
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
TABLE ELIMINATION ALGORITHM FOR ONE OUTER JOIN
|
2009-08-16 16:35:47 +02:00
|
|
|
|
|
|
|
As said above, we can remove inner side of an outer join if it is
|
|
|
|
|
|
|
|
1. not referred to from any other parts of the query
|
|
|
|
2. always produces one matching record combination.
|
|
|
|
|
|
|
|
We check #1 by doing a recursive descent down the join->join_list while
|
|
|
|
maintaining a union of used_tables() attribute of all expressions we've seen
|
2009-08-16 20:01:59 +02:00
|
|
|
"elsewhere". When we encounter an outer join, we check if the bitmap of
|
|
|
|
tables on its inner side has an intersection with tables that are used
|
|
|
|
elsewhere. No intersection means that inner side of the outer join could
|
2009-08-16 16:35:47 +02:00
|
|
|
potentially be eliminated.
|
|
|
|
|
2009-08-16 20:01:59 +02:00
|
|
|
In order to check #2, one needs to prove that inner side of an outer join
|
|
|
|
is functionally dependent on the outside. We prove dependency by proving
|
|
|
|
functional dependency of intermediate objects:
|
|
|
|
|
|
|
|
- Inner side of outer join is functionally dependent when each of its tables
|
|
|
|
are functionally dependent. (We assume a table is functionally dependent
|
|
|
|
when its dependencies allow to uniquely identify one table record, or no
|
|
|
|
records).
|
|
|
|
|
|
|
|
- Table is functionally dependent when it has got a unique key whose columns
|
|
|
|
are functionally dependent.
|
|
|
|
|
|
|
|
- A column is functionally dependent when we could locate an AND-part of a
|
|
|
|
certain ON clause in form
|
|
|
|
|
|
|
|
tblX.columnY= expr
|
|
|
|
|
|
|
|
where expr is functionally-depdendent.
|
|
|
|
|
|
|
|
Apparently the above rules can be applied recursively. Also, certain entities
|
|
|
|
depend on multiple other entities. We model this by a bipartite graph which
|
|
|
|
has two kinds of nodes:
|
|
|
|
|
|
|
|
Value nodes:
|
|
|
|
- Table column values (each is a value of tblX.columnY)
|
|
|
|
- Table nodes (each node represents a table inside an eliminable join nest).
|
|
|
|
each value is either bound (i.e. functionally dependent) or not.
|
|
|
|
|
|
|
|
Module nodes:
|
2009-08-18 23:18:10 +02:00
|
|
|
- Modules representing tblX.colY=expr equalities. Equality module has
|
2009-08-16 20:01:59 +02:00
|
|
|
= incoming edges from columns used in expr
|
|
|
|
= outgoing edge to tblX.colY column.
|
|
|
|
- Nodes representing unique keys. Unique key has
|
2009-08-18 23:18:10 +02:00
|
|
|
= incoming edges from key component value modules
|
|
|
|
= outgoing edge to key's table module
|
|
|
|
- Inner side of outer join module. Outer join module has
|
|
|
|
= incoming edges from table value modules
|
2009-08-16 20:01:59 +02:00
|
|
|
= No outgoing edges. Once we reach it, we know we can eliminate the
|
|
|
|
outer join.
|
|
|
|
A module may depend on multiple values, and hence its primary attribute is
|
|
|
|
the number of its depedencies that are not bound.
|
|
|
|
|
|
|
|
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
|
2009-08-18 23:18:10 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
HANDLING MULTIPLE NESTED OUTER JOINS
|
|
|
|
(TODO : explanations why 'local bottom up is sufficient')
|
|
|
|
|
2009-08-16 16:35:47 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
class Value_dep;
|
|
|
|
class Field_value;
|
|
|
|
class Table_value;
|
|
|
|
|
|
|
|
|
|
|
|
class Module_dep;
|
|
|
|
class Equality_module;
|
|
|
|
class Outer_join_module;
|
|
|
|
class Key_module;
|
|
|
|
|
|
|
|
class Table_elimination;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-17 17:02:29 +02:00
|
|
|
A value, something that can be bound or not bound. Also, values can be linked
|
|
|
|
in a list.
|
2009-06-25 12:05:53 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-16 09:25:24 +02:00
|
|
|
class Value_dep : public Sql_alloc
|
2009-08-15 08:08:03 +02:00
|
|
|
{
|
|
|
|
public:
|
2009-08-18 23:18:10 +02:00
|
|
|
Value_dep(): bound(FALSE), next(NULL) {}
|
|
|
|
virtual void now_bound(Table_elimination *te, Module_dep **bound_modules)=0;
|
|
|
|
virtual ~Value_dep() {} /* only to shut up compiler warnings */
|
2009-08-15 08:08:03 +02:00
|
|
|
|
|
|
|
bool bound;
|
|
|
|
Value_dep *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-16 16:35:47 +02:00
|
|
|
A table field value. There is exactly only one such object for any tblX.fieldY
|
2009-08-15 08:08:03 +02:00
|
|
|
- the field epends on its table and equalities
|
|
|
|
- expressions that use the field are its dependencies
|
|
|
|
*/
|
|
|
|
class Field_value : public Value_dep
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Field_value(Table_value *table_arg, Field *field_arg) :
|
|
|
|
table(table_arg), field(field_arg)
|
2009-08-18 23:18:10 +02:00
|
|
|
{}
|
2009-08-15 08:08:03 +02:00
|
|
|
|
|
|
|
Table_value *table; /* Table this field is from */
|
|
|
|
Field *field;
|
|
|
|
|
|
|
|
/*
|
2009-08-18 23:18:10 +02:00
|
|
|
Field_deps that belong to one table form a linked list, ordered by
|
|
|
|
field_index
|
2009-08-15 08:08:03 +02:00
|
|
|
*/
|
|
|
|
Field_value *next_table_field;
|
|
|
|
uint bitmap_offset; /* Offset of our part of the bitmap */
|
2009-08-18 23:18:10 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
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);
|
2009-08-15 08:08:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2009-08-16 16:35:47 +02:00
|
|
|
A table value. There is one Table_value object for every table that can
|
|
|
|
potentially be eliminated.
|
2009-08-15 08:08:03 +02:00
|
|
|
- table depends on any of its unique keys
|
|
|
|
- has its fields and embedding outer join as dependency.
|
|
|
|
*/
|
|
|
|
class Table_value : public Value_dep
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Table_value(TABLE *table_arg) :
|
2009-08-18 23:18:10 +02:00
|
|
|
table(table_arg), fields(NULL), keys(NULL)
|
|
|
|
{}
|
2009-08-15 08:08:03 +02:00
|
|
|
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 */
|
2009-08-18 23:18:10 +02:00
|
|
|
//Outer_join_module *outer_join_dep;
|
|
|
|
void now_bound(Table_elimination *te, Module_dep **bound_modules);
|
2009-08-15 08:08:03 +02:00
|
|
|
};
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-13 21:10:53 +02:00
|
|
|
/*
|
2009-08-17 17:02:29 +02:00
|
|
|
A 'module'. Module has dependencies
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
class Module_dep : public Sql_alloc
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
2009-08-15 12:29:53 +02:00
|
|
|
MODULE_OUTER_JOIN
|
2009-08-13 21:10:53 +02:00
|
|
|
} type; /* Type of the object */
|
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
virtual bool now_bound(Table_elimination *te, Value_dep **bound_modules)=0;
|
|
|
|
virtual ~Module_dep(){}
|
2009-08-13 21:10:53 +02:00
|
|
|
/*
|
|
|
|
Used to make a linked list of elements that became bound and thus can
|
|
|
|
make elements that depend on them bound, too.
|
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
Module_dep *next;
|
2009-08-16 11:15:49 +02:00
|
|
|
uint unknown_args;
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
Module_dep() : next(NULL), unknown_args(0) {}
|
|
|
|
};
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-13 21:10:53 +02:00
|
|
|
A "tbl.column= expr" equality dependency. tbl.column depends on fields
|
|
|
|
used in expr.
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
class Equality_module : public Module_dep
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
public:
|
2009-08-15 08:08:03 +02:00
|
|
|
Field_value *field;
|
2009-08-15 17:39:12 +02:00
|
|
|
Item *expression;
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-13 21:10:53 +02:00
|
|
|
/* Used during condition analysis only, similar to KEYUSE::level */
|
|
|
|
uint level;
|
2009-08-18 23:18:10 +02:00
|
|
|
bool now_bound(Table_elimination *te, Value_dep **bound_values);
|
2009-08-13 00:34:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-13 22:44:52 +02:00
|
|
|
A Unique key.
|
|
|
|
- Unique key depends on all of its components
|
|
|
|
- Key's table is its dependency
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
class Key_module: public Module_dep
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
public:
|
2009-08-15 08:08:03 +02:00
|
|
|
Key_module(Table_value *table_arg, uint keyno_arg, uint n_parts_arg) :
|
|
|
|
table(table_arg), keyno(keyno_arg), next_table_key(NULL)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
unknown_args= n_parts_arg;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-08-15 08:08:03 +02:00
|
|
|
Table_value *table; /* Table this key is from */
|
2009-08-13 21:10:53 +02:00
|
|
|
uint keyno;
|
2009-08-13 22:44:52 +02:00
|
|
|
/* Unique keys form a linked list, ordered by keyno */
|
2009-08-15 08:08:03 +02:00
|
|
|
Key_module *next_table_key;
|
2009-08-18 23:18:10 +02:00
|
|
|
bool now_bound(Table_elimination *te, Value_dep **bound_values);
|
2009-08-13 00:34:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-13 22:44:52 +02:00
|
|
|
An outer join nest that is subject to elimination
|
|
|
|
- it depends on all tables inside it
|
|
|
|
- has its parent outer join as dependency
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
class Outer_join_module: public Module_dep
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
public:
|
2009-08-18 23:18:10 +02:00
|
|
|
Outer_join_module(uint n_children)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
unknown_args= n_children;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-08-18 23:18:10 +02:00
|
|
|
bool now_bound(Table_elimination *te, Value_dep **bound_values);
|
2009-08-13 00:34:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
|
|
|
Table elimination context
|
|
|
|
*/
|
2009-08-13 00:34:21 +02:00
|
|
|
class Table_elimination
|
|
|
|
{
|
|
|
|
public:
|
2009-08-18 17:01:51 +02:00
|
|
|
Table_elimination(JOIN *join_arg) : join(join_arg)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
bzero(table_deps, sizeof(table_deps));
|
|
|
|
}
|
|
|
|
|
|
|
|
JOIN *join;
|
|
|
|
/* Array of equality dependencies */
|
2009-08-15 08:08:03 +02:00
|
|
|
Equality_module *equality_deps;
|
2009-08-13 00:34:21 +02:00
|
|
|
uint n_equality_deps; /* Number of elements in the array */
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
/* tablenr -> Table_value* mapping. */
|
|
|
|
Table_value *table_deps[MAX_KEY];
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
Outer_join_module *outer_join_dep;
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
/* Bitmap of how expressions depend on bits */
|
|
|
|
MY_BITMAP expr_deps;
|
|
|
|
};
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
void eliminate_tables(JOIN *join);
|
|
|
|
|
|
|
|
static bool
|
|
|
|
eliminate_tables_for_list(Table_elimination *te,
|
|
|
|
List<TABLE_LIST> *join_list,
|
|
|
|
table_map tables_in_list,
|
|
|
|
Item *on_expr,
|
|
|
|
table_map tables_used_elsewhere);
|
2009-08-18 23:18:10 +02:00
|
|
|
static
|
|
|
|
bool check_func_dependency(Table_elimination *te,
|
|
|
|
table_map dep_tables,
|
|
|
|
List_iterator<TABLE_LIST> *it,
|
|
|
|
TABLE_LIST *oj_tbl,
|
|
|
|
Item* cond);
|
2009-08-16 16:35:47 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
static
|
2009-08-16 14:43:31 +02:00
|
|
|
bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps,
|
2009-08-13 22:44:52 +02:00
|
|
|
uint *and_level, Item *cond,
|
|
|
|
table_map usable_tables);
|
2009-08-13 00:34:21 +02:00
|
|
|
static
|
2009-08-16 14:43:31 +02:00
|
|
|
bool add_eq_dep(Table_elimination *te, Equality_module **eq_dep,
|
2009-08-16 14:17:08 +02:00
|
|
|
uint and_level,
|
|
|
|
Item_func *cond, Item *left, Item *right,
|
|
|
|
table_map usable_tables);
|
2009-08-13 00:34:21 +02:00
|
|
|
static
|
2009-08-18 23:18:10 +02:00
|
|
|
Equality_module *merge_func_deps(Equality_module *start,
|
|
|
|
Equality_module *new_fields,
|
|
|
|
Equality_module *end, uint and_level);
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
static Table_value *get_table_value(Table_elimination *te, TABLE *table);
|
|
|
|
static Field_value *get_field_value(Table_elimination *te, Field *field);
|
2009-06-29 15:51:15 +02:00
|
|
|
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl);
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
static void dbug_print_deps(Table_elimination *te);
|
|
|
|
#endif
|
2009-08-16 16:35:47 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
/*******************************************************************************************/
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
/*
|
2009-08-13 22:44:52 +02:00
|
|
|
Produce Eq_dep elements for given condition.
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2009-08-13 22:44:52 +02:00
|
|
|
build_eq_deps_for_cond()
|
|
|
|
te Table elimination context
|
|
|
|
fdeps INOUT Put produced equality conditions here
|
|
|
|
and_level INOUT AND-level (like in add_key_fields)
|
|
|
|
cond Condition to process
|
|
|
|
usable_tables Tables which fields we're interested in. That is,
|
2009-08-18 15:03:58 +02:00
|
|
|
Equality_module represent "tbl.col=expr" and we'll
|
2009-08-13 22:44:52 +02:00
|
|
|
produce them only if tbl is in usable_tables.
|
2009-06-25 12:05:53 +02:00
|
|
|
DESCRIPTION
|
2009-08-13 22:44:52 +02:00
|
|
|
This function is modeled after add_key_fields()
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
static
|
2009-08-16 14:43:31 +02:00
|
|
|
bool build_eq_deps_for_cond(Table_elimination *te, Equality_module **fdeps,
|
2009-08-13 22:44:52 +02:00
|
|
|
uint *and_level, Item *cond,
|
|
|
|
table_map usable_tables)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
if (cond->type() == Item_func::COND_ITEM)
|
|
|
|
{
|
|
|
|
List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list());
|
2009-08-15 08:08:03 +02:00
|
|
|
Equality_module *org_key_fields= *fdeps;
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
/* AND/OR */
|
|
|
|
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
|
|
|
|
{
|
|
|
|
Item *item;
|
|
|
|
while ((item=li++))
|
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
if (build_eq_deps_for_cond(te, fdeps, and_level, item, usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
for (; org_key_fields != *fdeps ; org_key_fields++)
|
|
|
|
org_key_fields->level= *and_level;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(*and_level)++;
|
2009-08-16 14:43:31 +02:00
|
|
|
if (build_eq_deps_for_cond(te, fdeps, and_level, li++, usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
Item *item;
|
|
|
|
while ((item=li++))
|
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Equality_module *start_key_fields= *fdeps;
|
2009-08-13 00:34:21 +02:00
|
|
|
(*and_level)++;
|
2009-08-16 14:43:31 +02:00
|
|
|
if (build_eq_deps_for_cond(te, fdeps, and_level, item, usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
*fdeps= merge_func_deps(org_key_fields, start_key_fields, *fdeps,
|
|
|
|
++(*and_level));
|
|
|
|
}
|
|
|
|
}
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
if (cond->type() != Item::FUNC_ITEM)
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-16 14:17:08 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
Item_func *cond_func= (Item_func*) cond;
|
2009-08-16 14:17:08 +02:00
|
|
|
Item **args= cond_func->arguments();
|
|
|
|
|
|
|
|
switch (cond_func->functype()) {
|
|
|
|
case Item_func::IN_FUNC:
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
if (cond_func->argument_count() == 2)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
if (add_eq_dep(te, fdeps, *and_level, cond_func, args[0], args[1],
|
|
|
|
usable_tables) ||
|
|
|
|
add_eq_dep(te, fdeps, *and_level, cond_func, args[1], args[0],
|
|
|
|
usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-08-16 14:17:08 +02:00
|
|
|
}
|
|
|
|
case Item_func::BETWEEN:
|
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
Item *fld;
|
2009-08-16 14:17:08 +02:00
|
|
|
if (!((Item_func_between*)cond)->negated &&
|
2009-08-16 14:43:31 +02:00
|
|
|
(fld= args[0]->real_item())->type() == Item::FIELD_ITEM &&
|
2009-08-16 14:17:08 +02:00
|
|
|
args[1]->eq(args[2], ((Item_field*)fld)->field->binary()))
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
if (add_eq_dep(te, fdeps, *and_level, cond_func, args[0], args[1],
|
|
|
|
usable_tables) ||
|
|
|
|
add_eq_dep(te, fdeps, *and_level, cond_func, args[1], args[0],
|
|
|
|
usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-08-16 14:17:08 +02:00
|
|
|
case Item_func::EQ_FUNC:
|
|
|
|
case Item_func::EQUAL_FUNC:
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
add_eq_dep(te, fdeps, *and_level, cond_func, args[0], args[1],
|
|
|
|
usable_tables);
|
|
|
|
add_eq_dep(te, fdeps, *and_level, cond_func, args[1], args[0],
|
|
|
|
usable_tables);
|
2009-08-13 00:34:21 +02:00
|
|
|
break;
|
|
|
|
}
|
2009-08-16 14:17:08 +02:00
|
|
|
case Item_func::ISNULL_FUNC:
|
|
|
|
{
|
|
|
|
Item *tmp=new Item_null;
|
2009-08-16 14:43:31 +02:00
|
|
|
if (!tmp || add_eq_dep(te, fdeps, *and_level, cond_func, args[0], args[1],
|
|
|
|
usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
break;
|
2009-08-16 14:17:08 +02:00
|
|
|
}
|
|
|
|
case Item_func::MULT_EQUAL_FUNC:
|
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
Item_equal *item_equal= (Item_equal *) cond;
|
|
|
|
Item *const_item= item_equal->get_const();
|
|
|
|
Item_equal_iterator it(*item_equal);
|
|
|
|
Item_field *item;
|
|
|
|
if (const_item)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
For each field field1 from item_equal consider the equality
|
|
|
|
field1=const_item as a condition allowing an index access of the table
|
|
|
|
with field1 by the keys value of field1.
|
|
|
|
*/
|
|
|
|
while ((item= it++))
|
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
if (add_eq_dep(te, fdeps, *and_level, cond_func, item, const_item,
|
|
|
|
usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Consider all pairs of different fields included into item_equal.
|
|
|
|
For each of them (field1, field1) consider the equality
|
|
|
|
field1=field2 as a condition allowing an index access of the table
|
|
|
|
with field1 by the keys value of field2.
|
|
|
|
*/
|
|
|
|
Item_equal_iterator fi(*item_equal);
|
|
|
|
while ((item= fi++))
|
|
|
|
{
|
|
|
|
Field *field= item->field;
|
2009-08-16 14:17:08 +02:00
|
|
|
Item_field *item2;
|
|
|
|
while ((item2= it++))
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
if (!field->eq(item2->field))
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:43:31 +02:00
|
|
|
if (add_eq_dep(te, fdeps, *and_level, cond_func, item, item2,
|
|
|
|
usable_tables))
|
|
|
|
return TRUE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
it.rewind();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-08-16 14:17:08 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
/*
|
2009-08-15 08:08:03 +02:00
|
|
|
Perform an OR operation on two (adjacent) Equality_module arrays.
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
merge_func_deps()
|
2009-08-13 22:44:52 +02:00
|
|
|
start Start of left OR-part
|
|
|
|
new_fields Start of right OR-part
|
|
|
|
end End of right OR-part
|
|
|
|
and_level AND-level.
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-15 08:08:03 +02:00
|
|
|
This function is invoked for two adjacent arrays of Equality_module elements:
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
$LEFT_PART $RIGHT_PART
|
|
|
|
+-----------------------+-----------------------+
|
|
|
|
start new_fields end
|
|
|
|
|
|
|
|
The goal is to produce an array which would correspnd to the combined
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
$LEFT_PART OR $RIGHT_PART
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
condition. This is achieved as follows: First, we apply distrubutive law:
|
|
|
|
|
|
|
|
(fdep_A_1 AND fdep_A_2 AND ...) OR (fdep_B_1 AND fdep_B_2 AND ...) =
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
= AND_ij (fdep_A_[i] OR fdep_B_[j])
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
Then we walk over the obtained "fdep_A_[i] OR fdep_B_[j]" pairs, and
|
|
|
|
- Discard those that that have left and right part referring to different
|
|
|
|
columns. We can't infer anything useful from "col1=expr1 OR col2=expr2".
|
|
|
|
- When left and right parts refer to the same column, we check if they are
|
|
|
|
essentially the same.
|
|
|
|
= If they are the same, we keep one copy
|
|
|
|
"t.col=expr OR t.col=expr" -> "t.col=expr
|
|
|
|
= if they are different , then we discard both
|
|
|
|
"t.col=expr1 OR t.col=expr2" -> (nothing useful)
|
|
|
|
|
|
|
|
(no per-table or for-index FUNC_DEPS exist yet at this phase).
|
|
|
|
|
|
|
|
See also merge_key_fields().
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
End of the result array
|
|
|
|
*/
|
|
|
|
|
|
|
|
static
|
2009-08-15 08:08:03 +02:00
|
|
|
Equality_module *merge_func_deps(Equality_module *start, Equality_module *new_fields,
|
2009-08-18 23:18:10 +02:00
|
|
|
Equality_module *end, uint and_level)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
if (start == new_fields)
|
|
|
|
return start; // Impossible or
|
|
|
|
if (new_fields == end)
|
|
|
|
return start; // No new fields, skip all
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
Equality_module *first_free=new_fields;
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
for (; new_fields != end ; new_fields++)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Equality_module *old=start ; old != first_free ; old++)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
TODO: does it make sense to attempt to merging multiple-equalities?
|
|
|
|
A: YES.
|
|
|
|
(a=b=c) OR (a=b=d) produce "a=b".
|
|
|
|
QQ:
|
|
|
|
What to use for merging? Trivial N*M algorithm or pre-sort and then
|
|
|
|
merge ordered sequences?
|
|
|
|
*/
|
|
|
|
if (old->field == new_fields->field)
|
|
|
|
{
|
2009-08-15 17:39:12 +02:00
|
|
|
if (!new_fields->expression->const_item())
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the value matches, we can use the key reference.
|
|
|
|
If not, we keep it until we have examined all new values
|
|
|
|
*/
|
2009-08-15 17:39:12 +02:00
|
|
|
if (old->expression->eq(new_fields->expression, old->field->field->binary()))
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
old->level= and_level;
|
|
|
|
}
|
|
|
|
}
|
2009-08-15 17:39:12 +02:00
|
|
|
else if (old->expression->eq_by_collation(new_fields->expression,
|
2009-08-13 00:34:21 +02:00
|
|
|
old->field->field->binary(),
|
|
|
|
old->field->field->charset()))
|
|
|
|
{
|
|
|
|
old->level= and_level;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* The expressions are different. */
|
|
|
|
if (old == --first_free) // If last item
|
|
|
|
break;
|
|
|
|
*old= *first_free; // Remove old value
|
|
|
|
old--; // Retry this value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Ok, the results are within the [start, first_free) range, and the useful
|
2009-08-18 17:01:51 +02:00
|
|
|
elements have level==and_level. Now, remove all unusable elements:
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Equality_module *old=start ; old != first_free ;)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
if (old->level != and_level)
|
|
|
|
{ // Not used in all levels
|
|
|
|
if (old == --first_free)
|
|
|
|
break;
|
|
|
|
*old= *first_free; // Remove old value
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
old++;
|
|
|
|
}
|
|
|
|
return first_free;
|
|
|
|
}
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
/*
|
2009-08-18 17:01:51 +02:00
|
|
|
Add an Equality_module element for left=right condition
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_eq_dep()
|
|
|
|
te Table elimination context
|
|
|
|
eq_mod INOUT Store created Equality_module here and increment ptr if
|
|
|
|
you do so
|
|
|
|
and_level AND-level ()
|
|
|
|
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
|
|
|
|
belongs to this set.
|
2009-08-13 22:44:52 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-18 17:01:51 +02:00
|
|
|
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
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
static
|
2009-08-18 17:01:51 +02:00
|
|
|
bool add_eq_dep(Table_elimination *te, Equality_module **eq_mod,
|
2009-08-16 16:35:47 +02:00
|
|
|
uint and_level, Item_func *cond, Item *left, Item *right,
|
|
|
|
table_map usable_tables)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
if ((left->used_tables() & usable_tables) &&
|
|
|
|
!(right->used_tables() & RAND_TABLE_BIT) &&
|
|
|
|
left->real_item()->type() == Item::FIELD_ITEM)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
Field *field= ((Item_field*)left->real_item())->field;
|
|
|
|
if (field->result_type() == STRING_RESULT)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-16 14:17:08 +02:00
|
|
|
if (right->result_type() != STRING_RESULT)
|
|
|
|
{
|
|
|
|
if (field->cmp_type() != right->result_type())
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-16 14:17:08 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2009-08-18 17:01:51 +02:00
|
|
|
We can't assume there's a functional dependency if the effective
|
|
|
|
collation of the operation differ from the field collation.
|
2009-08-16 14:17:08 +02:00
|
|
|
*/
|
|
|
|
if (field->cmp_type() == STRING_RESULT &&
|
|
|
|
((Field_str*)field)->charset() != cond->compare_collation())
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-16 14:17:08 +02:00
|
|
|
}
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
if (!((*eq_mod)->field= get_field_value(te, field)))
|
2009-08-16 14:43:31 +02:00
|
|
|
return TRUE;
|
2009-08-18 17:01:51 +02:00
|
|
|
(*eq_mod)->expression= right;
|
|
|
|
(*eq_mod)->level= and_level;
|
|
|
|
(*eq_mod)++;
|
2009-08-16 14:17:08 +02:00
|
|
|
}
|
2009-08-16 14:43:31 +02:00
|
|
|
return FALSE;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
|
2009-08-15 12:29:53 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
2009-08-15 08:08:03 +02:00
|
|
|
Get a Table_value object for the given table, creating it if necessary.
|
2009-08-13 22:44:52 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
static Table_value *get_table_value(Table_elimination *te, TABLE *table)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-15 12:29:53 +02:00
|
|
|
Table_value *tbl_dep;
|
|
|
|
if (!(tbl_dep= new Table_value(table)))
|
|
|
|
return NULL;
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-15 12:29:53 +02:00
|
|
|
Key_module **key_list= &(tbl_dep->keys);
|
2009-08-13 00:34:21 +02:00
|
|
|
/* 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)
|
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Key_module *key_dep= new Key_module(tbl_dep, i, key->key_parts);
|
2009-08-13 00:34:21 +02:00
|
|
|
*key_list= key_dep;
|
|
|
|
key_list= &(key_dep->next_table_key);
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
}
|
2009-08-15 12:29:53 +02:00
|
|
|
return te->table_deps[table->tablenr]= tbl_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
/*
|
2009-08-15 08:08:03 +02:00
|
|
|
Get a Field_value object for the given field, creating it if necessary
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
static Field_value *get_field_value(Table_elimination *te, Field *field)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
TABLE *table= field->table;
|
2009-08-15 08:08:03 +02:00
|
|
|
Table_value *tbl_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/* First, get the table*/
|
2009-08-13 00:34:21 +02:00
|
|
|
if (!(tbl_dep= te->table_deps[table->tablenr]))
|
2009-08-15 12:29:53 +02:00
|
|
|
{
|
|
|
|
if (!(tbl_dep= get_table_value(te, table)))
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-08-13 22:44:52 +02:00
|
|
|
|
|
|
|
/* Try finding the field in field list */
|
2009-08-15 08:08:03 +02:00
|
|
|
Field_value **pfield= &(tbl_dep->fields);
|
2009-08-13 00:34:21 +02:00
|
|
|
while (*pfield && (*pfield)->field->field_index < field->field_index)
|
|
|
|
{
|
|
|
|
pfield= &((*pfield)->next_table_field);
|
|
|
|
}
|
|
|
|
if (*pfield && (*pfield)->field->field_index == field->field_index)
|
|
|
|
return *pfield;
|
2009-06-29 15:51:15 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/* Create the field and insert it in the list */
|
2009-08-15 08:08:03 +02:00
|
|
|
Field_value *new_field= new Field_value(tbl_dep, field);
|
2009-08-13 00:34:21 +02:00
|
|
|
new_field->next_table_field= *pfield;
|
|
|
|
*pfield= new_field;
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
return new_field;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
2009-08-15 08:08:03 +02:00
|
|
|
Create an Outer_join_module object for the given outer join
|
2009-08-13 22:44:52 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-15 08:08:03 +02:00
|
|
|
Outer_join_module objects for children (or further descendants) are always
|
2009-08-13 22:44:52 +02:00
|
|
|
created before the parents.
|
|
|
|
*/
|
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
#if 0
|
2009-08-13 22:44:52 +02:00
|
|
|
static
|
2009-08-15 08:08:03 +02:00
|
|
|
Outer_join_module *get_outer_join_dep(Table_elimination *te,
|
2009-08-18 15:03:58 +02:00
|
|
|
// TABLE_LIST *outer_join,
|
2009-08-15 12:29:53 +02:00
|
|
|
table_map deps_map)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Outer_join_module *oj_dep;
|
2009-08-18 15:03:58 +02:00
|
|
|
if (!(oj_dep= new Outer_join_module(/*outer_join, */my_count_bits(deps_map))))
|
2009-08-16 16:35:47 +02:00
|
|
|
return NULL;
|
2009-08-13 22:44:52 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Collect a bitmap fo tables that we depend on, and also set parent pointer
|
|
|
|
for descendant outer join elements.
|
|
|
|
*/
|
2009-08-13 00:34:21 +02:00
|
|
|
Table_map_iterator it(deps_map);
|
|
|
|
int idx;
|
|
|
|
while ((idx= it.next_bit()) != Table_map_iterator::BITMAP_END)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Table_value *table_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
if (!(table_dep= te->table_deps[idx]))
|
|
|
|
{
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
|
|
|
We get here only when ON expression had no references to inner tables
|
|
|
|
and Table_map objects weren't created for them. This is a rare/
|
|
|
|
unimportant case so it's ok to do not too efficient searches.
|
|
|
|
*/
|
2009-08-13 00:34:21 +02:00
|
|
|
TABLE *table= NULL;
|
2009-08-13 11:36:13 +02:00
|
|
|
for (TABLE_LIST *tlist= te->join->select_lex->leaf_tables; tlist;
|
|
|
|
tlist=tlist->next_leaf)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-13 11:36:13 +02:00
|
|
|
if (tlist->table->tablenr == (uint)idx)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-13 11:36:13 +02:00
|
|
|
table=tlist->table;
|
2009-08-13 00:34:21 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_ASSERT(table);
|
2009-08-15 12:29:53 +02:00
|
|
|
if (!(table_dep= get_table_value(te, table)))
|
|
|
|
return NULL;
|
2009-08-13 00:34:21 +02:00
|
|
|
}
|
2009-08-18 17:01:51 +02:00
|
|
|
table_dep->outer_join_dep= oj_dep;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-08-13 00:34:21 +02:00
|
|
|
return oj_dep;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-08-18 23:18:10 +02:00
|
|
|
#endif
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
2009-08-16 11:15:49 +02:00
|
|
|
This is used to analyze expressions in "tbl.col=expr" dependencies so
|
2009-08-13 22:44:52 +02:00
|
|
|
that we can figure out which fields the expression depends on.
|
2009-06-25 12:05:53 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
class Field_dependency_recorder : public Field_enumerator
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
public:
|
2009-08-18 17:01:51 +02:00
|
|
|
Field_dependency_recorder(Table_elimination *te_arg): te(te_arg)
|
2009-08-13 00:34:21 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
void see_field(Field *field)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Table_value *tbl_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
if ((tbl_dep= te->table_deps[field->table->tablenr]))
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Field_value *field_dep= tbl_dep->fields; field_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
field_dep= field_dep->next_table_field)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
if (field->field_index == field_dep->field->field_index)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
uint offs= field_dep->bitmap_offset + expr_offset;
|
|
|
|
if (!bitmap_is_set(&te->expr_deps, offs))
|
|
|
|
te->equality_deps[expr_offset].unknown_args++;
|
|
|
|
bitmap_set_bit(&te->expr_deps, offs);
|
|
|
|
return;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
}
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2009-08-13 00:34:21 +02:00
|
|
|
te->equality_deps[expr_offset].unknown_args++;
|
|
|
|
}
|
|
|
|
}
|
2009-08-13 22:44:52 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
Table_elimination *te;
|
2009-08-13 22:44:52 +02:00
|
|
|
/* Offset of the expression we're processing in the dependency bitmap */
|
|
|
|
uint expr_offset;
|
2009-08-13 00:34:21 +02:00
|
|
|
};
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
|
|
|
Setup equality dependencies
|
2009-08-18 17:01:51 +02:00
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
SYNOPSIS
|
|
|
|
setup_equality_deps()
|
|
|
|
te 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)
|
2009-08-18 17:01:51 +02:00
|
|
|
DESCRIPTION
|
|
|
|
RETURN
|
|
|
|
|
2009-08-13 22:44:52 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
static
|
2009-08-15 08:08:03 +02:00
|
|
|
bool setup_equality_deps(Table_elimination *te, Module_dep **bound_deps_list)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("setup_equality_deps");
|
|
|
|
|
2009-08-18 15:03:58 +02:00
|
|
|
if (!te->n_equality_deps)
|
|
|
|
DBUG_RETURN(TRUE);
|
2009-08-13 22:44:52 +02:00
|
|
|
/*
|
2009-08-15 08:08:03 +02:00
|
|
|
Count Field_value objects and assign each of them a unique bitmap_offset.
|
2009-08-13 22:44:52 +02:00
|
|
|
*/
|
2009-08-13 00:34:21 +02:00
|
|
|
uint offset= 0;
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Table_value **tbl_dep=te->table_deps;
|
2009-08-13 00:34:21 +02:00
|
|
|
tbl_dep < te->table_deps + MAX_TABLES;
|
2009-08-18 23:18:10 +02:00
|
|
|
tbl_dep++) // psergey-todo: Wipe this out altogether
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
|
|
|
if (*tbl_dep)
|
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Field_value *field_dep= (*tbl_dep)->fields;
|
2009-08-13 00:34:21 +02:00
|
|
|
field_dep;
|
|
|
|
field_dep= field_dep->next_table_field)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
field_dep->bitmap_offset= offset;
|
|
|
|
offset += te->n_equality_deps;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
void *buf;
|
|
|
|
if (!(buf= current_thd->alloc(bitmap_buffer_size(offset))) ||
|
|
|
|
bitmap_init(&te->expr_deps, (my_bitmap_map*)buf, offset, FALSE))
|
|
|
|
{
|
|
|
|
DBUG_RETURN(TRUE);
|
|
|
|
}
|
|
|
|
bitmap_clear_all(&te->expr_deps);
|
|
|
|
|
|
|
|
/*
|
2009-08-13 22:44:52 +02:00
|
|
|
Analyze all "field=expr" dependencies, and have te->expr_deps encode
|
|
|
|
dependencies of expressions from fields.
|
|
|
|
|
|
|
|
Also collect a linked list of equalities that are bound.
|
2009-08-13 00:34:21 +02:00
|
|
|
*/
|
2009-08-15 08:08:03 +02:00
|
|
|
Module_dep *bound_dep= NULL;
|
2009-08-18 17:01:51 +02:00
|
|
|
Field_dependency_recorder deps_recorder(te);
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Equality_module *eq_dep= te->equality_deps;
|
2009-08-13 00:34:21 +02:00
|
|
|
eq_dep < te->equality_deps + te->n_equality_deps;
|
|
|
|
eq_dep++)
|
|
|
|
{
|
2009-08-18 17:01:51 +02:00
|
|
|
deps_recorder.expr_offset= eq_dep - te->equality_deps;
|
2009-08-13 00:34:21 +02:00
|
|
|
eq_dep->unknown_args= 0;
|
2009-08-15 17:39:12 +02:00
|
|
|
eq_dep->expression->walk(&Item::check_column_usage_processor, FALSE,
|
2009-08-18 17:01:51 +02:00
|
|
|
(uchar*)&deps_recorder);
|
2009-08-13 00:34:21 +02:00
|
|
|
if (!eq_dep->unknown_args)
|
|
|
|
{
|
|
|
|
eq_dep->next= bound_dep;
|
|
|
|
bound_dep= eq_dep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*bound_deps_list= bound_dep;
|
|
|
|
|
2009-08-16 11:15:49 +02:00
|
|
|
DBUG_EXECUTE("test", dbug_print_deps(te); );
|
2009-08-13 00:34:21 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-08-13 00:34:21 +02:00
|
|
|
Perform table elimination
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-06-25 12:05:53 +02:00
|
|
|
SYNOPSIS
|
2009-08-13 00:34:21 +02:00
|
|
|
eliminate_tables()
|
|
|
|
join Join to work on
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-18 17:01:51 +02:00
|
|
|
This is the entry point for table elimination. Grep for MODULE INTERFACE
|
|
|
|
section in this file for calling convention.
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
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
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
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.
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
void eliminate_tables(JOIN *join)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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++)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
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 and DELETE: don't eliminate the tables we modify: */
|
|
|
|
used_tables |= thd->table_map_for_update;
|
|
|
|
|
|
|
|
/* Multi-table UPDATE: don't eliminate tables referred from SET statement */
|
|
|
|
if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI)
|
2009-06-30 15:11:00 +02:00
|
|
|
{
|
2009-08-13 00:34:21 +02:00
|
|
|
List_iterator<Item> it2(thd->lex->value_list);
|
|
|
|
while ((item= it2++))
|
|
|
|
used_tables |= item->used_tables();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
table_map all_tables= join->all_tables_map();
|
|
|
|
if (all_tables & ~used_tables)
|
|
|
|
{
|
|
|
|
/* There are some tables that we probably could eliminate. Try it. */
|
2009-08-18 17:01:51 +02:00
|
|
|
//psergey-todo: move allocs to somewhere else.
|
2009-08-13 00:34:21 +02:00
|
|
|
Table_elimination te(join);
|
|
|
|
uint m= max(thd->lex->current_select->max_equal_elems,1);
|
|
|
|
uint max_elems= ((thd->lex->current_select->cond_count+1)*2 +
|
|
|
|
thd->lex->current_select->between_count)*m + 1 + 10;
|
2009-08-15 08:08:03 +02:00
|
|
|
if (!(te.equality_deps= new Equality_module[max_elems]))
|
2009-08-13 00:34:21 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2009-08-18 15:03:58 +02:00
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
eliminate_tables_for_list(&te, join->join_list, all_tables, NULL,
|
|
|
|
used_tables);
|
2009-08-15 08:08:03 +02:00
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
|
2009-08-18 15:03:58 +02:00
|
|
|
/*
|
|
|
|
Perform table elimination in a given join list
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
eliminate_tables_for_list()
|
2009-08-18 17:01:51 +02:00
|
|
|
te 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.
|
2009-08-18 15:03:58 +02:00
|
|
|
tables_used_elsewhere Bitmap of tables that are referred to from
|
|
|
|
somewhere outside of the join list (e.g.
|
2009-08-18 17:01:51 +02:00
|
|
|
select list, HAVING, other ON expressions, etc).
|
2009-08-18 15:03:58 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-18 17:01:51 +02:00
|
|
|
Perform table elimination in a given join list.
|
|
|
|
|
2009-08-18 15:03:58 +02:00
|
|
|
RETURN
|
2009-08-18 17:01:51 +02:00
|
|
|
TRUE The entire join list eliminated
|
|
|
|
FALSE Join list wasn't eliminated (but some of its possibly were)
|
2009-08-18 15:03:58 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
static bool
|
2009-08-18 15:03:58 +02:00
|
|
|
eliminate_tables_for_list(Table_elimination *te, List<TABLE_LIST> *join_list,
|
2009-08-18 17:01:51 +02:00
|
|
|
table_map list_tables, Item *on_expr,
|
2009-08-18 15:03:58 +02:00
|
|
|
table_map tables_used_elsewhere)
|
|
|
|
{
|
|
|
|
TABLE_LIST *tbl;
|
|
|
|
List_iterator<TABLE_LIST> it(*join_list);
|
|
|
|
table_map tables_used_on_left= 0;
|
2009-08-18 17:01:51 +02:00
|
|
|
bool all_eliminated= TRUE;
|
2009-08-18 15:03:58 +02:00
|
|
|
|
|
|
|
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(te,
|
|
|
|
&tbl->nested_join->join_list,
|
|
|
|
tbl->nested_join->used_tables,
|
2009-08-18 17:01:51 +02:00
|
|
|
tbl->on_expr,
|
2009-08-18 15:03:58 +02:00
|
|
|
outside_used_tables))
|
|
|
|
{
|
|
|
|
mark_as_eliminated(te->join, tbl);
|
|
|
|
}
|
|
|
|
else
|
2009-08-18 17:01:51 +02:00
|
|
|
all_eliminated= FALSE;
|
2009-08-18 15:03:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This is "... LEFT JOIN tbl ON cond" */
|
|
|
|
if (!(tbl->table->map & outside_used_tables) &&
|
2009-08-18 23:18:10 +02:00
|
|
|
check_func_dependency(te, tbl->table->map, NULL, tbl,
|
|
|
|
tbl->on_expr))
|
2009-08-18 15:03:58 +02:00
|
|
|
{
|
|
|
|
mark_as_eliminated(te->join, tbl);
|
|
|
|
}
|
|
|
|
else
|
2009-08-18 17:01:51 +02:00
|
|
|
all_eliminated= FALSE;
|
2009-08-18 15:03:58 +02:00
|
|
|
}
|
|
|
|
tables_used_on_left |= tbl->on_expr->used_tables();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(!tbl->nested_join);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try eliminating the nest we're called for */
|
2009-08-18 17:01:51 +02:00
|
|
|
if (all_eliminated && on_expr && !(list_tables & tables_used_elsewhere))
|
|
|
|
{
|
2009-08-18 23:18:10 +02:00
|
|
|
it.rewind();
|
2009-08-18 17:01:51 +02:00
|
|
|
return check_func_dependency(te, list_tables & ~te->join->eliminated_tables,
|
2009-08-18 23:18:10 +02:00
|
|
|
&it, NULL, on_expr);
|
2009-08-18 17:01:51 +02:00
|
|
|
}
|
2009-08-18 15:03:58 +02:00
|
|
|
return FALSE; /* not eliminated */
|
|
|
|
}
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
|
2009-08-18 15:03:58 +02:00
|
|
|
/*
|
2009-08-18 17:01:51 +02:00
|
|
|
Check if condition makes the a set of tables functionally-dependent
|
2009-08-18 15:03:58 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
2009-08-18 17:01:51 +02:00
|
|
|
check_func_dependency()
|
|
|
|
te Table elimination context
|
|
|
|
tables Set of tables we want to be functionally dependent
|
|
|
|
cond Condition to use
|
2009-08-18 15:03:58 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-08-18 17:01:51 +02:00
|
|
|
Check if condition allows to conclude that the table set is functionally
|
|
|
|
dependent on everything else.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
TRUE - Yes, functionally dependent
|
|
|
|
FALSE - No, or error
|
2009-08-18 15:03:58 +02:00
|
|
|
*/
|
|
|
|
|
2009-08-18 17:01:51 +02:00
|
|
|
static
|
2009-08-18 23:18:10 +02:00
|
|
|
bool check_func_dependency(Table_elimination *te,
|
|
|
|
table_map dep_tables,
|
|
|
|
List_iterator<TABLE_LIST> *it,
|
|
|
|
TABLE_LIST *oj_tbl,
|
|
|
|
Item* cond)
|
2009-08-18 15:03:58 +02:00
|
|
|
{
|
2009-08-18 17:01:51 +02:00
|
|
|
uint and_level=0;
|
|
|
|
Equality_module* eq_dep= te->equality_deps;
|
|
|
|
Module_dep *bound_modules;
|
2009-08-18 23:18:10 +02:00
|
|
|
|
|
|
|
bzero(te->table_deps, sizeof(te->table_deps));
|
|
|
|
|
|
|
|
if (oj_tbl)
|
2009-08-18 15:03:58 +02:00
|
|
|
{
|
2009-08-18 23:18:10 +02:00
|
|
|
if (!get_table_value(te, oj_tbl->table))
|
|
|
|
return FALSE;
|
2009-08-18 15:03:58 +02:00
|
|
|
}
|
2009-08-18 23:18:10 +02:00
|
|
|
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 */
|
2009-08-18 15:03:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
/*
|
|
|
|
Table is known means that
|
|
|
|
- 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)
|
2009-08-15 08:08:03 +02:00
|
|
|
{
|
|
|
|
for (uint i=0; i < te->n_equality_deps; i++)
|
|
|
|
{
|
2009-08-18 23:18:10 +02:00
|
|
|
if (bitmap_is_set(&te->expr_deps, bitmap_offset + i) &&
|
2009-08-15 08:08:03 +02:00
|
|
|
te->equality_deps[i].unknown_args &&
|
|
|
|
!--te->equality_deps[i].unknown_args)
|
2009-08-13 00:34:21 +02:00
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
/* Mark as bound and add to the list */
|
|
|
|
Equality_module* eq_dep= &te->equality_deps[i];
|
|
|
|
eq_dep->next= *bound_modules;
|
|
|
|
*bound_modules= eq_dep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
bool Outer_join_module::now_bound(Table_elimination *te,
|
|
|
|
Value_dep **bound_values)
|
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("Outer join eliminated"));
|
|
|
|
return TRUE; /* Signal to finish the process */
|
|
|
|
}
|
2009-08-18 17:01:51 +02:00
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
|
|
|
|
bool Equality_module::now_bound(Table_elimination *te,
|
|
|
|
Value_dep **bound_values)
|
2009-08-15 08:08:03 +02:00
|
|
|
{
|
2009-08-18 23:18:10 +02:00
|
|
|
/* For field=expr and we got to know the expr, so we know the field */
|
|
|
|
if (!field->bound)
|
2009-08-15 08:08:03 +02:00
|
|
|
{
|
2009-08-18 23:18:10 +02:00
|
|
|
/* Mark as bound and add to the list */
|
|
|
|
field->bound= TRUE;
|
|
|
|
field->next= *bound_values;
|
|
|
|
*bound_values= field;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-08-18 17:01:51 +02:00
|
|
|
|
2009-08-18 23:18:10 +02:00
|
|
|
/* Unique key is known means its table is known */
|
|
|
|
bool Key_module::now_bound(Table_elimination *te, Value_dep **bound_values)
|
|
|
|
{
|
|
|
|
if (!table->bound)
|
|
|
|
{
|
|
|
|
/* Mark as bound and add to the list */
|
|
|
|
table->bound= TRUE;
|
|
|
|
table->next= *bound_values;
|
|
|
|
*bound_values= table;
|
2009-06-30 15:11:00 +02:00
|
|
|
}
|
2009-08-18 15:03:58 +02:00
|
|
|
return FALSE;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Mark one table or the whole join nest as eliminated.
|
|
|
|
*/
|
2009-08-18 23:18:10 +02:00
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl)
|
|
|
|
{
|
|
|
|
TABLE *table;
|
|
|
|
/*
|
|
|
|
NOTE: there are TABLE_LIST object that have
|
|
|
|
tbl->table!= NULL && tbl->nested_join!=NULL and
|
|
|
|
tbl->table == tbl->nested_join->join_list->element(..)->table
|
|
|
|
*/
|
|
|
|
if (tbl->nested_join)
|
|
|
|
{
|
|
|
|
TABLE_LIST *child;
|
|
|
|
List_iterator<TABLE_LIST> it(tbl->nested_join->join_list);
|
|
|
|
while ((child= it++))
|
|
|
|
mark_as_eliminated(join, child);
|
|
|
|
}
|
|
|
|
else if ((table= tbl->table))
|
|
|
|
{
|
|
|
|
JOIN_TAB *tab= tbl->table->reginfo.join_tab;
|
|
|
|
if (!(join->const_table_map & tab->table->map))
|
|
|
|
{
|
|
|
|
DBUG_PRINT("info", ("Eliminated table %s", table->alias));
|
|
|
|
tab->type= JT_CONST;
|
|
|
|
join->eliminated_tables |= table->map;
|
|
|
|
join->const_table_map|= table->map;
|
|
|
|
set_position(join, join->const_tables++, tab, (KEYUSE*)0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tbl->on_expr)
|
|
|
|
tbl->on_expr->walk(&Item::mark_as_eliminated_processor, FALSE, NULL);
|
|
|
|
}
|
|
|
|
|
2009-08-13 00:34:21 +02:00
|
|
|
|
|
|
|
#ifndef DBUG_OFF
|
|
|
|
static
|
|
|
|
void dbug_print_deps(Table_elimination *te)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("dbug_print_deps");
|
|
|
|
DBUG_LOCK_FILE;
|
|
|
|
|
|
|
|
fprintf(DBUG_FILE,"deps {\n");
|
|
|
|
|
|
|
|
/* Start with printing equalities */
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Equality_module *eq_dep= te->equality_deps;
|
2009-08-13 00:34:21 +02:00
|
|
|
eq_dep != te->equality_deps + te->n_equality_deps; eq_dep++)
|
|
|
|
{
|
|
|
|
char buf[128];
|
|
|
|
String str(buf, sizeof(buf), &my_charset_bin);
|
|
|
|
str.length(0);
|
2009-08-15 17:39:12 +02:00
|
|
|
eq_dep->expression->print(&str, QT_ORDINARY);
|
2009-08-13 00:34:21 +02:00
|
|
|
fprintf(DBUG_FILE, " equality%d: %s -> %s.%s\n",
|
|
|
|
eq_dep - te->equality_deps,
|
|
|
|
str.c_ptr(),
|
|
|
|
eq_dep->field->table->table->alias,
|
|
|
|
eq_dep->field->field->field_name);
|
|
|
|
}
|
|
|
|
fprintf(DBUG_FILE,"\n");
|
|
|
|
|
|
|
|
/* Then tables and their fields */
|
|
|
|
for (uint i=0; i < MAX_TABLES; i++)
|
|
|
|
{
|
2009-08-15 08:08:03 +02:00
|
|
|
Table_value *table_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
if ((table_dep= te->table_deps[i]))
|
|
|
|
{
|
|
|
|
/* Print table */
|
|
|
|
fprintf(DBUG_FILE, " table %s\n", table_dep->table->alias);
|
|
|
|
/* Print fields */
|
2009-08-15 08:08:03 +02:00
|
|
|
for (Field_value *field_dep= table_dep->fields; field_dep;
|
2009-08-13 00:34:21 +02:00
|
|
|
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 + te->n_equality_deps; bit++)
|
|
|
|
{
|
|
|
|
if (bitmap_is_set(&te->expr_deps, bit))
|
|
|
|
fprintf(DBUG_FILE, " equality%d ", bit - ofs);
|
|
|
|
}
|
|
|
|
fprintf(DBUG_FILE, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(DBUG_FILE,"\n}\n");
|
|
|
|
DBUG_UNLOCK_FILE;
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2009-08-15 08:08:03 +02:00
|
|
|
#endif
|
2009-06-25 12:05:53 +02:00
|
|
|
/**
|
|
|
|
@} (end of group Table_Elimination)
|
|
|
|
*/
|
|
|
|
|