Commit 3f595889 authored by Sergey Petrunya's avatar Sergey Petrunya

Subqueries: Inside-out execution for non-semijoin materialized subqueries that...

Subqueries: Inside-out execution for non-semijoin materialized subqueries that are AND-parts of the WHERE
- Code cleanup
- Query plan change is due to s/ha_rows JOIN_TAB::read_time/double JOIN_TAB::read_time/
parent ed8aa986
...@@ -122,11 +122,11 @@ a1 a2 ...@@ -122,11 +122,11 @@ a1 a2
explain extended explain extended
select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY SUBQUERY#2 ALL distinct_key # # # 3 100.00 # 1 PRIMARY t1i index it1i1,it1i2,it1i3 # # # 3 100.00 #
1 PRIMARY t1i ref it1i1,it1i2,it1i3 # # # 1 100.00 # 1 PRIMARY SUBQUERY#2 eq_ref distinct_key # # # 1 100.00 #
2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # 2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 #
Warnings: Warnings:
Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1` AS `b1`,`test`.`t2i`.`b2` AS `b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`,`test`.`t2i`.`b2`) join `test`.`t1i` where ((`test`.`t1i`.`a2` = `SUBQUERY#2`.`b2`) and (`test`.`t1i`.`a1` = `SUBQUERY#2`.`b1`)) Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1` AS `b1`,`test`.`t2i`.`b2` AS `b2` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`,`test`.`t2i`.`b2`) join `test`.`t1i` where ((`SUBQUERY#2`.`b2` = `test`.`t1i`.`a2`) and (`SUBQUERY#2`.`b1` = `test`.`t1i`.`a1`))
select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2); select * from t1i where (a1, a2) in (select b1, b2 from t2i where b1 > '0' group by b1, b2);
a1 a2 a1 a2
1 - 01 2 - 01 1 - 01 2 - 01
...@@ -134,11 +134,11 @@ a1 a2 ...@@ -134,11 +134,11 @@ a1 a2
explain extended explain extended
select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY SUBQUERY#2 ALL distinct_key # # # 3 100.00 # 1 PRIMARY t1i index it1i1,it1i2,it1i3 # # # 3 100.00 #
1 PRIMARY t1i ref it1i1,it1i2,it1i3 # # # 1 100.00 # 1 PRIMARY SUBQUERY#2 eq_ref distinct_key # # # 1 100.00 #
2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 # 2 SUBQUERY t2i range it2i1,it2i3 # # # 3 100.00 #
Warnings: Warnings:
Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1` AS `b1`,min(`test`.`t2i`.`b2`) AS `min(b2)` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`) join `test`.`t1i` where ((`test`.`t1i`.`a2` = `SUBQUERY#2`.`min(b2)`) and (`test`.`t1i`.`a1` = `SUBQUERY#2`.`b1`)) Note 1003 select `test`.`t1i`.`a1` AS `a1`,`test`.`t1i`.`a2` AS `a2` from <materialize> (select `test`.`t2i`.`b1` AS `b1`,min(`test`.`t2i`.`b2`) AS `min(b2)` from `test`.`t2i` where (`test`.`t2i`.`b1` > '0') group by `test`.`t2i`.`b1`) join `test`.`t1i` where ((`SUBQUERY#2`.`min(b2)` = `test`.`t1i`.`a2`) and (`SUBQUERY#2`.`b1` = `test`.`t1i`.`a1`))
select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1); select * from t1i where (a1, a2) in (select b1, min(b2) from t2i where b1 > '0' group by b1);
a1 a2 a1 a2
1 - 01 2 - 01 1 - 01 2 - 01
......
...@@ -190,10 +190,6 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) ...@@ -190,10 +190,6 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
changed= 1; changed= 1;
inside_first_fix_fields= FALSE; inside_first_fix_fields= FALSE;
// all transformation is done (used by prepared statements)
changed= 1;
/* /*
Substitute the current item with an Item_in_optimizer that was Substitute the current item with an Item_in_optimizer that was
created by Item_in_subselect::select_in_like_transformer and created by Item_in_subselect::select_in_like_transformer and
......
...@@ -393,12 +393,12 @@ class Item_in_subselect :public Item_exists_subselect ...@@ -393,12 +393,12 @@ class Item_in_subselect :public Item_exists_subselect
join->sj_subselects. join->sj_subselects.
jtbm-todo: option 1: let sj_subselects list pairs. jtbm-todo: option 1: let sj_subselects list pairs.
*/ */
bool convert_to_semi_join; bool is_flattenable_semijoin;
/* /*
Cost to populate the temporary table (set on if-needed basis). Cost to populate the temporary table (set on if-needed basis).
*/ */
double startup_cost; //double startup_cost;
bool *get_cond_guard(int i) bool *get_cond_guard(int i)
{ {
......
...@@ -2285,7 +2285,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, ...@@ -2285,7 +2285,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
quick=0; quick=0;
needed_reg.clear_all(); needed_reg.clear_all();
quick_keys.clear_all(); quick_keys.clear_all();
if (keys_to_use.is_clear_all() || head->pos_in_table_list->jtbm_subselect) DBUG_ASSERT(!head->is_filled_at_execution());
if (keys_to_use.is_clear_all() || head->is_filled_at_execution())
DBUG_RETURN(0); DBUG_RETURN(0);
records= head->file->stats.records; records= head->file->stats.records;
if (!records) if (!records)
......
...@@ -172,7 +172,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -172,7 +172,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
(void)subquery_types_allow_materialization(in_subs); (void)subquery_types_allow_materialization(in_subs);
in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest;
in_subs->convert_to_semi_join= TRUE; //JTBM in_subs->is_flattenable_semijoin= TRUE;
/* Register the subquery for further processing in flatten_subqueries() */ /* Register the subquery for further processing in flatten_subqueries() */
select_lex-> select_lex->
...@@ -240,7 +240,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) ...@@ -240,7 +240,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
thd->thd_marker.emb_on_expr_nest == (TABLE_LIST*)0x1) thd->thd_marker.emb_on_expr_nest == (TABLE_LIST*)0x1)
{ {
in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest;
in_subs->convert_to_semi_join= FALSE; in_subs->is_flattenable_semijoin= FALSE;
select_lex->outer_select()-> select_lex->outer_select()->
join->sj_subselects.append(thd->mem_root, in_subs); join->sj_subselects.append(thd->mem_root, in_subs);
} }
...@@ -358,7 +358,19 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) ...@@ -358,7 +358,19 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
} }
static bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item) /*
Finalize IN->EXISTS conversion in case we couldn't use materialization.
DESCRIPTION Invoke the IN->EXISTS converter
Replace the Item_in_subselect with its wrapper Item_in_optimizer in WHERE.
RETURN
FALSE - Ok
TRUE - Fatal error
*/
static
bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *item)
{ {
DBUG_ENTER("make_in_exists_conversion"); DBUG_ENTER("make_in_exists_conversion");
JOIN *child_join= item->unit->first_select()->join; JOIN *child_join= item->unit->first_select()->join;
...@@ -381,20 +393,15 @@ static bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *i ...@@ -381,20 +393,15 @@ static bool make_in_exists_conversion(THD *thd, JOIN *join, Item_in_subselect *i
Item *substitute= item->substitution; Item *substitute= item->substitution;
bool do_fix_fields= !item->substitution->fixed; bool do_fix_fields= !item->substitution->fixed;
Item **tree= (item->emb_on_expr_nest == (TABLE_LIST*)1)?
&join->conds : &(item->emb_on_expr_nest->on_expr);
Item *replace_me= item;
/* /*
JTBM: the subquery was already mapped with Item_in_optimizer, so we The Item_subselect has already been wrapped with Item_in_optimizer, so we
should search for that, not for original Item_in_subselect. should search for item->optimizer, not 'item'.
TODO: what about delaying that rewrite until here?
*/ */
if (!item->convert_to_semi_join) Item *replace_me= item->optimizer;
{ //psergey-jtbm-fix: this branch is always taken?? DBUG_ASSERT(replace_me==substitute);
replace_me= item->optimizer;
}
Item **tree= (item->emb_on_expr_nest == (TABLE_LIST*)1)?
&join->conds : &(item->emb_on_expr_nest->on_expr);
if (replace_where_subcondition(join, tree, replace_me, substitute, if (replace_where_subcondition(join, tree, replace_me, substitute,
do_fix_fields)) do_fix_fields))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -528,7 +535,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) ...@@ -528,7 +535,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
in_subq++) in_subq++)
{ {
bool remove_item= TRUE; bool remove_item= TRUE;
if ((*in_subq)->convert_to_semi_join) if ((*in_subq)->is_flattenable_semijoin)
{ {
if (convert_subq_to_sj(join, *in_subq)) if (convert_subq_to_sj(join, *in_subq))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -548,7 +555,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) ...@@ -548,7 +555,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
should search for that, not for original Item_in_subselect. should search for that, not for original Item_in_subselect.
TODO: what about delaying that rewrite until here? TODO: what about delaying that rewrite until here?
*/ */
if (!(*in_subq)->convert_to_semi_join) if (!(*in_subq)->is_flattenable_semijoin)
{ {
replace_me= (*in_subq)->optimizer; replace_me= (*in_subq)->optimizer;
} }
...@@ -593,7 +600,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) ...@@ -593,7 +600,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
should search for that, not for original Item_in_subselect. should search for that, not for original Item_in_subselect.
TODO: what about delaying that rewrite until here? TODO: what about delaying that rewrite until here?
*/ */
if (!(*in_subq)->convert_to_semi_join) if (!(*in_subq)->is_flattenable_semijoin)
{ {
replace_me= (*in_subq)->optimizer; replace_me= (*in_subq)->optimizer;
} }
...@@ -622,9 +629,33 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) ...@@ -622,9 +629,33 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
} }
void get_temptable_params(Item_in_subselect *item, ha_rows *out_rows, /*
ha_rows *scan_time) Get #output_rows and scan_time estimates for a "delayed" table.
SYNOPSIS
get_delayed_table_estimates()
table IN Table to get estimates for
out_rows OUT E(#rows in the table)
scan_time OUT E(scan_time).
startup_cost OUT cost to populate the table.
DESCRIPTION
Get #output_rows and scan_time estimates for a "delayed" table. By
"delayed" here we mean that the table is filled at the start of query
execution. This means that the optimizer can't use table statistics to
get #rows estimate for it, it has to call this function instead.
This function is expected to make different actions depending on the nature
of the table. At the moment there is only one kind of delayed tables,
non-flattenable semi-joins.
*/
void get_delayed_table_estimates(TABLE *table,
ha_rows *out_rows,
double *scan_time,
double *startup_cost)
{ {
Item_in_subselect *item= table->pos_in_table_list->jtbm_subselect;
item->optimize(); item->optimize();
DBUG_ASSERT(item->engine->engine_type() == DBUG_ASSERT(item->engine->engine_type() ==
...@@ -644,7 +675,7 @@ void get_temptable_params(Item_in_subselect *item, ha_rows *out_rows, ...@@ -644,7 +675,7 @@ void get_temptable_params(Item_in_subselect *item, ha_rows *out_rows,
read_time += join->best_positions[i].read_time; read_time += join->best_positions[i].read_time;
} }
*out_rows= rows; *out_rows= rows;
item->startup_cost= read_time; *startup_cost= read_time;
/* Calculate cost of scanning the temptable */ /* Calculate cost of scanning the temptable */
double data_size= rows * hash_sj_engine->tmp_table->s->reclength; double data_size= rows * hash_sj_engine->tmp_table->s->reclength;
/* Do like in handler::read_time */ /* Do like in handler::read_time */
......
...@@ -367,8 +367,10 @@ int clear_sj_tmp_tables(JOIN *join); ...@@ -367,8 +367,10 @@ int clear_sj_tmp_tables(JOIN *join);
int rewrite_to_index_subquery_engine(JOIN *join); int rewrite_to_index_subquery_engine(JOIN *join);
void get_temptable_params(Item_in_subselect *item, ha_rows *out_rows, void get_delayed_table_estimates(TABLE *table,
ha_rows *scan_time); ha_rows *out_rows,
double *scan_time,
double *startup_cost);
bool do_jtbm_materialization_if_needed(JOIN_TAB *tab); bool do_jtbm_materialization_if_needed(JOIN_TAB *tab);
...@@ -2581,10 +2581,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2581,10 +2581,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
{ {
/* s is the only inner table of an outer join */ /* s is the only inner table of an outer join */
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (!table->pos_in_table_list->jtbm_subselect && if (!table->is_filled_at_execution() &&
(!table->file->stats.records || table->no_partitions_used) && !embedding) (!table->file->stats.records || table->no_partitions_used) && !embedding)
#else #else
if (!table->pos_in_table_list->jtbm_subselect && if (!table->is_filled_at_execution() &&
!table->file->stats.records && !embedding) !table->file->stats.records && !embedding)
#endif #endif
{ // Empty table { // Empty table
...@@ -2619,7 +2619,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2619,7 +2619,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
#else #else
const bool no_partitions_used= FALSE; const bool no_partitions_used= FALSE;
#endif #endif
if (!table->pos_in_table_list->jtbm_subselect && if (!table->is_filled_at_execution() &&
(table->s->system || table->file->stats.records <= 1 || (table->s->system || table->file->stats.records <= 1 ||
no_partitions_used) && no_partitions_used) &&
!s->dependent && !s->dependent &&
...@@ -2719,7 +2719,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2719,7 +2719,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
{ {
table=s->table; table=s->table;
if (table->pos_in_table_list->jtbm_subselect) if (table->is_filled_at_execution())
continue; continue;
/* /*
...@@ -2874,6 +2874,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2874,6 +2874,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
for (s=stat ; s < stat_end ; s++) for (s=stat ; s < stat_end ; s++)
{ {
s->startup_cost= 0;
if (s->type == JT_SYSTEM || s->type == JT_CONST) if (s->type == JT_SYSTEM || s->type == JT_CONST)
{ {
/* Only one matching row */ /* Only one matching row */
...@@ -2882,18 +2883,17 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2882,18 +2883,17 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
} }
/* Approximate found rows and time to read them */ /* Approximate found rows and time to read them */
if (s->table->pos_in_table_list->jtbm_subselect) if (s->table->is_filled_at_execution())
{ {
get_temptable_params(s->table->pos_in_table_list->jtbm_subselect, get_delayed_table_estimates(s->table, &s->records, &s->read_time,
&s->records, &s->startup_cost);
&s->read_time);
s->found_records= s->records; s->found_records= s->records;
table->quick_condition_rows=s->records; table->quick_condition_rows=s->records;
} }
else else
{ {
s->found_records=s->records=s->table->file->stats.records; s->found_records= s->records= s->table->file->stats.records;
s->read_time=(ha_rows) s->table->file->scan_time(); s->read_time= s->table->file->scan_time();
} }
...@@ -2922,7 +2922,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2922,7 +2922,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
(!s->table->pos_in_table_list->embedding || // (2) (!s->table->pos_in_table_list->embedding || // (2)
(s->table->pos_in_table_list->embedding && // (3) (s->table->pos_in_table_list->embedding && // (3)
s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3) s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3)
!s->table->pos_in_table_list->jtbm_subselect) !s->table->is_filled_at_execution())
{ {
ha_rows records; ha_rows records;
SQL_SELECT *select; SQL_SELECT *select;
...@@ -2960,7 +2960,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2960,7 +2960,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
if (records != HA_POS_ERROR) if (records != HA_POS_ERROR)
{ {
s->found_records=records; s->found_records=records;
s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0); s->read_time= s->quick ? s->quick->read_time : 0.0;
} }
delete select; delete select;
} }
...@@ -4288,7 +4288,6 @@ best_access_path(JOIN *join, ...@@ -4288,7 +4288,6 @@ best_access_path(JOIN *join,
ha_rows rec; ha_rows rec;
bool best_uses_jbuf= FALSE; bool best_uses_jbuf= FALSE;
Item_in_subselect* jtbm_subselect= s->table->pos_in_table_list->jtbm_subselect; Item_in_subselect* jtbm_subselect= s->table->pos_in_table_list->jtbm_subselect;
bool jtbm_ref_used= FALSE;
Loose_scan_opt loose_scan_opt; Loose_scan_opt loose_scan_opt;
DBUG_ENTER("best_access_path"); DBUG_ENTER("best_access_path");
...@@ -4641,8 +4640,8 @@ best_access_path(JOIN *join, ...@@ -4641,8 +4640,8 @@ best_access_path(JOIN *join,
else else
tmp= best_time; // Do nothing tmp= best_time; // Do nothing
} }
if (jtbm_subselect)
tmp += jtbm_subselect->startup_cost; tmp += s->startup_cost;
loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp);
} /* not ft_key */ } /* not ft_key */
if (tmp < best_time - records/(double) TIME_FOR_COMPARE) if (tmp < best_time - records/(double) TIME_FOR_COMPARE)
...@@ -4653,8 +4652,6 @@ best_access_path(JOIN *join, ...@@ -4653,8 +4652,6 @@ best_access_path(JOIN *join,
best_key= start_key; best_key= start_key;
best_max_key_part= max_key_part; best_max_key_part= max_key_part;
best_ref_depends_map= found_ref; best_ref_depends_map= found_ref;
if (jtbm_subselect)
jtbm_ref_used= TRUE;
} }
} /* for each key */ } /* for each key */
records= best_records; records= best_records;
...@@ -4687,6 +4684,11 @@ best_access_path(JOIN *join, ...@@ -4687,6 +4684,11 @@ best_access_path(JOIN *join,
Since we have a 'ref' access path, and FORCE INDEX instructs us to Since we have a 'ref' access path, and FORCE INDEX instructs us to
choose it over ALL/index, there is no need to consider a full table choose it over ALL/index, there is no need to consider a full table
scan. scan.
(5) Non-flattenable semi-joins: don't consider doing a scan of temporary
table if we had an option to make lookups into it. In real-world cases,
lookups are cheaper than full scans, but when the table is small, they
can be [considered to be] more expensive, which causes lookups not to
be used for cases with small datasets, which is annoying.
*/ */
if ((records >= s->found_records || best > s->read_time) && // (1) if ((records >= s->found_records || best > s->read_time) && // (1)
!(s->quick && best_key && s->quick->index == best_key->key && // (2) !(s->quick && best_key && s->quick->index == best_key->key && // (2)
...@@ -4694,7 +4696,7 @@ best_access_path(JOIN *join, ...@@ -4694,7 +4696,7 @@ best_access_path(JOIN *join,
!((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3) !((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3) ! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
!(s->table->force_index && best_key && !s->quick) && // (4) !(s->table->force_index && best_key && !s->quick) && // (4)
!jtbm_ref_used) !(best_key && jtbm_subselect)) // (5)
{ // Check full join { // Check full join
ha_rows rnd_records= s->found_records; ha_rows rnd_records= s->found_records;
/* /*
...@@ -4741,12 +4743,19 @@ best_access_path(JOIN *join, ...@@ -4741,12 +4743,19 @@ best_access_path(JOIN *join,
} }
else else
{ {
#if 0
/* Estimate cost of reading table. */ /* Estimate cost of reading table. */
if (jtbm_subselect) if (jtbm_subselect) //psergey-jtbm-todo: why the difference?
tmp= s->read_time; tmp= s->read_time;
else else
tmp= s->table->file->scan_time(); tmp= s->table->file->scan_time();
//psergey-debug:
if (!jtbm_subselect && fabs(s->read_time - s->table->file->scan_time()) > 1.0)
{
fprintf(stderr, "Q:%s\n", thd->query());
}
#endif
tmp= s->read_time;
if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache
{ {
/* /*
...@@ -4775,8 +4784,7 @@ best_access_path(JOIN *join, ...@@ -4775,8 +4784,7 @@ best_access_path(JOIN *join,
} }
} }
if (jtbm_subselect) tmp += s->startup_cost;
tmp += jtbm_subselect->startup_cost;
/* /*
We estimate the cost of evaluating WHERE clause for found records We estimate the cost of evaluating WHERE clause for found records
as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus
...@@ -6722,12 +6730,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6722,12 +6730,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
the index if we are using limit and this is the first table the index if we are using limit and this is the first table
*/ */
if ((cond && if (!tab->table->is_filled_at_execution() &&
(!tab->keys.is_subset(tab->const_keys) && i > 0)) || ((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) ||
(!tab->const_keys.is_clear_all() && i == join->const_tables && (!tab->const_keys.is_clear_all() && i == join->const_tables &&
join->unit->select_limit_cnt < join->unit->select_limit_cnt <
join->best_positions[i].records_read && join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS))) !(join->select_options & OPTION_FOUND_ROWS))))
{ {
/* Join with outer join condition */ /* Join with outer join condition */
COND *orig_cond=sel->cond; COND *orig_cond=sel->cond;
......
...@@ -210,7 +210,10 @@ typedef struct st_join_table { ...@@ -210,7 +210,10 @@ typedef struct st_join_table {
method (but not 'index' for some reason), i.e. this matches method which method (but not 'index' for some reason), i.e. this matches method which
E(#records) is in found_records. E(#records) is in found_records.
*/ */
ha_rows read_time; double read_time;
/* Startup cost for execution */
double startup_cost;
table_map dependent,key_dependent; table_map dependent,key_dependent;
uint use_quick,index; uint use_quick,index;
......
...@@ -5114,6 +5114,12 @@ bool st_table::is_children_attached(void) ...@@ -5114,6 +5114,12 @@ bool st_table::is_children_attached(void)
(parent && parent->children_attached)); (parent && parent->children_attached));
} }
bool st_table::is_filled_at_execution()
{
return test(pos_in_table_list->jtbm_subselect);
}
/* /*
Cleanup this table for re-execution. Cleanup this table for re-execution.
......
...@@ -914,6 +914,13 @@ struct st_table { ...@@ -914,6 +914,13 @@ struct st_table {
inline bool needs_reopen_or_name_lock() inline bool needs_reopen_or_name_lock()
{ return s->version != refresh_version; } { return s->version != refresh_version; }
bool is_children_attached(void); bool is_children_attached(void);
/*
If TRUE, the table is filled at execution phase (and so, the optimizer
should not do things like range analysis or constant table detection on
it).
*/
bool is_filled_at_execution();
}; };
enum enum_schema_table_state enum enum_schema_table_state
......
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