Commit 79087c9e authored by Igor Babaev's avatar Igor Babaev

Ported the fix for bug #57024 (a performance issue for outer joins).

Employed the same kind of optimization as in the fix for the cases
when join buffer is used.
The optimization performs early evaluation of the conditions from 
on expression with table references to only outer tables of
an outer join.
parent f1d42ec9
...@@ -1308,4 +1308,63 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); ...@@ -1308,4 +1308,63 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
f1 f2 f3 f1 f2 f1 f2 f3 f1 f2
1 NULL 3 NULL NULL 1 NULL 3 NULL NULL
DROP TABLE t1, t2; DROP TABLE t1, t2;
#
# Bug#57024: Poor performance when conjunctive condition over the outer
# table is used in the on condition of an outer join
#
create table t1 (a int);
insert into t1 values (NULL), (NULL), (NULL), (NULL);
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 values (4), (2), (1), (3);
create table t2 like t1;
insert into t2 select if(t1.a is null, 10, t1.a) from t1;
create table t3 (a int, b int, index idx(a));
insert into t3 values (1, 100), (3, 301), (4, 402), (1, 102), (1, 101);
analyze table t1,t2,t3;
Table Op Msg_type Msg_text
test.t1 analyze status OK
test.t2 analyze status OK
test.t3 analyze status OK
flush status;
select sum(t3.b) from t1 left join t3 on t3.a=t1.a and t1.a is not null;
sum(t3.b)
1006
show status like "handler_read%";
Variable_name Value
Handler_read_first 0
Handler_read_key 4
Handler_read_next 5
Handler_read_prev 0
Handler_read_rnd 0
Handler_read_rnd_next 1048581
flush status;
select sum(t3.b) from t2 left join t3 on t3.a=t2.a and t2.a <> 10;
sum(t3.b)
1006
show status like "handler_read%";
Variable_name Value
Handler_read_first 0
Handler_read_key 4
Handler_read_next 5
Handler_read_prev 0
Handler_read_rnd 0
Handler_read_rnd_next 1048581
drop table t1,t2,t3;
End of 5.1 tests End of 5.1 tests
...@@ -1315,6 +1315,65 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); ...@@ -1315,6 +1315,65 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
f1 f2 f3 f1 f2 f1 f2 f3 f1 f2
1 NULL 3 NULL NULL 1 NULL 3 NULL NULL
DROP TABLE t1, t2; DROP TABLE t1, t2;
#
# Bug#57024: Poor performance when conjunctive condition over the outer
# table is used in the on condition of an outer join
#
create table t1 (a int);
insert into t1 values (NULL), (NULL), (NULL), (NULL);
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 values (4), (2), (1), (3);
create table t2 like t1;
insert into t2 select if(t1.a is null, 10, t1.a) from t1;
create table t3 (a int, b int, index idx(a));
insert into t3 values (1, 100), (3, 301), (4, 402), (1, 102), (1, 101);
analyze table t1,t2,t3;
Table Op Msg_type Msg_text
test.t1 analyze status OK
test.t2 analyze status OK
test.t3 analyze status OK
flush status;
select sum(t3.b) from t1 left join t3 on t3.a=t1.a and t1.a is not null;
sum(t3.b)
1006
show status like "handler_read%";
Variable_name Value
Handler_read_first 0
Handler_read_key 4
Handler_read_next 5
Handler_read_prev 0
Handler_read_rnd 5
Handler_read_rnd_next 1048581
flush status;
select sum(t3.b) from t2 left join t3 on t3.a=t2.a and t2.a <> 10;
sum(t3.b)
1006
show status like "handler_read%";
Variable_name Value
Handler_read_first 0
Handler_read_key 4
Handler_read_next 5
Handler_read_prev 0
Handler_read_rnd 5
Handler_read_rnd_next 1048581
drop table t1,t2,t3;
End of 5.1 tests End of 5.1 tests
set join_cache_level=default; set join_cache_level=default;
show variables like 'join_cache_level'; show variables like 'join_cache_level';
......
...@@ -423,8 +423,8 @@ FOUND_ROWS() ...@@ -423,8 +423,8 @@ FOUND_ROWS()
SHOW STATUS LIKE "handler_read%"; SHOW STATUS LIKE "handler_read%";
Variable_name Value Variable_name Value
Handler_read_first 0 Handler_read_first 0
Handler_read_key 8 Handler_read_key 6
Handler_read_next 31942 Handler_read_next 2
Handler_read_prev 0 Handler_read_prev 0
Handler_read_rnd 0 Handler_read_rnd 0
Handler_read_rnd_next 5 Handler_read_rnd_next 5
......
...@@ -914,4 +914,48 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2)); ...@@ -914,4 +914,48 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
DROP TABLE t1, t2; DROP TABLE t1, t2;
--echo #
--echo # Bug#57024: Poor performance when conjunctive condition over the outer
--echo # table is used in the on condition of an outer join
--echo #
create table t1 (a int);
insert into t1 values (NULL), (NULL), (NULL), (NULL);
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 select * from t1;
insert into t1 values (4), (2), (1), (3);
create table t2 like t1;
insert into t2 select if(t1.a is null, 10, t1.a) from t1;
create table t3 (a int, b int, index idx(a));
insert into t3 values (1, 100), (3, 301), (4, 402), (1, 102), (1, 101);
analyze table t1,t2,t3;
flush status;
select sum(t3.b) from t1 left join t3 on t3.a=t1.a and t1.a is not null;
show status like "handler_read%";
flush status;
select sum(t3.b) from t2 left join t3 on t3.a=t2.a and t2.a <> 10;
show status like "handler_read%";
drop table t1,t2,t3;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -223,7 +223,7 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param, ...@@ -223,7 +223,7 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
int handler::multi_range_read_next(char **range_info) int handler::multi_range_read_next(char **range_info)
{ {
int UNINIT_VAR(result); int result= HA_ERR_END_OF_FILE;
int range_res; int range_res;
DBUG_ENTER("handler::multi_range_read_next"); DBUG_ENTER("handler::multi_range_read_next");
......
This diff is collapsed.
...@@ -7090,9 +7090,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -7090,9 +7090,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
used_tables2|= current_map; used_tables2|= current_map;
COND *tmp_cond= make_cond_for_table(on_expr, used_tables2, COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
current_map, FALSE); current_map, FALSE);
add_cond_and_fix(&tmp_cond, tab->on_precond);
if (tmp_cond) if (tmp_cond)
{ {
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab; JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
Item **sel_cond_ref= tab < first_inner_tab ?
&first_inner_tab->on_precond :
&tab->select_cond;
/* /*
First add the guards for match variables of First add the guards for match variables of
all embedding outer join operations. all embedding outer join operations.
...@@ -7115,15 +7119,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -7115,15 +7119,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tmp_cond->quick_fix_field(); tmp_cond->quick_fix_field();
/* Add the predicate to other pushed down predicates */ /* Add the predicate to other pushed down predicates */
DBUG_PRINT("info", ("Item_cond_and")); DBUG_PRINT("info", ("Item_cond_and"));
cond_tab->select_cond= !cond_tab->select_cond ? tmp_cond : *sel_cond_ref= !(*sel_cond_ref) ?
new Item_cond_and(cond_tab->select_cond, tmp_cond :
tmp_cond); new Item_cond_and(*sel_cond_ref, tmp_cond);
DBUG_PRINT("info", ("Item_cond_and 0x%lx", DBUG_PRINT("info", ("Item_cond_and 0x%lx",
(ulong)cond_tab->select_cond)); (ulong)(*sel_cond_ref)));
if (!cond_tab->select_cond) if (!(*sel_cond_ref))
DBUG_RETURN(1); DBUG_RETURN(1);
cond_tab->select_cond->update_used_tables(); (*sel_cond_ref)->quick_fix_field();
cond_tab->select_cond->quick_fix_field(); (*sel_cond_ref)->update_used_tables();
if (cond_tab->select) if (cond_tab->select)
cond_tab->select->cond= cond_tab->select_cond; cond_tab->select->cond= cond_tab->select_cond;
} }
...@@ -13246,7 +13250,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) ...@@ -13246,7 +13250,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
DBUG_RETURN(nls); DBUG_RETURN(nls);
} }
int error; int error;
enum_nested_loop_state rc; enum_nested_loop_state rc= NESTED_LOOP_OK;
READ_RECORD *info= &join_tab->read_record; READ_RECORD *info= &join_tab->read_record;
if (join_tab->flush_weedout_table) if (join_tab->flush_weedout_table)
...@@ -13279,19 +13283,22 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) ...@@ -13279,19 +13283,22 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
/* Set first_unmatched for the last inner table of this group */ /* Set first_unmatched for the last inner table of this group */
join_tab->last_inner->first_unmatched= join_tab; join_tab->last_inner->first_unmatched= join_tab;
if (join_tab->on_precond && !join_tab->on_precond->val_int())
rc= NESTED_LOOP_NO_MORE_ROWS;
} }
join->thd->row_count= 0; join->thd->row_count= 0;
if (join_tab->loosescan_match_tab) if (join_tab->loosescan_match_tab)
join_tab->loosescan_match_tab->found_match= FALSE; join_tab->loosescan_match_tab->found_match= FALSE;
if (rc != NESTED_LOOP_NO_MORE_ROWS)
{
error= (*join_tab->read_first_record)(join_tab); error= (*join_tab->read_first_record)(join_tab);
if (join_tab->keep_current_rowid) if (join_tab->keep_current_rowid)
join_tab->table->file->position(join_tab->table->record[0]); join_tab->table->file->position(join_tab->table->record[0]);
rc= evaluate_join_record(join, join_tab, error); rc= evaluate_join_record(join, join_tab, error);
} }
}
/* /*
Note: psergey has added the 2nd part of the following condition; the Note: psergey has added the 2nd part of the following condition; the
......
...@@ -161,6 +161,8 @@ typedef struct st_join_table { ...@@ -161,6 +161,8 @@ typedef struct st_join_table {
KEYUSE *keyuse; /**< pointer to first used key */ KEYUSE *keyuse; /**< pointer to first used key */
SQL_SELECT *select; SQL_SELECT *select;
COND *select_cond; COND *select_cond;
COND *on_precond; /**< part of on condition to check before
accessing the first inner table */
QUICK_SELECT_I *quick; QUICK_SELECT_I *quick;
/* /*
The value of select_cond before we've attempted to do Index Condition The value of select_cond before we've attempted to do Index Condition
...@@ -678,6 +680,14 @@ protected: ...@@ -678,6 +680,14 @@ protected:
*/ */
uchar *curr_rec_link; uchar *curr_rec_link;
/*
This flag is set to TRUE if join_tab is the first inner table of an outer
join and the latest record written to the join buffer is detected to be
null complemented after checking on conditions over the outer tables for
this outer join operation
*/
bool last_written_is_null_compl;
/* /*
The number of fields put in the join buffer of the join cache that are The number of fields put in the join buffer of the join cache that are
used in building keys to access the table join_tab used in building keys to access the table join_tab
...@@ -792,8 +802,17 @@ protected: ...@@ -792,8 +802,17 @@ protected:
/* Read a referenced field from the join buffer */ /* Read a referenced field from the join buffer */
bool read_referenced_field(CACHE_FIELD *copy, uchar *rec_ptr, uint *len); bool read_referenced_field(CACHE_FIELD *copy, uchar *rec_ptr, uint *len);
/* Shall skip record from the join buffer if its match flag is on */ /*
virtual bool skip_recurrent_match(); Shall skip record from the join buffer if its match flag
is set to MATCH_FOUND
*/
virtual bool skip_if_matched();
/*
Shall skip record from the join buffer if its match flag
commands to do so
*/
virtual bool skip_if_not_needed_match();
/* /*
True if rec_ptr points to the record whose blob data stay in True if rec_ptr points to the record whose blob data stay in
...@@ -836,9 +855,9 @@ protected: ...@@ -836,9 +855,9 @@ protected:
virtual uchar *get_next_candidate_for_match()= 0; virtual uchar *get_next_candidate_for_match()= 0;
/* /*
Shall check whether the given record from the join buffer has its match Shall check whether the given record from the join buffer has its match
flag set on and, if so, skip the record in the buffer. flag settings commands to skip the record in the buffer.
*/ */
virtual bool skip_recurrent_candidate_for_match(uchar *rec_ptr)= 0; virtual bool skip_next_candidate_for_match(uchar *rec_ptr)= 0;
/* /*
Shall read the given record from the join buffer into the Shall read the given record from the join buffer into the
the corresponding record buffer the corresponding record buffer
...@@ -868,6 +887,21 @@ protected: ...@@ -868,6 +887,21 @@ protected:
public: public:
/*
The enumeration type Match_flag describes possible states of the match flag
field stored for the records of the first inner tables of outer joins and
semi-joins in the cases when the first match strategy is used for them.
When a record with match flag field is written into the join buffer the
state of the field usually is MATCH_NOT_FOUND unless this is a record of the
first inner table of the outer join for which the on precondition (the
condition from on expression over outer tables) has turned out not to be
true. In the last case the state of the match flag is MATCH_IMPOSSIBLE.
The state of the match flag field is changed to MATCH_FOUND as soon as
the first full matching combination of inner tables of the outer join or
the semi-join is discovered.
*/
enum Match_flag { MATCH_NOT_FOUND, MATCH_FOUND, MATCH_IMPOSSIBLE };
/* Table to be joined with the partial join records from the cache */ /* Table to be joined with the partial join records from the cache */
JOIN_TAB *join_tab; JOIN_TAB *join_tab;
...@@ -920,7 +954,7 @@ public: ...@@ -920,7 +954,7 @@ public:
virtual void get_record_by_pos(uchar *rec_ptr); virtual void get_record_by_pos(uchar *rec_ptr);
/* Shall return the value of the match flag for the positioned record */ /* Shall return the value of the match flag for the positioned record */
virtual bool get_match_flag_by_pos(uchar *rec_ptr); virtual enum Match_flag get_match_flag_by_pos(uchar *rec_ptr);
/* Shall return the position of the current record */ /* Shall return the position of the current record */
virtual uchar *get_curr_rec() { return curr_rec_pos; } virtual uchar *get_curr_rec() { return curr_rec_pos; }
...@@ -1189,8 +1223,17 @@ protected: ...@@ -1189,8 +1223,17 @@ protected:
return max(last_key_entry-end_pos-aux_buff_size,0); return max(last_key_entry-end_pos-aux_buff_size,0);
} }
/* Skip record from a hashed join buffer if its match flag is on */ /*
bool skip_recurrent_match(); Skip record from a hashed join buffer if its match flag
is set to MATCH_FOUND
*/
bool skip_if_matched();
/*
Skip record from a hashed join buffer if its match flag setting
commands to do so
*/
bool skip_if_not_needed_match();
/* Search for a key in the hash table of the join buffer */ /* Search for a key in the hash table of the join buffer */
bool key_search(uchar *key, uint key_len, uchar **key_ref_ptr); bool key_search(uchar *key, uint key_len, uchar **key_ref_ptr);
...@@ -1314,7 +1357,7 @@ protected: ...@@ -1314,7 +1357,7 @@ protected:
uchar *get_next_candidate_for_match(); uchar *get_next_candidate_for_match();
bool skip_recurrent_candidate_for_match(uchar *rec_ptr); bool skip_next_candidate_for_match(uchar *rec_ptr);
void read_next_candidate_for_match(uchar *rec_ptr); void read_next_candidate_for_match(uchar *rec_ptr);
...@@ -1391,7 +1434,7 @@ protected: ...@@ -1391,7 +1434,7 @@ protected:
uchar *get_next_candidate_for_match(); uchar *get_next_candidate_for_match();
bool skip_recurrent_candidate_for_match(uchar *rec_ptr); bool skip_next_candidate_for_match(uchar *rec_ptr);
void read_next_candidate_for_match(uchar *rec_ptr); void read_next_candidate_for_match(uchar *rec_ptr);
...@@ -1527,7 +1570,7 @@ protected: ...@@ -1527,7 +1570,7 @@ protected:
uchar *get_next_candidate_for_match(); uchar *get_next_candidate_for_match();
bool skip_recurrent_candidate_for_match(uchar *rec_ptr); bool skip_next_candidate_for_match(uchar *rec_ptr);
void read_next_candidate_for_match(uchar *rec_ptr); void read_next_candidate_for_match(uchar *rec_ptr);
......
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