Commit 9e65634b authored by Sergey Petrunia's avatar Sergey Petrunia

MWL#17: Table elimination

- Make elimination check to be able detect cases like  t.primary_key_col1=othertbl.col AND t.primary_key_col2=func(t.primary_key_col1).
  These are needed to handle e.g. the case of func() being a correlated subquery that selects the latest value.
- If we've removed a condition with subquery predicate, EXPLAIN [EXTENDED] won't show the subquery anymore

sql/item.cc:
  MWL#17: Table elimination
  - Add tem_field::check_column_usage_processor(). it allows to check which key parts a condition depends on.
sql/item.h:
  MWL#17: Table elimination
  - Add tem_field::check_column_usage_processor(). it allows to check which key parts a condition depends on.
sql/item_subselect.cc:
  MWL#17: Table elimination
  - Item_subselect got 'eliminated' attribute. It is used only to determine if the subselect should be printed by EXPLAIN.
  - Item_subselect got List<Item> refers_to - a list of item in the current select that are referred to from within the subselect.
  - Added Item_*::check_column_usage_processor(). it allows to check which key parts a condition depends on.
  - Added a comment about possible problem in Item_subselect::walk
sql/item_subselect.h:
  MWL#17: Table elimination
  - Item_subselect got 'eliminated' attribute. It is used only to determine if the subselect should be printed by EXPLAIN.
  - Item_subselect got List<Item> refers_to - a list of item in the current select that are referred to from within the subselect.
  - Added Item_*::check_column_usage_processor(). it allows to check which key parts a condition depends on.
sql/item_sum.cc:
  MWL#17: Table elimination
sql/sql_lex.cc:
  MWL#17: Table elimination
sql/sql_lex.h:
  MWL#17: Table elimination
sql/sql_select.h:
  MWL#17: Table elimination
parent 7a9f45c6
...@@ -1915,6 +1915,30 @@ void Item_field::reset_field(Field *f) ...@@ -1915,6 +1915,30 @@ void Item_field::reset_field(Field *f)
name= (char*) f->field_name; name= (char*) f->field_name;
} }
bool Item_field::check_column_usage_processor(uchar *arg)
{
Field_processor_info* info=(Field_processor_info*)arg;
if (used_tables() & ~info->allowed_tables)
return FALSE;
if (field->table == info->table)
{
if (!(field->part_of_key.is_set(info->keyno)))
return TRUE;
KEY *key= &field->table->key_info[info->keyno];
for (uint part= 0; part < key->key_parts; part++)
{
if (field->field_index == key->key_part[part].field->field_index)
{
info->needed_key_parts |= key_part_map(1) << part;
break;
}
}
}
return FALSE;
}
const char *Item_ident::full_name() const const char *Item_ident::full_name() const
{ {
char *tmp; char *tmp;
...@@ -3380,7 +3404,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, ...@@ -3380,7 +3404,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
/* store pointer on SELECT_LEX from which item is dependent */ /* store pointer on SELECT_LEX from which item is dependent */
if (mark_item) if (mark_item)
mark_item->depended_from= last; mark_item->depended_from= last;
current->mark_as_dependent(last); current->mark_as_dependent(last, resolved_item);
if (thd->lex->describe & DESCRIBE_EXTENDED) if (thd->lex->describe & DESCRIBE_EXTENDED)
{ {
char warn_buff[MYSQL_ERRMSG_SIZE]; char warn_buff[MYSQL_ERRMSG_SIZE];
......
...@@ -888,6 +888,8 @@ public: ...@@ -888,6 +888,8 @@ public:
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; } virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
virtual bool is_expensive_processor(uchar *arg) { return 0; } virtual bool is_expensive_processor(uchar *arg) { return 0; }
virtual bool register_field_in_read_map(uchar *arg) { return 0; } virtual bool register_field_in_read_map(uchar *arg) { return 0; }
virtual bool check_column_usage_processor(uchar *arg) { return 0; }
virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; }
/* /*
Check if a partition function is allowed Check if a partition function is allowed
SYNOPSIS SYNOPSIS
...@@ -1012,6 +1014,14 @@ public: ...@@ -1012,6 +1014,14 @@ public:
}; };
typedef struct
{
table_map allowed_tables;
TABLE *table;
uint keyno;
uint needed_key_parts;
} Field_processor_info;
class sp_head; class sp_head;
...@@ -1477,6 +1487,7 @@ public: ...@@ -1477,6 +1487,7 @@ public:
bool find_item_in_field_list_processor(uchar *arg); bool find_item_in_field_list_processor(uchar *arg);
bool register_field_in_read_map(uchar *arg); bool register_field_in_read_map(uchar *arg);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_column_usage_processor(uchar *arg);
void cleanup(); void cleanup();
bool result_as_longlong() bool result_as_longlong()
{ {
......
...@@ -39,7 +39,7 @@ inline Item * and_items(Item* cond, Item *item) ...@@ -39,7 +39,7 @@ inline Item * and_items(Item* cond, Item *item)
Item_subselect::Item_subselect(): Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), thd(0), substitution(0), Item_result_field(), value_assigned(0), thd(0), substitution(0),
engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0),
const_item_cache(1), engine_changed(0), changed(0), is_correlated(FALSE) const_item_cache(1), in_fix_fields(0), engine_changed(0), changed(0), is_correlated(FALSE)
{ {
with_subselect= 1; with_subselect= 1;
reset(); reset();
...@@ -151,10 +151,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) ...@@ -151,10 +151,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param)); engine->set_thd((thd= thd_param));
if (!in_fix_fields)
refers_to.empty();
eliminated= FALSE;
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE; return TRUE;
in_fix_fields++;
res= engine->prepare(); res= engine->prepare();
// all transformation is done (used by prepared statements) // all transformation is done (used by prepared statements)
...@@ -181,12 +185,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) ...@@ -181,12 +185,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (!(*ref)->fixed) if (!(*ref)->fixed)
ret= (*ref)->fix_fields(thd, ref); ret= (*ref)->fix_fields(thd, ref);
thd->where= save_where; thd->where= save_where;
in_fix_fields--;
return ret; return ret;
} }
// Is it one field subselect? // Is it one field subselect?
if (engine->cols() > max_columns) if (engine->cols() > max_columns)
{ {
my_error(ER_OPERAND_COLUMNS, MYF(0), 1); my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
in_fix_fields--;
return TRUE; return TRUE;
} }
fix_length_and_dec(); fix_length_and_dec();
...@@ -203,11 +209,30 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) ...@@ -203,11 +209,30 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
fixed= 1; fixed= 1;
err: err:
in_fix_fields--;
thd->where= save_where; thd->where= save_where;
return res; return res;
} }
bool Item_subselect::check_column_usage_processor(uchar *arg)
{
List_iterator<Item> it(refers_to);
Item *item;
while ((item= it++))
{
if (item->walk(&Item::check_column_usage_processor,FALSE, arg))
return TRUE;
}
return FALSE;
}
bool Item_subselect::mark_as_eliminated_processor(uchar *arg)
{
eliminated= TRUE;
return FALSE;
}
bool Item_subselect::walk(Item_processor processor, bool walk_subquery, bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
uchar *argument) uchar *argument)
{ {
...@@ -225,6 +250,7 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery, ...@@ -225,6 +250,7 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
if (lex->having && (lex->having)->walk(processor, walk_subquery, if (lex->having && (lex->having)->walk(processor, walk_subquery,
argument)) argument))
return 1; return 1;
/* TODO: why doesn't this walk the OUTER JOINs' ON expressions */
while ((item=li++)) while ((item=li++))
{ {
......
...@@ -52,8 +52,16 @@ protected: ...@@ -52,8 +52,16 @@ protected:
bool have_to_be_excluded; bool have_to_be_excluded;
/* cache of constant state */ /* cache of constant state */
bool const_item_cache; bool const_item_cache;
public: public:
/*
References from inside the subquery to the select that this predicate is
in. References to parent selects not included.
*/
List<Item> refers_to;
int in_fix_fields;
bool eliminated;
/* changed engine indicator */ /* changed engine indicator */
bool engine_changed; bool engine_changed;
/* subquery is transformed */ /* subquery is transformed */
...@@ -126,6 +134,8 @@ public: ...@@ -126,6 +134,8 @@ public:
virtual void reset_value_registration() {} virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; } enum_parsing_place place() { return parsing_place; }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
bool mark_as_eliminated_processor(uchar *arg);
bool check_column_usage_processor(uchar *arg);
/** /**
Get the SELECT_LEX structure associated with this Item. Get the SELECT_LEX structure associated with this Item.
......
...@@ -350,7 +350,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) ...@@ -350,7 +350,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl= sl->master_unit()->outer_select() ) sl= sl->master_unit()->outer_select() )
sl->master_unit()->item->with_sum_func= 1; sl->master_unit()->item->with_sum_func= 1;
} }
thd->lex->current_select->mark_as_dependent(aggr_sel); thd->lex->current_select->mark_as_dependent(aggr_sel, NULL);
return FALSE; return FALSE;
} }
......
...@@ -1778,7 +1778,7 @@ void st_select_lex_unit::exclude_tree() ...@@ -1778,7 +1778,7 @@ void st_select_lex_unit::exclude_tree()
'last' should be reachable from this st_select_lex_node 'last' should be reachable from this st_select_lex_node
*/ */
void st_select_lex::mark_as_dependent(st_select_lex *last) void st_select_lex::mark_as_dependent(st_select_lex *last, Item *dependency)
{ {
/* /*
Mark all selects from resolved to 1 before select where was Mark all selects from resolved to 1 before select where was
...@@ -1804,6 +1804,8 @@ void st_select_lex::mark_as_dependent(st_select_lex *last) ...@@ -1804,6 +1804,8 @@ void st_select_lex::mark_as_dependent(st_select_lex *last)
} }
is_correlated= TRUE; is_correlated= TRUE;
this->master_unit()->item->is_correlated= TRUE; this->master_unit()->item->is_correlated= TRUE;
if (dependency)
this->master_unit()->item->refers_to.push_back(dependency);
} }
bool st_select_lex_node::set_braces(bool value) { return 1; } bool st_select_lex_node::set_braces(bool value) { return 1; }
......
...@@ -743,7 +743,7 @@ public: ...@@ -743,7 +743,7 @@ public:
return master_unit()->return_after_parsing(); return master_unit()->return_after_parsing();
} }
void mark_as_dependent(st_select_lex *last); void mark_as_dependent(st_select_lex *last, Item *dependency);
bool set_braces(bool value); bool set_braces(bool value);
bool inc_in_sum_expr(); bool inc_in_sum_expr();
......
...@@ -2478,6 +2478,14 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select, ...@@ -2478,6 +2478,14 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
} }
typedef struct st_keyuse_w_needed_reg
{
KEYUSE *first;
key_part_map second;
} Keyuse_w_needed_reg;
static
bool has_eq_ref_access_candidate(TABLE *table, table_map can_refer_to_these) bool has_eq_ref_access_candidate(TABLE *table, table_map can_refer_to_these)
{ {
KEYUSE *keyuse= table->reginfo.join_tab->keyuse; KEYUSE *keyuse= table->reginfo.join_tab->keyuse;
...@@ -2494,24 +2502,85 @@ bool has_eq_ref_access_candidate(TABLE *table, table_map can_refer_to_these) ...@@ -2494,24 +2502,85 @@ bool has_eq_ref_access_candidate(TABLE *table, table_map can_refer_to_these)
{ {
uint key= keyuse->key; uint key= keyuse->key;
key_part_map bound_parts=0; key_part_map bound_parts=0;
bool ft_key= test(keyuse->keypart == FT_KEYPART); 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 */ do /* For each keypart and each way to read it */
{ {
if (!(keyuse->used_tables & ~can_refer_to_these) && if (keyuse->usable)
!(keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))
{ {
bound_parts |= keyuse->keypart_map; if(!(keyuse->used_tables & ~can_refer_to_these) &&
!(keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL))
{
bound_parts |= keyuse->keypart_map;
}
} }
else
n_unusable++;
keyuse++; keyuse++;
} while (keyuse->table && keyuse->key == key); } while (keyuse->table == table && keyuse->key == key);
if (ft_key || ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY))
!= HA_NOSAME))
{
continue;
}
KEY *keyinfo= table->key_info + key; if (bound_parts == PREV_BITS(key_part_map, keyinfo->key_parts))
if (!ft_key &&
((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) &&
bound_parts == PREV_BITS(key_part_map, keyinfo->key_parts))
{
return TRUE; return TRUE;
/*
Ok, usable keyuse elements didn't help us. Try making use of
unusable KEYUSEs (psergey-todo: sane comments:)
*/
if (n_unusable && bound_parts)
{
/*
Check if unusable KEYUSE elements cause all parts of key to be
bound. An unusable keyuse element makes a key part bound when it
represents the following:
keyXpartY=func(bound_columns, preceding_tables)
.
*/
Keyuse_w_needed_reg *uses;
if (!(uses= (Keyuse_w_needed_reg*)my_alloca(sizeof(Keyuse_w_needed_reg)*n_unusable)))
return FALSE;
uint n_uses=0;
for (KEYUSE *k= key_start; k!=keyuse; k++)
{
if (!k->usable && !(k->used_tables & ~can_refer_to_these))
{
//Walk k->val and check which key parts it depends on.
Field_processor_info fp= {can_refer_to_these, table, k->key, 0};
if (!k->val->walk(&Item::check_column_usage_processor, FALSE,
(uchar*)&fp))
{
uses[n_uses].first= k;
uses[n_uses].second= fp.needed_key_parts;
n_uses++;
}
}
}
/* Now compute transitive closure */
uint n_bounded;
do
{
n_bounded= 0;
for (uint i=0; i< n_uses; i++)
{
/* needed_parts is covered by what is already bound*/
if (!(uses[i].second & ~bound_parts))
{
bound_parts|= key_part_map(1) << uses[i].first->keypart;
n_bounded++;
}
if (bound_parts == PREV_BITS(key_part_map, keyinfo->key_parts))
return TRUE;
}
} while (n_bounded != 0);
} }
} }
} }
...@@ -2657,6 +2726,7 @@ eliminate_tables_for_join_list(JOIN *join, List<TABLE_LIST> *join_list, ...@@ -2657,6 +2726,7 @@ eliminate_tables_for_join_list(JOIN *join, List<TABLE_LIST> *join_list,
eliminated += tbl->nested_join->join_list.elements; eliminated += tbl->nested_join->join_list.elements;
//psergey-todo: do we need to do anything about removing the join //psergey-todo: do we need to do anything about removing the join
//nest? //nest?
tbl->on_expr->walk(&Item::mark_as_eliminated_processor, FALSE, NULL);
} }
else else
{ {
...@@ -2673,6 +2743,7 @@ eliminate_tables_for_join_list(JOIN *join, List<TABLE_LIST> *join_list, ...@@ -2673,6 +2743,7 @@ eliminate_tables_for_join_list(JOIN *join, List<TABLE_LIST> *join_list,
{ {
mark_table_as_eliminated(join, tbl->table, const_tbl_count, mark_table_as_eliminated(join, tbl->table, const_tbl_count,
const_tables); const_tables);
tbl->on_expr->walk(&Item::mark_as_eliminated_processor, FALSE, NULL);
eliminated += 1; eliminated += 1;
} }
} }
...@@ -3065,14 +3136,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -3065,14 +3136,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
{ {
start_keyuse=keyuse; start_keyuse=keyuse;
key=keyuse->key; key=keyuse->key;
s->keys.set_bit(key); // QQ: remove this ? if (keyuse->usable)
s->keys.set_bit(key); // QQ: remove this ?
refs=0; refs=0;
const_ref.clear_all(); const_ref.clear_all();
eq_part.clear_all(); eq_part.clear_all();
do do
{ {
if (keyuse->val->type() != Item::NULL_ITEM && !keyuse->optimize) if (keyuse->usable && 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);
...@@ -3276,6 +3349,7 @@ typedef struct key_field_t { ...@@ -3276,6 +3349,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;
} KEY_FIELD; } KEY_FIELD;
...@@ -3284,6 +3358,26 @@ typedef struct key_field_t { ...@@ -3284,6 +3358,26 @@ typedef struct key_field_t {
This is called for OR between different levels. This is called for OR between different levels.
That is, the function operates on an array of KEY_FIELD elements which has
two parts:
$LEFT_PART $RIGHT_PART
+-----------------------+-----------------------+
start new_fields end
$LEFT_PART and $RIGHT_PART are arrays that have KEY_FIELD elements for two
parts of the OR condition. Our task is to produce an array of KEY_FIELD
elements that would correspond to "$LEFT_PART OR $RIGHT_PART".
The rules for combining elements are as follows:
(keyfieldA1 AND keyfieldA2 AND ...) OR (keyfieldB1 AND keyfieldB2 AND ...)=
AND_ij (keyfieldA_i OR keyfieldB_j)
We discard all (keyfieldA_i OR keyfieldB_j) that refer to different
fields. For those referring to the same field, the logic is as follows:
t.keycol=
To be able to do 'ref_or_null' we merge a comparison of a column To be able to do 'ref_or_null' we merge a comparison of a column
and 'column IS NULL' to one test. This is useful for sub select queries and 'column IS NULL' to one test. This is useful for sub select queries
that are internally transformed to something like:. that are internally transformed to something like:.
...@@ -3348,13 +3442,18 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, ...@@ -3348,13 +3442,18 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
KEY_OPTIMIZE_REF_OR_NULL)); KEY_OPTIMIZE_REF_OR_NULL));
old->null_rejecting= (old->null_rejecting && old->null_rejecting= (old->null_rejecting &&
new_fields->null_rejecting); new_fields->null_rejecting);
/*
The conditions are the same, hence their usabilities should
be, too (TODO: shouldn't that apply to the above
null_rejecting and optimize attributes?)
*/
DBUG_ASSERT(old->usable == new_fields->usable);
} }
} }
else if (old->eq_func && new_fields->eq_func && else if (old->eq_func && new_fields->eq_func &&
old->val->eq_by_collation(new_fields->val, old->val->eq_by_collation(new_fields->val,
old->field->binary(), old->field->binary(),
old->field->charset())) old->field->charset()))
{ {
old->level= and_level; old->level= and_level;
old->optimize= ((old->optimize & new_fields->optimize & old->optimize= ((old->optimize & new_fields->optimize &
...@@ -3363,10 +3462,14 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, ...@@ -3363,10 +3462,14 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
KEY_OPTIMIZE_REF_OR_NULL)); KEY_OPTIMIZE_REF_OR_NULL));
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
DBUG_ASSERT(old->usable && new_fields->usable);
} }
else if (old->eq_func && new_fields->eq_func && else if (old->eq_func && new_fields->eq_func &&
((old->val->const_item() && old->val->is_null()) || ((new_fields->usable && old->val->const_item() &&
new_fields->val->is_null())) old->val->is_null()) ||
((old->usable && new_fields->val->is_null()))))
/* TODO ^ why is the above asymmetric, why const_item()? */
{ {
/* field = expression OR field IS NULL */ /* field = expression OR field IS NULL */
old->level= and_level; old->level= and_level;
...@@ -3437,6 +3540,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3437,6 +3540,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
table_map usable_tables, SARGABLE_PARAM **sargables) table_map usable_tables, SARGABLE_PARAM **sargables)
{ {
uint exists_optimize= 0; uint exists_optimize= 0;
bool optimizable=0;
if (!(field->flags & PART_KEY_FLAG)) if (!(field->flags & PART_KEY_FLAG))
{ {
// Don't remove column IS NULL on a LEFT JOIN table // Don't remove column IS NULL on a LEFT JOIN table
...@@ -3449,15 +3553,15 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3449,15 +3553,15 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
else else
{ {
table_map used_tables=0; table_map used_tables=0;
bool optimizable=0;
for (uint i=0; i<num_values; i++) for (uint i=0; i<num_values; i++)
{ {
used_tables|=(value[i])->used_tables(); used_tables|=(value[i])->used_tables();
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;
} }
if (!optimizable) // psergey-tbl-elim:
return; // 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 ||
...@@ -3470,7 +3574,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3470,7 +3574,8 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
JOIN_TAB *stat=field->table->reginfo.join_tab; JOIN_TAB *stat=field->table->reginfo.join_tab;
key_map possible_keys=field->key_start; key_map possible_keys=field->key_start;
possible_keys.intersect(field->table->keys_in_use_for_query); possible_keys.intersect(field->table->keys_in_use_for_query);
stat[0].keys.merge(possible_keys); // Add possible keys if (optimizable)
stat[0].keys.merge(possible_keys); // Add possible keys
/* /*
Save the following cases: Save the following cases:
...@@ -3563,6 +3668,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3563,6 +3668,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;
/* /*
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
...@@ -3874,6 +3980,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) ...@@ -3874,6 +3980,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;
keyuse.usable= key_field->usable;
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse)); VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
} }
} }
...@@ -3954,6 +4061,11 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) ...@@ -3954,6 +4061,11 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
return (int) (a->key - b->key); return (int) (a->key - b->key);
if (a->keypart != b->keypart) if (a->keypart != b->keypart)
return (int) (a->keypart - b->keypart); return (int) (a->keypart - b->keypart);
// Usable ones go before the unusable
if (a->usable != b->usable)
return (int)a->usable - (int)b->usable;
// 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)) -
test((b->used_tables & ~OUTER_REF_TABLE_BIT)))) test((b->used_tables & ~OUTER_REF_TABLE_BIT))))
...@@ -4164,7 +4276,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -4164,7 +4276,8 @@ 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->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL) if (use->usable && !use->used_tables &&
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)
{ {
...@@ -4188,7 +4301,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -4188,7 +4301,8 @@ 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;
use->table->reginfo.join_tab->checked_keys.set_bit(use->key); if (use->usable)
use->table->reginfo.join_tab->checked_keys.set_bit(use->key);
save_pos++; save_pos++;
} }
i=(uint) (save_pos-(KEYUSE*) keyuse->buffer); i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
...@@ -4218,7 +4332,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) ...@@ -4218,7 +4332,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->used_tables & if (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)))
{ {
...@@ -4411,7 +4525,8 @@ best_access_path(JOIN *join, ...@@ -4411,7 +4525,8 @@ 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 (!(remaining_tables & keyuse->used_tables) && if (keyuse->usable &&
!(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)))
{ {
...@@ -5915,9 +6030,11 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5915,9 +6030,11 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
uint i; uint i;
for (i=0 ; i < keyparts ; keyuse++,i++) for (i=0 ; i < keyparts ; keyuse++,i++)
{ {
while (keyuse->keypart != i || while (keyuse->keypart != i || ((~used_tables) & keyuse->used_tables) ||
((~used_tables) & keyuse->used_tables)) !keyuse->usable)
{
keyuse++; /* Skip other parts */ keyuse++; /* Skip other parts */
}
uint maybe_null= test(keyinfo->key_part[i].null_bit); uint maybe_null= test(keyinfo->key_part[i].null_bit);
j->ref.items[i]=keyuse->val; // Save for cond removal j->ref.items[i]=keyuse->val; // Save for cond removal
...@@ -16853,8 +16970,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -16853,8 +16970,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
unit; unit;
unit= unit->next_unit()) unit= unit->next_unit())
{ {
if (mysql_explain_union(thd, unit, result)) if (!(unit->item && unit->item->eliminated))
DBUG_VOID_RETURN; {
if (mysql_explain_union(thd, unit, result))
DBUG_VOID_RETURN;
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -51,6 +51,7 @@ typedef struct keyuse_t { ...@@ -51,6 +51,7 @@ typedef struct keyuse_t {
NULL - Otherwise (the source equality can't be turned off) NULL - Otherwise (the source equality can't be turned off)
*/ */
bool *cond_guard; bool *cond_guard;
bool usable;
} KEYUSE; } KEYUSE;
class store_key; class store_key;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment