/*
Copyright ( c ) 2000 , 2013 , Oracle and / or its affiliates .
Copyright ( c ) 2011 , 2021 , Monty Program Ab .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/*
Atomic rename of table ; RENAME TABLE t1 to t2 , tmp to t1 [ , . . . ]
*/
#include "mariadb.h"
#include "sql_class.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_rename.h"
#include "sql_cache.h" // query_cache_*
#include "sql_table.h" // write_bin_log
#include "sql_view.h" // mysql_frm_type, mysql_rename_view
#include "sql_trigger.h"
#include "sql_base.h" // tdc_remove_table, lock_table_names,
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_statistics.h"
#include "ddl_log.h"
#include "wsrep_mysqld.h"
#include "debug.h"
/* used to hold table entries for as part of list of renamed temporary tables */
struct TABLE_PAIR
{
TABLE_LIST *from, *to;
};
static bool rename_tables(THD *thd, TABLE_LIST *table_list,
DDL_LOG_STATE *ddl_log_state,
bool skip_error, bool if_exits,
bool *force_if_exists,
bool *not_logged_temporary_tables);
/*
Every two entries in the table_list form a pair of original name and
the new name .
*/
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent,
bool if_exists)
{
bool error= 1 ;
bool binlog_error= 0 , force_if_exists, not_logged_temporary_tables;
TABLE_LIST *ren_table= 0 ;
int to_table;
const char *rename_log_table[2 ]= {NULL, NULL};
DDL_LOG_STATE ddl_log_state;
Postponed_error_handler postponed_handler(thd->mem_root);
DBUG_ENTER("mysql_rename_tables" );
/*
Avoid problems with a rename on a table that we have locked or
if the user is trying to do this in a transaction context
*/
if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER_THD(thd, ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0 ));
DBUG_RETURN(1 );
}
mysql_ha_rm_tables(thd, table_list);
if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
logger.is_log_table_enabled(QUERY_LOG_SLOW))
{
/*
Rules for rename of a log table :
IF 1 . Log tables are enabled
AND 2 . Rename operates on the log table and nothing is being
renamed to the log table .
DO 3 . Throw an error message .
ELSE 4 . Perform rename .
*/
for (to_table= 0 , ren_table= table_list; ren_table;
to_table= 1 - to_table, ren_table= ren_table->next_local)
{
int log_table_rename;
if ((log_table_rename= check_if_log_table(ren_table, TRUE , NullS)))
{
/*
as we use log_table_rename as an array index , we need it to start
with 0 , while QUERY_LOG_SLOW = = 1 and QUERY_LOG_GENERAL = = 2 .
So , we shift the value to start with 0 ;
*/
log_table_rename--;
if (rename_log_table[log_table_rename])
{
if (to_table)
rename_log_table[log_table_rename]= NULL;
else
{
/*
Two renames of " log_table TO " w / o rename " TO log_table " in
between .
*/
my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0 ),
ren_table->table_name.str,
ren_table->table_name.str);
goto err;
}
}
else
{
if (to_table)
{
/*
Attempt to rename a table TO log_table w / o renaming
log_table TO some table .
*/
my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0 ),
ren_table->table_name.str,
ren_table->table_name.str);
goto err;
}
else
{
/* save the name of the log table to report an error */
rename_log_table[log_table_rename]= ren_table->table_name.str;
}
}
}
}
if (rename_log_table[0 ] || rename_log_table[1 ])
{
if (rename_log_table[0 ])
my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0 ), rename_log_table[0 ],
rename_log_table[0 ]);
else
my_error(ER_CANT_RENAME_LOG_TABLE, MYF(0 ), rename_log_table[1 ],
rename_log_table[1 ]);
goto err;
}
}
if (lock_table_names(thd, table_list, 0 , thd->variables.lock_wait_timeout,
0 ))
goto err;
error=0 ;
bzero(&ddl_log_state, sizeof (ddl_log_state));
/*
An exclusive lock on table names is satisfactory to ensure
no other thread accesses this table .
*/
/*
Do not emit errors which may occur during the sequence of renamings ,
because they will raise the ' thd - > is_error ( ) ' flag , and ` ddl_log_revert ( ) `
may fail due to this . Instead , capture and collect errors and warnings
and emit them only after the renaming and possible reversion are complete .
*/
thd->push_internal_handler(&postponed_handler);
error= rename_tables(thd, table_list, &ddl_log_state,
0 , if_exists, &force_if_exists,
¬_logged_temporary_tables);
if (likely(!silent && !error))
{
ulonglong save_option_bits= thd->variables.option_bits;
if (force_if_exists && ! if_exists)
{
/* Add IF EXISTS to binary log */
thd->variables.option_bits|= OPTION_IF_EXISTS;
}
debug_crash_here("ddl_log_rename_before_binlog" );
/*
Store xid in ddl log and binary log so that we can check on ddl recovery
if the item is in the binary log ( and thus the operation was complete
*/
thd->binlog_xid= thd->query_id;
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
if (mysql_bin_log.is_open())
{
if (not_logged_temporary_tables)
binlog_error= thd->binlog_renamed_tmp_tables(table_list);
else
binlog_error= write_bin_log(thd, TRUE , thd->query(),
thd->query_length());
if (binlog_error)
error= 1 ;
}
thd->binlog_xid= 0 ;
thd->variables.option_bits= save_option_bits;
debug_crash_here("ddl_log_rename_after_binlog" );
if (likely(!binlog_error))
my_ok(thd);
}
thd->pop_internal_handler();
if (likely(!error))
{
query_cache_invalidate3(thd, table_list, 0 );
ddl_log_complete(&ddl_log_state);
DBUG_ASSERT(!postponed_handler.has_errors());
}
else
{
/* Revert the renames of normal tables with the help of the ddl log */
ddl_log_revert(thd, &ddl_log_state);
postponed_handler.emit_errors();
}
err:
DBUG_RETURN(error || binlog_error);
}
static bool
do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table)
{
LEX_CSTRING *new_alias;
DBUG_ENTER("do_rename_temporary" );
new_alias= (lower_case_table_names == 2 ) ? &new_table->alias :
&new_table->table_name;
if (thd->find_temporary_table(new_table, THD::TMP_TABLE_ANY))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0 ), new_alias->str);
DBUG_RETURN(1 ); // This can't be skipped
}
DBUG_RETURN(thd->rename_temporary_table(ren_table->table,
&new_table->db, new_alias));
}
/**
Parameters for do_rename
*/
struct rename_param
{
Lex_ident_table old_alias, new_alias;
LEX_CUSTRING old_version;
handlerton *from_table_hton;
};
/**
check_rename ( )
Check pre - conditions for rename
- From table should exists
- To table should not exists .
SYNOPSIS
@ param new_table_name The new table / view name
@ param new_table_alias The new table / view alias
@ param if_exists If not set , give an error if the table does not
exists . If set , just give a warning in this case .
@ return
@ retval 0 ok
@ retval > 0 Error ( from table doesn ' t exists or to table exists )
@ retval < 0 Can ' t do rename , but no error
*/
static int
check_rename(THD *thd, rename_param *param,
const TABLE_LIST *ren_table,
const Lex_ident_db &new_db,
const Lex_ident_table &new_table_name,
const Lex_ident_table &new_table_alias,
bool if_exists)
{
DBUG_ENTER("check_rename" );
DBUG_PRINT("enter" , ("if_exists: %d" , (int ) if_exists));
if (lower_case_table_names == 2 )
{
param->old_alias= ren_table->alias;
param->new_alias= new_table_alias;
}
else
{
param->old_alias= ren_table->table_name;
param->new_alias= new_table_name;
}
DBUG_ASSERT(param->new_alias.str);
if (!ha_table_exists(thd, &ren_table->db, ¶m->old_alias,
¶m->old_version, ¶m->from_table_hton) ||
!param->from_table_hton)
{
my_error(ER_NO_SUCH_TABLE, MYF(if_exists ? ME_NOTE : 0 ),
ren_table->db.str, param->old_alias.str);
DBUG_RETURN(if_exists ? -1 : 1 );
}
if (param->from_table_hton != view_pseudo_hton &&
ha_check_if_updates_are_ignored(thd, param->from_table_hton, "RENAME" ))
{
/*
Shared table . Just drop the old . frm as it ' s not correct anymore
Discovery will find the old table when it ' s accessed
*/
tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
quick_rm_table(thd, 0 , &ren_table->db, ¶m->old_alias, QRMT_FRM);
DBUG_RETURN(-1 );
}
if (ha_table_exists(thd, &new_db, ¶m->new_alias))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0 ), param->new_alias.str);
DBUG_RETURN(1 ); // This can't be skipped
}
DBUG_RETURN(0 );
}
/*
Rename a single table or a view
SYNPOSIS
do_rename ( )
thd Thread handle
ren_table A table / view to be renamed
new_db The database to which the table to be moved to
skip_error Skip error , but only if the table didn ' t exists
force_if_exists Set to 1 if we have to log the query with ' IF EXISTS '
Otherwise don ' t touch the value
DESCRIPTION
Rename a single table or a view .
In case of failure , all changes will be reverted
RETURN
false Ok
true rename failed
*/
static bool
do_rename(THD *thd, const rename_param *param, DDL_LOG_STATE *ddl_log_state,
TABLE_LIST *ren_table, const Lex_ident_db *new_db,
bool skip_error, bool *force_if_exists)
{
int rc= 1 ;
handlerton *hton;
TRIGGER_RENAME_PARAM rename_param;
DBUG_ENTER("do_rename" );
DBUG_PRINT("enter" , ("skip_error: %d" , (int ) skip_error));
const Lex_ident_table * const old_alias= ¶m->old_alias;
const Lex_ident_table * const new_alias= ¶m->new_alias;
hton= param->from_table_hton;
DBUG_ASSERT(!thd->locked_tables_mode);
#ifdef WITH_WSREP
if (WSREP(thd) && hton && hton != view_pseudo_hton &&
!wsrep_should_replicate_ddl(thd, hton))
DBUG_RETURN(1 );
#endif
/*
SQLCOM_ALTER_DB_UPGRADE is a special case , it moves tables
from the # mysql50 # db to db ( from old name to new name )
and old db might have tables with # mysql50 # prefix .
*/
if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
error_if_mysql50_prefix(new_alias->str, ER_WRONG_TABLE_NAME))
DBUG_RETURN(1 );
tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
if (hton != view_pseudo_hton)
{
if (hton->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
*force_if_exists= 1 ;
/* Check if we can rename triggers */
if (Table_triggers_list::prepare_for_rename(thd, &rename_param,
ren_table->db,
*old_alias,
ren_table->table_name,
*new_db,
*new_alias))
DBUG_RETURN(!skip_error);
thd->replication_flags= 0 ;
if (ddl_log_rename_table(ddl_log_state, hton,
&ren_table->db, old_alias, new_db, new_alias))
DBUG_RETURN(1 );
debug_crash_here("ddl_log_rename_before_rename_table" );
if (!(rc= mysql_rename_table(hton, &ren_table->db, old_alias,
new_db, new_alias, ¶m->old_version,
QRMT_DEFAULT)))
{
/* Table rename succeded.
It ' s safe to start recovery at rename trigger phase
*/
debug_crash_here("ddl_log_rename_before_phase_trigger" );
ddl_log_update_phase(ddl_log_state, DDL_RENAME_PHASE_TRIGGER);
debug_crash_here("ddl_log_rename_before_rename_trigger" );
if (!(rc= Table_triggers_list::change_table_name(thd,
&rename_param,
&ren_table->db,
old_alias,
&ren_table->table_name,
new_db,
new_alias)))
{
debug_crash_here("ddl_log_rename_before_stat_tables" );
(void ) rename_table_in_stat_tables(thd, &ren_table->db,
&ren_table->table_name,
new_db, new_alias);
debug_crash_here("ddl_log_rename_after_stat_tables" );
}
else
{
/*
We ' ve succeeded in renaming table ' s . frm and in updating
corresponding handler data , but have failed to update table ' s
triggers appropriately . So let us revert operations on . frm
and handler ' s data and report about failure to rename table .
*/
debug_crash_here("ddl_log_rename_after_failed_rename_trigger" );
(void ) mysql_rename_table(hton, new_db, new_alias,
&ren_table->db, old_alias, ¶m->old_version,
QRMT_DEFAULT | NO_FK_CHECKS);
debug_crash_here("ddl_log_rename_after_revert_rename_table" );
ddl_log_disable_entry(ddl_log_state);
debug_crash_here("ddl_log_rename_after_disable_entry" );
}
}
if (thd->replication_flags & OPTION_IF_EXISTS)
*force_if_exists= 1 ;
}
else
{
/*
Change of schema is not allowed
except of ALTER . . . UPGRADE DATA DIRECTORY NAME command
because a view has valid internal db & table names in this case .
*/
if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
cmp(&ren_table->db, new_db))
{
my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0 ), ren_table->db.str, new_db->str);
DBUG_RETURN(1 );
}
ddl_log_rename_view(ddl_log_state, &ren_table->db,
&ren_table->table_name, new_db, new_alias);
debug_crash_here("ddl_log_rename_before_rename_view" );
rc= mysql_rename_view(thd, new_db, new_alias, &ren_table->db,
&ren_table->table_name);
debug_crash_here("ddl_log_rename_after_rename_view" );
if (rc)
{
/*
On error mysql_rename_view ( ) will leave things as such .
*/
ddl_log_disable_entry(ddl_log_state);
debug_crash_here("ddl_log_rename_after_disable_entry" );
}
}
DBUG_RETURN(rc && !skip_error ? 1 : 0 );
}
/*
Rename all tables in list ; Return pointer to wrong entry if something goes
wrong . Note that the table_list may be empty !
*/
/*
Rename tables / views in the list
SYNPOSIS
rename_tables ( )
thd Thread handle
table_list List of tables to rename
ddl_log_state ddl logging
skip_error Whether to skip errors
if_exists Don ' t give an error if table doesn ' t exists
force_if_exists Set to 1 if we have to log the query with ' IF EXISTS '
Otherwise set it to 0
not_logged_temporary_tables Set to 1 if there was a temporary table in the statement
that was not in the binary logged .
DESCRIPTION
Take a table / view name from and odd list element and rename it to a
the name taken from list element + 1 . Note that the table_list may be
empty .
RETURN
0 Ok
1 error
All tables are reverted to their original names
*/
static bool
rename_tables(THD *thd, TABLE_LIST *table_list, DDL_LOG_STATE *ddl_log_state,
bool skip_error, bool if_exists, bool *force_if_exists,
bool *not_logged_temporary_tables)
{
TABLE_LIST *ren_table, *new_table;
List<TABLE_PAIR> tmp_tables;
DBUG_ENTER("rename_tables" );
*force_if_exists= 0 ;
*not_logged_temporary_tables= 0 ;
for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
{
new_table= ren_table->next_local;
if (is_temporary_table(ren_table))
{
/*
Store renamed temporary tables into a list .
We don ' t store these in the ddl log to avoid writes and syncs
when only using temporary tables . We don ' t need the log as
all temporary tables will disappear anyway in a crash .
*/
TABLE_PAIR *pair= thd->alloc<TABLE_PAIR>(1 );
if (! pair || tmp_tables.push_front(pair, thd->mem_root))
goto revert_rename;
pair->from= ren_table;
pair->to= new_table;
if (do_rename_temporary(thd, ren_table, new_table))
goto revert_rename;
if (!ren_table->table->s->table_creation_was_logged)
*not_logged_temporary_tables= 1 ;
}
else
{
int error;
rename_param param;
error= check_rename(thd, ¶m, ren_table, new_table->db,
new_table->table_name,
new_table->alias, (skip_error || if_exists));
if (error < 0 )
continue ; // Ignore rename (if exists)
if (error > 0 )
goto revert_rename;
if (do_rename(thd, ¶m, ddl_log_state,
ren_table, &new_table->db,
skip_error, force_if_exists))
goto revert_rename;
}
}
DBUG_RETURN(0 );
revert_rename:
/* Revert temporary tables. Normal tables are reverted in the caller */
List_iterator_fast<TABLE_PAIR> it(tmp_tables);
while (TABLE_PAIR *pair= it++)
do_rename_temporary(thd, pair->to, pair->from);
DBUG_RETURN(1 );
}
Messung V0.5 in Prozent C=94 H=81 G=87
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland