1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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 Foundation,
21    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 /*
24   Atomic rename of table;  RENAME TABLE t1 to t2, tmp to t1 [,...]
25 */
26 
27 #include "sql_rename.h"
28 #include "sql_cache.h"                          // query_cache_*
29 #include "sql_table.h"                         // build_table_filename
30 #include "sql_trigger.h"          // change_trigger_table_name
31 #include "sql_view.h"             // mysql_frm_type, mysql_rename_view
32 #include "lock.h"       // MYSQL_OPEN_SKIP_TEMPORARY
33 #include "sql_base.h"   // tdc_remove_table, lock_table_names,
34 #include "sql_handler.h"                        // mysql_ha_rm_tables
35 #include "datadict.h"
36 #include "log.h"
37 
38 static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
39 				 bool skip_error);
40 
41 static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
42 
43 /*
44   Every two entries in the table_list form a pair of original name and
45   the new name.
46 */
47 
mysql_rename_tables(THD * thd,TABLE_LIST * table_list,bool silent)48 bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
49 {
50   bool error= 1;
51   bool binlog_error= 0;
52   TABLE_LIST *ren_table= 0;
53   int to_table;
54   char *rename_log_table[2]= {NULL, NULL};
55   DBUG_ENTER("mysql_rename_tables");
56 
57   /*
58     Avoid problems with a rename on a table that we have locked or
59     if the user is trying to to do this in a transcation context
60   */
61 
62   if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
63   {
64     my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
65                ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
66     DBUG_RETURN(1);
67   }
68 
69   mysql_ha_rm_tables(thd, table_list);
70 
71   if (query_logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
72       query_logger.is_log_table_enabled(QUERY_LOG_SLOW))
73   {
74 
75     /*
76       Rules for rename of a log table:
77 
78       IF   1. Log tables are enabled
79       AND  2. Rename operates on the log table and nothing is being
80               renamed to the log table.
81       DO   3. Throw an error message.
82       ELSE 4. Perform rename.
83     */
84 
85     for (to_table= 0, ren_table= table_list; ren_table;
86          to_table= 1 - to_table, ren_table= ren_table->next_local)
87     {
88       int log_table_rename= 0;
89 
90       if ((log_table_rename= query_logger.check_if_log_table(ren_table, true)))
91       {
92         /*
93           as we use log_table_rename as an array index, we need it to start
94           with 0, while QUERY_LOG_SLOW == 1 and QUERY_LOG_GENERAL == 2.
95           So, we shift the value to start with 0;
96         */
97         log_table_rename--;
98         if (rename_log_table[log_table_rename])
99         {
100           if (to_table)
101             rename_log_table[log_table_rename]= NULL;
102           else
103           {
104             /*
105               Two renames of "log_table TO" w/o rename "TO log_table" in
106               between.
107             */
108             my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
109                      ren_table->table_name);
110             goto err;
111           }
112         }
113         else
114         {
115           if (to_table)
116           {
117             /*
118               Attempt to rename a table TO log_table w/o renaming
119               log_table TO some table.
120             */
121             my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
122                      ren_table->table_name);
123             goto err;
124           }
125           else
126           {
127             /* save the name of the log table to report an error */
128             rename_log_table[log_table_rename]=
129               const_cast<char*>(ren_table->table_name);
130           }
131         }
132       }
133     }
134     if (rename_log_table[0] || rename_log_table[1])
135     {
136       if (rename_log_table[0])
137         my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[0],
138                  rename_log_table[0]);
139       else
140         my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[1],
141                  rename_log_table[1]);
142       goto err;
143     }
144   }
145 
146   if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, 0))
147     goto err;
148 
149   for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
150     tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
151                      ren_table->table_name, FALSE);
152 
153   error=0;
154   /*
155     An exclusive lock on table names is satisfactory to ensure
156     no other thread accesses this table.
157   */
158   if ((ren_table=rename_tables(thd,table_list,0)))
159   {
160     /* Rename didn't succeed;  rename back the tables in reverse order */
161     TABLE_LIST *table;
162 
163     /* Reverse the table list */
164     table_list= reverse_table_list(table_list);
165 
166     /* Find the last renamed table */
167     for (table= table_list;
168 	 table->next_local != ren_table ;
169 	 table= table->next_local->next_local) ;
170     table= table->next_local->next_local;		// Skip error table
171     /* Revert to old names */
172     rename_tables(thd, table, 1);
173 
174     /* Revert the table list (for prepared statements) */
175     table_list= reverse_table_list(table_list);
176 
177     error= 1;
178   }
179 
180   if (!silent && !error)
181   {
182     binlog_error= write_bin_log(thd, true,
183                                 thd->query().str, thd->query().length);
184     if (!binlog_error)
185       my_ok(thd);
186   }
187 
188   if (!error)
189     query_cache.invalidate(thd, table_list, FALSE);
190 
191 err:
192   DBUG_RETURN(error || binlog_error);
193 }
194 
195 
196 /*
197   reverse table list
198 
199   SYNOPSIS
200     reverse_table_list()
201     table_list pointer to table _list
202 
203   RETURN
204     pointer to new (reversed) list
205 */
reverse_table_list(TABLE_LIST * table_list)206 static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list)
207 {
208   TABLE_LIST *prev= 0;
209 
210   while (table_list)
211   {
212     TABLE_LIST *next= table_list->next_local;
213     table_list->next_local= prev;
214     prev= table_list;
215     table_list= next;
216   }
217   return (prev);
218 }
219 
220 
221 /*
222   Rename a single table or a view
223 
224   SYNPOSIS
225     do_rename()
226       thd               Thread handle
227       ren_table         A table/view to be renamed
228       new_db            The database to which the table to be moved to
229       new_table_name    The new table/view name
230       new_table_alias   The new table/view alias
231       skip_error        Whether to skip error
232 
233   DESCRIPTION
234     Rename a single table or a view.
235 
236   RETURN
237     false     Ok
238     true      rename failed
239 */
240 
241 bool
do_rename(THD * thd,TABLE_LIST * ren_table,const char * new_db,const char * new_table_name,const char * new_table_alias,bool skip_error)242 do_rename(THD *thd, TABLE_LIST *ren_table,
243           const char *new_db, const char *new_table_name,
244           const char *new_table_alias, bool skip_error)
245 {
246   int rc= 1;
247   char name[FN_REFLEN + 1];
248   const char *new_alias, *old_alias;
249   frm_type_enum frm_type;
250   enum legacy_db_type table_type;
251 
252   DBUG_ENTER("do_rename");
253 
254   if (lower_case_table_names == 2)
255   {
256     old_alias= ren_table->alias;
257     new_alias= new_table_alias;
258   }
259   else
260   {
261     old_alias= ren_table->table_name;
262     new_alias= new_table_name;
263   }
264   assert(new_alias);
265 
266   build_table_filename(name, sizeof(name) - 1,
267                        new_db, new_alias, reg_ext, 0);
268   if (!access(name,F_OK))
269   {
270     my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
271     DBUG_RETURN(1);			// This can't be skipped
272   }
273   build_table_filename(name, sizeof(name) - 1,
274                        ren_table->db, old_alias, reg_ext, 0);
275 
276   frm_type= dd_frm_type(thd, name, &table_type);
277   switch (frm_type)
278   {
279     case FRMTYPE_TABLE:
280       {
281         handlerton *hton= ha_resolve_by_legacy_type(thd, table_type);
282         if (table_type != DB_TYPE_UNKNOWN && !hton)
283         {
284           my_error(ER_STORAGE_ENGINE_NOT_LOADED, MYF(0), ren_table->db, old_alias);
285           DBUG_RETURN(1);
286         }
287         if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias,
288                                      new_db, new_alias, 0)))
289         {
290           if ((rc= change_trigger_table_name(thd, ren_table->db, old_alias,
291                                              ren_table->table_name,
292                                              new_db, new_alias)))
293           {
294             /*
295               We've succeeded in renaming table's .frm and in updating
296               corresponding handler data, but have failed to update table's
297               triggers appropriately. So let us revert operations on .frm
298               and handler's data and report about failure to rename table.
299             */
300             (void) mysql_rename_table(hton, new_db, new_alias,
301                                       ren_table->db, old_alias, NO_FK_CHECKS);
302           }
303         }
304       }
305       break;
306     case FRMTYPE_VIEW:
307       /*
308          change of schema is not allowed
309          except of ALTER ...UPGRADE DATA DIRECTORY NAME command
310          because a view has valid internal db&table names in this case.
311       */
312       if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
313           strcmp(ren_table->db, new_db))
314         my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db,
315                  new_db);
316       else
317         rc= mysql_rename_view(thd, new_db, new_alias, ren_table);
318       break;
319     default:
320       assert(0); // should never happen
321     case FRMTYPE_ERROR:
322       {
323         char errbuf[MYSYS_STRERROR_SIZE];
324         my_error(ER_FILE_NOT_FOUND, MYF(0), name,
325                  my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
326       }
327       break;
328   }
329 
330   thd->add_to_binlog_accessed_dbs(ren_table->db);
331   thd->add_to_binlog_accessed_dbs(new_db);
332 
333   if (rc && !skip_error)
334     DBUG_RETURN(1);
335 
336   DBUG_RETURN(0);
337 
338 }
339 /*
340   Rename all tables in list; Return pointer to wrong entry if something goes
341   wrong.  Note that the table_list may be empty!
342 */
343 
344 /*
345   Rename tables/views in the list
346 
347   SYNPOSIS
348     rename_tables()
349       thd               Thread handle
350       table_list        List of tables to rename
351       skip_error        Whether to skip errors
352 
353   DESCRIPTION
354     Take a table/view name from and odd list element and rename it to a
355     the name taken from list element+1. Note that the table_list may be
356     empty.
357 
358   RETURN
359     false     Ok
360     true      rename failed
361 */
362 
363 static TABLE_LIST *
rename_tables(THD * thd,TABLE_LIST * table_list,bool skip_error)364 rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
365 {
366   TABLE_LIST *ren_table, *new_table;
367 
368   DBUG_ENTER("rename_tables");
369 
370   for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
371   {
372     new_table= ren_table->next_local;
373     if (do_rename(thd, ren_table, new_table->db, new_table->table_name,
374                   new_table->alias, skip_error))
375       DBUG_RETURN(ren_table);
376   }
377   DBUG_RETURN(0);
378 }
379