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