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.');
drop table if exists t1,t2,t3,t4;
drop database if exists mysqltest1;
drop database if exists client_test_db;
create table t1
(
......
......@@ -641,7 +641,7 @@ set debug_dbug='+d,show_explain_probe_join_exec_start';
SHOW INDEX FROM t1;
show explain for $thr2;
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:
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
......
#
# 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
--disable_warnings
drop table if exists t1,t2,t3,t4;
drop database if exists mysqltest1;
# Avoid wrong warnings if mysql_client_test fails
drop database if exists client_test_db;
--enable_warnings
......
......@@ -210,6 +210,42 @@ public:
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);
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate);
......
......@@ -2282,6 +2282,9 @@ int THD::send_explain_fields(select_result *result)
/*
Populate the provided field_list with EXPLAIN output columns.
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)
......@@ -2317,11 +2320,25 @@ void THD::make_explain_field_list(List<Item> &field_list)
item->maybe_null=1;
field_list.push_back(item= new Item_return_int("rows", 10,
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));
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;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
}
......
......@@ -3961,6 +3961,20 @@ public:
};
/*
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 {
protected:
sql_exchange *exchange;
......
......@@ -223,6 +223,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
bool with_select= !select_lex->item_list.is_empty();
Explain_delete *explain;
Delete_plan query_plan(thd->mem_root);
query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE;
......@@ -538,9 +539,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
goto cleanup;
}
explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan();
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
explain->on_record_read();
if (table->vfield)
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
......@@ -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
if (!select || select->skip_record(thd) > 0)
{
explain->on_record_after_where();
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
......@@ -666,6 +670,11 @@ cleanup:
}
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
if (thd->lex->analyze_stmt)
{
error= thd->lex->explain->send_explain(thd);
}
else
if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
......@@ -1283,7 +1292,7 @@ bool multi_delete::send_eof()
if (local_error != 0)
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);
}
......
This diff is collapsed.
......@@ -14,6 +14,38 @@
along with this program; if not, write to the Free Software
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 @@ public:
}
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,
uint8 explain_flags);
uint8 explain_flags, bool is_analyze);
virtual ~Explain_node(){}
};
......@@ -109,6 +141,12 @@ public:
join_tabs[n_join_tabs++]= tab;
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:
int select_id;
......@@ -134,7 +172,14 @@ public:
bool using_filesort;
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 @@ public:
union_members.append(select_no);
}
int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags);
uint8 explain_flags, bool is_analyze);
const char *fake_select_type;
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;
class Explain_delete;
class Explain_insert;
/*
Explain structure for a query (i.e. a statement).
......@@ -238,13 +297,14 @@ public:
Explain_union *get_union(uint select_id);
/* 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 */
int send_explain(THD *thd);
/* 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 */
bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
......@@ -252,6 +312,8 @@ public:
void query_plan_ready();
MEM_ROOT *mem_root;
Explain_update *get_upd_del_plan() { return upd_del_plan; }
private:
/* Explain_delete inherits from Explain_update */
Explain_update *upd_del_plan;
......@@ -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;
const char *join_alg;
StringBuffer<64> mrr_type;
} EXPLAIN_BKA_TYPE;
bool is_using_jbuf() { return (join_alg != NULL); }
};
/*
......@@ -386,6 +452,7 @@ private:
/*
EXPLAIN data structure for a single JOIN_TAB.
*/
class Explain_table_access : public Sql_alloc
{
public:
......@@ -459,8 +526,14 @@ public:
StringBuffer<32> firstmatch_table_name;
int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze,
uint select_id, const char *select_type,
bool using_temporary, bool using_filesort);
/* ANALYZE members*/
Table_access_tracker tracker;
Table_access_tracker jbuf_tracker;
private:
void append_tag_name(String *str, enum explain_extra_tag tag);
};
......@@ -502,8 +575,17 @@ public:
bool using_filesort;
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,
uint8 explain_flags);
uint8 explain_flags, bool is_analyze);
};
......@@ -523,7 +605,7 @@ public:
int get_select_id() { return 1; /* always root */ }
int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags);
uint8 explain_flags, bool is_analyze);
};
......@@ -544,7 +626,7 @@ public:
virtual int get_select_id() { return 1; /* always root */ }
virtual int print_explain(Explain_query *query, select_result_sink *output,
uint8 explain_flags);
uint8 explain_flags, bool is_analyze);
};
......@@ -2260,7 +2260,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
*/
goto finish;
}
while (!(error= join_tab_scan->next()))
{
if (join->thd->check_killed())
......@@ -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 */
if (prepare_look_for_matches(skip_last))
continue;
join_tab->jbuf_tracker->r_scans++;
uchar *rec_ptr;
/* Read each possible candidate from the buffer and look for matches */
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
the next record read from the join buffer, then the record is skipped.
......@@ -2451,6 +2453,8 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr)
if (join_tab->select && join_tab->select->skip_record(join->thd) <= 0)
DBUG_RETURN(FALSE);
join_tab->jbuf_tracker->r_rows_after_where++;
if (!join_tab->is_last_inner_table())
DBUG_RETURN(TRUE);
......@@ -2574,7 +2578,7 @@ finish:
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);
......@@ -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);
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);
add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
......@@ -3333,6 +3337,7 @@ int JOIN_TAB_SCAN::open()
{
save_or_restore_used_tabs(join_tab, FALSE);
is_first_record= TRUE;
join_tab->tracker->r_scans++;
return join_init_read_record(join_tab);
}
......@@ -3371,8 +3376,14 @@ int JOIN_TAB_SCAN::next()
is_first_record= FALSE;
else
err= info->read_record(info);
if (!err && table->vfield)
update_virtual_fields(thd, table);
if (!err)
{
join_tab->tracker->r_rows++;
if (table->vfield)
update_virtual_fields(thd, table);
}
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
{
if (thd->check_killed() || skip_rc < 0)
......@@ -3382,9 +3393,16 @@ int JOIN_TAB_SCAN::next()
meet the condition pushed to the table join_tab.
*/
err= info->read_record(info);
if (!err && table->vfield)
update_virtual_fields(thd, table);
}
if (!err)
{
join_tab->tracker->r_rows++;
if (table->vfield)
update_virtual_fields(thd, table);
}
}
if (!err)
join_tab->tracker->r_rows_after_where++;
return err;
}
......
......@@ -63,7 +63,7 @@ typedef struct st_cache_field {
class JOIN_TAB_SCAN;
struct st_explain_bka_type;
class EXPLAIN_BKA_TYPE;
/*
JOIN_CACHE is the base class to support the implementations of
......@@ -662,7 +662,7 @@ public:
enum_nested_loop_state join_records(bool skip_last);
/* 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();
......@@ -1340,7 +1340,7 @@ public:
/* Check index condition of the joined table for a record from BKA cache */
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 @@ public:
/* Check index condition of the joined table for a record from BKAH cache */
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)
if (lex->select_lex.group_list_ptrs)
lex->select_lex.group_list_ptrs->clear();
lex->describe= 0;
lex->analyze_stmt= 0;
lex->subqueries= FALSE;
lex->context_analysis_only= 0;
lex->derived_tables= 0;
......@@ -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,
bool *printed_anything)
bool is_analyze, bool *printed_anything)
{
int res;
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;
}
else
......
......@@ -2268,6 +2268,7 @@ public:
void save_explain_data(Explain_query *query);
void save_explain_data_intern(Explain_query *query, Explain_update *eu);
virtual ~Update_plan() {}
Update_plan(MEM_ROOT *mem_root_arg) :
......@@ -2459,6 +2460,7 @@ struct LEX: public Query_tables_list
*/
uint table_count;
uint8 describe;
bool analyze_stmt; /* TRUE<=> this is "ANALYZE $stmt" */
/*
A flag that indicates what kinds of derived tables are present in the
query (0 if no derived tables, otherwise a combination of flags
......@@ -2735,7 +2737,7 @@ struct LEX: public Query_tables_list
}
int print_explain(select_result_sink *output, uint8 explain_flags,
bool *printed_anything);
bool is_analyze, bool *printed_anything);
};
......
......@@ -3581,7 +3581,6 @@ end_with_restore_list:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
bool explain= MY_TEST(lex->describe);
multi_delete *result;
if ((res= multi_delete_precheck(thd, all_tables)))
......@@ -3627,7 +3626,7 @@ end_with_restore_list:
result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
else
{
if (explain)
if (lex->describe || lex->analyze_stmt)
res= thd->lex->explain->send_explain(thd);
}
delete result;
......@@ -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
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 */
if (!res)
......@@ -5233,7 +5232,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
top-level 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)
{
char buff[1024];
......@@ -5243,7 +5242,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
The warnings system requires input in utf8, @see
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,
ER_YES, str.c_ptr_safe());
}
......@@ -5257,12 +5256,37 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
}
else
{
if (!result && !(result= new select_send()))
return 1; /* purecov: inspected */
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()))
return 1; /* purecov: inspected */
}
query_cache_store_query(thd, all_tables);
res= handle_select(thd, lex, result, 0);
if (result != lex->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 */
......
This diff is collapsed.
......@@ -250,7 +250,9 @@ typedef struct st_join_table {
/* Special content for EXPLAIN 'Extra' column or NULL if none */
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'
column, or 0 if there is no info.
......@@ -536,6 +538,11 @@ typedef struct st_join_table {
}
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;
......@@ -1342,7 +1349,6 @@ public:
sjm_lookup_tables= 0;
filesort_found_rows= false;
exec_saved_explain= false;
/*
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
......@@ -1351,13 +1357,6 @@ public:
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,
COND *conds, uint og_num, ORDER *order, bool skip_order_by,
ORDER *group, Item *having, ORDER *proc_param, SELECT_LEX *select,
......@@ -1848,14 +1847,14 @@ void push_index_cond(JOIN_TAB *tab, uint keyno);
/* EXPLAIN-related utility functions */
int print_explain_message_line(select_result_sink *result,
uint8 options,
uint8 options, bool is_analyze,
uint select_number,
const char *select_type,
ha_rows *rows,
const char *message);
void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
int print_explain_row(select_result_sink *result,
uint8 options,
uint8 options, bool is_analyze,
uint select_number,
const char *select_type,
const char *table_name,
......@@ -1866,6 +1865,8 @@ int print_explain_row(select_result_sink *result,
const char *key_len,
const char *ref,
ha_rows *rows,
ha_rows *r_rows,
double r_filtered,
const char *extra);
void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
......
This diff is collapsed.
......@@ -150,6 +150,69 @@ public:
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. */
bool ignore_db_dirs_init();
void ignore_db_dirs_free();
......
......@@ -277,6 +277,7 @@ int mysql_update(THD *thd,
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
Update_plan query_plan(thd->mem_root);
Explain_update *explain;
query_plan.index= MAX_KEY;
query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_update");
......@@ -717,15 +718,16 @@ int mysql_update(THD *thd,
if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
table->prepare_for_position();
explain= thd->lex->explain->get_upd_del_plan();
/*
We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if
if all updated columns are read
*/
can_compare_record= records_are_comparable(table);
while (!(error=info.read_record(&info)) && !thd->killed)
{
explain->on_record_read();
if (table->vfield)
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
......@@ -736,6 +738,7 @@ int mysql_update(THD *thd,
if (table->file->was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */
explain->on_record_after_where();
store_record(table,record[1]);
if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0,
TRG_EVENT_UPDATE))
......@@ -993,7 +996,11 @@ int mysql_update(THD *thd,
id= thd->arg_of_last_insert_id_function ?
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];
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
......@@ -1563,7 +1570,7 @@ bool mysql_multi_update(THD *thd,
(*result)->abort_result_set();
else
{
if (thd->lex->describe)
if (thd->lex->describe || thd->lex->analyze_stmt)
res= thd->lex->explain->send_explain(thd);
}
thd->abort_on_warning= 0;
......@@ -2502,11 +2509,14 @@ bool multi_update::send_eof()
DBUG_RETURN(TRUE);
}
id= thd->arg_of_last_insert_id_function ?
if (!thd->lex->analyze_stmt)
{
id= thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt : 0;
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
(ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
(ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
}
DBUG_RETURN(FALSE);
}
......@@ -982,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
This makes the code grep-able, and helps maintenance.
*/
%token ABORT_SYM /* INTERNAL (used in lex) */
%token ACCESSIBLE_SYM
%token ACTION /* SQL-2003-N */
......@@ -1804,6 +1804,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <dyncol_def_list> dyncall_create_list
%type <NONE>
analyze_stmt_command
query verb_clause create change select do drop insert replace insert2
insert_values update delete truncate rename
show describe load alter optimize keycache preload flush
......@@ -1990,6 +1991,7 @@ verb_clause:
statement:
alter
| analyze
| analyze_stmt_command
| binlog_base64_event
| call
| change
......@@ -12766,6 +12768,13 @@ describe_command:
| DESCRIBE
;
analyze_stmt_command:
ANALYZE_SYM explainable_command
{
Lex->analyze_stmt= true;
}
;
opt_extended_describe:
/* empty */ {}
| EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
......
......@@ -1499,6 +1499,7 @@ typedef struct st_schema_table
uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
} ST_SCHEMA_TABLE;
class IS_table_read_plan;
/*
Types of derived tables. The ending part is a bitmap of phases that are
......@@ -2044,12 +2045,23 @@ struct TABLE_LIST
/* TRUE <=> this table is a const one and was 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;
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;
/*
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;
/* Something like a "query plan" for reading INFORMATION_SCHEMA table */
IS_table_read_plan *is_table_read_plan;
MDL_request mdl_request;
#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