diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 55f05c3762c..e9ffac39352 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -103,6 +103,7 @@ When one supplies long data for a placeholder: #include "sql_derived.h" // mysql_derived_prepare, // mysql_handle_derived #include "sql_cursor.h" +#include "sql_show.h" #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -1811,6 +1812,42 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } +/** + Validate and prepare for execution CREATE TABLE statement. + + @param stmt prepared statement + @param tables list of tables used in this query + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static int mysql_test_show_create_table(Prepared_statement *stmt, + TABLE_LIST *tables) +{ + DBUG_ENTER("mysql_test_show_create_table"); + THD *thd= stmt->thd; + List fields; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + + if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) + goto err_exit; + + if (send_prep_stmt(stmt, fields.elements) || + thd->protocol->send_result_set_metadata(&fields, Protocol::SEND_EOF) || + thd->protocol->flush()) + goto err_exit; + + DBUG_RETURN(2); + +err_exit: + DBUG_RETURN(1); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2144,7 +2181,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_CREATE_TABLE: res= mysql_test_create_table(stmt); break; - + case SQLCOM_SHOW_CREATE: + res= mysql_test_show_create_table(stmt, tables); + if (res == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index dd7a71f15fa..b2bf5a9ffb0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1091,6 +1091,106 @@ public: }; +/* + Return metadata for CREATE command for table or view + + @param thd Thread handler + @param table_list Table / view + @param field_list resulting list of fields + @param buffer resulting CREATE statement + + @return + @retval 0 OK + @retval 1 Error + +*/ + +bool +mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, + List *field_list, String *buffer) +{ + bool error= TRUE; + MEM_ROOT *mem_root= thd->mem_root; + DBUG_ENTER("mysqld_show_create_get_fields"); + DBUG_PRINT("enter",("db: %s table: %s",table_list->db, + table_list->table_name)); + + /* We want to preserve the tree for views. */ + thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; + + { + /* + Use open_tables() directly rather than + open_normal_and_derived_tables(). This ensures that + close_thread_tables() is not called if open tables fails and the + error is ignored. This allows us to handle broken views nicely. + */ + uint counter; + Show_create_error_handler view_error_suppressor(thd, table_list); + thd->push_internal_handler(&view_error_suppressor); + bool open_error= + open_tables(thd, &table_list, &counter, + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) || + mysql_handle_derived(thd->lex, DT_PREPARE); + thd->pop_internal_handler(); + if (open_error && (thd->killed || thd->is_error())) + goto exit; + } + + /* TODO: add environment variables show when it become possible */ + if (thd->lex->only_view && !table_list->view) + { + my_error(ER_WRONG_OBJECT, MYF(0), + table_list->db, table_list->table_name, "VIEW"); + goto exit; + } + + buffer->length(0); + + if (table_list->view) + buffer->set_charset(table_list->view_creation_ctx->get_client_cs()); + + if ((table_list->view ? + show_create_view(thd, table_list, buffer) : + show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) + goto exit; + + if (table_list->view) + { + field_list->push_back(new (mem_root) + Item_empty_string(thd, "View", NAME_CHAR_LEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Create View", + MY_MAX(buffer->length(),1024)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "character_set_client", + MY_CS_NAME_SIZE), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "collation_connection", + MY_CS_NAME_SIZE), + mem_root); + } + else + { + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Table", NAME_CHAR_LEN), + mem_root); + // 1024 is for not to confuse old clients + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Create Table", + MY_MAX(buffer->length(),1024)), + mem_root); + } + error= FALSE; + +exit: + DBUG_RETURN(error); +} + + /* Return CREATE command for table or view @@ -1125,75 +1225,9 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); - /* We want to preserve the tree for views. */ - thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; - { - /* - Use open_tables() directly rather than - open_normal_and_derived_tables(). This ensures that - close_thread_tables() is not called if open tables fails and the - error is ignored. This allows us to handle broken views nicely. - */ - uint counter; - Show_create_error_handler view_error_suppressor(thd, table_list); - thd->push_internal_handler(&view_error_suppressor); - bool open_error= - open_tables(thd, &table_list, &counter, - MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) || - mysql_handle_derived(thd->lex, DT_PREPARE); - thd->pop_internal_handler(); - if (open_error && (thd->killed || thd->is_error())) - goto exit; - } - - /* TODO: add environment variables show when it become possible */ - if (thd->lex->only_view && !table_list->view) - { - my_error(ER_WRONG_OBJECT, MYF(0), - table_list->db, table_list->table_name, "VIEW"); + if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) goto exit; - } - - buffer.length(0); - - if (table_list->view) - buffer.set_charset(table_list->view_creation_ctx->get_client_cs()); - - if ((table_list->view ? - show_create_view(thd, table_list, &buffer) : - show_create_table(thd, table_list, &buffer, NULL, WITHOUT_DB_NAME))) - goto exit; - - if (table_list->view) - { - field_list.push_back(new (mem_root) - Item_empty_string(thd, "View", NAME_CHAR_LEN), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Create View", - MY_MAX(buffer.length(),1024)), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "character_set_client", - MY_CS_NAME_SIZE), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "collation_connection", - MY_CS_NAME_SIZE), - mem_root); - } - else - { - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Table", NAME_CHAR_LEN), - mem_root); - // 1024 is for not to confuse old clients - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Create Table", - MY_MAX(buffer.length(),1024)), - mem_root); - } if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | diff --git a/sql/sql_show.h b/sql/sql_show.h index 029249f4129..06ff909733a 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -85,6 +85,8 @@ bool append_identifier(THD *thd, String *packet, const char *name, uint length); void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); +bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, + List *field_list, String *buffer); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name, LEX_STRING *orig_db_name, diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index a1a52e832dd..b14b4f4dab9 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -422,6 +422,15 @@ static void test_prepare_simple() mysql_stmt_close(stmt); + /* show create */ + strmov(query, "SHOW CREATE TABLE test_prepare_simple"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + + DIE_UNLESS(mysql_stmt_field_count(stmt) == 2); + + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc);