Commit b032c7d8 authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#17: Table elimination

- Continue addressing review feedback: remove "unusable KEYUSEs" 
  extension as it is no longer needed.

sql/item.h:
  MWL#17: Table elimination
  - Code cleanup
sql/opt_table_elimination.cc:
  MWL#17: Table elimination
  - Code cleanup
parent 854bb82b
...@@ -1017,18 +1017,6 @@ public: ...@@ -1017,18 +1017,6 @@ public:
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
}; };
#if 0
typedef struct
{
TABLE *table; /* Table of interest */
uint keyno; /* Index of interest */
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;
} Field_processor_info;
#endif
/* Data for Item::check_column_usage_processor */ /* Data for Item::check_column_usage_processor */
class Field_enumerator class Field_enumerator
......
...@@ -1119,7 +1119,6 @@ void eliminate_tables(JOIN *join) ...@@ -1119,7 +1119,6 @@ void eliminate_tables(JOIN *join)
case Func_dep::FD_OUTER_JOIN: case Func_dep::FD_OUTER_JOIN:
{ {
Outer_join_dep *outer_join_dep= (Outer_join_dep*)bound_dep; Outer_join_dep *outer_join_dep= (Outer_join_dep*)bound_dep;
/* TODO what do here? Stop if eliminated the top-level? */
mark_as_eliminated(te.join, outer_join_dep->table_list); mark_as_eliminated(te.join, outer_join_dep->table_list);
Outer_join_dep *parent= outer_join_dep->parent; Outer_join_dep *parent= outer_join_dep->parent;
if (parent && if (parent &&
...@@ -1236,38 +1235,6 @@ void dbug_print_deps(Table_elimination *te) ...@@ -1236,38 +1235,6 @@ void dbug_print_deps(Table_elimination *te)
#endif #endif
/***********************************************************************************************/
#if 0
static void dbug_print_fdep(FUNC_DEP *fd)
{
switch (fd->type) {
case FUNC_DEP::FD_OUTER_JOIN:
{
fprintf(DBUG_FILE, "outer_join(");
if (fd->table_list->nested_join)
{
bool first= TRUE;
List_iterator<TABLE_LIST> it(fd->table_list->nested_join->join_list);
TABLE_LIST *tbl;
while ((tbl= it++))
{
fprintf(DBUG_FILE, "%s%s", first?"":" ",
tbl->table? tbl->table->alias : "...");
first= FALSE;
}
fprintf(DBUG_FILE, ")");
}
else
fprintf(DBUG_FILE, "%s", fd->table_list->table->alias);
fprintf(DBUG_FILE, ")");
break;
}
}
}
#endif
/** /**
@} (end of group Table_Elimination) @} (end of group Table_Elimination)
*/ */
......
...@@ -2474,7 +2474,6 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select, ...@@ -2474,7 +2474,6 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR); /* This shouldn't happend */ DBUG_RETURN(HA_POS_ERROR); /* This shouldn't happend */
} }
/* /*
This structure is used to collect info on potentially sargable This structure is used to collect info on potentially sargable
predicates in order to check whether they become sargable after predicates in order to check whether they become sargable after
...@@ -2762,16 +2761,14 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2762,16 +2761,14 @@ 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->type == KEYUSE_USABLE) s->keys.set_bit(key); // QQ: remove this ?
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->type == KEYUSE_USABLE && if (keyuse->val->type() != Item::NULL_ITEM && !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,9 +2968,11 @@ typedef struct key_field_t { ...@@ -2971,9 +2968,11 @@ 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 */
enum keyuse_type type; /* See KEYUSE::type */
} KEY_FIELD; } KEY_FIELD;
/* Values in optimize */
#define KEY_OPTIMIZE_EXISTS 1
#define KEY_OPTIMIZE_REF_OR_NULL 2
/** /**
Merge new key definitions to old ones, remove those not used in both. Merge new key definitions to old ones, remove those not used in both.
...@@ -3064,18 +3063,13 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, ...@@ -3064,18 +3063,13 @@ 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->type == new_fields->type);
} }
} }
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 &
...@@ -3084,15 +3078,10 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, ...@@ -3084,15 +3078,10 @@ 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->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->type == KEYUSE_USABLE && ((old->val->const_item() && old->val->is_null()) ||
old->val->const_item() && old->val->is_null()) || new_fields->val->is_null()))
((old->type == KEYUSE_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;
...@@ -3163,7 +3152,6 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3163,7 +3152,6 @@ 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
...@@ -3176,12 +3164,15 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3176,12 +3164,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)
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 ||
...@@ -3194,8 +3185,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3194,8 +3185,7 @@ 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);
if (optimizable) stat[0].keys.merge(possible_keys); // Add possible keys
stat[0].keys.merge(possible_keys); // Add possible keys
/* /*
Save the following cases: Save the following cases:
...@@ -3288,7 +3278,6 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, ...@@ -3288,7 +3278,6 @@ 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)->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
...@@ -3600,7 +3589,6 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) ...@@ -3600,7 +3589,6 @@ 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.type= key_field->type;
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse)); VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
} }
} }
...@@ -3609,6 +3597,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) ...@@ -3609,6 +3597,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
} }
#define FT_KEYPART (MAX_REF_PARTS+10)
static void static void
add_ft_keys(DYNAMIC_ARRAY *keyuse_array, add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
...@@ -3667,7 +3656,6 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, ...@@ -3667,7 +3656,6 @@ 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.type= KEYUSE_USABLE;
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse)); VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
} }
...@@ -3682,13 +3670,6 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) ...@@ -3682,13 +3670,6 @@ 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
int a_ok= test(a->type == KEYUSE_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)) -
test((b->used_tables & ~OUTER_REF_TABLE_BIT)))) test((b->used_tables & ~OUTER_REF_TABLE_BIT))))
...@@ -3899,8 +3880,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -3899,8 +3880,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->type == KEYUSE_USABLE && !use->used_tables && if (!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)
{ {
...@@ -3924,8 +3904,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -3924,8 +3904,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->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++;
} }
i=(uint) (save_pos-(KEYUSE*) keyuse->buffer); i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
...@@ -3955,7 +3934,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) ...@@ -3955,7 +3934,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->type == KEYUSE_USABLE && keyuse->used_tables & if (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)))
{ {
...@@ -4147,8 +4126,7 @@ best_access_path(JOIN *join, ...@@ -4147,8 +4126,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->type == KEYUSE_USABLE && if (!(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)))
{ {
...@@ -5602,8 +5580,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5602,8 +5580,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
*/ */
do do
{ {
if (!(~used_tables & keyuse->used_tables) && 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))
...@@ -5653,11 +5630,9 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5653,11 +5630,9 @@ 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 || ((~used_tables) & keyuse->used_tables) || while (keyuse->keypart != i ||
!(keyuse->type == KEYUSE_USABLE)) ((~used_tables) & keyuse->used_tables))
{
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
......
...@@ -28,45 +28,6 @@ ...@@ -28,45 +28,6 @@
#include "procedure.h" #include "procedure.h"
#include <myisam.h> #include <myisam.h>
#define FT_KEYPART (MAX_REF_PARTS+10)
/* Values in optimize */
#define KEY_OPTIMIZE_EXISTS 1
#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 */
...@@ -90,15 +51,6 @@ typedef struct keyuse_t { ...@@ -90,15 +51,6 @@ 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;
/*
1 <=> This keyuse can be used to construct key access.
0 <=> Otherwise. Currently unusable KEYUSEs represent equalities
where one table column refers to another one, like this:
t.keyXpartA=func(t.keyXpartB)
This equality cannot be used for index access but is useful
for table elimination.
*/
enum keyuse_type type;
} KEYUSE; } KEYUSE;
class store_key; class store_key;
...@@ -258,7 +210,7 @@ typedef struct st_join_table { ...@@ -258,7 +210,7 @@ typedef struct st_join_table {
JOIN *join; JOIN *join;
/** Bitmap of nested joins this table is part of */ /** Bitmap of nested joins this table is part of */
nested_join_map embedding_map; nested_join_map embedding_map;
void cleanup(); void cleanup();
inline bool is_using_loose_index_scan() inline bool is_using_loose_index_scan()
{ {
......
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