1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-18844 Implement EXCEPT ALL and INTERSECT ALL operations

This commit is contained in:
WayneXia
2019-08-24 21:42:35 +08:00
parent afe969ba05
commit a896bebfa6
20 changed files with 4813 additions and 250 deletions

View File

@ -5708,17 +5708,18 @@ public:
class select_unit :public select_result_interceptor
{
public:
uint curr_step, prev_step, curr_sel;
enum sub_select_type step;
public:
Item_int *intersect_mark;
TMP_TABLE_PARAM tmp_table_param;
/* Number of additional (hidden) field of the used temporary table */
int addon_cnt;
int write_err; /* Error code from the last send_data->ha_write_row call. */
TABLE *table;
select_unit(THD *thd_arg):
select_result_interceptor(thd_arg),
intersect_mark(0), table(0)
select_result_interceptor(thd_arg), addon_cnt(0), table(0)
{
init();
tmp_table_param.init();
@ -5735,6 +5736,9 @@ public:
virtual bool postponed_prepare(List<Item> &types)
{ return false; }
int send_data(List<Item> &items);
int write_record();
int update_counter(Field *counter, longlong value);
int delete_record();
bool send_eof();
virtual bool flush();
void cleanup();
@ -5753,7 +5757,148 @@ public:
step= UNION_TYPE;
write_err= 0;
}
virtual void change_select();
virtual bool force_enable_index_if_needed() { return false; }
};
/**
@class select_unit_ext
The class used when processing rows produced by operands of query expressions
containing INTERSECT ALL and/or EXCEPT all operations. One or two extra fields
of the temporary to store the rows of the partial and final result can be employed.
Both of them contain counters. The second additional field is used only when
the processed query expression contains INTERSECT ALL.
Consider how these extra fields are used.
Let
table t1 (f char(8))
table t2 (f char(8))
table t3 (f char(8))
contain the following sets:
("b"),("a"),("d"),("c"),("b"),("a"),("c"),("a")
("c"),("b"),("c"),("c"),("a"),("b"),("g")
("c"),("a"),("b"),("d"),("b"),("e")
- Let's demonstrate how the the set operation INTERSECT ALL is proceesed
for the query
SELECT f FROM t1 INTERSECT ALL SELECT f FROM t2
When send_data() is called for the rows of the first operand we put
the processed record into the temporary table if there was no such record
setting dup_cnt field to 1 and add_cnt field to 0 and increment the
counter in the dup_cnt field by one otherwise. We get
|add_cnt|dup_cnt| f |
|0 |2 |b |
|0 |3 |a |
|0 |1 |d |
|0 |2 |c |
The call of send_eof() for the first operand swaps the values stored in
dup_cnt and add_cnt. After this, we'll see the following rows in the
temporary table
|add_cnt|dup_cnt| f |
|2 |0 |b |
|3 |0 |a |
|1 |0 |d |
|2 |0 |c |
When send_data() is called for the rows of the second operand we increment
the counter in dup_cnt if the processed row is found in the table and do
nothing otherwise. As a result we get
|add_cnt|dup_cnt| f |
|2 |2 |b |
|3 |1 |a |
|1 |0 |d |
|2 |3 |c |
At the call of send_eof() for the second operand first we disable index.
Then for each record, the minimum of counters from dup_cnt and add_cnt m is
taken. If m == 0 then the record is deleted. Otherwise record is replaced
with m copies of it. Yet the counter in this copies are set to 1 for
dup_cnt and to 0 for add_cnt
|add_cnt|dup_cnt| f |
|0 |1 |b |
|0 |1 |b |
|0 |1 |a |
|0 |1 |c |
|0 |1 |c |
- Let's demonstrate how the the set operation EXCEPT ALL is proceesed
for the query
SELECT f FROM t1 EXCEPT ALL SELECT f FROM t3
Only one additional counter field dup_cnt is used for EXCEPT ALL.
After the first operand has been processed we have in the temporary table
|dup_cnt| f |
|2 |b |
|3 |a |
|1 |d |
|2 |c |
When send_data() is called for the rows of the second operand we decrement
the counter in dup_cnt if the processed row is found in the table and do
nothing otherwise. If the counter becomes 0 we delete the record
|dup_cnt| f |
|2 |a |
|1 |c |
Finally at the call of send_eof() for the second operand we disable index
unfold rows adding duplicates
|dup_cnt| f |
|1 |a |
|1 |a |
|1 |c |
*/
class select_unit_ext :public select_unit
{
public:
select_unit_ext(THD *thd_arg):
select_unit(thd_arg), increment(0), is_index_enabled(TRUE),
curr_op_type(UNSPECIFIED)
{
};
int send_data(List<Item> &items);
void change_select();
int unfold_record(int cnt);
bool send_eof();
bool force_enable_index_if_needed()
{
is_index_enabled= true;
return true;
}
bool disable_index_if_needed(SELECT_LEX *curr_sl);
/*
How to change increment/decrement the counter in duplicate_cnt field
when processing a record produced by the current operand in send_data().
The value can be 1 or -1
*/
int increment;
/* TRUE <=> the index of the result temporary table is enabled */
bool is_index_enabled;
/* The type of the set operation currently executed */
enum set_op_type curr_op_type;
/*
Points to the extra field of the temporary table where
duplicate counters are stored
*/
Field *duplicate_cnt;
/*
Points to the extra field of the temporary table where additional
counters used only for INTERSECT ALL operations are stored
*/
Field *additional_cnt;
};
class select_union_recursive :public select_unit