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