Commit dad93f2c authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#90, code movearound to unify merged and non-merged semi-join materialization processing

- First code, needs cleanup.
parent 0cc37246
...@@ -5733,6 +5733,8 @@ Item_field* Item_equal::get_first(Item_field *field) ...@@ -5733,6 +5733,8 @@ Item_field* Item_equal::get_first(Item_field *field)
It's a field from an materialized semi-join. We can substitute it only It's a field from an materialized semi-join. We can substitute it only
for a field from the same semi-join. for a field from the same semi-join.
*/ */
#if 0
psergey3:remove:
JOIN_TAB *first; JOIN_TAB *first;
JOIN *join= field_tab->join; JOIN *join= field_tab->join;
int tab_idx= field_tab - field_tab->join->join_tab; int tab_idx= field_tab - field_tab->join->join_tab;
...@@ -5746,10 +5748,12 @@ Item_field* Item_equal::get_first(Item_field *field) ...@@ -5746,10 +5748,12 @@ Item_field* Item_equal::get_first(Item_field *field)
// Found first tab that doesn't belong to current SJ. // Found first tab that doesn't belong to current SJ.
break; break;
} }
#endif
/* Find an item to substitute for. */ /* Find an item to substitute for. */
while ((item= it++)) while ((item= it++))
{ {
if (item->field->table->reginfo.join_tab >= first) //if (item->field->table->reginfo.join_tab >= first)
if (item->field->table->pos_in_table_list->embedding == emb_nest)
{ {
/* /*
If we found given field then return NULL to avoid unnecessary If we found given field then return NULL to avoid unnecessary
......
...@@ -2607,6 +2607,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) ...@@ -2607,6 +2607,9 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
} }
} }
enum_nested_loop_state
end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
/* /*
Setup semi-join materialization strategy for one semi-join nest Setup semi-join materialization strategy for one semi-join nest
...@@ -2628,10 +2631,11 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join) ...@@ -2628,10 +2631,11 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
TRUE Error TRUE Error
*/ */
bool setup_sj_materialization(JOIN_TAB *tab) bool setup_sj_materialization(JOIN_TAB *sjm_tab)
{ {
uint i; uint i;
DBUG_ENTER("setup_sj_materialization"); DBUG_ENTER("setup_sj_materialization");
JOIN_TAB *tab= sjm_tab->bush_children->start;
TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding;
SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info;
THD *thd= tab->join->thd; THD *thd= tab->join->thd;
...@@ -2659,10 +2663,13 @@ bool setup_sj_materialization(JOIN_TAB *tab) ...@@ -2659,10 +2663,13 @@ bool setup_sj_materialization(JOIN_TAB *tab)
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
sjm->table->file->extra(HA_EXTRA_WRITE_CACHE); sjm->table->file->extra(HA_EXTRA_WRITE_CACHE);
sjm->table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); sjm->table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
//psergey2-todo: need this or can take advantage of re-init functionality?
tab->join->sj_tmp_tables.push_back(sjm->table); tab->join->sj_tmp_tables.push_back(sjm->table);
tab->join->sjm_info_list.push_back(sjm); tab->join->sjm_info_list.push_back(sjm);
sjm->materialized= FALSE; sjm->materialized= FALSE;
sjm_tab->table= sjm->table;
if (!sjm->is_sj_scan) if (!sjm->is_sj_scan)
{ {
KEY *tmp_key; /* The only index on the temporary table. */ KEY *tmp_key; /* The only index on the temporary table. */
...@@ -2675,8 +2682,9 @@ bool setup_sj_materialization(JOIN_TAB *tab) ...@@ -2675,8 +2682,9 @@ bool setup_sj_materialization(JOIN_TAB *tab)
temptable. temptable.
*/ */
TABLE_REF *tab_ref; TABLE_REF *tab_ref;
if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF)))) //if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF))))
DBUG_RETURN(TRUE); /* purecov: inspected */ // DBUG_RETURN(TRUE); /* purecov: inspected */
tab_ref= &sjm_tab->ref;
tab_ref->key= 0; /* The only temp table index. */ tab_ref->key= 0; /* The only temp table index. */
tab_ref->key_length= tmp_key->key_length; tab_ref->key_length= tmp_key->key_length;
if (!(tab_ref->key_buff= if (!(tab_ref->key_buff=
...@@ -2733,6 +2741,7 @@ bool setup_sj_materialization(JOIN_TAB *tab) ...@@ -2733,6 +2741,7 @@ bool setup_sj_materialization(JOIN_TAB *tab)
if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm, if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm,
emb_sj_nest->sj_subq_pred))) emb_sj_nest->sj_subq_pred)))
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
sjm_tab->type= JT_EQ_REF;
} }
else else
{ {
...@@ -2818,8 +2827,18 @@ bool setup_sj_materialization(JOIN_TAB *tab) ...@@ -2818,8 +2827,18 @@ bool setup_sj_materialization(JOIN_TAB *tab)
/* The write_set for source tables must be set up to allow the copying */ /* The write_set for source tables must be set up to allow the copying */
bitmap_set_bit(copy_to->table->write_set, copy_to->field_index); bitmap_set_bit(copy_to->table->write_set, copy_to->field_index);
} }
sjm_tab->type= JT_ALL;
/* Initialize full scan */
sjm_tab->read_first_record= join_read_record_no_init;
sjm_tab->read_record.copy_field= sjm->copy_field;
sjm_tab->read_record.copy_field_end= sjm->copy_field +
sjm->sjm_table_cols.elements;
sjm_tab->read_record.read_record= rr_sequential_and_unpack;
} }
sjm_tab->bush_children->end[-1].next_select= end_sj_materialize;
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
...@@ -3919,8 +3938,26 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) ...@@ -3919,8 +3938,26 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where)
} }
bool do_jtbm_materialization_if_needed(JOIN_TAB *tab) /*
Join tab execution startup function.
DESCRIPTION
Join tab execution startup function. This is different from
tab->read_first_record in the regard that this has actions that are to be
done once per join execution.
Currently there are only two possible startup functions, so we have them
both here inside if (...) branches. In future we could switch to function
pointers.
RETURN
FALSE Ok
TRUE Error, join execution is not possible.
*/
bool join_tab_execution_startup(JOIN_TAB *tab)
{ {
DBUG_ENTER("join_tab_execution_startup");
Item_in_subselect *in_subs; Item_in_subselect *in_subs;
if (tab->table->pos_in_table_list && if (tab->table->pos_in_table_list &&
(in_subs= tab->table->pos_in_table_list->jtbm_subselect)) (in_subs= tab->table->pos_in_table_list->jtbm_subselect))
...@@ -3936,9 +3973,54 @@ bool do_jtbm_materialization_if_needed(JOIN_TAB *tab) ...@@ -3936,9 +3973,54 @@ bool do_jtbm_materialization_if_needed(JOIN_TAB *tab)
hash_sj_engine->is_materialized= TRUE; hash_sj_engine->is_materialized= TRUE;
if (hash_sj_engine->materialize_join->error || tab->join->thd->is_fatal_error) if (hash_sj_engine->materialize_join->error || tab->join->thd->is_fatal_error)
return TRUE; DBUG_RETURN(TRUE);
} }
} }
return FALSE; else if (tab->bush_children)
{
/* It's a merged SJM nest */
int rc; // psergey3: todo: error codes!
JOIN *join= tab->join;
SJ_MATERIALIZATION_INFO *sjm= tab->bush_children->start->emb_sj_nest->sj_mat_info;
JOIN_TAB *join_tab= tab->bush_children->start;
if (!sjm->materialized)
{
/*
Now run the join for the inner tables. The first call is to run the
join, the second one is to signal EOF (this is essential for some
join strategies, e.g. it will make join buffering flush the records)
*/
if ((rc= sub_select(join, join_tab, FALSE/* no EOF */)) < 0 ||
(rc= sub_select(join, join_tab, TRUE/* now EOF */)) < 0)
{
//psergey3-todo: set sjm->materialized=TRUE here, too??
DBUG_RETURN(rc); /* it's NESTED_LOOP_(ERROR|KILLED)*/
}
/*
Ok, materialization finished. Initialize the access to the temptable
*/
sjm->materialized= TRUE;
#if 0
psergey3: already done at setup:
if (sjm->is_sj_scan)
{
/* Initialize full scan */
JOIN_TAB *last_tab= join_tab + (sjm->tables - 1);
init_read_record(&last_tab->read_record, join->thd,
sjm->table, NULL, TRUE, TRUE, FALSE);
DBUG_ASSERT(last_tab->read_record.read_record == rr_sequential);
last_tab->read_first_record= join_read_record_no_init;
last_tab->read_record.copy_field= sjm->copy_field;
last_tab->read_record.copy_field_end= sjm->copy_field +
sjm->sjm_table_cols.elements;
last_tab->read_record.read_record= rr_sequential_and_unpack;
}
#endif
}
}
DBUG_RETURN(0);
} }
...@@ -372,5 +372,5 @@ void get_delayed_table_estimates(TABLE *table, ...@@ -372,5 +372,5 @@ void get_delayed_table_estimates(TABLE *table,
double *scan_time, double *scan_time,
double *startup_cost); double *startup_cost);
bool do_jtbm_materialization_if_needed(JOIN_TAB *tab); bool join_tab_execution_startup(JOIN_TAB *tab);
...@@ -1778,7 +1778,7 @@ enum_nested_loop_state JOIN_CACHE_BNL::join_matching_records(bool skip_last) ...@@ -1778,7 +1778,7 @@ enum_nested_loop_state JOIN_CACHE_BNL::join_matching_records(bool skip_last)
/* Start retrieving all records of the joined table */ /* Start retrieving all records of the joined table */
if (do_jtbm_materialization_if_needed(join_tab)) if (join_tab_execution_startup(join_tab))
{ {
rc= NESTED_LOOP_ERROR; rc= NESTED_LOOP_ERROR;
goto finish; goto finish;
......
...@@ -96,7 +96,7 @@ static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item); ...@@ -96,7 +96,7 @@ static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item);
static bool make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after); static bool make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after);
static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables); static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables);
static void update_depend_map(JOIN *join); static void update_depend_map(JOIN *join);
static void update_depend_map(JOIN *join, ORDER *order); static void update_depend_map_for_order(JOIN *join, ORDER *order);
static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond,
bool change_list, bool *simple_order); bool change_list, bool *simple_order);
static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables,
...@@ -237,8 +237,6 @@ static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab); ...@@ -237,8 +237,6 @@ static void add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab);
void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg, void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg,
double *record_count_arg); double *record_count_arg);
static uint make_join_orderinfo(JOIN *join); static uint make_join_orderinfo(JOIN *join);
static int
join_read_record_no_init(JOIN_TAB *tab);
Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
bool *inherited_fl); bool *inherited_fl);
...@@ -1008,15 +1006,26 @@ JOIN::optimize() ...@@ -1008,15 +1006,26 @@ JOIN::optimize()
/* /*
Permorm the the optimization on fields evaluation mentioned above Permorm the the optimization on fields evaluation mentioned above
for all on expressions. for all on expressions.
*/ */
for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++)
{ {
if (*tab->on_expr_ref) List_iterator<JOIN_TAB_RANGE> it(join_tab_ranges);
JOIN_TAB_RANGE *jt_range;
bool first= TRUE;
while ((jt_range= it++))
{ {
*tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref, for (JOIN_TAB *tab= jt_range->start + (first ? const_tables : 0);
tab->cond_equal, tab < jt_range->end; tab++)
map2table); {
(*tab->on_expr_ref)->update_used_tables(); if (*tab->on_expr_ref)
{
*tab->on_expr_ref= substitute_for_best_equal_field(*tab->on_expr_ref,
tab->cond_equal,
map2table);
(*tab->on_expr_ref)->update_used_tables();
}
}
first= FALSE;
} }
} }
...@@ -1026,6 +1035,7 @@ JOIN::optimize() ...@@ -1026,6 +1035,7 @@ JOIN::optimize()
{ {
conds=new Item_int((longlong) 0,1); // Always false conds=new Item_int((longlong) 0,1); // Always false
} }
if (make_join_select(this, select, conds)) if (make_join_select(this, select, conds))
{ {
zero_result_cause= zero_result_cause=
...@@ -1289,7 +1299,8 @@ JOIN::optimize() ...@@ -1289,7 +1299,8 @@ JOIN::optimize()
if (need_tmp || select_distinct || group_list || order) if (need_tmp || select_distinct || group_list || order)
{ {
for (uint i = const_tables; i < tables; i++) for (uint i = const_tables; i < tables; i++)
join_tab[i].table->prepare_for_position(); table[i]->prepare_for_position();
} }
DBUG_EXECUTE("info",TEST_join(this);); DBUG_EXECUTE("info",TEST_join(this););
...@@ -2099,7 +2110,7 @@ JOIN::exec() ...@@ -2099,7 +2110,7 @@ JOIN::exec()
WHERE clause for any tables after the sorted one. WHERE clause for any tables after the sorted one.
*/ */
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables+1]; JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables+1];
JOIN_TAB *end_table= &curr_join->join_tab[curr_join->tables]; JOIN_TAB *end_table= &curr_join->join_tab[curr_join->tables]; //psergey2-todo: check this!
for (; curr_table < end_table ; curr_table++) for (; curr_table < end_table ; curr_table++)
{ {
/* /*
...@@ -2569,6 +2580,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds, ...@@ -2569,6 +2580,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
all_table_map|= s->table->map; all_table_map|= s->table->map;
s->join=join; s->join=join;
s->info=0; // For describe s->info=0; // For describe
s->bush_root_tab= NULL;
s->dependent= tables->dep_tables; s->dependent= tables->dep_tables;
s->key_dependent= 0; s->key_dependent= 0;
...@@ -5852,6 +5864,99 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref) ...@@ -5852,6 +5864,99 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref)
} }
JOIN_TAB *first_linear_tab(JOIN *join, bool after_const_tables)
{
JOIN_TAB *first= join->join_tab;
if (after_const_tables)
first += join->const_tables;
if (first < join->join_tab + join->top_jtrange_tables)
return first;
else
return NULL;
}
/*
A helper function to loop over all join's join_tab in sequential fashion
DESCRIPTION
Depending on include_bush_roots parameter, JOIN_TABS that represent
SJM-scan/lookups are produced or omitted.
SJM Bush children are returned right after (or in place of) their container
join tab (TODO: does anybody depend on this? A: make_join_readinfo() seems
to.)
*/
JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab, bool include_bush_roots) //psergey2: added
{
if (include_bush_roots && tab->bush_children)
return tab->bush_children->start;
if (tab->last_leaf_in_bush)
tab= tab->bush_root_tab;
if (tab->bush_root_tab)
return ++tab;
if (++tab == join->join_tab + join->top_jtrange_tables /*join->join_tab_ranges.head()->end*/)
return NULL;
if (!include_bush_roots && tab->bush_children)
{
tab= tab->bush_children->start;
}
return tab;
}
/*
A helper function to iterate over all join tables in bush-children-first order
DESCRIPTION
For example, for this join plan
ot1 ot2 sjm ot3
| +--------+
| |
it1 it2 it3
the function will return
ot1-ot2-it1-it2-it3-sjm-ot3 ...
*/
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) //psergey2: added
{
bool start= FALSE;
if (tab == NULL)
{
/* This means we're starting. */
if (join->const_tables == join->top_jtrange_tables)
return NULL;
tab= join->join_tab + join->const_tables;
start= TRUE;
}
if (tab->last_leaf_in_bush)
return tab->bush_root_tab;
if ((start? tab: ++tab) == join->join_tab_ranges.head()->end)
return NULL; /* End */
if (tab->bush_children)
return tab->bush_children->start;
return tab;
}
static Item *null_ptr= NULL;
/* /*
Set up join struct according to the picked join order in Set up join struct according to the picked join order in
...@@ -5866,7 +5971,12 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref) ...@@ -5866,7 +5971,12 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref)
fix_semijoin_strategies_for_picked_join_order) fix_semijoin_strategies_for_picked_join_order)
- create join->join_tab array and put there the JOIN_TABs in the join order - create join->join_tab array and put there the JOIN_TABs in the join order
- create data structures describing ref access methods. - create data structures describing ref access methods.
NOTE
In this function we switch from pre-join-optimization JOIN_TABs to
post-join-optimization JOIN_TABs. This is achieved by copying the entire
JOIN_TAB objects.
RETURN RETURN
FALSE OK FALSE OK
TRUE Out of memory TRUE Out of memory
...@@ -5875,7 +5985,7 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref) ...@@ -5875,7 +5985,7 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref)
static bool static bool
get_best_combination(JOIN *join) get_best_combination(JOIN *join)
{ {
uint i,tablenr; uint tablenr;
table_map used_tables; table_map used_tables;
JOIN_TAB *join_tab,*j; JOIN_TAB *join_tab,*j;
KEYUSE *keyuse; KEYUSE *keyuse;
...@@ -5893,11 +6003,73 @@ get_best_combination(JOIN *join) ...@@ -5893,11 +6003,73 @@ get_best_combination(JOIN *join)
used_tables= OUTER_REF_TABLE_BIT; // Outer row is already read used_tables= OUTER_REF_TABLE_BIT; // Outer row is already read
fix_semijoin_strategies_for_picked_join_order(join); fix_semijoin_strategies_for_picked_join_order(join);
/*
psergey2-todo: Here: switch to nested structure when copying.
*/
JOIN_TAB_RANGE *root_range= new JOIN_TAB_RANGE;
root_range->start= join->join_tab;
/* root_range->end will be set later */
join->join_tab_ranges.empty();
join->join_tab_ranges.push_back(root_range);
JOIN_TAB *sjm_nest_end= NULL;
JOIN_TAB *sjm_saved_tab; /* protected by sjm_nest_end */
for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++) for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++)
{ {
TABLE *form; TABLE *form;
POSITION *cur_pos= &join->best_positions[tablenr];
if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE ||
cur_pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
{
/*
Ok, we've entered an SJ-Materialization semi-join (note that this can't
be done recursively, semi-joins are not allowed to be nested).
*/
/*
1. Put into main join order a JOIN_TAB that represents a lookup or scan
in the temptable.
// TODO: record this join_tab to be processed by
// setup_semijoin_elimination?
*/
bzero(j, sizeof(JOIN_TAB));
j->join= join;
j->table= NULL; //temporary way to tell SJM tables from others.
j->ref.key = -1;
j->ref.key_parts=0;
j->loosescan_match_tab= NULL; //non-nulls will be set later
j->use_join_cache= FALSE;
j->on_expr_ref= &null_ptr;
j->cache= NULL;
/*
2. Proceed with processing SJM nest's join tabs, putting them into the
sub-order
*/
SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info;
JOIN_TAB *jt= (JOIN_TAB*)join->thd->alloc(sizeof(JOIN_TAB) * sjm->tables);
JOIN_TAB_RANGE *jt_range= new JOIN_TAB_RANGE;
jt_range->start= jt;
jt_range->end= jt + sjm->tables;
//sjm->jt_range= jt_range;
join->join_tab_ranges.push_back(jt_range);
j->bush_children= jt_range;
j->bush_root_tab= NULL; //note: a lot of code depends on bush nodes not containing one another
j->quick= NULL;
sjm_nest_end= jt + sjm->tables;
sjm_saved_tab= j;
j= jt;
//goto loop_end_not_table;
}
*j= *join->best_positions[tablenr].table; *j= *join->best_positions[tablenr].table;
if (sjm_nest_end)
j->bush_root_tab= sjm_saved_tab;
else
root_range->end= j+1;
form=join->table[tablenr]=j->table; form=join->table[tablenr]=j->table;
used_tables|= form->map; used_tables|= form->map;
form->reginfo.join_tab=j; form->reginfo.join_tab=j;
...@@ -5905,14 +6077,14 @@ get_best_combination(JOIN *join) ...@@ -5905,14 +6077,14 @@ get_best_combination(JOIN *join)
form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN
DBUG_PRINT("info",("type: %d", j->type)); DBUG_PRINT("info",("type: %d", j->type));
if (j->type == JT_CONST) if (j->type == JT_CONST)
continue; // Handled in make_join_stat.. goto loop_end; // Handled in make_join_stat..
j->loosescan_match_tab= NULL; //non-nulls will be set later j->loosescan_match_tab= NULL; //non-nulls will be set later
j->ref.key = -1; j->ref.key = -1;
j->ref.key_parts=0; j->ref.key_parts=0;
if (j->type == JT_SYSTEM) if (j->type == JT_SYSTEM)
continue; goto loop_end;
if (j->keys.is_clear_all() || !(keyuse= join->best_positions[tablenr].key) || if (j->keys.is_clear_all() || !(keyuse= join->best_positions[tablenr].key) ||
(join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN)) (join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN))
{ {
...@@ -5923,10 +6095,24 @@ get_best_combination(JOIN *join) ...@@ -5923,10 +6095,24 @@ get_best_combination(JOIN *join)
} }
else if (create_ref_for_key(join, j, keyuse, used_tables)) else if (create_ref_for_key(join, j, keyuse, used_tables))
DBUG_RETURN(TRUE); // Something went wrong DBUG_RETURN(TRUE); // Something went wrong
j->records_read= join->best_positions[tablenr].records_read;
loop_end:
join->map2table[j->table->tablenr]= j;
// If we've reached the end of sjm nest, switch back to main sequence
if (j + 1 == sjm_nest_end)
{
j->last_leaf_in_bush= TRUE;
j= sjm_saved_tab;
sjm_nest_end= NULL;
}
} }
for (i=0 ; i < table_count ; i++) join->top_jtrange_tables= join->join_tab_ranges.head()->end -
join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; join->join_tab_ranges.head()->start;
//for (i=0 ; i < table_count ; i++)
// join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i;
update_depend_map(join); update_depend_map(join);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -6175,6 +6361,11 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table) ...@@ -6175,6 +6361,11 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
join_tab= parent->join_tab_reexec; join_tab= parent->join_tab_reexec;
//psergey2: hopefully this is ok:
// join_tab_ranges.head()->start= join_tab;
// join_tab_ranges.head()->end= join_tab + 1;
top_jtrange_tables= 1;
table= &parent->table_reexec[0]; parent->table_reexec[0]= tmp_table; table= &parent->table_reexec[0]; parent->table_reexec[0]= tmp_table;
tables= 1; tables= 1;
const_tables= 0; const_tables= 0;
...@@ -6214,6 +6405,9 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table) ...@@ -6214,6 +6405,9 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
join_tab->do_firstmatch= NULL; join_tab->do_firstmatch= NULL;
join_tab->loosescan_match_tab= NULL; join_tab->loosescan_match_tab= NULL;
join_tab->emb_sj_nest= NULL; join_tab->emb_sj_nest= NULL;
join_tab->bush_root_tab= NULL;
join_tab->bush_children= NULL;
join_tab->last_leaf_in_bush= FALSE;
bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record));
tmp_table->status=0; tmp_table->status=0;
tmp_table->null_row=0; tmp_table->null_row=0;
...@@ -6238,6 +6432,7 @@ inline void add_cond_and_fix(Item **e1, Item *e2) ...@@ -6238,6 +6432,7 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
} }
/** /**
Add to join_tab->select_cond[i] "table.field IS NOT NULL" conditions Add to join_tab->select_cond[i] "table.field IS NOT NULL" conditions
we've inferred from ref/eq_ref access performed. we've inferred from ref/eq_ref access performed.
...@@ -6292,9 +6487,13 @@ inline void add_cond_and_fix(Item **e1, Item *e2) ...@@ -6292,9 +6487,13 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
static void add_not_null_conds(JOIN *join) static void add_not_null_conds(JOIN *join)
{ {
DBUG_ENTER("add_not_null_conds"); DBUG_ENTER("add_not_null_conds");
for (uint i=join->const_tables ; i < join->tables ; i++)
//for (uint i=join->const_tables ; i < join->tables ; i++)
for (JOIN_TAB *tab= first_linear_tab(join, TRUE);
tab;
tab= next_linear_tab(join, tab, FALSE)) //psergey-todo: should be TRUE here?
{ {
JOIN_TAB *tab=join->join_tab+i; //JOIN_TAB *tab=join->join_tab+i;
if ((tab->type == JT_REF || tab->type == JT_EQ_REF || if ((tab->type == JT_REF || tab->type == JT_EQ_REF ||
tab->type == JT_REF_OR_NULL) && tab->type == JT_REF_OR_NULL) &&
!tab->table->maybe_null) !tab->table->maybe_null)
...@@ -6418,58 +6617,68 @@ static void ...@@ -6418,58 +6617,68 @@ static void
make_outerjoin_info(JOIN *join) make_outerjoin_info(JOIN *join)
{ {
DBUG_ENTER("make_outerjoin_info"); DBUG_ENTER("make_outerjoin_info");
for (uint i=join->const_tables ; i < join->tables ; i++) bool top= TRUE;
{ List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges);
JOIN_TAB *tab=join->join_tab+i; JOIN_TAB_RANGE *jt_range;
TABLE *table=tab->table;
TABLE_LIST *tbl= table->pos_in_table_list;
TABLE_LIST *embedding= tbl->embedding;
if (tbl->outer_join) while ((jt_range= it++))
{
for (JOIN_TAB *tab=jt_range->start + (top ? join->const_tables : 0);
tab != jt_range->end; tab++)
{ {
/* TABLE *table=tab->table;
Table tab is the only one inner table for outer join. if (!table)
(Like table t4 for the table reference t3 LEFT JOIN t4 ON t3.a=t4.a continue; //psergey2: fix this when we get SJM+outer joins really working.
is in the query above.) TABLE_LIST *tbl= table->pos_in_table_list;
*/ TABLE_LIST *embedding= tbl->embedding;
tab->last_inner= tab->first_inner= tab;
tab->on_expr_ref= &tbl->on_expr; if (tbl->outer_join)
tab->cond_equal= tbl->cond_equal;
if (embedding)
tab->first_upper= embedding->nested_join->first_nested;
}
for ( ; embedding ; embedding= embedding->embedding)
{
/* Ignore sj-nests: */
if (!embedding->on_expr)
continue;
NESTED_JOIN *nested_join= embedding->nested_join;
if (!nested_join->counter)
{ {
/* /*
Table tab is the first inner table for nested_join. Table tab is the only one inner table for outer join.
Save reference to it in the nested join structure. (Like table t4 for the table reference t3 LEFT JOIN t4 ON t3.a=t4.a
*/ is in the query above.)
nested_join->first_nested= tab; */
tab->on_expr_ref= &embedding->on_expr; tab->last_inner= tab->first_inner= tab;
tab->on_expr_ref= &tbl->on_expr;
tab->cond_equal= tbl->cond_equal; tab->cond_equal= tbl->cond_equal;
if (embedding->embedding) if (embedding)
tab->first_upper= embedding->embedding->nested_join->first_nested; tab->first_upper= embedding->nested_join->first_nested;
} }
if (!tab->first_inner) for ( ; embedding ; embedding= embedding->embedding)
tab->first_inner= nested_join->first_nested;
if (tab->table->reginfo.not_exists_optimize)
tab->first_inner->table->reginfo.not_exists_optimize= 1;
if (++nested_join->counter < nested_join->n_tables)
break;
/* Table tab is the last inner table for nested join. */
nested_join->first_nested->last_inner= tab;
if (tab->first_inner->table->reginfo.not_exists_optimize)
{ {
for (JOIN_TAB *join_tab= tab->first_inner; join_tab <= tab; join_tab++) /* Ignore sj-nests: */
join_tab->table->reginfo.not_exists_optimize= 1; if (!embedding->on_expr)
} continue;
NESTED_JOIN *nested_join= embedding->nested_join;
if (!nested_join->counter)
{
/*
Table tab is the first inner table for nested_join.
Save reference to it in the nested join structure.
*/
nested_join->first_nested= tab;
tab->on_expr_ref= &embedding->on_expr;
tab->cond_equal= tbl->cond_equal;
if (embedding->embedding)
tab->first_upper= embedding->embedding->nested_join->first_nested;
}
if (!tab->first_inner)
tab->first_inner= nested_join->first_nested;
if (tab->table->reginfo.not_exists_optimize)
tab->first_inner->table->reginfo.not_exists_optimize= 1;
if (++nested_join->counter < nested_join->n_tables)
break;
/* Table tab is the last inner table for nested join. */
nested_join->first_nested->last_inner= tab;
if (tab->first_inner->table->reginfo.not_exists_optimize)
{
for (JOIN_TAB *join_tab= tab->first_inner; join_tab <= tab; join_tab++)
join_tab->table->reginfo.not_exists_optimize= 1;
}
}
} }
top= FALSE;
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -6512,8 +6721,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6512,8 +6721,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->const_table_map, join->const_table_map,
(table_map) 0, TRUE); (table_map) 0, TRUE);
DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY);); DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables; // psergey2: not extracting conditions from inside bushy nests?
tab < join->join_tab+join->tables ; tab++) //for (JOIN_TAB *tab= join->join_tab+join->const_tables;
// tab < join->join_tab+join->tables ; tab++)
for (JOIN_TAB *tab= first_linear_tab(join, TRUE);
tab;
tab= next_linear_tab(join, tab, FALSE))
{ {
if (*tab->on_expr_ref) if (*tab->on_expr_ref)
{ {
...@@ -6552,18 +6765,27 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6552,18 +6765,27 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
JOIN_TAB *tab; JOIN_TAB *tab;
table_map current_map; table_map current_map;
for (uint i=join->const_tables ; i < join->tables ; i++) uint i= join->const_tables;
for (tab= next_depth_first_tab(join, NULL); tab;
tab= next_depth_first_tab(join, tab), i++)
//for (uint i=join->const_tables ; i < join->tables ; i++)
{ {
tab= join->join_tab+i; //tab= join->join_tab+i;
/* /*
first_inner is the X in queries like: first_inner is the X in queries like:
SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X
*/ */
JOIN_TAB *first_inner_tab= tab->first_inner; JOIN_TAB *first_inner_tab= tab->first_inner;
current_map= tab->table->map; //psergey2-todo: change this to table bitmap.
if (tab->table)
current_map= tab->table->map;
else
current_map= tab->bush_children->start->emb_sj_nest->sj_inner_tables;
bool use_quick_range=0; bool use_quick_range=0;
COND *tmp; COND *tmp;
// psergey2-todo: is the below ok? seems to be yes.
/* /*
Tables that are within SJ-Materialization nests cannot have their Tables that are within SJ-Materialization nests cannot have their
conditions referring to preceding non-const tables. conditions referring to preceding non-const tables.
...@@ -6607,8 +6829,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6607,8 +6829,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
} }
tmp= NULL; tmp= NULL;
if (cond) if (cond)
tmp= make_cond_for_table(cond, used_tables, current_map, FALSE); {
if (tab->bush_children)
{
// Reached the materialization tab
tmp= make_cond_after_sjm(cond, cond, save_used_tables, used_tables);
used_tables= save_used_tables | used_tables;
save_used_tables= 0;
}
else
tmp= make_cond_for_table(cond, used_tables, current_map, FALSE);
}
if (cond && !tmp && tab->quick) if (cond && !tmp && tab->quick)
{ // Outer join { // Outer join
if (tab->type != JT_ALL) if (tab->type != JT_ALL)
...@@ -6806,7 +7040,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6806,7 +7040,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
} }
/* /*
Push down conditions from all on expressions. Push down conditions from all ON expressions.
Each of these conditions are guarded by a variable Each of these conditions are guarded by a variable
that turns if off just before null complemented row for that turns if off just before null complemented row for
outer joins is formed. Thus, the condition from an outer joins is formed. Thus, the condition from an
...@@ -6815,8 +7049,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6815,8 +7049,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
*/ */
/* First push down constant conditions from on expressions */ /* First push down constant conditions from on expressions */
for (JOIN_TAB *join_tab= join->join_tab+join->const_tables; //for (JOIN_TAB *join_tab= join->join_tab+join->const_tables;
join_tab < join->join_tab+join->tables ; join_tab++) // join_tab < join->join_tab+join->tables ; join_tab++)
for (JOIN_TAB *join_tab= first_linear_tab(join, TRUE);
join_tab;
join_tab= next_linear_tab(join, join_tab, FALSE))
{ {
if (*join_tab->on_expr_ref) if (*join_tab->on_expr_ref)
{ {
...@@ -6844,7 +7081,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6844,7 +7081,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
/* Push down non-constant conditions from on expressions */ /* Push down non-constant conditions from on expressions */
JOIN_TAB *last_tab= tab; JOIN_TAB *last_tab= tab;
while (first_inner_tab && first_inner_tab->last_inner == last_tab) while (first_inner_tab && first_inner_tab->last_inner == last_tab)
{ {
//JOIN_TAB *tab; //psergey2: have our own 'tab'
/* /*
Table tab is the last inner table of an outer join. Table tab is the last inner table of an outer join.
An on expression is always attached to it. An on expression is always attached to it.
...@@ -6853,7 +7091,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6853,7 +7091,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
table_map used_tables2= (join->const_table_map | table_map used_tables2= (join->const_table_map |
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT); OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++) //for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++)
for (JOIN_TAB *tab= first_linear_tab(join, TRUE);
tab;
tab= next_linear_tab(join, tab, TRUE))
{ {
current_map= tab->table->map; current_map= tab->table->map;
used_tables2|= current_map; used_tables2|= current_map;
...@@ -6899,7 +7140,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6899,7 +7140,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
} }
first_inner_tab= first_inner_tab->first_upper; first_inner_tab= first_inner_tab->first_upper;
} }
#if 0
psergey2-todo:remove:
if (save_used_tables && !(used_tables & if (save_used_tables && !(used_tables &
~(tab->emb_sj_nest->sj_inner_tables | ~(tab->emb_sj_nest->sj_inner_tables |
join->const_table_map | PSEUDO_TABLE_BITS))) join->const_table_map | PSEUDO_TABLE_BITS)))
...@@ -6925,7 +7167,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6925,7 +7167,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
used_tables= save_used_tables | used_tables; used_tables= save_used_tables | used_tables;
save_used_tables= 0; save_used_tables= 0;
} }
#endif
} }
} }
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -7114,7 +7356,7 @@ void revise_cache_usage(JOIN_TAB *join_tab) ...@@ -7114,7 +7356,7 @@ void revise_cache_usage(JOIN_TAB *join_tab)
SYNOPSIS SYNOPSIS
end_sj_materialize() end_sj_materialize()
join The join join The join
join_tab Last join table join_tab Points to right after the last join_tab in materialization bush
end_of_records FALSE <=> This call is made to pass another record end_of_records FALSE <=> This call is made to pass another record
combination combination
TRUE <=> EOF (no action) TRUE <=> EOF (no action)
...@@ -7132,7 +7374,7 @@ void revise_cache_usage(JOIN_TAB *join_tab) ...@@ -7132,7 +7374,7 @@ void revise_cache_usage(JOIN_TAB *join_tab)
NESTED_LOOP_ERROR NESTED_LOOP_ERROR
*/ */
static enum_nested_loop_state enum_nested_loop_state
end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
{ {
int error; int error;
...@@ -7407,8 +7649,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) ...@@ -7407,8 +7649,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); bool statistics= test(!(join->select_options & SELECT_DESCRIBE));
bool ordered_set= 0; bool ordered_set= 0;
bool sorted= 1; bool sorted= 1;
uint first_sjm_table= MAX_TABLES; //uint first_sjm_table= MAX_TABLES;
uint last_sjm_table= MAX_TABLES; //uint last_sjm_table= MAX_TABLES;
DBUG_ENTER("make_join_readinfo"); DBUG_ENTER("make_join_readinfo");
...@@ -7416,15 +7658,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) ...@@ -7416,15 +7658,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
setup_semijoin_dups_elimination(join, options, no_jbuf_after)) setup_semijoin_dups_elimination(join, options, no_jbuf_after))
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
for (i=join->const_tables ; i < join->tables ; i++) //for (i=join->const_tables ; i < join->tables ; i++)
i= 0;
for (JOIN_TAB *tab= first_linear_tab(join, TRUE);
tab;
tab= next_linear_tab(join, tab, TRUE), i++)
{ {
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table; TABLE *table=tab->table;
bool icp_other_tables_ok; bool icp_other_tables_ok;
tab->read_record.table= table;
tab->read_record.file=table->file;
tab->read_record.unlock_row= rr_unlock_row;
tab->next_select=sub_select; /* normal select */
/* /*
Determine if the set is already ordered for ORDER BY, so it can Determine if the set is already ordered for ORDER BY, so it can
...@@ -7442,24 +7683,42 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) ...@@ -7442,24 +7683,42 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
tab->sorted= sorted; tab->sorted= sorted;
sorted= 0; // only first must be sorted sorted= 0; // only first must be sorted
if (tab->loosescan_match_tab)
{
if (!(tab->loosescan_buf= (uchar*)join->thd->alloc(tab-> //if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy))
loosescan_key_len))) if (tab->bush_children) // SJM
return TRUE; /* purecov: inspected */
}
if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy))
{ {
/* This is a start of semi-join nest */ /* This is a start of semi-join nest */
first_sjm_table= i; //first_sjm_table= i;
last_sjm_table= i + join->best_positions[i].n_sj_tables; //last_sjm_table= i + join->best_positions[i].n_sj_tables;
/*
psergey2: dont:
if (i == join->const_tables) if (i == join->const_tables)
join->first_select= sub_select_sjm; join->first_select= sub_select_sjm;
else else
tab[-1].next_select= sub_select_sjm; tab[-1].next_select= sub_select_sjm;
*/
if (setup_sj_materialization(tab)) if (setup_sj_materialization(tab))
return TRUE; return TRUE;
table= tab->table;
}
tab->read_record.table= table;
tab->read_record.file=table->file;
tab->read_record.unlock_row= rr_unlock_row;
if (!(tab->bush_root_tab &&
tab->bush_root_tab->bush_children->end == tab + 1))
{
tab->next_select=sub_select; /* normal select */
}
if (tab->loosescan_match_tab)
{
if (!(tab->loosescan_buf= (uchar*)join->thd->alloc(tab->
loosescan_key_len)))
return TRUE; /* purecov: inspected */
} }
table->status=STATUS_NO_RECORD; table->status=STATUS_NO_RECORD;
pick_table_access_method (tab); pick_table_access_method (tab);
...@@ -7551,7 +7810,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) ...@@ -7551,7 +7810,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
} }
else else
{ {
tab->read_first_record= join_init_read_record; if (!tab->bush_children)
tab->read_first_record= join_init_read_record;
if (i == join->const_tables) if (i == join->const_tables)
{ {
if (tab->select && tab->select->quick) if (tab->select && tab->select->quick)
...@@ -7629,13 +7889,16 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) ...@@ -7629,13 +7889,16 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
abort(); /* purecov: deadcode */ abort(); /* purecov: deadcode */
} }
} }
join->join_tab[join->tables-1].next_select=0; /* Set by do_select */ uint n_top_tables= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
join->join_tab[n_top_tables - 1].next_select=0; /* Set by do_select */
/* /*
If a join buffer is used to join a table the ordering by an index If a join buffer is used to join a table the ordering by an index
for the first non-constant table cannot be employed anymore. for the first non-constant table cannot be employed anymore.
*/ */
for (i=join->const_tables ; i < join->tables ; i++) //for (i=join->const_tables ; i < join->tables ; i++)
for (i=join->const_tables ; i < n_top_tables ; i++)
{ {
JOIN_TAB *tab=join->join_tab+i; JOIN_TAB *tab=join->join_tab+i;
if (tab->use_join_cache) if (tab->use_join_cache)
...@@ -7692,6 +7955,9 @@ bool error_if_full_join(JOIN *join) ...@@ -7692,6 +7955,9 @@ bool error_if_full_join(JOIN *join)
/** /**
cleanup JOIN_TAB. cleanup JOIN_TAB.
DESCRIPTION
This is invoked when we've finished all join executions.
*/ */
void JOIN_TAB::cleanup() void JOIN_TAB::cleanup()
...@@ -7700,6 +7966,7 @@ void JOIN_TAB::cleanup() ...@@ -7700,6 +7966,7 @@ void JOIN_TAB::cleanup()
select= 0; select= 0;
delete quick; delete quick;
quick= 0; quick= 0;
//psergey3-todo: empty merged SJM temptables here.
if (cache) if (cache)
{ {
cache->free(); cache->free();
...@@ -7848,7 +8115,7 @@ void JOIN::cleanup(bool full) ...@@ -7848,7 +8115,7 @@ void JOIN::cleanup(bool full)
if (table) if (table)
{ {
JOIN_TAB *tab,*end; JOIN_TAB *tab;
/* /*
Only a sorted table may be cached. This sorted table is always the Only a sorted table may be cached. This sorted table is always the
first non const table in join->table first non const table in join->table
...@@ -7861,13 +8128,13 @@ void JOIN::cleanup(bool full) ...@@ -7861,13 +8128,13 @@ void JOIN::cleanup(bool full)
if (full) if (full)
{ {
for (tab= join_tab, end= tab+tables; tab != end; tab++) for (tab= top_jtrange_tables?join_tab:NULL; tab; tab= next_linear_tab(this, tab, TRUE))
tab->cleanup(); tab->cleanup();
table= 0; table= 0;
} }
else else
{ {
for (tab= join_tab, end= tab+tables; tab != end; tab++) for (tab= top_jtrange_tables?join_tab:NULL; tab; tab= next_linear_tab(this, tab, TRUE))
{ {
if (tab->table) if (tab->table)
tab->table->file->ha_index_or_rnd_end(); tab->table->file->ha_index_or_rnd_end();
...@@ -8005,24 +8272,29 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) ...@@ -8005,24 +8272,29 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
static void update_depend_map(JOIN *join) static void update_depend_map(JOIN *join)
{ {
JOIN_TAB *join_tab=join->join_tab, *end=join_tab+join->tables; List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges);
JOIN_TAB_RANGE *jt_range;
for (; join_tab != end ; join_tab++)
{ while ((jt_range= it++))
TABLE_REF *ref= &join_tab->ref; {
table_map depend_map=0; for (JOIN_TAB *join_tab=jt_range->start; join_tab != jt_range->end;
Item **item=ref->items; join_tab++)
uint i; {
for (i=0 ; i < ref->key_parts ; i++,item++) TABLE_REF *ref= &join_tab->ref;
depend_map|=(*item)->used_tables(); table_map depend_map=0;
ref->depend_map=depend_map & ~OUTER_REF_TABLE_BIT; Item **item=ref->items;
depend_map&= ~OUTER_REF_TABLE_BIT; uint i;
for (JOIN_TAB **tab=join->map2table; for (i=0 ; i < ref->key_parts ; i++,item++)
depend_map ; depend_map|=(*item)->used_tables();
tab++,depend_map>>=1 ) ref->depend_map=depend_map & ~OUTER_REF_TABLE_BIT;
{ depend_map&= ~OUTER_REF_TABLE_BIT;
if (depend_map & 1) for (JOIN_TAB **tab=join->map2table;
ref->depend_map|=(*tab)->ref.depend_map; depend_map ;
tab++,depend_map>>=1 )
{
if (depend_map & 1)
ref->depend_map|=(*tab)->ref.depend_map;
}
} }
} }
} }
...@@ -8030,7 +8302,7 @@ static void update_depend_map(JOIN *join) ...@@ -8030,7 +8302,7 @@ static void update_depend_map(JOIN *join)
/** Update the dependency map for the sort order. */ /** Update the dependency map for the sort order. */
static void update_depend_map(JOIN *join, ORDER *order) static void update_depend_map_for_order(JOIN *join, ORDER *order)
{ {
for (; order ; order=order->next) for (; order ; order=order->next)
{ {
...@@ -8081,17 +8353,25 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, ...@@ -8081,17 +8353,25 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
return change_list ? 0 : first_order; // No need to sort return change_list ? 0 : first_order; // No need to sort
ORDER *order,**prev_ptr; ORDER *order,**prev_ptr;
table_map first_table= join->join_tab[join->const_tables].table->map; table_map first_table;
table_map not_const_tables= ~join->const_table_map; table_map not_const_tables= ~join->const_table_map;
table_map ref; table_map ref;
bool first_is_base_table= FALSE;
DBUG_ENTER("remove_const"); DBUG_ENTER("remove_const");
if (join->join_tab[join->const_tables].table)
{
first_table= join->join_tab[join->const_tables].table->map;
first_is_base_table= TRUE;
}
prev_ptr= &first_order; prev_ptr= &first_order;
*simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1; *simple_order= *join->join_tab[join->const_tables].on_expr_ref ? 0 : 1;
/* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */ /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */
update_depend_map(join, first_order); update_depend_map_for_order(join, first_order);
for (order=first_order; order ; order=order->next) for (order=first_order; order ; order=order->next)
{ {
table_map order_tables=order->item[0]->used_tables(); table_map order_tables=order->item[0]->used_tables();
...@@ -8128,7 +8408,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, ...@@ -8128,7 +8408,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
continue; continue;
} }
if ((ref=order_tables & (not_const_tables ^ first_table))) if (first_is_base_table && (ref=order_tables & (not_const_tables ^ first_table)))
{ {
if (!(order_tables & first_table) && if (!(order_tables & first_table) &&
only_eq_ref_tables(join,first_order, ref)) only_eq_ref_tables(join,first_order, ref))
...@@ -8204,6 +8484,8 @@ static void clear_tables(JOIN *join) ...@@ -8204,6 +8484,8 @@ static void clear_tables(JOIN *join)
must clear only the non-const tables, as const tables must clear only the non-const tables, as const tables
are not re-calculated. are not re-calculated.
*/ */
//psergey2: this should be ok as it walks through TABLE*
// psergey2: What is this for? perhaps, we should reset the SJM temptables, too??
for (uint i=join->const_tables ; i < join->tables ; i++) for (uint i=join->const_tables ; i < join->tables ; i++)
mark_as_null_row(join->table[i]); // All fields are NULL mark_as_null_row(join->table[i]); // All fields are NULL
} }
...@@ -9025,7 +9307,18 @@ static int compare_fields_by_table_order(Item_field *field1, ...@@ -9025,7 +9307,18 @@ static int compare_fields_by_table_order(Item_field *field1,
if (outer_ref) if (outer_ref)
return cmp; return cmp;
JOIN_TAB **idx= (JOIN_TAB **) table_join_idx; JOIN_TAB **idx= (JOIN_TAB **) table_join_idx;
cmp= idx[field2->field->table->tablenr]-idx[field1->field->table->tablenr];
//psergey2:
JOIN_TAB *tab1= idx[field1->field->table->tablenr];
if (tab1->bush_root_tab)
tab1= tab1->bush_root_tab;
JOIN_TAB *tab2= idx[field2->field->table->tablenr];
if (tab2->bush_root_tab)
tab2= tab2->bush_root_tab;
cmp= tab2 - tab1;
return cmp < 0 ? -1 : (cmp ? 1 : 0); return cmp < 0 ? -1 : (cmp ? 1 : 0);
} }
...@@ -9111,7 +9404,8 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, ...@@ -9111,7 +9404,8 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
/* /*
Pick the "head" item: the constant one or the first in the join order Pick the "head" item: the constant one or the first in the join order
that's not inside some SJM nest. that's not inside some SJM nest. psergey2: out-of-date comment. It is ok
inside SJM, too.
*/ */
if (item_const) if (item_const)
head= item_const; head= item_const;
...@@ -12367,8 +12661,11 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) ...@@ -12367,8 +12661,11 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
Next_select_func end_select= setup_end_select_func(join); Next_select_func end_select= setup_end_select_func(join);
if (join->tables) if (join->tables)
{ {
join->join_tab[join->tables-1].next_select= end_select; //join->join_tab[join->tables-1].next_select= end_select;
//psergey3:
//int n_top_tables= join->join_tab_ranges.head()->end -
// join->join_tab_ranges.head()->start;
join->join_tab[join->top_jtrange_tables - 1].next_select= end_select;
join_tab=join->join_tab+join->const_tables; join_tab=join->join_tab+join->const_tables;
} }
join->send_records=0; join->send_records=0;
...@@ -12497,7 +12794,7 @@ int rr_sequential_and_unpack(READ_RECORD *info) ...@@ -12497,7 +12794,7 @@ int rr_sequential_and_unpack(READ_RECORD *info)
RETURN RETURN
One of enum_nested_loop_state values One of enum_nested_loop_state values
*/ */
#if 0
enum_nested_loop_state enum_nested_loop_state
sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
{ {
...@@ -12596,7 +12893,7 @@ sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) ...@@ -12596,7 +12893,7 @@ sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
end_of_records); end_of_records);
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
#endif
/* /*
Fill the join buffer with partial records, retrieve all full matches for them Fill the join buffer with partial records, retrieve all full matches for them
...@@ -12848,7 +13145,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) ...@@ -12848,7 +13145,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
} }
join->thd->row_count= 0; join->thd->row_count= 0;
if (do_jtbm_materialization_if_needed(join_tab)) if (join_tab_execution_startup(join_tab))
DBUG_RETURN(NESTED_LOOP_ERROR); DBUG_RETURN(NESTED_LOOP_ERROR);
error= (*join_tab->read_first_record)(join_tab); error= (*join_tab->read_first_record)(join_tab);
...@@ -13627,13 +13924,24 @@ int join_init_read_record(JOIN_TAB *tab) ...@@ -13627,13 +13924,24 @@ int join_init_read_record(JOIN_TAB *tab)
return (*tab->read_record.read_record)(&tab->read_record); return (*tab->read_record.read_record)(&tab->read_record);
} }
static int int
join_read_record_no_init(JOIN_TAB *tab) join_read_record_no_init(JOIN_TAB *tab)
{ {
Copy_field *save_copy, *save_copy_end;
save_copy= tab->read_record.copy_field;
save_copy_end= tab->read_record.copy_field_end;
init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select,1,1, FALSE);
tab->read_record.copy_field= save_copy;
tab->read_record.copy_field_end= save_copy_end;
tab->read_record.read_record= rr_sequential_and_unpack;
return (*tab->read_record.read_record)(&tab->read_record); return (*tab->read_record.read_record)(&tab->read_record);
} }
static int static int
join_read_first(JOIN_TAB *tab) join_read_first(JOIN_TAB *tab)
{ {
...@@ -14276,8 +14584,25 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -14276,8 +14584,25 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
*****************************************************************************/ *****************************************************************************/
/** /**
@return Check if a given equality is guaranteed to be true by use of ref access
1 if right_item is used removable reference key on left_item
SYNOPSIS
test_if_ref()
root_cond
left_item
right_item
DESCRIPTION
Check if the given "left_item = right_item" equality is guaranteed to be
true by use of [eq_]ref access method.
We need root_cond as we can't remove ON expressions even if employed ref
access guarantees that they are true. This is because TODO
RETURN
TRUE if right_item is used removable reference key on left_item
FALSE Otherwise
*/ */
bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
...@@ -14335,7 +14660,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) ...@@ -14335,7 +14660,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
SYNOPSIS SYNOPSIS
make_cond_for_table() make_cond_for_table()
cond Condition to analyze cond Condition to analyze
tables Tables for which "current field values" are available tables Tables for which "current field values" are available (this
includes used_table)
used_table Table that we're extracting the condition for (may used_table Table that we're extracting the condition for (may
also include PSEUDO_TABLE_BITS also include PSEUDO_TABLE_BITS
exclude_expensive_cond Do not push expensive conditions exclude_expensive_cond Do not push expensive conditions
...@@ -14369,7 +14695,8 @@ make_cond_for_table(Item *cond, table_map tables, table_map used_table, ...@@ -14369,7 +14695,8 @@ make_cond_for_table(Item *cond, table_map tables, table_map used_table,
return make_cond_for_table_from_pred(cond, cond, tables, used_table, return make_cond_for_table_from_pred(cond, cond, tables, used_table,
exclude_expensive_cond); exclude_expensive_cond);
} }
static Item * static Item *
make_cond_for_table_from_pred(Item *root_cond, Item *cond, make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables, table_map used_table, table_map tables, table_map used_table,
...@@ -14390,6 +14717,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14390,6 +14717,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
*/ */
!((used_table & 1) && cond->is_expensive())) !((used_table & 1) && cond->is_expensive()))
return (COND*) 0; // Already checked return (COND*) 0; // Already checked
if (cond->type() == Item::COND_ITEM) if (cond->type() == Item::COND_ITEM)
{ {
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
...@@ -14464,6 +14792,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14464,6 +14792,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
*/ */
(!used_table && exclude_expensive_cond && cond->is_expensive())) (!used_table && exclude_expensive_cond && cond->is_expensive()))
return (COND*) 0; // Can't check this yet return (COND*) 0; // Can't check this yet
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK) if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
return cond; // Not boolean op return cond; // Not boolean op
...@@ -14490,14 +14819,29 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond, ...@@ -14490,14 +14819,29 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
} }
/*
The difference of this from make_cond_for_table() is that we're in the
following state:
1. conditions referring to 'tables' have been checked
2. conditions referring to sjm_tables have been checked, too
3. We need condition that couldn't be checked in #1 or #2 but
can be checked when we get both (tables | sjm_tables).
*/
static COND * static COND *
make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables, make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables,
table_map sjm_tables) table_map sjm_tables)
{ {
/*
We assume that conditions that refer to only join prefix tables or
sjm_tables have already been checked.
*/
if ((!(cond->used_tables() & ~tables) || if ((!(cond->used_tables() & ~tables) ||
!(cond->used_tables() & ~sjm_tables))) !(cond->used_tables() & ~sjm_tables)))
return (COND*) 0; // Already checked return (COND*) 0; // Already checked
/* AND/OR recursive descent */
if (cond->type() == Item::COND_ITEM) if (cond->type() == Item::COND_ITEM)
{ {
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
...@@ -17934,16 +18278,29 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -17934,16 +18278,29 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{ {
table_map used_tables=0; table_map used_tables=0;
/* psergey2
uchar sjm_nests[MAX_TABLES]; uchar sjm_nests[MAX_TABLES];
uint sjm_nests_cur=0; uint sjm_nests_cur=0;
uint sjm_nests_end= 0; uint sjm_nests_end= 0;
uint end_table= join->tables; uint end_table= join->tables;
*/
bool printing_materialize_nest= FALSE; bool printing_materialize_nest= FALSE;
uint select_id= join->select_lex->select_number; uint select_id= join->select_lex->select_number;
for (uint i=0 ; i < end_table ; i++) //for (uint i=0 ; i < end_table ; i++)
List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges);
JOIN_TAB_RANGE *jt_range;
while ((jt_range= it++))
{ {
JOIN_TAB *tab=join->join_tab+i; if (jt_range != join->join_tab_ranges.head())
{
select_id= jt_range->start->emb_sj_nest->sj_subq_pred->get_identifier();
printing_materialize_nest= TRUE;
}
for (JOIN_TAB *tab= jt_range->start + 0; tab < jt_range->end; tab++)
{
//JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table; TABLE *table=tab->table;
TABLE_LIST *table_list= tab->table->pos_in_table_list; TABLE_LIST *table_list= tab->table->pos_in_table_list;
char buff[512]; char buff[512];
...@@ -17980,28 +18337,31 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -17980,28 +18337,31 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
Special processing for SJ-Materialization nests: print the fake table Special processing for SJ-Materialization nests: print the fake table
and delay printing of the SJM nest contents until later. and delay printing of the SJM nest contents until later.
*/ */
uint sj_strategy= join->best_positions[i].sj_strategy; //uint sj_strategy= join->best_positions[i].sj_strategy;
if (sj_is_materialize_strategy(sj_strategy) && //if (sj_is_materialize_strategy(sj_strategy) &&
!printing_materialize_nest) // /*!printing_materialize_nest*/)
if (tab->bush_children)
{ {
JOIN_TAB *ctab= tab->bush_children->start;
/* table */ /* table */
int len= my_snprintf(table_name_buffer, int len= my_snprintf(table_name_buffer,
sizeof(table_name_buffer)-1, sizeof(table_name_buffer)-1,
"<subquery%d>", "<subquery%d>",
tab->emb_sj_nest->sj_subq_pred->get_identifier()); ctab->emb_sj_nest->sj_subq_pred->get_identifier());
item_list.push_back(new Item_string(table_name_buffer, len, cs)); item_list.push_back(new Item_string(table_name_buffer, len, cs));
/* partitions */ /* partitions */
if (join->thd->lex->describe & DESCRIBE_PARTITIONS) if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
item_list.push_back(item_null); item_list.push_back(item_null);
/* type */ /* type */
uint type= (sj_strategy == SJ_OPT_MATERIALIZE_SCAN)? JT_ALL : JT_EQ_REF; uint is_scan= test(ctab->emb_sj_nest->sj_mat_info->is_sj_scan);
uint type= is_scan? JT_ALL : JT_EQ_REF;
item_list.push_back(new Item_string(join_type_str[type], item_list.push_back(new Item_string(join_type_str[type],
strlen(join_type_str[type]), strlen(join_type_str[type]),
cs)); cs));
/* possible_keys */ /* possible_keys */
item_list.push_back(new Item_string("unique_key", item_list.push_back(new Item_string("unique_key",
strlen("unique_key"), cs)); strlen("unique_key"), cs));
if (sj_strategy == SJ_OPT_MATERIALIZE_SCAN) if (is_scan)
{ {
item_list.push_back(item_null); /* key */ item_list.push_back(item_null); /* key */
item_list.push_back(item_null); /* key_len */ item_list.push_back(item_null); /* key_len */
...@@ -18012,15 +18372,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18012,15 +18372,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* key */ /* key */
item_list.push_back(new Item_string("unique_key", strlen("unique_key"), cs)); item_list.push_back(new Item_string("unique_key", strlen("unique_key"), cs));
/* key_len */ /* key_len */
uint klen= tab->emb_sj_nest->sj_mat_info->table->key_info[0].key_length; uint klen= ctab->emb_sj_nest->sj_mat_info->table->key_info[0].key_length;
uint buflen= longlong2str(klen, keylen_str_buf, 10) - keylen_str_buf; uint buflen= longlong2str(klen, keylen_str_buf, 10) - keylen_str_buf;
item_list.push_back(new Item_string(keylen_str_buf, buflen, cs)); item_list.push_back(new Item_string(keylen_str_buf, buflen, cs));
/* ref */ /* ref */
item_list.push_back(new Item_string("func", strlen("func"), cs)); item_list.push_back(new Item_string("func", strlen("func"), cs));
} }
/* rows */ /* rows */
ha_rows rows= (sj_strategy == SJ_OPT_MATERIALIZE_SCAN)? ha_rows rows= is_scan ? ctab->emb_sj_nest->sj_mat_info->rows : 1;
tab->emb_sj_nest->sj_mat_info->rows : 1;
item_list.push_back(new Item_int((longlong)rows, item_list.push_back(new Item_int((longlong)rows,
MY_INT64_NUM_DECIMAL_DIGITS)); MY_INT64_NUM_DECIMAL_DIGITS));
/* filtered */ /* filtered */
...@@ -18049,8 +18408,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18049,8 +18408,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(str, extra_len, cs)); item_list.push_back(new Item_string(str, extra_len, cs));
/* Register the nest for further processing: */ /* Register the nest for further processing: */
sjm_nests[sjm_nests_end++]= i; // sjm_nests[sjm_nests_end++]= i;
i += join->best_positions[i].n_sj_tables-1; //i += join->best_positions[i].n_sj_tables-1;
goto loop_end; goto loop_end;
} }
...@@ -18215,7 +18574,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18215,7 +18574,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else if (tab->type == JT_NEXT || tab->type == JT_ALL) else if (tab->type == JT_NEXT || tab->type == JT_ALL)
examined_rows= tab->limit ? tab->limit : tab->records; examined_rows= tab->limit ? tab->limit : tab->records;
else else
examined_rows=(ha_rows)join->best_positions[i].records_read; //examined_rows=(ha_rows)join->best_positions[i].records_read;
examined_rows=(ha_rows)tab->records_read;
item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows,
MY_INT64_NUM_DECIMAL_DIGITS)); MY_INT64_NUM_DECIMAL_DIGITS));
...@@ -18236,7 +18596,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18236,7 +18596,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
*/ */
float f= 0.0; float f= 0.0;
if (examined_rows) if (examined_rows)
f= (float) (100.0 * join->best_positions[i].records_read / f= (float) (100.0 * tab->records_read/*join->best_positions[i].records_read*/ /
examined_rows); examined_rows);
item_list.push_back(new Item_float(f, 2)); item_list.push_back(new Item_float(f, 2));
} }
...@@ -18400,25 +18760,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18400,25 +18760,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
} }
} }
/*
if (sj_is_materialize_strategy(sj_strategy))
{
if (join->best_positions[i].n_sj_tables == 1)
extra.append(STRING_WITH_LEN("; Materialize"));
else
{
last_sjm_table= i + join->best_positions[i].n_sj_tables - 1;
extra.append(STRING_WITH_LEN("; Start materialize"));
}
if (sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
extra.append(STRING_WITH_LEN("; Scan"));
}
else if (last_sjm_table == i)
{
extra.append(STRING_WITH_LEN("; End materialize"));
}
*/
for (uint part= 0; part < tab->ref.key_parts; part++) for (uint part= 0; part < tab->ref.key_parts; part++)
{ {
if (tab->ref.cond_guards[part]) if (tab->ref.cond_guards[part])
...@@ -18428,7 +18769,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18428,7 +18769,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
} }
} }
if (i > 0 && tab[-1].next_select == sub_select_cache) //if (i > 0 && tab[-1].next_select == sub_select_cache)
if ((tab != jt_range->start) && tab[-1].next_select == sub_select_cache)
extra.append(STRING_WITH_LEN("; Using join buffer")); extra.append(STRING_WITH_LEN("; Using join buffer"));
/* Skip initial "; "*/ /* Skip initial "; "*/
...@@ -18442,19 +18784,21 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -18442,19 +18784,21 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(str, len, cs)); item_list.push_back(new Item_string(str, len, cs));
} }
loop_end: loop_end:
/* psergey2
if (i+1 == end_table && sjm_nests_cur != sjm_nests_end) if (i+1 == end_table && sjm_nests_cur != sjm_nests_end)
{ {
printing_materialize_nest= TRUE; printing_materialize_nest= TRUE;
i= sjm_nests[sjm_nests_cur++] - 1; i= sjm_nests[sjm_nests_cur++] - 1;
end_table= (i+1) + join->best_positions[i+1].n_sj_tables; end_table= (i+1) + join->best_positions[i+1].n_sj_tables;
select_id= join->join_tab[i+1].emb_sj_nest->sj_subq_pred->get_identifier(); select_id= join->join_tab[i+1].emb_sj_nest->sj_subq_pred->get_identifier();
} }*/
// For next iteration // For next iteration
used_tables|=table->map; used_tables|=table->map;
if (result->send_data(item_list)) if (result->send_data(item_list))
join->error= 1; join->error= 1;
} }
}
} }
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit(); for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit; unit;
......
...@@ -142,13 +142,25 @@ enum enum_nested_loop_state ...@@ -142,13 +142,25 @@ enum enum_nested_loop_state
typedef enum_nested_loop_state typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool); (*Next_select_func)(JOIN *, struct st_join_table *, bool);
/*
Function prototype for reading first record for a join tab
RETURN
0 - OK
-1 - Record not found
Other - Error
*/
typedef int (*Read_record_func)(struct st_join_table *tab); typedef int (*Read_record_func)(struct st_join_table *tab);
Next_select_func setup_end_select_func(JOIN *join); Next_select_func setup_end_select_func(JOIN *join);
int rr_sequential(READ_RECORD *info); int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
class JOIN_CACHE; class JOIN_CACHE;
class SJ_TMP_TABLE; class SJ_TMP_TABLE;
class JOIN_TAB_RANGE;
typedef struct st_join_table { typedef struct st_join_table {
st_join_table() {} /* Remove gcc warning */ st_join_table() {} /* Remove gcc warning */
...@@ -173,6 +185,14 @@ typedef struct st_join_table { ...@@ -173,6 +185,14 @@ typedef struct st_join_table {
st_join_table *last_inner; /**< last table table for embedding outer join */ st_join_table *last_inner; /**< last table table for embedding outer join */
st_join_table *first_upper; /**< first inner table for embedding outer join */ st_join_table *first_upper; /**< first inner table for embedding outer join */
st_join_table *first_unmatched; /**< used for optimization purposes only */ st_join_table *first_unmatched; /**< used for optimization purposes only */
/*
psergey2: for join tabs that are inside a bush: root of this bush.
*/
st_join_table *bush_root_tab;
bool last_leaf_in_bush;
JOIN_TAB_RANGE *bush_children;
/* Special content for EXPLAIN 'Extra' column or NULL if none */ /* Special content for EXPLAIN 'Extra' column or NULL if none */
const char *info; const char *info;
...@@ -211,6 +231,8 @@ typedef struct st_join_table { ...@@ -211,6 +231,8 @@ typedef struct st_join_table {
E(#records) is in found_records. E(#records) is in found_records.
*/ */
double read_time; double read_time;
ha_rows records_read;
/* Startup cost for execution */ /* Startup cost for execution */
double startup_cost; double startup_cost;
...@@ -292,7 +314,7 @@ typedef struct st_join_table { ...@@ -292,7 +314,7 @@ typedef struct st_join_table {
/* /*
Semi-join strategy to be used for this join table. This is a copy of Semi-join strategy to be used for this join table. This is a copy of
POSITION::sj_strategy field. This field is set up by the POSITION::sj_strategy field. This field is set up by the
fix_semijion_strategies_for_picked_join_order. fix_semijoin_strategies_for_picked_join_order.
*/ */
uint sj_strategy; uint sj_strategy;
...@@ -1365,17 +1387,29 @@ inline bool sj_is_materialize_strategy(uint strategy) ...@@ -1365,17 +1387,29 @@ inline bool sj_is_materialize_strategy(uint strategy)
} }
class JOIN_TAB_RANGE: public Sql_alloc
{
public:
JOIN_TAB *start;
JOIN_TAB *end;
};
class JOIN :public Sql_alloc class JOIN :public Sql_alloc
{ {
JOIN(const JOIN &rhs); /**< not implemented */ JOIN(const JOIN &rhs); /**< not implemented */
JOIN& operator=(const JOIN &rhs); /**< not implemented */ JOIN& operator=(const JOIN &rhs); /**< not implemented */
public: public:
JOIN_TAB *join_tab,**best_ref; JOIN_TAB *join_tab, **best_ref;
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
List<JOIN_TAB_RANGE> join_tab_ranges;
/* /*
Base tables participating in the join. After join optimization is done, the Base tables participating in the join. After join optimization is done, the
tables are stored in the join order. tables are stored in the join order (but the only really important part is
that const tables are first).
*/ */
TABLE **table; TABLE **table;
/** /**
...@@ -1387,6 +1421,13 @@ class JOIN :public Sql_alloc ...@@ -1387,6 +1421,13 @@ class JOIN :public Sql_alloc
uint tables; /**< Number of tables in the join */ uint tables; /**< Number of tables in the join */
uint outer_tables; /**< Number of tables that are not inside semijoin */ uint outer_tables; /**< Number of tables that are not inside semijoin */
uint const_tables; uint const_tables;
/*
Number of tables in the top join_tab array. Normally this matches
(join_tab_ranges.head()->end - join_tab_ranges.head()->start).
We keep it here so that it is saved/restored with JOIN::restore_tmp.
*/
uint top_jtrange_tables;
uint send_group_parts; uint send_group_parts;
bool group; /**< If query contains GROUP BY clause */ bool group; /**< If query contains GROUP BY clause */
/** /**
...@@ -1595,6 +1636,7 @@ class JOIN :public Sql_alloc ...@@ -1595,6 +1636,7 @@ class JOIN :public Sql_alloc
join_tab= join_tab_save= 0; join_tab= join_tab_save= 0;
table= 0; table= 0;
tables= 0; tables= 0;
top_jtrange_tables= 0;
const_tables= 0; const_tables= 0;
eliminated_tables= 0; eliminated_tables= 0;
join_list= 0; join_list= 0;
...@@ -1933,6 +1975,7 @@ COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value); ...@@ -1933,6 +1975,7 @@ COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
int test_if_item_cache_changed(List<Cached_item> &list); int test_if_item_cache_changed(List<Cached_item> &list);
void calc_used_field_length(THD *thd, JOIN_TAB *join_tab); void calc_used_field_length(THD *thd, JOIN_TAB *join_tab);
int join_init_read_record(JOIN_TAB *tab); int join_init_read_record(JOIN_TAB *tab);
int join_read_record_no_init(JOIN_TAB *tab);
void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key); void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key);
inline Item * and_items(Item* cond, Item *item) inline Item * and_items(Item* cond, Item *item)
{ {
......
...@@ -165,58 +165,66 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length) ...@@ -165,58 +165,66 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
void void
TEST_join(JOIN *join) TEST_join(JOIN *join)
{ {
uint i,ref; uint ref;
int i;
List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges);
JOIN_TAB_RANGE *jt_range;
DBUG_ENTER("TEST_join"); DBUG_ENTER("TEST_join");
/*
Assemble results of all the calls to full_name() first,
in order not to garble the tabular output below.
*/
String ref_key_parts[MAX_TABLES];
for (i= 0; i < join->tables; i++)
{
JOIN_TAB *tab= join->join_tab + i;
for (ref= 0; ref < tab->ref.key_parts; ref++)
{
ref_key_parts[i].append(tab->ref.items[ref]->full_name());
ref_key_parts[i].append(" ");
}
}
DBUG_LOCK_FILE; DBUG_LOCK_FILE;
VOID(fputs("\nInfo about JOIN\n",DBUG_FILE)); VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
for (i=0 ; i < join->tables ; i++)
while ((jt_range= it++))
{ {
JOIN_TAB *tab=join->join_tab+i; /*
TABLE *form=tab->table; Assemble results of all the calls to full_name() first,
char key_map_buff[128]; in order not to garble the tabular output below.
fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n", */
form->alias, String ref_key_parts[MAX_TABLES];
join_type_str[tab->type], for (i= 0; i < (jt_range->end - jt_range->start); i++)
tab->keys.print(key_map_buff),
tab->ref.key_parts,
tab->ref.key,
tab->ref.key_length);
if (tab->select)
{ {
char buf[MAX_KEY/8+1]; JOIN_TAB *tab= jt_range->start + i;
if (tab->use_quick == 2) for (ref= 0; ref < tab->ref.key_parts; ref++)
fprintf(DBUG_FILE,
" quick select checked for each record (keys: %s)\n",
tab->select->quick_keys.print(buf));
else if (tab->select->quick)
{ {
fprintf(DBUG_FILE, " quick select used:\n"); ref_key_parts[i].append(tab->ref.items[ref]->full_name());
tab->select->quick->dbug_dump(18, FALSE); ref_key_parts[i].append(" ");
} }
else
VOID(fputs(" select used\n",DBUG_FILE));
} }
if (tab->ref.key_parts)
for (i= 0; i < (jt_range->end - jt_range->start); i++)
{ {
fprintf(DBUG_FILE, JOIN_TAB *tab= jt_range->start + i;
" refs: %s\n", ref_key_parts[i].ptr()); TABLE *form=tab->table;
char key_map_buff[128];
fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n",
form->alias,
join_type_str[tab->type],
tab->keys.print(key_map_buff),
tab->ref.key_parts,
tab->ref.key,
tab->ref.key_length);
if (tab->select)
{
char buf[MAX_KEY/8+1];
if (tab->use_quick == 2)
fprintf(DBUG_FILE,
" quick select checked for each record (keys: %s)\n",
tab->select->quick_keys.print(buf));
else if (tab->select->quick)
{
fprintf(DBUG_FILE, " quick select used:\n");
tab->select->quick->dbug_dump(18, FALSE);
}
else
VOID(fputs(" select used\n",DBUG_FILE));
}
if (tab->ref.key_parts)
{
fprintf(DBUG_FILE,
" refs: %s\n", ref_key_parts[i].ptr());
}
} }
VOID(fputs("\n",DBUG_FILE));
} }
DBUG_UNLOCK_FILE; DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
...@@ -703,6 +703,7 @@ bool st_select_lex_unit::cleanup() ...@@ -703,6 +703,7 @@ bool st_select_lex_unit::cleanup()
{ {
join->tables_list= 0; join->tables_list= 0;
join->tables= 0; join->tables= 0;
join->top_jtrange_tables= 0;
} }
error|= fake_select_lex->cleanup(); error|= fake_select_lex->cleanup();
/* /*
......
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