1 /* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql_class.h" // THD
24 #include "keycaches.h" // get_key_cache
25 #include "sql_base.h" // Open_table_context
26 #include "lock.h" // MYSQL_OPEN_*
27 #include "partition_element.h" // PART_ADMIN
28 #include "sql_partition.h" // set_part_state
29 #include "transaction.h" // trans_rollback_stmt
30 #include "sql_view.h" // view_checksum
31 #include "sql_table.h" // mysql_recreate_table
32 #include "debug_sync.h" // DEBUG_SYNC
33 #include "sql_acl.h" // *_ACL
34 #include "sp.h" // Sroutine_hash_entry
35 #include "sql_parse.h" // check_table_access
36 #include "sql_admin.h"
37
send_check_errmsg(THD * thd,TABLE_LIST * table,const char * operator_name,const char * errmsg)38 static int send_check_errmsg(THD *thd, TABLE_LIST* table,
39 const char* operator_name, const char* errmsg)
40
41 {
42 Protocol *protocol= thd->protocol;
43 protocol->prepare_for_resend();
44 protocol->store(table->alias, system_charset_info);
45 protocol->store((char*) operator_name, system_charset_info);
46 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
47 protocol->store(errmsg, system_charset_info);
48 thd->clear_error();
49 if (protocol->write())
50 return -1;
51 return 1;
52 }
53
54
prepare_for_repair(THD * thd,TABLE_LIST * table_list,HA_CHECK_OPT * check_opt)55 static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
56 HA_CHECK_OPT *check_opt)
57 {
58 int error= 0;
59 TABLE tmp_table, *table;
60 TABLE_SHARE *share;
61 bool has_mdl_lock= FALSE;
62 char from[FN_REFLEN],tmp[FN_REFLEN+32];
63 const char **ext;
64 MY_STAT stat_info;
65 Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
66 MYSQL_OPEN_HAS_MDL_LOCK |
67 MYSQL_LOCK_IGNORE_TIMEOUT));
68 DBUG_ENTER("prepare_for_repair");
69
70 if (!(check_opt->sql_flags & TT_USEFRM))
71 DBUG_RETURN(0);
72
73 if (!(table= table_list->table))
74 {
75 const char *key;
76 uint key_length;
77 /*
78 If the table didn't exist, we have a shared metadata lock
79 on it that is left from mysql_admin_table()'s attempt to
80 open it. Release the shared metadata lock before trying to
81 acquire the exclusive lock to satisfy MDL asserts and avoid
82 deadlocks.
83 */
84 thd->mdl_context.release_transactional_locks();
85 /*
86 Attempt to do full-blown table open in mysql_admin_table() has failed.
87 Let us try to open at least a .FRM for this table.
88 */
89 my_hash_value_type hash_value;
90
91 table_list->mdl_request.init(MDL_key::TABLE,
92 table_list->db, table_list->table_name,
93 MDL_EXCLUSIVE, MDL_TRANSACTION);
94
95 if (lock_table_names(thd, table_list, table_list->next_global,
96 thd->variables.lock_wait_timeout, 0))
97 DBUG_RETURN(0);
98 has_mdl_lock= TRUE;
99
100 key_length= get_table_def_key(table_list, &key);
101
102 hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
103 mysql_mutex_lock(&LOCK_open);
104 share= get_table_share(thd, table_list, key, key_length, 0,
105 &error, hash_value);
106 mysql_mutex_unlock(&LOCK_open);
107 if (share == NULL)
108 DBUG_RETURN(0); // Can't open frm file
109
110 if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
111 {
112 mysql_mutex_lock(&LOCK_open);
113 release_table_share(share);
114 mysql_mutex_unlock(&LOCK_open);
115 DBUG_RETURN(0); // Out of memory
116 }
117 table= &tmp_table;
118 }
119
120 /*
121 REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
122 */
123 if (table->s->tmp_table)
124 {
125 error= send_check_errmsg(thd, table_list, "repair",
126 "Cannot repair temporary table from .frm file");
127 goto end;
128 }
129
130 /*
131 User gave us USE_FRM which means that the header in the index file is
132 trashed.
133 In this case we will try to fix the table the following way:
134 - Rename the data file to a temporary name
135 - Truncate the table
136 - Replace the new data file with the old one
137 - Run a normal repair using the new index file and the old data file
138 */
139
140 if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
141 {
142 error= send_check_errmsg(thd, table_list, "repair",
143 "Failed repairing incompatible .frm file");
144 goto end;
145 }
146
147 /*
148 Check if this is a table type that stores index and data separately,
149 like ISAM or MyISAM. We assume fixed order of engine file name
150 extentions array. First element of engine file name extentions array
151 is meta/index file extention. Second element - data file extention.
152 */
153 ext= table->file->bas_ext();
154 if (!ext[0] || !ext[1])
155 goto end; // No data file
156
157 /* A MERGE table must not come here. */
158 DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
159
160 // Name of data file
161 strxmov(from, table->s->normalized_path.str, ext[1], NullS);
162 if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
163 goto end; // Can't use USE_FRM flag
164
165 my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
166 from, current_pid, thd->thread_id);
167
168 if (table_list->table)
169 {
170 /*
171 Table was successfully open in mysql_admin_table(). Now we need
172 to close it, but leave it protected by exclusive metadata lock.
173 */
174 if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
175 goto end;
176 close_all_tables_for_name(thd, table_list->table->s, false, NULL);
177 table_list->table= 0;
178 }
179 /*
180 After this point we have an exclusive metadata lock on our table
181 in both cases when table was successfully open in mysql_admin_table()
182 and when it was open in prepare_for_repair().
183 */
184
185 if (my_rename(from, tmp, MYF(MY_WME)))
186 {
187 error= send_check_errmsg(thd, table_list, "repair",
188 "Failed renaming data file");
189 goto end;
190 }
191 if (dd_recreate_table(thd, table_list->db, table_list->table_name))
192 {
193 error= send_check_errmsg(thd, table_list, "repair",
194 "Failed generating table from .frm file");
195 goto end;
196 }
197 /*
198 'FALSE' for 'using_transactions' means don't postpone
199 invalidation till the end of a transaction, but do it
200 immediately.
201 */
202 query_cache_invalidate3(thd, table_list, FALSE);
203 if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
204 {
205 error= send_check_errmsg(thd, table_list, "repair",
206 "Failed restoring .MYD file");
207 goto end;
208 }
209
210 if (thd->locked_tables_list.reopen_tables(thd))
211 goto end;
212
213 /*
214 Now we should be able to open the partially repaired table
215 to finish the repair in the handler later on.
216 */
217 if (open_table(thd, table_list, &ot_ctx))
218 {
219 error= send_check_errmsg(thd, table_list, "repair",
220 "Failed to open partially repaired table");
221 goto end;
222 }
223
224 end:
225 thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
226 if (table == &tmp_table)
227 {
228 mysql_mutex_lock(&LOCK_open);
229 closefrm(table, 1); // Free allocated memory
230 mysql_mutex_unlock(&LOCK_open);
231 }
232 /* In case of a temporary table there will be no metadata lock. */
233 if (error && has_mdl_lock)
234 thd->mdl_context.release_transactional_locks();
235
236 DBUG_RETURN(error);
237 }
238
239
240 /**
241 Check if a given error is something that could occur during
242 open_and_lock_tables() that does not indicate table corruption.
243
244 @param sql_errno Error number to check.
245
246 @retval TRUE Error does not indicate table corruption.
247 @retval FALSE Error could indicate table corruption.
248 */
249
table_not_corrupt_error(uint sql_errno)250 static inline bool table_not_corrupt_error(uint sql_errno)
251 {
252 return (sql_errno == ER_NO_SUCH_TABLE ||
253 sql_errno == ER_FILE_NOT_FOUND ||
254 sql_errno == ER_LOCK_WAIT_TIMEOUT ||
255 sql_errno == ER_LOCK_DEADLOCK ||
256 sql_errno == ER_CANT_LOCK_LOG_TABLE ||
257 sql_errno == ER_OPEN_AS_READONLY ||
258 sql_errno == ER_WRONG_OBJECT);
259 }
260
261
262 /*
263 RETURN VALUES
264 FALSE Message sent to net (admin operation went ok)
265 TRUE Message should be sent by caller
266 (admin operation or network communication failed)
267 */
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 repair_table_use_frm,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 *))268 static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
269 HA_CHECK_OPT* check_opt,
270 const char *operator_name,
271 thr_lock_type lock_type,
272 bool open_for_modify,
273 bool repair_table_use_frm,
274 uint extra_open_options,
275 int (*prepare_func)(THD *, TABLE_LIST *,
276 HA_CHECK_OPT *),
277 int (handler::*operator_func)(THD *,
278 HA_CHECK_OPT *),
279 int (view_operator_func)(THD *, TABLE_LIST*))
280 {
281 TABLE_LIST *table;
282 SELECT_LEX *select= &thd->lex->select_lex;
283 List<Item> field_list;
284 Item *item;
285 Protocol *protocol= thd->protocol;
286 LEX *lex= thd->lex;
287 int result_code;
288 bool gtid_rollback_must_be_skipped=
289 ((thd->variables.gtid_next.type == GTID_GROUP) &&
290 (!thd->skip_gtid_rollback));
291 DBUG_ENTER("mysql_admin_table");
292
293 field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
294 item->maybe_null = 1;
295 field_list.push_back(item = new Item_empty_string("Op", 10));
296 item->maybe_null = 1;
297 field_list.push_back(item = new Item_empty_string("Msg_type", 10));
298 item->maybe_null = 1;
299 field_list.push_back(item = new Item_empty_string("Msg_text",
300 SQL_ADMIN_MSG_TEXT_SIZE));
301 item->maybe_null = 1;
302 if (protocol->send_result_set_metadata(&field_list,
303 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
304 DBUG_RETURN(TRUE);
305
306 /*
307 Close all temporary tables which were pre-open to simplify
308 privilege checking. Clear all references to closed tables.
309 */
310 close_thread_tables(thd);
311 for (table= tables; table; table= table->next_local)
312 table->table= NULL;
313
314 /*
315 If this statement goes to binlog and GTID_NEXT was set to a GTID_GROUP
316 (like SQL thread do when applying statements from the relay log of a
317 master server with GTIDs enabled) we have to avoid losing the ownership of
318 the GTID_GROUP by some trans_rollback_stmt() when processing individual
319 tables.
320 */
321 if (gtid_rollback_must_be_skipped)
322 thd->skip_gtid_rollback= true;
323
324 for (table= tables; table; table= table->next_local)
325 {
326 char table_name[NAME_LEN*2+2];
327 char* db = table->db;
328 bool fatal_error=0;
329 bool open_error;
330
331 DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
332 DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
333 strxmov(table_name, db, ".", table->table_name, NullS);
334 thd->open_options|= extra_open_options;
335 table->lock_type= lock_type;
336 /*
337 To make code safe for re-execution we need to reset type of MDL
338 request as code below may change it.
339 To allow concurrent execution of read-only operations we acquire
340 weak metadata lock for them.
341 */
342 table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
343 MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
344 /* open only one table from local list of command */
345 {
346 TABLE_LIST *save_next_global, *save_next_local;
347 save_next_global= table->next_global;
348 table->next_global= 0;
349 save_next_local= table->next_local;
350 table->next_local= 0;
351 select->table_list.first= table;
352 /*
353 Time zone tables and SP tables can be add to lex->query_tables list,
354 so it have to be prepared.
355 TODO: Investigate if we can put extra tables into argument instead of
356 using lex->query_tables
357 */
358 lex->query_tables= table;
359 lex->query_tables_last= &table->next_global;
360 lex->query_tables_own_last= 0;
361
362 /*
363 CHECK TABLE command is allowed for views as well. Check on alter flags
364 to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
365 allowed.
366 */
367 if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
368 view_operator_func == NULL)
369 table->required_type=FRMTYPE_TABLE;
370
371 if (!thd->locked_tables_mode && repair_table_use_frm)
372 {
373 /*
374 If we're not under LOCK TABLES and we're executing REPAIR TABLE
375 USE_FRM, we need to ignore errors from open_and_lock_tables().
376 REPAIR TABLE USE_FRM is a heavy weapon used when a table is
377 critically damaged, so open_and_lock_tables() will most likely
378 report errors. Those errors are not interesting for the user
379 because it's already known that the table is badly damaged.
380 */
381
382 Diagnostics_area *da= thd->get_stmt_da();
383 Warning_info tmp_wi(thd->query_id, false);
384
385 da->push_warning_info(&tmp_wi);
386
387 open_error= open_temporary_tables(thd, table);
388
389 if (!open_error)
390 open_error= open_and_lock_tables(thd, table, TRUE, 0);
391
392 da->pop_warning_info();
393 }
394 else
395 {
396 /*
397 It's assumed that even if it is REPAIR TABLE USE_FRM, the table
398 can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
399 would fail). Thus, the only errors we could have from
400 open_and_lock_tables() are logical ones, like incorrect locking
401 mode. It does make sense for the user to see such errors.
402 */
403
404 open_error= open_temporary_tables(thd, table);
405
406 if (!open_error)
407 open_error= open_and_lock_tables(thd, table, TRUE, 0);
408 }
409
410 table->next_global= save_next_global;
411 table->next_local= save_next_local;
412 thd->open_options&= ~extra_open_options;
413
414 /*
415 If open_and_lock_tables() failed, close_thread_tables() will close
416 the table and table->table can therefore be invalid.
417 */
418 if (open_error)
419 table->table= NULL;
420
421 /*
422 Under locked tables, we know that the table can be opened,
423 so any errors opening the table are logical errors.
424 In these cases it does not make sense to try to repair.
425 */
426 if (open_error && thd->locked_tables_mode)
427 {
428 result_code= HA_ADMIN_FAILED;
429 goto send_result;
430 }
431 #ifdef WITH_PARTITION_STORAGE_ENGINE
432 if (table->table)
433 {
434 /*
435 Set up which partitions that should be processed
436 if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
437 CACHE INDEX/LOAD INDEX for specified partitions
438 */
439 Alter_info *alter_info= &lex->alter_info;
440
441 if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
442 {
443 if (!table->table->part_info)
444 {
445 my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
446 result_code= HA_ADMIN_FAILED;
447 goto send_result;
448 }
449
450 if (set_part_state(alter_info, table->table->part_info, PART_ADMIN))
451 {
452 my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), table_name);
453 result_code= HA_ADMIN_FAILED;
454 goto send_result;
455 }
456 }
457 }
458 #endif
459 }
460 DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
461
462 if (prepare_func)
463 {
464 DBUG_PRINT("admin", ("calling prepare_func"));
465 switch ((*prepare_func)(thd, table, check_opt)) {
466 case 1: // error, message written to net
467 trans_rollback_stmt(thd);
468 trans_rollback(thd);
469 /* Make sure this table instance is not reused after the operation. */
470 if (table->table)
471 table->table->m_needs_reopen= true;
472 close_thread_tables(thd);
473 thd->mdl_context.release_transactional_locks();
474 DBUG_PRINT("admin", ("simple error, admin next table"));
475 continue;
476 case -1: // error, message could be written to net
477 /* purecov: begin inspected */
478 DBUG_PRINT("admin", ("severe error, stop"));
479 goto err;
480 /* purecov: end */
481 default: // should be 0 otherwise
482 DBUG_PRINT("admin", ("prepare_func succeeded"));
483 ;
484 }
485 }
486
487 /*
488 CHECK TABLE command is only command where VIEW allowed here and this
489 command use only temporary teble method for VIEWs resolving => there
490 can't be VIEW tree substitition of join view => if opening table
491 succeed then table->table will have real TABLE pointer as value (in
492 case of join view substitution table->table can be 0, but here it is
493 impossible)
494 */
495 if (!table->table)
496 {
497 DBUG_PRINT("admin", ("open table failed"));
498 if (thd->get_stmt_da()->is_warning_info_empty())
499 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
500 ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
501 /* if it was a view will check md5 sum */
502 if (table->view &&
503 view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
504 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
505 ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
506 if (thd->get_stmt_da()->is_error() &&
507 table_not_corrupt_error(thd->get_stmt_da()->sql_errno()))
508 result_code= HA_ADMIN_FAILED;
509 else
510 /* Default failure code is corrupt table */
511 result_code= HA_ADMIN_CORRUPT;
512 goto send_result;
513 }
514
515 if (table->view)
516 {
517 DBUG_PRINT("admin", ("calling view_operator_func"));
518 result_code= (*view_operator_func)(thd, table);
519 goto send_result;
520 }
521
522 if (table->schema_table)
523 {
524 result_code= HA_ADMIN_NOT_IMPLEMENTED;
525 goto send_result;
526 }
527
528 if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
529 {
530 /* purecov: begin inspected */
531 char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
532 size_t length;
533 enum_sql_command save_sql_command= lex->sql_command;
534 DBUG_PRINT("admin", ("sending error message"));
535 protocol->prepare_for_resend();
536 protocol->store(table_name, system_charset_info);
537 protocol->store(operator_name, system_charset_info);
538 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
539 length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
540 table_name);
541 protocol->store(buff, length, system_charset_info);
542 trans_commit_stmt(thd);
543 trans_commit(thd);
544 /* Make sure this table instance is not reused after the operation. */
545 if (table->table)
546 table->table->m_needs_reopen= true;
547 close_thread_tables(thd);
548 thd->mdl_context.release_transactional_locks();
549 lex->reset_query_tables_list(FALSE);
550 /*
551 Restore Query_tables_list::sql_command value to make statement
552 safe for re-execution.
553 */
554 lex->sql_command= save_sql_command;
555 table->table=0; // For query cache
556 if (protocol->write())
557 goto err;
558 thd->get_stmt_da()->reset_diagnostics_area();
559 continue;
560 /* purecov: end */
561 }
562
563 /*
564 Close all instances of the table to allow MyISAM "repair"
565 to rename files.
566 @todo: This code does not close all instances of the table.
567 It only closes instances in other connections, but if this
568 connection has LOCK TABLE t1 a READ, t1 b WRITE,
569 both t1 instances will be kept open.
570 There is no need to execute this branch for InnoDB, which does
571 repair by recreate. There is no need to do it for OPTIMIZE,
572 which doesn't move files around.
573 Hence, this code should be moved to prepare_for_repair(),
574 and executed only for MyISAM engine.
575 */
576 if (lock_type == TL_WRITE && !table->table->s->tmp_table)
577 {
578 if (wait_while_table_is_used(thd, table->table,
579 HA_EXTRA_PREPARE_FOR_RENAME))
580 goto err;
581 DEBUG_SYNC(thd, "after_admin_flush");
582 /* Flush entries in the query cache involving this table. */
583 query_cache_invalidate3(thd, table->table, 0);
584 /*
585 XXX: hack: switch off open_for_modify to skip the
586 flush that is made later in the execution flow.
587 */
588 open_for_modify= 0;
589 }
590
591 if (table->table->s->crashed && operator_func == &handler::ha_check)
592 {
593 /* purecov: begin inspected */
594 DBUG_PRINT("admin", ("sending crashed warning"));
595 protocol->prepare_for_resend();
596 protocol->store(table_name, system_charset_info);
597 protocol->store(operator_name, system_charset_info);
598 protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
599 protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
600 system_charset_info);
601 if (protocol->write())
602 goto err;
603 /* purecov: end */
604 }
605
606 if (operator_func == &handler::ha_repair &&
607 !(check_opt->sql_flags & TT_USEFRM))
608 {
609 if ((table->table->file->check_old_types() == HA_ADMIN_NEEDS_ALTER) ||
610 (table->table->file->ha_check_for_upgrade(check_opt) ==
611 HA_ADMIN_NEEDS_ALTER))
612 {
613 DBUG_PRINT("admin", ("recreating table"));
614 /*
615 Temporary table are always created by current server so they never
616 require upgrade. So we don't need to pre-open them before calling
617 mysql_recreate_table().
618 */
619 DBUG_ASSERT(! table->table->s->tmp_table);
620
621 trans_rollback_stmt(thd);
622 trans_rollback(thd);
623 /* Make sure this table instance is not reused after the operation. */
624 if (table->table)
625 table->table->m_needs_reopen= true;
626 close_thread_tables(thd);
627 thd->mdl_context.release_transactional_locks();
628
629 /*
630 table_list->table has been closed and freed. Do not reference
631 uninitialized data. open_tables() could fail.
632 */
633 table->table= NULL;
634 /* Same applies to MDL ticket. */
635 table->mdl_request.ticket= NULL;
636
637 tmp_disable_binlog(thd); // binlogging is done by caller if wanted
638 result_code= mysql_recreate_table(thd, table, false);
639 reenable_binlog(thd);
640 /*
641 mysql_recreate_table() can push OK or ERROR.
642 Clear 'OK' status. If there is an error, keep it:
643 we will store the error message in a result set row
644 and then clear.
645 */
646 if (thd->get_stmt_da()->is_ok())
647 thd->get_stmt_da()->reset_diagnostics_area();
648 table->table= NULL;
649 result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
650 goto send_result;
651 }
652 }
653
654 DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
655 result_code = (table->table->file->*operator_func)(thd, check_opt);
656 DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
657
658 send_result:
659
660 lex->cleanup_after_one_table_open();
661 thd->clear_error(); // these errors shouldn't get client
662 {
663 Diagnostics_area::Sql_condition_iterator it=
664 thd->get_stmt_da()->sql_conditions();
665 const Sql_condition *err;
666 while ((err= it++))
667 {
668 protocol->prepare_for_resend();
669 protocol->store(table_name, system_charset_info);
670 protocol->store((char*) operator_name, system_charset_info);
671 protocol->store(warning_level_names[err->get_level()].str,
672 warning_level_names[err->get_level()].length,
673 system_charset_info);
674 protocol->store(err->get_message_text(), system_charset_info);
675 if (protocol->write())
676 goto err;
677 }
678 thd->get_stmt_da()->clear_warning_info(thd->query_id);
679 }
680 protocol->prepare_for_resend();
681 protocol->store(table_name, system_charset_info);
682 protocol->store(operator_name, system_charset_info);
683
684 send_result_message:
685
686 DBUG_PRINT("info", ("result_code: %d", result_code));
687 switch (result_code) {
688 case HA_ADMIN_NOT_IMPLEMENTED:
689 {
690 char buf[MYSQL_ERRMSG_SIZE];
691 size_t length=my_snprintf(buf, sizeof(buf),
692 ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
693 protocol->store(STRING_WITH_LEN("note"), system_charset_info);
694 protocol->store(buf, length, system_charset_info);
695 }
696 break;
697
698 case HA_ADMIN_NOT_BASE_TABLE:
699 {
700 char buf[MYSQL_ERRMSG_SIZE];
701
702 String tbl_name;
703 tbl_name.append(String(db,system_charset_info));
704 tbl_name.append('.');
705 tbl_name.append(String(table_name,system_charset_info));
706
707 size_t length= my_snprintf(buf, sizeof(buf),
708 ER(ER_BAD_TABLE_ERROR), tbl_name.c_ptr());
709 protocol->store(STRING_WITH_LEN("note"), system_charset_info);
710 protocol->store(buf, length, system_charset_info);
711 }
712 break;
713
714 case HA_ADMIN_OK:
715 protocol->store(STRING_WITH_LEN("status"), system_charset_info);
716 protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
717 break;
718
719 case HA_ADMIN_FAILED:
720 protocol->store(STRING_WITH_LEN("status"), system_charset_info);
721 protocol->store(STRING_WITH_LEN("Operation failed"),
722 system_charset_info);
723 break;
724
725 case HA_ADMIN_REJECT:
726 protocol->store(STRING_WITH_LEN("status"), system_charset_info);
727 protocol->store(STRING_WITH_LEN("Operation need committed state"),
728 system_charset_info);
729 open_for_modify= FALSE;
730 break;
731
732 case HA_ADMIN_ALREADY_DONE:
733 protocol->store(STRING_WITH_LEN("status"), system_charset_info);
734 protocol->store(STRING_WITH_LEN("Table is already up to date"),
735 system_charset_info);
736 break;
737
738 case HA_ADMIN_CORRUPT:
739 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
740 protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
741 fatal_error=1;
742 break;
743
744 case HA_ADMIN_INVALID:
745 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
746 protocol->store(STRING_WITH_LEN("Invalid argument"),
747 system_charset_info);
748 break;
749
750 case HA_ADMIN_TRY_ALTER:
751 {
752 uint save_flags;
753 Alter_info *alter_info= &lex->alter_info;
754
755 /* Store the original value of alter_info->flags */
756 save_flags= alter_info->flags;
757 /*
758 This is currently used only by InnoDB. ha_innobase::optimize() answers
759 "try with alter", so here we close the table, do an ALTER TABLE,
760 reopen the table and do ha_innobase::analyze() on it.
761 We have to end the row, so analyze could return more rows.
762 */
763 trans_commit_stmt(thd);
764 trans_commit(thd);
765 close_thread_tables(thd);
766 thd->mdl_context.release_transactional_locks();
767
768 /*
769 table_list->table has been closed and freed. Do not reference
770 uninitialized data. open_tables() could fail.
771 */
772 table->table= NULL;
773 /* Same applies to MDL ticket. */
774 table->mdl_request.ticket= NULL;
775
776 DEBUG_SYNC(thd, "ha_admin_try_alter");
777 protocol->store(STRING_WITH_LEN("note"), system_charset_info);
778 if(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
779 {
780 protocol->store(STRING_WITH_LEN(
781 "Table does not support optimize on partitions. All partitions "
782 "will be rebuilt and analyzed."),system_charset_info);
783 }
784 else
785 {
786 protocol->store(STRING_WITH_LEN(
787 "Table does not support optimize, doing recreate + analyze instead"),
788 system_charset_info);
789 }
790 if (protocol->write())
791 goto err;
792 DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
793 TABLE_LIST *save_next_local= table->next_local,
794 *save_next_global= table->next_global;
795 table->next_local= table->next_global= 0;
796 tmp_disable_binlog(thd); // binlogging is done by caller if wanted
797 /* Don't forget to pre-open temporary tables. */
798 result_code= (open_temporary_tables(thd, table) ||
799 mysql_recreate_table(thd, table, false));
800 reenable_binlog(thd);
801 /*
802 mysql_recreate_table() can push OK or ERROR.
803 Clear 'OK' status. If there is an error, keep it:
804 we will store the error message in a result set row
805 and then clear.
806 */
807 if (thd->get_stmt_da()->is_ok())
808 thd->get_stmt_da()->reset_diagnostics_area();
809 trans_commit_stmt(thd);
810 trans_commit(thd);
811 close_thread_tables(thd);
812 thd->mdl_context.release_transactional_locks();
813 /* Clear references to TABLE and MDL_ticket after releasing them. */
814 table->table= NULL;
815 table->mdl_request.ticket= NULL;
816 if (!result_code) // recreation went ok
817 {
818 DEBUG_SYNC(thd, "ha_admin_open_ltable");
819 table->mdl_request.set_type(MDL_SHARED_READ);
820 if (!open_temporary_tables(thd, table) &&
821 (table->table= open_n_lock_single_table(thd, table,
822 TL_READ_NO_INSERT, 0)))
823 {
824 /*
825 Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
826 to force analyze on all partitions.
827 */
828 alter_info->flags &= ~(Alter_info::ALTER_ADMIN_PARTITION);
829 result_code= table->table->file->ha_analyze(thd, check_opt);
830 if (result_code == HA_ADMIN_ALREADY_DONE)
831 result_code= HA_ADMIN_OK;
832 else if (result_code) // analyze failed
833 table->table->file->print_error(result_code, MYF(0));
834 alter_info->flags= save_flags;
835 }
836 else
837 result_code= -1; // open failed
838 }
839 /* Start a new row for the final status row */
840 protocol->prepare_for_resend();
841 protocol->store(table_name, system_charset_info);
842 protocol->store(operator_name, system_charset_info);
843 if (result_code) // either mysql_recreate_table or analyze failed
844 {
845 DBUG_ASSERT(thd->is_error() || thd->killed);
846 if (thd->is_error())
847 {
848 const char *err_msg= thd->get_stmt_da()->message();
849 if (!thd->vio_ok())
850 {
851 sql_print_error("%s", err_msg);
852 }
853 else
854 {
855 /* Hijack the row already in-progress. */
856 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
857 protocol->store(err_msg, system_charset_info);
858 if (protocol->write())
859 goto err;
860 /* Start off another row for HA_ADMIN_FAILED */
861 protocol->prepare_for_resend();
862 protocol->store(table_name, system_charset_info);
863 protocol->store(operator_name, system_charset_info);
864 }
865 thd->clear_error();
866 }
867 /* Make sure this table instance is not reused after the operation. */
868 if (table->table)
869 table->table->m_needs_reopen= true;
870 }
871 result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
872 table->next_local= save_next_local;
873 table->next_global= save_next_global;
874 goto send_result_message;
875 }
876 case HA_ADMIN_WRONG_CHECKSUM:
877 {
878 protocol->store(STRING_WITH_LEN("note"), system_charset_info);
879 protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
880 system_charset_info);
881 break;
882 }
883
884 case HA_ADMIN_NEEDS_UPGRADE:
885 case HA_ADMIN_NEEDS_ALTER:
886 {
887 char buf[MYSQL_ERRMSG_SIZE];
888 size_t length;
889
890 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
891 if (table->table->file->ha_table_flags() & HA_CAN_REPAIR)
892 length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
893 table->table_name);
894 else
895 length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_REBUILD),
896 table->table_name);
897 protocol->store(buf, length, system_charset_info);
898 fatal_error=1;
899 break;
900 }
901
902 default: // Probably HA_ADMIN_INTERNAL_ERROR
903 {
904 char buf[MYSQL_ERRMSG_SIZE];
905 size_t length=my_snprintf(buf, sizeof(buf),
906 "Unknown - internal error %d during operation",
907 result_code);
908 protocol->store(STRING_WITH_LEN("error"), system_charset_info);
909 protocol->store(buf, length, system_charset_info);
910 fatal_error=1;
911 break;
912 }
913 }
914 if (table->table)
915 {
916 if (table->table->s->tmp_table)
917 {
918 /*
919 If the table was not opened successfully, do not try to get
920 status information. (Bug#47633)
921 */
922 if (open_for_modify && !open_error)
923 table->table->file->info(HA_STATUS_CONST);
924 }
925 else if (open_for_modify || fatal_error)
926 {
927 tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
928 table->db, table->table_name, FALSE);
929 /*
930 May be something modified. Consequently, we have to
931 invalidate the query cache.
932 */
933 table->table= 0; // For query cache
934 query_cache_invalidate3(thd, table, 0);
935 }
936 }
937 /* Error path, a admin command failed. */
938 if (thd->transaction_rollback_request)
939 {
940 /*
941 Unlikely, but transaction rollback was requested by one of storage
942 engines (e.g. due to deadlock). Perform it.
943 */
944 if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd))
945 goto err;
946 }
947 else
948 {
949 if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
950 goto err;
951 }
952 close_thread_tables(thd);
953 thd->mdl_context.release_transactional_locks();
954
955 /*
956 If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
957 separate open_tables() for each CHECK TABLE argument.
958 Right now we do not have a separate method to reset the prelocking
959 state in the lex to the state after parsing, so each open will pollute
960 this state: add elements to lex->srotuines_list, TABLE_LISTs to
961 lex->query_tables. Below is a lame attempt to recover from this
962 pollution.
963 @todo: have a method to reset a prelocking context, or use separate
964 contexts for each open.
965 */
966 for (Sroutine_hash_entry *rt=
967 (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
968 rt; rt= rt->next)
969 rt->mdl_request.ticket= NULL;
970
971 if (protocol->write())
972 goto err;
973 }
974
975 my_eof(thd);
976
977 if (gtid_rollback_must_be_skipped)
978 thd->skip_gtid_rollback= false;
979
980 DBUG_RETURN(FALSE);
981
982 err:
983 if (gtid_rollback_must_be_skipped)
984 thd->skip_gtid_rollback= false;
985
986 trans_rollback_stmt(thd);
987 trans_rollback(thd);
988 /* Make sure this table instance is not reused after the operation. */
989 if (table->table)
990 table->table->m_needs_reopen= true;
991 close_thread_tables(thd); // Shouldn't be needed
992 thd->mdl_context.release_transactional_locks();
993 DBUG_RETURN(TRUE);
994 }
995
996
997 /*
998 Assigned specified indexes for a table into key cache
999
1000 SYNOPSIS
1001 mysql_assign_to_keycache()
1002 thd Thread object
1003 tables Table list (one table only)
1004
1005 RETURN VALUES
1006 FALSE ok
1007 TRUE error
1008 */
1009
mysql_assign_to_keycache(THD * thd,TABLE_LIST * tables,LEX_STRING * key_cache_name)1010 bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
1011 LEX_STRING *key_cache_name)
1012 {
1013 HA_CHECK_OPT check_opt;
1014 KEY_CACHE *key_cache;
1015 DBUG_ENTER("mysql_assign_to_keycache");
1016
1017 check_opt.init();
1018 mysql_mutex_lock(&LOCK_global_system_variables);
1019 if (!(key_cache= get_key_cache(key_cache_name)))
1020 {
1021 mysql_mutex_unlock(&LOCK_global_system_variables);
1022 my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
1023 DBUG_RETURN(TRUE);
1024 }
1025 mysql_mutex_unlock(&LOCK_global_system_variables);
1026 if (!key_cache->key_cache_inited)
1027 {
1028 my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
1029 DBUG_RETURN(true);
1030 }
1031 check_opt.key_cache= key_cache;
1032 DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
1033 "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
1034 0, 0, &handler::assign_to_keycache, 0));
1035 }
1036
1037
1038 /*
1039 Preload specified indexes for a table into key cache
1040
1041 SYNOPSIS
1042 mysql_preload_keys()
1043 thd Thread object
1044 tables Table list (one table only)
1045
1046 RETURN VALUES
1047 FALSE ok
1048 TRUE error
1049 */
1050
mysql_preload_keys(THD * thd,TABLE_LIST * tables)1051 bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
1052 {
1053 DBUG_ENTER("mysql_preload_keys");
1054 /*
1055 We cannot allow concurrent inserts. The storage engine reads
1056 directly from the index file, bypassing the cache. It could read
1057 outdated information if parallel inserts into cache blocks happen.
1058 */
1059 DBUG_RETURN(mysql_admin_table(thd, tables, 0,
1060 "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
1061 &handler::preload_keys, 0));
1062 }
1063
1064
execute(THD * thd)1065 bool Sql_cmd_analyze_table::execute(THD *thd)
1066 {
1067 TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
1068 bool res= TRUE;
1069 thr_lock_type lock_type = TL_READ_NO_INSERT;
1070 DBUG_ENTER("Sql_cmd_analyze_table::execute");
1071
1072 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
1073 FALSE, UINT_MAX, FALSE))
1074 goto error;
1075 thd->enable_slow_log= opt_log_slow_admin_statements;
1076 res= mysql_admin_table(thd, first_table, &thd->lex->check_opt,
1077 "analyze", lock_type, 1, 0, 0, 0,
1078 &handler::ha_analyze, 0);
1079 /* ! we write after unlocking the table */
1080 if (!res && !thd->lex->no_write_to_binlog)
1081 {
1082 /*
1083 Presumably, ANALYZE and binlog writing doesn't require synchronization
1084 */
1085 res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
1086 }
1087 thd->lex->select_lex.table_list.first= first_table;
1088 thd->lex->query_tables= first_table;
1089
1090 error:
1091 DBUG_RETURN(res);
1092 }
1093
1094
execute(THD * thd)1095 bool Sql_cmd_check_table::execute(THD *thd)
1096 {
1097 TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
1098 thr_lock_type lock_type = TL_READ_NO_INSERT;
1099 bool res= TRUE;
1100 DBUG_ENTER("Sql_cmd_check_table::execute");
1101
1102 if (check_table_access(thd, SELECT_ACL, first_table,
1103 TRUE, UINT_MAX, FALSE))
1104 goto error; /* purecov: inspected */
1105 thd->enable_slow_log= opt_log_slow_admin_statements;
1106
1107 res= mysql_admin_table(thd, first_table, &thd->lex->check_opt, "check",
1108 lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
1109 &handler::ha_check, &view_checksum);
1110
1111 thd->lex->select_lex.table_list.first= first_table;
1112 thd->lex->query_tables= first_table;
1113
1114 error:
1115 DBUG_RETURN(res);
1116 }
1117
1118
execute(THD * thd)1119 bool Sql_cmd_optimize_table::execute(THD *thd)
1120 {
1121 TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
1122 bool res= TRUE;
1123 DBUG_ENTER("Sql_cmd_optimize_table::execute");
1124
1125 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
1126 FALSE, UINT_MAX, FALSE))
1127 goto error; /* purecov: inspected */
1128 thd->enable_slow_log= opt_log_slow_admin_statements;
1129 res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
1130 mysql_recreate_table(thd, first_table, true) :
1131 mysql_admin_table(thd, first_table, &thd->lex->check_opt,
1132 "optimize", TL_WRITE, 1, 0, 0, 0,
1133 &handler::ha_optimize, 0);
1134 /* ! we write after unlocking the table */
1135 if (!res && !thd->lex->no_write_to_binlog)
1136 {
1137 /*
1138 Presumably, OPTIMIZE and binlog writing doesn't require synchronization
1139 */
1140 res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
1141 }
1142 thd->lex->select_lex.table_list.first= first_table;
1143 thd->lex->query_tables= first_table;
1144
1145 error:
1146 DBUG_RETURN(res);
1147 }
1148
1149
execute(THD * thd)1150 bool Sql_cmd_repair_table::execute(THD *thd)
1151 {
1152 TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
1153 bool res= TRUE;
1154 DBUG_ENTER("Sql_cmd_repair_table::execute");
1155
1156 if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
1157 FALSE, UINT_MAX, FALSE))
1158 goto error; /* purecov: inspected */
1159 thd->enable_slow_log= opt_log_slow_admin_statements;
1160 res= mysql_admin_table(thd, first_table, &thd->lex->check_opt, "repair",
1161 TL_WRITE, 1,
1162 MY_TEST(thd->lex->check_opt.sql_flags & TT_USEFRM),
1163 HA_OPEN_FOR_REPAIR, &prepare_for_repair,
1164 &handler::ha_repair, 0);
1165
1166 /* ! we write after unlocking the table */
1167 if (!res && !thd->lex->no_write_to_binlog)
1168 {
1169 /*
1170 Presumably, REPAIR and binlog writing doesn't require synchronization
1171 */
1172 res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
1173 }
1174 thd->lex->select_lex.table_list.first= first_table;
1175 thd->lex->query_tables= first_table;
1176
1177 error:
1178 DBUG_RETURN(res);
1179 }
1180