You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1062 lines
37 KiB

Patch that refactors global read lock implementation and fixes bug #57006 "Deadlock between HANDLER and FLUSH TABLES WITH READ LOCK" and bug #54673 "It takes too long to get readlock for 'FLUSH TABLES WITH READ LOCK'". The first bug manifested itself as a deadlock which occurred when a connection, which had some table open through HANDLER statement, tried to update some data through DML statement while another connection tried to execute FLUSH TABLES WITH READ LOCK concurrently. What happened was that FTWRL in the second connection managed to perform first step of GRL acquisition and thus blocked all upcoming DML. After that it started to wait for table open through HANDLER statement to be flushed. When the first connection tried to execute DML it has started to wait for GRL/the second connection creating deadlock. The second bug manifested itself as starvation of FLUSH TABLES WITH READ LOCK statements in cases when there was a constant stream of concurrent DML statements (in two or more connections). This has happened because requests for protection against GRL which were acquired by DML statements were ignoring presence of pending GRL and thus the latter was starved. This patch solves both these problems by re-implementing GRL using metadata locks. Similar to the old implementation acquisition of GRL in new implementation is two-step. During the first step we block all concurrent DML and DDL statements by acquiring global S metadata lock (each DML and DDL statement acquires global IX lock for its duration). During the second step we block commits by acquiring global S lock in COMMIT namespace (commit code acquires global IX lock in this namespace). Note that unlike in old implementation acquisition of protection against GRL in DML and DDL is semi-automatic. We assume that any statement which should be blocked by GRL will either open and acquires write-lock on tables or acquires metadata locks on objects it is going to modify. For any such statement global IX metadata lock is automatically acquired for its duration. The first problem is solved because waits for GRL become visible to deadlock detector in metadata locking subsystem and thus deadlocks like one in the first bug become impossible. The second problem is solved because global S locks which are used for GRL implementation are given preference over IX locks which are acquired by concurrent DML (and we can switch to fair scheduling in future if needed). Important change: FTWRL/GRL no longer blocks DML and DDL on temporary tables. Before this patch behavior was not consistent in this respect: in some cases DML/DDL statements on temporary tables were blocked while in others they were not. Since the main use cases for FTWRL are various forms of backups and temporary tables are not preserved during backups we have opted for consistently allowing DML/DDL on temporary tables during FTWRL/GRL. Important change: This patch changes thread state names which are used when DML/DDL of FTWRL is waiting for global read lock. It is now either "Waiting for global read lock" or "Waiting for commit lock" depending on the stage on which FTWRL is. Incompatible change: To solve deadlock in events code which was exposed by this patch we have to replace LOCK_event_metadata mutex with metadata locks on events. As result we have to prohibit DDL on events under LOCK TABLES. This patch also adds extensive test coverage for interaction of DML/DDL and FTWRL. Performance of new and old global read lock implementations in sysbench tests were compared. There were no significant difference between new and old implementations.
15 years ago
Bug #56494 Segfault in upgrade_shared_lock_to_exclusive() for REPAIR of merge table Bug #56422 CHECK TABLE run when the table is locked reports corruption along with timeout The crash happened if a table maintenance statement (ANALYZE TABLE, REPAIR TABLE, etc.) was executed on a MERGE table and opening and locking a child table failed. This could for example happen if a child table did not exist or if a lock timeout happened while waiting for a conflicting metadata lock to disappear. Since opening and locking the MERGE table and its children failed, the tables would be closed and the metadata locks released. However, TABLE_LIST::table for the MERGE table would still be set, with its value invalid since the tables had been closed. This caused the table maintenance statement to try to continue and upgrade the metadata lock on the MERGE table. But since the lock already had been released, this caused a segfault. This patch fixes the problem by setting TABLE_LIST::table to NULL if open_and_lock_tables() fails. This prevents maintenance statements from continuing and trying to upgrade the metadata lock. The patch includes a 5.5 version of the fix for Bug #46339 crash on REPAIR TABLE merge table USE_FRM. This bug caused REPAIR TABLE ... USE_FRM to give an assert when used on merge tables. The patch also enables the CHECK TABLE statement for log tables. Before, CHECK TABLE for log tables gave ER_CANT_LOCK_LOG_TABLE, yet still counted the statement as successfully executed. With the changes to table maintenance statement error handling in this patch, CHECK TABLE would no longer be considered as successful in this case. This would have caused upgrade scripts to mistakenly think that the general and slow logs are corrupted and have to be repaired. Enabling CHECK TABLES for log tables prevents this from happening. Finally, the patch changes the error message from "Corrupt" to "Operation failed" for a number of issues not related to table corruption. For example "Lock wait timeout exceeded" and "Deadlock found trying to get lock". Test cases added to merge.test and check.test.
15 years ago
Bug #56494 Segfault in upgrade_shared_lock_to_exclusive() for REPAIR of merge table Bug #56422 CHECK TABLE run when the table is locked reports corruption along with timeout The crash happened if a table maintenance statement (ANALYZE TABLE, REPAIR TABLE, etc.) was executed on a MERGE table and opening and locking a child table failed. This could for example happen if a child table did not exist or if a lock timeout happened while waiting for a conflicting metadata lock to disappear. Since opening and locking the MERGE table and its children failed, the tables would be closed and the metadata locks released. However, TABLE_LIST::table for the MERGE table would still be set, with its value invalid since the tables had been closed. This caused the table maintenance statement to try to continue and upgrade the metadata lock on the MERGE table. But since the lock already had been released, this caused a segfault. This patch fixes the problem by setting TABLE_LIST::table to NULL if open_and_lock_tables() fails. This prevents maintenance statements from continuing and trying to upgrade the metadata lock. The patch includes a 5.5 version of the fix for Bug #46339 crash on REPAIR TABLE merge table USE_FRM. This bug caused REPAIR TABLE ... USE_FRM to give an assert when used on merge tables. The patch also enables the CHECK TABLE statement for log tables. Before, CHECK TABLE for log tables gave ER_CANT_LOCK_LOG_TABLE, yet still counted the statement as successfully executed. With the changes to table maintenance statement error handling in this patch, CHECK TABLE would no longer be considered as successful in this case. This would have caused upgrade scripts to mistakenly think that the general and slow logs are corrupted and have to be repaired. Enabling CHECK TABLES for log tables prevents this from happening. Finally, the patch changes the error message from "Corrupt" to "Operation failed" for a number of issues not related to table corruption. For example "Lock wait timeout exceeded" and "Deadlock found trying to get lock". Test cases added to merge.test and check.test.
15 years ago
Bug #56494 Segfault in upgrade_shared_lock_to_exclusive() for REPAIR of merge table Bug #56422 CHECK TABLE run when the table is locked reports corruption along with timeout The crash happened if a table maintenance statement (ANALYZE TABLE, REPAIR TABLE, etc.) was executed on a MERGE table and opening and locking a child table failed. This could for example happen if a child table did not exist or if a lock timeout happened while waiting for a conflicting metadata lock to disappear. Since opening and locking the MERGE table and its children failed, the tables would be closed and the metadata locks released. However, TABLE_LIST::table for the MERGE table would still be set, with its value invalid since the tables had been closed. This caused the table maintenance statement to try to continue and upgrade the metadata lock on the MERGE table. But since the lock already had been released, this caused a segfault. This patch fixes the problem by setting TABLE_LIST::table to NULL if open_and_lock_tables() fails. This prevents maintenance statements from continuing and trying to upgrade the metadata lock. The patch includes a 5.5 version of the fix for Bug #46339 crash on REPAIR TABLE merge table USE_FRM. This bug caused REPAIR TABLE ... USE_FRM to give an assert when used on merge tables. The patch also enables the CHECK TABLE statement for log tables. Before, CHECK TABLE for log tables gave ER_CANT_LOCK_LOG_TABLE, yet still counted the statement as successfully executed. With the changes to table maintenance statement error handling in this patch, CHECK TABLE would no longer be considered as successful in this case. This would have caused upgrade scripts to mistakenly think that the general and slow logs are corrupted and have to be repaired. Enabling CHECK TABLES for log tables prevents this from happening. Finally, the patch changes the error message from "Corrupt" to "Operation failed" for a number of issues not related to table corruption. For example "Lock wait timeout exceeded" and "Deadlock found trying to get lock". Test cases added to merge.test and check.test.
15 years ago
Bug #56494 Segfault in upgrade_shared_lock_to_exclusive() for REPAIR of merge table Bug #56422 CHECK TABLE run when the table is locked reports corruption along with timeout The crash happened if a table maintenance statement (ANALYZE TABLE, REPAIR TABLE, etc.) was executed on a MERGE table and opening and locking a child table failed. This could for example happen if a child table did not exist or if a lock timeout happened while waiting for a conflicting metadata lock to disappear. Since opening and locking the MERGE table and its children failed, the tables would be closed and the metadata locks released. However, TABLE_LIST::table for the MERGE table would still be set, with its value invalid since the tables had been closed. This caused the table maintenance statement to try to continue and upgrade the metadata lock on the MERGE table. But since the lock already had been released, this caused a segfault. This patch fixes the problem by setting TABLE_LIST::table to NULL if open_and_lock_tables() fails. This prevents maintenance statements from continuing and trying to upgrade the metadata lock. The patch includes a 5.5 version of the fix for Bug #46339 crash on REPAIR TABLE merge table USE_FRM. This bug caused REPAIR TABLE ... USE_FRM to give an assert when used on merge tables. The patch also enables the CHECK TABLE statement for log tables. Before, CHECK TABLE for log tables gave ER_CANT_LOCK_LOG_TABLE, yet still counted the statement as successfully executed. With the changes to table maintenance statement error handling in this patch, CHECK TABLE would no longer be considered as successful in this case. This would have caused upgrade scripts to mistakenly think that the general and slow logs are corrupted and have to be repaired. Enabling CHECK TABLES for log tables prevents this from happening. Finally, the patch changes the error message from "Corrupt" to "Operation failed" for a number of issues not related to table corruption. For example "Lock wait timeout exceeded" and "Deadlock found trying to get lock". Test cases added to merge.test and check.test.
15 years ago
  1. /* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation; version 2 of the License.
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License
  10. along with this program; if not, write to the Free Software
  11. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
  12. #include "sql_class.h" // THD
  13. #include "keycaches.h" // get_key_cache
  14. #include "sql_base.h" // Open_table_context
  15. #include "lock.h" // MYSQL_OPEN_*
  16. #include "sql_handler.h" // mysql_ha_rm_tables
  17. #include "partition_element.h" // PART_ADMIN
  18. #include "sql_partition.h" // set_part_state
  19. #include "transaction.h" // trans_rollback_stmt
  20. #include "sql_view.h" // view_checksum
  21. #include "sql_table.h" // mysql_recreate_table
  22. #include "debug_sync.h" // DEBUG_SYNC
  23. #include "sql_acl.h" // *_ACL
  24. #include "sp.h" // Sroutine_hash_entry
  25. #include "sql_parse.h" // check_table_access
  26. #include "sql_admin.h"
  27. static int send_check_errmsg(THD *thd, TABLE_LIST* table,
  28. const char* operator_name, const char* errmsg)
  29. {
  30. Protocol *protocol= thd->protocol;
  31. protocol->prepare_for_resend();
  32. protocol->store(table->alias, system_charset_info);
  33. protocol->store((char*) operator_name, system_charset_info);
  34. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  35. protocol->store(errmsg, system_charset_info);
  36. thd->clear_error();
  37. if (protocol->write())
  38. return -1;
  39. return 1;
  40. }
  41. static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
  42. HA_CHECK_OPT *check_opt)
  43. {
  44. int error= 0;
  45. TABLE tmp_table, *table;
  46. TABLE_SHARE *share;
  47. bool has_mdl_lock= FALSE;
  48. char from[FN_REFLEN],tmp[FN_REFLEN+32];
  49. const char **ext;
  50. MY_STAT stat_info;
  51. Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
  52. MYSQL_OPEN_HAS_MDL_LOCK |
  53. MYSQL_LOCK_IGNORE_TIMEOUT));
  54. DBUG_ENTER("prepare_for_repair");
  55. if (!(check_opt->sql_flags & TT_USEFRM))
  56. DBUG_RETURN(0);
  57. if (!(table= table_list->table))
  58. {
  59. char key[MAX_DBKEY_LENGTH];
  60. uint key_length;
  61. /*
  62. If the table didn't exist, we have a shared metadata lock
  63. on it that is left from mysql_admin_table()'s attempt to
  64. open it. Release the shared metadata lock before trying to
  65. acquire the exclusive lock to satisfy MDL asserts and avoid
  66. deadlocks.
  67. */
  68. thd->mdl_context.release_transactional_locks();
  69. /*
  70. Attempt to do full-blown table open in mysql_admin_table() has failed.
  71. Let us try to open at least a .FRM for this table.
  72. */
  73. my_hash_value_type hash_value;
  74. key_length= create_table_def_key(thd, key, table_list, 0);
  75. table_list->mdl_request.init(MDL_key::TABLE,
  76. table_list->db, table_list->table_name,
  77. MDL_EXCLUSIVE, MDL_TRANSACTION);
  78. if (lock_table_names(thd, table_list, table_list->next_global,
  79. thd->variables.lock_wait_timeout,
  80. MYSQL_OPEN_SKIP_TEMPORARY))
  81. DBUG_RETURN(0);
  82. has_mdl_lock= TRUE;
  83. hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
  84. mysql_mutex_lock(&LOCK_open);
  85. share= get_table_share(thd, table_list, key, key_length, 0,
  86. &error, hash_value);
  87. mysql_mutex_unlock(&LOCK_open);
  88. if (share == NULL)
  89. DBUG_RETURN(0); // Can't open frm file
  90. if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
  91. {
  92. mysql_mutex_lock(&LOCK_open);
  93. release_table_share(share);
  94. mysql_mutex_unlock(&LOCK_open);
  95. DBUG_RETURN(0); // Out of memory
  96. }
  97. table= &tmp_table;
  98. }
  99. /*
  100. REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
  101. */
  102. if (table->s->tmp_table)
  103. {
  104. error= send_check_errmsg(thd, table_list, "repair",
  105. "Cannot repair temporary table from .frm file");
  106. goto end;
  107. }
  108. /*
  109. User gave us USE_FRM which means that the header in the index file is
  110. trashed.
  111. In this case we will try to fix the table the following way:
  112. - Rename the data file to a temporary name
  113. - Truncate the table
  114. - Replace the new data file with the old one
  115. - Run a normal repair using the new index file and the old data file
  116. */
  117. if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
  118. {
  119. error= send_check_errmsg(thd, table_list, "repair",
  120. "Failed repairing incompatible .frm file");
  121. goto end;
  122. }
  123. /*
  124. Check if this is a table type that stores index and data separately,
  125. like ISAM or MyISAM. We assume fixed order of engine file name
  126. extentions array. First element of engine file name extentions array
  127. is meta/index file extention. Second element - data file extention.
  128. */
  129. ext= table->file->bas_ext();
  130. if (!ext[0] || !ext[1])
  131. goto end; // No data file
  132. /* A MERGE table must not come here. */
  133. DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
  134. // Name of data file
  135. strxmov(from, table->s->normalized_path.str, ext[1], NullS);
  136. if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
  137. goto end; // Can't use USE_FRM flag
  138. my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
  139. from, current_pid, thd->thread_id);
  140. if (table_list->table)
  141. {
  142. /*
  143. Table was successfully open in mysql_admin_table(). Now we need
  144. to close it, but leave it protected by exclusive metadata lock.
  145. */
  146. if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
  147. goto end;
  148. close_all_tables_for_name(thd, table_list->table->s, FALSE);
  149. table_list->table= 0;
  150. }
  151. /*
  152. After this point we have an exclusive metadata lock on our table
  153. in both cases when table was successfully open in mysql_admin_table()
  154. and when it was open in prepare_for_repair().
  155. */
  156. if (my_rename(from, tmp, MYF(MY_WME)))
  157. {
  158. error= send_check_errmsg(thd, table_list, "repair",
  159. "Failed renaming data file");
  160. goto end;
  161. }
  162. if (dd_recreate_table(thd, table_list->db, table_list->table_name))
  163. {
  164. error= send_check_errmsg(thd, table_list, "repair",
  165. "Failed generating table from .frm file");
  166. goto end;
  167. }
  168. /*
  169. 'FALSE' for 'using_transactions' means don't postpone
  170. invalidation till the end of a transaction, but do it
  171. immediately.
  172. */
  173. query_cache_invalidate3(thd, table_list, FALSE);
  174. if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
  175. {
  176. error= send_check_errmsg(thd, table_list, "repair",
  177. "Failed restoring .MYD file");
  178. goto end;
  179. }
  180. if (thd->locked_tables_list.reopen_tables(thd))
  181. goto end;
  182. /*
  183. Now we should be able to open the partially repaired table
  184. to finish the repair in the handler later on.
  185. */
  186. if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
  187. {
  188. error= send_check_errmsg(thd, table_list, "repair",
  189. "Failed to open partially repaired table");
  190. goto end;
  191. }
  192. end:
  193. thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
  194. if (table == &tmp_table)
  195. {
  196. mysql_mutex_lock(&LOCK_open);
  197. closefrm(table, 1); // Free allocated memory
  198. mysql_mutex_unlock(&LOCK_open);
  199. }
  200. /* In case of a temporary table there will be no metadata lock. */
  201. if (error && has_mdl_lock)
  202. thd->mdl_context.release_transactional_locks();
  203. DBUG_RETURN(error);
  204. }
  205. /**
  206. Check if a given error is something that could occur during
  207. open_and_lock_tables() that does not indicate table corruption.
  208. @param sql_errno Error number to check.
  209. @retval TRUE Error does not indicate table corruption.
  210. @retval FALSE Error could indicate table corruption.
  211. */
  212. static inline bool table_not_corrupt_error(uint sql_errno)
  213. {
  214. return (sql_errno == ER_NO_SUCH_TABLE ||
  215. sql_errno == ER_FILE_NOT_FOUND ||
  216. sql_errno == ER_LOCK_WAIT_TIMEOUT ||
  217. sql_errno == ER_LOCK_DEADLOCK ||
  218. sql_errno == ER_CANT_LOCK_LOG_TABLE ||
  219. sql_errno == ER_OPEN_AS_READONLY);
  220. }
  221. /*
  222. RETURN VALUES
  223. FALSE Message sent to net (admin operation went ok)
  224. TRUE Message should be sent by caller
  225. (admin operation or network communication failed)
  226. */
  227. static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
  228. HA_CHECK_OPT* check_opt,
  229. const char *operator_name,
  230. thr_lock_type lock_type,
  231. bool open_for_modify,
  232. bool repair_table_use_frm,
  233. uint extra_open_options,
  234. int (*prepare_func)(THD *, TABLE_LIST *,
  235. HA_CHECK_OPT *),
  236. int (handler::*operator_func)(THD *,
  237. HA_CHECK_OPT *),
  238. int (view_operator_func)(THD *, TABLE_LIST*))
  239. {
  240. TABLE_LIST *table;
  241. SELECT_LEX *select= &thd->lex->select_lex;
  242. List<Item> field_list;
  243. Item *item;
  244. Protocol *protocol= thd->protocol;
  245. LEX *lex= thd->lex;
  246. int result_code;
  247. DBUG_ENTER("mysql_admin_table");
  248. field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
  249. item->maybe_null = 1;
  250. field_list.push_back(item = new Item_empty_string("Op", 10));
  251. item->maybe_null = 1;
  252. field_list.push_back(item = new Item_empty_string("Msg_type", 10));
  253. item->maybe_null = 1;
  254. field_list.push_back(item = new Item_empty_string("Msg_text", 255));
  255. item->maybe_null = 1;
  256. if (protocol->send_result_set_metadata(&field_list,
  257. Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
  258. DBUG_RETURN(TRUE);
  259. mysql_ha_rm_tables(thd, tables);
  260. for (table= tables; table; table= table->next_local)
  261. {
  262. char table_name[NAME_LEN*2+2];
  263. char* db = table->db;
  264. bool fatal_error=0;
  265. bool open_error;
  266. DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
  267. DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
  268. strxmov(table_name, db, ".", table->table_name, NullS);
  269. thd->open_options|= extra_open_options;
  270. table->lock_type= lock_type;
  271. /*
  272. To make code safe for re-execution we need to reset type of MDL
  273. request as code below may change it.
  274. To allow concurrent execution of read-only operations we acquire
  275. weak metadata lock for them.
  276. */
  277. table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
  278. MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
  279. /* open only one table from local list of command */
  280. {
  281. TABLE_LIST *save_next_global, *save_next_local;
  282. save_next_global= table->next_global;
  283. table->next_global= 0;
  284. save_next_local= table->next_local;
  285. table->next_local= 0;
  286. select->table_list.first= table;
  287. /*
  288. Time zone tables and SP tables can be add to lex->query_tables list,
  289. so it have to be prepared.
  290. TODO: Investigate if we can put extra tables into argument instead of
  291. using lex->query_tables
  292. */
  293. lex->query_tables= table;
  294. lex->query_tables_last= &table->next_global;
  295. lex->query_tables_own_last= 0;
  296. if (view_operator_func == NULL)
  297. table->required_type=FRMTYPE_TABLE;
  298. if (!thd->locked_tables_mode && repair_table_use_frm)
  299. {
  300. /*
  301. If we're not under LOCK TABLES and we're executing REPAIR TABLE
  302. USE_FRM, we need to ignore errors from open_and_lock_tables().
  303. REPAIR TABLE USE_FRM is a heavy weapon used when a table is
  304. critically damaged, so open_and_lock_tables() will most likely
  305. report errors. Those errors are not interesting for the user
  306. because it's already known that the table is badly damaged.
  307. */
  308. Warning_info wi(thd->query_id, false);
  309. Warning_info *wi_saved= thd->warning_info;
  310. thd->warning_info= &wi;
  311. open_error= open_and_lock_tables(thd, table, TRUE, 0);
  312. thd->warning_info= wi_saved;
  313. }
  314. else
  315. {
  316. /*
  317. It's assumed that even if it is REPAIR TABLE USE_FRM, the table
  318. can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
  319. would fail). Thus, the only errors we could have from
  320. open_and_lock_tables() are logical ones, like incorrect locking
  321. mode. It does make sense for the user to see such errors.
  322. */
  323. open_error= open_and_lock_tables(thd, table, TRUE, 0);
  324. }
  325. table->next_global= save_next_global;
  326. table->next_local= save_next_local;
  327. thd->open_options&= ~extra_open_options;
  328. /*
  329. If open_and_lock_tables() failed, close_thread_tables() will close
  330. the table and table->table can therefore be invalid.
  331. */
  332. if (open_error)
  333. table->table= NULL;
  334. /*
  335. Under locked tables, we know that the table can be opened,
  336. so any errors opening the table are logical errors.
  337. In these cases it does not make sense to try to repair.
  338. */
  339. if (open_error && thd->locked_tables_mode)
  340. {
  341. result_code= HA_ADMIN_FAILED;
  342. goto send_result;
  343. }
  344. #ifdef WITH_PARTITION_STORAGE_ENGINE
  345. if (table->table)
  346. {
  347. /*
  348. Set up which partitions that should be processed
  349. if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
  350. CACHE INDEX/LOAD INDEX for specified partitions
  351. */
  352. Alter_info *alter_info= &lex->alter_info;
  353. if (alter_info->flags & ALTER_ADMIN_PARTITION)
  354. {
  355. if (!table->table->part_info)
  356. {
  357. my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
  358. DBUG_RETURN(TRUE);
  359. }
  360. uint num_parts_found;
  361. uint num_parts_opt= alter_info->partition_names.elements;
  362. num_parts_found= set_part_state(alter_info, table->table->part_info,
  363. PART_ADMIN);
  364. if (num_parts_found != num_parts_opt &&
  365. (!(alter_info->flags & ALTER_ALL_PARTITION)))
  366. {
  367. char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
  368. size_t length;
  369. DBUG_PRINT("admin", ("sending non existent partition error"));
  370. protocol->prepare_for_resend();
  371. protocol->store(table_name, system_charset_info);
  372. protocol->store(operator_name, system_charset_info);
  373. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  374. length= my_snprintf(buff, sizeof(buff),
  375. ER(ER_DROP_PARTITION_NON_EXISTENT),
  376. table_name);
  377. protocol->store(buff, length, system_charset_info);
  378. if(protocol->write())
  379. goto err;
  380. my_eof(thd);
  381. goto err;
  382. }
  383. }
  384. }
  385. #endif
  386. }
  387. DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
  388. if (prepare_func)
  389. {
  390. DBUG_PRINT("admin", ("calling prepare_func"));
  391. switch ((*prepare_func)(thd, table, check_opt)) {
  392. case 1: // error, message written to net
  393. trans_rollback_stmt(thd);
  394. trans_rollback(thd);
  395. close_thread_tables(thd);
  396. thd->mdl_context.release_transactional_locks();
  397. DBUG_PRINT("admin", ("simple error, admin next table"));
  398. continue;
  399. case -1: // error, message could be written to net
  400. /* purecov: begin inspected */
  401. DBUG_PRINT("admin", ("severe error, stop"));
  402. goto err;
  403. /* purecov: end */
  404. default: // should be 0 otherwise
  405. DBUG_PRINT("admin", ("prepare_func succeeded"));
  406. ;
  407. }
  408. }
  409. /*
  410. CHECK TABLE command is only command where VIEW allowed here and this
  411. command use only temporary teble method for VIEWs resolving => there
  412. can't be VIEW tree substitition of join view => if opening table
  413. succeed then table->table will have real TABLE pointer as value (in
  414. case of join view substitution table->table can be 0, but here it is
  415. impossible)
  416. */
  417. if (!table->table)
  418. {
  419. DBUG_PRINT("admin", ("open table failed"));
  420. if (thd->warning_info->is_empty())
  421. push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
  422. ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
  423. /* if it was a view will check md5 sum */
  424. if (table->view &&
  425. view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
  426. push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
  427. ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
  428. if (thd->stmt_da->is_error() &&
  429. table_not_corrupt_error(thd->stmt_da->sql_errno()))
  430. result_code= HA_ADMIN_FAILED;
  431. else
  432. /* Default failure code is corrupt table */
  433. result_code= HA_ADMIN_CORRUPT;
  434. goto send_result;
  435. }
  436. if (table->view)
  437. {
  438. DBUG_PRINT("admin", ("calling view_operator_func"));
  439. result_code= (*view_operator_func)(thd, table);
  440. goto send_result;
  441. }
  442. if (table->schema_table)
  443. {
  444. result_code= HA_ADMIN_NOT_IMPLEMENTED;
  445. goto send_result;
  446. }
  447. if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
  448. {
  449. /* purecov: begin inspected */
  450. char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
  451. size_t length;
  452. enum_sql_command save_sql_command= lex->sql_command;
  453. DBUG_PRINT("admin", ("sending error message"));
  454. protocol->prepare_for_resend();
  455. protocol->store(table_name, system_charset_info);
  456. protocol->store(operator_name, system_charset_info);
  457. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  458. length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
  459. table_name);
  460. protocol->store(buff, length, system_charset_info);
  461. trans_commit_stmt(thd);
  462. trans_commit(thd);
  463. close_thread_tables(thd);
  464. thd->mdl_context.release_transactional_locks();
  465. lex->reset_query_tables_list(FALSE);
  466. /*
  467. Restore Query_tables_list::sql_command value to make statement
  468. safe for re-execution.
  469. */
  470. lex->sql_command= save_sql_command;
  471. table->table=0; // For query cache
  472. if (protocol->write())
  473. goto err;
  474. thd->stmt_da->reset_diagnostics_area();
  475. continue;
  476. /* purecov: end */
  477. }
  478. /*
  479. Close all instances of the table to allow MyISAM "repair"
  480. to rename files.
  481. @todo: This code does not close all instances of the table.
  482. It only closes instances in other connections, but if this
  483. connection has LOCK TABLE t1 a READ, t1 b WRITE,
  484. both t1 instances will be kept open.
  485. There is no need to execute this branch for InnoDB, which does
  486. repair by recreate. There is no need to do it for OPTIMIZE,
  487. which doesn't move files around.
  488. Hence, this code should be moved to prepare_for_repair(),
  489. and executed only for MyISAM engine.
  490. */
  491. if (lock_type == TL_WRITE && !table->table->s->tmp_table)
  492. {
  493. if (wait_while_table_is_used(thd, table->table,
  494. HA_EXTRA_PREPARE_FOR_RENAME))
  495. goto err;
  496. DEBUG_SYNC(thd, "after_admin_flush");
  497. /* Flush entries in the query cache involving this table. */
  498. query_cache_invalidate3(thd, table->table, 0);
  499. /*
  500. XXX: hack: switch off open_for_modify to skip the
  501. flush that is made later in the execution flow.
  502. */
  503. open_for_modify= 0;
  504. }
  505. if (table->table->s->crashed && operator_func == &handler::ha_check)
  506. {
  507. /* purecov: begin inspected */
  508. DBUG_PRINT("admin", ("sending crashed warning"));
  509. protocol->prepare_for_resend();
  510. protocol->store(table_name, system_charset_info);
  511. protocol->store(operator_name, system_charset_info);
  512. protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
  513. protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
  514. system_charset_info);
  515. if (protocol->write())
  516. goto err;
  517. /* purecov: end */
  518. }
  519. if (operator_func == &handler::ha_repair &&
  520. !(check_opt->sql_flags & TT_USEFRM))
  521. {
  522. if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
  523. (table->table->file->ha_check_for_upgrade(check_opt) ==
  524. HA_ADMIN_NEEDS_ALTER))
  525. {
  526. DBUG_PRINT("admin", ("recreating table"));
  527. trans_rollback_stmt(thd);
  528. trans_rollback(thd);
  529. close_thread_tables(thd);
  530. thd->mdl_context.release_transactional_locks();
  531. tmp_disable_binlog(thd); // binlogging is done by caller if wanted
  532. result_code= mysql_recreate_table(thd, table);
  533. reenable_binlog(thd);
  534. /*
  535. mysql_recreate_table() can push OK or ERROR.
  536. Clear 'OK' status. If there is an error, keep it:
  537. we will store the error message in a result set row
  538. and then clear.
  539. */
  540. if (thd->stmt_da->is_ok())
  541. thd->stmt_da->reset_diagnostics_area();
  542. table->table= NULL;
  543. result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
  544. goto send_result;
  545. }
  546. }
  547. DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
  548. result_code = (table->table->file->*operator_func)(thd, check_opt);
  549. DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
  550. send_result:
  551. lex->cleanup_after_one_table_open();
  552. thd->clear_error(); // these errors shouldn't get client
  553. {
  554. List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
  555. MYSQL_ERROR *err;
  556. while ((err= it++))
  557. {
  558. protocol->prepare_for_resend();
  559. protocol->store(table_name, system_charset_info);
  560. protocol->store((char*) operator_name, system_charset_info);
  561. protocol->store(warning_level_names[err->get_level()].str,
  562. warning_level_names[err->get_level()].length,
  563. system_charset_info);
  564. protocol->store(err->get_message_text(), system_charset_info);
  565. if (protocol->write())
  566. goto err;
  567. }
  568. thd->warning_info->clear_warning_info(thd->query_id);
  569. }
  570. protocol->prepare_for_resend();
  571. protocol->store(table_name, system_charset_info);
  572. protocol->store(operator_name, system_charset_info);
  573. send_result_message:
  574. DBUG_PRINT("info", ("result_code: %d", result_code));
  575. switch (result_code) {
  576. case HA_ADMIN_NOT_IMPLEMENTED:
  577. {
  578. char buf[MYSQL_ERRMSG_SIZE];
  579. size_t length=my_snprintf(buf, sizeof(buf),
  580. ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
  581. protocol->store(STRING_WITH_LEN("note"), system_charset_info);
  582. protocol->store(buf, length, system_charset_info);
  583. }
  584. break;
  585. case HA_ADMIN_NOT_BASE_TABLE:
  586. {
  587. char buf[MYSQL_ERRMSG_SIZE];
  588. size_t length= my_snprintf(buf, sizeof(buf),
  589. ER(ER_BAD_TABLE_ERROR), table_name);
  590. protocol->store(STRING_WITH_LEN("note"), system_charset_info);
  591. protocol->store(buf, length, system_charset_info);
  592. }
  593. break;
  594. case HA_ADMIN_OK:
  595. protocol->store(STRING_WITH_LEN("status"), system_charset_info);
  596. protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
  597. break;
  598. case HA_ADMIN_FAILED:
  599. protocol->store(STRING_WITH_LEN("status"), system_charset_info);
  600. protocol->store(STRING_WITH_LEN("Operation failed"),
  601. system_charset_info);
  602. break;
  603. case HA_ADMIN_REJECT:
  604. protocol->store(STRING_WITH_LEN("status"), system_charset_info);
  605. protocol->store(STRING_WITH_LEN("Operation need committed state"),
  606. system_charset_info);
  607. open_for_modify= FALSE;
  608. break;
  609. case HA_ADMIN_ALREADY_DONE:
  610. protocol->store(STRING_WITH_LEN("status"), system_charset_info);
  611. protocol->store(STRING_WITH_LEN("Table is already up to date"),
  612. system_charset_info);
  613. break;
  614. case HA_ADMIN_CORRUPT:
  615. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  616. protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
  617. fatal_error=1;
  618. break;
  619. case HA_ADMIN_INVALID:
  620. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  621. protocol->store(STRING_WITH_LEN("Invalid argument"),
  622. system_charset_info);
  623. break;
  624. case HA_ADMIN_TRY_ALTER:
  625. {
  626. /*
  627. This is currently used only by InnoDB. ha_innobase::optimize() answers
  628. "try with alter", so here we close the table, do an ALTER TABLE,
  629. reopen the table and do ha_innobase::analyze() on it.
  630. We have to end the row, so analyze could return more rows.
  631. */
  632. trans_commit_stmt(thd);
  633. trans_commit(thd);
  634. close_thread_tables(thd);
  635. thd->mdl_context.release_transactional_locks();
  636. DEBUG_SYNC(thd, "ha_admin_try_alter");
  637. protocol->store(STRING_WITH_LEN("note"), system_charset_info);
  638. protocol->store(STRING_WITH_LEN(
  639. "Table does not support optimize, doing recreate + analyze instead"),
  640. system_charset_info);
  641. if (protocol->write())
  642. goto err;
  643. DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
  644. TABLE_LIST *save_next_local= table->next_local,
  645. *save_next_global= table->next_global;
  646. table->next_local= table->next_global= 0;
  647. tmp_disable_binlog(thd); // binlogging is done by caller if wanted
  648. result_code= mysql_recreate_table(thd, table);
  649. reenable_binlog(thd);
  650. /*
  651. mysql_recreate_table() can push OK or ERROR.
  652. Clear 'OK' status. If there is an error, keep it:
  653. we will store the error message in a result set row
  654. and then clear.
  655. */
  656. if (thd->stmt_da->is_ok())
  657. thd->stmt_da->reset_diagnostics_area();
  658. trans_commit_stmt(thd);
  659. trans_commit(thd);
  660. close_thread_tables(thd);
  661. thd->mdl_context.release_transactional_locks();
  662. table->table= NULL;
  663. if (!result_code) // recreation went ok
  664. {
  665. /* Clear the ticket released above. */
  666. table->mdl_request.ticket= NULL;
  667. DEBUG_SYNC(thd, "ha_admin_open_ltable");
  668. table->mdl_request.set_type(MDL_SHARED_WRITE);
  669. if ((table->table= open_ltable(thd, table, lock_type, 0)))
  670. {
  671. result_code= table->table->file->ha_analyze(thd, check_opt);
  672. if (result_code == HA_ADMIN_ALREADY_DONE)
  673. result_code= HA_ADMIN_OK;
  674. else if (result_code) // analyze failed
  675. table->table->file->print_error(result_code, MYF(0));
  676. }
  677. else
  678. result_code= -1; // open failed
  679. }
  680. /* Start a new row for the final status row */
  681. protocol->prepare_for_resend();
  682. protocol->store(table_name, system_charset_info);
  683. protocol->store(operator_name, system_charset_info);
  684. if (result_code) // either mysql_recreate_table or analyze failed
  685. {
  686. DBUG_ASSERT(thd->is_error() || thd->killed);
  687. if (thd->is_error())
  688. {
  689. const char *err_msg= thd->stmt_da->message();
  690. if (!thd->vio_ok())
  691. {
  692. sql_print_error("%s", err_msg);
  693. }
  694. else
  695. {
  696. /* Hijack the row already in-progress. */
  697. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  698. protocol->store(err_msg, system_charset_info);
  699. if (protocol->write())
  700. goto err;
  701. /* Start off another row for HA_ADMIN_FAILED */
  702. protocol->prepare_for_resend();
  703. protocol->store(table_name, system_charset_info);
  704. protocol->store(operator_name, system_charset_info);
  705. }
  706. thd->clear_error();
  707. }
  708. }
  709. result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
  710. table->next_local= save_next_local;
  711. table->next_global= save_next_global;
  712. goto send_result_message;
  713. }
  714. case HA_ADMIN_WRONG_CHECKSUM:
  715. {
  716. protocol->store(STRING_WITH_LEN("note"), system_charset_info);
  717. protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
  718. system_charset_info);
  719. break;
  720. }
  721. case HA_ADMIN_NEEDS_UPGRADE:
  722. case HA_ADMIN_NEEDS_ALTER:
  723. {
  724. char buf[MYSQL_ERRMSG_SIZE];
  725. size_t length;
  726. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  727. if (table->table->file->ha_table_flags() & HA_CAN_REPAIR)
  728. length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
  729. table->table_name);
  730. else
  731. length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_REBUILD),
  732. table->table_name);
  733. protocol->store(buf, length, system_charset_info);
  734. fatal_error=1;
  735. break;
  736. }
  737. default: // Probably HA_ADMIN_INTERNAL_ERROR
  738. {
  739. char buf[MYSQL_ERRMSG_SIZE];
  740. size_t length=my_snprintf(buf, sizeof(buf),
  741. "Unknown - internal error %d during operation",
  742. result_code);
  743. protocol->store(STRING_WITH_LEN("error"), system_charset_info);
  744. protocol->store(buf, length, system_charset_info);
  745. fatal_error=1;
  746. break;
  747. }
  748. }
  749. if (table->table)
  750. {
  751. if (table->table->s->tmp_table)
  752. {
  753. /*
  754. If the table was not opened successfully, do not try to get
  755. status information. (Bug#47633)
  756. */
  757. if (open_for_modify && !open_error)
  758. table->table->file->info(HA_STATUS_CONST);
  759. }
  760. else if (open_for_modify || fatal_error)
  761. {
  762. tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
  763. table->db, table->table_name, FALSE);
  764. /*
  765. May be something modified. Consequently, we have to
  766. invalidate the query cache.
  767. */
  768. table->table= 0; // For query cache
  769. query_cache_invalidate3(thd, table, 0);
  770. }
  771. }
  772. /* Error path, a admin command failed. */
  773. trans_commit_stmt(thd);
  774. trans_commit_implicit(thd);
  775. close_thread_tables(thd);
  776. thd->mdl_context.release_transactional_locks();
  777. /*
  778. If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
  779. separate open_tables() for each CHECK TABLE argument.
  780. Right now we do not have a separate method to reset the prelocking
  781. state in the lex to the state after parsing, so each open will pollute
  782. this state: add elements to lex->srotuines_list, TABLE_LISTs to
  783. lex->query_tables. Below is a lame attempt to recover from this
  784. pollution.
  785. @todo: have a method to reset a prelocking context, or use separate
  786. contexts for each open.
  787. */
  788. for (Sroutine_hash_entry *rt=
  789. (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
  790. rt; rt= rt->next)
  791. rt->mdl_request.ticket= NULL;
  792. if (protocol->write())
  793. goto err;
  794. }
  795. my_eof(thd);
  796. DBUG_RETURN(FALSE);
  797. err:
  798. trans_rollback_stmt(thd);
  799. trans_rollback(thd);
  800. close_thread_tables(thd); // Shouldn't be needed
  801. thd->mdl_context.release_transactional_locks();
  802. if (table)
  803. table->table=0;
  804. DBUG_RETURN(TRUE);
  805. }
  806. /*
  807. Assigned specified indexes for a table into key cache
  808. SYNOPSIS
  809. mysql_assign_to_keycache()
  810. thd Thread object
  811. tables Table list (one table only)
  812. RETURN VALUES
  813. FALSE ok
  814. TRUE error
  815. */
  816. bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
  817. LEX_STRING *key_cache_name)
  818. {
  819. HA_CHECK_OPT check_opt;
  820. KEY_CACHE *key_cache;
  821. DBUG_ENTER("mysql_assign_to_keycache");
  822. check_opt.init();
  823. mysql_mutex_lock(&LOCK_global_system_variables);
  824. if (!(key_cache= get_key_cache(key_cache_name)))
  825. {
  826. mysql_mutex_unlock(&LOCK_global_system_variables);
  827. my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
  828. DBUG_RETURN(TRUE);
  829. }
  830. mysql_mutex_unlock(&LOCK_global_system_variables);
  831. check_opt.key_cache= key_cache;
  832. DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
  833. "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
  834. 0, 0, &handler::assign_to_keycache, 0));
  835. }
  836. /*
  837. Preload specified indexes for a table into key cache
  838. SYNOPSIS
  839. mysql_preload_keys()
  840. thd Thread object
  841. tables Table list (one table only)
  842. RETURN VALUES
  843. FALSE ok
  844. TRUE error
  845. */
  846. bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
  847. {
  848. DBUG_ENTER("mysql_preload_keys");
  849. /*
  850. We cannot allow concurrent inserts. The storage engine reads
  851. directly from the index file, bypassing the cache. It could read
  852. outdated information if parallel inserts into cache blocks happen.
  853. */
  854. DBUG_RETURN(mysql_admin_table(thd, tables, 0,
  855. "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
  856. &handler::preload_keys, 0));
  857. }
  858. bool Analyze_table_statement::execute(THD *thd)
  859. {
  860. TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
  861. bool res= TRUE;
  862. thr_lock_type lock_type = TL_READ_NO_INSERT;
  863. DBUG_ENTER("Analyze_table_statement::execute");
  864. if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
  865. FALSE, UINT_MAX, FALSE))
  866. goto error;
  867. thd->enable_slow_log= opt_log_slow_admin_statements;
  868. res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
  869. "analyze", lock_type, 1, 0, 0, 0,
  870. &handler::ha_analyze, 0);
  871. /* ! we write after unlocking the table */
  872. if (!res && !m_lex->no_write_to_binlog)
  873. {
  874. /*
  875. Presumably, ANALYZE and binlog writing doesn't require synchronization
  876. */
  877. res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
  878. }
  879. m_lex->select_lex.table_list.first= first_table;
  880. m_lex->query_tables= first_table;
  881. error:
  882. DBUG_RETURN(res);
  883. }
  884. bool Check_table_statement::execute(THD *thd)
  885. {
  886. TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
  887. thr_lock_type lock_type = TL_READ_NO_INSERT;
  888. bool res= TRUE;
  889. DBUG_ENTER("Check_table_statement::execute");
  890. if (check_table_access(thd, SELECT_ACL, first_table,
  891. TRUE, UINT_MAX, FALSE))
  892. goto error; /* purecov: inspected */
  893. thd->enable_slow_log= opt_log_slow_admin_statements;
  894. res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check",
  895. lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
  896. &handler::ha_check, &view_checksum);
  897. m_lex->select_lex.table_list.first= first_table;
  898. m_lex->query_tables= first_table;
  899. error:
  900. DBUG_RETURN(res);
  901. }
  902. bool Optimize_table_statement::execute(THD *thd)
  903. {
  904. TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
  905. bool res= TRUE;
  906. DBUG_ENTER("Optimize_table_statement::execute");
  907. if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
  908. FALSE, UINT_MAX, FALSE))
  909. goto error; /* purecov: inspected */
  910. thd->enable_slow_log= opt_log_slow_admin_statements;
  911. res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
  912. mysql_recreate_table(thd, first_table) :
  913. mysql_admin_table(thd, first_table, &m_lex->check_opt,
  914. "optimize", TL_WRITE, 1, 0, 0, 0,
  915. &handler::ha_optimize, 0);
  916. /* ! we write after unlocking the table */
  917. if (!res && !m_lex->no_write_to_binlog)
  918. {
  919. /*
  920. Presumably, OPTIMIZE and binlog writing doesn't require synchronization
  921. */
  922. res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
  923. }
  924. m_lex->select_lex.table_list.first= first_table;
  925. m_lex->query_tables= first_table;
  926. error:
  927. DBUG_RETURN(res);
  928. }
  929. bool Repair_table_statement::execute(THD *thd)
  930. {
  931. TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
  932. bool res= TRUE;
  933. DBUG_ENTER("Repair_table_statement::execute");
  934. if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
  935. FALSE, UINT_MAX, FALSE))
  936. goto error; /* purecov: inspected */
  937. thd->enable_slow_log= opt_log_slow_admin_statements;
  938. res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
  939. TL_WRITE, 1,
  940. test(m_lex->check_opt.sql_flags & TT_USEFRM),
  941. HA_OPEN_FOR_REPAIR, &prepare_for_repair,
  942. &handler::ha_repair, 0);
  943. /* ! we write after unlocking the table */
  944. if (!res && !m_lex->no_write_to_binlog)
  945. {
  946. /*
  947. Presumably, REPAIR and binlog writing doesn't require synchronization
  948. */
  949. res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
  950. }
  951. m_lex->select_lex.table_list.first= first_table;
  952. m_lex->query_tables= first_table;
  953. error:
  954. DBUG_RETURN(res);
  955. }