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