summaryrefslogtreecommitdiff
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
parentb38b44cfe1c62b4c2211aec7c3f1407f14a2aa21 (diff)
downloadmariadb-git-d2995031d9214206689660069024525808c8a683.tar.gz
SHOW EXPLAIN for MariaDB
- Support [SHOW] EXPLAIN UPDATE (needs code cleanup).
-rw-r--r--mysql-test/r/explain_non_select.result53
-rw-r--r--mysql-test/r/show_explain.result18
-rw-r--r--mysql-test/r/show_explain_non_select.result24
-rw-r--r--mysql-test/t/explain_non_select.test39
-rw-r--r--mysql-test/t/show_explain.test4
-rw-r--r--mysql-test/t/show_explain_non_select.test31
-rw-r--r--sql/sql_delete.cc26
-rw-r--r--sql/sql_lex.cc8
-rw-r--r--sql/sql_lex.h41
-rw-r--r--sql/sql_select.cc3
-rw-r--r--sql/sql_update.cc134
11 files changed, 318 insertions, 63 deletions
diff --git a/mysql-test/r/explain_non_select.result b/mysql-test/r/explain_non_select.result
index aa592f0b198..1c8e444c4b9 100644
--- a/mysql-test/r/explain_non_select.result
+++ b/mysql-test/r/explain_non_select.result
@@ -38,7 +38,7 @@ id select_type table type possible_keys key key_len ref rows Extra
explain delete from t1 where a < (select max(a) from t0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 range a a 5 NULL 1 Using where
-2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted
+2 SUBQUERY t0 ALL NULL NULL NULL NULL 8
explain delete from t1 where a < (select max(a) from t0 where a < t1.b);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where
@@ -51,3 +51,54 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
1 SIMPLE t1 ref a a 5 test.t0.a 4 Using index
drop table t0, t1;
+# ###################################################################
+# ## EXPLAIN UPDATE tests
+# ###################################################################
+create table t0 (a int) engine=myisam;
+insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
+explain update t0 set a=3 where a=4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
+create table t1 (a int, b int, filler char(100), key(a), key(b));
+insert into t1
+select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
+from t0 A, t0 B, t0 C;
+explain update t1 set a=a+1 where 3>4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible where
+explain update t1 set a=a+1 where a=3 and a=4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible 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;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 512
+# This should use range, possible_keys={a,b}
+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
+1 SIMPLE t1 range a,b a 5 NULL 1 Using where
+# This should use ALL + filesort
+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
+1 SIMPLE t1 ALL NULL NULL NULL NULL 512
+# This should use range + using filesort
+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
+1 SIMPLE t1 range a a 5 NULL 1 Using where
+# Try some subqueries:
+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
+1 PRIMARY t1 range a a 5 NULL 1 Using where
+2 SUBQUERY t0 ALL NULL NULL NULL NULL 8
+explain update t1 set filler='fooo' where a < (select max(a) from t0 where a < t1.b);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 512 Using where
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 8 Using where
+#
+# Tests for multi-table UPDATE
+#
+explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 8 Using where
+1 SIMPLE t1 ref a a 5 test.t0.a 4 Using index
+drop table t0, t1;
diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result
index 8baf4d40e7c..1152baaf835 100644
--- a/mysql-test/r/show_explain.result
+++ b/mysql-test/r/show_explain.result
@@ -186,9 +186,17 @@ set @show_explain_probe_select_id=2;
set debug_dbug='+d,show_explain_probe_join_exec_start';
update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
show explain for $thr2;
-ERROR HY000: Target is not running an EXPLAINable command
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
+Warnings:
+Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3
show explain for $thr2;
-ERROR HY000: Target is not running an EXPLAINable command
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
+Warnings:
+Note 1003 update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3
drop table t2;
set debug_dbug=@old_debug;
#
@@ -207,7 +215,7 @@ Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where
-2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3
drop table t2;
@@ -228,13 +236,13 @@ Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3
-2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL NULL NULL NULL NULL 3
-2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Query plan already deleted
+2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 Using where
Warnings:
Note 1003 select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2
a SUBQ
diff --git a/mysql-test/r/show_explain_non_select.result b/mysql-test/r/show_explain_non_select.result
index 5358981e6f2..0bd1e959405 100644
--- a/mysql-test/r/show_explain_non_select.result
+++ b/mysql-test/r/show_explain_non_select.result
@@ -1,19 +1,27 @@
drop table if exists t0, t1;
+SET @old_debug= @@session.debug;
+set debug_sync='RESET';
create table t0 (a int) engine=myisam;
insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
create table t1 (a int, b int, filler char(100), key(a), key(b));
insert into t1
select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
from t0 A, t0 B, t0 C;
-set debug_dbug='d,show_explain_probe_delete_exec_start';
+#
+# Test SHOW EXPLAIN for single-table DELETE
+#
+set debug_dbug='+d,show_explain_probe_delete_exec_start';
delete from t1 where a<10 and b+1>1000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 1 Using where
Warnings:
Note 1003 delete from t1 where a<10 and b+1>1000
+#
+# Test SHOW EXPLAIN for multi-table DELETE
+#
set @show_explain_probe_select_id=1;
-set debug_dbug='d,show_explain_probe_do_select';
+set debug_dbug='+d,show_explain_probe_do_select';
delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000;
show explain for $thr2;
id select_type table type possible_keys key key_len ref rows Extra
@@ -21,4 +29,16 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref a a 5 test.t0.a 4 Using where
Warnings:
Note 1003 delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000
+#
+# Test SHOW EXPLAIN for single-table UPDATE
+#
+set debug_dbug='+d,show_explain_probe_update_exec_start';
+update t1 set filler='filler-data-2' where a<10 and b+1>1000;
+show explain for $thr2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a a 5 NULL 1 Using where
+Warnings:
+Note 1003 update t1 set filler='filler-data-2' where a<10 and b+1>1000
drop table t0,t1;
+set debug_dbug=@old_debug;
+set debug_sync='RESET';
diff --git a/mysql-test/t/explain_non_select.test b/mysql-test/t/explain_non_select.test
index bc3c2bb6c16..66432a8e4a5 100644
--- a/mysql-test/t/explain_non_select.test
+++ b/mysql-test/t/explain_non_select.test
@@ -43,5 +43,44 @@ explain delete from t1 where a < (select max(a) from t0 where a < t1.b);
--echo # Tests for multi-table DELETE
--echo #
explain delete t1 from t0, t1 where t0.a = t1.a;
+drop table t0, t1;
+
+--echo # ###################################################################
+--echo # ## EXPLAIN UPDATE tests
+--echo # ###################################################################
+create table t0 (a int) engine=myisam;
+insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
+
+explain update t0 set a=3 where a=4;
+
+create table t1 (a int, b int, filler char(100), key(a), key(b));
+insert into t1
+select A.a+10*B.a + 10*C.a, A.a+10*B.a + 10*C.a, 'filler'
+from t0 A, t0 B, t0 C;
+
+explain update t1 set a=a+1 where 3>4;
+explain update t1 set a=a+1 where a=3 and a=4;
+
+--echo # 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;
+
+--echo # This should use range, possible_keys={a,b}
+explain update t1 set filler='fooo' where a<20 and b < 10;
+
+--echo # This should use ALL + filesort
+explain update t1 set filler='fooo' order by a+1 limit 2;
+
+--echo # This should use range + using filesort
+explain update t1 set filler='fooo' where a<20 order by b limit 2;
+
+--echo # 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 where a < t1.b);
+
+--echo #
+--echo # Tests for multi-table UPDATE
+--echo #
+explain update t0, t1 set t1.a=t1.a+1 where t0.a = t1.a;
+
drop table t0, t1;
diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test
index d87bf2f14d4..0694160b151 100644
--- a/mysql-test/t/show_explain.test
+++ b/mysql-test/t/show_explain.test
@@ -238,10 +238,10 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
connection default;
--source include/wait_condition.inc
---error ER_TARGET_NOT_EXPLAINABLE
+#--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2;
--source include/wait_condition.inc
---error ER_TARGET_NOT_EXPLAINABLE
+#--error ER_TARGET_NOT_EXPLAINABLE
evalp show explain for $thr2;
connection con1;
reap;
diff --git a/mysql-test/t/show_explain_non_select.test b/mysql-test/t/show_explain_non_select.test
index da788779626..f71affd8a6e 100644
--- a/mysql-test/t/show_explain_non_select.test
+++ b/mysql-test/t/show_explain_non_select.test
@@ -9,6 +9,9 @@
drop table if exists t0, t1;
--enable_warnings
+SET @old_debug= @@session.debug;
+set debug_sync='RESET';
+
#
# Setup two threads and their ids
#
@@ -31,11 +34,11 @@ from t0 A, t0 B, t0 C;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
-#
-# Test SHOW EXPLAIN for single-table DELETEs
-#
+--echo #
+--echo # Test SHOW EXPLAIN for single-table DELETE
+--echo #
connection con2;
-set debug_dbug='d,show_explain_probe_delete_exec_start';
+set debug_dbug='+d,show_explain_probe_delete_exec_start';
send delete from t1 where a<10 and b+1>1000;
connection default;
@@ -44,8 +47,11 @@ evalp show explain for $thr2;
connection con2;
reap;
+--echo #
+--echo # Test SHOW EXPLAIN for multi-table DELETE
+--echo #
set @show_explain_probe_select_id=1;
-set debug_dbug='d,show_explain_probe_do_select';
+set debug_dbug='+d,show_explain_probe_do_select';
send delete t1 from t1, t0 where t0.a=t1.a and t1.b +1 > 1000;
connection default;
--source include/wait_condition.inc
@@ -53,5 +59,20 @@ evalp show explain for $thr2;
connection con2;
reap;
+--echo #
+--echo # Test SHOW EXPLAIN for single-table UPDATE
+--echo #
+connection con2;
+set debug_dbug='+d,show_explain_probe_update_exec_start';
+send update t1 set filler='filler-data-2' where a<10 and b+1>1000;
+
+connection default;
+--source include/wait_condition.inc
+evalp show explain for $thr2;
+connection con2;
+reap;
drop table t0,t1;
+
+set debug_dbug=@old_debug;
+set debug_sync='RESET';
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index d7a612f3d56..df659871a64 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -54,9 +54,27 @@
int Delete_plan::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything)
{
- if (deleting_all_rows || impossible_where)
+ if (deleting_all_rows)
{
- const char *msg= deleting_all_rows? "Deleting all rows": "Impossible where";
+ const char *msg= "Deleting all rows";
+ if (print_explain_message_line(output, explain_flags, 1/*select number*/,
+ "SIMPLE", msg))
+ {
+ return 1;
+ }
+ *printed_anything= true;
+ return 0;
+ }
+ return Update_plan::print_explain(output, explain_flags, printed_anything);
+}
+
+
+int Update_plan::print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything)
+{
+ if (impossible_where)
+ {
+ const char *msg= "Impossible where";
if (print_explain_message_line(output, explain_flags, 1/*select number*/,
"SIMPLE", msg))
{
@@ -404,7 +422,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query_plan.select= select;
query_plan.possible_keys= table->quick_keys;
query_plan.table_rows= table->file->stats.records;
- thd->lex->delete_plan= &query_plan;
+ thd->lex->upd_del_plan= &query_plan;
/*
Ok, we have generated a query plan for the DELETE.
@@ -629,7 +647,7 @@ cleanup:
/* Special exits */
exit_without_my_ok:
- thd->lex->delete_plan= &query_plan;
+ thd->lex->upd_del_plan= &query_plan;
select_send *result;
bool printed_anything;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 74e4b3e1162..714ab373b17 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -448,7 +448,7 @@ void lex_start(THD *thd)
lex->thd= lex->unit.thd= thd;
- lex->delete_plan= NULL;
+ lex->upd_del_plan= NULL;
lex->context_stack.empty();
lex->unit.init_query();
lex->unit.init_select();
@@ -2558,7 +2558,7 @@ LEX::LEX()
INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
reset_query_tables_list(TRUE);
mi.init();
- delete_plan= NULL;
+ upd_del_plan= NULL;
}
@@ -4171,9 +4171,9 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything)
{
- if (delete_plan)
+ if (upd_del_plan)
{
- delete_plan->print_explain(output, explain_flags, printed_anything);
+ upd_del_plan->print_explain(output, explain_flags, printed_anything);
return 0;
}
int res= unit.print_explain(output, explain_flags, printed_anything);
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index af35ccb01b0..588ad172a3c 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2347,29 +2347,48 @@ protected:
class Delete_plan;
class SQL_SELECT;
-/* Query plan of a single-table DELETE */
-class Delete_plan
+/*
+ Query plan of a single-table UPDATE.
+ (This is actually a plan for single-table DELETE also)
+*/
+class Update_plan
{
- bool deleting_all_rows;
+protected:
bool impossible_where;
public:
-
TABLE *table;
SQL_SELECT *select;
uint index;
ha_rows table_rows; /* Use if select==NULL */
- bool using_filesort;
- key_map possible_keys;
-
/*
Top-level select_lex. Most of its fields are not used, we need it only to
get to the subqueries.
*/
SELECT_LEX *select_lex;
+
+ key_map possible_keys;
+ bool using_filesort;
+
+ /* Set this plan to be a plan to do nothing because of impossible WHRE*/
+ void set_impossible_where() { impossible_where= true; }
+
+ virtual int print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything);
+ virtual ~Update_plan() {}
+
+ Update_plan() : impossible_where(false), using_filesort(false) {}
+};
+
+
+/* Query plan of a single-table DELETE */
+class Delete_plan : public Update_plan
+{
+ bool deleting_all_rows;
+public:
/* Construction functions */
Delete_plan() :
- deleting_all_rows(false), impossible_where(false), using_filesort(false) {}
+ deleting_all_rows(false) {}
/* Set this query plan to be a plan to make a call to h->delete_all_rows() */
void set_delete_all_rows(ha_rows rows_arg)
@@ -2377,10 +2396,6 @@ public:
deleting_all_rows= true;
table_rows= rows_arg;
}
-
- /* Set this plan to be a plan to do nothing because of impossible WHRE*/
- void set_impossible_where() { impossible_where= true; }
-
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
};
@@ -2398,7 +2413,7 @@ struct LEX: public Query_tables_list
SELECT_LEX *all_selects_list;
/* For single-table DELETE: its query plan */
- Delete_plan *delete_plan;
+ Update_plan *upd_del_plan;
char *length,*dec,*change;
LEX_STRING name;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 230355c550e..d9177b9240e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -10756,7 +10756,8 @@ void JOIN::cleanup(bool full)
DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full));
- have_query_plan= QEP_DELETED;
+ if (full)
+ have_query_plan= QEP_DELETED; //psergey: this is a problem!
if (table)
{
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);
}