diff options
Diffstat (limited to 'sql/sql_admin.cc')
-rw-r--r-- | sql/sql_admin.cc | 299 |
1 files changed, 209 insertions, 90 deletions
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 7cddf50a896..a3e13fe0e5a 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "sql_class.h" // THD +#include "sql_class.h" // THD and my_global.h #include "keycaches.h" // get_key_cache #include "sql_base.h" // Open_table_context #include "lock.h" // MYSQL_OPEN_* @@ -28,7 +28,9 @@ #include "sql_acl.h" // *_ACL #include "sp.h" // Sroutine_hash_entry #include "sql_parse.h" // check_table_access +#include "strfunc.h" #include "sql_admin.h" +#include "sql_statistics.h" /* Prepare, run and cleanup for mysql_recreate_table() */ @@ -41,9 +43,19 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) trans_rollback(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); + + /* + table_list->table has been closed and freed. Do not reference + uninitialized data. open_tables() could fail. + */ + table_list->table= NULL; + /* Same applies to MDL ticket. */ + table_list->mdl_request.ticket= NULL; + DEBUG_SYNC(thd, "ha_admin_try_alter"); tmp_disable_binlog(thd); // binlogging is done by caller if wanted - result_code= mysql_recreate_table(thd, table_list); + result_code= (open_temporary_tables(thd, table_list) || + mysql_recreate_table(thd, table_list, false)); reenable_binlog(thd); /* mysql_recreate_table() can push OK or ERROR. @@ -51,8 +63,8 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) we will store the error message in a result set row and then clear. */ - if (thd->stmt_da->is_ok()) - thd->stmt_da->reset_diagnostics_area(); + if (thd->get_stmt_da()->is_ok()) + thd->get_stmt_da()->reset_diagnostics_area(); table_list->table= NULL; DBUG_RETURN(result_code); } @@ -87,7 +99,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, const char **ext; MY_STAT stat_info; Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH | - MYSQL_OPEN_FOR_REPAIR | MYSQL_OPEN_HAS_MDL_LOCK | MYSQL_LOCK_IGNORE_TIMEOUT)); DBUG_ENTER("prepare_for_repair"); @@ -97,8 +108,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!(table= table_list->table)) { - char key[MAX_DBKEY_LENGTH]; - uint key_length; /* If the table didn't exist, we have a shared metadata lock on it that is left from mysql_admin_table()'s attempt to @@ -111,32 +120,23 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, Attempt to do full-blown table open in mysql_admin_table() has failed. Let us try to open at least a .FRM for this table. */ - my_hash_value_type hash_value; - key_length= create_table_def_key(thd, key, table_list, 0); table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE, MDL_TRANSACTION); if (lock_table_names(thd, table_list, table_list->next_global, - thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) + thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(0); has_mdl_lock= TRUE; - hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length); - mysql_mutex_lock(&LOCK_open); - share= get_table_share(thd, table_list, key, key_length, 0, - &error, hash_value); - mysql_mutex_unlock(&LOCK_open); + share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE); if (share == NULL) DBUG_RETURN(0); // Can't open frm file if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE)) { - mysql_mutex_lock(&LOCK_open); - release_table_share(share); - mysql_mutex_unlock(&LOCK_open); + tdc_release_share(share); DBUG_RETURN(0); // Out of memory } table= &tmp_table; @@ -198,13 +198,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, to close it, but leave it protected by exclusive metadata lock. */ pos_in_locked_tables= table->pos_in_locked_tables; - if (wait_while_table_is_used(thd, table, - HA_EXTRA_PREPARE_FOR_FORCED_CLOSE, - TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)) + if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_FORCED_CLOSE)) goto end; /* Close table but don't remove from locked list */ close_all_tables_for_name(thd, table_list->table->s, - HA_EXTRA_NOT_USED); + HA_EXTRA_NOT_USED, NULL); table_list->table= 0; } /* @@ -262,11 +260,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, end: thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); if (table == &tmp_table) - { - mysql_mutex_lock(&LOCK_open); closefrm(table, 1); // Free allocated memory - mysql_mutex_unlock(&LOCK_open); - } /* In case of a temporary table there will be no metadata lock. */ if (error && has_mdl_lock) thd->mdl_context.release_transactional_locks(); @@ -325,7 +319,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Protocol *protocol= thd->protocol; LEX *lex= thd->lex; int result_code; + int compl_result_code; bool need_repair_or_alter= 0; + wait_for_commit* suspended_wfc; + DBUG_ENTER("mysql_admin_table"); DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options)); @@ -342,8 +339,23 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); + /* + This function calls trans_commit() during its operation, but that does not + imply that the operation is complete or binlogged. So we have to suspend + temporarily the wakeup_subsequent_commits() calls (if used). + */ + suspended_wfc= thd->suspend_subsequent_commits(); + mysql_ha_rm_tables(thd, tables); + /* + Close all temporary tables which were pre-open to simplify + privilege checking. Clear all references to closed tables. + */ + close_thread_tables(thd); + for (table= tables; table; table= table->next_local) + table->table= NULL; + for (table= tables; table; table= table->next_local) { char table_name[SAFE_NAME_LEN*2+2]; @@ -386,7 +398,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, to differentiate from ALTER TABLE...CHECK PARTITION on which view is not allowed. */ - if (lex->alter_info.flags & ALTER_ADMIN_PARTITION || + if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION || view_operator_func == NULL) { table->required_type=FRMTYPE_TABLE; @@ -417,14 +429,15 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, because it's already known that the table is badly damaged. */ - Warning_info wi(thd->query_id, false); - Warning_info *wi_saved= thd->warning_info; + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info tmp_wi(thd->query_id, false, true); - thd->warning_info= &wi; + da->push_warning_info(&tmp_wi); - open_error= open_and_lock_tables(thd, table, TRUE, 0); + open_error= (open_temporary_tables(thd, table) || + open_and_lock_tables(thd, table, TRUE, 0)); - thd->warning_info= wi_saved; + da->pop_warning_info(); } else { @@ -436,7 +449,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, mode. It does make sense for the user to see such errors. */ - open_error= open_and_lock_tables(thd, table, TRUE, 0); + open_error= (open_temporary_tables(thd, table) || + open_and_lock_tables(thd, table, TRUE, 0)); } thd->prepare_derived_at_open= FALSE; @@ -483,12 +497,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, */ Alter_info *alter_info= &lex->alter_info; - if (alter_info->flags & ALTER_ADMIN_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) { if (!table->table->part_info) { my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); - DBUG_RETURN(TRUE); + goto err2; } if (set_part_state(alter_info, table->table->part_info, PART_ADMIN)) { @@ -547,16 +561,16 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (!table->table) { DBUG_PRINT("admin", ("open table failed")); - if (thd->warning_info->is_empty()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + if (thd->get_stmt_da()->is_warning_info_empty()) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE)); /* if it was a view will check md5 sum */ if (table->view && view_check(thd, table, check_opt) == HA_ADMIN_WRONG_CHECKSUM) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM)); - if (thd->stmt_da->is_error() && - table_not_corrupt_error(thd->stmt_da->sql_errno())) + if (thd->get_stmt_da()->is_error() && + table_not_corrupt_error(thd->get_stmt_da()->sql_errno())) result_code= HA_ADMIN_FAILED; else /* Default failure code is corrupt table */ @@ -604,7 +618,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, table->table=0; // For query cache if (protocol->write()) goto err; - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); continue; /* purecov: end */ } @@ -624,10 +638,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, */ if (lock_type == TL_WRITE && !table->table->s->tmp_table) { - table->table->s->protect_against_usage(); if (wait_while_table_is_used(thd, table->table, - HA_EXTRA_PREPARE_FOR_RENAME, - TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)) + HA_EXTRA_PREPARE_FOR_RENAME)) goto err; DEBUG_SYNC(thd, "after_admin_flush"); /* Flush entries in the query cache involving this table. */ @@ -677,11 +689,105 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } } - DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); - thd_proc_info(thd, "executing"); - result_code = (table->table->file->*operator_func)(thd, check_opt); - thd_proc_info(thd, "Sending data"); - DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); + result_code= compl_result_code= HA_ADMIN_OK; + + if (operator_func == &handler::ha_analyze) + { + TABLE *tab= table->table; + Field **field_ptr= tab->field; + + if (lex->with_persistent_for_clause && + tab->s->table_category != TABLE_CATEGORY_USER) + { + compl_result_code= result_code= HA_ADMIN_INVALID; + } + + if (!lex->column_list) + { + uint fields= 0; + for ( ; *field_ptr; field_ptr++, fields++) ; + bitmap_set_prefix(tab->read_set, fields); + } + else + { + int pos; + LEX_STRING *column_name; + List_iterator_fast<LEX_STRING> it(*lex->column_list); + + bitmap_clear_all(tab->read_set); + while ((column_name= it++)) + { + if (tab->s->fieldnames.type_names == 0 || + (pos= find_type(&tab->s->fieldnames, column_name->str, + column_name->length, 1)) <= 0) + { + compl_result_code= result_code= HA_ADMIN_INVALID; + break; + } + bitmap_set_bit(tab->read_set, pos-1); + } + tab->file->column_bitmaps_signal(); + } + + if (!lex->index_list) + { + tab->keys_in_use_for_query.init(tab->s->keys); + } + else + { + int pos; + LEX_STRING *index_name; + List_iterator_fast<LEX_STRING> it(*lex->index_list); + + tab->keys_in_use_for_query.clear_all(); + while ((index_name= it++)) + { + if (tab->s->keynames.type_names == 0 || + (pos= find_type(&tab->s->keynames, index_name->str, + index_name->length, 1)) <= 0) + { + compl_result_code= result_code= HA_ADMIN_INVALID; + break; + } + tab->keys_in_use_for_query.set_bit(--pos); + } + } + } + + if (result_code == HA_ADMIN_OK) + { + DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); + THD_STAGE_INFO(thd, stage_executing); + result_code = (table->table->file->*operator_func)(thd, check_opt); + THD_STAGE_INFO(thd, stage_sending_data); + DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); + } + + if (compl_result_code == HA_ADMIN_OK && + operator_func == &handler::ha_analyze && + table->table->s->table_category == TABLE_CATEGORY_USER && + (get_use_stat_tables_mode(thd) > NEVER || + lex->with_persistent_for_clause)) + { + if (!(compl_result_code= + alloc_statistics_for_table(thd, table->table)) && + !(compl_result_code= + collect_statistics_for_table(thd, table->table))) + compl_result_code= update_statistics_for_table(thd, table->table); + if (compl_result_code) + result_code= HA_ADMIN_FAILED; + else + { + protocol->prepare_for_resend(); + protocol->store(table_name, system_charset_info); + protocol->store(operator_name, system_charset_info); + protocol->store(STRING_WITH_LEN("status"), system_charset_info); + protocol->store(STRING_WITH_LEN("Engine-independent statistics collected"), + system_charset_info); + if (protocol->write()) + goto err; + } + } if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter) { @@ -696,8 +802,9 @@ send_result: lex->cleanup_after_one_table_open(); thd->clear_error(); // these errors shouldn't get client { - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; while ((err= it++)) { protocol->prepare_for_resend(); @@ -710,7 +817,7 @@ send_result: if (protocol->write()) goto err; } - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); } protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -778,19 +885,10 @@ send_result_message: case HA_ADMIN_TRY_ALTER: { - uint save_flags; Alter_info *alter_info= &lex->alter_info; - /* Store the original value of alter_info->flags */ - save_flags= alter_info->flags; - /* - This is currently used only by InnoDB. ha_innobase::optimize() answers - "try with alter", so here we close the table, do an ALTER TABLE, - reopen the table and do ha_innobase::analyze() on it. - We have to end the row, so analyze could return more rows. - */ protocol->store(STRING_WITH_LEN("note"), system_charset_info); - if(alter_info->flags & ALTER_ADMIN_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) { protocol->store(STRING_WITH_LEN( "Table does not support optimize on partitions. All partitions " @@ -804,17 +902,21 @@ send_result_message: } if (protocol->write()) goto err; - thd_proc_info(thd, "recreating table"); + THD_STAGE_INFO(thd, stage_recreating_table); DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze...")); TABLE_LIST *save_next_local= table->next_local, *save_next_global= table->next_global; table->next_local= table->next_global= 0; - result_code= admin_recreate_table(thd, table); + tmp_disable_binlog(thd); // binlogging is done by caller if wanted + result_code= admin_recreate_table(thd, table); + reenable_binlog(thd); trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); + /* Clear references to TABLE and MDL_ticket after releasing them. */ + table->mdl_request.ticket= NULL; if (!result_code) // recreation went ok { @@ -822,22 +924,27 @@ send_result_message: table->mdl_request.ticket= NULL; DEBUG_SYNC(thd, "ha_admin_open_ltable"); table->mdl_request.set_type(MDL_SHARED_WRITE); - /* - Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags - to force analyze on all partitions. - */ - alter_info->flags &= ~(ALTER_ADMIN_PARTITION); - if ((table->table= open_ltable(thd, table, lock_type, 0))) + if (!open_temporary_tables(thd, table) && + (table->table= open_ltable(thd, table, lock_type, 0))) { + uint save_flags; + /* Store the original value of alter_info->flags */ + save_flags= alter_info->flags; + + /* + Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags + to force analyze on all partitions. + */ + alter_info->flags &= ~(Alter_info::ALTER_ADMIN_PARTITION); result_code= table->table->file->ha_analyze(thd, check_opt); if (result_code == HA_ADMIN_ALREADY_DONE) result_code= HA_ADMIN_OK; else if (result_code) // analyze failed table->table->file->print_error(result_code, MYF(0)); + alter_info->flags= save_flags; } else result_code= -1; // open failed - alter_info->flags= save_flags; } /* Start a new row for the final status row */ protocol->prepare_for_resend(); @@ -845,10 +952,10 @@ send_result_message: protocol->store(operator_name, system_charset_info); if (result_code) // either mysql_recreate_table or analyze failed { - DBUG_ASSERT(thd->is_error() || thd->killed); + DBUG_ASSERT(thd->is_error()); if (thd->is_error()) { - const char *err_msg= thd->stmt_da->message(); + const char *err_msg= thd->get_stmt_da()->message(); if (!thd->vio_ok()) { sql_print_error("%s", err_msg); @@ -867,6 +974,9 @@ send_result_message: } thd->clear_error(); } + /* Make sure this table instance is not reused after the operation. */ + if (table->table) + table->table->m_needs_reopen= true; } result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK; table->next_local= save_next_local; @@ -982,18 +1092,23 @@ send_result_message: } my_eof(thd); + thd->resume_subsequent_commits(suspended_wfc); + DBUG_EXECUTE_IF("inject_analyze_table_sleep", my_sleep(500000);); DBUG_RETURN(FALSE); err: /* Make sure this table instance is not reused after the failure. */ - if (table && table->table) - table->table->m_needs_reopen= true; trans_rollback_stmt(thd); trans_rollback(thd); + if (table && table->table) + { + table->table->m_needs_reopen= true; + table->table= 0; + } close_thread_tables(thd); // Shouldn't be needed thd->mdl_context.release_transactional_locks(); - if (table) - table->table=0; +err2: + thd->resume_subsequent_commits(suspended_wfc); DBUG_RETURN(TRUE); } @@ -1018,7 +1133,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables, KEY_CACHE *key_cache; DBUG_ENTER("mysql_assign_to_keycache"); - thd_proc_info(thd, "Finding key cache"); + THD_STAGE_INFO(thd, stage_finding_key_cache); check_opt.init(); mysql_mutex_lock(&LOCK_global_system_variables); if (!(key_cache= get_key_cache(key_cache_name))) @@ -1067,12 +1182,13 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) } -bool Analyze_table_statement::execute(THD *thd) +bool Sql_cmd_analyze_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; thr_lock_type lock_type = TL_READ_NO_INSERT; - DBUG_ENTER("Analyze_table_statement::execute"); + DBUG_ENTER("Sql_cmd_analyze_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) @@ -1097,12 +1213,13 @@ error: } -bool Check_table_statement::execute(THD *thd) +bool Sql_cmd_check_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; thr_lock_type lock_type = TL_READ_NO_INSERT; bool res= TRUE; - DBUG_ENTER("Check_table_statement::execute"); + DBUG_ENTER("Sql_cmd_check_table::execute"); if (check_table_access(thd, SELECT_ACL, first_table, TRUE, UINT_MAX, FALSE)) @@ -1121,18 +1238,19 @@ error: } -bool Optimize_table_statement::execute(THD *thd) +bool Sql_cmd_optimize_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; - DBUG_ENTER("Optimize_table_statement::execute"); + DBUG_ENTER("Sql_cmd_optimize_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; - res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? - mysql_recreate_table(thd, first_table) : + res= (specialflag & SPECIAL_NO_NEW_FUNC) ? + mysql_recreate_table(thd, first_table, true) : mysql_admin_table(thd, first_table, &m_lex->check_opt, "optimize", TL_WRITE, 1, 0, 0, 0, &handler::ha_optimize, 0); @@ -1152,11 +1270,12 @@ error: } -bool Repair_table_statement::execute(THD *thd) +bool Sql_cmd_repair_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; - DBUG_ENTER("Repair_table_statement::execute"); + DBUG_ENTER("Sql_cmd_repair_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) @@ -1164,7 +1283,7 @@ bool Repair_table_statement::execute(THD *thd) thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, - test(m_lex->check_opt.sql_flags & TT_USEFRM), + MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM), HA_OPEN_FOR_REPAIR, &prepare_for_repair, &handler::ha_repair, &view_repair); |