summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
authorSergey Petrunya <psergey@askmonty.org>2013-02-12 14:37:08 +0400
committerSergey Petrunya <psergey@askmonty.org>2013-02-12 14:37:08 +0400
commitd2995031d9214206689660069024525808c8a683 (patch)
tree2d08506cf8da9753867bdc81bdc22f03c3106683 /sql/sql_update.cc
parentb38b44cfe1c62b4c2211aec7c3f1407f14a2aa21 (diff)
downloadmariadb-git-d2995031d9214206689660069024525808c8a683.tar.gz
SHOW EXPLAIN for MariaDB
- Support [SHOW] EXPLAIN UPDATE (needs code cleanup).
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc134
1 files changed, 108 insertions, 26 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 84adfc2c3f5..11cb493da97 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -260,7 +260,7 @@ int mysql_update(THD *thd,
bool can_compare_record;
int res;
int error, loc_error;
- uint used_index, dup_key_found;
+ uint dup_key_found;
bool need_sort= TRUE;
bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -270,12 +270,15 @@ int mysql_update(THD *thd,
ha_rows updated, found;
key_map old_covering_keys;
TABLE *table;
- SQL_SELECT *select;
+ SQL_SELECT *select= NULL;
READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex;
ulonglong id;
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
+ Update_plan query_plan;
+ query_plan.index= MAX_KEY;
+ query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_update");
if (open_tables(thd, &table_list, &table_count, 0))
@@ -314,6 +317,8 @@ int mysql_update(THD *thd,
table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all();
+ query_plan.select_lex= &thd->lex->select_lex;
+ query_plan.table= table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Force privilege re-checking for views after they have been opened. */
want_privilege= (table_list->view ? UPDATE_ACL :
@@ -370,7 +375,12 @@ int mysql_update(THD *thd,
Item::cond_result cond_value;
conds= remove_eq_conds(thd, conds, &cond_value);
if (cond_value == Item::COND_FALSE)
+ {
limit= 0; // Impossible WHERE
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+ }
}
/*
@@ -400,6 +410,10 @@ int mysql_update(THD *thd,
if (error || !limit || thd->is_error() ||
(select && select->check_quick(thd, safe_update, limit)))
{
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
delete select;
free_underlaid_joins(thd, select_lex);
/*
@@ -438,16 +452,16 @@ int mysql_update(THD *thd,
if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered"): Ok to use with key field UPDATE
need_sort= FALSE;
- used_index= MAX_KEY;
+ query_plan.index= MAX_KEY;
used_key_is_modified= FALSE;
}
else
{
- used_index= get_index_for_order(order, table, select, limit,
- &need_sort, &reverse);
+ query_plan.index= get_index_for_order(order, table, select, limit,
+ &need_sort, &reverse);
if (select && select->quick)
{
- DBUG_ASSERT(need_sort || used_index == select->quick->index);
+ DBUG_ASSERT(need_sort || query_plan.index == select->quick->index);
used_key_is_modified= (!select->quick->unique_key_range() &&
select->quick->is_keys_used(table->write_set));
}
@@ -455,14 +469,37 @@ int mysql_update(THD *thd,
{
if (need_sort)
{ // Assign table scan index to check below for modified key fields:
- used_index= table->file->key_used_on_scan;
+ query_plan.index= table->file->key_used_on_scan;
}
- if (used_index != MAX_KEY)
+ if (query_plan.index != MAX_KEY)
{ // Check if we are modifying a key that we are used to search with:
- used_key_is_modified= is_key_used(table, used_index, table->write_set);
+ used_key_is_modified= is_key_used(table, query_plan.index, table->write_set);
}
}
}
+
+ /*
+ Query optimization is finished at this point.
+ - Save the decisions in the query plan
+ - if we're running EXPLAIN UPDATE, get out
+ */
+ query_plan.select= select;
+ query_plan.possible_keys= table->quick_keys;
+ query_plan.table_rows= table->file->stats.records;
+ thd->lex->upd_del_plan= &query_plan;
+
+ /*
+ Ok, we have generated a query plan for the UPDATE.
+ - if we're running EXPLAIN UPDATE, goto produce explain output
+ - otherwise, execute the query plan
+ */
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
+ thd->apc_target.enable();
+ DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
+ dbug_serve_apcs(thd, 1););
+
if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set))
@@ -476,8 +513,8 @@ int mysql_update(THD *thd,
DBUG_ASSERT(table->read_set == &table->def_read_set);
DBUG_ASSERT(table->write_set == &table->def_write_set);
- if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
- table->add_read_columns_used_by_index(used_index);
+ if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index))
+ table->add_read_columns_used_by_index(query_plan.index);
else
table->use_all_columns();
@@ -534,22 +571,22 @@ int mysql_update(THD *thd,
/*
When we get here, we have one of the following options:
- A. used_index == MAX_KEY
+ A. query_plan.index == MAX_KEY
This means we should use full table scan, and start it with
init_read_record call
- B. used_index != MAX_KEY
+ B. query_plan.index != MAX_KEY
B.1 quick select is used, start the scan with init_read_record
B.2 quick select is not used, this is full index scan (with LIMIT)
Full index scan must be started with init_read_record_idx
*/
- if (used_index == MAX_KEY || (select && select->quick))
+ if (query_plan.index == MAX_KEY || (select && select->quick))
{
if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
goto err;
}
else
- init_read_record_idx(&info, thd, table, 1, used_index, reverse);
+ init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse);
thd_proc_info(thd, "Searching rows for update");
ha_rows tmp_limit= limit;
@@ -610,6 +647,7 @@ int mysql_update(THD *thd,
select= new SQL_SELECT;
select->head=table;
}
+ //psergey-todo: disable SHOW EXPLAIN because the plan was deleted?
if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
error=1; /* purecov: inspected */
select->file=tempfile; // Read row ptrs from this file
@@ -884,6 +922,7 @@ int mysql_update(THD *thd,
if (!transactional_table && updated > 0)
thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->apc_target.disable(); //psergey-todo.
end_read_record(&info);
delete select;
thd_proc_info(thd, "end");
@@ -962,6 +1001,27 @@ err:
table->disable_keyread();
thd->abort_on_warning= 0;
DBUG_RETURN(1);
+
+exit_without_my_ok:
+ thd->lex->upd_del_plan= &query_plan;
+
+ select_send *result;
+ bool printed_anything;
+ if (!(result= new select_send()))
+ return 1; /* purecov: inspected */
+ List<Item> dummy; /* note: looked in 5.6 and they too use a dummy list like this */
+ result->prepare(dummy, &thd->lex->unit);
+ thd->send_explain_fields(result);
+ int err2= thd->lex->print_explain(result, 0 /* explain flags*/, &printed_anything);
+
+ if (err2)
+ result->abort_result_set();
+ else
+ result->send_eof();
+
+ delete select;
+ free_underlaid_joins(thd, select_lex);
+ DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
}
/*
@@ -1381,23 +1441,37 @@ bool mysql_multi_update(THD *thd,
multi_update **result)
{
bool res;
+ select_result *output;
+ bool explain= test(thd->lex->describe);
DBUG_ENTER("mysql_multi_update");
-
- if (!(*result= new multi_update(table_list,
- &thd->lex->select_lex.leaf_tables,
- fields, values,
- handle_duplicates, ignore)))
+
+ if (explain)
{
- DBUG_RETURN(TRUE);
+ /* Handle EXPLAIN UPDATE */
+ if (!(output= new select_send()) ||
+ thd->send_explain_fields(output))
+ {
+ delete output;
+ DBUG_RETURN(TRUE);
+ }
+ select_lex->set_explain_type(FALSE);
+ *result= NULL; /* no multi_update object */
+ }
+ else
+ {
+ if (!(*result= new multi_update(table_list,
+ &thd->lex->select_lex.leaf_tables,
+ fields, values,
+ handle_duplicates, ignore)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ output= *result;
}
thd->abort_on_warning= test(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES));
-/*
- psergey-explain-todo:
- This is the place where EXPLAIN <multi-table-update> should be handled.
-*/
List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
@@ -1407,12 +1481,20 @@ bool mysql_multi_update(THD *thd,
(ORDER *)NULL,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE,
- *result, unit, select_lex);
+ output, unit, select_lex);
DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error()));
res|= thd->is_error();
if (unlikely(res))
(*result)->abort_result_set();
+ else
+ {
+ if (explain)
+ {
+ output->send_eof();
+ delete output;
+ }
+ }
thd->abort_on_warning= 0;
DBUG_RETURN(res);
}