summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergey Petrunya <psergey@askmonty.org>2012-07-10 21:23:00 +0400
committerSergey Petrunya <psergey@askmonty.org>2012-07-10 21:23:00 +0400
commit725d76e1e844b587eeeab23fb0caa670735e47b6 (patch)
treef7340259144195962f9a718e1d1c983566ece65d /sql
parenta931467e17c4826ce4fa473de0479953d9bbcc59 (diff)
downloadmariadb-git-725d76e1e844b587eeeab23fb0caa670735e47b6.tar.gz
MWL#182: Explain running statements: address review feedback
- switch SHOW EXPLAIN to using an INFORMATION_SCHEMA table.
Diffstat (limited to 'sql')
-rw-r--r--sql/handler.h1
-rw-r--r--sql/my_apc.cc28
-rw-r--r--sql/protocol.h14
-rw-r--r--sql/sql_class.cc82
-rw-r--r--sql/sql_class.h23
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc55
-rw-r--r--sql/sql_show.cc66
-rw-r--r--sql/sql_yacc.yy5
9 files changed, 103 insertions, 174 deletions
diff --git a/sql/handler.h b/sql/handler.h
index ee1731af563..148801f8fe7 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -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,
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
index c7ba25ad3ba..8551c0187a4 100644
--- a/sql/my_apc.cc
+++ b/sql/my_apc.cc
@@ -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.
diff --git a/sql/protocol.h b/sql/protocol.h
index 3627e625c07..1c0a28560bd 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -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; }
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 35cc28bcae7..d6d14c45b47 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -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()
{
diff --git a/sql/sql_class.h b/sql/sql_class.h
index d1183225a83..d34e285ead9 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3319,32 +3319,27 @@ public:
/*
- A select result sink that collects the sent data and then can flush it to
- network when requested.
-
- This class is targeted at collecting EXPLAIN output:
- - Unoptimized data storage (can't handle big datasets)
+ 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
+
+ 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;
};
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 1967a15ef5a..d8316dcc03c 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 9ebb1b3f36e..3a6bb4909f2 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -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;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 2319a13de8c..dc5f32b5728 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -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()
@@ -2042,7 +2034,7 @@ void mysqld_show_explain(THD *thd, const char *calling_user, ulong thread_id)
}
}
mysql_mutex_unlock(&LOCK_thread_count);
-
+
if (tmp)
{
Security_context *tmp_sctx= tmp->security_ctx;
@@ -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,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d60fcbff35e..aed8edab027 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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;
}
;