Merge bb-10.1-explain-analyze into 10.1

This commit is contained in:
Sergei Petrunia 2014-06-25 16:46:42 +04:00
commit 424d5de89d
23 changed files with 1354 additions and 550 deletions

View File

@ -0,0 +1,149 @@
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;

View File

@ -1,5 +1,6 @@
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
( (

View File

@ -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

View File

@ -0,0 +1,109 @@
#
# 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;

View File

@ -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

View File

@ -210,6 +210,42 @@ public:
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);

View File

@ -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)
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));
} }

View File

@ -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 { class select_to_file :public select_result_interceptor {
protected: protected:
sql_exchange *exchange; sql_exchange *exchange;

View File

@ -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,
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,
// 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 @@ cleanup:
} }
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))
{ {

View File

@ -140,7 +140,7 @@ int Explain_query::send_explain(THD *thd)
return 1; return 1;
int res; int res;
if ((res= print_explain(result, lex->describe))) if ((res= print_explain(result, lex->describe, lex->analyze_stmt)))
result->abort_result_set(); result->abort_result_set();
else else
result->send_eof(); result->send_eof();
@ -154,16 +154,16 @@ int Explain_query::send_explain(THD *thd)
*/ */
int Explain_query::print_explain(select_result_sink *output, int Explain_query::print_explain(select_result_sink *output,
uint8 explain_flags) uint8 explain_flags, bool is_analyze)
{ {
if (upd_del_plan) if (upd_del_plan)
{ {
upd_del_plan->print_explain(this, output, explain_flags); upd_del_plan->print_explain(this, output, explain_flags, is_analyze);
return 0; return 0;
} }
else if (insert_plan) else if (insert_plan)
{ {
insert_plan->print_explain(this, output, explain_flags); insert_plan->print_explain(this, output, explain_flags, is_analyze);
return 0; return 0;
} }
else else
@ -172,14 +172,14 @@ int Explain_query::print_explain(select_result_sink *output,
Explain_node *node= get_node(1); Explain_node *node= get_node(1);
if (!node) if (!node)
return 1; /* No query plan */ return 1; /* No query plan */
return node->print_explain(this, output, explain_flags); return node->print_explain(this, output, explain_flags, is_analyze);
} }
} }
bool print_explain_query(LEX *lex, THD *thd, String *str) bool print_explain_query(LEX *lex, THD *thd, String *str)
{ {
return lex->explain->print_explain_str(thd, str); return lex->explain->print_explain_str(thd, str, false);
} }
@ -187,14 +187,14 @@ bool print_explain_query(LEX *lex, THD *thd, String *str)
Return tabular EXPLAIN output as a text string Return tabular EXPLAIN output as a text string
*/ */
bool Explain_query::print_explain_str(THD *thd, String *out_str) bool Explain_query::print_explain_str(THD *thd, String *out_str, bool is_analyze)
{ {
List<Item> fields; List<Item> fields;
thd->make_explain_field_list(fields); thd->make_explain_field_list(fields);
select_result_text_buffer output_buf(thd); select_result_text_buffer output_buf(thd);
output_buf.send_result_set_metadata(fields, thd->lex->describe); output_buf.send_result_set_metadata(fields, thd->lex->describe);
if (print_explain(&output_buf, thd->lex->describe)) if (print_explain(&output_buf, thd->lex->describe, is_analyze))
return true; return true;
output_buf.save_to(out_str); output_buf.save_to(out_str);
return false; return false;
@ -217,7 +217,8 @@ static void push_string(List<Item> *item_list, String *str)
int Explain_union::print_explain(Explain_query *query, int Explain_union::print_explain(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags,
bool is_analyze)
{ {
char table_name_buffer[SAFE_NAME_LEN]; char table_name_buffer[SAFE_NAME_LEN];
@ -225,7 +226,7 @@ int Explain_union::print_explain(Explain_query *query,
for (int i= 0; i < (int) union_members.elements(); i++) for (int i= 0; i < (int) union_members.elements(); i++)
{ {
Explain_select *sel= query->get_select(union_members.at(i)); Explain_select *sel= query->get_select(union_members.at(i));
sel->print_explain(query, output, explain_flags); sel->print_explain(query, output, explain_flags, is_analyze);
} }
/* Print a line with "UNION RESULT" */ /* Print a line with "UNION RESULT" */
@ -287,9 +288,21 @@ int Explain_union::print_explain(Explain_query *query,
/* `rows` */ /* `rows` */
item_list.push_back(item_null); item_list.push_back(item_null);
/* `r_rows` */
if (is_analyze)
{
ha_rows avg_rows= fake_select_lex_tracker.get_avg_rows();
item_list.push_back(new Item_int((longlong) (ulonglong) avg_rows,
MY_INT64_NUM_DECIMAL_DIGITS));
}
/* `filtered` */ /* `filtered` */
if (explain_flags & DESCRIBE_EXTENDED) if (explain_flags & DESCRIBE_EXTENDED || is_analyze)
item_list.push_back(item_null);
/* `r_filtered` */
if (is_analyze)
item_list.push_back(item_null); item_list.push_back(item_null);
/* `Extra` */ /* `Extra` */
@ -309,7 +322,7 @@ int Explain_union::print_explain(Explain_query *query,
Print all subquery children (UNION children have already been printed at Print all subquery children (UNION children have already been printed at
the start of this function) the start of this function)
*/ */
return print_explain_for_children(query, output, explain_flags); return print_explain_for_children(query, output, explain_flags, is_analyze);
} }
@ -319,18 +332,26 @@ int Explain_union::print_explain(Explain_query *query,
int Explain_node::print_explain_for_children(Explain_query *query, int Explain_node::print_explain_for_children(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags,
bool is_analyze)
{ {
for (int i= 0; i < (int) children.elements(); i++) for (int i= 0; i < (int) children.elements(); i++)
{ {
Explain_node *node= query->get_node(children.at(i)); Explain_node *node= query->get_node(children.at(i));
if (node->print_explain(query, output, explain_flags)) if (node->print_explain(query, output, explain_flags, is_analyze))
return 1; return 1;
} }
return 0; return 0;
} }
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
{
delete join_tabs[idx];
join_tabs[idx]= new_tab;
}
Explain_select::~Explain_select() Explain_select::~Explain_select()
{ {
if (join_tabs) if (join_tabs)
@ -344,7 +365,7 @@ Explain_select::~Explain_select()
int Explain_select::print_explain(Explain_query *query, int Explain_select::print_explain(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags, bool is_analyze)
{ {
if (message) if (message)
{ {
@ -359,8 +380,17 @@ int Explain_select::print_explain(Explain_query *query,
item_list.push_back(item_null); item_list.push_back(item_null);
if (explain_flags & DESCRIBE_PARTITIONS) if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null); item_list.push_back(item_null);
if (explain_flags & DESCRIBE_EXTENDED)
/* filtered */
if (is_analyze || explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null); item_list.push_back(item_null);
if (is_analyze)
{
/* r_rows, r_filtered */
item_list.push_back(item_null);
item_list.push_back(item_null);
}
item_list.push_back(new Item_string(message,strlen(message),cs)); item_list.push_back(new Item_string(message,strlen(message),cs));
@ -373,7 +403,7 @@ int Explain_select::print_explain(Explain_query *query,
bool using_fs= using_filesort; bool using_fs= using_filesort;
for (uint i=0; i< n_join_tabs; i++) for (uint i=0; i< n_join_tabs; i++)
{ {
join_tabs[i]->print_explain(output, explain_flags, select_id, join_tabs[i]->print_explain(output, explain_flags, is_analyze, select_id,
select_type, using_tmp, using_fs); select_type, using_tmp, using_fs);
if (i == 0) if (i == 0)
{ {
@ -387,7 +417,7 @@ int Explain_select::print_explain(Explain_query *query,
} }
} }
return print_explain_for_children(query, output, explain_flags); return print_explain_for_children(query, output, explain_flags, is_analyze);
} }
@ -398,8 +428,9 @@ void Explain_table_access::push_extra(enum explain_extra_tag extra_tag)
int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags, int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags,
uint select_id, const char *select_type, bool is_analyze,
bool using_temporary, bool using_filesort) uint select_id, const char *select_type,
bool using_temporary, bool using_filesort)
{ {
const CHARSET_INFO *cs= system_charset_info; const CHARSET_INFO *cs= system_charset_info;
const char *hash_key_prefix= "#hash#"; const char *hash_key_prefix= "#hash#";
@ -519,8 +550,23 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
else else
item_list.push_back(item_null); item_list.push_back(item_null);
/* `r_rows` */
if (is_analyze)
{
if (!tracker.has_scans())
{
item_list.push_back(item_null);
}
else
{
ha_rows avg_rows= tracker.get_avg_rows();
item_list.push_back(new Item_int((longlong) (ulonglong) avg_rows,
MY_INT64_NUM_DECIMAL_DIGITS));
}
}
/* `filtered` */ /* `filtered` */
if (explain_flags & DESCRIBE_EXTENDED) if (explain_flags & DESCRIBE_EXTENDED || is_analyze)
{ {
if (filtered_set) if (filtered_set)
{ {
@ -530,6 +576,22 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
item_list.push_back(item_null); item_list.push_back(item_null);
} }
/* `r_filtered` */
if (is_analyze)
{
if (!tracker.has_scans())
{
item_list.push_back(item_null);
}
else
{
double r_filtered= tracker.get_filtered_after_where();
if (bka_type.is_using_jbuf())
r_filtered *= jbuf_tracker.get_filtered_after_where();
item_list.push_back(new Item_float(r_filtered*100.0, 2));
}
}
/* `Extra` */ /* `Extra` */
StringBuffer<256> extra_buf; StringBuffer<256> extra_buf;
bool first= true; bool first= true;
@ -802,12 +864,13 @@ void Explain_quick_select::print_key_len(String *str)
int Explain_delete::print_explain(Explain_query *query, int Explain_delete::print_explain(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags,
bool is_analyze)
{ {
if (deleting_all_rows) if (deleting_all_rows)
{ {
const char *msg= "Deleting all rows"; const char *msg= "Deleting all rows";
int res= print_explain_message_line(output, explain_flags, int res= print_explain_message_line(output, explain_flags, is_analyze,
1 /*select number*/, 1 /*select number*/,
select_type, &rows, msg); select_type, &rows, msg);
return res; return res;
@ -815,14 +878,16 @@ int Explain_delete::print_explain(Explain_query *query,
} }
else else
{ {
return Explain_update::print_explain(query, output, explain_flags); return Explain_update::print_explain(query, output, explain_flags,
is_analyze);
} }
} }
int Explain_update::print_explain(Explain_query *query, int Explain_update::print_explain(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags,
bool is_analyze)
{ {
StringBuffer<64> key_buf; StringBuffer<64> key_buf;
StringBuffer<64> key_len_buf; StringBuffer<64> key_len_buf;
@ -832,7 +897,7 @@ int Explain_update::print_explain(Explain_query *query,
const char *msg= impossible_where ? const char *msg= impossible_where ?
"Impossible WHERE" : "Impossible WHERE" :
"No matching rows after partition pruning"; "No matching rows after partition pruning";
int res= print_explain_message_line(output, explain_flags, int res= print_explain_message_line(output, explain_flags, is_analyze,
1 /*select number*/, 1 /*select number*/,
select_type, select_type,
NULL, /* rows */ NULL, /* rows */
@ -892,8 +957,9 @@ int Explain_update::print_explain(Explain_query *query,
Single-table DELETE commands do not do "Using temporary". Single-table DELETE commands do not do "Using temporary".
"Using index condition" is also not possible (which is an unjustified limitation) "Using index condition" is also not possible (which is an unjustified limitation)
*/ */
double r_filtered= 100 * (r_rows?((double)r_rows_after_where/r_rows):1.0);
print_explain_row(output, explain_flags, print_explain_row(output, explain_flags, is_analyze,
1, /* id */ 1, /* id */
select_type, select_type,
table_name.c_ptr(), table_name.c_ptr(),
@ -904,18 +970,21 @@ int Explain_update::print_explain(Explain_query *query,
key_len_buf.length() ? key_len_buf.c_ptr() : NULL, key_len_buf.length() ? key_len_buf.c_ptr() : NULL,
NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */ NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
&rows, &rows,
&r_rows,
r_filtered,
extra_str.c_ptr_safe()); extra_str.c_ptr_safe());
return print_explain_for_children(query, output, explain_flags); return print_explain_for_children(query, output, explain_flags, is_analyze);
} }
int Explain_insert::print_explain(Explain_query *query, int Explain_insert::print_explain(Explain_query *query,
select_result_sink *output, select_result_sink *output,
uint8 explain_flags) uint8 explain_flags,
bool is_analyze)
{ {
const char *select_type="INSERT"; const char *select_type="INSERT";
print_explain_row(output, explain_flags, print_explain_row(output, explain_flags, is_analyze,
1, /* id */ 1, /* id */
select_type, select_type,
table_name.c_ptr(), table_name.c_ptr(),
@ -926,9 +995,11 @@ int Explain_insert::print_explain(Explain_query *query,
NULL, // key_len NULL, // key_len
NULL, // ref NULL, // ref
NULL, // rows NULL, // rows
NULL, // r_rows
100.0, // r_filtered
NULL); NULL);
return print_explain_for_children(query, output, explain_flags); return print_explain_for_children(query, output, explain_flags, is_analyze);
} }

View File

@ -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 @@ public:
} }
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(){}
}; };
@ -109,6 +141,12 @@ public:
join_tabs[n_join_tabs++]= tab; join_tabs[n_join_tabs++]= tab;
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;
@ -134,7 +172,14 @@ public:
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 @@ public:
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;
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 @@ public:
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 @@ public:
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
}; };
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 @@ private:
/* /*
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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);
}; };

View File

@ -2260,7 +2260,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
*/ */
goto finish; goto finish;
} }
while (!(error= join_tab_scan->next())) while (!(error= join_tab_scan->next()))
{ {
if (join->thd->check_killed()) 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 */ /* 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.
@ -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) 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 @@ finish:
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)
} }
} }
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()
{ {
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()
is_first_record= FALSE; is_first_record= FALSE;
else else
err= info->read_record(info); 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) 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()
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)
update_virtual_fields(thd, table); {
} join_tab->tracker->r_rows++;
if (table->vfield)
update_virtual_fields(thd, table);
}
}
if (!err)
join_tab->tracker->r_rows_after_where++;
return err; return err;
} }

View File

@ -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 @@ public:
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 @@ public:
/* 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 @@ public:
/* 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);
}; };

View File

@ -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)
*/ */
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

View File

@ -2268,6 +2268,7 @@ public:
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
*/ */
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
} }
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);
}; };

View File

@ -5233,7 +5233,8 @@ 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); thd->lex->explain->print_explain(result, thd->lex->describe,
thd->lex->analyze_stmt);
if (lex->describe & DESCRIBE_EXTENDED) if (lex->describe & DESCRIBE_EXTENDED)
{ {
char buff[1024]; char buff[1024];
@ -5257,12 +5258,37 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
} }
else else
{ {
if (!result && !(result= new select_send())) //psergey-todo: ANALYZE should hook in here...
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); 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;
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 */

File diff suppressed because it is too large Load Diff

View File

@ -250,7 +250,9 @@ 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 {
} }
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;
@ -1848,14 +1855,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 +1873,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);

View File

@ -121,41 +121,6 @@ append_algorithm(TABLE_LIST *table, String *buff);
static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table); static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
/**
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;
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *); bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
/*************************************************************************** /***************************************************************************
@ -2395,7 +2360,7 @@ void Show_explain_request::call_in_target_thread()
DBUG_ASSERT(current_thd == target_thd); DBUG_ASSERT(current_thd == target_thd);
set_current_thd(request_thd); set_current_thd(request_thd);
if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/, if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
&printed_anything)) false /*TODO: analyze? */, &printed_anything))
{ {
failed_to_produce= TRUE; failed_to_produce= TRUE;
} }
@ -4676,6 +4641,10 @@ public:
from frm files and storage engine are filled by the function from frm files and storage engine are filled by the function
get_all_tables(). get_all_tables().
@note This function assumes optimize_for_get_all_tables() has been
run for the table and produced a "read plan" in
tables->is_table_read_plan.
@param[in] thd thread handler @param[in] thd thread handler
@param[in] tables I_S table @param[in] tables I_S table
@param[in] cond 'WHERE' condition @param[in] cond 'WHERE' condition
@ -4692,16 +4661,16 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE_LIST table_acl_check; TABLE_LIST table_acl_check;
SELECT_LEX *lsel= tables->schema_select_lex; SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table; ST_SCHEMA_TABLE *schema_table= tables->schema_table;
LOOKUP_FIELD_VALUES lookup_field_vals; IS_table_read_plan *plan= tables->is_table_read_plan;
enum enum_schema_tables schema_table_idx; enum enum_schema_tables schema_table_idx;
Dynamic_array<LEX_STRING*> db_names; Dynamic_array<LEX_STRING*> db_names;
COND *partial_cond= 0; Item *partial_cond= plan->partial_cond;
int error= 1; int error= 1;
Open_tables_backup open_tables_state_backup; Open_tables_backup open_tables_state_backup;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx; Security_context *sctx= thd->security_ctx;
#endif #endif
uint table_open_method; uint table_open_method= tables->table_open_method;
bool can_deadlock; bool can_deadlock;
DBUG_ENTER("get_all_tables"); DBUG_ENTER("get_all_tables");
@ -4725,9 +4694,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
thd->reset_n_backup_open_tables_state(&open_tables_state_backup); thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
schema_table_idx= get_schema_table_idx(schema_table); schema_table_idx= get_schema_table_idx(schema_table);
tables->table_open_method= table_open_method=
get_table_open_method(tables, schema_table, schema_table_idx);
DBUG_PRINT("open_method", ("%d", tables->table_open_method));
/* /*
this branch processes SHOW FIELDS, SHOW INDEXES commands. this branch processes SHOW FIELDS, SHOW INDEXES commands.
see sql_parse.cc, prepare_schema_table() function where see sql_parse.cc, prepare_schema_table() function where
@ -4751,44 +4717,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
goto err; goto err;
} }
if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals)) if (plan->no_rows)
{ {
error= 0; error= 0;
goto err; goto err;
} }
DBUG_PRINT("info",("db_name='%s', table_name='%s'",
lookup_field_vals.db_value.str,
lookup_field_vals.table_value.str));
if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value)
{
/*
if lookup value is empty string then
it's impossible table name or db name
*/
if ((lookup_field_vals.db_value.str &&
!lookup_field_vals.db_value.str[0]) ||
(lookup_field_vals.table_value.str &&
!lookup_field_vals.table_value.str[0]))
{
error= 0;
goto err;
}
}
if (lookup_field_vals.db_value.length &&
!lookup_field_vals.wild_db_value)
tables->has_db_lookup_value= TRUE;
if (lookup_field_vals.table_value.length &&
!lookup_field_vals.wild_table_value)
tables->has_table_lookup_value= TRUE;
if (tables->has_db_lookup_value && tables->has_table_lookup_value)
partial_cond= 0;
else
partial_cond= make_cond_for_info_schema(cond, tables);
if (lex->describe) if (lex->describe)
{ {
/* EXPLAIN SELECT */ /* EXPLAIN SELECT */
@ -4798,7 +4732,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
bzero((char*) &table_acl_check, sizeof(table_acl_check)); bzero((char*) &table_acl_check, sizeof(table_acl_check));
if (make_db_list(thd, &db_names, &lookup_field_vals)) if (make_db_list(thd, &db_names, &plan->lookup_field_vals))
goto err; goto err;
for (size_t i=0; i < db_names.elements(); i++) for (size_t i=0; i < db_names.elements(); i++)
{ {
@ -4814,7 +4748,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{ {
Dynamic_array<LEX_STRING*> table_names; Dynamic_array<LEX_STRING*> table_names;
int res= make_table_name_list(thd, &table_names, lex, int res= make_table_name_list(thd, &table_names, lex,
&lookup_field_vals, db_name); &plan->lookup_field_vals, db_name);
if (res == 2) /* Not fatal error, continue */ if (res == 2) /* Not fatal error, continue */
continue; continue;
if (res) if (res)
@ -4852,8 +4786,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
already created by make_table_name_list() function). already created by make_table_name_list() function).
*/ */
if (!table_open_method && schema_table_idx == SCH_TABLES && if (!table_open_method && schema_table_idx == SCH_TABLES &&
(!lookup_field_vals.table_value.length || (!plan->lookup_field_vals.table_value.length ||
lookup_field_vals.wild_table_value)) plan->lookup_field_vals.wild_table_value))
{ {
table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info); table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
if (schema_table_store_record(thd, table)) if (schema_table_store_record(thd, table))
@ -8030,6 +7964,137 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
} }
/*
Optimize reading from an I_S table.
@detail
This function prepares a plan for populating an I_S table with
get_all_tables().
The plan is in IS_table_read_plan structure, it is saved in
tables->is_table_read_plan.
@return
false - Ok
true - Out Of Memory
*/
static bool optimize_for_get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
enum enum_schema_tables schema_table_idx;
IS_table_read_plan *plan;
DBUG_ENTER("get_all_tables");
if (!(plan= new IS_table_read_plan()))
DBUG_RETURN(1);
tables->is_table_read_plan= plan;
schema_table_idx= get_schema_table_idx(schema_table);
tables->table_open_method= get_table_open_method(tables, schema_table,
schema_table_idx);
DBUG_PRINT("open_method", ("%d", tables->table_open_method));
/*
this branch processes SHOW FIELDS, SHOW INDEXES commands.
see sql_parse.cc, prepare_schema_table() function where
this values are initialized
*/
if (lsel && lsel->table_list.first)
{
/* These do not need to have a query plan */
goto end;
}
if (get_lookup_field_values(thd, cond, tables, &plan->lookup_field_vals))
{
plan->no_rows= true;
goto end;
}
DBUG_PRINT("info",("db_name='%s', table_name='%s'",
plan->lookup_field_vals.db_value.str,
plan->lookup_field_vals.table_value.str));
if (!plan->lookup_field_vals.wild_db_value &&
!plan->lookup_field_vals.wild_table_value)
{
/*
if lookup value is empty string then
it's impossible table name or db name
*/
if ((plan->lookup_field_vals.db_value.str &&
!plan->lookup_field_vals.db_value.str[0]) ||
(plan->lookup_field_vals.table_value.str &&
!plan->lookup_field_vals.table_value.str[0]))
{
plan->no_rows= true;
goto end;
}
}
if (plan->has_db_lookup_value() && plan->has_table_lookup_value())
plan->partial_cond= 0;
else
plan->partial_cond= make_cond_for_info_schema(cond, tables);
end:
DBUG_RETURN(0);
}
/*
This is the optimizer part of get_schema_tables_result().
*/
bool optimize_schema_tables_reads(JOIN *join)
{
THD *thd= join->thd;
bool result= 0;
DBUG_ENTER("optimize_schema_tables_reads");
for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
if (!tab->table || !tab->table->pos_in_table_list)
continue;
TABLE_LIST *table_list= tab->table->pos_in_table_list;
if (table_list->schema_table && thd->fill_information_schema_tables())
{
/* A value of 0 indicates a dummy implementation */
if (table_list->schema_table->fill_table == 0)
continue;
/* skip I_S optimizations specific to get_all_tables */
if (table_list->schema_table->fill_table != get_all_tables)
continue;
Item *cond= tab->select_cond;
if (tab->cache_select && tab->cache_select->cond)
{
/*
If join buffering is used, we should use the condition that is
attached to the join cache. Cache condition has a part of WHERE that
can be checked when we're populating this table.
join_tab->select_cond is of no interest, because it only has
conditions that depend on both this table and previous tables in the
join order.
*/
cond= tab->cache_select->cond;
}
optimize_for_get_all_tables(thd, table_list, cond);
}
}
DBUG_RETURN(result);
}
/* /*
Fill temporary schema tables before SELECT Fill temporary schema tables before SELECT
@ -8038,6 +8103,10 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
join join which use schema tables join join which use schema tables
executed_place place where I_S table processed executed_place place where I_S table processed
SEE ALSO
The optimization part is done by get_schema_tables_result(). This function
is run on query execution.
RETURN RETURN
FALSE success FALSE success
TRUE error TRUE error
@ -8058,7 +8127,7 @@ bool get_schema_tables_result(JOIN *join,
for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
tab; tab;
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{ {
if (!tab->table || !tab->table->pos_in_table_list) if (!tab->table || !tab->table->pos_in_table_list)
break; break;

View File

@ -150,6 +150,69 @@ public:
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();

View File

@ -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,
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,
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,
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,

View File

@ -982,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
This makes the code grep-able, and helps maintenance. This makes the code grep-able, and helps maintenance.
*/ */
%token ABORT_SYM /* INTERNAL (used in lex) */ %token ABORT_SYM /* INTERNAL (used in lex) */
%token ACCESSIBLE_SYM %token ACCESSIBLE_SYM
%token ACTION /* SQL-2003-N */ %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 <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:
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:
| 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; }

View File

@ -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
/* 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