Commit 9fa1bce4 authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#17: Table elimination

mysql-test/r/table_elim.result:
  MWL#17: Table elimination
  - More tests
mysql-test/t/table_elim.test:
  MWL#17: Table elimination
  - More tests
sql/opt_table_elimination.cc:
  MWL#17: Table elimination
  - Code cleanup
sql/sql_select.cc:
  MWL#17: Table elimination
  - Code cleanup
sql/sql_select.h:
  MWL#17: Table elimination
  - Code cleanup
sql/table.h:
  MWL#17: Table elimination
  - Code cleanup
parent d764108a
...@@ -172,3 +172,33 @@ Note 1276 Field or reference 'test.F.id' of SELECT #3 was resolved in SELECT #1 ...@@ -172,3 +172,33 @@ Note 1276 Field or reference 'test.F.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t2` `A2` where ((`F`.`id` = `A2`.`id`) and (`A2`.`attr2` between 12 and 14) and (`A2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `F`.`id`)))) Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t2` `A2` where ((`F`.`id` = `A2`.`id`) and (`A2`.`attr2` between 12 and 14) and (`A2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `F`.`id`))))
drop view v1, v2; drop view v1, v2;
drop table t0, t1, t2; drop table t0, t1, t2;
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t2 (pk1 int, pk2 int, pk3 int, col int, primary key(pk1, pk2, pk3));
insert into t2 select a,a,a,a from t1;
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk2=t2.pk1+1 and
t2.pk3=t2.pk2+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
This must use both:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+t2.col;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk2=t1.a and
t2.pk1=t2.pk2+1 and
t2.pk3=t2.pk1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
drop table t1, t2;
...@@ -125,3 +125,36 @@ explain extended select id from v2 where attr2 between 12 and 14; ...@@ -125,3 +125,36 @@ explain extended select id from v2 where attr2 between 12 and 14;
drop view v1, v2; drop view v1, v2;
drop table t0, t1, t2; drop table t0, t1, t2;
#
# Tests for the code that uses t.keypartX=func(t.keypartY) equalities to
# make table elimination inferences
#
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t2 (pk1 int, pk2 int, pk3 int, col int, primary key(pk1, pk2, pk3));
insert into t2 select a,a,a,a from t1;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk2=t2.pk1+1 and
t2.pk3=t2.pk2+1;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+1;
--echo This must use both:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+t2.col;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk2=t1.a and
t2.pk1=t2.pk2+1 and
t2.pk3=t2.pk1;
drop table t1, t2;
This diff is collapsed.
...@@ -2653,12 +2653,13 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2653,12 +2653,13 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
~outer_join, join->select_lex, &sargables)) ~outer_join, join->select_lex, &sargables))
goto error; goto error;
/* Read tables with 0 or 1 rows (system tables) */
join->const_table_map= 0; join->const_table_map= 0;
join->const_tables= const_count;
eliminate_tables(join, &const_count, &found_const_table_map); eliminate_tables(join);
join->const_table_map= found_const_table_map; const_count= join->const_tables;
found_const_table_map= join->const_table_map;
/* Read tables with 0 or 1 rows (system tables) */
for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count; for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count;
p_pos < p_end ; p_pos < p_end ;
p_pos++) p_pos++)
...@@ -2761,7 +2762,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2761,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) if (keyuse->usable == 1)
s->keys.set_bit(key); // QQ: remove this ? s->keys.set_bit(key); // QQ: remove this ?
refs=0; refs=0;
...@@ -2769,7 +2770,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2769,7 +2770,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
eq_part.clear_all(); eq_part.clear_all();
do do
{ {
if (keyuse->usable && keyuse->val->type() != Item::NULL_ITEM && if (keyuse->usable==1 && keyuse->val->type() != Item::NULL_ITEM &&
!keyuse->optimize) !keyuse->optimize)
{ {
if (!((~found_const_table_map) & keyuse->used_tables)) if (!((~found_const_table_map) & keyuse->used_tables))
...@@ -3601,7 +3602,12 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) ...@@ -3601,7 +3602,12 @@ 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; if (!(keyuse.usable= key_field->usable))
{
/* 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));
} }
} }
...@@ -3668,7 +3674,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, ...@@ -3668,7 +3674,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= TRUE; keyuse.usable= 1;
VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse)); VOID(insert_dynamic(keyuse_array,(uchar*) &keyuse));
} }
...@@ -3686,7 +3692,7 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) ...@@ -3686,7 +3692,7 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
// Usable ones go before the unusable // Usable ones go before the unusable
if (a->usable != b->usable) if (a->usable != b->usable)
return (int)a->usable - (int)b->usable; return (int)b->usable - (int)a->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)) -
...@@ -3898,7 +3904,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -3898,7 +3904,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 && !use->used_tables && if (use->usable == 1 && !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)
...@@ -3923,7 +3929,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, ...@@ -3923,7 +3929,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) if (use->usable == 1)
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++;
} }
...@@ -3954,7 +3960,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) ...@@ -3954,7 +3960,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 && keyuse->used_tables & if (keyuse->usable == 1 && 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)))
{ {
...@@ -4146,7 +4152,7 @@ best_access_path(JOIN *join, ...@@ -4146,7 +4152,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 && if (keyuse->usable == 1&&
!(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)))
...@@ -5601,7 +5607,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5601,7 +5607,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->usable == 1)
{ {
if (keyparts == keyuse->keypart && if (keyparts == keyuse->keypart &&
!(found_part_ref_or_null & keyuse->optimize)) !(found_part_ref_or_null & keyuse->optimize))
...@@ -5652,7 +5658,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5652,7 +5658,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) !(keyuse->usable == 1))
{ {
keyuse++; /* Skip other parts */ keyuse++; /* Skip other parts */
} }
...@@ -8985,6 +8991,20 @@ static void restore_prev_nj_state(JOIN_TAB *last) ...@@ -8985,6 +8991,20 @@ static void restore_prev_nj_state(JOIN_TAB *last)
JOIN *join= last->join; JOIN *join= last->join;
while (last_emb) while (last_emb)
{ {
/*
psergey-elim: (nevermind)
new_prefix= cur_prefix & ~last;
if (!(new_prefix & cur_table_map)) // removed last inner table
{
join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
}
else (current)
{
// Won't hurt doing it all the time:
join->cur_embedding_map |= ...;
}
else
*/
if (!(--last_emb->nested_join->counter)) if (!(--last_emb->nested_join->counter))
join->cur_embedding_map&= ~last_emb->nested_join->nj_map; join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
else if (last_emb->nested_join->n_tables-1 == else if (last_emb->nested_join->n_tables-1 ==
...@@ -16685,13 +16705,13 @@ static void print_join(THD *thd, ...@@ -16685,13 +16705,13 @@ static void print_join(THD *thd,
DBUG_ASSERT(tables->elements >= 1); DBUG_ASSERT(tables->elements >= 1);
/* /*
Assert that the first table in the list isn't eliminated (if it was we Assert that the first table in the list isn't eliminated. This comes from
would have skipped the entire join nest) 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;
......
...@@ -57,14 +57,14 @@ typedef struct keyuse_t { ...@@ -57,14 +57,14 @@ typedef struct keyuse_t {
*/ */
bool *cond_guard; bool *cond_guard;
/* /*
TRUE <=> This keyuse can be used to construct key access. 1 <=> This keyuse can be used to construct key access.
FALSE <=> Otherwise. Currently unusable KEYUSEs represent equalities 0 <=> Otherwise. Currently unusable KEYUSEs represent equalities
where one table column refers to another one, like this: where one table column refers to another one, like this:
t.keyXpartA=func(t.keyXpartB) t.keyXpartA=func(t.keyXpartB)
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.
*/ */
bool usable; int usable;
} KEYUSE; } KEYUSE;
class store_key; class store_key;
...@@ -299,7 +299,12 @@ class JOIN :public Sql_alloc ...@@ -299,7 +299,12 @@ class JOIN :public Sql_alloc
fetching data from a cursor fetching data from a cursor
*/ */
bool resume_nested_loop; bool resume_nested_loop;
table_map const_table_map,found_const_table_map; table_map const_table_map;
/*
Constant tables for which we have found a row (as opposed to those for
which we didn't).
*/
table_map found_const_table_map;
/* Tables removed by table elimination. Set to 0 before the elimination. */ /* Tables removed by table elimination. Set to 0 before the elimination. */
table_map eliminated_tables; table_map eliminated_tables;
...@@ -548,6 +553,10 @@ class JOIN :public Sql_alloc ...@@ -548,6 +553,10 @@ class JOIN :public Sql_alloc
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 || return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
select_lex == unit->fake_select_lex)); select_lex == unit->fake_select_lex));
} }
inline table_map all_tables_map()
{
return (table_map(1) << tables) - 1;
}
private: private:
bool make_simple_join(JOIN *join, TABLE *tmp_table); bool make_simple_join(JOIN *join, TABLE *tmp_table);
}; };
...@@ -755,6 +764,5 @@ inline bool optimizer_flag(THD *thd, uint flag) ...@@ -755,6 +764,5 @@ inline bool optimizer_flag(THD *thd, uint flag)
return (thd->variables.optimizer_switch & flag); return (thd->variables.optimizer_switch & flag);
} }
void eliminate_tables(JOIN *join, uint *const_tbl_count, void eliminate_tables(JOIN *join);
table_map *const_tables);
...@@ -1616,7 +1616,10 @@ class Field_iterator_table_ref: public Field_iterator ...@@ -1616,7 +1616,10 @@ class Field_iterator_table_ref: public Field_iterator
typedef struct st_nested_join 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 */
table_map used_tables; /* bitmap of tables in the nested join */ /*
Bitmap of tables within this nested join (including those embedded within
its children). Eliminated tables are still in the bitmap */
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 */
/* /*
...@@ -1625,6 +1628,8 @@ typedef struct st_nested_join ...@@ -1625,6 +1628,8 @@ 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 */ /* Tables left after elimination */
......
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