Commit 3c6ac669 authored by Sergey Petrunya's avatar Sergey Petrunya

MDEV-3798: EXPLAIN UPDATE/DELETE

- Produce correct #rows for ORDER BY ... LIMIT N queries that take advantage of 
  ordered index read to read only N rows.
parent 161d6875
...@@ -21,7 +21,7 @@ from t0 A, t0 B, t0 C; ...@@ -21,7 +21,7 @@ from t0 A, t0 B, t0 C;
# This should use an index, possible_keys=NULL because there is no WHERE # This should use an index, possible_keys=NULL because there is no WHERE
explain delete from t1 order by a limit 2; explain delete from t1 order by a limit 2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL a NULL NULL 512 1 SIMPLE t1 index NULL a NULL NULL 2
# This should use range, possible_keys={a,b} # This should use range, possible_keys={a,b}
explain delete from t1 where a<20 and b < 10; explain delete from t1 where a<20 and b < 10;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
...@@ -72,7 +72,7 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -72,7 +72,7 @@ id select_type table type possible_keys key key_len ref rows Extra
# This should use an index, possible_keys=NULL because there is no WHERE # This should use an index, possible_keys=NULL because there is no WHERE
explain update t1 set a=a+1 order by a limit 2; explain update t1 set a=a+1 order by a limit 2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 512 1 SIMPLE t1 ALL NULL NULL NULL NULL 512 Using filesort
# This should use range, possible_keys={a,b} # This should use range, possible_keys={a,b}
explain update t1 set filler='fooo' where a<20 and b < 10; explain update t1 set filler='fooo' where a<20 and b < 10;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
...@@ -80,11 +80,11 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -80,11 +80,11 @@ id select_type table type possible_keys key key_len ref rows Extra
# This should use ALL + filesort # This should use ALL + filesort
explain update t1 set filler='fooo' order by a+1 limit 2; explain update t1 set filler='fooo' order by a+1 limit 2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 512 1 SIMPLE t1 ALL NULL NULL NULL NULL 512 Using filesort
# This should use range + using filesort # This should use range + using filesort
explain update t1 set filler='fooo' where a<20 order by b limit 2; explain update t1 set filler='fooo' where a<20 order by b limit 2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where 1 SIMPLE t1 range a a 5 NULL 1 Using where; Using filesort
# Try some subqueries: # Try some subqueries:
explain update t1 set filler='fooo' where a < (select max(a) from t0); explain update t1 set filler='fooo' where a < (select max(a) from t0);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
......
...@@ -59,7 +59,7 @@ void Delete_plan::save_explain_data(Explain_query *query) ...@@ -59,7 +59,7 @@ void Delete_plan::save_explain_data(Explain_query *query)
{ {
explain->deleting_all_rows= true; explain->deleting_all_rows= true;
explain->select_type= "SIMPLE"; explain->select_type= "SIMPLE";
explain->rows= table_rows; explain->rows= scanned_rows;
} }
else else
{ {
...@@ -161,7 +161,7 @@ void Update_plan::save_explain_data_intern(Explain_query *query, ...@@ -161,7 +161,7 @@ void Update_plan::save_explain_data_intern(Explain_query *query,
} }
// key_len stays NULL // key_len stays NULL
} }
explain->rows= select ? select->records : table_rows; explain->rows= scanned_rows;
if (select && select->quick && if (select && select->quick &&
select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE) select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
...@@ -421,6 +421,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -421,6 +421,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK) if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_QUICK); (void) table->file->extra(HA_EXTRA_QUICK);
query_plan.scanned_rows= select? select->records: table->file->stats.records;
if (order) if (order)
{ {
table->update_const_key_parts(conds); table->update_const_key_parts(conds);
...@@ -432,14 +433,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -432,14 +433,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query_plan.index= MAX_KEY; query_plan.index= MAX_KEY;
} }
else else
{
ha_rows scanned_limit= query_plan.scanned_rows;
query_plan.index= get_index_for_order(order, table, select, limit, query_plan.index= get_index_for_order(order, table, select, limit,
&scanned_limit,
&query_plan.using_filesort, &query_plan.using_filesort,
&reverse); &reverse);
if (!query_plan.using_filesort)
query_plan.scanned_rows= scanned_limit;
}
} }
query_plan.select= select; query_plan.select= select;
query_plan.possible_keys= select? select->possible_keys: key_map(0); query_plan.possible_keys= select? select->possible_keys: key_map(0);
query_plan.table_rows= table->file->stats.records;
/* /*
Ok, we have generated a query plan for the DELETE. Ok, we have generated a query plan for the DELETE.
......
...@@ -2397,7 +2397,7 @@ public: ...@@ -2397,7 +2397,7 @@ public:
TABLE *table; TABLE *table;
SQL_SELECT *select; SQL_SELECT *select;
uint index; uint index;
ha_rows table_rows; /* Use if select==NULL */ ha_rows scanned_rows;
/* /*
Top-level select_lex. Most of its fields are not used, we need it only to Top-level select_lex. Most of its fields are not used, we need it only to
get to the subqueries. get to the subqueries.
...@@ -2440,7 +2440,7 @@ public: ...@@ -2440,7 +2440,7 @@ public:
void set_delete_all_rows(ha_rows rows_arg) void set_delete_all_rows(ha_rows rows_arg)
{ {
deleting_all_rows= true; deleting_all_rows= true;
table_rows= rows_arg; scanned_rows= rows_arg;
} }
void save_explain_data(Explain_query *query); void save_explain_data(Explain_query *query);
......
...@@ -24126,6 +24126,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, ...@@ -24126,6 +24126,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
@param table Table to find a key @param table Table to find a key
@param select Pointer to access/update select->quick (if any) @param select Pointer to access/update select->quick (if any)
@param limit LIMIT clause parameter @param limit LIMIT clause parameter
@param [out] scanned_limit How many records we expect to scan
Valid if *need_sort=FALSE.
@param [out] need_sort TRUE if filesort needed @param [out] need_sort TRUE if filesort needed
@param [out] reverse @param [out] reverse
TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY) TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY)
...@@ -24143,7 +24145,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, ...@@ -24143,7 +24145,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/ */
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select, uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
ha_rows limit, bool *need_sort, bool *reverse) ha_rows limit, ha_rows *scanned_limit,
bool *need_sort, bool *reverse)
{ {
if (!order) if (!order)
{ {
...@@ -24185,6 +24188,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select, ...@@ -24185,6 +24188,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
{ {
select->set_quick(reverse_quick); select->set_quick(reverse_quick);
*need_sort= FALSE; *need_sort= FALSE;
*scanned_limit= select->quick->records;
return select->quick->index; return select->quick->index;
} }
else else
...@@ -24213,6 +24217,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select, ...@@ -24213,6 +24217,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
!is_key_used(table, key, table->write_set)) !is_key_used(table, key, table->write_set))
{ {
*need_sort= FALSE; *need_sort= FALSE;
*scanned_limit= limit;
*reverse= (direction < 0); *reverse= (direction < 0);
return key; return key;
} }
......
...@@ -1833,7 +1833,8 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, ...@@ -1833,7 +1833,8 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 select_options); SELECT_LEX *select_lex, uint8 select_options);
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select, uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
ha_rows limit, bool *need_sort, bool *reverse); ha_rows limit, ha_rows *scanned_limit,
bool *need_sort, bool *reverse);
ORDER *simple_remove_const(ORDER *order, COND *where); ORDER *simple_remove_const(ORDER *order, COND *where);
bool const_expression_in_where(COND *cond, Item *comp_item, bool const_expression_in_where(COND *cond, Item *comp_item,
Field *comp_field= NULL, Field *comp_field= NULL,
......
...@@ -456,6 +456,7 @@ int mysql_update(THD *thd, ...@@ -456,6 +456,7 @@ int mysql_update(THD *thd,
table->update_const_key_parts(conds); table->update_const_key_parts(conds);
order= simple_remove_const(order, conds); order= simple_remove_const(order, conds);
query_plan.scanned_rows= select? select->records: table->file->stats.records;
if (select && select->quick && select->quick->unique_key_range()) if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered"): Ok to use with key field UPDATE { // Single row select (always "ordered"): Ok to use with key field UPDATE
...@@ -465,8 +466,12 @@ int mysql_update(THD *thd, ...@@ -465,8 +466,12 @@ int mysql_update(THD *thd,
} }
else else
{ {
ha_rows scanned_limit= query_plan.scanned_rows;
query_plan.index= get_index_for_order(order, table, select, limit, query_plan.index= get_index_for_order(order, table, select, limit,
&need_sort, &reverse); &scanned_limit, &need_sort, &reverse);
if (!need_sort)
query_plan.scanned_rows= scanned_limit;
if (select && select->quick) if (select && select->quick)
{ {
DBUG_ASSERT(need_sort || query_plan.index == select->quick->index); DBUG_ASSERT(need_sort || query_plan.index == select->quick->index);
...@@ -492,7 +497,6 @@ int mysql_update(THD *thd, ...@@ -492,7 +497,6 @@ int mysql_update(THD *thd,
- if we're running EXPLAIN UPDATE, get out - if we're running EXPLAIN UPDATE, get out
*/ */
query_plan.select= select; query_plan.select= select;
query_plan.table_rows= table->file->stats.records;
query_plan.possible_keys= select? select->possible_keys: key_map(0); query_plan.possible_keys= select? select->possible_keys: key_map(0);
if (used_key_is_modified || order || if (used_key_is_modified || order ||
...@@ -504,7 +508,6 @@ int mysql_update(THD *thd, ...@@ -504,7 +508,6 @@ int mysql_update(THD *thd,
query_plan.using_io_buffer= true; query_plan.using_io_buffer= true;
} }
query_plan.save_explain_data(thd->lex->explain);
/* /*
Ok, we have generated a query plan for the UPDATE. Ok, we have generated a query plan for the UPDATE.
...@@ -513,6 +516,8 @@ int mysql_update(THD *thd, ...@@ -513,6 +516,8 @@ int mysql_update(THD *thd,
*/ */
if (thd->lex->describe) if (thd->lex->describe)
goto exit_without_my_ok; goto exit_without_my_ok;
query_plan.save_explain_data(thd->lex->explain);
thd->apc_target.enable(); thd->apc_target.enable();
apc_target_enabled= true; apc_target_enabled= true;
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
...@@ -1041,6 +1046,7 @@ err: ...@@ -1041,6 +1046,7 @@ err:
exit_without_my_ok: exit_without_my_ok:
DBUG_ASSERT(!apc_target_enabled); DBUG_ASSERT(!apc_target_enabled);
query_plan.save_explain_data(thd->lex->explain);
int err2= thd->lex->explain->send_explain(thd); int err2= thd->lex->explain->send_explain(thd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment