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"
|
|
|
|
#include "sql_select.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
OVERVIEW
|
2009-06-25 22:07:29 +02:00
|
|
|
|
|
|
|
The module has one entry point - eliminate_tables() function, which one
|
|
|
|
needs to call (once) sometime after update_ref_and_keys() but before the
|
|
|
|
join optimization.
|
|
|
|
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.
|
|
|
|
* In addition to this, they are recorded in JOIN::eliminated_tables bitmap.
|
|
|
|
|
|
|
|
* All join nests have their NESTED_JOIN::n_tables updated to discount
|
|
|
|
the eliminated tables
|
|
|
|
|
|
|
|
* 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
|
|
|
|
- At the moment the only Item that cares 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.
|
|
|
|
|
|
|
|
Table elimination is redone on every PS re-execution.
|
2009-06-25 12:05:53 +02:00
|
|
|
*/
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl);
|
|
|
|
static bool table_has_one_match(TABLE *table, table_map bound_tables,
|
|
|
|
bool *multiple_matches);
|
|
|
|
static uint
|
|
|
|
eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|
|
|
List<TABLE_LIST> *join_list,
|
|
|
|
bool its_outer_join,
|
|
|
|
table_map tables_in_list,
|
|
|
|
table_map tables_used_elsewhere,
|
|
|
|
bool *multiple_matches);
|
2009-06-25 12:05:53 +02:00
|
|
|
static bool
|
|
|
|
extra_keyuses_bind_all_keyparts(table_map bound_tables, TABLE *table,
|
|
|
|
KEYUSE *key_start, KEYUSE *key_end,
|
|
|
|
uint n_keyuses, table_map bound_parts);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Perform table elimination
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
eliminate_tables()
|
|
|
|
join Join to work on
|
|
|
|
const_tbl_count INOUT Number of constant tables (this includes
|
|
|
|
eliminated tables)
|
|
|
|
const_tables INOUT Bitmap of constant tables
|
|
|
|
|
|
|
|
DESCRIPTION
|
2009-06-25 22:07:29 +02:00
|
|
|
This function is the entry point for table elimination.
|
|
|
|
The idea behind table elimination is that if we have an outer join:
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
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.
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
SIDE EFFECTS
|
|
|
|
See the OVERVIEW section at the top of this file.
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
void eliminate_tables(JOIN *join)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
|
|
|
Item *item;
|
|
|
|
table_map used_tables;
|
|
|
|
DBUG_ENTER("eliminate_tables");
|
|
|
|
|
|
|
|
DBUG_ASSERT(join->eliminated_tables == 0);
|
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
/* If there are no outer joins, we have nothing to eliminate: */
|
2009-06-25 12:05:53 +02:00
|
|
|
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++)
|
|
|
|
{
|
|
|
|
for (ORDER *cur_list= all_lists[i]; cur_list; cur_list= cur_list->next)
|
|
|
|
used_tables |= (*(cur_list->item))->used_tables();
|
|
|
|
}
|
|
|
|
|
|
|
|
THD* thd= join->thd;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
List_iterator<Item> it2(thd->lex->value_list);
|
|
|
|
while ((item= it2++))
|
|
|
|
used_tables |= item->used_tables();
|
|
|
|
}
|
|
|
|
}
|
2009-06-29 15:51:15 +02:00
|
|
|
|
|
|
|
table_map all_tables= join->all_tables_map();
|
|
|
|
if (all_tables & ~used_tables)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-25 22:07:29 +02:00
|
|
|
/* There are some tables that we probably could eliminate. Try it. */
|
2009-06-29 15:51:15 +02:00
|
|
|
TABLE *leaves_array[MAX_TABLES];
|
|
|
|
bool multiple_matches= FALSE;
|
|
|
|
eliminate_tables_for_list(join, leaves_array, join->join_list, FALSE,
|
|
|
|
all_tables, used_tables, &multiple_matches);
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-06-25 22:07:29 +02:00
|
|
|
Perform table elimination in a given join list
|
2009-06-25 12:05:53 +02:00
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
SYNOPSIS
|
2009-06-29 15:51:15 +02:00
|
|
|
eliminate_tables_for_list()
|
2009-06-25 22:07:29 +02:00
|
|
|
join The join
|
2009-06-30 15:20:18 +02:00
|
|
|
leaves_arr OUT Store here an array of leaf (base) tables that
|
|
|
|
are descendants of the join_list, and increment
|
|
|
|
the pointer to point right above the array.
|
2009-06-25 22:07:29 +02:00
|
|
|
join_list Join list to work on
|
2009-06-30 15:20:18 +02:00
|
|
|
its_outer_join TRUE <=> join_list is an inner side of an outer
|
|
|
|
join
|
|
|
|
FALSE <=> otherwise (this is top-level join list)
|
|
|
|
tables_in_list Bitmap of tables embedded in the join_list.
|
2009-06-25 22:07:29 +02:00
|
|
|
tables_used_elsewhere Bitmap of tables that are referred to from
|
|
|
|
somewhere outside of the join list (e.g.
|
|
|
|
select list, HAVING, etc).
|
2009-06-30 15:20:18 +02:00
|
|
|
|
2009-06-25 22:07:29 +02:00
|
|
|
DESCRIPTION
|
2009-06-30 15:20:18 +02:00
|
|
|
Perform table elimination for a join list.
|
|
|
|
Try eliminating children nests first.
|
|
|
|
The "all tables in join nest can produce only one matching record
|
|
|
|
combination" property checking is modeled after constant table detection,
|
|
|
|
plus we reuse info attempts to eliminate child join nests.
|
2009-06-25 22:07:29 +02:00
|
|
|
|
|
|
|
RETURN
|
2009-06-30 15:11:00 +02:00
|
|
|
Number of children left after elimination. 0 means everything was
|
|
|
|
eliminated.
|
2009-06-25 12:05:53 +02:00
|
|
|
*/
|
2009-06-29 15:51:15 +02:00
|
|
|
static uint
|
|
|
|
eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|
|
|
List<TABLE_LIST> *join_list,
|
|
|
|
bool its_outer_join,
|
|
|
|
table_map tables_in_list,
|
|
|
|
table_map tables_used_elsewhere,
|
|
|
|
bool *multiple_matches)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *tbl;
|
2009-06-29 15:51:15 +02:00
|
|
|
List_iterator<TABLE_LIST> it(*join_list);
|
|
|
|
table_map tables_used_on_left= 0;
|
|
|
|
TABLE **cur_table= leaves_arr;
|
|
|
|
bool children_have_multiple_matches= FALSE;
|
2009-06-30 15:11:00 +02:00
|
|
|
uint remaining_children= 0;
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
while ((tbl= it++))
|
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
if (tbl->on_expr)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
table_map outside_used_tables= tables_used_elsewhere |
|
|
|
|
tables_used_on_left;
|
|
|
|
bool multiple_matches= FALSE;
|
|
|
|
if (tbl->nested_join)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
/* This is "... LEFT JOIN (join_nest) ON cond" */
|
|
|
|
uint n;
|
|
|
|
if (!(n= eliminate_tables_for_list(join, cur_table,
|
|
|
|
&tbl->nested_join->join_list, TRUE,
|
|
|
|
tbl->nested_join->used_tables,
|
|
|
|
outside_used_tables,
|
|
|
|
&multiple_matches)))
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
mark_as_eliminated(join, tbl);
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-06-30 15:11:00 +02:00
|
|
|
else
|
|
|
|
remaining_children++;
|
2009-06-29 15:51:15 +02:00
|
|
|
tbl->nested_join->n_tables= n;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* This is "... LEFT JOIN tbl ON cond" */
|
|
|
|
if (!(tbl->table->map & outside_used_tables) &&
|
|
|
|
table_has_one_match(tbl->table, join->all_tables_map(),
|
|
|
|
&multiple_matches))
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
mark_as_eliminated(join, tbl);
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-06-29 15:51:15 +02:00
|
|
|
else
|
2009-06-30 15:11:00 +02:00
|
|
|
remaining_children++;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-06-29 15:51:15 +02:00
|
|
|
tables_used_on_left |= tbl->on_expr->used_tables();
|
|
|
|
children_have_multiple_matches= children_have_multiple_matches ||
|
|
|
|
multiple_matches;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-06-29 15:51:15 +02:00
|
|
|
else
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
DBUG_ASSERT(!tbl->nested_join);
|
2009-06-30 15:11:00 +02:00
|
|
|
remaining_children++;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
if (tbl->table)
|
|
|
|
*(cur_table++)= tbl->table;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
*multiple_matches |= children_have_multiple_matches;
|
|
|
|
|
|
|
|
/* Try eliminating the nest we're called for */
|
|
|
|
if (its_outer_join && !children_have_multiple_matches &&
|
|
|
|
!(tables_in_list & tables_used_elsewhere))
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
2009-06-29 15:51:15 +02:00
|
|
|
table_map bound_tables= join->const_table_map | (join->all_tables_map() &
|
|
|
|
~tables_in_list);
|
|
|
|
table_map old_bound_tables;
|
|
|
|
TABLE **leaves_end= cur_table;
|
|
|
|
/*
|
|
|
|
Do the same as const table search table: try to expand the set of bound
|
|
|
|
tables until it covers all tables in the join_list
|
|
|
|
*/
|
|
|
|
do
|
|
|
|
{
|
|
|
|
old_bound_tables= bound_tables;
|
|
|
|
for (cur_table= leaves_arr; cur_table != leaves_end; cur_table++)
|
|
|
|
{
|
|
|
|
if (!((*cur_table)->map & join->eliminated_tables) &&
|
|
|
|
table_has_one_match(*cur_table, bound_tables, multiple_matches))
|
|
|
|
{
|
|
|
|
bound_tables |= (*cur_table)->map;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (old_bound_tables != bound_tables);
|
|
|
|
|
|
|
|
if (!(tables_in_list & ~bound_tables))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
This join_list can be eliminated. Signal about this to the caller by
|
|
|
|
returning number of tables.
|
|
|
|
*/
|
2009-06-30 15:11:00 +02:00
|
|
|
remaining_children= 0;
|
2009-06-29 15:51:15 +02:00
|
|
|
}
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
2009-06-30 15:11:00 +02:00
|
|
|
return remaining_children;
|
2009-06-25 12:05:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-06-25 22:07:29 +02:00
|
|
|
Check if the table will produce at most one matching record
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
table_has_one_match()
|
2009-06-29 15:51:15 +02:00
|
|
|
table The [base] table being checked
|
|
|
|
bound_tables Tables that should be considered bound.
|
|
|
|
multiple_matches OUT Set to TRUE when there is no way we could
|
|
|
|
find find a limitation that would give us one-match
|
|
|
|
property.
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
2009-06-25 22:07:29 +02:00
|
|
|
Check if table will produce at most one matching record for each record
|
|
|
|
combination of tables in bound_tables bitmap.
|
|
|
|
|
|
|
|
The check is based on ref analysis data, KEYUSE structures. We're
|
|
|
|
handling two cases:
|
|
|
|
|
|
|
|
1. Table has a UNIQUE KEY(uk_col_1, ... uk_col_N), and for each uk_col_i
|
|
|
|
there is a KEYUSE that represents a limitation in form
|
|
|
|
|
|
|
|
table.uk_col_i = func(bound_tables) (X)
|
|
|
|
|
|
|
|
2. Same as above but we also handle limitations in form
|
|
|
|
|
|
|
|
table.uk_col_i = func(bound_tables, uk_col_j1, ... uk_col_j2) (XX)
|
|
|
|
|
|
|
|
where values of uk_col_jN are known to be bound because for them we
|
|
|
|
have an equality of form (X) or (XX).
|
2009-06-25 12:05:53 +02:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
TRUE Yes, at most one match
|
|
|
|
FALSE No
|
|
|
|
*/
|
|
|
|
|
2009-06-29 15:51:15 +02:00
|
|
|
static bool table_has_one_match(TABLE *table, table_map bound_tables,
|
|
|
|
bool *multiple_matches)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
|
|
|
KEYUSE *keyuse= table->reginfo.join_tab->keyuse;
|
|
|
|
if (keyuse)
|
|
|
|
{
|
|
|
|
while (keyuse->table == table)
|
|
|
|
{
|
|
|
|
uint key= keyuse->key;
|
|
|
|
key_part_map bound_parts=0;
|
|
|
|
uint n_unusable=0;
|
|
|
|
bool ft_key= test(keyuse->keypart == FT_KEYPART);
|
|
|
|
KEY *keyinfo= table->key_info + key;
|
|
|
|
KEYUSE *key_start = keyuse;
|
|
|
|
|
|
|
|
do /* For each keypart and each way to read it */
|
|
|
|
{
|
2009-06-30 15:11:00 +02:00
|
|
|
if (keyuse->type == KEYUSE_USABLE)
|
2009-06-25 12:05:53 +02:00
|
|
|
{
|
|
|
|
if(!(keyuse->used_tables & ~bound_tables) &&
|
|
|
|
!(keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))
|
|
|
|
{
|
|
|
|
bound_parts |= keyuse->keypart_map;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
n_unusable++;
|
|
|
|
keyuse++;
|
|
|
|
} while (keyuse->table == table && keyuse->key == key);
|
|
|
|
|
|
|
|
if (ft_key || ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY))
|
|
|
|
!= HA_NOSAME))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bound_parts == PREV_BITS(key_part_map, keyinfo->key_parts) ||
|
|
|
|
extra_keyuses_bind_all_keyparts(bound_tables, table, key_start,
|
|
|
|
keyuse, n_unusable, bound_parts))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-06-25 22:07:29 +02:00
|
|
|
Check if KEYUSE elemements with unusable==TRUE bind all parts of the key
|
|
|
|
|
2009-06-25 12:05:53 +02:00
|
|
|
SYNOPSIS
|
2009-06-25 22:07:29 +02:00
|
|
|
|
2009-06-25 12:05:53 +02:00
|
|
|
extra_keyuses_bind_all_keyparts()
|
|
|
|
bound_tables Tables which can be considered constants
|
|
|
|
table Table we're examining
|
|
|
|
key_start Start of KEYUSE array with elements describing the key
|
|
|
|
of interest
|
|
|
|
key_end End of the array + 1
|
2009-06-25 22:07:29 +02:00
|
|
|
n_keyuses Number of elements in the array that have unusable==TRUE
|
2009-06-25 12:05:53 +02:00
|
|
|
bound_parts Key parts whose values are known to be bound.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Check if unusable KEYUSE elements cause all parts of key to be bound. An
|
|
|
|
unusable keyuse element makes a keypart bound when it
|
|
|
|
represents the following:
|
|
|
|
|
|
|
|
keyXpartY=func(bound_columns, preceding_tables)
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
TRUE Yes, at most one match
|
|
|
|
FALSE No
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool
|
|
|
|
extra_keyuses_bind_all_keyparts(table_map bound_tables, TABLE *table,
|
|
|
|
KEYUSE *key_start, KEYUSE *key_end,
|
|
|
|
uint n_keyuses, table_map bound_parts)
|
|
|
|
{
|
2009-06-25 22:07:29 +02:00
|
|
|
/*
|
2009-06-30 15:11:00 +02:00
|
|
|
We need
|
|
|
|
- some 'unusable' KEYUSE elements to work on
|
|
|
|
- some keyparts to be already bound to start inferences:
|
2009-06-25 22:07:29 +02:00
|
|
|
*/
|
2009-06-25 12:05:53 +02:00
|
|
|
if (n_keyuses && bound_parts)
|
|
|
|
{
|
2009-06-30 15:11:00 +02:00
|
|
|
KEY *keyinfo= table->key_info + key_start->key;
|
|
|
|
bool bound_more_parts;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
bound_more_parts= FALSE;
|
|
|
|
for (KEYUSE *k= key_start; k!=key_end; k++)
|
|
|
|
{
|
|
|
|
if (k->type == KEYUSE_UNKNOWN)
|
|
|
|
{
|
|
|
|
Field_processor_info fp= {table, k->key, k->keypart, 0, 0};
|
|
|
|
if (k->val->walk(&Item::check_column_usage_processor, FALSE,
|
|
|
|
(uchar*)&fp))
|
|
|
|
k->type= KEYUSE_NO_BIND;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
k->used_tables= fp.used_tables;
|
|
|
|
k->keypart_map= fp.needed_key_parts;
|
|
|
|
k->type= KEYUSE_BIND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (k->type == KEYUSE_BIND)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If this is a binding keyuse, such that
|
|
|
|
- all tables it refers to are bound,
|
|
|
|
- all parts it refers to are bound
|
|
|
|
- but the key part it binds is not itself bound
|
|
|
|
*/
|
|
|
|
if (!(k->used_tables & ~bound_tables) &&
|
|
|
|
!(k->keypart_map & ~bound_parts) &&
|
|
|
|
!(bound_parts & key_part_map(1) << k->keypart))
|
|
|
|
{
|
|
|
|
bound_parts|= key_part_map(1) << k->keypart;
|
|
|
|
if (bound_parts == PREV_BITS(key_part_map, keyinfo->key_parts))
|
|
|
|
return TRUE;
|
|
|
|
bound_more_parts= TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (bound_more_parts);
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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-06-25 12:05:53 +02:00
|
|
|
/**
|
|
|
|
@} (end of group Table_Elimination)
|
|
|
|
*/
|
|
|
|
|