Commit 648b957f authored by Sergei Petrunia's avatar Sergei Petrunia

Merge branch 'bb-10.1-explain-analyze' into 10.1

parents 0bf9fd89 68bf3c50
drop table if exists t0,t1,t2,t3;
create table t0 (a int) engine=myisam;
INSERT INTO t0 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int) engine=myisam;
INSERT INTO t1 select * from t0;
# Try a few basic selects to see that r_rows and r_filtered columns work
analyze select * from t1;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 100.00
analyze select * from t1 where a<5;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
analyze select * from t1 where a>100;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 0.00 Using where
# ANALYZE DELETE will delete rows:
analyze delete from t1 where a in (2,3,4);
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 NULL 100.00 30.00 Using where
select * from t1;
a
0
1
5
6
7
8
9
drop table t1;
# ANALYZE UPDATE will make updates:
create table t1(a int, b int);
insert into t1 select a,a from t0;
analyze update t1 set b=100+b where a in (6,7,8);
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 NULL 100.00 30.00 Using where
select * from t1;
a b
0 0
1 1
2 2
3 3
4 4
5 5
6 106
7 107
8 108
9 9
drop table t1;
# Check that UNION works
create table t1(a int, b int);
insert into t1 select a,a from t0;
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (5,6));
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
2 UNION B ALL NULL NULL NULL NULL 10 10 100.00 20.00 Using where
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL 7 NULL NULL
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (1,2));
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY A ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
2 UNION B ALL NULL NULL NULL NULL 10 10 100.00 20.00 Using where
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL 5 NULL NULL
drop table t1;
drop table t0;
#
# Try a subquery.
#
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t1 (a int, b int);
insert into t1 values (1,1),(2,2),(3,3);
# See .test file for the right values of r_rows and r_filtered.
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3 100.00 100.00
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 3 100.00 33.33 Using where
# Try a subquery that is never executed
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1 where t1.a > 5;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3 100.00 0.00 Using where
2 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 10 NULL 100.00 NULL Using where
drop table t0, t1;
#
# Tests for join buffering
#
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t1 like t0;
insert into t1 select * from t0;
explain select * from t0, t1 where t0.a<5 and t1.a<5;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
# These should have filtered=50
analyze select * from t0, t1 where t0.a<5 and t1.a<5;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where; Using join buffer (flat, BNL join)
explain select * from t0, t1 where t0.a<5 and t1.b=t0.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
# Now, t1 should have filtered=10
analyze select * from t0, t1 where t0.a<5 and t1.b=t0.b;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 10.00 Using where; Using join buffer (flat, BNL join)
explain select * from t0, t1 where t0.a<5 and t1.a<5 and t1.b=t0.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
# Now, t1 should have filtered=10
analyze select * from t0, t1 where t0.a<5 and t1.a<5 and t1.b=t0.b;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 10 100.00 50.00 Using where
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 10.00 Using where; Using join buffer (flat, BNL join)
# TODO: Check what is counted for "range checked for each record".
#
# Test for joins
#
create table t2 (key1 int, key2x int, col1 int, key(key1), key(key2x));
insert into t2 select A.a + 10 *B.a +100 * C.a,
(A.a + 10 *B.a +100 * C.a)*2,
A.a + 10 *B.a +100 * C.a
from t0 A, t0 B, t0 C;
# This always has matches, filtered=100%.
analyze select * from t1,t2 where t2.key1=t1.a;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 100.00 Using where
1 SIMPLE t2 ref key1 key1 5 test.t1.a 1 1 100.00 100.00
# This shows r_rows=0. It is actually 0.5 (should r_rows be changed to double?)
analyze select * from t1,t2 where t2.key2x=t1.a;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 100.00 Using where
1 SIMPLE t2 ref key2x key2x 5 test.t1.a 1 0 100.00 100.00
select * from t1,t2 where t2.key2x=t1.a;
a b key1 key2x col1
0 0 0 0 0
2 2 1 2 1
4 4 2 4 2
6 6 3 6 3
8 8 4 8 4
# This has t2.filtered=40% (there are 5 values: {0,1,2,3,4}. two of them have mod=0)
analyze select * from t1,t2 where t2.key2x=t1.a and mod(t2.col1,4)=0;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 10 10 100.00 100.00 Using where
1 SIMPLE t2 ref key2x key2x 5 test.t1.a 1 0 100.00 40.00 Using where
drop table t0,t1,t2;
#
# Check non-merged derived tables
#
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
update t0 set b=b/3;
analyze select * from (select count(*),max(a),b from t0 group by b) T;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 10 4 100.00 100.00
2 DERIVED t0 ALL NULL NULL NULL NULL 10 10 100.00 100.00 Using temporary; Using filesort
drop table t0;
#
# Check ORDER/GROUP BY
#
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
analyze select count(*),max(a),b from t0 where a<7 group by b;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 10 10 100.00 70.00 Using where; Using temporary; Using filesort
drop table t0;
#
# Check multi-table UPDATE/DELETE.
#
create table t0 (a int, b int);
create table t1 (a int, b int);
insert into t0 values (0,0),(2,2),(4,4), (8,8);
insert into t1 values (0,0),(2,2), (6,6);
analyze select * from t0,t1 where t0.a=t1.a;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 3 100.00 100.00
1 SIMPLE t0 ALL NULL NULL NULL NULL 4 4 100.00 16.67 Using where; Using join buffer (flat, BNL join)
analyze update t0,t1 set t1.b=5555 where t0.a=t1.a;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 3 100.00 100.00
1 SIMPLE t0 ALL NULL NULL NULL NULL 4 4 100.00 16.67 Using where
select * from t1;
a b
0 5555
2 5555
6 6
analyze delete t1 from t1, t0 where t0.a=t1.a;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 3 100.00 100.00
1 SIMPLE t0 ALL NULL NULL NULL NULL 4 4 100.00 16.67 Using where
select * from t1;
a b
6 6
drop table t0, t1;
call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.'); call mtr.add_suppression('Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT.');
drop table if exists t1,t2,t3,t4; drop table if exists t1,t2,t3,t4;
drop database if exists mysqltest1;
drop database if exists client_test_db; drop database if exists client_test_db;
create table t1 create table t1
( (
......
...@@ -641,7 +641,7 @@ set debug_dbug='+d,show_explain_probe_join_exec_start'; ...@@ -641,7 +641,7 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
SHOW INDEX FROM t1; SHOW INDEX FROM t1;
show explain for $thr2; show explain for $thr2;
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 STATISTICS ALL NULL NULL NULL NULL NULL Skip_open_table; Scanned all databases 1 SIMPLE STATISTICS ALL NULL TABLE_SCHEMA,TABLE_NAME NULL NULL NULL Open_full_table; Scanned 0 databases
Warnings: Warnings:
Note 1003 SHOW INDEX FROM t1 Note 1003 SHOW INDEX FROM t1
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
......
#
# Tests for "ANALYZE $statement" feature (PostgreSQL's analog is called EXPLAIN ANALYZE)
#
--disable_warnings
drop table if exists t0,t1,t2,t3;
--enable_warnings
create table t0 (a int) engine=myisam;
INSERT INTO t0 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int) engine=myisam;
INSERT INTO t1 select * from t0;
--echo # Try a few basic selects to see that r_rows and r_filtered columns work
analyze select * from t1;
analyze select * from t1 where a<5;
analyze select * from t1 where a>100;
--echo # ANALYZE DELETE will delete rows:
analyze delete from t1 where a in (2,3,4);
select * from t1;
drop table t1;
--echo # ANALYZE UPDATE will make updates:
create table t1(a int, b int);
insert into t1 select a,a from t0;
analyze update t1 set b=100+b where a in (6,7,8);
select * from t1;
drop table t1;
--echo # Check that UNION works
create table t1(a int, b int);
insert into t1 select a,a from t0;
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (5,6));
analyze (select * from t1 A where a<5) union (select * from t1 B where a in (1,2));
drop table t1;
drop table t0;
--echo #
--echo # Try a subquery.
--echo #
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t1 (a int, b int);
insert into t1 values (1,1),(2,2),(3,3);
#
# t1 t0
# a=1 (0,1) 2 rows
# a=2 (0,1,2) 3 rows
# a=3 (0,1,2,3) 4 rows
#
# TOTAL TOTAL= 9 rows. 3 executions, avg=3 rows.
# WHERE is satisfied for 1 row per query, which gives filtered=33.3
--echo # See .test file for the right values of r_rows and r_filtered.
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1;
--echo # Try a subquery that is never executed
analyze select a, a in (select t0.b from t0 where t0.b+1=t1.b+1) from t1 where t1.a > 5;
drop table t0, t1;
--echo #
--echo # Tests for join buffering
--echo #
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
create table t1 like t0;
insert into t1 select * from t0;
explain select * from t0, t1 where t0.a<5 and t1.a<5;
--echo # These should have filtered=50
analyze select * from t0, t1 where t0.a<5 and t1.a<5;
explain select * from t0, t1 where t0.a<5 and t1.b=t0.b;
--echo # Now, t1 should have filtered=10
analyze select * from t0, t1 where t0.a<5 and t1.b=t0.b;
explain select * from t0, t1 where t0.a<5 and t1.a<5 and t1.b=t0.b;
--echo # Now, t1 should have filtered=10
analyze select * from t0, t1 where t0.a<5 and t1.a<5 and t1.b=t0.b;
--echo # TODO: Check what is counted for "range checked for each record".
--echo #
--echo # Test for joins
--echo #
create table t2 (key1 int, key2x int, col1 int, key(key1), key(key2x));
insert into t2 select A.a + 10 *B.a +100 * C.a,
(A.a + 10 *B.a +100 * C.a)*2,
A.a + 10 *B.a +100 * C.a
from t0 A, t0 B, t0 C;
--echo # This always has matches, filtered=100%.
analyze select * from t1,t2 where t2.key1=t1.a;
--echo # This shows r_rows=0. It is actually 0.5 (should r_rows be changed to double?)
analyze select * from t1,t2 where t2.key2x=t1.a;
select * from t1,t2 where t2.key2x=t1.a;
--echo # This has t2.filtered=40% (there are 5 values: {0,1,2,3,4}. two of them have mod=0)
analyze select * from t1,t2 where t2.key2x=t1.a and mod(t2.col1,4)=0;
drop table t0,t1,t2;
--echo #
--echo # Check non-merged derived tables
--echo #
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
update t0 set b=b/3;
analyze select * from (select count(*),max(a),b from t0 group by b) T;
drop table t0;
--echo #
--echo # Check ORDER/GROUP BY
--echo #
create table t0 (a int, b int);
insert into t0 values
(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
analyze select count(*),max(a),b from t0 where a<7 group by b;
drop table t0;
--echo #
--echo # Check multi-table UPDATE/DELETE.
--echo #
create table t0 (a int, b int);
create table t1 (a int, b int);
insert into t0 values (0,0),(2,2),(4,4), (8,8);
insert into t1 values (0,0),(2,2), (6,6);
analyze select * from t0,t1 where t0.a=t1.a;
analyze update t0,t1 set t1.b=5555 where t0.a=t1.a;
select * from t1;
analyze delete t1 from t1, t0 where t0.a=t1.a;
select * from t1;
drop table t0, t1;
...@@ -7,6 +7,7 @@ call mtr.add_suppression('Unsafe statement written to the binary log using state ...@@ -7,6 +7,7 @@ call mtr.add_suppression('Unsafe statement written to the binary log using state
--disable_warnings --disable_warnings
drop table if exists t1,t2,t3,t4; drop table if exists t1,t2,t3,t4;
drop database if exists mysqltest1;
# Avoid wrong warnings if mysql_client_test fails # Avoid wrong warnings if mysql_client_test fails
drop database if exists client_test_db; drop database if exists client_test_db;
--enable_warnings --enable_warnings
......
...@@ -210,6 +210,42 @@ class Protocol_binary :public Protocol ...@@ -210,6 +210,42 @@ class Protocol_binary :public Protocol
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; }; virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
}; };
/*
A helper for "ANALYZE $stmt" which looks a real network procotol but doesn't
write results to the network.
At first glance, class select_send looks like a more appropriate place to
implement the "write nothing" hook. This is not true, because
- we need to evaluate the value of every item, and do it the way
select_send does it (i.e. call item->val_int() or val_real() or...)
- select_send::send_data() has some other code, like telling the storage
engine that the row can be unlocked. We want to keep that also.
as a result, "ANALYZE $stmt" uses a select_send_analyze which still uses
select_send::send_data() & co., and also uses Protocol_discard object.
*/
class Protocol_discard : public Protocol_text
{
public:
Protocol_discard(THD *thd_arg) : Protocol_text(thd_arg) {}
/* The real writing is done only in write() */
virtual bool write() { return 0; }
virtual bool send_result_set_metadata(List<Item> *list, uint flags)
{
// Don't pas Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF flags
return Protocol_text::send_result_set_metadata(list, 0);
}
// send_error is intentionally not overloaded.
virtual bool send_eof(uint server_status, uint statement_warn_count)
{
return 0;
}
};
void send_warning(THD *thd, uint sql_errno, const char *err=0); void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno, const char *err, bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate); const char* sqlstate);
......
...@@ -2282,6 +2282,9 @@ int THD::send_explain_fields(select_result *result) ...@@ -2282,6 +2282,9 @@ int THD::send_explain_fields(select_result *result)
/* /*
Populate the provided field_list with EXPLAIN output columns. Populate the provided field_list with EXPLAIN output columns.
this->lex->describe has the EXPLAIN flags this->lex->describe has the EXPLAIN flags
The set/order of columns must be kept in sync with
Explain_query::print_explain and co.
*/ */
void THD::make_explain_field_list(List<Item> &field_list) void THD::make_explain_field_list(List<Item> &field_list)
...@@ -2317,11 +2320,25 @@ void THD::make_explain_field_list(List<Item> &field_list) ...@@ -2317,11 +2320,25 @@ void THD::make_explain_field_list(List<Item> &field_list)
item->maybe_null=1; item->maybe_null=1;
field_list.push_back(item= new Item_return_int("rows", 10, field_list.push_back(item= new Item_return_int("rows", 10,
MYSQL_TYPE_LONGLONG)); MYSQL_TYPE_LONGLONG));
if (lex->describe & DESCRIBE_EXTENDED) if (lex->analyze_stmt)
{
field_list.push_back(item= new Item_return_int("r_rows", 10,
MYSQL_TYPE_LONGLONG));
item->maybe_null=1;
}
if (lex->analyze_stmt || lex->describe & DESCRIBE_EXTENDED)
{ {
field_list.push_back(item= new Item_float("filtered", 0.1234, 2, 4)); field_list.push_back(item= new Item_float("filtered", 0.1234, 2, 4));
item->maybe_null=1; item->maybe_null=1;
} }
if (lex->analyze_stmt)
{
field_list.push_back(item= new Item_float("r_filtered", 0.1234, 2, 4));
item->maybe_null=1;
}
item->maybe_null= 1; item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs)); field_list.push_back(new Item_empty_string("Extra", 255, cs));
} }
......
...@@ -3961,6 +3961,20 @@ class select_send :public select_result { ...@@ -3961,6 +3961,20 @@ class select_send :public select_result {
}; };
/*
We need this class, because select_send::send_eof() will call ::my_eof.
See also class Protocol_discard.
*/
class select_send_analyze : public select_send
{
bool send_result_set_metadata(List<Item> &list, uint flags) { return 0; }
bool send_eof() { return 0; }
void abort_result_set() {}
};
class select_to_file :public select_result_interceptor { class select_to_file :public select_result_interceptor {
protected: protected:
sql_exchange *exchange; sql_exchange *exchange;
......
...@@ -223,6 +223,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -223,6 +223,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
killed_state killed_status= NOT_KILLED; killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
bool with_select= !select_lex->item_list.is_empty(); bool with_select= !select_lex->item_list.is_empty();
Explain_delete *explain;
Delete_plan query_plan(thd->mem_root); Delete_plan query_plan(thd->mem_root);
query_plan.index= MAX_KEY; query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE; query_plan.using_filesort= FALSE;
...@@ -538,9 +539,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -538,9 +539,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
goto cleanup; goto cleanup;
} }
explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan();
while (!(error=info.read_record(&info)) && !thd->killed && while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error()) ! thd->is_error())
{ {
explain->on_record_read();
if (table->vfield) if (table->vfield)
update_virtual_fields(thd, table, update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL : table->triggers ? VCOL_UPDATE_ALL :
...@@ -549,6 +552,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -549,6 +552,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
// thd->is_error() is tested to disallow delete row on error // thd->is_error() is tested to disallow delete row on error
if (!select || select->skip_record(thd) > 0) if (!select || select->skip_record(thd) > 0)
{ {
explain->on_record_after_where();
if (table->triggers && if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE, table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE)) TRG_ACTION_BEFORE, FALSE))
...@@ -666,6 +670,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -666,6 +670,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
} }
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table); DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
if (thd->lex->analyze_stmt)
{
error= thd->lex->explain->send_explain(thd);
}
else
if (error < 0 || if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error)) (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{ {
...@@ -1283,7 +1292,7 @@ bool multi_delete::send_eof() ...@@ -1283,7 +1292,7 @@ bool multi_delete::send_eof()
if (local_error != 0) if (local_error != 0)
error_handled= TRUE; // to force early leave from ::abort_result_set() error_handled= TRUE; // to force early leave from ::abort_result_set()
if (!local_error) if (!local_error && !thd->lex->analyze_stmt)
{ {
::my_ok(thd, deleted); ::my_ok(thd, deleted);
} }
......
This diff is collapsed.
...@@ -14,6 +14,38 @@ ...@@ -14,6 +14,38 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Data structures for ANALYZE */
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_table_cond; /* Rows after applying the table condition */
ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
bool has_scans() { return (r_scans != 0); }
ha_rows get_avg_rows()
{
return r_scans ? (ha_rows)rint((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;
}
};
/************************************************************************************** /**************************************************************************************
...@@ -60,10 +92,10 @@ class Explain_node : public Sql_alloc ...@@ -60,10 +92,10 @@ class Explain_node : public Sql_alloc
} }
virtual int print_explain(Explain_query *query, select_result_sink *output, virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags)=0; uint8 explain_flags, bool is_analyze)=0;
int print_explain_for_children(Explain_query *query, select_result_sink *output, int print_explain_for_children(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
virtual ~Explain_node(){} virtual ~Explain_node(){}
}; };
...@@ -110,6 +142,12 @@ class Explain_select : public Explain_node ...@@ -110,6 +142,12 @@ class Explain_select : public Explain_node
return false; return false;
} }
/*
This is used to save the results of "late" test_if_skip_sort_order() calls
that are made from JOIN::exec
*/
void replace_table(uint idx, Explain_table_access *new_tab);
public: public:
int select_id; int select_id;
const char *select_type; const char *select_type;
...@@ -134,7 +172,14 @@ class Explain_select : public Explain_node ...@@ -134,7 +172,14 @@ class Explain_select : public Explain_node
bool using_filesort; bool using_filesort;
int print_explain(Explain_query *query, select_result_sink *output, int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
Table_access_tracker *get_using_temporary_read_tracker()
{
return &using_temporary_read_tracker;
}
private:
Table_access_tracker using_temporary_read_tracker;
}; };
...@@ -172,10 +217,23 @@ class Explain_union : public Explain_node ...@@ -172,10 +217,23 @@ class Explain_union : public Explain_node
union_members.append(select_no); union_members.append(select_no);
} }
int print_explain(Explain_query *query, select_result_sink *output, int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
const char *fake_select_type; const char *fake_select_type;
bool using_filesort; bool using_filesort;
Table_access_tracker *get_fake_select_lex_tracker()
{
return &fake_select_lex_tracker;
}
Table_access_tracker *get_tmptable_read_tracker()
{
return &tmptable_read_tracker;
}
private:
Table_access_tracker fake_select_lex_tracker;
/* This one is for reading after ORDER BY */
Table_access_tracker tmptable_read_tracker;
}; };
...@@ -183,6 +241,7 @@ class Explain_update; ...@@ -183,6 +241,7 @@ class Explain_update;
class Explain_delete; class Explain_delete;
class Explain_insert; class Explain_insert;
/* /*
Explain structure for a query (i.e. a statement). Explain structure for a query (i.e. a statement).
...@@ -238,13 +297,14 @@ class Explain_query : public Sql_alloc ...@@ -238,13 +297,14 @@ class Explain_query : public Sql_alloc
Explain_union *get_union(uint select_id); Explain_union *get_union(uint select_id);
/* Produce a tabular EXPLAIN output */ /* Produce a tabular EXPLAIN output */
int print_explain(select_result_sink *output, uint8 explain_flags); int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze);
/* Send tabular EXPLAIN to the client */ /* Send tabular EXPLAIN to the client */
int send_explain(THD *thd); int send_explain(THD *thd);
/* Return tabular EXPLAIN output as a text string */ /* Return tabular EXPLAIN output as a text string */
bool print_explain_str(THD *thd, String *out_str); bool print_explain_str(THD *thd, String *out_str, bool is_analyze);
/* If true, at least part of EXPLAIN can be printed */ /* If true, at least part of EXPLAIN can be printed */
bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
...@@ -252,6 +312,8 @@ class Explain_query : public Sql_alloc ...@@ -252,6 +312,8 @@ class Explain_query : public Sql_alloc
void query_plan_ready(); void query_plan_ready();
MEM_ROOT *mem_root; MEM_ROOT *mem_root;
Explain_update *get_upd_del_plan() { return upd_del_plan; }
private: private:
/* Explain_delete inherits from Explain_update */ /* Explain_delete inherits from Explain_update */
Explain_update *upd_del_plan; Explain_update *upd_del_plan;
...@@ -320,13 +382,17 @@ enum explain_extra_tag ...@@ -320,13 +382,17 @@ enum explain_extra_tag
}; };
typedef struct st_explain_bka_type class EXPLAIN_BKA_TYPE
{ {
public:
EXPLAIN_BKA_TYPE() : join_alg(NULL) {}
bool incremental; bool incremental;
const char *join_alg; const char *join_alg;
StringBuffer<64> mrr_type; StringBuffer<64> mrr_type;
} EXPLAIN_BKA_TYPE; bool is_using_jbuf() { return (join_alg != NULL); }
};
/* /*
...@@ -386,6 +452,7 @@ class Explain_quick_select : public Sql_alloc ...@@ -386,6 +452,7 @@ class Explain_quick_select : public Sql_alloc
/* /*
EXPLAIN data structure for a single JOIN_TAB. EXPLAIN data structure for a single JOIN_TAB.
*/ */
class Explain_table_access : public Sql_alloc class Explain_table_access : public Sql_alloc
{ {
public: public:
...@@ -459,8 +526,14 @@ class Explain_table_access : public Sql_alloc ...@@ -459,8 +526,14 @@ class Explain_table_access : public Sql_alloc
StringBuffer<32> firstmatch_table_name; StringBuffer<32> firstmatch_table_name;
int print_explain(select_result_sink *output, uint8 explain_flags, int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze,
uint select_id, const char *select_type, uint select_id, const char *select_type,
bool using_temporary, bool using_filesort); bool using_temporary, bool using_filesort);
/* ANALYZE members*/
Table_access_tracker tracker;
Table_access_tracker jbuf_tracker;
private: private:
void append_tag_name(String *str, enum explain_extra_tag tag); void append_tag_name(String *str, enum explain_extra_tag tag);
}; };
...@@ -502,8 +575,17 @@ class Explain_update : public Explain_node ...@@ -502,8 +575,17 @@ class Explain_update : public Explain_node
bool using_filesort; bool using_filesort;
bool using_io_buffer; bool using_io_buffer;
/* ANALYZE members and methods */
ha_rows r_rows;
ha_rows r_rows_after_where;
inline void on_record_read() { r_rows++; }
inline void on_record_after_where() { r_rows_after_where++; }
Explain_update() :
r_rows(0), r_rows_after_where(0)
{}
virtual int print_explain(Explain_query *query, select_result_sink *output, virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
}; };
...@@ -523,7 +605,7 @@ class Explain_insert : public Explain_node ...@@ -523,7 +605,7 @@ class Explain_insert : public Explain_node
int get_select_id() { return 1; /* always root */ } int get_select_id() { return 1; /* always root */ }
int print_explain(Explain_query *query, select_result_sink *output, int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
}; };
...@@ -544,7 +626,7 @@ class Explain_delete: public Explain_update ...@@ -544,7 +626,7 @@ class Explain_delete: public Explain_update
virtual int get_select_id() { return 1; /* always root */ } virtual int get_select_id() { return 1; /* always root */ }
virtual int print_explain(Explain_query *query, select_result_sink *output, virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags); uint8 explain_flags, bool is_analyze);
}; };
...@@ -2277,11 +2277,13 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) ...@@ -2277,11 +2277,13 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
/* Prepare to read matching candidates from the join buffer */ /* Prepare to read matching candidates from the join buffer */
if (prepare_look_for_matches(skip_last)) if (prepare_look_for_matches(skip_last))
continue; continue;
join_tab->jbuf_tracker->r_scans++;
uchar *rec_ptr; uchar *rec_ptr;
/* Read each possible candidate from the buffer and look for matches */ /* Read each possible candidate from the buffer and look for matches */
while ((rec_ptr= get_next_candidate_for_match())) while ((rec_ptr= get_next_candidate_for_match()))
{ {
join_tab->jbuf_tracker->r_rows++;
/* /*
If only the first match is needed, and, it has been already found for If only the first match is needed, and, it has been already found for
the next record read from the join buffer, then the record is skipped. the next record read from the join buffer, then the record is skipped.
...@@ -2452,6 +2454,8 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) ...@@ -2452,6 +2454,8 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr)
if (join_tab->select && join_tab->select->skip_record(join->thd) <= 0) if (join_tab->select && join_tab->select->skip_record(join->thd) <= 0)
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
join_tab->jbuf_tracker->r_rows_after_where++;
if (!join_tab->is_last_inner_table()) if (!join_tab->is_last_inner_table())
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -2574,7 +2578,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) ...@@ -2574,7 +2578,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
none none
*/ */
void JOIN_CACHE::save_explain_data(struct st_explain_bka_type *explain) void JOIN_CACHE::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{ {
explain->incremental= MY_TEST(prev_cache); explain->incremental= MY_TEST(prev_cache);
...@@ -2619,14 +2623,14 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file) ...@@ -2619,14 +2623,14 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file)
} }
} }
void JOIN_CACHE_BKA::save_explain_data(struct st_explain_bka_type *explain) void JOIN_CACHE_BKA::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{ {
JOIN_CACHE::save_explain_data(explain); JOIN_CACHE::save_explain_data(explain);
add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
} }
void JOIN_CACHE_BKAH::save_explain_data(struct st_explain_bka_type *explain) void JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain)
{ {
JOIN_CACHE::save_explain_data(explain); JOIN_CACHE::save_explain_data(explain);
add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file); add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
...@@ -3333,6 +3337,7 @@ int JOIN_TAB_SCAN::open() ...@@ -3333,6 +3337,7 @@ int JOIN_TAB_SCAN::open()
{ {
save_or_restore_used_tabs(join_tab, FALSE); save_or_restore_used_tabs(join_tab, FALSE);
is_first_record= TRUE; is_first_record= TRUE;
join_tab->tracker->r_scans++;
return join_init_read_record(join_tab); return join_init_read_record(join_tab);
} }
...@@ -3371,8 +3376,14 @@ int JOIN_TAB_SCAN::next() ...@@ -3371,8 +3376,14 @@ int JOIN_TAB_SCAN::next()
is_first_record= FALSE; is_first_record= FALSE;
else else
err= info->read_record(info); err= info->read_record(info);
if (!err && table->vfield)
if (!err)
{
join_tab->tracker->r_rows++;
if (table->vfield)
update_virtual_fields(thd, table); update_virtual_fields(thd, table);
}
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0) while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
{ {
if (thd->check_killed() || skip_rc < 0) if (thd->check_killed() || skip_rc < 0)
...@@ -3382,9 +3393,16 @@ int JOIN_TAB_SCAN::next() ...@@ -3382,9 +3393,16 @@ int JOIN_TAB_SCAN::next()
meet the condition pushed to the table join_tab. meet the condition pushed to the table join_tab.
*/ */
err= info->read_record(info); err= info->read_record(info);
if (!err && table->vfield) if (!err)
{
join_tab->tracker->r_rows++;
if (table->vfield)
update_virtual_fields(thd, table); update_virtual_fields(thd, table);
} }
}
if (!err)
join_tab->tracker->r_rows_after_where++;
return err; return err;
} }
......
...@@ -63,7 +63,7 @@ typedef struct st_cache_field { ...@@ -63,7 +63,7 @@ typedef struct st_cache_field {
class JOIN_TAB_SCAN; class JOIN_TAB_SCAN;
struct st_explain_bka_type; class EXPLAIN_BKA_TYPE;
/* /*
JOIN_CACHE is the base class to support the implementations of JOIN_CACHE is the base class to support the implementations of
...@@ -662,7 +662,7 @@ class JOIN_CACHE :public Sql_alloc ...@@ -662,7 +662,7 @@ class JOIN_CACHE :public Sql_alloc
enum_nested_loop_state join_records(bool skip_last); enum_nested_loop_state join_records(bool skip_last);
/* Add a comment on the join algorithm employed by the join cache */ /* Add a comment on the join algorithm employed by the join cache */
virtual void save_explain_data(struct st_explain_bka_type *explain); virtual void save_explain_data(EXPLAIN_BKA_TYPE *explain);
THD *thd(); THD *thd();
...@@ -1340,7 +1340,7 @@ class JOIN_CACHE_BKA :public JOIN_CACHE ...@@ -1340,7 +1340,7 @@ class JOIN_CACHE_BKA :public JOIN_CACHE
/* Check index condition of the joined table for a record from BKA cache */ /* Check index condition of the joined table for a record from BKA cache */
bool skip_index_tuple(range_id_t range_info); bool skip_index_tuple(range_id_t range_info);
void save_explain_data(struct st_explain_bka_type *explain); void save_explain_data(EXPLAIN_BKA_TYPE *explain);
}; };
...@@ -1431,5 +1431,5 @@ class JOIN_CACHE_BKAH :public JOIN_CACHE_BNLH ...@@ -1431,5 +1431,5 @@ class JOIN_CACHE_BKAH :public JOIN_CACHE_BNLH
/* Check index condition of the joined table for a record from BKAH cache */ /* Check index condition of the joined table for a record from BKAH cache */
bool skip_index_tuple(range_id_t range_info); bool skip_index_tuple(range_id_t range_info);
void save_explain_data(struct st_explain_bka_type *explain); void save_explain_data(EXPLAIN_BKA_TYPE *explain);
}; };
...@@ -483,6 +483,7 @@ void lex_start(THD *thd) ...@@ -483,6 +483,7 @@ void lex_start(THD *thd)
if (lex->select_lex.group_list_ptrs) if (lex->select_lex.group_list_ptrs)
lex->select_lex.group_list_ptrs->clear(); lex->select_lex.group_list_ptrs->clear();
lex->describe= 0; lex->describe= 0;
lex->analyze_stmt= 0;
lex->subqueries= FALSE; lex->subqueries= FALSE;
lex->context_analysis_only= 0; lex->context_analysis_only= 0;
lex->derived_tables= 0; lex->derived_tables= 0;
...@@ -4181,12 +4182,12 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) ...@@ -4181,12 +4182,12 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
*/ */
int LEX::print_explain(select_result_sink *output, uint8 explain_flags, int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything) bool is_analyze, bool *printed_anything)
{ {
int res; int res;
if (explain && explain->have_query_plan()) if (explain && explain->have_query_plan())
{ {
res= explain->print_explain(output, explain_flags); res= explain->print_explain(output, explain_flags, is_analyze);
*printed_anything= true; *printed_anything= true;
} }
else else
......
...@@ -2268,6 +2268,7 @@ class Update_plan ...@@ -2268,6 +2268,7 @@ class Update_plan
void save_explain_data(Explain_query *query); void save_explain_data(Explain_query *query);
void save_explain_data_intern(Explain_query *query, Explain_update *eu); void save_explain_data_intern(Explain_query *query, Explain_update *eu);
virtual ~Update_plan() {} virtual ~Update_plan() {}
Update_plan(MEM_ROOT *mem_root_arg) : Update_plan(MEM_ROOT *mem_root_arg) :
...@@ -2459,6 +2460,7 @@ struct LEX: public Query_tables_list ...@@ -2459,6 +2460,7 @@ struct LEX: public Query_tables_list
*/ */
uint table_count; uint table_count;
uint8 describe; uint8 describe;
bool analyze_stmt; /* TRUE<=> this is "ANALYZE $stmt" */
/* /*
A flag that indicates what kinds of derived tables are present in the A flag that indicates what kinds of derived tables are present in the
query (0 if no derived tables, otherwise a combination of flags query (0 if no derived tables, otherwise a combination of flags
...@@ -2735,7 +2737,7 @@ struct LEX: public Query_tables_list ...@@ -2735,7 +2737,7 @@ struct LEX: public Query_tables_list
} }
int print_explain(select_result_sink *output, uint8 explain_flags, int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything); bool is_analyze, bool *printed_anything);
}; };
......
...@@ -3581,7 +3581,6 @@ case SQLCOM_PREPARE: ...@@ -3581,7 +3581,6 @@ case SQLCOM_PREPARE:
{ {
DBUG_ASSERT(first_table == all_tables && first_table != 0); DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first; TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
bool explain= MY_TEST(lex->describe);
multi_delete *result; multi_delete *result;
if ((res= multi_delete_precheck(thd, all_tables))) if ((res= multi_delete_precheck(thd, all_tables)))
...@@ -3627,7 +3626,7 @@ case SQLCOM_PREPARE: ...@@ -3627,7 +3626,7 @@ case SQLCOM_PREPARE:
result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */ result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
else else
{ {
if (explain) if (lex->describe || lex->analyze_stmt)
res= thd->lex->explain->send_explain(thd); res= thd->lex->explain->send_explain(thd);
} }
delete result; delete result;
...@@ -5223,7 +5222,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -5223,7 +5222,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
This will call optimize() for all parts of query. The query plan is This will call optimize() for all parts of query. The query plan is
printed out below. printed out below.
*/ */
res= mysql_explain_union(thd, &thd->lex->unit, result); res= mysql_explain_union(thd, &lex->unit, result);
/* Print EXPLAIN only if we don't have an error */ /* Print EXPLAIN only if we don't have an error */
if (!res) if (!res)
...@@ -5233,7 +5232,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -5233,7 +5232,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
top-level LIMIT top-level LIMIT
*/ */
result->reset_offset_limit(); result->reset_offset_limit();
thd->lex->explain->print_explain(result, thd->lex->describe); lex->explain->print_explain(result, lex->describe, lex->analyze_stmt);
if (lex->describe & DESCRIBE_EXTENDED) if (lex->describe & DESCRIBE_EXTENDED)
{ {
char buff[1024]; char buff[1024];
...@@ -5243,7 +5242,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -5243,7 +5242,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
The warnings system requires input in utf8, @see The warnings system requires input in utf8, @see
mysqld_show_warnings(). mysqld_show_warnings().
*/ */
thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET); lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_YES, str.c_ptr_safe()); ER_YES, str.c_ptr_safe());
} }
...@@ -5256,13 +5255,38 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) ...@@ -5256,13 +5255,38 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
delete result; delete result;
} }
else else
{
select_result *save_result;
Protocol *save_protocol;
if (lex->analyze_stmt)
{
save_result= result;
result= new select_send_analyze();
save_protocol= thd->protocol;
thd->protocol= new Protocol_discard(thd);
}
else
{ {
if (!result && !(result= new select_send())) if (!result && !(result= new select_send()))
return 1; /* purecov: inspected */ return 1; /* purecov: inspected */
}
query_cache_store_query(thd, all_tables); query_cache_store_query(thd, all_tables);
res= handle_select(thd, lex, result, 0); res= handle_select(thd, lex, result, 0);
if (result != lex->result) if (result != lex->result)
delete result; delete result;
if (lex->analyze_stmt)
{
result= save_result;
if (!result && !(result= new select_send()))
return 1;
delete thd->protocol;
thd->protocol= save_protocol;
thd->lex->explain->send_explain(thd);
if (result != lex->result)
delete result;
}
} }
} }
/* Count number of empty select queries */ /* Count number of empty select queries */
......
This diff is collapsed.
...@@ -251,6 +251,8 @@ typedef struct st_join_table { ...@@ -251,6 +251,8 @@ typedef struct st_join_table {
/* Special content for EXPLAIN 'Extra' column or NULL if none */ /* Special content for EXPLAIN 'Extra' column or NULL if none */
enum explain_extra_tag info; enum explain_extra_tag info;
Table_access_tracker *tracker;
Table_access_tracker *jbuf_tracker;
/* /*
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
column, or 0 if there is no info. column, or 0 if there is no info.
...@@ -536,6 +538,11 @@ typedef struct st_join_table { ...@@ -536,6 +538,11 @@ typedef struct st_join_table {
} }
void remove_redundant_bnl_scan_conds(); void remove_redundant_bnl_scan_conds();
void save_explain_data(Explain_table_access *eta, table_map prefix_tables,
bool distinct, struct st_join_table *first_top_tab);
void update_explain_data(uint idx);
} JOIN_TAB; } JOIN_TAB;
...@@ -1342,7 +1349,6 @@ class JOIN :public Sql_alloc ...@@ -1342,7 +1349,6 @@ class JOIN :public Sql_alloc
sjm_lookup_tables= 0; sjm_lookup_tables= 0;
filesort_found_rows= false; filesort_found_rows= false;
exec_saved_explain= false;
/* /*
The following is needed because JOIN::cleanup(true) may be called for The following is needed because JOIN::cleanup(true) may be called for
joins for which JOIN::optimize was aborted with an error before a proper joins for which JOIN::optimize was aborted with an error before a proper
...@@ -1351,13 +1357,6 @@ class JOIN :public Sql_alloc ...@@ -1351,13 +1357,6 @@ class JOIN :public Sql_alloc
table_access_tabs= NULL; table_access_tabs= NULL;
} }
/*
TRUE <=> There was a JOIN::exec() call, which saved this JOIN's EXPLAIN.
The idea is that we also save at the end of JOIN::optimize(), but that
might not be the final plan.
*/
bool exec_saved_explain;
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num, int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
COND *conds, uint og_num, ORDER *order, bool skip_order_by, COND *conds, uint og_num, ORDER *order, bool skip_order_by,
ORDER *group, Item *having, ORDER *proc_param, SELECT_LEX *select, ORDER *group, Item *having, ORDER *proc_param, SELECT_LEX *select,
...@@ -1848,14 +1847,14 @@ void push_index_cond(JOIN_TAB *tab, uint keyno); ...@@ -1848,14 +1847,14 @@ void push_index_cond(JOIN_TAB *tab, uint keyno);
/* EXPLAIN-related utility functions */ /* EXPLAIN-related utility functions */
int print_explain_message_line(select_result_sink *result, int print_explain_message_line(select_result_sink *result,
uint8 options, uint8 options, bool is_analyze,
uint select_number, uint select_number,
const char *select_type, const char *select_type,
ha_rows *rows, ha_rows *rows,
const char *message); const char *message);
void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res); void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
int print_explain_row(select_result_sink *result, int print_explain_row(select_result_sink *result,
uint8 options, uint8 options, bool is_analyze,
uint select_number, uint select_number,
const char *select_type, const char *select_type,
const char *table_name, const char *table_name,
...@@ -1866,6 +1865,8 @@ int print_explain_row(select_result_sink *result, ...@@ -1866,6 +1865,8 @@ int print_explain_row(select_result_sink *result,
const char *key_len, const char *key_len,
const char *ref, const char *ref,
ha_rows *rows, ha_rows *rows,
ha_rows *r_rows,
double r_filtered,
const char *extra); const char *extra);
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line); void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
......
This diff is collapsed.
...@@ -150,6 +150,69 @@ class Show_explain_request : public Apc_target::Apc_call ...@@ -150,6 +150,69 @@ class Show_explain_request : public Apc_target::Apc_call
void call_in_target_thread(); void call_in_target_thread();
}; };
/**
Condition pushdown used for INFORMATION_SCHEMA / SHOW queries.
This structure is to implement an optimization when
accessing data dictionary data in the INFORMATION_SCHEMA
or SHOW commands.
When the query contain a TABLE_SCHEMA or TABLE_NAME clause,
narrow the search for data based on the constraints given.
*/
typedef struct st_lookup_field_values
{
/**
Value of a TABLE_SCHEMA clause.
Note that this value length may exceed @c NAME_LEN.
@sa wild_db_value
*/
LEX_STRING db_value;
/**
Value of a TABLE_NAME clause.
Note that this value length may exceed @c NAME_LEN.
@sa wild_table_value
*/
LEX_STRING table_value;
/**
True when @c db_value is a LIKE clause,
false when @c db_value is an '=' clause.
*/
bool wild_db_value;
/**
True when @c table_value is a LIKE clause,
false when @c table_value is an '=' clause.
*/
bool wild_table_value;
} LOOKUP_FIELD_VALUES;
/*
INFORMATION_SCHEMA: Execution plan for get_all_tables() call
*/
class IS_table_read_plan : public Sql_alloc
{
public:
IS_table_read_plan() : no_rows(false) {}
bool no_rows;
LOOKUP_FIELD_VALUES lookup_field_vals;
Item *partial_cond;
bool has_db_lookup_value()
{
return (lookup_field_vals.db_value.length &&
!lookup_field_vals.wild_db_value);
}
bool has_table_lookup_value()
{
return (lookup_field_vals.table_value.length &&
!lookup_field_vals.wild_table_value);
}
};
bool optimize_schema_tables_reads(JOIN *join);
/* Handle the ignored database directories list for SHOW/I_S. */ /* Handle the ignored database directories list for SHOW/I_S. */
bool ignore_db_dirs_init(); bool ignore_db_dirs_init();
void ignore_db_dirs_free(); void ignore_db_dirs_free();
......
...@@ -277,6 +277,7 @@ int mysql_update(THD *thd, ...@@ -277,6 +277,7 @@ int mysql_update(THD *thd,
List<Item> all_fields; List<Item> all_fields;
killed_state killed_status= NOT_KILLED; killed_state killed_status= NOT_KILLED;
Update_plan query_plan(thd->mem_root); Update_plan query_plan(thd->mem_root);
Explain_update *explain;
query_plan.index= MAX_KEY; query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE; query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
...@@ -717,15 +718,16 @@ int mysql_update(THD *thd, ...@@ -717,15 +718,16 @@ int mysql_update(THD *thd,
if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
table->prepare_for_position(); table->prepare_for_position();
explain= thd->lex->explain->get_upd_del_plan();
/* /*
We can use compare_record() to optimize away updates if We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if the table handler is returning all columns OR if
if all updated columns are read if all updated columns are read
*/ */
can_compare_record= records_are_comparable(table); can_compare_record= records_are_comparable(table);
while (!(error=info.read_record(&info)) && !thd->killed) while (!(error=info.read_record(&info)) && !thd->killed)
{ {
explain->on_record_read();
if (table->vfield) if (table->vfield)
update_virtual_fields(thd, table, update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL : table->triggers ? VCOL_UPDATE_ALL :
...@@ -736,6 +738,7 @@ int mysql_update(THD *thd, ...@@ -736,6 +738,7 @@ int mysql_update(THD *thd,
if (table->file->was_semi_consistent_read()) if (table->file->was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */ continue; /* repeat the read of the same row if it still exists */
explain->on_record_after_where();
store_record(table,record[1]); store_record(table,record[1]);
if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0,
TRG_EVENT_UPDATE)) TRG_EVENT_UPDATE))
...@@ -993,7 +996,11 @@ int mysql_update(THD *thd, ...@@ -993,7 +996,11 @@ int mysql_update(THD *thd,
id= thd->arg_of_last_insert_id_function ? id= thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt : 0; thd->first_successful_insert_id_in_prev_stmt : 0;
if (error < 0) if (thd->lex->analyze_stmt)
{
error= thd->lex->explain->send_explain(thd);
}
else if (error < 0)
{ {
char buff[MYSQL_ERRMSG_SIZE]; char buff[MYSQL_ERRMSG_SIZE];
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
...@@ -1563,7 +1570,7 @@ bool mysql_multi_update(THD *thd, ...@@ -1563,7 +1570,7 @@ bool mysql_multi_update(THD *thd,
(*result)->abort_result_set(); (*result)->abort_result_set();
else else
{ {
if (thd->lex->describe) if (thd->lex->describe || thd->lex->analyze_stmt)
res= thd->lex->explain->send_explain(thd); res= thd->lex->explain->send_explain(thd);
} }
thd->abort_on_warning= 0; thd->abort_on_warning= 0;
...@@ -2502,11 +2509,14 @@ bool multi_update::send_eof() ...@@ -2502,11 +2509,14 @@ bool multi_update::send_eof()
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
if (!thd->lex->analyze_stmt)
{
id= thd->arg_of_last_insert_id_function ? id= thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt : 0; thd->first_successful_insert_id_in_prev_stmt : 0;
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
(ulong) found, (ulong) updated, (ulong) thd->cuted_fields); (ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff); id, buff);
}
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
...@@ -1804,6 +1804,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1804,6 +1804,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <dyncol_def_list> dyncall_create_list %type <dyncol_def_list> dyncall_create_list
%type <NONE> %type <NONE>
analyze_stmt_command
query verb_clause create change select do drop insert replace insert2 query verb_clause create change select do drop insert replace insert2
insert_values update delete truncate rename insert_values update delete truncate rename
show describe load alter optimize keycache preload flush show describe load alter optimize keycache preload flush
...@@ -1990,6 +1991,7 @@ verb_clause: ...@@ -1990,6 +1991,7 @@ verb_clause:
statement: statement:
alter alter
| analyze | analyze
| analyze_stmt_command
| binlog_base64_event | binlog_base64_event
| call | call
| change | change
...@@ -12766,6 +12768,13 @@ describe_command: ...@@ -12766,6 +12768,13 @@ describe_command:
| DESCRIBE | DESCRIBE
; ;
analyze_stmt_command:
ANALYZE_SYM explainable_command
{
Lex->analyze_stmt= true;
}
;
opt_extended_describe: opt_extended_describe:
/* empty */ {} /* empty */ {}
| EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } | EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
......
...@@ -1499,6 +1499,7 @@ typedef struct st_schema_table ...@@ -1499,6 +1499,7 @@ typedef struct st_schema_table
uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */ uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
} ST_SCHEMA_TABLE; } ST_SCHEMA_TABLE;
class IS_table_read_plan;
/* /*
Types of derived tables. The ending part is a bitmap of phases that are Types of derived tables. The ending part is a bitmap of phases that are
...@@ -2044,12 +2045,23 @@ struct TABLE_LIST ...@@ -2044,12 +2045,23 @@ struct TABLE_LIST
/* TRUE <=> this table is a const one and was optimized away. */ /* TRUE <=> this table is a const one and was optimized away. */
bool optimized_away; bool optimized_away;
/* I_S: Flags to open_table (e.g. OPEN_TABLE_ONLY or OPEN_VIEW_ONLY) */
uint i_s_requested_object; uint i_s_requested_object;
bool has_db_lookup_value;
bool has_table_lookup_value; /*
I_S: how to read the tables (SKIP_OPEN_TABLE/OPEN_FRM_ONLY/OPEN_FULL_TABLE)
*/
uint table_open_method; uint table_open_method;
/*
I_S: where the schema table was filled
(this is a hack. The code should be able to figure out whether reading
from I_S should be done by create_sort_index() or by JOIN::exec.)
*/
enum enum_schema_table_state schema_table_state; enum enum_schema_table_state schema_table_state;
/* Something like a "query plan" for reading INFORMATION_SCHEMA table */
IS_table_read_plan *is_table_read_plan;
MDL_request mdl_request; MDL_request mdl_request;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
......
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