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)
It's a field from an materialized semi-join. We can substitute it only
for a field from the same semi-join.
*/
#if 0
psergey3:remove:
JOIN_TAB *first;
JOIN *join= field_tab->join;
int tab_idx= field_tab - field_tab->join->join_tab;
......@@ -5746,10 +5748,12 @@ Item_field* Item_equal::get_first(Item_field *field)
// Found first tab that doesn't belong to current SJ.
break;
}
#endif
/* Find an item to substitute for. */
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
......
......@@ -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
......@@ -2628,10 +2631,11 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
TRUE Error
*/
bool setup_sj_materialization(JOIN_TAB *tab)
bool setup_sj_materialization(JOIN_TAB *sjm_tab)
{
uint i;
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;
SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info;
THD *thd= tab->join->thd;
......@@ -2659,10 +2663,13 @@ bool setup_sj_materialization(JOIN_TAB *tab)
DBUG_RETURN(TRUE); /* purecov: inspected */
sjm->table->file->extra(HA_EXTRA_WRITE_CACHE);
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->sjm_info_list.push_back(sjm);
sjm->materialized= FALSE;
sjm_tab->table= sjm->table;
if (!sjm->is_sj_scan)
{
KEY *tmp_key; /* The only index on the temporary table. */
......@@ -2675,8 +2682,9 @@ bool setup_sj_materialization(JOIN_TAB *tab)
temptable.
*/
TABLE_REF *tab_ref;
if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF))))
DBUG_RETURN(TRUE); /* purecov: inspected */
//if (!(tab_ref= (TABLE_REF*) thd->alloc(sizeof(TABLE_REF))))
// DBUG_RETURN(TRUE); /* purecov: inspected */
tab_ref= &sjm_tab->ref;
tab_ref->key= 0; /* The only temp table index. */
tab_ref->key_length= tmp_key->key_length;
if (!(tab_ref->key_buff=
......@@ -2733,6 +2741,7 @@ bool setup_sj_materialization(JOIN_TAB *tab)
if (!(sjm->in_equality= create_subq_in_equalities(thd, sjm,
emb_sj_nest->sj_subq_pred)))
DBUG_RETURN(TRUE); /* purecov: inspected */
sjm_tab->type= JT_EQ_REF;
}
else
{
......@@ -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 */
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);
}
......@@ -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;
if (tab->table->pos_in_table_list &&
(in_subs= tab->table->pos_in_table_list->jtbm_subselect))
......@@ -3936,9 +3973,54 @@ bool do_jtbm_materialization_if_needed(JOIN_TAB *tab)
hash_sj_engine->is_materialized= TRUE;
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,
double *scan_time,
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)
/* 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;
goto finish;
......
......@@ -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 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, ORDER *order);
static void update_depend_map_for_order(JOIN *join, ORDER *order);
static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond,
bool change_list, bool *simple_order);
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);
void get_partial_join_cost(JOIN *join, uint idx, double *read_time_arg,
double *record_count_arg);
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,
bool *inherited_fl);
......@@ -1009,7 +1007,15 @@ JOIN::optimize()
Permorm the the optimization on fields evaluation mentioned above
for all on expressions.
*/
for (JOIN_TAB *tab= join_tab + const_tables; tab < join_tab + tables ; tab++)
{
List_iterator<JOIN_TAB_RANGE> it(join_tab_ranges);
JOIN_TAB_RANGE *jt_range;
bool first= TRUE;
while ((jt_range= it++))
{
for (JOIN_TAB *tab= jt_range->start + (first ? const_tables : 0);
tab < jt_range->end; tab++)
{
if (*tab->on_expr_ref)
{
......@@ -1019,6 +1025,9 @@ JOIN::optimize()
(*tab->on_expr_ref)->update_used_tables();
}
}
first= FALSE;
}
}
if (conds &&!outer_join && const_table_map != found_const_table_map &&
(select_options & SELECT_DESCRIBE) &&
......@@ -1026,6 +1035,7 @@ JOIN::optimize()
{
conds=new Item_int((longlong) 0,1); // Always false
}
if (make_join_select(this, select, conds))
{
zero_result_cause=
......@@ -1289,7 +1299,8 @@ JOIN::optimize()
if (need_tmp || select_distinct || group_list || order)
{
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););
......@@ -2099,7 +2110,7 @@ JOIN::exec()
WHERE clause for any tables after the sorted one.
*/
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++)
{
/*
......@@ -2569,6 +2580,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
all_table_map|= s->table->map;
s->join=join;
s->info=0; // For describe
s->bush_root_tab= NULL;
s->dependent= tables->dep_tables;
s->key_dependent= 0;
......@@ -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
......@@ -5867,6 +5972,11 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref)
- create join->join_tab array and put there the JOIN_TABs in the join order
- 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
FALSE OK
TRUE Out of memory
......@@ -5875,7 +5985,7 @@ prev_record_reads(JOIN *join, uint idx, table_map found_ref)
static bool
get_best_combination(JOIN *join)
{
uint i,tablenr;
uint tablenr;
table_map used_tables;
JOIN_TAB *join_tab,*j;
KEYUSE *keyuse;
......@@ -5894,10 +6004,72 @@ get_best_combination(JOIN *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++)
{
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;
if (sjm_nest_end)
j->bush_root_tab= sjm_saved_tab;
else
root_range->end= j+1;
form=join->table[tablenr]=j->table;
used_tables|= form->map;
form->reginfo.join_tab=j;
......@@ -5905,14 +6077,14 @@ get_best_combination(JOIN *join)
form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN
DBUG_PRINT("info",("type: %d", j->type));
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->ref.key = -1;
j->ref.key_parts=0;
if (j->type == JT_SYSTEM)
continue;
goto loop_end;
if (j->keys.is_clear_all() || !(keyuse= join->best_positions[tablenr].key) ||
(join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN))
{
......@@ -5923,10 +6095,24 @@ get_best_combination(JOIN *join)
}
else if (create_ref_for_key(join, j, keyuse, used_tables))
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;
}
}
join->top_jtrange_tables= join->join_tab_ranges.head()->end -
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;
//for (i=0 ; i < table_count ; i++)
// join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i;
update_depend_map(join);
DBUG_RETURN(0);
}
......@@ -6175,6 +6361,11 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
DBUG_RETURN(TRUE); /* purecov: inspected */
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;
tables= 1;
const_tables= 0;
......@@ -6214,6 +6405,9 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
join_tab->do_firstmatch= NULL;
join_tab->loosescan_match_tab= 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));
tmp_table->status=0;
tmp_table->null_row=0;
......@@ -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
we've inferred from ref/eq_ref access performed.
......@@ -6292,9 +6487,13 @@ inline void add_cond_and_fix(Item **e1, Item *e2)
static void add_not_null_conds(JOIN *join)
{
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 ||
tab->type == JT_REF_OR_NULL) &&
!tab->table->maybe_null)
......@@ -6418,10 +6617,18 @@ static void
make_outerjoin_info(JOIN *join)
{
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_RANGE *jt_range;
while ((jt_range= it++))
{
for (JOIN_TAB *tab=jt_range->start + (top ? join->const_tables : 0);
tab != jt_range->end; tab++)
{
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
if (!table)
continue; //psergey2: fix this when we get SJM+outer joins really working.
TABLE_LIST *tbl= table->pos_in_table_list;
TABLE_LIST *embedding= tbl->embedding;
......@@ -6471,6 +6678,8 @@ make_outerjoin_info(JOIN *join)
}
}
}
top= FALSE;
}
DBUG_VOID_RETURN;
}
......@@ -6512,8 +6721,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->const_table_map,
(table_map) 0, TRUE);
DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
for (JOIN_TAB *tab= join->join_tab+join->const_tables;
tab < join->join_tab+join->tables ; tab++)
// psergey2: not extracting conditions from inside bushy nests?
//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)
{
......@@ -6552,18 +6765,27 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
JOIN_TAB *tab;
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:
SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X
*/
JOIN_TAB *first_inner_tab= tab->first_inner;
//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;
COND *tmp;
// psergey2-todo: is the below ok? seems to be yes.
/*
Tables that are within SJ-Materialization nests cannot have their
conditions referring to preceding non-const tables.
......@@ -6607,8 +6829,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
tmp= NULL;
if (cond)
{
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)
{ // Outer join
if (tab->type != JT_ALL)
......@@ -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
that turns if off just before null complemented row for
outer joins is formed. Thus, the condition from an
......@@ -6815,8 +7049,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
*/
/* First push down constant conditions from on expressions */
for (JOIN_TAB *join_tab= join->join_tab+join->const_tables;
join_tab < join->join_tab+join->tables ; join_tab++)
//for (JOIN_TAB *join_tab= join->join_tab+join->const_tables;
// 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)
{
......@@ -6845,6 +7082,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *last_tab= 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.
An on expression is always attached to it.
......@@ -6853,7 +7091,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
table_map used_tables2= (join->const_table_map |
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;
used_tables2|= current_map;
......@@ -6899,7 +7140,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
first_inner_tab= first_inner_tab->first_upper;
}
#if 0
psergey2-todo:remove:
if (save_used_tables && !(used_tables &
~(tab->emb_sj_nest->sj_inner_tables |
join->const_table_map | PSEUDO_TABLE_BITS)))
......@@ -6925,7 +7167,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
used_tables= save_used_tables | used_tables;
save_used_tables= 0;
}
#endif
}
}
DBUG_RETURN(0);
......@@ -7114,7 +7356,7 @@ void revise_cache_usage(JOIN_TAB *join_tab)
SYNOPSIS
end_sj_materialize()
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
combination
TRUE <=> EOF (no action)
......@@ -7132,7 +7374,7 @@ void revise_cache_usage(JOIN_TAB *join_tab)
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)
{
int error;
......@@ -7407,8 +7649,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
bool statistics= test(!(join->select_options & SELECT_DESCRIBE));
bool ordered_set= 0;
bool sorted= 1;
uint first_sjm_table= MAX_TABLES;
uint last_sjm_table= MAX_TABLES;
//uint first_sjm_table= MAX_TABLES;
//uint last_sjm_table= MAX_TABLES;
DBUG_ENTER("make_join_readinfo");
......@@ -7416,15 +7658,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
setup_semijoin_dups_elimination(join, options, no_jbuf_after))
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;
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
......@@ -7442,24 +7683,42 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
tab->sorted= sorted;
sorted= 0; // only first must be sorted
if (tab->loosescan_match_tab)
{
if (!(tab->loosescan_buf= (uchar*)join->thd->alloc(tab->
loosescan_key_len)))
return TRUE; /* purecov: inspected */
}
if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy))
//if (sj_is_materialize_strategy(join->best_positions[i].sj_strategy))
if (tab->bush_children) // SJM
{
/* This is a start of semi-join nest */
first_sjm_table= i;
last_sjm_table= i + join->best_positions[i].n_sj_tables;
//first_sjm_table= i;
//last_sjm_table= i + join->best_positions[i].n_sj_tables;
/*
psergey2: dont:
if (i == join->const_tables)
join->first_select= sub_select_sjm;
else
tab[-1].next_select= sub_select_sjm;
*/
if (setup_sj_materialization(tab))
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;
pick_table_access_method (tab);
......@@ -7551,6 +7810,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
}
else
{
if (!tab->bush_children)
tab->read_first_record= join_init_read_record;
if (i == join->const_tables)
{
......@@ -7629,13 +7889,16 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
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
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;
if (tab->use_join_cache)
......@@ -7692,6 +7955,9 @@ bool error_if_full_join(JOIN *join)
/**
cleanup JOIN_TAB.
DESCRIPTION
This is invoked when we've finished all join executions.
*/
void JOIN_TAB::cleanup()
......@@ -7700,6 +7966,7 @@ void JOIN_TAB::cleanup()
select= 0;
delete quick;
quick= 0;
//psergey3-todo: empty merged SJM temptables here.
if (cache)
{
cache->free();
......@@ -7848,7 +8115,7 @@ void JOIN::cleanup(bool full)
if (table)
{
JOIN_TAB *tab,*end;
JOIN_TAB *tab;
/*
Only a sorted table may be cached. This sorted table is always the
first non const table in join->table
......@@ -7861,13 +8128,13 @@ void JOIN::cleanup(bool 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();
table= 0;
}
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)
tab->table->file->ha_index_or_rnd_end();
......@@ -8005,9 +8272,13 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
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++))
{
for (JOIN_TAB *join_tab=jt_range->start; join_tab != jt_range->end;
join_tab++)
{
TABLE_REF *ref= &join_tab->ref;
table_map depend_map=0;
......@@ -8025,12 +8296,13 @@ static void update_depend_map(JOIN *join)
ref->depend_map|=(*tab)->ref.depend_map;
}
}
}
}
/** 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)
{
......@@ -8081,17 +8353,25 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
return change_list ? 0 : first_order; // No need to sort
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 ref;
bool first_is_base_table= FALSE;
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;
*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 */
update_depend_map(join, first_order);
update_depend_map_for_order(join, first_order);
for (order=first_order; order ; order=order->next)
{
table_map order_tables=order->item[0]->used_tables();
......@@ -8128,7 +8408,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
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) &&
only_eq_ref_tables(join,first_order, ref))
......@@ -8204,6 +8484,8 @@ static void clear_tables(JOIN *join)
must clear only the non-const tables, as const tables
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++)
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,
if (outer_ref)
return cmp;
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);
}
......@@ -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
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)
head= item_const;
......@@ -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);
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->send_records=0;
......@@ -12497,7 +12794,7 @@ int rr_sequential_and_unpack(READ_RECORD *info)
RETURN
One of enum_nested_loop_state values
*/
#if 0
enum_nested_loop_state
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);
DBUG_RETURN(rc);
}
#endif
/*
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)
}
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);
error= (*join_tab->read_first_record)(join_tab);
......@@ -13627,13 +13924,24 @@ int join_init_read_record(JOIN_TAB *tab)
return (*tab->read_record.read_record)(&tab->read_record);
}
static int
int
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);
}
static int
join_read_first(JOIN_TAB *tab)
{
......@@ -14276,8 +14584,25 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
*****************************************************************************/
/**
@return
1 if right_item is used removable reference key on left_item
Check if a given equality is guaranteed to be true by use of ref access
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)
......@@ -14335,7 +14660,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
SYNOPSIS
make_cond_for_table()
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
also include PSEUDO_TABLE_BITS
exclude_expensive_cond Do not push expensive conditions
......@@ -14370,6 +14696,7 @@ make_cond_for_table(Item *cond, table_map tables, table_map used_table,
exclude_expensive_cond);
}
static Item *
make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_map tables, table_map used_table,
......@@ -14390,6 +14717,7 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
*/
!((used_table & 1) && cond->is_expensive()))
return (COND*) 0; // Already checked
if (cond->type() == Item::COND_ITEM)
{
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,
*/
(!used_table && exclude_expensive_cond && cond->is_expensive()))
return (COND*) 0; // Can't check this yet
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
return cond; // Not boolean op
......@@ -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 *
make_cond_after_sjm(Item *root_cond, Item *cond, table_map 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) ||
!(cond->used_tables() & ~sjm_tables)))
return (COND*) 0; // Already checked
/* AND/OR recursive descent */
if (cond->type() == Item::COND_ITEM)
{
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,
{
table_map used_tables=0;
/* psergey2
uchar sjm_nests[MAX_TABLES];
uint sjm_nests_cur=0;
uint sjm_nests_end= 0;
uint end_table= join->tables;
*/
bool printing_materialize_nest= FALSE;
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_LIST *table_list= tab->table->pos_in_table_list;
char buff[512];
......@@ -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
and delay printing of the SJM nest contents until later.
*/
uint sj_strategy= join->best_positions[i].sj_strategy;
if (sj_is_materialize_strategy(sj_strategy) &&
!printing_materialize_nest)
//uint sj_strategy= join->best_positions[i].sj_strategy;
//if (sj_is_materialize_strategy(sj_strategy) &&
// /*!printing_materialize_nest*/)
if (tab->bush_children)
{
JOIN_TAB *ctab= tab->bush_children->start;
/* table */
int len= my_snprintf(table_name_buffer,
sizeof(table_name_buffer)-1,
"<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));
/* partitions */
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* 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],
strlen(join_type_str[type]),
cs));
/* possible_keys */
item_list.push_back(new Item_string("unique_key",
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_len */
......@@ -18012,15 +18372,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* key */
item_list.push_back(new Item_string("unique_key", strlen("unique_key"), cs));
/* 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;
item_list.push_back(new Item_string(keylen_str_buf, buflen, cs));
/* ref */
item_list.push_back(new Item_string("func", strlen("func"), cs));
}
/* rows */
ha_rows rows= (sj_strategy == SJ_OPT_MATERIALIZE_SCAN)?
tab->emb_sj_nest->sj_mat_info->rows : 1;
ha_rows rows= is_scan ? ctab->emb_sj_nest->sj_mat_info->rows : 1;
item_list.push_back(new Item_int((longlong)rows,
MY_INT64_NUM_DECIMAL_DIGITS));
/* filtered */
......@@ -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));
/* Register the nest for further processing: */
sjm_nests[sjm_nests_end++]= i;
i += join->best_positions[i].n_sj_tables-1;
// sjm_nests[sjm_nests_end++]= i;
//i += join->best_positions[i].n_sj_tables-1;
goto loop_end;
}
......@@ -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)
examined_rows= tab->limit ? tab->limit : tab->records;
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,
MY_INT64_NUM_DECIMAL_DIGITS));
......@@ -18236,7 +18596,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
*/
float f= 0.0;
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);
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,
}
}
/*
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++)
{
if (tab->ref.cond_guards[part])
......@@ -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"));
/* Skip initial "; "*/
......@@ -18442,13 +18784,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(str, len, cs));
}
loop_end:
/* psergey2
if (i+1 == end_table && sjm_nests_cur != sjm_nests_end)
{
printing_materialize_nest= TRUE;
i= sjm_nests[sjm_nests_cur++] - 1;
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();
}
}*/
// For next iteration
used_tables|=table->map;
......@@ -18456,6 +18799,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
join->error= 1;
}
}
}
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
......
......@@ -142,13 +142,25 @@ enum enum_nested_loop_state
typedef enum_nested_loop_state
(*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);
Next_select_func setup_end_select_func(JOIN *join);
int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
class JOIN_CACHE;
class SJ_TMP_TABLE;
class JOIN_TAB_RANGE;
typedef struct st_join_table {
st_join_table() {} /* Remove gcc warning */
......@@ -174,6 +186,14 @@ typedef struct st_join_table {
st_join_table *first_upper; /**< first inner table for embedding outer join */
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 */
const char *info;
/*
......@@ -212,6 +232,8 @@ typedef struct st_join_table {
*/
double read_time;
ha_rows records_read;
/* Startup cost for execution */
double startup_cost;
......@@ -292,7 +314,7 @@ typedef struct st_join_table {
/*
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
fix_semijion_strategies_for_picked_join_order.
fix_semijoin_strategies_for_picked_join_order.
*/
uint sj_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
{
JOIN(const JOIN &rhs); /**< not implemented */
JOIN& operator=(const JOIN &rhs); /**< not implemented */
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 *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
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;
/**
......@@ -1387,6 +1421,13 @@ class JOIN :public Sql_alloc
uint tables; /**< Number of tables in the join */
uint outer_tables; /**< Number of tables that are not inside semijoin */
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;
bool group; /**< If query contains GROUP BY clause */
/**
......@@ -1595,6 +1636,7 @@ class JOIN :public Sql_alloc
join_tab= join_tab_save= 0;
table= 0;
tables= 0;
top_jtrange_tables= 0;
const_tables= 0;
eliminated_tables= 0;
join_list= 0;
......@@ -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);
void calc_used_field_length(THD *thd, JOIN_TAB *join_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);
inline Item * and_items(Item* cond, Item *item)
{
......
......@@ -165,17 +165,25 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
void
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_LOCK_FILE;
VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
while ((jt_range= it++))
{
/*
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++)
for (i= 0; i < (jt_range->end - jt_range->start); i++)
{
JOIN_TAB *tab= join->join_tab + i;
JOIN_TAB *tab= jt_range->start + i;
for (ref= 0; ref < tab->ref.key_parts; ref++)
{
ref_key_parts[i].append(tab->ref.items[ref]->full_name());
......@@ -183,11 +191,9 @@ TEST_join(JOIN *join)
}
}
DBUG_LOCK_FILE;
VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
for (i=0 ; i < join->tables ; i++)
for (i= 0; i < (jt_range->end - jt_range->start); i++)
{
JOIN_TAB *tab=join->join_tab+i;
JOIN_TAB *tab= jt_range->start + i;
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",
......@@ -218,6 +224,8 @@ TEST_join(JOIN *join)
" refs: %s\n", ref_key_parts[i].ptr());
}
}
VOID(fputs("\n",DBUG_FILE));
}
DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN;
}
......
......@@ -703,6 +703,7 @@ bool st_select_lex_unit::cleanup()
{
join->tables_list= 0;
join->tables= 0;
join->top_jtrange_tables= 0;
}
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