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:
151
sql/sql_class.h
151
sql/sql_class.h
@ -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
|
||||
|
Reference in New Issue
Block a user