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.
This commit is contained in:
parent
161d687594
commit
3c6ac6694d
@ -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
|
|||||||
# 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
|
|||||||
# 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)
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
}
|
}
|
||||||
// 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,
|
|||||||
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,
|
|||||||
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:
|
|||||||
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:
|
|||||||
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,
|
|||||||
@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,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
!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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
- 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,
|
|||||||
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,
|
|||||||
*/
|
*/
|
||||||
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:
|
|||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user