diff options
author | Sergey Petrunya <psergey@askmonty.org> | 2012-07-10 21:23:00 +0400 |
---|---|---|
committer | Sergey Petrunya <psergey@askmonty.org> | 2012-07-10 21:23:00 +0400 |
commit | 725d76e1e844b587eeeab23fb0caa670735e47b6 (patch) | |
tree | f7340259144195962f9a718e1d1c983566ece65d | |
parent | a931467e17c4826ce4fa473de0479953d9bbcc59 (diff) | |
download | mariadb-git-725d76e1e844b587eeeab23fb0caa670735e47b6.tar.gz |
MWL#182: Explain running statements: address review feedback
- switch SHOW EXPLAIN to using an INFORMATION_SCHEMA table.
-rw-r--r-- | mysql-test/r/show_explain.result | 7 | ||||
-rw-r--r-- | mysql-test/t/show_explain.test | 9 | ||||
-rw-r--r-- | sql/handler.h | 1 | ||||
-rw-r--r-- | sql/my_apc.cc | 28 | ||||
-rw-r--r-- | sql/protocol.h | 14 | ||||
-rw-r--r-- | sql/sql_class.cc | 82 | ||||
-rw-r--r-- | sql/sql_class.h | 23 | ||||
-rw-r--r-- | sql/sql_lex.h | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 55 | ||||
-rw-r--r-- | sql/sql_show.cc | 66 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 5 |
11 files changed, 118 insertions, 175 deletions
diff --git a/mysql-test/r/show_explain.result b/mysql-test/r/show_explain.result index c5891f96e82..60341a5d68b 100644 --- a/mysql-test/r/show_explain.result +++ b/mysql-test/r/show_explain.result @@ -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 diff --git a/mysql-test/t/show_explain.test b/mysql-test/t/show_explain.test index c8d52ad7bb5..2e47a2e9615 100644 --- a/mysql-test/t/show_explain.test +++ b/mysql-test/t/show_explain.test @@ -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%'; + 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; } ; |