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