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));
f1 f2 f3 f1 f2
1 NULL 3 NULL NULL
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
......@@ -1315,6 +1315,65 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
f1 f2 f3 f1 f2
1 NULL 3 NULL NULL
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
set join_cache_level=default;
show variables like 'join_cache_level';
......
......@@ -423,8 +423,8 @@ FOUND_ROWS()
SHOW STATUS LIKE "handler_read%";
Variable_name Value
Handler_read_first 0
Handler_read_key 8
Handler_read_next 31942
Handler_read_key 6
Handler_read_next 2
Handler_read_prev 0
Handler_read_rnd 0
Handler_read_rnd_next 5
......
......@@ -914,4 +914,48 @@ WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
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
......@@ -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 UNINIT_VAR(result);
int result= HA_ERR_END_OF_FILE;
int range_res;
DBUG_ENTER("handler::multi_range_read_next");
......
......@@ -790,7 +790,6 @@ int JOIN_CACHE::alloc_buffer()
{
JOIN_TAB *tab;
JOIN_CACHE *cache;
uint i= join->const_tables;
ulonglong curr_buff_space_sz= 0;
ulonglong curr_min_buff_space_sz= 0;
ulonglong join_buff_space_limit=
......@@ -816,9 +815,9 @@ int JOIN_CACHE::alloc_buffer()
}
if (curr_min_buff_space_sz > join_buff_space_limit ||
curr_buff_space_sz > join_buff_space_limit &&
join->shrink_join_buffers(join_tab, curr_buff_space_sz,
join_buff_space_limit))
(curr_buff_space_sz > join_buff_space_limit &&
join->shrink_join_buffers(join_tab, curr_buff_space_sz,
join_buff_space_limit)))
goto fail;
for (ulong buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; )
......@@ -1144,6 +1143,9 @@ bool JOIN_CACHE::check_emb_key_usage()
The 'last_rec_blob_data_is_in_rec_buff' is set on if the blob data
remains in the record buffers and not copied to the join buffer. It may
happen only to the blob data from the last record added into the cache.
If on_precond is attached to join_tab and it is not evaluated to TRUE
then MATCH_IMPOSSIBLE is placed in the match flag field of the record
written into the join buffer.
RETURN VALUE
length of the written record data
......@@ -1155,6 +1157,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
bool last_record;
CACHE_FIELD *copy;
CACHE_FIELD *copy_end;
uchar *flags_pos;
uchar *cp= pos;
uchar *init_pos= cp;
uchar *rec_len_ptr= 0;
......@@ -1233,6 +1236,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
/* First put into the cache the values of all flag fields */
copy_end= field_descr+flag_fields;
flags_pos= cp;
for ( ; copy < copy_end; copy++)
{
memcpy(cp, copy->str, copy->length);
......@@ -1335,6 +1339,20 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
last_rec_pos= curr_rec_pos;
end_pos= pos= cp;
*is_full= last_record;
if (!join_tab->first_unmatched && join_tab->on_precond)
{
join_tab->found= 0;
join_tab->not_null_compl= 1;
if (!join_tab->on_precond->val_int())
{
flags_pos[0]= MATCH_IMPOSSIBLE;
last_written_is_null_compl= 1;
}
}
else
last_written_is_null_compl= 0;
return (uint) (cp-init_pos);
}
......@@ -1489,7 +1507,7 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr)
/*
Test the match flag from the referenced record: the default implementation
Get the match flag from the referenced record: the default implementation
SYNOPSIS
get_match_flag_by_pos()
......@@ -1497,26 +1515,29 @@ void JOIN_CACHE::get_record_by_pos(uchar *rec_ptr)
DESCRIPTION
This default implementation of the virtual function get_match_flag_by_pos
test the match flag for the record pointed by the reference at the position
rec_ptr. If the match flag in placed one of the previous buffers the function
first reaches the linked record fields in this buffer.
get the match flag for the record pointed by the reference at the position
rec_ptr. If the match flag is placed in one of the previous buffers the
function first reaches the linked record fields in this buffer.
RETURN VALUE
TRUE if the match flag is set on
FALSE otherwise
match flag for the record at the position rec_ptr
*/
bool JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr)
enum JOIN_CACHE::Match_flag JOIN_CACHE::get_match_flag_by_pos(uchar *rec_ptr)
{
Match_flag match_fl= MATCH_NOT_FOUND;
if (with_match_flag)
return test(*rec_ptr);
{
match_fl= (enum Match_flag) rec_ptr[0];
return match_fl;
}
if (prev_cache)
{
uchar *prev_rec_ptr= prev_cache->get_rec_ref(rec_ptr);
return prev_cache->get_match_flag_by_pos(prev_rec_ptr);
}
DBUG_ASSERT(0);
return FALSE;
return match_fl;
}
......@@ -1604,6 +1625,12 @@ uint JOIN_CACHE::read_flag_fields()
uchar *init_pos= pos;
CACHE_FIELD *copy= field_descr;
CACHE_FIELD *copy_end= copy+flag_fields;
if (with_match_flag)
{
copy->str[0]= test((Match_flag) pos[0] == MATCH_FOUND);
pos+= copy->length;
copy++;
}
for ( ; copy < copy_end; copy++)
{
memcpy(copy->str, pos, copy->length);
......@@ -1754,30 +1781,69 @@ bool JOIN_CACHE::read_referenced_field(CACHE_FIELD *copy,
/*
Skip record from join buffer if its match flag is on: default implementation
Skip record from join buffer if's already matched: default implementation
SYNOPSIS
skip_recurrent_match()
skip_if_matched()
DESCRIPTION
This default implementation of the virtual function skip_record_if_match
skips the next record from the join buffer if its match flag is set on.
If the record is skipped the value of 'pos' is set to points to the position
This default implementation of the virtual function skip_if_matched
skips the next record from the join buffer if its match flag is set to
MATCH_FOUND.
If the record is skipped the value of 'pos' is set to point to the position
right after the record.
RETURN VALUE
TRUE the match flag is on and the record has been skipped
FALSE the match flag is off
TRUE the match flag is set to MATCH_FOUND and the record has been skipped
FALSE otherwise
*/
bool JOIN_CACHE::skip_recurrent_match()
bool JOIN_CACHE::skip_if_matched()
{
DBUG_ASSERT(with_length);
uint offset= size_of_rec_len;
if (prev_cache)
offset+= prev_cache->get_size_of_rec_offset();
/* Check whether the match flag is on */
if (get_match_flag_by_pos(pos+offset))
/* Check whether the match flag is MATCH_FOUND */
if (get_match_flag_by_pos(pos+offset) == MATCH_FOUND)
{
pos+= size_of_rec_len + get_rec_length(pos);
return TRUE;
}
return FALSE;
}
/*
Skip record from join buffer if the match isn't needed: default implementation
SYNOPSIS
skip_if_not_needed_match()
DESCRIPTION
This default implementation of the virtual function skip_if_not_needed_match
skips the next record from the join buffer if its match flag is not
MATCH_NOT_FOUND, and, either its value is MATCH_FOUND and join_tab is the
first inner table of an inner join, or, its value is MATCH_IMPOSSIBLE
and join_tab is the first inner table of an outer join.
If the record is skipped the value of 'pos' is set to point to the position
right after the record.
RETURN VALUE
TRUE the record has to be skipped
FALSE otherwise
*/
bool JOIN_CACHE::skip_if_not_needed_match()
{
DBUG_ASSERT(with_length);
enum Match_flag match_fl;
uint offset= size_of_rec_len;
if (prev_cache)
offset+= prev_cache->get_size_of_rec_offset();
if ((match_fl= get_match_flag_by_pos(pos+offset)) != MATCH_NOT_FOUND &&
(join_tab->check_only_first_match() == (match_fl == MATCH_FOUND)) )
{
pos+= size_of_rec_len + get_rec_length(pos);
return TRUE;
......@@ -1990,9 +2056,9 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
{
int error;
enum_nested_loop_state rc= NESTED_LOOP_OK;
bool check_only_first_match= join_tab->check_only_first_match();
join_tab->table->null_row= 0;
bool check_only_first_match= join_tab->check_only_first_match();
bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join();
/* Return at once if there are no records in the join buffer */
if (!records)
......@@ -2042,9 +2108,11 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
/*
If only the first match is needed, and, it has been already found for
the next record read from the join buffer, then the record is skipped.
Also those records that must be null complemented are not considered
as candidates for matches.
*/
if (!(check_only_first_match &&
skip_recurrent_candidate_for_match(rec_ptr)))
if ((!check_only_first_match && !outer_join_first_inner) ||
!skip_next_candidate_for_match(rec_ptr))
{
read_next_candidate_for_match(rec_ptr);
rc= generate_full_extensions(rec_ptr);
......@@ -2268,7 +2336,6 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
uint cnt;
enum_nested_loop_state rc= NESTED_LOOP_OK;
bool is_first_inner= join_tab == join_tab->first_unmatched;
bool is_last_inner= join_tab == join_tab->first_unmatched->last_inner;
/* Return at once if there are no records in the join buffer */
if (!records)
......@@ -2289,7 +2356,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
goto finish;
}
/* Just skip the whole record if a match for it has been already found */
if (!is_first_inner || !skip_recurrent_match())
if (!is_first_inner || !skip_if_matched())
{
get_record();
/* The outer row is complemented by nulls for each inner table */
......@@ -2527,6 +2594,8 @@ void JOIN_CACHE_HASHED::reset(bool for_writing)
is attached to the key entry. The key value is either placed in the hash
element added for the key or, if the use_emb_key flag is set, remains in
the record from the partial join.
If the match flag field of a record contains MATCH_IMPOSSIBLE the key is
not created for this record.
RETURN VALUE
TRUE if it has been decided that it should be the last record
......@@ -2550,6 +2619,9 @@ bool JOIN_CACHE_HASHED::put_record()
link= prev_cache->get_curr_rec_link();
write_record_data(link, &is_full);
if (last_written_is_null_compl)
return is_full;
if (use_emb_key)
key= get_curr_emb_key();
else
......@@ -2634,26 +2706,55 @@ bool JOIN_CACHE_HASHED::get_record()
/*
Skip record from a hashed join buffer if its match flag is on
Skip record from a hashed join buffer if its match flag is set to MATCH_FOUND
SYNOPSIS
skip_if_matched()
DESCRIPTION
This implementation of the virtual function skip_if_matched does
the same as the default implementation does, but it takes into account
the link element used to connect the records with the same key into a chain.
RETURN VALUE
TRUE the match flag is MATCH_FOUND and the record has been skipped
FALSE otherwise
*/
bool JOIN_CACHE_HASHED::skip_if_matched()
{
uchar *save_pos= pos;
pos+= get_size_of_rec_offset();
if (!this->JOIN_CACHE::skip_if_matched())
{
pos= save_pos;
return FALSE;
}
return TRUE;
}
/*
Skip record from a hashed join buffer if its match flag dictates to do so
SYNOPSIS
skip_recurrent_match()
skip_if_uneeded_match()
DESCRIPTION
This implementation of the virtual function skip_recurrent_match does
This implementation of the virtual function skip_if_not_needed_match does
the same as the default implementation does, but it takes into account
the link element used to connect the records with the same key into a chain.
RETURN VALUE
TRUE the match flag is on and the record has been skipped
TRUE the match flag dictates to skip the record
FALSE the match flag is off
*/
bool JOIN_CACHE_HASHED::skip_recurrent_match()
bool JOIN_CACHE_HASHED::skip_if_not_needed_match()
{
uchar *save_pos= pos;
pos+= get_size_of_rec_offset();
if (!this->JOIN_CACHE::skip_recurrent_match())
if (!this->JOIN_CACHE::skip_if_not_needed_match())
{
pos= save_pos;
return FALSE;
......@@ -2780,7 +2881,7 @@ void JOIN_CACHE_HASHED:: cleanup_hash_table()
last element of this chain.
RETURN VALUE
TRUE if each retrieved record has its match flag set on
TRUE if each retrieved record has its match flag set to MATCH_FOUND
FALSE otherwise
*/
......@@ -2792,7 +2893,7 @@ bool JOIN_CACHE_HASHED::check_all_match_flags_for_key(uchar *key_chain_ptr)
{
next_rec_ref_ptr= get_next_rec_ref(next_rec_ref_ptr);
uchar *rec_ptr= next_rec_ref_ptr+rec_fields_offset;
if (!get_match_flag_by_pos(rec_ptr))
if (get_match_flag_by_pos(rec_ptr) != MATCH_FOUND)
return FALSE;
}
while (next_rec_ref_ptr != last_rec_ref_ptr);
......@@ -3000,25 +3101,28 @@ uchar *JOIN_CACHE_BNL::get_next_candidate_for_match()
Check whether the matching record from the BNL cache is to be skipped
SYNOPSIS
skip_recurrent_candidate_for_match
skip_next_candidate_for_match
rec_ptr pointer to the position in the join buffer right after the prefix
of the current record
DESCRIPTION
This implementation of the virtual function just calls the
method skip_recurrent_match to check whether the record referenced by
ref_ptr has its match flag set on and, if so, just skips this record
setting the value of the cursor 'pos' to the position right after it.
method skip_if_not_needed_match to check whether the record referenced by
ref_ptr has its match flag set either to MATCH_FOUND and join_tab is the
first inner table of a semi-join, or it's set to MATCH_IMPOSSIBLE and
join_tab is the first inner table of an outer join.
If so, the function just skips this record setting the value of the
cursor 'pos' to the position right after it.
RETURN VALUE
TRUE the record referenced by rec_ptr has been skipped
FALSE otherwise
*/
bool JOIN_CACHE_BNL::skip_recurrent_candidate_for_match(uchar *rec_ptr)
bool JOIN_CACHE_BNL::skip_next_candidate_for_match(uchar *rec_ptr)
{
pos= rec_ptr-base_prefix_length;
return skip_recurrent_match();
return skip_if_not_needed_match();
}
......@@ -3162,7 +3266,7 @@ bool JOIN_CACHE_BNLH::prepare_look_for_matches(bool skip_last)
The methods performs the necessary preparations to read the next record
from the join buffer into the record buffer by the method
read_next_candidate_for_match, or, to skip the next record from the join
buffer by the method skip_recurrent_candidate_for_match.
buffer by the method skip_next_candidate_for_match.
This implementation of the virtual method moves to the next record
in the chain of all records from the join buffer that are to be
equi-joined with the current record from join_tab.
......@@ -3187,23 +3291,25 @@ uchar *JOIN_CACHE_BNLH::get_next_candidate_for_match()
Check whether the matching record from the BNLH cache is to be skipped
SYNOPSIS
skip_recurrent_candidate_for_match
skip_next_candidate_for_match
rec_ptr pointer to the position in the join buffer right after
the previous record
DESCRIPTION
This implementation of the virtual function just calls the
method get_match_flag_by_pos to check whether the record referenced
by ref_ptr has its match flag set on.
by ref_ptr has its match flag set to MATCH_FOUND.
RETURN VALUE
TRUE the record referenced by rec_ptr has its match flag set on
TRUE the record referenced by rec_ptr has its match flag set to
MATCH_FOUND
FALSE otherwise
*/
bool JOIN_CACHE_BNLH::skip_recurrent_candidate_for_match(uchar *rec_ptr)
bool JOIN_CACHE_BNLH::skip_next_candidate_for_match(uchar *rec_ptr)
{
return get_match_flag_by_pos(rec_ptr);
return join_tab->check_only_first_match() &&
(get_match_flag_by_pos(rec_ptr) == MATCH_FOUND);
}
......@@ -3364,7 +3470,7 @@ int JOIN_TAB_SCAN_MRR::open()
int JOIN_TAB_SCAN_MRR::next()
{
char **ptr= (char **) cache->get_curr_association_ptr();
uint rc= join_tab->table->file->multi_range_read_next(ptr) ? -1 : 0;
int rc= join_tab->table->file->multi_range_read_next(ptr) ? -1 : 0;
if (!rc)
{
/*
......@@ -3482,7 +3588,8 @@ bool bka_range_seq_skip_record(range_seq_t rseq, char *range_info, uchar *rowid)
{
DBUG_ENTER("bka_range_seq_skip_record");
JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
bool res= cache->get_match_flag_by_pos((uchar *) range_info);
bool res= cache->get_match_flag_by_pos((uchar *) range_info) ==
JOIN_CACHE::MATCH_FOUND;
DBUG_RETURN(res);
}
......@@ -3560,7 +3667,7 @@ bool JOIN_CACHE_BKA::prepare_look_for_matches(bool skip_last)
The method performs the necessary preparations to read the next record
from the join buffer into the record buffer by the method
read_next_candidate_for_match, or, to skip the next record from the join
buffer by the method skip_recurrent_match.
buffer by the method skip_if_not_needed_match.
This implementation of the virtual method get_next_candidate_for_match
just decrements the counter of the records that are to be iterated over
and returns the value of curr_association as a reference to the position
......@@ -3584,23 +3691,25 @@ uchar *JOIN_CACHE_BKA::get_next_candidate_for_match()
Check whether the matching record from the BKA cache is to be skipped
SYNOPSIS
skip_recurrent_candidate_for_match
skip_next_candidate_for_match
rec_ptr pointer to the position in the join buffer right after
the previous record
DESCRIPTION
This implementation of the virtual function just calls the
method get_match_flag_by_pos to check whether the record referenced
by ref_ptr has its match flag set on.
by ref_ptr has its match flag set to MATCH_FOUND.
RETURN VALUE
TRUE the record referenced by rec_ptr has its match flag set on
TRUE the record referenced by rec_ptr has its match flag set to
MATCH_FOUND
FALSE otherwise
*/
bool JOIN_CACHE_BKA::skip_recurrent_candidate_for_match(uchar *rec_ptr)
bool JOIN_CACHE_BKA::skip_next_candidate_for_match(uchar *rec_ptr)
{
return get_match_flag_by_pos(rec_ptr);
return join_tab->check_only_first_match() &&
(get_match_flag_by_pos(rec_ptr) == MATCH_FOUND);
}
......@@ -3691,7 +3800,9 @@ int JOIN_CACHE_BKA::init()
If is assumed that the functions starts reading at the position of
the record length which is provided for each records in a BKA cache.
After the key is built the 'pos' value points to the first position after
the current record.
the current record.
The function just skips the records with MATCH_IMPOSSIBLE in the
match flag field if there is any.
The function returns 0 if the initial position is after the beginning
of the record fields for last record from the join buffer.
......@@ -3708,6 +3819,8 @@ uint JOIN_CACHE_BKA::get_next_key(uchar ** key)
uchar *init_pos;
JOIN_CACHE *cache;
start:
/* Any record in a BKA cache is prepended with its length */
DBUG_ASSERT(with_length);
......@@ -3727,6 +3840,13 @@ uint JOIN_CACHE_BKA::get_next_key(uchar ** key)
/* Read all flag fields of the record */
read_flag_fields();
if (with_match_flag &&
(Match_flag) curr_rec_pos[0] == MATCH_IMPOSSIBLE )
{
pos= init_pos+rec_len;
goto start;
}
if (use_emb_key)
{
......
......@@ -7090,9 +7090,13 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
used_tables2|= current_map;
COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
current_map, FALSE);
add_cond_and_fix(&tmp_cond, tab->on_precond);
if (tmp_cond)
{
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
all embedding outer join operations.
......@@ -7115,15 +7119,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tmp_cond->quick_fix_field();
/* Add the predicate to other pushed down predicates */
DBUG_PRINT("info", ("Item_cond_and"));
cond_tab->select_cond= !cond_tab->select_cond ? tmp_cond :
new Item_cond_and(cond_tab->select_cond,
tmp_cond);
*sel_cond_ref= !(*sel_cond_ref) ?
tmp_cond :
new Item_cond_and(*sel_cond_ref, tmp_cond);
DBUG_PRINT("info", ("Item_cond_and 0x%lx",
(ulong)cond_tab->select_cond));
if (!cond_tab->select_cond)
DBUG_RETURN(1);
cond_tab->select_cond->update_used_tables();
cond_tab->select_cond->quick_fix_field();
(ulong)(*sel_cond_ref)));
if (!(*sel_cond_ref))
DBUG_RETURN(1);
(*sel_cond_ref)->quick_fix_field();
(*sel_cond_ref)->update_used_tables();
if (cond_tab->select)
cond_tab->select->cond= cond_tab->select_cond;
}
......@@ -13246,7 +13250,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
DBUG_RETURN(nls);
}
int error;
enum_nested_loop_state rc;
enum_nested_loop_state rc= NESTED_LOOP_OK;
READ_RECORD *info= &join_tab->read_record;
if (join_tab->flush_weedout_table)
......@@ -13279,18 +13283,21 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
/* Set first_unmatched for the last inner table of this group */
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;
if (join_tab->loosescan_match_tab)
join_tab->loosescan_match_tab->found_match= FALSE;
error= (*join_tab->read_first_record)(join_tab);
if (join_tab->keep_current_rowid)
join_tab->table->file->position(join_tab->table->record[0]);
rc= evaluate_join_record(join, join_tab, error);
if (rc != NESTED_LOOP_NO_MORE_ROWS)
{
error= (*join_tab->read_first_record)(join_tab);
if (join_tab->keep_current_rowid)
join_tab->table->file->position(join_tab->table->record[0]);
rc= evaluate_join_record(join, join_tab, error);
}
}
/*
......
......@@ -161,6 +161,8 @@ typedef struct st_join_table {
KEYUSE *keyuse; /**< pointer to first used key */
SQL_SELECT *select;
COND *select_cond;
COND *on_precond; /**< part of on condition to check before
accessing the first inner table */
QUICK_SELECT_I *quick;
/*
The value of select_cond before we've attempted to do Index Condition
......@@ -678,6 +680,14 @@ class JOIN_CACHE :public Sql_alloc
*/
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
used in building keys to access the table join_tab
......@@ -792,8 +802,17 @@ class JOIN_CACHE :public Sql_alloc
/* Read a referenced field from the join buffer */
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
......@@ -836,9 +855,9 @@ class JOIN_CACHE :public Sql_alloc
virtual uchar *get_next_candidate_for_match()= 0;
/*
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
the corresponding record buffer
......@@ -868,6 +887,21 @@ class JOIN_CACHE :public Sql_alloc
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 */
JOIN_TAB *join_tab;
......@@ -920,7 +954,7 @@ class JOIN_CACHE :public Sql_alloc
virtual void get_record_by_pos(uchar *rec_ptr);
/* 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 */
virtual uchar *get_curr_rec() { return curr_rec_pos; }
......@@ -1189,8 +1223,17 @@ class JOIN_CACHE_HASHED: public JOIN_CACHE
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 */
bool key_search(uchar *key, uint key_len, uchar **key_ref_ptr);
......@@ -1314,7 +1357,7 @@ class JOIN_CACHE_BNL :public JOIN_CACHE
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);
......@@ -1391,7 +1434,7 @@ class JOIN_CACHE_BNLH :public JOIN_CACHE_HASHED
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);
......@@ -1527,7 +1570,7 @@ class JOIN_CACHE_BKA :public JOIN_CACHE
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);
......
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