MWL#182: Explain running statements: address review feedback
- switch SHOW EXPLAIN to using an INFORMATION_SCHEMA table.
This commit is contained in:
parent
a931467e17
commit
725d76e1e8
@ -798,7 +798,7 @@ pk data
|
|||||||
20 data1
|
20 data1
|
||||||
set autocommit=0;
|
set autocommit=0;
|
||||||
select * from t1 where pk between 10 and 20 for update;
|
select * from t1 where pk between 10 and 20 for update;
|
||||||
show explain for 3;
|
# do: send_eval show explain for 3;
|
||||||
kill query $thr_default;
|
kill query $thr_default;
|
||||||
ERROR 70100: Query execution was interrupted
|
ERROR 70100: Query execution was interrupted
|
||||||
rollback;
|
rollback;
|
||||||
@ -816,3 +816,8 @@ pk data
|
|||||||
20 data1
|
20 data1
|
||||||
drop table t1;
|
drop table t1;
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
#
|
||||||
|
# Check that the I_S table is invisible
|
||||||
|
#
|
||||||
|
select table_name from information_schema.tables where table_schema='information_schema' and table_name like '%explain%';
|
||||||
|
table_name
|
||||||
|
@ -818,7 +818,10 @@ connection default;
|
|||||||
let $wait_condition= select State='Sending data' from information_schema.processlist where id=$thr2;
|
let $wait_condition= select State='Sending data' from information_schema.processlist where id=$thr2;
|
||||||
let $thr_default=`select connection_id()`;
|
let $thr_default=`select connection_id()`;
|
||||||
--source include/wait_condition.inc
|
--source include/wait_condition.inc
|
||||||
|
--echo # do: send_eval show explain for $thr2;
|
||||||
|
--disable_query_log
|
||||||
send_eval show explain for $thr2;
|
send_eval show explain for $thr2;
|
||||||
|
--enable_query_log
|
||||||
|
|
||||||
# kill the SHOW EXPLAIN command
|
# kill the SHOW EXPLAIN command
|
||||||
connection con3;
|
connection con3;
|
||||||
@ -844,3 +847,9 @@ disconnect con2;
|
|||||||
## thread and served together.
|
## thread and served together.
|
||||||
|
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Check that the I_S table is invisible
|
||||||
|
--echo #
|
||||||
|
select table_name from information_schema.tables where table_schema='information_schema' and table_name like '%explain%';
|
||||||
|
|
||||||
|
@ -600,6 +600,7 @@ enum enum_schema_tables
|
|||||||
SCH_COLUMN_PRIVILEGES,
|
SCH_COLUMN_PRIVILEGES,
|
||||||
SCH_ENGINES,
|
SCH_ENGINES,
|
||||||
SCH_EVENTS,
|
SCH_EVENTS,
|
||||||
|
SCH_EXPLAIN,
|
||||||
SCH_FILES,
|
SCH_FILES,
|
||||||
SCH_GLOBAL_STATUS,
|
SCH_GLOBAL_STATUS,
|
||||||
SCH_GLOBAL_VARIABLES,
|
SCH_GLOBAL_VARIABLES,
|
||||||
|
@ -24,7 +24,35 @@
|
|||||||
|
|
||||||
/* For standalone testing of APC system, see unittest/sql/my_apc-t.cc */
|
/* For standalone testing of APC system, see unittest/sql/my_apc-t.cc */
|
||||||
|
|
||||||
|
#ifndef MY_APC_STANDALONE
|
||||||
|
|
||||||
|
ST_FIELD_INFO show_explain_fields_info[]=
|
||||||
|
{
|
||||||
|
/* field_name, length, type, value, field_flags, old_name*/
|
||||||
|
{"id", 3, MYSQL_TYPE_LONGLONG, 0 /*value*/, MY_I_S_MAYBE_NULL, "id",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"select_type", 19, MYSQL_TYPE_STRING, 0 /*value*/, 0, "select_type",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"table", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0 /*value*/, MY_I_S_MAYBE_NULL,
|
||||||
|
"table", SKIP_OPEN_TABLE},
|
||||||
|
{"type", 10, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "type", SKIP_OPEN_TABLE},
|
||||||
|
{"possible_keys", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "possible_keys", SKIP_OPEN_TABLE},
|
||||||
|
{"key", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0/*value*/, MY_I_S_MAYBE_NULL,
|
||||||
|
"key", SKIP_OPEN_TABLE},
|
||||||
|
{"key_len", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "key_len", SKIP_OPEN_TABLE},
|
||||||
|
{"ref", NAME_CHAR_LEN*MAX_REF_PARTS, MYSQL_TYPE_STRING, 0/*value*/,
|
||||||
|
MY_I_S_MAYBE_NULL, "ref", SKIP_OPEN_TABLE},
|
||||||
|
{"rows", 10, MYSQL_TYPE_LONGLONG, 0/*value*/, MY_I_S_MAYBE_NULL, "rows",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{"Extra", 255, MYSQL_TYPE_STRING, 0/*value*/, 0 /*flags*/, "Extra",
|
||||||
|
SKIP_OPEN_TABLE},
|
||||||
|
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
/*
|
/*
|
||||||
Initialize the target.
|
Initialize the target.
|
||||||
|
|
||||||
|
@ -78,20 +78,6 @@ public:
|
|||||||
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
|
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
|
||||||
bool send_result_set_row(List<Item> *row_items);
|
bool send_result_set_row(List<Item> *row_items);
|
||||||
|
|
||||||
void get_packet(const char **start, size_t *length)
|
|
||||||
{
|
|
||||||
*start= packet->ptr();
|
|
||||||
*length= packet->length();
|
|
||||||
}
|
|
||||||
void set_packet(const char *start, size_t len)
|
|
||||||
{
|
|
||||||
packet->length(0);
|
|
||||||
packet->append(start, len);
|
|
||||||
#ifndef DBUG_OFF
|
|
||||||
field_pos= field_count - 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool store(I_List<i_string> *str_list);
|
bool store(I_List<i_string> *str_list);
|
||||||
bool store(const char *from, CHARSET_INFO *cs);
|
bool store(const char *from, CHARSET_INFO *cs);
|
||||||
String *storage_packet() { return packet; }
|
String *storage_packet() { return packet; }
|
||||||
|
@ -2309,90 +2309,16 @@ int select_send::send_data(List<Item> &items)
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*
|
|
||||||
Save the data being sent in our internal buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int select_result_explain_buffer::send_data(List<Item> &items)
|
int select_result_explain_buffer::send_data(List<Item> &items)
|
||||||
{
|
{
|
||||||
List_iterator_fast<Item> li(items);
|
fill_record(thd, dst_table->field, items, TRUE, FALSE);
|
||||||
char buff[MAX_FIELD_WIDTH];
|
if ((dst_table->file->ha_write_tmp_row(dst_table->record[0])))
|
||||||
String buffer(buff, sizeof(buff), &my_charset_bin);
|
return 1;
|
||||||
DBUG_ENTER("select_send::send_data");
|
return 0;
|
||||||
|
|
||||||
protocol->prepare_for_resend();
|
|
||||||
Item *item;
|
|
||||||
while ((item=li++))
|
|
||||||
{
|
|
||||||
if (item->send(protocol, &buffer))
|
|
||||||
{
|
|
||||||
protocol->free(); // Free used buffer
|
|
||||||
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Reset buffer to its original state, as it may have been altered in
|
|
||||||
Item::send().
|
|
||||||
*/
|
|
||||||
buffer.set(buff, sizeof(buff), &my_charset_bin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thd->is_error())
|
|
||||||
{
|
|
||||||
protocol->remove_last_row();
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Instead of calling protocol->write(), steal the packed and put it to our
|
|
||||||
buffer
|
|
||||||
*/
|
|
||||||
const char *packet_data;
|
|
||||||
size_t len;
|
|
||||||
protocol->get_packet(&packet_data, &len);
|
|
||||||
|
|
||||||
String *s= new (thd->mem_root) String;
|
|
||||||
s->append(packet_data, len);
|
|
||||||
data_rows.push_back(s);
|
|
||||||
protocol->remove_last_row(); // <-- this does nothing. Do we need it?
|
|
||||||
// prepare_for_resend() will wipe out the packet
|
|
||||||
DBUG_RETURN(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write the saved resultset to the client (via this->protocol) and free it. */
|
|
||||||
|
|
||||||
void select_result_explain_buffer::flush_data()
|
|
||||||
{
|
|
||||||
List_iterator<String> it(data_rows);
|
|
||||||
String *str;
|
|
||||||
while ((str= it++))
|
|
||||||
{
|
|
||||||
protocol->set_packet(str->ptr(), str->length());
|
|
||||||
protocol->write();
|
|
||||||
delete str;
|
|
||||||
}
|
|
||||||
data_rows.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Free the accumulated resultset */
|
|
||||||
|
|
||||||
void select_result_explain_buffer::discard_data()
|
|
||||||
{
|
|
||||||
List_iterator<String> it(data_rows);
|
|
||||||
String *str;
|
|
||||||
while ((str= it++))
|
|
||||||
{
|
|
||||||
delete str;
|
|
||||||
}
|
|
||||||
data_rows.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
bool select_send::send_eof()
|
bool select_send::send_eof()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -3319,32 +3319,27 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A select result sink that collects the sent data and then can flush it to
|
This is a select_result_sink which simply writes all data into a (temporary)
|
||||||
network when requested.
|
table. Creation/deletion of the table is outside of the scope of the class
|
||||||
|
|
||||||
This class is targeted at collecting EXPLAIN output:
|
It is aimed at capturing SHOW EXPLAIN output, so:
|
||||||
- Unoptimized data storage (can't handle big datasets)
|
|
||||||
- Unlike select_result class, we don't assume that the sent data is an
|
- Unlike select_result class, we don't assume that the sent data is an
|
||||||
output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the
|
output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the
|
||||||
unit)
|
unit)
|
||||||
|
- We don't try to convert the target table to MyISAM
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class select_result_explain_buffer : public select_result_sink
|
class select_result_explain_buffer : public select_result_sink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
|
||||||
|
thd(thd_arg), dst_table(table_arg) {};
|
||||||
|
|
||||||
THD *thd;
|
THD *thd;
|
||||||
Protocol *protocol;
|
TABLE *dst_table; /* table to write into */
|
||||||
select_result_explain_buffer(){};
|
|
||||||
|
|
||||||
/* The following is called in the child thread: */
|
/* The following is called in the child thread: */
|
||||||
int send_data(List<Item> &items);
|
int send_data(List<Item> &items);
|
||||||
|
|
||||||
/* this will be called in the parent thread: */
|
|
||||||
void flush_data();
|
|
||||||
|
|
||||||
void discard_data();
|
|
||||||
|
|
||||||
List<String> data_rows;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2357,7 +2357,8 @@ struct LEX: public Query_tables_list
|
|||||||
char *backup_dir; /* For RESTORE/BACKUP */
|
char *backup_dir; /* For RESTORE/BACKUP */
|
||||||
char* to_log; /* For PURGE MASTER LOGS TO */
|
char* to_log; /* For PURGE MASTER LOGS TO */
|
||||||
char* x509_subject,*x509_issuer,*ssl_cipher;
|
char* x509_subject,*x509_issuer,*ssl_cipher;
|
||||||
String *wild;
|
String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
|
||||||
|
Item *show_explain_for_thread; /* id in SHOW EXPLAIN FOR id */
|
||||||
sql_exchange *exchange;
|
sql_exchange *exchange;
|
||||||
select_result *result;
|
select_result *result;
|
||||||
Item *default_value, *on_update_value;
|
Item *default_value, *on_update_value;
|
||||||
|
@ -2144,6 +2144,32 @@ mysql_execute_command(THD *thd)
|
|||||||
execute_show_status(thd, all_tables);
|
execute_show_status(thd, all_tables);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SQLCOM_SHOW_EXPLAIN:
|
||||||
|
{
|
||||||
|
if (!thd->security_ctx->priv_user[0] &&
|
||||||
|
check_global_access(thd,PROCESS_ACL))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The select should use only one table, it's the SHOW EXPLAIN pseudo-table
|
||||||
|
*/
|
||||||
|
if (lex->sroutines.records || lex->query_tables->next_global)
|
||||||
|
{
|
||||||
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
|
||||||
|
"function calls as part of this statement");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item **it= &(lex->show_explain_for_thread);
|
||||||
|
if ((!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) ||
|
||||||
|
(*it)->check_cols(1))
|
||||||
|
{
|
||||||
|
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
|
||||||
|
MYF(0));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/* no break; fall through */
|
||||||
|
}
|
||||||
case SQLCOM_SHOW_DATABASES:
|
case SQLCOM_SHOW_DATABASES:
|
||||||
case SQLCOM_SHOW_TABLES:
|
case SQLCOM_SHOW_TABLES:
|
||||||
case SQLCOM_SHOW_TRIGGERS:
|
case SQLCOM_SHOW_TRIGGERS:
|
||||||
@ -3128,35 +3154,6 @@ end_with_restore_list:
|
|||||||
thd->security_ctx->priv_user),
|
thd->security_ctx->priv_user),
|
||||||
lex->verbose);
|
lex->verbose);
|
||||||
break;
|
break;
|
||||||
case SQLCOM_SHOW_EXPLAIN:
|
|
||||||
{
|
|
||||||
const char *effective_user;
|
|
||||||
/* Same security as SHOW PROCESSLIST (TODO check this) */
|
|
||||||
if (!thd->security_ctx->priv_user[0] &&
|
|
||||||
check_global_access(thd,PROCESS_ACL))
|
|
||||||
break;
|
|
||||||
|
|
||||||
Item *it= (Item *)lex->value_list.head();
|
|
||||||
|
|
||||||
if (lex->table_or_sp_used())
|
|
||||||
{
|
|
||||||
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
|
|
||||||
"function calls as part of this statement");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
|
|
||||||
{
|
|
||||||
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
|
|
||||||
MYF(0));
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
effective_user=(thd->security_ctx->master_access & PROCESS_ACL ? NullS :
|
|
||||||
thd->security_ctx->priv_user);
|
|
||||||
|
|
||||||
mysqld_show_explain(thd, effective_user, (ulong)it->val_int());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SQLCOM_SHOW_AUTHORS:
|
case SQLCOM_SHOW_AUTHORS:
|
||||||
res= mysqld_show_authors(thd);
|
res= mysqld_show_authors(thd);
|
||||||
break;
|
break;
|
||||||
|
@ -1999,32 +1999,24 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
const char *target_not_explainable_cmd="Target is not running EXPLAINable command";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SHOW EXPLAIN FOR command handler
|
Store the SHOW EXPLAIN output in the temporary table.
|
||||||
|
|
||||||
@param thd Current thread's thd
|
|
||||||
@param calling_user User that invoked SHOW EXPLAIN, or NULL if the user
|
|
||||||
has SUPER or PROCESS privileges, and so is allowed
|
|
||||||
to run SHOW EXPLAIN on anybody.
|
|
||||||
@param thread_id Thread whose explain we need
|
|
||||||
|
|
||||||
@notes
|
|
||||||
- Attempt to do "SHOW EXPLAIN FOR <myself>" will properly produce "target not
|
|
||||||
running EXPLAINable command".
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
|
int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
|
||||||
{
|
{
|
||||||
|
const char *calling_user;
|
||||||
THD *tmp;
|
THD *tmp;
|
||||||
Protocol *protocol= thd->protocol;
|
my_thread_id thread_id;
|
||||||
List<Item> field_list;
|
DBUG_ENTER("fill_show_explain");
|
||||||
DBUG_ENTER("mysqld_show_explain");
|
|
||||||
|
|
||||||
thd->make_explain_field_list(field_list);
|
|
||||||
if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
|
|
||||||
Protocol::SEND_EOF))
|
|
||||||
DBUG_VOID_RETURN;
|
|
||||||
|
|
||||||
|
DBUG_ASSERT(cond==NULL);
|
||||||
|
thread_id= thd->lex->show_explain_for_thread->val_int();
|
||||||
|
calling_user= (thd->security_ctx->master_access & PROCESS_ACL) ? NullS :
|
||||||
|
thd->security_ctx->priv_user;
|
||||||
/*
|
/*
|
||||||
Find the thread we need EXPLAIN for. Thread search code was copied from
|
Find the thread we need EXPLAIN for. Thread search code was copied from
|
||||||
kill_one_thread()
|
kill_one_thread()
|
||||||
@ -2058,7 +2050,15 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
|
|||||||
{
|
{
|
||||||
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESSLIST");
|
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESSLIST");
|
||||||
mysql_mutex_unlock(&tmp->LOCK_thd_data);
|
mysql_mutex_unlock(&tmp->LOCK_thd_data);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp == thd)
|
||||||
|
{
|
||||||
|
mysql_mutex_unlock(&tmp->LOCK_thd_data);
|
||||||
|
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), "SHOW EXPLAIN",
|
||||||
|
target_not_explainable_cmd);
|
||||||
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bres;
|
bool bres;
|
||||||
@ -2071,9 +2071,7 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
|
|||||||
Show_explain_request explain_req;
|
Show_explain_request explain_req;
|
||||||
select_result_explain_buffer *explain_buf;
|
select_result_explain_buffer *explain_buf;
|
||||||
|
|
||||||
explain_buf= new select_result_explain_buffer;
|
explain_buf= new select_result_explain_buffer(thd, table->table);
|
||||||
explain_buf->thd=thd;
|
|
||||||
explain_buf->protocol= thd->protocol;
|
|
||||||
|
|
||||||
explain_req.explain_buf= explain_buf;
|
explain_req.explain_buf= explain_buf;
|
||||||
explain_req.target_thd= tmp;
|
explain_req.target_thd= tmp;
|
||||||
@ -2099,29 +2097,22 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
|
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
|
||||||
"SHOW EXPLAIN",
|
"SHOW EXPLAIN", target_not_explainable_cmd);
|
||||||
"Target is not running EXPLAINable command");
|
|
||||||
}
|
}
|
||||||
bres= TRUE;
|
bres= TRUE;
|
||||||
explain_buf->discard_data();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||||
ER_YES, explain_req.query_str.c_ptr_safe());
|
ER_YES, explain_req.query_str.c_ptr_safe());
|
||||||
}
|
}
|
||||||
if (!bres)
|
DBUG_RETURN(bres);
|
||||||
{
|
|
||||||
explain_buf->flush_data();
|
|
||||||
my_eof(thd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
|
my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
|
||||||
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_VOID_RETURN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8436,6 +8427,7 @@ ST_FIELD_INFO keycache_fields_info[]=
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern ST_FIELD_INFO show_explain_fields_info[];
|
||||||
/*
|
/*
|
||||||
Description of ST_FIELD_INFO in table.h
|
Description of ST_FIELD_INFO in table.h
|
||||||
|
|
||||||
@ -8467,6 +8459,8 @@ ST_SCHEMA_TABLE schema_tables[]=
|
|||||||
{"EVENTS", events_fields_info, create_schema_table,
|
{"EVENTS", events_fields_info, create_schema_table,
|
||||||
0, make_old_format, 0, -1, -1, 0, 0},
|
0, make_old_format, 0, -1, -1, 0, 0},
|
||||||
#endif
|
#endif
|
||||||
|
{"EXPLAIN", show_explain_fields_info, create_schema_table, fill_show_explain,
|
||||||
|
make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
|
||||||
{"FILES", files_fields_info, create_schema_table,
|
{"FILES", files_fields_info, create_schema_table,
|
||||||
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
|
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
|
||||||
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
|
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
|
||||||
|
@ -11617,8 +11617,9 @@ show_param:
|
|||||||
| describe_command FOR_SYM expr
|
| describe_command FOR_SYM expr
|
||||||
{
|
{
|
||||||
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
|
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
|
||||||
Lex->value_list.empty();
|
if (prepare_schema_table(YYTHD, Lex, 0, SCH_EXPLAIN))
|
||||||
Lex->value_list.push_front($3);
|
MYSQL_YYABORT;
|
||||||
|
Lex->show_explain_for_thread= $3;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user