Commit 725d76e1 authored by Sergey Petrunya's avatar Sergey Petrunya

MWL#182: Explain running statements: address review feedback

- switch SHOW EXPLAIN to using an INFORMATION_SCHEMA table.
parent a931467e
......@@ -798,7 +798,7 @@ pk data
20 data1
set autocommit=0;
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;
ERROR 70100: Query execution was interrupted
rollback;
......@@ -816,3 +816,8 @@ pk data
20 data1
drop table t1;
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 $thr_default=`select connection_id()`;
--source include/wait_condition.inc
--echo # do: send_eval show explain for $thr2;
--disable_query_log
send_eval show explain for $thr2;
--enable_query_log
# kill the SHOW EXPLAIN command
connection con3;
......@@ -844,3 +847,9 @@ disconnect con2;
## thread and served together.
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_ENGINES,
SCH_EVENTS,
SCH_EXPLAIN,
SCH_FILES,
SCH_GLOBAL_STATUS,
SCH_GLOBAL_VARIABLES,
......
......@@ -24,7 +24,35 @@
/* 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.
......
......@@ -78,20 +78,6 @@ public:
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
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(const char *from, CHARSET_INFO *cs);
String *storage_packet() { return packet; }
......
......@@ -2309,89 +2309,15 @@ int select_send::send_data(List<Item> &items)
DBUG_RETURN(0);
}
//////////////////////////////////////////////////////////////////////////////
/*
Save the data being sent in our internal buffer.
*/
int select_result_explain_buffer::send_data(List<Item> &items)
{
List_iterator_fast<Item> li(items);
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), &my_charset_bin);
DBUG_ENTER("select_send::send_data");
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();
fill_record(thd, dst_table->field, items, TRUE, FALSE);
if ((dst_table->file->ha_write_tmp_row(dst_table->record[0])))
return 1;
return 0;
}
//////////////////////////////////////////////////////////////////////////////
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
network when requested.
This is a select_result_sink which simply writes all data into a (temporary)
table. Creation/deletion of the table is outside of the scope of the class
This class is targeted at collecting EXPLAIN output:
- Unoptimized data storage (can't handle big datasets)
It is aimed at capturing SHOW EXPLAIN output, so:
- 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
unit)
- We don't try to convert the target table to MyISAM
*/
class select_result_explain_buffer : public select_result_sink
{
public:
select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
thd(thd_arg), dst_table(table_arg) {};
THD *thd;
Protocol *protocol;
select_result_explain_buffer(){};
TABLE *dst_table; /* table to write into */
/* The following is called in the child thread: */
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* to_log; /* For PURGE MASTER LOGS TO */
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;
select_result *result;
Item *default_value, *on_update_value;
......
......@@ -2144,6 +2144,32 @@ mysql_execute_command(THD *thd)
execute_show_status(thd, all_tables);
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_TABLES:
case SQLCOM_SHOW_TRIGGERS:
......@@ -3128,35 +3154,6 @@ end_with_restore_list:
thd->security_ctx->priv_user),
lex->verbose);
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:
res= mysqld_show_authors(thd);
break;
......
......@@ -1999,32 +1999,24 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
}
/*
SHOW EXPLAIN FOR command handler
@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
static
const char *target_not_explainable_cmd="Target is not running EXPLAINable command";
@notes
- Attempt to do "SHOW EXPLAIN FOR <myself>" will properly produce "target not
running EXPLAINable command".
/*
Store the SHOW EXPLAIN output in the temporary table.
*/
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;
Protocol *protocol= thd->protocol;
List<Item> field_list;
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;
my_thread_id thread_id;
DBUG_ENTER("fill_show_explain");
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
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");
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;
......@@ -2071,9 +2071,7 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
Show_explain_request explain_req;
select_result_explain_buffer *explain_buf;
explain_buf= new select_result_explain_buffer;
explain_buf->thd=thd;
explain_buf->protocol= thd->protocol;
explain_buf= new select_result_explain_buffer(thd, table->table);
explain_req.explain_buf= explain_buf;
explain_req.target_thd= tmp;
......@@ -2099,29 +2097,22 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
else
{
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
"SHOW EXPLAIN",
"Target is not running EXPLAINable command");
"SHOW EXPLAIN", target_not_explainable_cmd);
}
bres= TRUE;
explain_buf->discard_data();
}
else
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_YES, explain_req.query_str.c_ptr_safe());
}
if (!bres)
{
explain_buf->flush_data();
my_eof(thd);
}
DBUG_RETURN(bres);
}
else
{
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
......@@ -8467,6 +8459,8 @@ ST_SCHEMA_TABLE schema_tables[]=
{"EVENTS", events_fields_info, create_schema_table,
0, make_old_format, 0, -1, -1, 0, 0},
#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,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
......
......@@ -11617,8 +11617,9 @@ show_param:
| describe_command FOR_SYM expr
{
Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
Lex->value_list.empty();
Lex->value_list.push_front($3);
if (prepare_schema_table(YYTHD, Lex, 0, SCH_EXPLAIN))
MYSQL_YYABORT;
Lex->show_explain_for_thread= $3;
}
;
......
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