MWL#17: Table elimination
- Last fixes sql/item.cc: MWL#17: Table elimination - Don't make multiple calls of ::walk(check_column_usage_processor), call once and cache the value sql/item.h: MWL#17: Table elimination - s/KEYUSE::usable/KEYUSE::type/, more comments sql/opt_table_elimination.cc: MWL#17: Table elimination - Don't make multiple calls of ::walk(check_column_usage_processor), call once and cache the value sql/sql_select.cc: MWL#17: Table elimination - s/KEYUSE::usable/KEYUSE::type/, more comments sql/sql_select.h: MWL#17: Table elimination - s/KEYUSE::usable/KEYUSE::type/, more comments sql/table.h: MWL#17: Table elimination - Better comments
This commit is contained in:
parent
9fa1bce436
commit
8156d9eb0a
15
sql/item.cc
15
sql/item.cc
@ -1920,10 +1920,6 @@ bool Item_field::check_column_usage_processor(uchar *arg)
|
|||||||
{
|
{
|
||||||
Field_processor_info* info=(Field_processor_info*)arg;
|
Field_processor_info* info=(Field_processor_info*)arg;
|
||||||
|
|
||||||
/* It is ok if this is a column of an allowed table: */
|
|
||||||
if (used_tables() & ~info->allowed_tables)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (field->table == info->table)
|
if (field->table == info->table)
|
||||||
{
|
{
|
||||||
/* It is not ok to use columns that are not part of the key of interest: */
|
/* It is not ok to use columns that are not part of the key of interest: */
|
||||||
@ -1936,18 +1932,17 @@ bool Item_field::check_column_usage_processor(uchar *arg)
|
|||||||
{
|
{
|
||||||
if (field->field_index == key->key_part[part].field->field_index)
|
if (field->field_index == key->key_part[part].field->field_index)
|
||||||
{
|
{
|
||||||
|
if (part == info->forbidden_part)
|
||||||
|
return TRUE;
|
||||||
info->needed_key_parts |= key_part_map(1) << part;
|
info->needed_key_parts |= key_part_map(1) << part;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/*
|
info->used_tables |= this->used_tables();
|
||||||
We get here when this refers to a table that's neither the table of
|
return FALSE;
|
||||||
interest, nor one of the allowed tables.
|
|
||||||
*/
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
11
sql/item.h
11
sql/item.h
@ -1018,11 +1018,14 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Data for Item::check_column_usage_processor */
|
/* Data for Item::check_column_usage_processor */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
table_map allowed_tables;
|
TABLE *table; /* Table of interest */
|
||||||
TABLE *table;
|
uint keyno; /* Index of interest */
|
||||||
uint keyno;
|
uint forbidden_part; /* key part which one is not allowed to refer to */
|
||||||
|
/* [Set by processor] used tables, besides the table of interest */
|
||||||
|
table_map used_tables;
|
||||||
|
/* [Set by processor] Parts of index of interest that expression refers to */
|
||||||
uint needed_key_parts;
|
uint needed_key_parts;
|
||||||
} Field_processor_info;
|
} Field_processor_info;
|
||||||
|
|
||||||
|
@ -166,9 +166,8 @@ void eliminate_tables(JOIN *join)
|
|||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
Number of base tables left after elimination. 0 means everything was
|
Number of children left after elimination. 0 means everything was
|
||||||
eliminated. Tables that belong to the
|
eliminated.
|
||||||
children of this join nest are also counted.
|
|
||||||
|
|
||||||
// TRUE The entire join list can be eliminated (caller should remove)
|
// TRUE The entire join list can be eliminated (caller should remove)
|
||||||
// FALSE Otherwise
|
// FALSE Otherwise
|
||||||
@ -188,7 +187,7 @@ eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|||||||
table_map tables_used_on_left= 0;
|
table_map tables_used_on_left= 0;
|
||||||
TABLE **cur_table= leaves_arr;
|
TABLE **cur_table= leaves_arr;
|
||||||
bool children_have_multiple_matches= FALSE;
|
bool children_have_multiple_matches= FALSE;
|
||||||
uint base_tables= 0;
|
uint remaining_children= 0;
|
||||||
|
|
||||||
while ((tbl= it++))
|
while ((tbl= it++))
|
||||||
{
|
{
|
||||||
@ -209,8 +208,9 @@ eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|||||||
{
|
{
|
||||||
mark_as_eliminated(join, tbl);
|
mark_as_eliminated(join, tbl);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
remaining_children++;
|
||||||
tbl->nested_join->n_tables= n;
|
tbl->nested_join->n_tables= n;
|
||||||
base_tables += n;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -222,7 +222,7 @@ eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|||||||
mark_as_eliminated(join, tbl);
|
mark_as_eliminated(join, tbl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
base_tables++;
|
remaining_children++;
|
||||||
}
|
}
|
||||||
tables_used_on_left |= tbl->on_expr->used_tables();
|
tables_used_on_left |= tbl->on_expr->used_tables();
|
||||||
children_have_multiple_matches= children_have_multiple_matches ||
|
children_have_multiple_matches= children_have_multiple_matches ||
|
||||||
@ -231,7 +231,7 @@ eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(!tbl->nested_join);
|
DBUG_ASSERT(!tbl->nested_join);
|
||||||
base_tables++;
|
remaining_children++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tbl->table)
|
if (tbl->table)
|
||||||
@ -271,10 +271,10 @@ eliminate_tables_for_list(JOIN *join, TABLE **leaves_arr,
|
|||||||
This join_list can be eliminated. Signal about this to the caller by
|
This join_list can be eliminated. Signal about this to the caller by
|
||||||
returning number of tables.
|
returning number of tables.
|
||||||
*/
|
*/
|
||||||
base_tables= 0;
|
remaining_children= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return base_tables;
|
return remaining_children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -330,7 +330,7 @@ static bool table_has_one_match(TABLE *table, table_map bound_tables,
|
|||||||
|
|
||||||
do /* For each keypart and each way to read it */
|
do /* For each keypart and each way to read it */
|
||||||
{
|
{
|
||||||
if (keyuse->usable == 1)
|
if (keyuse->type == KEYUSE_USABLE)
|
||||||
{
|
{
|
||||||
if(!(keyuse->used_tables & ~bound_tables) &&
|
if(!(keyuse->used_tables & ~bound_tables) &&
|
||||||
!(keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))
|
!(keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))
|
||||||
@ -400,12 +400,56 @@ extra_keyuses_bind_all_keyparts(table_map bound_tables, TABLE *table,
|
|||||||
uint n_keyuses, table_map bound_parts)
|
uint n_keyuses, table_map bound_parts)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Current implementation needs some keyparts to be already bound to start
|
We need
|
||||||
inferences:
|
- some 'unusable' KEYUSE elements to work on
|
||||||
|
- some keyparts to be already bound to start inferences:
|
||||||
*/
|
*/
|
||||||
if (n_keyuses && bound_parts)
|
if (n_keyuses && bound_parts)
|
||||||
{
|
{
|
||||||
KEY *keyinfo= table->key_info + key_start->key;
|
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;
|
||||||
|
#if 0
|
||||||
Keyuse_w_needed_reg *uses;
|
Keyuse_w_needed_reg *uses;
|
||||||
if (!(uses= (Keyuse_w_needed_reg*)my_alloca(sizeof(Keyuse_w_needed_reg)*
|
if (!(uses= (Keyuse_w_needed_reg*)my_alloca(sizeof(Keyuse_w_needed_reg)*
|
||||||
n_keyuses)))
|
n_keyuses)))
|
||||||
@ -450,8 +494,7 @@ extra_keyuses_bind_all_keyparts(table_map bound_tables, TABLE *table,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
} while (n_bounded != 0);
|
} while (n_bounded != 0);
|
||||||
}
|
#endif
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2762,7 +2762,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
|
|||||||
{
|
{
|
||||||
start_keyuse=keyuse;
|
start_keyuse=keyuse;
|
||||||
key=keyuse->key;
|
key=keyuse->key;
|
||||||
if (keyuse->usable == 1)
|
if (keyuse->type == KEYUSE_USABLE)
|
||||||
s->keys.set_bit(key); // QQ: remove this ?
|
s->keys.set_bit(key); // QQ: remove this ?
|
||||||
|
|
||||||
refs=0;
|
refs=0;
|
||||||
@ -2770,8 +2770,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
|
|||||||
eq_part.clear_all();
|
eq_part.clear_all();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (keyuse->usable==1 && keyuse->val->type() != Item::NULL_ITEM &&
|
if (keyuse->type == KEYUSE_USABLE &&
|
||||||
!keyuse->optimize)
|
keyuse->val->type() != Item::NULL_ITEM && !keyuse->optimize)
|
||||||
{
|
{
|
||||||
if (!((~found_const_table_map) & keyuse->used_tables))
|
if (!((~found_const_table_map) & keyuse->used_tables))
|
||||||
const_ref.set_bit(keyuse->keypart);
|
const_ref.set_bit(keyuse->keypart);
|
||||||
@ -2971,7 +2971,7 @@ typedef struct key_field_t {
|
|||||||
*/
|
*/
|
||||||
bool null_rejecting;
|
bool null_rejecting;
|
||||||
bool *cond_guard; /* See KEYUSE::cond_guard */
|
bool *cond_guard; /* See KEYUSE::cond_guard */
|
||||||
bool usable; /* See KEYUSE::usable */
|
enum keyuse_type type; /* See KEYUSE::type */
|
||||||
} KEY_FIELD;
|
} KEY_FIELD;
|
||||||
|
|
||||||
|
|
||||||
@ -3069,7 +3069,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
|
|||||||
be, too (TODO: shouldn't that apply to the above
|
be, too (TODO: shouldn't that apply to the above
|
||||||
null_rejecting and optimize attributes?)
|
null_rejecting and optimize attributes?)
|
||||||
*/
|
*/
|
||||||
DBUG_ASSERT(old->usable == new_fields->usable);
|
DBUG_ASSERT(old->type == new_fields->type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (old->eq_func && new_fields->eq_func &&
|
else if (old->eq_func && new_fields->eq_func &&
|
||||||
@ -3085,12 +3085,13 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
|
|||||||
old->null_rejecting= (old->null_rejecting &&
|
old->null_rejecting= (old->null_rejecting &&
|
||||||
new_fields->null_rejecting);
|
new_fields->null_rejecting);
|
||||||
// "t.key_col=const" predicates are always usable
|
// "t.key_col=const" predicates are always usable
|
||||||
DBUG_ASSERT(old->usable && new_fields->usable);
|
DBUG_ASSERT(old->type == KEYUSE_USABLE &&
|
||||||
|
new_fields->type == KEYUSE_USABLE);
|
||||||
}
|
}
|
||||||
else if (old->eq_func && new_fields->eq_func &&
|
else if (old->eq_func && new_fields->eq_func &&
|
||||||
((new_fields->usable && old->val->const_item() &&
|
((new_fields->type == KEYUSE_USABLE &&
|
||||||
old->val->is_null()) ||
|
old->val->const_item() && old->val->is_null()) ||
|
||||||
((old->usable && new_fields->val->is_null()))))
|
((old->type == KEYUSE_USABLE && new_fields->val->is_null()))))
|
||||||
/* TODO ^ why is the above asymmetric, why const_item()? */
|
/* TODO ^ why is the above asymmetric, why const_item()? */
|
||||||
{
|
{
|
||||||
/* field = expression OR field IS NULL */
|
/* field = expression OR field IS NULL */
|
||||||
@ -3181,9 +3182,6 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
|
|||||||
if (!((value[i])->used_tables() & (field->table->map | RAND_TABLE_BIT)))
|
if (!((value[i])->used_tables() & (field->table->map | RAND_TABLE_BIT)))
|
||||||
optimizable=1;
|
optimizable=1;
|
||||||
}
|
}
|
||||||
// psergey-tbl-elim:
|
|
||||||
// if (!optimizable)
|
|
||||||
// return;
|
|
||||||
if (!(usable_tables & field->table->map))
|
if (!(usable_tables & field->table->map))
|
||||||
{
|
{
|
||||||
if (!eq_func || (*value)->type() != Item::NULL_ITEM ||
|
if (!eq_func || (*value)->type() != Item::NULL_ITEM ||
|
||||||
@ -3290,7 +3288,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
|
|||||||
(*key_fields)->val= *value;
|
(*key_fields)->val= *value;
|
||||||
(*key_fields)->level= and_level;
|
(*key_fields)->level= and_level;
|
||||||
(*key_fields)->optimize= exists_optimize;
|
(*key_fields)->optimize= exists_optimize;
|
||||||
(*key_fields)->usable= optimizable;
|
(*key_fields)->type= optimizable? KEYUSE_USABLE : KEYUSE_UNKNOWN;
|
||||||
/*
|
/*
|
||||||
If the condition has form "tbl.keypart = othertbl.field" and
|
If the condition has form "tbl.keypart = othertbl.field" and
|
||||||
othertbl.field can be NULL, there will be no matches if othertbl.field
|
othertbl.field can be NULL, there will be no matches if othertbl.field
|
||||||
@ -3602,12 +3600,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
|
|||||||
keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
|
keyuse.optimize= key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL;
|
||||||
keyuse.null_rejecting= key_field->null_rejecting;
|
keyuse.null_rejecting= key_field->null_rejecting;
|
||||||
keyuse.cond_guard= key_field->cond_guard;
|
keyuse.cond_guard= key_field->cond_guard;
|
||||||
if (!(keyuse.usable= key_field->usable))
|
keyuse.type= key_field->type;
|
||||||
{
|
|
||||||
/* The following will have special meanings: */
|
|
||||||
keyuse.keypart_map= 0;
|
|
||||||
keyuse.used_tables= 0;
|
|
||||||
}
|
|
||||||
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
|
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3674,7 +3667,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
|
|||||||
keyuse.used_tables=cond_func->key_item()->used_tables();
|
keyuse.used_tables=cond_func->key_item()->used_tables();
|
||||||
keyuse.optimize= 0;
|
keyuse.optimize= 0;
|
||||||
keyuse.keypart_map= 0;
|
keyuse.keypart_map= 0;
|
||||||
keyuse.usable= 1;
|
keyuse.type= KEYUSE_USABLE;
|
||||||
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
|
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3691,8 +3684,10 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
|
|||||||
return (int) (a->keypart - b->keypart);
|
return (int) (a->keypart - b->keypart);
|
||||||
|
|
||||||
// Usable ones go before the unusable
|
// Usable ones go before the unusable
|
||||||
if (a->usable != b->usable)
|
int a_ok= test(a->type == KEYUSE_USABLE);
|
||||||
return (int)b->usable - (int)a->usable;
|
int b_ok= test(b->type == KEYUSE_USABLE);
|
||||||
|
if (a_ok != b_ok)
|
||||||
|
return a_ok? -1 : 1;
|
||||||
|
|
||||||
// Place const values before other ones
|
// Place const values before other ones
|
||||||
if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) -
|
if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) -
|
||||||
@ -3904,7 +3899,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
|
|||||||
found_eq_constant=0;
|
found_eq_constant=0;
|
||||||
for (i=0 ; i < keyuse->elements-1 ; i++,use++)
|
for (i=0 ; i < keyuse->elements-1 ; i++,use++)
|
||||||
{
|
{
|
||||||
if (use->usable == 1 && !use->used_tables &&
|
if (use->type == KEYUSE_USABLE && !use->used_tables &&
|
||||||
use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
|
use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
|
||||||
use->table->const_key_parts[use->key]|= use->keypart_map;
|
use->table->const_key_parts[use->key]|= use->keypart_map;
|
||||||
if (use->keypart != FT_KEYPART)
|
if (use->keypart != FT_KEYPART)
|
||||||
@ -3929,7 +3924,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
|
|||||||
/* Save ptr to first use */
|
/* Save ptr to first use */
|
||||||
if (!use->table->reginfo.join_tab->keyuse)
|
if (!use->table->reginfo.join_tab->keyuse)
|
||||||
use->table->reginfo.join_tab->keyuse=save_pos;
|
use->table->reginfo.join_tab->keyuse=save_pos;
|
||||||
if (use->usable == 1)
|
if (use->type == KEYUSE_USABLE)
|
||||||
use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
|
use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
|
||||||
save_pos++;
|
save_pos++;
|
||||||
}
|
}
|
||||||
@ -3960,7 +3955,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
|
|||||||
To avoid bad matches, we don't make ref_table_rows less than 100.
|
To avoid bad matches, we don't make ref_table_rows less than 100.
|
||||||
*/
|
*/
|
||||||
keyuse->ref_table_rows= ~(ha_rows) 0; // If no ref
|
keyuse->ref_table_rows= ~(ha_rows) 0; // If no ref
|
||||||
if (keyuse->usable == 1 && keyuse->used_tables &
|
if (keyuse->type == KEYUSE_USABLE && keyuse->used_tables &
|
||||||
(map= (keyuse->used_tables & ~join->const_table_map &
|
(map= (keyuse->used_tables & ~join->const_table_map &
|
||||||
~OUTER_REF_TABLE_BIT)))
|
~OUTER_REF_TABLE_BIT)))
|
||||||
{
|
{
|
||||||
@ -4152,7 +4147,7 @@ best_access_path(JOIN *join,
|
|||||||
if 1. expression doesn't refer to forward tables
|
if 1. expression doesn't refer to forward tables
|
||||||
2. we won't get two ref-or-null's
|
2. we won't get two ref-or-null's
|
||||||
*/
|
*/
|
||||||
if (keyuse->usable == 1&&
|
if (keyuse->type == KEYUSE_USABLE &&
|
||||||
!(remaining_tables & keyuse->used_tables) &&
|
!(remaining_tables & keyuse->used_tables) &&
|
||||||
!(ref_or_null_part && (keyuse->optimize &
|
!(ref_or_null_part && (keyuse->optimize &
|
||||||
KEY_OPTIMIZE_REF_OR_NULL)))
|
KEY_OPTIMIZE_REF_OR_NULL)))
|
||||||
@ -5607,7 +5602,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
|||||||
*/
|
*/
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (!(~used_tables & keyuse->used_tables) && keyuse->usable == 1)
|
if (!(~used_tables & keyuse->used_tables) &&
|
||||||
|
keyuse->type == KEYUSE_USABLE)
|
||||||
{
|
{
|
||||||
if (keyparts == keyuse->keypart &&
|
if (keyparts == keyuse->keypart &&
|
||||||
!(found_part_ref_or_null & keyuse->optimize))
|
!(found_part_ref_or_null & keyuse->optimize))
|
||||||
@ -5658,7 +5654,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
|
|||||||
for (i=0 ; i < keyparts ; keyuse++,i++)
|
for (i=0 ; i < keyparts ; keyuse++,i++)
|
||||||
{
|
{
|
||||||
while (keyuse->keypart != i || ((~used_tables) & keyuse->used_tables) ||
|
while (keyuse->keypart != i || ((~used_tables) & keyuse->used_tables) ||
|
||||||
!(keyuse->usable == 1))
|
!(keyuse->type == KEYUSE_USABLE))
|
||||||
{
|
{
|
||||||
keyuse++; /* Skip other parts */
|
keyuse++; /* Skip other parts */
|
||||||
}
|
}
|
||||||
@ -16709,9 +16705,9 @@ static void print_join(THD *thd,
|
|||||||
the fact that the first table can't be inner table of an outer join.
|
the fact that the first table can't be inner table of an outer join.
|
||||||
*/
|
*/
|
||||||
DBUG_ASSERT(!eliminated_tables ||
|
DBUG_ASSERT(!eliminated_tables ||
|
||||||
!((*table)->table && ((*table)->table->map & eliminated_tables) ||
|
!(((*table)->table && ((*table)->table->map & eliminated_tables)) ||
|
||||||
(*table)->nested_join && !((*table)->nested_join->used_tables &
|
((*table)->nested_join && !((*table)->nested_join->used_tables &
|
||||||
~eliminated_tables)));
|
~eliminated_tables))));
|
||||||
(*table)->print(thd, eliminated_tables, str, query_type);
|
(*table)->print(thd, eliminated_tables, str, query_type);
|
||||||
|
|
||||||
TABLE_LIST **end= table + tables->elements;
|
TABLE_LIST **end= table + tables->elements;
|
||||||
|
@ -33,6 +33,40 @@
|
|||||||
#define KEY_OPTIMIZE_EXISTS 1
|
#define KEY_OPTIMIZE_EXISTS 1
|
||||||
#define KEY_OPTIMIZE_REF_OR_NULL 2
|
#define KEY_OPTIMIZE_REF_OR_NULL 2
|
||||||
|
|
||||||
|
/* KEYUSE element types */
|
||||||
|
enum keyuse_type
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
val refers to the same table, this is either KEYUSE_BIND or KEYUSE_NO_BIND
|
||||||
|
type, we didn't determine which one yet.
|
||||||
|
*/
|
||||||
|
KEYUSE_UNKNOWN= 0,
|
||||||
|
/*
|
||||||
|
'regular' keyuse, i.e. it represents one of the following
|
||||||
|
* t.keyXpartY = func(constants, other-tables)
|
||||||
|
* t.keyXpartY IS NULL
|
||||||
|
* t.keyXpartY = func(constants, other-tables) OR t.keyXpartY IS NULL
|
||||||
|
and can be used to construct ref acces
|
||||||
|
*/
|
||||||
|
KEYUSE_USABLE,
|
||||||
|
/*
|
||||||
|
The keyuse represents a condition in form:
|
||||||
|
|
||||||
|
t.uniq_keyXpartY = func(other parts of uniq_keyX)
|
||||||
|
|
||||||
|
This can't be used to construct uniq_keyX but we could use it to determine
|
||||||
|
that the table will produce at most one match.
|
||||||
|
*/
|
||||||
|
KEYUSE_BIND,
|
||||||
|
/*
|
||||||
|
Keyuse that's not usable for ref access and doesn't meet the criteria of
|
||||||
|
KEYUSE_BIND. Examples:
|
||||||
|
t.keyXpartY = func(t.keyXpartY)
|
||||||
|
t.keyXpartY = func(column of t that's not covered by keyX)
|
||||||
|
*/
|
||||||
|
KEYUSE_NO_BIND
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct keyuse_t {
|
typedef struct keyuse_t {
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
Item *val; /**< or value if no field */
|
Item *val; /**< or value if no field */
|
||||||
@ -64,7 +98,7 @@ typedef struct keyuse_t {
|
|||||||
This equality cannot be used for index access but is useful
|
This equality cannot be used for index access but is useful
|
||||||
for table elimination.
|
for table elimination.
|
||||||
*/
|
*/
|
||||||
int usable;
|
enum keyuse_type type;
|
||||||
} KEYUSE;
|
} KEYUSE;
|
||||||
|
|
||||||
class store_key;
|
class store_key;
|
||||||
|
10
sql/table.h
10
sql/table.h
@ -1618,7 +1618,8 @@ typedef struct st_nested_join
|
|||||||
List<TABLE_LIST> join_list; /* list of elements in the nested join */
|
List<TABLE_LIST> join_list; /* list of elements in the nested join */
|
||||||
/*
|
/*
|
||||||
Bitmap of tables within this nested join (including those embedded within
|
Bitmap of tables within this nested join (including those embedded within
|
||||||
its children). Eliminated tables are still in the bitmap */
|
its children), including tables removed by table elimination.
|
||||||
|
*/
|
||||||
table_map used_tables;
|
table_map used_tables;
|
||||||
table_map not_null_tables; /* tables that rejects nulls */
|
table_map not_null_tables; /* tables that rejects nulls */
|
||||||
struct st_join_table *first_nested;/* the first nested table in the plan */
|
struct st_join_table *first_nested;/* the first nested table in the plan */
|
||||||
@ -1628,11 +1629,12 @@ typedef struct st_nested_join
|
|||||||
2. check_interleaving_with_nj/restore_prev_nj_state (these are called
|
2. check_interleaving_with_nj/restore_prev_nj_state (these are called
|
||||||
by the join optimizer.
|
by the join optimizer.
|
||||||
Before each use the counters are zeroed by reset_nj_counters.
|
Before each use the counters are zeroed by reset_nj_counters.
|
||||||
Meaning, in both cases: number of base tables within this nested join and
|
|
||||||
its children. Eliminated tables are not counted.
|
|
||||||
*/
|
*/
|
||||||
uint counter;
|
uint counter;
|
||||||
/* Tables left after elimination */
|
/*
|
||||||
|
Number of elements in join_list that were not (or contain table(s) that
|
||||||
|
weren't) removed by table elimination.
|
||||||
|
*/
|
||||||
uint n_tables;
|
uint n_tables;
|
||||||
nested_join_map nj_map; /* Bit used to identify this nested join*/
|
nested_join_map nj_map; /* Bit used to identify this nested join*/
|
||||||
} NESTED_JOIN;
|
} NESTED_JOIN;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user