mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
MDEV-8646: Re-engineer the code for post-join operations
- Make EXPLAIN code use the post-join operations - Remove Sort_and_group_tracker that was used for that purpose
This commit is contained in:
@ -685,13 +685,13 @@ ANALYZE
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 0,
|
||||
"volatile parameter": "REPLACED",
|
||||
"temporary_table": {
|
||||
"filesort": {
|
||||
"r_loops": 1,
|
||||
"volatile parameter": "REPLACED",
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 0,
|
||||
"volatile parameter": "REPLACED",
|
||||
"temporary_table": {
|
||||
"temporary_table": {
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
|
@ -173,7 +173,6 @@ EXPLAIN
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t0",
|
||||
"access_type": "ALL",
|
||||
@ -460,13 +459,13 @@ ANALYZE
|
||||
"r_limit": 1,
|
||||
"r_used_priority_queue": true,
|
||||
"r_output_rows": 2,
|
||||
"temporary_table": {
|
||||
"filesort": {
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"r_used_priority_queue": false,
|
||||
"r_output_rows": 6,
|
||||
"r_buffer_size": "REPLACED",
|
||||
"temporary_table": {
|
||||
"temporary_table": {
|
||||
"table": {
|
||||
"table_name": "t6",
|
||||
@ -512,7 +511,8 @@ EXPLAIN
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"table": {
|
||||
"table_name": "t6",
|
||||
"access_type": "ALL",
|
||||
@ -536,6 +536,8 @@ EXPLAIN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
drop table t5,t6,t7;
|
||||
drop table t3;
|
||||
#
|
||||
|
@ -231,8 +231,8 @@ CREATE VIEW v1 AS SELECT a, MIN(b) AS b FROM t2 GROUP BY a;
|
||||
EXPLAIN
|
||||
SELECT * FROM v1, t1 WHERE v1.b=t1.a ORDER BY v1.a;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 system NULL NULL NULL NULL 1 Using filesort
|
||||
1 PRIMARY <derived2> ref key0 key0 5 const 1 Using where
|
||||
1 PRIMARY t1 system NULL NULL NULL NULL 1
|
||||
1 PRIMARY <derived2> ref key0 key0 5 const 1 Using where; Using filesort
|
||||
2 DERIVED t2 ALL NULL NULL NULL NULL 10 Using temporary; Using filesort
|
||||
SELECT * FROM v1, t1 WHERE v1.b=t1.a ORDER BY v1.a;
|
||||
a b a
|
||||
|
@ -487,7 +487,6 @@ EXPLAIN
|
||||
"select_id": 2,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
@ -531,7 +530,6 @@ EXPLAIN
|
||||
"select_id": 2,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
@ -576,7 +574,6 @@ EXPLAIN
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
@ -1133,7 +1130,6 @@ EXPLAIN
|
||||
"having_condition": "(TOP > t2.a)",
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
@ -1152,7 +1148,6 @@ EXPLAIN
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
@ -1182,7 +1177,6 @@ EXPLAIN
|
||||
"select_id": 1,
|
||||
"filesort": {
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
@ -1380,7 +1374,6 @@ EXPLAIN
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"temporary_table": {
|
||||
"function": "buffer",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
|
@ -5412,9 +5412,9 @@ WHERE t2.c IN (SELECT c FROM t3,t4 WHERE t4.a < 10) AND
|
||||
t2.a BETWEEN 4 and 5
|
||||
ORDER BY t2.b;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 system NULL NULL NULL NULL 1 Using filesort
|
||||
1 PRIMARY t1 system NULL NULL NULL NULL 1
|
||||
1 PRIMARY t3 system NULL NULL NULL NULL 1
|
||||
1 PRIMARY t2 range a,c a 5 NULL 1 Using index condition; Using where
|
||||
1 PRIMARY t2 range a,c a 5 NULL 1 Using index condition; Using where; Using filesort
|
||||
1 PRIMARY t4 ref c c 5 test.t2.c 2 Using where; Start temporary; End temporary
|
||||
SELECT * FROM t1,t2
|
||||
WHERE t2.c IN (SELECT c FROM t3,t4 WHERE t4.a < 10) AND
|
||||
|
@ -1289,8 +1289,8 @@ SELECT t1.a, COUNT( t2.b ), SUM( t2.b ), MAX( t2.b )
|
||||
FROM t1 JOIN t2 USING( a )
|
||||
GROUP BY t1.a WITH ROLLUP;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system NULL NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where
|
||||
1 SIMPLE t1 system NULL NULL NULL NULL 1
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using filesort
|
||||
SELECT t1.a, COUNT( t2.b ), SUM( t2.b ), MAX( t2.b )
|
||||
FROM t1 JOIN t2 USING( a )
|
||||
GROUP BY t1.a WITH ROLLUP;
|
||||
@ -1429,8 +1429,8 @@ EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index; Using filesort
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
@ -1846,8 +1846,8 @@ EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index; Using filesort
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
|
@ -1300,8 +1300,8 @@ SELECT t1.a, COUNT( t2.b ), SUM( t2.b ), MAX( t2.b )
|
||||
FROM t1 JOIN t2 USING( a )
|
||||
GROUP BY t1.a WITH ROLLUP;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system NULL NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where
|
||||
1 SIMPLE t1 system NULL NULL NULL NULL 1
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using filesort
|
||||
SELECT t1.a, COUNT( t2.b ), SUM( t2.b ), MAX( t2.b )
|
||||
FROM t1 JOIN t2 USING( a )
|
||||
GROUP BY t1.a WITH ROLLUP;
|
||||
@ -1440,8 +1440,8 @@ EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index; Using filesort
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
@ -1857,8 +1857,8 @@ EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1 Using filesort
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index
|
||||
1 SIMPLE t1 system PRIMARY NULL NULL NULL 1
|
||||
1 SIMPLE t2 ref PRIMARY PRIMARY 4 const 1 Using where; Using index; Using filesort
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON t2.f1 = t1.f1
|
||||
WHERE t1.f1 = 4 AND t2.f1 IS NOT NULL AND t2.f2 IS NOT NULL
|
||||
GROUP BY t2.f1, t2.f2;
|
||||
|
@ -7,8 +7,8 @@ explain select 1 from
|
||||
(select f2, f3, val, count(id) from t4 join t2 left join t3 on 0) top
|
||||
join t1 on f1 = f3 where f3 = 'aaaa' order by val;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 12 const 1 Using index; Using filesort
|
||||
1 PRIMARY <derived2> ref key0 key0 13 const 0 Using where
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 12 const 1 Using index
|
||||
1 PRIMARY <derived2> ref key0 key0 13 const 0 Using where; Using filesort
|
||||
2 DERIVED t4 ALL NULL NULL NULL NULL 1
|
||||
2 DERIVED t2 ALL NULL NULL NULL NULL 1 Using join buffer (flat, BNL join)
|
||||
2 DERIVED t3 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join)
|
||||
|
@ -2003,8 +2003,8 @@ FROM t2 JOIN t3 ON t3.f4 = t2.f4
|
||||
WHERE t3.f1 = 8
|
||||
GROUP BY 1, 2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t3 system NULL NULL NULL NULL 1 Using filesort
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
|
||||
1 PRIMARY t3 system NULL NULL NULL NULL 1
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using filesort
|
||||
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
|
||||
3 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
|
||||
PREPARE st1 FROM "
|
||||
|
@ -50,6 +50,8 @@ public:
|
||||
/** true means we are using Priority Queue for order by with limit. */
|
||||
bool using_pq;
|
||||
|
||||
Filesort_tracker *tracker;
|
||||
|
||||
Filesort(ORDER *order_arg, ha_rows limit_arg, SQL_SELECT *select_arg):
|
||||
order(order_arg),
|
||||
limit(limit_arg),
|
||||
|
@ -69,75 +69,3 @@ void Filesort_tracker::print_json_members(Json_writer *writer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Report that we are doing a filesort.
|
||||
@return
|
||||
Tracker object to be used with filesort
|
||||
*/
|
||||
|
||||
Filesort_tracker *Sort_and_group_tracker::report_sorting(THD *thd)
|
||||
{
|
||||
DBUG_ASSERT(cur_action < MAX_QEP_ACTIONS);
|
||||
|
||||
if (total_actions)
|
||||
{
|
||||
/* This is not the first execution. Check */
|
||||
if (qep_actions[cur_action] != EXPL_ACTION_FILESORT)
|
||||
{
|
||||
varied_executions= true;
|
||||
cur_action++;
|
||||
if (!dummy_fsort_tracker)
|
||||
dummy_fsort_tracker= new (thd->mem_root) Filesort_tracker(is_analyze);
|
||||
return dummy_fsort_tracker;
|
||||
}
|
||||
return qep_actions_data[cur_action++].filesort_tracker;
|
||||
}
|
||||
|
||||
Filesort_tracker *fs_tracker= new(thd->mem_root)Filesort_tracker(is_analyze);
|
||||
qep_actions_data[cur_action].filesort_tracker= fs_tracker;
|
||||
qep_actions[cur_action++]= EXPL_ACTION_FILESORT;
|
||||
|
||||
return fs_tracker;
|
||||
}
|
||||
|
||||
|
||||
void Sort_and_group_tracker::report_tmp_table(TABLE *tbl)
|
||||
{
|
||||
DBUG_ASSERT(cur_action < MAX_QEP_ACTIONS);
|
||||
if (total_actions)
|
||||
{
|
||||
/* This is not the first execution. Check if the steps match. */
|
||||
// todo: should also check that tmp.table kinds are the same.
|
||||
if (qep_actions[cur_action] != EXPL_ACTION_TEMPTABLE)
|
||||
varied_executions= true;
|
||||
}
|
||||
|
||||
if (!varied_executions)
|
||||
{
|
||||
qep_actions[cur_action]= EXPL_ACTION_TEMPTABLE;
|
||||
// qep_actions_data[cur_action]= ....
|
||||
}
|
||||
|
||||
cur_action++;
|
||||
}
|
||||
|
||||
|
||||
void Sort_and_group_tracker::report_duplicate_removal()
|
||||
{
|
||||
DBUG_ASSERT(cur_action < MAX_QEP_ACTIONS);
|
||||
if (total_actions)
|
||||
{
|
||||
/* This is not the first execution. Check if the steps match. */
|
||||
if (qep_actions[cur_action] != EXPL_ACTION_REMOVE_DUPS)
|
||||
varied_executions= true;
|
||||
}
|
||||
|
||||
if (!varied_executions)
|
||||
{
|
||||
qep_actions[cur_action]= EXPL_ACTION_REMOVE_DUPS;
|
||||
}
|
||||
|
||||
cur_action++;
|
||||
}
|
||||
|
||||
|
@ -284,174 +284,3 @@ private:
|
||||
ulonglong sort_buffer_size;
|
||||
};
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EXPL_NO_TMP_TABLE=0,
|
||||
EXPL_TMP_TABLE_BUFFER,
|
||||
EXPL_TMP_TABLE_GROUP,
|
||||
EXPL_TMP_TABLE_DISTINCT
|
||||
} enum_tmp_table_use;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EXPL_ACTION_EOF, /* not-an-action */
|
||||
EXPL_ACTION_FILESORT,
|
||||
EXPL_ACTION_TEMPTABLE,
|
||||
EXPL_ACTION_REMOVE_DUPS,
|
||||
} enum_qep_action;
|
||||
|
||||
|
||||
/*
|
||||
This is to track how a JOIN object has resolved ORDER/GROUP BY/DISTINCT
|
||||
|
||||
We are not tied to the query plan at all, because query plan does not have
|
||||
sufficient information. *A lot* of decisions about ordering/grouping are
|
||||
made at very late stages (in JOIN::exec, JOIN::init_execution, in
|
||||
create_sort_index and even in create_tmp_table).
|
||||
|
||||
The idea is that operations that happen during select execution will report
|
||||
themselves. We have these operations:
|
||||
- Sorting with filesort()
|
||||
- Duplicate row removal (the one done by remove_duplicates()).
|
||||
- Use of temporary table to buffer the result.
|
||||
|
||||
There is also "Selection" operation, done by do_select(). It reads rows,
|
||||
there are several distinct cases:
|
||||
1. doing the join operation on the base tables
|
||||
2. reading the temporary table
|
||||
3. reading the filesort output
|
||||
it would be nice to build execution graph, e.g.
|
||||
|
||||
Select(JOIN op) -> temp.table -> filesort -> Select(filesort result)
|
||||
|
||||
the problem is that there is no way to tell what a do_select() call will do.
|
||||
|
||||
Our solution is not to have explicit selection operations. We make these
|
||||
assumptions about the query plan:
|
||||
- Select(JOIN op) is the first operation in the query plan
|
||||
- Unless the first recorded operation is filesort(). filesort() is unable
|
||||
read result of a select, so when we find it first, the query plan is:
|
||||
|
||||
filesort(first join table) -> Select(JOIN op) -> ...
|
||||
|
||||
the other popular query plan is:
|
||||
|
||||
Select (JOIN op) -> temp.table -> filesort() -> ...
|
||||
|
||||
///TODO: handle repeated execution with subselects!
|
||||
*/
|
||||
|
||||
class Sort_and_group_tracker : public Sql_alloc
|
||||
{
|
||||
enum { MAX_QEP_ACTIONS = 5 };
|
||||
|
||||
/* Query actions in the order they were made. */
|
||||
enum_qep_action qep_actions[MAX_QEP_ACTIONS];
|
||||
|
||||
/* Number for the next action */
|
||||
int cur_action;
|
||||
|
||||
/*
|
||||
Non-zero means there was already an execution which had
|
||||
#total_actions actions
|
||||
*/
|
||||
int total_actions;
|
||||
|
||||
int get_n_actions()
|
||||
{
|
||||
return total_actions? total_actions: cur_action;
|
||||
}
|
||||
|
||||
/*
|
||||
TRUE<=>there were executions which took different sort/buffer/de-duplicate
|
||||
routes. The counter values are not meaningful.
|
||||
*/
|
||||
bool varied_executions;
|
||||
|
||||
/* Details about query actions */
|
||||
union
|
||||
{
|
||||
Filesort_tracker *filesort_tracker;
|
||||
enum_tmp_table_use tmp_table;
|
||||
}
|
||||
qep_actions_data[MAX_QEP_ACTIONS];
|
||||
|
||||
Filesort_tracker *dummy_fsort_tracker;
|
||||
bool is_analyze;
|
||||
public:
|
||||
Sort_and_group_tracker(bool is_analyze_arg) :
|
||||
cur_action(0), total_actions(0), varied_executions(false),
|
||||
dummy_fsort_tracker(NULL),
|
||||
is_analyze(is_analyze_arg)
|
||||
{}
|
||||
|
||||
/*************** Reporting interface ***************/
|
||||
/* Report that join execution is started */
|
||||
void report_join_start()
|
||||
{
|
||||
if (!total_actions && cur_action != 0)
|
||||
{
|
||||
/* This is a second execution */
|
||||
total_actions= cur_action;
|
||||
}
|
||||
cur_action= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Report that a temporary table is created. The next step is to write to the
|
||||
this tmp. table
|
||||
*/
|
||||
void report_tmp_table(TABLE *tbl);
|
||||
|
||||
/*
|
||||
Report that we are doing a filesort.
|
||||
@return
|
||||
Tracker object to be used with filesort
|
||||
*/
|
||||
Filesort_tracker *report_sorting(THD *thd);
|
||||
|
||||
/*
|
||||
Report that remove_duplicates() is invoked [on a temp. table].
|
||||
We don't collect any statistics on this operation, yet.
|
||||
*/
|
||||
void report_duplicate_removal();
|
||||
|
||||
friend class Iterator;
|
||||
/*************** Statistics retrieval interface ***************/
|
||||
bool had_varied_executions() { return varied_executions; }
|
||||
|
||||
class Iterator
|
||||
{
|
||||
Sort_and_group_tracker *owner;
|
||||
int idx;
|
||||
public:
|
||||
Iterator(Sort_and_group_tracker *owner_arg) :
|
||||
owner(owner_arg), idx(owner_arg->get_n_actions() - 1)
|
||||
{}
|
||||
|
||||
enum_qep_action get_next(Filesort_tracker **tracker/*,
|
||||
enum_tmp_table_use *tmp_table_use*/)
|
||||
{
|
||||
/* Walk back through the array... */
|
||||
if (idx < 0)
|
||||
return EXPL_ACTION_EOF;
|
||||
switch (owner->qep_actions[idx])
|
||||
{
|
||||
case EXPL_ACTION_FILESORT:
|
||||
*tracker= owner->qep_actions_data[idx].filesort_tracker;
|
||||
break;
|
||||
case EXPL_ACTION_TEMPTABLE:
|
||||
//*tmp_table_use= tmp_table_kind[tmp_table_idx++];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return owner->qep_actions[idx--];
|
||||
}
|
||||
|
||||
bool is_last_element() { return idx == -1; }
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -697,14 +697,6 @@ bool Explain_node::print_explain_json_cache(Json_writer *writer,
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
|
||||
{
|
||||
delete join_tabs[idx];
|
||||
join_tabs[idx]= new_tab;
|
||||
}
|
||||
#endif
|
||||
|
||||
Explain_basic_join::~Explain_basic_join()
|
||||
{
|
||||
if (join_tabs)
|
||||
@ -755,35 +747,23 @@ int Explain_select::print_explain(Explain_query *query,
|
||||
}
|
||||
else
|
||||
{
|
||||
bool using_tmp;
|
||||
bool using_fs;
|
||||
bool using_tmp= false;
|
||||
bool using_fs= false;
|
||||
|
||||
if (is_analyze)
|
||||
for (Explain_aggr_node *node= aggr_tree; node; node= node->child)
|
||||
{
|
||||
/*
|
||||
Get the data about "Using temporary; Using filesort" from execution
|
||||
tracking system.
|
||||
*/
|
||||
using_tmp= false;
|
||||
using_fs= false;
|
||||
Sort_and_group_tracker::Iterator iter(&ops_tracker);
|
||||
enum_qep_action action;
|
||||
Filesort_tracker *dummy;
|
||||
|
||||
while ((action= iter.get_next(&dummy)) != EXPL_ACTION_EOF)
|
||||
switch (node->get_type())
|
||||
{
|
||||
if (action == EXPL_ACTION_FILESORT)
|
||||
using_fs= true;
|
||||
else if (action == EXPL_ACTION_TEMPTABLE)
|
||||
case AGGR_OP_TEMP_TABLE:
|
||||
using_tmp= true;
|
||||
break;
|
||||
case AGGR_OP_FILESORT:
|
||||
using_fs= true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use imprecise "estimates" we got with the query plan */
|
||||
using_tmp= using_temporary;
|
||||
using_fs= using_filesort;
|
||||
}
|
||||
|
||||
for (uint i=0; i< n_join_tabs; i++)
|
||||
{
|
||||
@ -877,88 +857,34 @@ void Explain_select::print_explain_json(Explain_query *query,
|
||||
}
|
||||
}
|
||||
|
||||
Filesort_tracker *first_table_sort= NULL;
|
||||
bool first_table_sort_used= false;
|
||||
int started_objects= 0;
|
||||
|
||||
if (is_analyze)
|
||||
{
|
||||
/* ANALYZE has collected this part of query plan independently */
|
||||
if (ops_tracker.had_varied_executions())
|
||||
{
|
||||
writer->add_member("varied-sort-and-tmp").start_object();
|
||||
started_objects++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Sort_and_group_tracker::Iterator iter(&ops_tracker);
|
||||
enum_qep_action action;
|
||||
Filesort_tracker *fs_tracker;
|
||||
Explain_aggr_node *node= aggr_tree;
|
||||
|
||||
while ((action= iter.get_next(&fs_tracker)) != EXPL_ACTION_EOF)
|
||||
for (; node; node= node->child)
|
||||
{
|
||||
if (action == EXPL_ACTION_FILESORT)
|
||||
switch (node->get_type())
|
||||
{
|
||||
if (iter.is_last_element())
|
||||
case AGGR_OP_TEMP_TABLE:
|
||||
writer->add_member("temporary_table").start_object();
|
||||
break;
|
||||
case AGGR_OP_FILESORT:
|
||||
{
|
||||
first_table_sort= fs_tracker;
|
||||
writer->add_member("filesort").start_object();
|
||||
if (is_analyze)
|
||||
((Explain_aggr_filesort*)node)->tracker->print_json_members(writer);
|
||||
break;
|
||||
}
|
||||
writer->add_member("filesort").start_object();
|
||||
started_objects++;
|
||||
fs_tracker->print_json_members(writer);
|
||||
}
|
||||
else if (action == EXPL_ACTION_TEMPTABLE)
|
||||
{
|
||||
writer->add_member("temporary_table").start_object();
|
||||
started_objects++;
|
||||
/*
|
||||
if (tmp == EXPL_TMP_TABLE_BUFFER)
|
||||
func= "buffer";
|
||||
else if (tmp == EXPL_TMP_TABLE_GROUP)
|
||||
func= "group-by";
|
||||
else
|
||||
func= "distinct";
|
||||
writer->add_member("function").add_str(func);
|
||||
*/
|
||||
}
|
||||
else if (action == EXPL_ACTION_REMOVE_DUPS)
|
||||
{
|
||||
case AGGR_OP_REMOVE_DUPLICATES:
|
||||
writer->add_member("duplicate_removal").start_object();
|
||||
started_objects++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (first_table_sort)
|
||||
first_table_sort_used= true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This is just EXPLAIN. Try to produce something meaningful */
|
||||
if (using_temporary)
|
||||
{
|
||||
started_objects= 1;
|
||||
if (using_filesort)
|
||||
{
|
||||
started_objects++;
|
||||
writer->add_member("filesort").start_object();
|
||||
}
|
||||
writer->add_member("temporary_table").start_object();
|
||||
writer->add_member("function").add_str("buffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (using_filesort)
|
||||
first_table_sort_used= true;
|
||||
}
|
||||
}
|
||||
|
||||
Explain_basic_join::print_explain_json_interns(query, writer, is_analyze,
|
||||
first_table_sort,
|
||||
first_table_sort_used);
|
||||
Explain_basic_join::print_explain_json_interns(query, writer, is_analyze);
|
||||
|
||||
for (;started_objects; started_objects--)
|
||||
writer->end_object();
|
||||
@ -978,7 +904,7 @@ void Explain_basic_join::print_explain_json(Explain_query *query,
|
||||
writer->add_member("query_block").start_object();
|
||||
writer->add_member("select_id").add_ll(select_id);
|
||||
|
||||
print_explain_json_interns(query, writer, is_analyze, NULL, false);
|
||||
print_explain_json_interns(query, writer, is_analyze);
|
||||
|
||||
writer->end_object();
|
||||
}
|
||||
@ -987,9 +913,7 @@ void Explain_basic_join::print_explain_json(Explain_query *query,
|
||||
void Explain_basic_join::
|
||||
print_explain_json_interns(Explain_query *query,
|
||||
Json_writer *writer,
|
||||
bool is_analyze,
|
||||
Filesort_tracker *first_table_sort,
|
||||
bool first_table_sort_used)
|
||||
bool is_analyze)
|
||||
{
|
||||
Json_writer_nesting_guard guard(writer);
|
||||
for (uint i=0; i< n_join_tabs; i++)
|
||||
@ -997,12 +921,7 @@ print_explain_json_interns(Explain_query *query,
|
||||
if (join_tabs[i]->start_dups_weedout)
|
||||
writer->add_member("duplicates_removal").start_object();
|
||||
|
||||
join_tabs[i]->print_explain_json(query, writer, is_analyze,
|
||||
first_table_sort,
|
||||
first_table_sort_used);
|
||||
|
||||
first_table_sort= NULL;
|
||||
first_table_sort_used= false;
|
||||
join_tabs[i]->print_explain_json(query, writer, is_analyze);
|
||||
|
||||
if (join_tabs[i]->end_dups_weedout)
|
||||
writer->end_object();
|
||||
@ -1294,7 +1213,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
|
||||
extra_buf.append(STRING_WITH_LEN("Using temporary"));
|
||||
}
|
||||
|
||||
if (using_filesort)
|
||||
if (using_filesort || this->using_filesort)
|
||||
{
|
||||
if (first)
|
||||
first= false;
|
||||
@ -1493,13 +1412,11 @@ void add_json_keyset(Json_writer *writer, const char *elem_name,
|
||||
|
||||
void Explain_table_access::print_explain_json(Explain_query *query,
|
||||
Json_writer *writer,
|
||||
bool is_analyze,
|
||||
Filesort_tracker *fs_tracker,
|
||||
bool first_table_sort_used)
|
||||
bool is_analyze)
|
||||
{
|
||||
Json_writer_nesting_guard guard(writer);
|
||||
|
||||
if (first_table_sort_used)
|
||||
if (using_filesort)
|
||||
{
|
||||
/* filesort was invoked on this join tab before doing the join with the rest */
|
||||
writer->add_member("read_sorted_file").start_object();
|
||||
@ -1526,6 +1443,7 @@ void Explain_table_access::print_explain_json(Explain_query *query,
|
||||
}
|
||||
}
|
||||
writer->add_member("filesort").start_object();
|
||||
|
||||
if (is_analyze)
|
||||
fs_tracker->print_json_members(writer);
|
||||
}
|
||||
@ -1718,7 +1636,7 @@ void Explain_table_access::print_explain_json(Explain_query *query,
|
||||
writer->end_object();
|
||||
}
|
||||
|
||||
if (first_table_sort_used)
|
||||
if (using_filesort)
|
||||
{
|
||||
writer->end_object(); // filesort
|
||||
writer->end_object(); // read_sorted_file
|
||||
|
@ -176,9 +176,7 @@ public:
|
||||
bool is_analyze);
|
||||
|
||||
void print_explain_json_interns(Explain_query *query, Json_writer *writer,
|
||||
bool is_analyze,
|
||||
Filesort_tracker *first_table_sort,
|
||||
bool first_table_sort_used);
|
||||
bool is_analyze);
|
||||
|
||||
/* A flat array of Explain structs for tables. */
|
||||
Explain_table_access** join_tabs;
|
||||
@ -186,6 +184,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class Explain_aggr_node;
|
||||
/*
|
||||
EXPLAIN structure for a SELECT.
|
||||
|
||||
@ -212,17 +211,9 @@ public:
|
||||
having(NULL), having_value(Item::COND_UNDEF),
|
||||
using_temporary(false), using_filesort(false),
|
||||
time_tracker(is_analyze),
|
||||
ops_tracker(is_analyze)
|
||||
aggr_tree(NULL)
|
||||
{}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
This is used to save the results of "late" test_if_skip_sort_order() calls
|
||||
that are made from JOIN::exec
|
||||
*/
|
||||
void replace_table(uint idx, Explain_table_access *new_tab);
|
||||
#endif
|
||||
|
||||
public:
|
||||
const char *select_type;
|
||||
|
||||
@ -246,7 +237,11 @@ public:
|
||||
/* ANALYZE members */
|
||||
Time_and_counter_tracker time_tracker;
|
||||
|
||||
Sort_and_group_tracker ops_tracker;
|
||||
/*
|
||||
Part of query plan describing sorting, temp.table usage, and duplicate
|
||||
removal
|
||||
*/
|
||||
Explain_aggr_node* aggr_tree;
|
||||
|
||||
int print_explain(Explain_query *query, select_result_sink *output,
|
||||
uint8 explain_flags, bool is_analyze);
|
||||
@ -261,6 +256,48 @@ private:
|
||||
Table_access_tracker using_temporary_read_tracker;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// EXPLAIN structures for ORDER/GROUP operations.
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
typedef enum
|
||||
{
|
||||
AGGR_OP_TEMP_TABLE,
|
||||
AGGR_OP_FILESORT,
|
||||
//AGGR_OP_READ_SORTED_FILE, // need this?
|
||||
AGGR_OP_REMOVE_DUPLICATES
|
||||
//AGGR_OP_JOIN // Need this?
|
||||
} enum_explain_aggr_node_type;
|
||||
|
||||
|
||||
class Explain_aggr_node : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
virtual enum_explain_aggr_node_type get_type()= 0;
|
||||
virtual ~Explain_aggr_node() {}
|
||||
Explain_aggr_node *child;
|
||||
};
|
||||
|
||||
class Explain_aggr_filesort : public Explain_aggr_node
|
||||
{
|
||||
public:
|
||||
enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; }
|
||||
Filesort_tracker *tracker;
|
||||
};
|
||||
|
||||
class Explain_aggr_tmp_table : public Explain_aggr_node
|
||||
{
|
||||
public:
|
||||
enum_explain_aggr_node_type get_type() { return AGGR_OP_TEMP_TABLE; }
|
||||
};
|
||||
|
||||
class Explain_aggr_remove_dups : public Explain_aggr_node
|
||||
{
|
||||
public:
|
||||
enum_explain_aggr_node_type get_type() { return AGGR_OP_REMOVE_DUPLICATES; }
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
Explain structure for a UNION.
|
||||
@ -618,7 +655,9 @@ public:
|
||||
where_cond(NULL),
|
||||
cache_cond(NULL),
|
||||
pushed_index_cond(NULL),
|
||||
sjm_nest(NULL)
|
||||
sjm_nest(NULL),
|
||||
using_filesort(false),
|
||||
fs_tracker(NULL)
|
||||
{}
|
||||
~Explain_table_access() { delete sjm_nest; }
|
||||
|
||||
@ -712,6 +751,8 @@ public:
|
||||
|
||||
Explain_basic_join *sjm_nest;
|
||||
|
||||
bool using_filesort;
|
||||
Filesort_tracker *fs_tracker;
|
||||
/* ANALYZE members */
|
||||
|
||||
/* Tracker for reading the table */
|
||||
@ -724,9 +765,7 @@ public:
|
||||
uint select_id, const char *select_type,
|
||||
bool using_temporary, bool using_filesort);
|
||||
void print_explain_json(Explain_query *query, Json_writer *writer,
|
||||
bool is_analyze,
|
||||
Filesort_tracker *fs_tracker,
|
||||
bool first_table_sort_used);
|
||||
bool is_analyze);
|
||||
|
||||
private:
|
||||
void append_tag_name(String *str, enum explain_extra_tag tag);
|
||||
|
@ -3110,6 +3110,14 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
|
||||
Explain_union *eu= output->get_union(nr);
|
||||
explain= &eu->fake_select_lex_explain;
|
||||
join_tab[0].tracker= eu->get_fake_select_lex_tracker();
|
||||
for (int i=0 ; i < top_join_tab_count + aggr_tables; i++)
|
||||
{
|
||||
if (join_tab[i].filesort)
|
||||
{
|
||||
join_tab[i].filesort->tracker=
|
||||
new Filesort_tracker(thd->lex->analyze_stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3123,7 +3131,6 @@ void JOIN::exec()
|
||||
dbug_serve_apcs(thd, 1);
|
||||
);
|
||||
ANALYZE_START_TRACKING(&explain->time_tracker);
|
||||
explain->ops_tracker.report_join_start();
|
||||
exec_inner();
|
||||
ANALYZE_STOP_TRACKING(&explain->time_tracker);
|
||||
|
||||
@ -17773,12 +17780,8 @@ do_select(JOIN *join, Procedure *procedure)
|
||||
join->select_lex->select_number))
|
||||
dbug_serve_apcs(join->thd, 1);
|
||||
);
|
||||
JOIN_TAB *join_tab=join->join_tab +join->top_join_tab_count;
|
||||
for (uint i= 0; i < join->aggr_tables; i++, join_tab++)
|
||||
{
|
||||
join->explain->ops_tracker.report_tmp_table(join_tab->table);
|
||||
}
|
||||
join_tab= join->join_tab + join->const_tables;
|
||||
|
||||
JOIN_TAB *join_tab= join->join_tab + join->const_tables;
|
||||
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
|
||||
error= NESTED_LOOP_NO_MORE_ROWS;
|
||||
else
|
||||
@ -21260,7 +21263,7 @@ create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort)
|
||||
table->file->info(HA_STATUS_VARIABLE); // Get record count
|
||||
filesort_retval= filesort(thd, table, fsort, tab->keep_current_rowid,
|
||||
&examined_rows, &found_rows,
|
||||
join->explain->ops_tracker.report_sorting(thd));
|
||||
fsort->tracker);
|
||||
table->sort.found_records= filesort_retval;
|
||||
tab->records= found_rows; // For SQL_CALC_ROWS
|
||||
|
||||
@ -21359,7 +21362,7 @@ JOIN_TAB::remove_duplicates()
|
||||
DBUG_ASSERT(join->aggr_tables > 0 && table->s->tmp_table != NO_TMP_TABLE);
|
||||
THD_STAGE_INFO(join->thd, stage_removing_duplicates);
|
||||
|
||||
join->explain->ops_tracker.report_duplicate_removal();
|
||||
//join->explain->ops_tracker.report_duplicate_removal();
|
||||
|
||||
table->reginfo.lock_type=TL_WRITE;
|
||||
|
||||
@ -23701,7 +23704,7 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table,
|
||||
|
||||
void JOIN_TAB::save_explain_data(Explain_table_access *eta,
|
||||
table_map prefix_tables,
|
||||
bool distinct, JOIN_TAB *first_top_tab)
|
||||
bool distinct_arg, JOIN_TAB *first_top_tab)
|
||||
{
|
||||
int quick_type;
|
||||
CHARSET_INFO *cs= system_charset_info;
|
||||
@ -23717,6 +23720,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
|
||||
explain_plan= eta;
|
||||
eta->key.clear();
|
||||
eta->quick_info= NULL;
|
||||
eta->using_filesort= false;
|
||||
|
||||
SQL_SELECT *tab_select;
|
||||
/*
|
||||
We assume that if this table does pre-sorting, then it doesn't do filtering
|
||||
@ -23725,6 +23730,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
|
||||
DBUG_ASSERT(!(select && filesort));
|
||||
tab_select= (filesort)? filesort->select : select;
|
||||
|
||||
if (filesort)
|
||||
{
|
||||
eta->using_filesort= true; // This fixes EXPLAIN
|
||||
eta->fs_tracker= filesort->tracker=
|
||||
new Filesort_tracker(thd->lex->analyze_stmt);
|
||||
}
|
||||
|
||||
tracker= &eta->tracker;
|
||||
jbuf_tracker= &eta->jbuf_tracker;
|
||||
|
||||
@ -24128,6 +24140,46 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Walk through join->aggr_tables and save aggregation/grouping query plan into
|
||||
an Explain_select object
|
||||
*/
|
||||
|
||||
void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel)
|
||||
{
|
||||
JOIN_TAB *join_tab=join->join_tab + join->top_join_tab_count;
|
||||
Explain_aggr_node *prev_node;
|
||||
Explain_aggr_node *node= xpl_sel->aggr_tree;
|
||||
|
||||
for (uint i= 0; i < join->aggr_tables; i++, join_tab++)
|
||||
{
|
||||
// Each aggregate means a temp.table
|
||||
prev_node= node;
|
||||
node= new Explain_aggr_tmp_table;
|
||||
node->child= prev_node;
|
||||
|
||||
if (join_tab->distinct)
|
||||
{
|
||||
prev_node= node;
|
||||
node= new Explain_aggr_remove_dups;
|
||||
node->child= prev_node;
|
||||
}
|
||||
|
||||
if (join_tab->filesort)
|
||||
{
|
||||
Explain_aggr_filesort *eaf = new Explain_aggr_filesort;
|
||||
eaf->tracker= new Filesort_tracker(join->thd->lex->analyze_stmt);
|
||||
join_tab->filesort->tracker= eaf->tracker;
|
||||
|
||||
prev_node= node;
|
||||
node= eaf;
|
||||
node->child= prev_node;
|
||||
}
|
||||
}
|
||||
xpl_sel->aggr_tree= node;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Save Query Plan Footprint
|
||||
|
||||
@ -24135,8 +24187,9 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
|
||||
Currently, this function may be called multiple times
|
||||
*/
|
||||
|
||||
int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
bool need_order, bool distinct,
|
||||
int JOIN::save_explain_data_intern(Explain_query *output,
|
||||
bool need_tmp_table_arg,
|
||||
bool need_order_arg, bool distinct_arg,
|
||||
const char *message)
|
||||
{
|
||||
JOIN *join= this; /* Legacy: this code used to be a non-member function */
|
||||
@ -24166,7 +24219,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
explain->select_id= join->select_lex->select_number;
|
||||
explain->select_type= join->select_lex->type;
|
||||
explain->using_temporary= need_tmp;
|
||||
explain->using_filesort= need_order;
|
||||
explain->using_filesort= need_order_arg;
|
||||
/* Setting explain->message means that all other members are invalid */
|
||||
explain->message= message;
|
||||
|
||||
@ -24183,7 +24236,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
explain->select_id= select_lex->select_number;
|
||||
explain->select_type= select_lex->type;
|
||||
explain->using_temporary= need_tmp;
|
||||
explain->using_filesort= need_order;
|
||||
explain->using_filesort= need_order_arg;
|
||||
explain->message= "Storage engine handles GROUP BY";
|
||||
|
||||
if (select_lex->master_unit()->derived)
|
||||
@ -24204,42 +24257,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
if (select_lex->master_unit()->derived)
|
||||
xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
|
||||
|
||||
if (need_tmp_table)
|
||||
xpl_sel->using_temporary= true;
|
||||
|
||||
if (need_order)
|
||||
xpl_sel->using_filesort= true;
|
||||
|
||||
/*
|
||||
Check whether we should display "Using filesort" or "Using temporary".
|
||||
This is a temporary code, we need to save the 'true' plan structure for
|
||||
EXPLAIN FORMAT=JSON.
|
||||
*/
|
||||
{
|
||||
bool using_filesort_= false;
|
||||
bool using_temporary_ = false;
|
||||
/* The first non-const join table may do sorting */
|
||||
JOIN_TAB *tab= first_top_level_tab(this, WITHOUT_CONST_TABLES);
|
||||
if (tab)
|
||||
{
|
||||
if (tab->filesort)
|
||||
using_filesort_= true;
|
||||
if (tab->aggr)
|
||||
using_temporary_= true;
|
||||
}
|
||||
|
||||
/* Aggregation tabs are located at the end of top-level join tab array. */
|
||||
JOIN_TAB *curr_tab= join_tab + top_join_tab_count;
|
||||
for (uint i= 0; i < aggr_tables; i++, curr_tab++)
|
||||
{
|
||||
if (curr_tab->filesort)
|
||||
using_filesort_= true;
|
||||
if (curr_tab->aggr)
|
||||
using_temporary_= true;
|
||||
}
|
||||
xpl_sel->using_temporary= using_temporary_;
|
||||
xpl_sel->using_filesort= using_filesort_;
|
||||
}
|
||||
save_agg_explain_data(this, xpl_sel);
|
||||
|
||||
xpl_sel->exec_const_cond= exec_const_cond;
|
||||
if (tmp_having)
|
||||
@ -24297,7 +24315,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
prev_bush_root_tab= tab->bush_root_tab;
|
||||
|
||||
cur_parent->add_table(eta, output);
|
||||
tab->save_explain_data(eta, used_tables, distinct, first_top_tab);
|
||||
tab->save_explain_data(eta, used_tables, distinct_arg, first_top_tab);
|
||||
|
||||
if (saved_join_tab)
|
||||
tab= saved_join_tab;
|
||||
|
@ -1484,6 +1484,7 @@ bool Window_func_runner::setup(THD *thd)
|
||||
spec->partition_list->first,
|
||||
spec->order_list->first);
|
||||
filesort= new (thd->mem_root) Filesort(sort_order, HA_POS_ERROR, NULL);
|
||||
filesort->tracker= new Filesort_tracker(thd->lex->analyze_stmt);
|
||||
|
||||
win_func->setup_partition_border_check(thd);
|
||||
|
||||
|
Reference in New Issue
Block a user