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