summaryrefslogtreecommitdiff
path: root/sql/sql_admin.cc
diff options
context:
space:
mode:
authorJon Olav Hauglid <jon.hauglid@oracle.com>2010-08-18 13:29:04 +0200
committerJon Olav Hauglid <jon.hauglid@oracle.com>2010-08-18 13:29:04 +0200
commit5139bf6c8f2cde4b598aa0e822f126936346f914 (patch)
treeb38d061771d52ab55fe9d428e45627d4c08d5f63 /sql/sql_admin.cc
parentbd75361929caadac565d471d7e374060ff93794d (diff)
parent8977575cff0ed295692e866b1cd543bdf49c0a20 (diff)
downloadmariadb-git-5139bf6c8f2cde4b598aa0e822f126936346f914.tar.gz
Manual merge from mysql-5.5-bugfixing to mysql-5.5-runtime.
Diffstat (limited to 'sql/sql_admin.cc')
-rw-r--r--sql/sql_admin.cc1001
1 files changed, 1001 insertions, 0 deletions
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
new file mode 100644
index 00000000000..1f96f1cf0e4
--- /dev/null
+++ b/sql/sql_admin.cc
@@ -0,0 +1,1001 @@
+/* Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ 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 "keycaches.h" // get_key_cache
+#include "sql_base.h" // Open_table_context
+#include "lock.h" // MYSQL_OPEN_*
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "partition_element.h" // PART_ADMIN
+#include "sql_partition.h" // set_part_state
+#include "transaction.h" // trans_rollback_stmt
+#include "sql_view.h" // view_checksum
+#include "sql_table.h" // mysql_recreate_table
+#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_acl.h" // *_ACL
+#include "sp.h" // Sroutine_hash_entry
+#include "sql_parse.h" // check_table_access
+#include "sql_admin.h"
+
+static int send_check_errmsg(THD *thd, TABLE_LIST* table,
+ const char* operator_name, const char* errmsg)
+
+{
+ Protocol *protocol= thd->protocol;
+ protocol->prepare_for_resend();
+ protocol->store(table->alias, system_charset_info);
+ protocol->store((char*) operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(errmsg, system_charset_info);
+ thd->clear_error();
+ if (protocol->write())
+ return -1;
+ return 1;
+}
+
+
+static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
+ HA_CHECK_OPT *check_opt)
+{
+ int error= 0;
+ TABLE tmp_table, *table;
+ TABLE_SHARE *share;
+ bool has_mdl_lock= FALSE;
+ char from[FN_REFLEN],tmp[FN_REFLEN+32];
+ const char **ext;
+ MY_STAT stat_info;
+ Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_HAS_MDL_LOCK |
+ MYSQL_LOCK_IGNORE_TIMEOUT));
+ DBUG_ENTER("prepare_for_repair");
+
+ if (!(check_opt->sql_flags & TT_USEFRM))
+ DBUG_RETURN(0);
+
+ 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
+ open it. Release the shared metadata lock before trying to
+ acquire the exclusive lock to satisfy MDL asserts and avoid
+ deadlocks.
+ */
+ thd->mdl_context.release_transactional_locks();
+ /*
+ 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);
+
+ if (lock_table_names(thd, table_list, table_list->next_global,
+ thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ 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);
+ 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);
+ DBUG_RETURN(0); // Out of memory
+ }
+ table= &tmp_table;
+ }
+
+ /* A MERGE table must not come here. */
+ DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
+
+ /*
+ REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
+ */
+ if (table->s->tmp_table)
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Cannot repair temporary table from .frm file");
+ goto end;
+ }
+
+ /*
+ User gave us USE_FRM which means that the header in the index file is
+ trashed.
+ In this case we will try to fix the table the following way:
+ - Rename the data file to a temporary name
+ - Truncate the table
+ - Replace the new data file with the old one
+ - Run a normal repair using the new index file and the old data file
+ */
+
+ if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed repairing incompatible .frm file");
+ goto end;
+ }
+
+ /*
+ Check if this is a table type that stores index and data separately,
+ like ISAM or MyISAM. We assume fixed order of engine file name
+ extentions array. First element of engine file name extentions array
+ is meta/index file extention. Second element - data file extention.
+ */
+ ext= table->file->bas_ext();
+ if (!ext[0] || !ext[1])
+ goto end; // No data file
+
+ // Name of data file
+ strxmov(from, table->s->normalized_path.str, ext[1], NullS);
+ if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
+ goto end; // Can't use USE_FRM flag
+
+ my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
+ from, current_pid, thd->thread_id);
+
+ if (table_list->table)
+ {
+ /*
+ Table was successfully open in mysql_admin_table(). Now we need
+ to close it, but leave it protected by exclusive metadata lock.
+ */
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ goto end;
+ close_all_tables_for_name(thd, table_list->table->s, FALSE);
+ table_list->table= 0;
+ }
+ /*
+ After this point we have an exclusive metadata lock on our table
+ in both cases when table was successfully open in mysql_admin_table()
+ and when it was open in prepare_for_repair().
+ */
+
+ if (my_rename(from, tmp, MYF(MY_WME)))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed renaming data file");
+ goto end;
+ }
+ if (dd_recreate_table(thd, table_list->db, table_list->table_name))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed generating table from .frm file");
+ goto end;
+ }
+ /*
+ 'FALSE' for 'using_transactions' means don't postpone
+ invalidation till the end of a transaction, but do it
+ immediately.
+ */
+ query_cache_invalidate3(thd, table_list, FALSE);
+ if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed restoring .MYD file");
+ goto end;
+ }
+
+ if (thd->locked_tables_list.reopen_tables(thd))
+ goto end;
+
+ /*
+ Now we should be able to open the partially repaired table
+ to finish the repair in the handler later on.
+ */
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed to open partially repaired table");
+ goto end;
+ }
+
+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();
+
+ DBUG_RETURN(error);
+}
+
+
+
+/*
+ RETURN VALUES
+ FALSE Message sent to net (admin operation went ok)
+ TRUE Message should be sent by caller
+ (admin operation or network communication failed)
+*/
+static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
+ HA_CHECK_OPT* check_opt,
+ const char *operator_name,
+ thr_lock_type lock_type,
+ bool open_for_modify,
+ bool no_warnings_for_error,
+ uint extra_open_options,
+ int (*prepare_func)(THD *, TABLE_LIST *,
+ HA_CHECK_OPT *),
+ int (handler::*operator_func)(THD *,
+ HA_CHECK_OPT *),
+ int (view_operator_func)(THD *, TABLE_LIST*))
+{
+ TABLE_LIST *table;
+ SELECT_LEX *select= &thd->lex->select_lex;
+ List<Item> field_list;
+ Item *item;
+ Protocol *protocol= thd->protocol;
+ LEX *lex= thd->lex;
+ int result_code;
+ DBUG_ENTER("mysql_admin_table");
+
+ field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Op", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_type", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_text", 255));
+ item->maybe_null = 1;
+ if (protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ mysql_ha_rm_tables(thd, tables);
+
+ for (table= tables; table; table= table->next_local)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = table->db;
+ bool fatal_error=0;
+ bool open_error;
+
+ DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
+ DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
+ strxmov(table_name, db, ".", table->table_name, NullS);
+ thd->open_options|= extra_open_options;
+ table->lock_type= lock_type;
+ /*
+ To make code safe for re-execution we need to reset type of MDL
+ request as code below may change it.
+ To allow concurrent execution of read-only operations we acquire
+ weak metadata lock for them.
+ */
+ table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
+ /* open only one table from local list of command */
+ {
+ TABLE_LIST *save_next_global, *save_next_local;
+ save_next_global= table->next_global;
+ table->next_global= 0;
+ save_next_local= table->next_local;
+ table->next_local= 0;
+ select->table_list.first= table;
+ /*
+ Time zone tables and SP tables can be add to lex->query_tables list,
+ so it have to be prepared.
+ TODO: Investigate if we can put extra tables into argument instead of
+ using lex->query_tables
+ */
+ lex->query_tables= table;
+ lex->query_tables_last= &table->next_global;
+ lex->query_tables_own_last= 0;
+ thd->no_warnings_for_error= no_warnings_for_error;
+ if (view_operator_func == NULL)
+ table->required_type=FRMTYPE_TABLE;
+
+ open_error= open_and_lock_tables(thd, table, TRUE, 0);
+ thd->no_warnings_for_error= 0;
+ table->next_global= save_next_global;
+ table->next_local= save_next_local;
+ thd->open_options&= ~extra_open_options;
+ /*
+ Under locked tables, we know that the table can be opened,
+ so any errors opening the table are logical errors.
+ In these cases it does not make sense to try to repair.
+ */
+ if (open_error && thd->locked_tables_mode)
+ {
+ result_code= HA_ADMIN_FAILED;
+ goto send_result;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->table)
+ {
+ /*
+ Set up which partitions that should be processed
+ if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
+ CACHE INDEX/LOAD INDEX for specified partitions
+ */
+ Alter_info *alter_info= &lex->alter_info;
+
+ if (alter_info->flags & ALTER_ADMIN_PARTITION)
+ {
+ if (!table->table->part_info)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ uint num_parts_found;
+ uint num_parts_opt= alter_info->partition_names.elements;
+ num_parts_found= set_part_state(alter_info, table->table->part_info,
+ PART_ADMIN);
+ if (num_parts_found != num_parts_opt &&
+ (!(alter_info->flags & ALTER_ALL_PARTITION)))
+ {
+ char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
+ size_t length;
+ DBUG_PRINT("admin", ("sending non existent partition error"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ length= my_snprintf(buff, sizeof(buff),
+ ER(ER_DROP_PARTITION_NON_EXISTENT),
+ table_name);
+ protocol->store(buff, length, system_charset_info);
+ if(protocol->write())
+ goto err;
+ my_eof(thd);
+ goto err;
+ }
+ }
+ }
+#endif
+ }
+ DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
+
+ if (prepare_func)
+ {
+ DBUG_PRINT("admin", ("calling prepare_func"));
+ switch ((*prepare_func)(thd, table, check_opt)) {
+ case 1: // error, message written to net
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ DBUG_PRINT("admin", ("simple error, admin next table"));
+ continue;
+ case -1: // error, message could be written to net
+ /* purecov: begin inspected */
+ DBUG_PRINT("admin", ("severe error, stop"));
+ goto err;
+ /* purecov: end */
+ default: // should be 0 otherwise
+ DBUG_PRINT("admin", ("prepare_func succeeded"));
+ ;
+ }
+ }
+
+ /*
+ CHECK TABLE command is only command where VIEW allowed here and this
+ command use only temporary teble method for VIEWs resolving => there
+ can't be VIEW tree substitition of join view => if opening table
+ succeed then table->table will have real TABLE pointer as value (in
+ case of join view substitution table->table can be 0, but here it is
+ impossible)
+ */
+ if (!table->table)
+ {
+ DBUG_PRINT("admin", ("open table failed"));
+ if (thd->warning_info->is_empty())
+ push_warning(thd, MYSQL_ERROR::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_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
+ if (thd->stmt_da->is_error() &&
+ (thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE ||
+ thd->stmt_da->sql_errno() == ER_FILE_NOT_FOUND))
+ /* A missing table is just issued as a failed command */
+ result_code= HA_ADMIN_FAILED;
+ else
+ /* Default failure code is corrupt table */
+ result_code= HA_ADMIN_CORRUPT;
+ goto send_result;
+ }
+
+ if (table->view)
+ {
+ DBUG_PRINT("admin", ("calling view_operator_func"));
+ result_code= (*view_operator_func)(thd, table);
+ goto send_result;
+ }
+
+ if (table->schema_table)
+ {
+ result_code= HA_ADMIN_NOT_IMPLEMENTED;
+ goto send_result;
+ }
+
+ if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
+ {
+ /* purecov: begin inspected */
+ char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
+ size_t length;
+ enum_sql_command save_sql_command= lex->sql_command;
+ DBUG_PRINT("admin", ("sending error message"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
+ table_name);
+ protocol->store(buff, length, system_charset_info);
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ lex->reset_query_tables_list(FALSE);
+ /*
+ Restore Query_tables_list::sql_command value to make statement
+ safe for re-execution.
+ */
+ lex->sql_command= save_sql_command;
+ table->table=0; // For query cache
+ if (protocol->write())
+ goto err;
+ thd->stmt_da->reset_diagnostics_area();
+ continue;
+ /* purecov: end */
+ }
+
+ /*
+ Close all instances of the table to allow MyISAM "repair"
+ to rename files.
+ @todo: This code does not close all instances of the table.
+ It only closes instances in other connections, but if this
+ connection has LOCK TABLE t1 a READ, t1 b WRITE,
+ both t1 instances will be kept open.
+ There is no need to execute this branch for InnoDB, which does
+ repair by recreate. There is no need to do it for OPTIMIZE,
+ which doesn't move files around.
+ Hence, this code should be moved to prepare_for_repair(),
+ and executed only for MyISAM engine.
+ */
+ if (lock_type == TL_WRITE && !table->table->s->tmp_table)
+ {
+ if (wait_while_table_is_used(thd, table->table,
+ HA_EXTRA_PREPARE_FOR_RENAME))
+ goto err;
+ DEBUG_SYNC(thd, "after_admin_flush");
+ /* Flush entries in the query cache involving this table. */
+ query_cache_invalidate3(thd, table->table, 0);
+ /*
+ XXX: hack: switch off open_for_modify to skip the
+ flush that is made later in the execution flow.
+ */
+ open_for_modify= 0;
+ }
+
+ if (table->table->s->crashed && operator_func == &handler::ha_check)
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("admin", ("sending crashed warning"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ /* purecov: end */
+ }
+
+ if (operator_func == &handler::ha_repair &&
+ !(check_opt->sql_flags & TT_USEFRM))
+ {
+ if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
+ (table->table->file->ha_check_for_upgrade(check_opt) ==
+ HA_ADMIN_NEEDS_ALTER))
+ {
+ DBUG_PRINT("admin", ("recreating table"));
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ tmp_disable_binlog(thd); // binlogging is done by caller if wanted
+ result_code= mysql_recreate_table(thd, table);
+ reenable_binlog(thd);
+ /*
+ mysql_recreate_table() can push OK or ERROR.
+ Clear 'OK' status. If there is an error, keep it:
+ 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();
+ table->table= NULL;
+ result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
+ goto send_result;
+ }
+ }
+
+ DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
+ result_code = (table->table->file->*operator_func)(thd, check_opt);
+ DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
+
+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;
+ while ((err= it++))
+ {
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store((char*) operator_name, system_charset_info);
+ protocol->store(warning_level_names[err->get_level()].str,
+ warning_level_names[err->get_level()].length,
+ system_charset_info);
+ protocol->store(err->get_message_text(), system_charset_info);
+ if (protocol->write())
+ goto err;
+ }
+ thd->warning_info->clear_warning_info(thd->query_id);
+ }
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+
+send_result_message:
+
+ DBUG_PRINT("info", ("result_code: %d", result_code));
+ switch (result_code) {
+ case HA_ADMIN_NOT_IMPLEMENTED:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length=my_snprintf(buf, sizeof(buf),
+ ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ }
+ break;
+
+ case HA_ADMIN_NOT_BASE_TABLE:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length= my_snprintf(buf, sizeof(buf),
+ ER(ER_BAD_TABLE_ERROR), table_name);
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ }
+ break;
+
+ case HA_ADMIN_OK:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
+ break;
+
+ case HA_ADMIN_FAILED:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Operation failed"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_REJECT:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Operation need committed state"),
+ system_charset_info);
+ open_for_modify= FALSE;
+ break;
+
+ case HA_ADMIN_ALREADY_DONE:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Table is already up to date"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_CORRUPT:
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
+ fatal_error=1;
+ break;
+
+ case HA_ADMIN_INVALID:
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Invalid argument"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_TRY_ALTER:
+ {
+ /*
+ 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.
+ */
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ DEBUG_SYNC(thd, "ha_admin_try_alter");
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(STRING_WITH_LEN(
+ "Table does not support optimize, doing recreate + analyze instead"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ 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;
+ tmp_disable_binlog(thd); // binlogging is done by caller if wanted
+ result_code= mysql_recreate_table(thd, table);
+ reenable_binlog(thd);
+ /*
+ mysql_recreate_table() can push OK or ERROR.
+ Clear 'OK' status. If there is an error, keep it:
+ 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();
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ table->table= NULL;
+ if (!result_code) // recreation went ok
+ {
+ /* Clear the ticket released above. */
+ table->mdl_request.ticket= NULL;
+ DEBUG_SYNC(thd, "ha_admin_open_ltable");
+ table->mdl_request.set_type(MDL_SHARED_WRITE);
+ if ((table->table= open_ltable(thd, table, lock_type, 0)))
+ {
+ 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));
+ }
+ else
+ result_code= -1; // open failed
+ }
+ /* Start a new row for the final status row */
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ if (result_code) // either mysql_recreate_table or analyze failed
+ {
+ DBUG_ASSERT(thd->is_error());
+ if (thd->is_error())
+ {
+ const char *err_msg= thd->stmt_da->message();
+ if (!thd->vio_ok())
+ {
+ sql_print_error("%s", err_msg);
+ }
+ else
+ {
+ /* Hijack the row already in-progress. */
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(err_msg, system_charset_info);
+ if (protocol->write())
+ goto err;
+ /* Start off another row for HA_ADMIN_FAILED */
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ }
+ thd->clear_error();
+ }
+ }
+ result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
+ table->next_local= save_next_local;
+ table->next_global= save_next_global;
+ goto send_result_message;
+ }
+ case HA_ADMIN_WRONG_CHECKSUM:
+ {
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
+ system_charset_info);
+ break;
+ }
+
+ case HA_ADMIN_NEEDS_UPGRADE:
+ case HA_ADMIN_NEEDS_ALTER:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length;
+
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
+ table->table_name);
+ protocol->store(buf, length, system_charset_info);
+ fatal_error=1;
+ break;
+ }
+
+ default: // Probably HA_ADMIN_INTERNAL_ERROR
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length=my_snprintf(buf, sizeof(buf),
+ "Unknown - internal error %d during operation",
+ result_code);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ fatal_error=1;
+ break;
+ }
+ }
+ if (table->table)
+ {
+ if (table->table->s->tmp_table)
+ {
+ /*
+ If the table was not opened successfully, do not try to get
+ status information. (Bug#47633)
+ */
+ if (open_for_modify && !open_error)
+ table->table->file->info(HA_STATUS_CONST);
+ }
+ else if (open_for_modify || fatal_error)
+ {
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table->db, table->table_name, FALSE);
+ /*
+ May be something modified. Consequently, we have to
+ invalidate the query cache.
+ */
+ table->table= 0; // For query cache
+ query_cache_invalidate3(thd, table, 0);
+ }
+ }
+ /* Error path, a admin command failed. */
+ trans_commit_stmt(thd);
+ trans_commit_implicit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+
+ /*
+ If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
+ separate open_tables() for each CHECK TABLE argument.
+ Right now we do not have a separate method to reset the prelocking
+ state in the lex to the state after parsing, so each open will pollute
+ this state: add elements to lex->srotuines_list, TABLE_LISTs to
+ lex->query_tables. Below is a lame attempt to recover from this
+ pollution.
+ @todo: have a method to reset a prelocking context, or use separate
+ contexts for each open.
+ */
+ for (Sroutine_hash_entry *rt=
+ (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
+ rt; rt= rt->next)
+ rt->mdl_request.ticket= NULL;
+
+ if (protocol->write())
+ goto err;
+ }
+
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+
+err:
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd); // Shouldn't be needed
+ thd->mdl_context.release_transactional_locks();
+ if (table)
+ table->table=0;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Assigned specified indexes for a table into key cache
+
+ SYNOPSIS
+ mysql_assign_to_keycache()
+ thd Thread object
+ tables Table list (one table only)
+
+ RETURN VALUES
+ FALSE ok
+ TRUE error
+*/
+
+bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
+ LEX_STRING *key_cache_name)
+{
+ HA_CHECK_OPT check_opt;
+ KEY_CACHE *key_cache;
+ DBUG_ENTER("mysql_assign_to_keycache");
+
+ check_opt.init();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (!(key_cache= get_key_cache(key_cache_name)))
+ {
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
+ DBUG_RETURN(TRUE);
+ }
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ check_opt.key_cache= key_cache;
+ DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
+ "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
+ 0, 0, &handler::assign_to_keycache, 0));
+}
+
+
+/*
+ Preload specified indexes for a table into key cache
+
+ SYNOPSIS
+ mysql_preload_keys()
+ thd Thread object
+ tables Table list (one table only)
+
+ RETURN VALUES
+ FALSE ok
+ TRUE error
+*/
+
+bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
+{
+ DBUG_ENTER("mysql_preload_keys");
+ /*
+ We cannot allow concurrent inserts. The storage engine reads
+ directly from the index file, bypassing the cache. It could read
+ outdated information if parallel inserts into cache blocks happen.
+ */
+ DBUG_RETURN(mysql_admin_table(thd, tables, 0,
+ "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
+ &handler::preload_keys, 0));
+}
+
+
+bool Analyze_table_statement::execute(THD *thd)
+{
+ 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");
+
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
+ FALSE, UINT_MAX, FALSE))
+ goto error;
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
+ "analyze", lock_type, 1, 0, 0, 0,
+ &handler::ha_analyze, 0);
+ /* ! we write after unlocking the table */
+ if (!res && !m_lex->no_write_to_binlog)
+ {
+ /*
+ Presumably, ANALYZE and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Check_table_statement::execute(THD *thd)
+{
+ 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");
+
+ if (check_table_access(thd, SELECT_ACL, first_table,
+ TRUE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+
+ res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check",
+ lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
+ &handler::ha_check, &view_checksum);
+
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Optimize_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ bool res= TRUE;
+ DBUG_ENTER("Optimize_table_statement::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) :
+ mysql_admin_table(thd, first_table, &m_lex->check_opt,
+ "optimize", TL_WRITE, 1, 0, 0, 0,
+ &handler::ha_optimize, 0);
+ /* ! we write after unlocking the table */
+ if (!res && !m_lex->no_write_to_binlog)
+ {
+ /*
+ Presumably, OPTIMIZE and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Repair_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ bool res= TRUE;
+ DBUG_ENTER("Repair_table_statement::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= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
+ TL_WRITE, 1,
+ test(m_lex->check_opt.sql_flags & TT_USEFRM),
+ HA_OPEN_FOR_REPAIR, &prepare_for_repair,
+ &handler::ha_repair, 0);
+
+ /* ! we write after unlocking the table */
+ if (!res && !m_lex->no_write_to_binlog)
+ {
+ /*
+ Presumably, REPAIR and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}