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