MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_rename.cc
1 /* Copyright (c) 2000, 2013, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 /*
17  Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...]
18 */
19 
20 #include "sql_priv.h"
21 #include "unireg.h"
22 #include "sql_rename.h"
23 #include "sql_cache.h" // query_cache_*
24 #include "sql_table.h" // build_table_filename
25 #include "sql_view.h" // mysql_frm_type, mysql_rename_view
26 #include "sql_trigger.h"
27 #include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
28 #include "sql_base.h" // tdc_remove_table, lock_table_names,
29 #include "sql_handler.h" // mysql_ha_rm_tables
30 #include "datadict.h"
31 
32 static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
33  bool skip_error);
34 
35 static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
36 
37 /*
38  Every two entries in the table_list form a pair of original name and
39  the new name.
40 */
41 
42 bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
43 {
44  bool error= 1;
45  bool binlog_error= 0;
46  TABLE_LIST *ren_table= 0;
47  int to_table;
48  char *rename_log_table[2]= {NULL, NULL};
49  DBUG_ENTER("mysql_rename_tables");
50 
51  /*
52  Avoid problems with a rename on a table that we have locked or
53  if the user is trying to to do this in a transcation context
54  */
55 
56  if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
57  {
58  my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
59  ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
60  DBUG_RETURN(1);
61  }
62 
63  mysql_ha_rm_tables(thd, table_list);
64 
65  if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
66  logger.is_log_table_enabled(QUERY_LOG_SLOW))
67  {
68 
69  /*
70  Rules for rename of a log table:
71 
72  IF 1. Log tables are enabled
73  AND 2. Rename operates on the log table and nothing is being
74  renamed to the log table.
75  DO 3. Throw an error message.
76  ELSE 4. Perform rename.
77  */
78 
79  for (to_table= 0, ren_table= table_list; ren_table;
80  to_table= 1 - to_table, ren_table= ren_table->next_local)
81  {
82  int log_table_rename= 0;
83 
84  if ((log_table_rename=
85  check_if_log_table(ren_table->db_length, ren_table->db,
86  ren_table->table_name_length,
87  ren_table->table_name, 1)))
88  {
89  /*
90  as we use log_table_rename as an array index, we need it to start
91  with 0, while QUERY_LOG_SLOW == 1 and QUERY_LOG_GENERAL == 2.
92  So, we shift the value to start with 0;
93  */
94  log_table_rename--;
95  if (rename_log_table[log_table_rename])
96  {
97  if (to_table)
98  rename_log_table[log_table_rename]= NULL;
99  else
100  {
101  /*
102  Two renames of "log_table TO" w/o rename "TO log_table" in
103  between.
104  */
105  my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
106  ren_table->table_name);
107  goto err;
108  }
109  }
110  else
111  {
112  if (to_table)
113  {
114  /*
115  Attempt to rename a table TO log_table w/o renaming
116  log_table TO some table.
117  */
118  my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), ren_table->table_name,
119  ren_table->table_name);
120  goto err;
121  }
122  else
123  {
124  /* save the name of the log table to report an error */
125  rename_log_table[log_table_rename]= ren_table->table_name;
126  }
127  }
128  }
129  }
130  if (rename_log_table[0] || rename_log_table[1])
131  {
132  if (rename_log_table[0])
133  my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[0],
134  rename_log_table[0]);
135  else
136  my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0), rename_log_table[1],
137  rename_log_table[1]);
138  goto err;
139  }
140  }
141 
142  if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, 0))
143  goto err;
144 
145  for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
146  tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
147  ren_table->table_name, FALSE);
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 (!silent && !error)
177  {
178  binlog_error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
179  if (!binlog_error)
180  my_ok(thd);
181  }
182 
183  if (!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 */
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 /*
217  Rename a single table or a view
218 
219  SYNPOSIS
220  do_rename()
221  thd Thread handle
222  ren_table A table/view to be renamed
223  new_db The database to which the table to be moved to
224  new_table_name The new table/view name
225  new_table_alias The new table/view alias
226  skip_error Whether to skip error
227 
228  DESCRIPTION
229  Rename a single table or a view.
230 
231  RETURN
232  false Ok
233  true rename failed
234 */
235 
236 bool
237 do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
238  char *new_table_alias, bool skip_error)
239 {
240  int rc= 1;
241  char name[FN_REFLEN + 1];
242  const char *new_alias, *old_alias;
243  frm_type_enum frm_type;
244  enum legacy_db_type table_type;
245 
246  DBUG_ENTER("do_rename");
247 
248  if (lower_case_table_names == 2)
249  {
250  old_alias= ren_table->alias;
251  new_alias= new_table_alias;
252  }
253  else
254  {
255  old_alias= ren_table->table_name;
256  new_alias= new_table_name;
257  }
258  DBUG_ASSERT(new_alias);
259 
260  build_table_filename(name, sizeof(name) - 1,
261  new_db, new_alias, reg_ext, 0);
262  if (!access(name,F_OK))
263  {
264  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
265  DBUG_RETURN(1); // This can't be skipped
266  }
267  build_table_filename(name, sizeof(name) - 1,
268  ren_table->db, old_alias, reg_ext, 0);
269 
270  frm_type= dd_frm_type(thd, name, &table_type);
271  switch (frm_type)
272  {
273  case FRMTYPE_TABLE:
274  {
275  if (!(rc= mysql_rename_table(ha_resolve_by_legacy_type(thd,
276  table_type),
277  ren_table->db, old_alias,
278  new_db, new_alias, 0)))
279  {
280  if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
281  old_alias,
282  ren_table->table_name,
283  new_db,
284  new_alias)))
285  {
286  /*
287  We've succeeded in renaming table's .frm and in updating
288  corresponding handler data, but have failed to update table's
289  triggers appropriately. So let us revert operations on .frm
290  and handler's data and report about failure to rename table.
291  */
292  (void) mysql_rename_table(ha_resolve_by_legacy_type(thd,
293  table_type),
294  new_db, new_alias,
295  ren_table->db, old_alias, NO_FK_CHECKS);
296  }
297  }
298  }
299  break;
300  case FRMTYPE_VIEW:
301  /*
302  change of schema is not allowed
303  except of ALTER ...UPGRADE DATA DIRECTORY NAME command
304  because a view has valid internal db&table names in this case.
305  */
306  if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
307  strcmp(ren_table->db, new_db))
308  my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db,
309  new_db);
310  else
311  rc= mysql_rename_view(thd, new_db, new_alias, ren_table);
312  break;
313  default:
314  DBUG_ASSERT(0); // should never happen
315  case FRMTYPE_ERROR:
316  {
317  char errbuf[MYSYS_STRERROR_SIZE];
318  my_error(ER_FILE_NOT_FOUND, MYF(0), name,
319  my_errno, my_strerror(errbuf, sizeof(errbuf), my_errno));
320  }
321  break;
322  }
323 
324  thd->add_to_binlog_accessed_dbs(ren_table->db);
325  thd->add_to_binlog_accessed_dbs(new_db);
326 
327  if (rc && !skip_error)
328  DBUG_RETURN(1);
329 
330  DBUG_RETURN(0);
331 
332 }
333 /*
334  Rename all tables in list; Return pointer to wrong entry if something goes
335  wrong. Note that the table_list may be empty!
336 */
337 
338 /*
339  Rename tables/views in the list
340 
341  SYNPOSIS
342  rename_tables()
343  thd Thread handle
344  table_list List of tables to rename
345  skip_error Whether to skip errors
346 
347  DESCRIPTION
348  Take a table/view name from and odd list element and rename it to a
349  the name taken from list element+1. Note that the table_list may be
350  empty.
351 
352  RETURN
353  false Ok
354  true rename failed
355 */
356 
357 static TABLE_LIST *
358 rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
359 {
360  TABLE_LIST *ren_table, *new_table;
361 
362  DBUG_ENTER("rename_tables");
363 
364  for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
365  {
366  new_table= ren_table->next_local;
367  if (do_rename(thd, ren_table, new_table->db, new_table->table_name,
368  new_table->alias, skip_error))
369  DBUG_RETURN(ren_table);
370  }
371  DBUG_RETURN(0);
372 }