summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Petrunia <psergey@askmonty.org>2015-04-12 04:48:42 +0300
committerSergei Petrunia <psergey@askmonty.org>2015-04-12 04:48:42 +0300
commit4938b822634b173c0d7ef882f721b553b223fadd (patch)
treef7fc465829170e1afb3816e940e4b8304502ff28 /sql
parent66ff1632f53ef2378c2f2546e0716547ee5d3217 (diff)
downloadmariadb-git-4938b822634b173c0d7ef882f721b553b223fadd.tar.gz
MDEV-7836: ANALYZE FORMAT=JSON should provide info about GROUP/ORDER BY
Provide basic info about sorting/grouping done by the queries.
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt2
-rw-r--r--sql/filesort.cc9
-rw-r--r--sql/filesort.h4
-rw-r--r--sql/handler.h6
-rw-r--r--sql/my_json_writer.cc21
-rw-r--r--sql/my_json_writer.h1
-rw-r--r--sql/sql_analyze_stmt.cc62
-rw-r--r--sql/sql_analyze_stmt.h304
-rw-r--r--sql/sql_delete.cc25
-rw-r--r--sql/sql_explain.cc278
-rw-r--r--sql/sql_explain.h89
-rw-r--r--sql/sql_lex.cc11
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_select.cc48
-rw-r--r--sql/sql_select.h7
-rw-r--r--sql/sql_table.cc4
-rw-r--r--sql/sql_union.cc2
-rw-r--r--sql/sql_update.cc10
18 files changed, 721 insertions, 165 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 8c426d010f8..cab320bb8c9 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -105,7 +105,7 @@ SET (SQL_SOURCE
# added in MariaDB:
sql_explain.h sql_explain.cc
- sql_analyze_stmt.h
+ sql_analyze_stmt.h sql_analyze_stmt.cc
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
create_options.cc multi_range_read.cc
opt_index_cond_pushdown.cc opt_subselect.cc
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 76f744df720..66087d3271e 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -145,7 +145,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
SQL_SELECT *select, ha_rows max_rows,
bool sort_positions,
ha_rows *examined_rows,
- ha_rows *found_rows)
+ ha_rows *found_rows,
+ Filesort_tracker* tracker)
{
int error;
size_t memory_available= thd->variables.sortbuff_size;
@@ -211,6 +212,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
else
thd->inc_status_sort_scan();
thd->query_plan_flags|= QPLAN_FILESORT;
+ tracker->report_use(max_rows);
// If number of rows is not known, use as much of sort buffer as possible.
num_rows= table->file->estimate_rows_upper_bound();
@@ -226,6 +228,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
DBUG_PRINT("info", ("filesort PQ is applicable"));
thd->query_plan_flags|= QPLAN_FILESORT_PRIORITY_QUEUE;
status_var_increment(thd->status_var.filesort_pq_sorts_);
+ tracker->incr_pq_used();
const size_t compare_length= param.sort_length;
if (pq.init(param.max_rows,
true, // max_at_top
@@ -282,6 +285,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
goto err;
}
+ tracker->report_sort_buffer_size(table_sort.sort_buffer_size());
}
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
@@ -300,6 +304,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
goto err;
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
+ tracker->report_merge_passes_at_start(thd->query_plan_fsort_passes);
+ tracker->report_row_numbers(param.examined_rows, *found_rows, num_rows);
if (maxbuffer == 0) // The whole set is in memory
{
@@ -365,6 +371,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
err:
my_free(param.tmp_buffer);
+ tracker->report_merge_passes_at_end(thd->query_plan_fsort_passes);
if (!subselect || !subselect->is_uncacheable())
{
table_sort.free_sort_buffer();
diff --git a/sql/filesort.h b/sql/filesort.h
index 8960fa6cb66..4c95f1202b2 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -25,11 +25,13 @@ class SQL_SELECT;
class THD;
struct TABLE;
typedef struct st_sort_field SORT_FIELD;
+class Filesort_tracker;
ha_rows filesort(THD *thd, TABLE *table, st_sort_field *sortorder,
uint s_length, SQL_SELECT *select,
ha_rows max_rows, bool sort_positions,
- ha_rows *examined_rows, ha_rows *found_rows);
+ ha_rows *examined_rows, ha_rows *found_rows,
+ Filesort_tracker* tracker);
void filesort_free_buffers(TABLE *table, bool full);
void change_double_for_sort(double nr,uchar *to);
diff --git a/sql/handler.h b/sql/handler.h
index 0ecfa756ab9..d0478824158 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -2596,9 +2596,13 @@ public:
ulonglong rows_changed;
/* One bigger than needed to avoid to test if key == MAX_KEY */
ulonglong index_rows_read[MAX_KEY+1];
-
+
+private:
/* ANALYZE time tracker, if present */
Exec_time_tracker *tracker;
+public:
+ void set_time_tracker(Exec_time_tracker *tracker_arg) { tracker=tracker_arg;}
+
Item *pushed_idx_cond;
uint pushed_idx_cond_keyno; /* The index which the above condition is for */
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index 7a3dc776093..e97db210da7 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -130,6 +130,27 @@ void Json_writer::add_ll(longlong val)
}
+/* Add a memory size, printing in Kb, Kb, Gb if necessary */
+void Json_writer::add_size(longlong val)
+{
+ char buf[64];
+ if (val < 1024)
+ my_snprintf(buf, sizeof(buf), "%ld", val);
+ else if (val < 1024*1024*16)
+ {
+ /* Values less than 16MB are specified in KB for precision */
+ size_t len= my_snprintf(buf, sizeof(buf), "%ld", val/1024);
+ strcpy(buf + len, "Kb");
+ }
+ else
+ {
+ size_t len= my_snprintf(buf, sizeof(buf), "%ld", val/(1024*1024));
+ strcpy(buf + len, "Mb");
+ }
+ add_str(buf);
+}
+
+
void Json_writer::add_double(double val)
{
char buf[64];
diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h
index 3a7defc3566..8ab20b10d73 100644
--- a/sql/my_json_writer.h
+++ b/sql/my_json_writer.h
@@ -108,6 +108,7 @@ public:
void add_str(const String &str);
void add_ll(longlong val);
+ void add_size(longlong val);
void add_double(double val);
void add_bool(bool val);
void add_null();
diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc
new file mode 100644
index 00000000000..4874c33a544
--- /dev/null
+++ b/sql/sql_analyze_stmt.cc
@@ -0,0 +1,62 @@
+/*
+ Copyright (c) 2015 MariaDB Corporation Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include <my_global.h>
+#include "sql_priv.h"
+#include "sql_select.h"
+#include "my_json_writer.h"
+
+void Filesort_tracker::print_json(Json_writer *writer)
+{
+ const char *varied_str= "(varied across executions)";
+ writer->add_member("r_loops").add_ll(r_loops);
+
+ if (r_limit != HA_POS_ERROR)
+ {
+ writer->add_member("r_limit");
+ if (r_limit == 0)
+ writer->add_str(varied_str);
+ else
+ writer->add_ll(rint(r_limit/r_loops));
+ }
+
+ writer->add_member("r_used_priority_queue");
+ if (r_used_pq == r_loops)
+ writer->add_bool(true);
+ else if (r_used_pq == 0)
+ writer->add_bool(false);
+ else
+ writer->add_str(varied_str);
+
+ writer->add_member("r_output_rows").add_ll(rint(r_output_rows / r_loops));
+
+ if (sort_passes)
+ writer->add_member("r_sort_passes").add_ll(rint(sort_passes / r_loops));
+
+ if (sort_buffer_size != 0)
+ {
+ writer->add_member("r_buffer_size");
+ if (sort_buffer_size == ulonglong(-1))
+ writer->add_str(varied_str);
+ else
+ writer->add_size(sort_buffer_size);
+ }
+}
+
diff --git a/sql/sql_analyze_stmt.h b/sql/sql_analyze_stmt.h
index d7a7f1337e0..abd4fc1ea4b 100644
--- a/sql/sql_analyze_stmt.h
+++ b/sql/sql_analyze_stmt.h
@@ -14,6 +14,29 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+
+== ANALYZE-stmt classes ==
+
+This file contains classes for supporting "ANALYZE statement" feature. These are
+a set of data structures that can be used to store the data about how the
+statement executed.
+
+There are two kinds of data collection:
+
+1. Various counters. We assume that incrementing counters has very low
+overhead. Because of that, execution code increments counters unconditionally
+(even when not running "ANALYZE $statement" commands. You run regular SELECT/
+UPDATE/DELETE/etc and the counters are incremented).
+
+As a free bonus, this lets us print detailed information into the slow query
+log, should the query be slow.
+
+2. Timing data. Measuring the time it took to run parts of query has noticeable
+overhead. Because of that, we measure the time only when running "ANALYZE
+$stmt").
+
+*/
/*
A class for tracking time it takes to do a certain action
@@ -85,4 +108,285 @@ public:
if (unlikely((tracker)->timed)) \
{ (tracker)->stop_tracking(); }
+/*
+ A class for collecting read statistics.
+
+ The idea is that we run several scans. Each scans gets rows, and then filters
+ some of them out. We count scans, rows, and rows left after filtering.
+
+ (note: at the moment, the class is not actually tied to a physical table.
+ It can be used to track reading from files, buffers, etc).
+*/
+
+class Table_access_tracker
+{
+public:
+ Table_access_tracker() :
+ r_scans(0), r_rows(0), /*r_rows_after_table_cond(0),*/
+ r_rows_after_where(0)
+ {}
+
+ ha_rows r_scans; /* How many scans were ran on this join_tab */
+ ha_rows r_rows; /* How many rows we've got after that */
+ ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
+
+ bool has_scans() { return (r_scans != 0); }
+ ha_rows get_loops() { return r_scans; }
+ double get_avg_rows()
+ {
+ return r_scans ? ((double)r_rows / r_scans): 0;
+ }
+
+ double get_filtered_after_where()
+ {
+ double r_filtered;
+ if (r_rows > 0)
+ r_filtered= (double)r_rows_after_where / r_rows;
+ else
+ r_filtered= 1.0;
+
+ return r_filtered;
+ }
+
+ inline void on_scan_init() { r_scans++; }
+ inline void on_record_read() { r_rows++; }
+ inline void on_record_after_where() { r_rows_after_where++; }
+};
+
+
+class Json_writer;
+
+/*
+ This stores the data about how filesort executed.
+
+ A few things from here (e.g. r_used_pq, r_limit) belong to the query plan,
+ however, these parameters are calculated right during the execution so we
+ can't easily put them into the query plan.
+
+ The class is designed to handle multiple invocations of filesort().
+*/
+
+class Filesort_tracker : public Sql_alloc
+{
+public:
+ Filesort_tracker() :
+ r_loops(0), r_limit(0), r_used_pq(0),
+ r_examined_rows(0), r_sorted_rows(0), r_output_rows(0),
+ sort_passes(0),
+ sort_buffer_size(0)
+ {}
+
+ /* Functions that filesort uses to report various things about its execution */
+
+ inline void report_use(ha_rows r_limit_arg)
+ {
+ if (!r_loops++)
+ r_limit= r_limit_arg;
+ else
+ r_limit= (r_limit != r_limit_arg)? 0: r_limit_arg;
+ }
+ inline void incr_pq_used() { r_used_pq++; }
+
+ inline void report_row_numbers(ha_rows examined_rows,
+ ha_rows sorted_rows,
+ ha_rows returned_rows)
+ {
+ r_examined_rows += examined_rows;
+ r_sorted_rows += sorted_rows;
+ r_output_rows += returned_rows;
+ }
+
+ inline void report_merge_passes_at_start(ulong passes)
+ {
+ sort_passes -= passes;
+ }
+ inline void report_merge_passes_at_end(ulong passes)
+ {
+ sort_passes += passes;
+ }
+
+ inline void report_sort_buffer_size(size_t bufsize)
+ {
+ if (sort_buffer_size)
+ sort_buffer_size= ulonglong(-1); // multiple buffers of different sizes
+ else
+ sort_buffer_size= bufsize;
+ }
+
+ /* Functions to get the statistics */
+ void print_json(Json_writer *writer);
+
+ ulonglong get_r_loops() { return r_loops; }
+ double get_avg_examined_rows()
+ {
+ return ((double)r_examined_rows) / r_loops;
+ }
+ double get_avg_returned_rows()
+ {
+ return ((double)r_output_rows) / r_loops;
+ }
+ double get_r_filtered()
+ {
+ if (r_examined_rows > 0)
+ return ((double)r_sorted_rows / r_examined_rows);
+ else
+ return 1.0;
+ }
+private:
+ ulonglong r_loops; /* How many times filesort was invoked */
+ /*
+ LIMIT is typically a constant. There is never "LIMIT 0".
+ HA_POS_ERROR means we never had a limit
+ 0 means different values of LIMIT were used in
+ different filesort invocations
+ other value means the same LIMIT value was used every time.
+ */
+ ulonglong r_limit;
+ ulonglong r_used_pq; /* How many times PQ was used */
+
+ /* How many rows were examined (before checking the select->cond) */
+ ulonglong r_examined_rows;
+
+ /*
+ How many rows were put into sorting (this is examined_rows minus rows that
+ didn't pass the WHERE condition)
+ */
+ ulonglong r_sorted_rows;
+
+ /*
+ How many rows were returned. This is equal to r_sorted_rows, unless there
+ was a LIMIT N clause in which case filesort would not have returned more
+ than N rows.
+ */
+ ulonglong r_output_rows;
+
+ /* How many sorts in total (divide by r_count to get the average) */
+ ulonglong sort_passes;
+
+ /*
+ 0 - means not used (or not known
+ (ulonglong)-1 - multiple
+ other - value
+ */
+ 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_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];
+ uint n_actions;
+
+ /*
+ Trackers for filesort operation. JOIN::exec() may need at most two sorting
+ operations.
+ */
+ Filesort_tracker filesort_tracker[2];
+ int cur_tracker;
+
+ /* Information about temporary tables */
+ enum_tmp_table_use tmp_table_kind[2];
+ int cur_tmp_table;
+
+ friend class Explain_select;
+
+public:
+ Sort_and_group_tracker() :
+ n_actions(0),
+ cur_tracker(0),
+ cur_tmp_table(0)
+ {}
+
+ /*************** Reporting interface ***************/
+ /* Report that join execution is started */
+ void report_join_start()
+ {
+ n_actions= 0;
+ cur_tracker= 0;
+ cur_tmp_table= 0;
+ }
+
+ /* Report that a temporary table is created. */
+ void report_tmp_table(TABLE *tbl)
+ {
+ DBUG_ASSERT(n_actions < MAX_QEP_ACTIONS);
+ qep_actions[n_actions++]= EXPL_ACTION_TEMPTABLE;
+
+ DBUG_ASSERT(cur_tmp_table < 2);
+ cur_tmp_table++;
+ }
+
+ /* Report that we are doing a filesort. */
+ Filesort_tracker *report_sorting()
+ {
+ DBUG_ASSERT(n_actions < MAX_QEP_ACTIONS);
+ qep_actions[n_actions++]= EXPL_ACTION_FILESORT;
+
+ DBUG_ASSERT(cur_tracker < 2);
+ return &filesort_tracker[cur_tracker++];
+ }
+
+ /*************** Statistics retrieval interface ***************/
+ //enum_tmp_table_use get_tmp_table_type() { return join_result_tmp_table; }
+};
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index fda74481f11..b149747649e 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -67,7 +67,8 @@ Explain_delete* Delete_plan::save_explain_delete_data(MEM_ROOT *mem_root, THD *t
else
{
explain->deleting_all_rows= false;
- Update_plan::save_explain_data_intern(mem_root, query, explain);
+ Update_plan::save_explain_data_intern(mem_root, explain,
+ thd->lex->analyze_stmt);
}
query->add_upd_del_plan(explain);
@@ -81,15 +82,15 @@ Update_plan::save_explain_update_data(MEM_ROOT *mem_root, THD *thd)
Explain_query *query= thd->lex->explain;
Explain_update* explain=
new (mem_root) Explain_update(mem_root, thd->lex->analyze_stmt);
- save_explain_data_intern(mem_root, query, explain);
+ save_explain_data_intern(mem_root, explain, thd->lex->analyze_stmt);
query->add_upd_del_plan(explain);
return explain;
}
void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
- Explain_query *query,
- Explain_update *explain)
+ Explain_update *explain,
+ bool is_analyze)
{
explain->select_type= "SIMPLE";
explain->table_name.append(table->pos_in_table_list->alias);
@@ -109,6 +110,9 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
return;
}
+ if (is_analyze)
+ table->file->set_time_tracker(&explain->table_tracker);
+
select_lex->set_explain_type(TRUE);
explain->select_type= select_lex->type;
/* Partitions */
@@ -151,7 +155,9 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
explain->using_where= MY_TEST(select && select->cond);
explain->where_cond= select? select->cond: NULL;
- explain->using_filesort= using_filesort;
+
+ if (using_filesort)
+ explain->filesort_tracker= new (mem_root) Filesort_tracker;
explain->using_io_buffer= using_io_buffer;
append_possible_keys(mem_root, explain->possible_keys, table,
@@ -486,13 +492,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL |
MY_THREAD_SPECIFIC));
-
+ Filesort_tracker *fs_tracker=
+ thd->lex->explain->get_upd_del_plan()->filesort_tracker;
+
if (!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records= filesort(thd, table, sortorder, length,
select, HA_POS_ERROR,
true,
- &examined_rows, &found_rows))
- == HA_POS_ERROR)
+ &examined_rows, &found_rows,
+ fs_tracker))
+ == HA_POS_ERROR)
{
delete select;
free_underlaid_joins(thd, &thd->lex->select_lex);
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 15f6753130c..3c1ebc288ab 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -743,11 +743,6 @@ void Explain_select::print_explain_json(Explain_query *query,
}
else
{
- /*
- TODO: how does this approach allow to print ORDER BY members?
- Explain_basic_join does not have ORDER/GROUP.
- A: factor out join tab printing loop into a common func.
- */
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id);
@@ -761,8 +756,67 @@ void Explain_select::print_explain_json(Explain_query *query,
writer->add_member("const_condition");
write_item(writer, exec_const_cond);
}
+
+ Filesort_tracker *first_table_sort= false;
+ int started_objects= 0;
+
+ if (is_analyze)
+ {
+ /* ANALYZE has collected this part of query plan independently */
+ for (int i= ops_tracker.n_actions-1; i >= 0; i--)
+ {
+ if (ops_tracker.qep_actions[i] == EXPL_ACTION_FILESORT)
+ {
+ if (i == 0)
+ {
+ /* filesort operation was the first in the pipeline */
+ first_table_sort= &ops_tracker.filesort_tracker[0];
+ break;
+ }
+ writer->add_member("filesort").start_object();
+ started_objects++;
+ }
+ else if (ops_tracker.qep_actions[i] == 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 (ops_tracker.qep_actions[i] == EXPL_ACTION_REMOVE_DUPS)
+ {
+ writer->add_member("duplicate_removal").start_object();
+ started_objects++;
+ }
+ else
+ DBUG_ASSERT(0);
+ }
+
+ }
+ else
+ {
+ /* This is just EXPLAIN. Try to produce something meaningful */
+ if (using_temporary)
+ {
+ started_objects= 1;
+ writer->add_member("temporary_table").start_object();
+ writer->add_member("function").add_str("buffer");
+ }
+ }
+
+ Explain_basic_join::print_explain_json_interns(query, writer, is_analyze,
+ first_table_sort);
+
+ for (;started_objects; started_objects--)
+ writer->end_object();
- Explain_basic_join::print_explain_json_interns(query, writer, is_analyze);
writer->end_object();
}
@@ -776,24 +830,27 @@ 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);
+ print_explain_json_interns(query, writer, is_analyze, NULL);
writer->end_object();
}
-void Explain_basic_join::print_explain_json_interns(Explain_query *query,
- Json_writer *writer,
- bool is_analyze)
+void Explain_basic_join::
+print_explain_json_interns(Explain_query *query,
+ Json_writer *writer,
+ bool is_analyze,
+ Filesort_tracker *first_table_sort)
{
Json_writer_nesting_guard guard(writer);
for (uint i=0; i< n_join_tabs; i++)
{
if (join_tabs[i]->start_dups_weedout)
writer->add_member("duplicates_removal").start_object();
-
- join_tabs[i]->print_explain_json(query, writer, is_analyze);
-
+
+ join_tabs[i]->print_explain_json(query, writer, is_analyze,
+ (i==0)? first_table_sort : NULL);
+
if (join_tabs[i]->end_dups_weedout)
writer->end_object();
}
@@ -1230,11 +1287,47 @@ void add_json_keyset(Json_writer *writer, const char *elem_name,
}
+/*
+ @param fs_tracker Normally NULL. When not NULL, it means that the join tab
+ used filesort.
+*/
+
void Explain_table_access::print_explain_json(Explain_query *query,
Json_writer *writer,
- bool is_analyze)
+ bool is_analyze,
+ Filesort_tracker *fs_tracker)
{
Json_writer_nesting_guard guard(writer);
+
+ if (fs_tracker)
+ {
+ /* filesort was invoked on this join tab before doing the join with the rest */
+ writer->add_member("read_sorted_file").start_object();
+ if (is_analyze)
+ {
+ writer->add_member("r_rows");
+ /*
+ r_rows when reading filesort result. This can be less than the number
+ of rows produced by filesort due to NL-join having LIMIT.
+ */
+ if (tracker.has_scans())
+ writer->add_double(tracker.get_avg_rows());
+ else
+ writer->add_null();
+
+ /*
+ r_filtered when reading filesort result. We should have checked the
+ WHERE while doing filesort but lets check just in case.
+ */
+ if (tracker.has_scans() && tracker.get_filtered_after_where() < 1.0)
+ {
+ writer->add_member("r_filtered");
+ writer->add_double(tracker.get_filtered_after_where()*100.0);
+ }
+ }
+ writer->add_member("filesort").start_object();
+ fs_tracker->print_json(writer);
+ }
if (bka_type.is_using_jbuf())
{
@@ -1322,13 +1415,21 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (is_analyze)
{
writer->add_member("r_rows");
- if (tracker.has_scans())
+ if (fs_tracker)
{
- double avg_rows= tracker.get_avg_rows();
- writer->add_double(avg_rows);
+ /* Get r_rows value from filesort */
+ if (fs_tracker->get_r_loops())
+ writer->add_double(fs_tracker->get_avg_examined_rows());
+ else
+ writer->add_null();
}
else
- writer->add_null();
+ {
+ if (tracker.has_scans())
+ writer->add_double(tracker.get_avg_rows());
+ else
+ writer->add_null();
+ }
if (op_tracker.get_loops())
{
@@ -1345,10 +1446,22 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (is_analyze)
{
writer->add_member("r_filtered");
- if (tracker.has_scans())
- writer->add_double(tracker.get_filtered_after_where()*100.0);
+ if (fs_tracker)
+ {
+ /* Get r_filtered value from filesort */
+ if (fs_tracker->get_r_loops())
+ writer->add_double(fs_tracker->get_r_filtered());
+ else
+ writer->add_null();
+ }
else
- writer->add_null();
+ {
+ /* Get r_filtered from the NL-join runtime */
+ if (tracker.has_scans())
+ writer->add_double(tracker.get_filtered_after_where()*100.0);
+ else
+ writer->add_null();
+ }
}
for (int i=0; i < (int)extra_tags.elements(); i++)
@@ -1414,6 +1527,12 @@ void Explain_table_access::print_explain_json(Explain_query *query,
writer->end_object();
}
+ if (fs_tracker)
+ {
+ writer->end_object(); // filesort
+ writer->end_object(); // read_sorted_file
+ }
+
writer->end_object();
}
@@ -1777,7 +1896,7 @@ int Explain_update::print_explain(Explain_query *query,
extra_str.append(mrr_type);
}
- if (using_filesort)
+ if (is_using_filesort())
{
if (extra_str.length() !=0)
extra_str.append(STRING_WITH_LEN("; "));
@@ -1825,7 +1944,14 @@ void Explain_update::print_explain_json(Explain_query *query,
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(1);
-
+
+ /* This is the total time it took to do the UPDATE/DELETE */
+ if (is_analyze && command_tracker.get_loops())
+ {
+ writer->add_member("r_total_time_ms").
+ add_double(command_tracker.get_time_ms());
+ }
+
if (impossible_where || no_partitions)
{
const char *msg= impossible_where ? STR_IMPOSSIBLE_WHERE :
@@ -1837,6 +1963,25 @@ void Explain_update::print_explain_json(Explain_query *query,
return;
}
+ DBUG_ASSERT(!(is_using_filesort() && using_io_buffer));
+
+ bool doing_buffering= false;
+
+ if (is_using_filesort())
+ {
+ writer->add_member("filesort").start_object();
+ if (is_analyze)
+ filesort_tracker->print_json(writer);
+ doing_buffering= true;
+ }
+
+ if (using_io_buffer)
+ {
+ writer->add_member("buffer").start_object();
+ doing_buffering= true;
+ }
+
+ /* Produce elements that are common for buffered and un-buffered cases */
writer->add_member("table").start_object();
if (get_type() == EXPLAIN_UPDATE)
@@ -1898,50 +2043,58 @@ void Explain_update::print_explain_json(Explain_query *query,
writer->end_object();
}
-#if 0
- /* `ref` */
- if (!ref_list.is_empty())
- {
- List_iterator_fast<char> it(ref_list);
- const char *str;
- writer->add_member("ref").start_array();
- while ((str= it++))
- writer->add_str(str);
- writer->end_array();
- }
-#endif
-
/* `rows` */
writer->add_member("rows").add_ll(rows);
- /* `r_rows` */
- if (is_analyze && tracker.has_scans())
- {
- double avg_rows= tracker.get_avg_rows();
- writer->add_member("r_rows").add_double(avg_rows);
- }
-
- /* UPDATE/DELETE do not produce `filtered` estimate */
- /* `r_filtered` */
+ if (mrr_type.length() != 0)
+ writer->add_member("mrr_type").add_str(mrr_type.ptr());
+
if (is_analyze)
{
- double r_filtered= tracker.get_filtered_after_where() * 100.0;
- writer->add_member("r_filtered").add_double(r_filtered);
- }
+ if (doing_buffering)
+ {
+ ha_rows r_rows;
+ double r_filtered;
- if (mrr_type.length() != 0)
- writer->add_member("mrr_type").add_str(mrr_type.ptr());
-
- if (using_filesort)
- writer->add_member("using_filesort").add_ll(1);
+ if (is_using_filesort())
+ {
+ if (filesort_tracker->get_r_loops())
+ r_rows= filesort_tracker->get_avg_examined_rows();
+ else
+ r_rows= 0;
+ r_filtered= filesort_tracker->get_r_filtered() * 100.0;
+ }
+ else
+ {
+ if (buf_tracker.has_scans())
+ r_rows= (ha_rows) buf_tracker.get_avg_rows();
+ else
+ r_rows= 0;
+ r_filtered= buf_tracker.get_filtered_after_where() * 100.0;
+ }
+ writer->add_member("r_rows").add_ll(r_rows);
+ writer->add_member("r_filtered").add_double(r_filtered);
+ }
+ else /* Not doing buffering */
+ {
+ writer->add_member("r_rows");
+ if (tracker.has_scans())
+ writer->add_double(tracker.get_avg_rows());
+ else
+ writer->add_null();
- if (using_io_buffer)
- writer->add_member("using_io_buffer").add_ll(1);
+ /* There is no 'filtered' estimate in UPDATE/DELETE atm */
+ double r_filtered= tracker.get_filtered_after_where() * 100.0;
+ writer->add_member("r_filtered").add_double(r_filtered);
+ }
- if (is_analyze && command_tracker.get_loops())
- writer->
- add_member("r_total_time_ms").add_double(command_tracker.get_time_ms());
+ if (table_tracker.get_loops())
+ {
+ writer->add_member("r_total_time_ms").
+ add_double(table_tracker.get_time_ms());
+ }
+ }
if (where_cond)
{
@@ -1949,7 +2102,15 @@ void Explain_update::print_explain_json(Explain_query *query,
write_item(writer, where_cond);
}
+ /*** The part of plan that is before the buffering/sorting ends here ***/
+ if (is_using_filesort())
+ writer->end_object();
+
+ if (using_io_buffer)
+ writer->end_object();
+
writer->end_object(); // table
+
print_explain_json_for_children(query, writer, is_analyze);
writer->end_object(); // query_block
}
@@ -2105,3 +2266,4 @@ void Explain_range_checked_fer::print_json(Json_writer *writer,
writer->end_object();
}
}
+
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 9c6171aa6d0..2513e03cf1c 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -59,51 +59,6 @@ public:
class Json_writer;
-/*
- A class for collecting read statistics.
-
- The idea is that we run several scans. Each scans gets rows, and then filters
- some of them out. We count scans, rows, and rows left after filtering.
-
- (note: at the moment, the class is not actually tied to a physical table.
- It can be used to track reading from files, buffers, etc).
-*/
-
-class Table_access_tracker
-{
-public:
- Table_access_tracker() :
- r_scans(0), r_rows(0), /*r_rows_after_table_cond(0),*/
- r_rows_after_where(0)
- {}
-
- ha_rows r_scans; /* How many scans were ran on this join_tab */
- ha_rows r_rows; /* How many rows we've got after that */
- ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
-
- bool has_scans() { return (r_scans != 0); }
- ha_rows get_loops() { return r_scans; }
- double get_avg_rows()
- {
- return r_scans ? ((double)r_rows / r_scans): 0;
- }
-
- double get_filtered_after_where()
- {
- double r_filtered;
- if (r_rows > 0)
- r_filtered= (double)r_rows_after_where / r_rows;
- else
- r_filtered= 1.0;
-
- return r_filtered;
- }
-
- inline void on_scan_init() { r_scans++; }
- inline void on_record_read() { r_rows++; }
- inline void on_record_after_where() { r_rows_after_where++; }
-};
-
/**************************************************************************************
Data structures for producing EXPLAIN outputs.
@@ -212,7 +167,8 @@ public:
bool is_analyze);
void print_explain_json_interns(Explain_query *query, Json_writer *writer,
- bool is_analyze);
+ bool is_analyze,
+ Filesort_tracker *first_table_sort);
/* A flat array of Explain structs for tables. */
Explain_table_access** join_tabs;
@@ -271,6 +227,8 @@ public:
/* ANALYZE members */
Time_and_counter_tracker time_tracker;
+
+ Sort_and_group_tracker ops_tracker;
int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags, bool is_analyze);
@@ -295,9 +253,9 @@ private:
class Explain_union : public Explain_node
{
public:
- Explain_union(MEM_ROOT *root) :
- Explain_node(root),
- time_tracker(false)
+ Explain_union(MEM_ROOT *root, bool is_analyze) :
+ Explain_node(root),
+ fake_select_lex_explain(root, is_analyze)
{}
enum explain_node_type get_type() { return EXPLAIN_UNION; }
@@ -332,8 +290,13 @@ public:
const char *fake_select_type;
bool using_filesort;
bool using_tmp;
- /* TODO: the below is not printed yet:*/
- Time_and_counter_tracker time_tracker;
+
+ /*
+ Explain data structure for "fake_select_lex" (i.e. for the degenerate
+ SELECT that reads UNION result).
+ It doesn't have a query plan, but we still need execution tracker, etc.
+ */
+ Explain_select fake_select_lex_explain;
Table_access_tracker *get_fake_select_lex_tracker()
{
@@ -729,13 +692,13 @@ public:
Table_access_tracker tracker;
Exec_time_tracker op_tracker;
Table_access_tracker jbuf_tracker;
-
+
int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze,
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);
+ bool is_analyze, Filesort_tracker *fs_tracker);
private:
void append_tag_name(String *str, enum explain_extra_tag tag);
@@ -759,6 +722,7 @@ public:
Explain_update(MEM_ROOT *root, bool is_analyze) :
Explain_node(root),
+ filesort_tracker(NULL),
command_tracker(is_analyze)
{}
@@ -793,15 +757,30 @@ public:
ha_rows rows;
- bool using_filesort;
bool using_io_buffer;
+ /* Tracker for doing reads when filling the buffer */
+ Table_access_tracker buf_tracker;
+
+ bool is_using_filesort() { return filesort_tracker? true: false; }
+ /*
+ Non-null value of filesort_tracker means "using filesort"
+
+ if we are using filesort, then table_tracker is for the io done inside
+ filesort.
+
+ 'tracker' is for tracking post-filesort reads.
+ */
+ Filesort_tracker *filesort_tracker;
+
/* ANALYZE members and methods */
Table_access_tracker tracker;
/* This tracks execution of the whole command */
Time_and_counter_tracker command_tracker;
- //psergey-todo: io-tracker here.
+
+ /* TODO: This tracks time to read rows from the table */
+ Exec_time_tracker table_tracker;
virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags, bool is_analyze);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index c30fcd95d73..e5d261e02d4 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -4331,7 +4331,9 @@ void LEX::restore_set_statement_var()
int st_select_lex_unit::save_union_explain(Explain_query *output)
{
SELECT_LEX *first= first_select();
- Explain_union *eu= new (output->mem_root) Explain_union(output->mem_root);
+ Explain_union *eu=
+ new (output->mem_root) Explain_union(output->mem_root,
+ thd->lex->analyze_stmt);
if (derived)
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
@@ -4374,12 +4376,7 @@ int st_select_lex_unit::save_union_explain_part2(Explain_query *output)
eu->add_child(unit->first_select()->select_number);
}
}
-
- /*
- Having a time tracker for reading UNION result is not very interesting
- but is easier, as JOIN::exec now relies on having a tracker.
- */
- fake_select_lex->join->tracker= &eu->time_tracker;
+ fake_select_lex->join->explain= &eu->fake_select_lex_explain;
}
return 0;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 6ad0760c45c..64b07b1927d 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2335,8 +2335,7 @@ public:
Explain_update* save_explain_update_data(MEM_ROOT *mem_root, THD *thd);
protected:
- void save_explain_data_intern(MEM_ROOT *mem_root, Explain_query *query,
- Explain_update *eu);
+ void save_explain_data_intern(MEM_ROOT *mem_root, Explain_update *eu, bool is_analyze);
public:
virtual ~Update_plan() {}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 207bee922e8..e7c2e91a15c 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1988,7 +1988,7 @@ int JOIN::init_execution()
group_list && simple_group,
select_options, tmp_rows_limit, "")))
DBUG_RETURN(1);
-
+ explain->ops_tracker.report_tmp_table(exec_tmp_table1);
/*
We don't have to store rows in temp table that doesn't match HAVING if:
- we are sorting the table and writing complete group rows to the
@@ -2378,8 +2378,8 @@ void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
*/
uint nr= select_lex->master_unit()->first_select()->select_number;
Explain_union *eu= output->get_union(nr);
+ explain= &eu->fake_select_lex_explain;
join_tab[0].tracker= eu->get_fake_select_lex_tracker();
- tracker= &eu->time_tracker;
}
}
@@ -2392,9 +2392,10 @@ void JOIN::exec()
select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
- ANALYZE_START_TRACKING(tracker);
+ ANALYZE_START_TRACKING(&explain->time_tracker);
+ explain->ops_tracker.report_join_start();
exec_inner();
- ANALYZE_STOP_TRACKING(tracker);
+ ANALYZE_STOP_TRACKING(&explain->time_tracker);
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
if (dbug_user_var_equals_int(thd,
@@ -2768,6 +2769,7 @@ void JOIN::exec_inner()
HA_POS_ERROR, "")))
DBUG_VOID_RETURN;
curr_join->exec_tmp_table2= exec_tmp_table2;
+ explain->ops_tracker.report_tmp_table(exec_tmp_table2);
}
if (curr_join->group_list)
{
@@ -2869,6 +2871,7 @@ void JOIN::exec_inner()
curr_join->select_distinct=0;
}
curr_tmp_table->reginfo.lock_type= TL_UNLOCK;
+ // psergey-todo: here is one place where we switch to
if (curr_join->make_simple_join(this, curr_tmp_table))
DBUG_VOID_RETURN;
calc_group_buffer(curr_join, curr_join->group_list);
@@ -3057,7 +3060,6 @@ void JOIN::exec_inner()
curr_join->table_count,
(int) curr_join->select_limit,
(int) unit->select_limit_cnt));
-
if (create_sort_index(thd,
curr_join,
order_arg,
@@ -20931,7 +20933,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
table->file->info(HA_STATUS_VARIABLE); // Get record count
filesort_retval= filesort(thd, table, join->sortorder, length,
select, filesort_limit, 0,
- &examined_rows, &found_rows);
+ &examined_rows, &found_rows,
+ join->explain->ops_tracker.report_sorting());
table->sort.found_records= filesort_retval;
tab->records= found_rows; // For SQL_CALC_ROWS
@@ -23442,10 +23445,10 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
tab->tracker= &eta->tracker;
tab->jbuf_tracker= &eta->jbuf_tracker;
-
+
/* Enable the table access time tracker only for "ANALYZE stmt" */
if (thd->lex->analyze_stmt)
- tab->table->file->tracker= &eta->op_tracker;
+ tab->table->file->set_time_tracker(&eta->op_tracker);
/* No need to save id and select_type here, they are kept in Explain_select */
@@ -23849,7 +23852,6 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
bool need_order, bool distinct,
const char *message)
{
- Explain_node *explain_node= 0;
JOIN *join= this; /* Legacy: this code used to be a non-member function */
int error= 0;
DBUG_ENTER("JOIN::save_explain_data_intern");
@@ -23864,33 +23866,32 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
DBUG_ASSERT(!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived());
+ explain= NULL;
+
/* Don't log this into the slow query log */
if (message)
{
- Explain_select *xpl_sel;
- explain_node= xpl_sel=
- new (output->mem_root) Explain_select(output->mem_root,
- thd->lex->analyze_stmt);
+ explain= new (output->mem_root) Explain_select(output->mem_root,
+ thd->lex->analyze_stmt);
join->select_lex->set_explain_type(true);
- xpl_sel->select_id= join->select_lex->select_number;
- xpl_sel->select_type= join->select_lex->type;
- xpl_sel->message= message;
- tracker= &xpl_sel->time_tracker;
+ explain->select_id= join->select_lex->select_number;
+ explain->select_type= join->select_lex->type;
+ /* Setting explain->message means that all other members are invalid */
+ explain->message= message;
+
if (select_lex->master_unit()->derived)
- xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
- /* Setting xpl_sel->message means that all other members are invalid */
- output->add_node(xpl_sel);
+ explain->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
+ output->add_node(explain);
}
else
{
Explain_select *xpl_sel;
- explain_node= xpl_sel=
+ explain= xpl_sel=
new (output->mem_root) Explain_select(output->mem_root,
thd->lex->analyze_stmt);
table_map used_tables=0;
- tracker= &xpl_sel->time_tracker;
join->select_lex->set_explain_type(true);
xpl_sel->select_id= join->select_lex->select_number;
@@ -23986,13 +23987,12 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
if (!(unit->item && unit->item->eliminated) && // (1)
(!unit->derived || unit->derived->is_materialized_derived())) // (2)
{
- explain_node->add_child(unit->first_select()->select_number);
+ explain->add_child(unit->first_select()->select_number);
}
}
if (!error && select_lex->is_top_level_node())
output->query_plan_ready();
-
DBUG_RETURN(error);
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index c1db919ae2c..33468dcfdc7 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1225,7 +1225,8 @@ public:
/** Is set if we have a GROUP BY and we have ORDER BY on a constant. */
bool skip_sort_order;
- bool need_tmp, hidden_group_fields;
+ bool need_tmp;
+ bool hidden_group_fields;
/* TRUE if there was full cleunap of the JOIN */
bool cleaned;
DYNAMIC_ARRAY keyuse;
@@ -1282,7 +1283,7 @@ public:
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls
- Time_and_counter_tracker *tracker;
+ Explain_select *explain;
enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
@@ -1377,7 +1378,7 @@ public:
no_rows_in_result_called= 0;
positions= best_positions= 0;
- tracker= NULL;
+ explain= NULL;
all_fields= fields_arg;
if (&fields_list != &fields_arg) /* Avoid valgrind-warning */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 4550b10c40c..bc25951bafa 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -9340,6 +9340,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
tables.db= from->s->db.str;
THD_STAGE_INFO(thd, stage_sorting);
+ Filesort_tracker dummy_tracker;
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order) ||
@@ -9347,7 +9348,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
(from->sort.found_records= filesort(thd, from, sortorder, length,
NULL, HA_POS_ERROR,
true,
- &examined_rows, &found_rows)) ==
+ &examined_rows, &found_rows,
+ &dummy_tracker)) ==
HA_POS_ERROR)
goto err;
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index be2a73dabb7..2ac83773c34 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -969,7 +969,7 @@ bool st_select_lex_unit::exec()
1st execution sets certain members (e.g. select_result) to perform
subquery execution rather than EXPLAIN line production. In order
to reset them back, we re-do all of the actions (yes it is ugly):
- */
+ */ // psergey-todo: is the above really necessary anymore??
join->init(thd, item_list, fake_select_lex->options, result);
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 20a9a1fa915..cf6a0f0f140 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -554,11 +554,15 @@ int mysql_update(THD *thd,
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL |
MY_THREAD_SPECIFIC));
+ Filesort_tracker *fs_tracker=
+ thd->lex->explain->get_upd_del_plan()->filesort_tracker;
+
if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records= filesort(thd, table, sortorder, length,
select, limit,
true,
- &examined_rows, &found_rows))
+ &examined_rows, &found_rows,
+ fs_tracker))
== HA_POS_ERROR)
{
goto err;
@@ -578,7 +582,7 @@ int mysql_update(THD *thd,
we go trough the matching rows, save a pointer to them and
update these in a separate loop based on the pointer.
*/
-
+ explain->buf_tracker.on_scan_init();
IO_CACHE tempfile;
if (open_cached_file(&tempfile, mysql_tmpdir,TEMP_PREFIX,
DISK_BUFFER_SIZE, MYF(MY_WME)))
@@ -619,6 +623,7 @@ int mysql_update(THD *thd,
while (!(error=info.read_record(&info)) && !thd->killed)
{
+ explain->buf_tracker.on_record_read();
if (table->vfield)
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
@@ -629,6 +634,7 @@ int mysql_update(THD *thd,
if (table->file->was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */
+ explain->buf_tracker.on_record_after_where();
table->file->position(table->record[0]);
if (my_b_write(&tempfile,table->file->ref,
table->file->ref_length))