/* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */ /* Rename a table */ #include "ma_fulltext.h" #include "trnman_public.h" /** @brief renames a table @param old_name current name of table @param new_name table should be renamed to this name @return Operation status @retval 0 OK @retval !=0 Error */ int maria_rename(const char *old_name, const char *new_name) { char from[FN_REFLEN],to[FN_REFLEN]; int data_file_rename_error= 0, index_file_rename_error= 0; #ifdef USE_RAID uint raid_type=0,raid_chunks=0; #endif MARIA_HA *info; MARIA_SHARE *share; myf sync_dir= 0; my_bool ddl_recovery= 0; DBUG_ENTER("maria_rename"); #ifdef EXTRA_DEBUG _ma_check_table_is_closed(old_name,"rename old_table"); _ma_check_table_is_closed(new_name,"rename new table2"); #endif /** @todo LOCK take X-lock on table */ if (!(info= maria_open(old_name, O_RDWR, HA_OPEN_FOR_REPAIR, 0))) { int error= my_errno; /* Check if we are in recovery from a rename that failed in the middle and we are now renaming things back. */ if (error == ENOENT) { char *index_file= from; char *data_file= to; fn_format(index_file, old_name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); fn_format(data_file, old_name, "", MARIA_NAME_DEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); if (!access(data_file, F_OK) && access(index_file, F_OK)) { ddl_recovery= 1; goto forced_rename; } } DBUG_RETURN(error); } share= info->s; #ifdef USE_RAID raid_type = share->base.raid_type; raid_chunks = share->base.raid_chunks; #endif /* the renaming of an internal table to the final table (like in ALTER TABLE) is the moment when this table receives its correct create_rename_lsn and this is important; make sure transactionality has been re-enabled. */ DBUG_ASSERT(share->now_transactional == share->base.born_transactional); if (share->now_transactional && !share->temporary && !maria_in_recovery) { LSN lsn; LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS + 2]; size_t old_name_len= strlen(old_name)+1, new_name_len= strlen(new_name)+1; sync_dir= MY_SYNC_DIR; log_array[TRANSLOG_INTERNAL_PARTS + 0].str= (uchar*)old_name; log_array[TRANSLOG_INTERNAL_PARTS + 0].length= old_name_len; log_array[TRANSLOG_INTERNAL_PARTS + 1].str= (uchar*)new_name; log_array[TRANSLOG_INTERNAL_PARTS + 1].length= new_name_len; /* For this record to be of any use for Recovery, we need the upper MySQL layer to be crash-safe, which it is not now (that would require work using the ddl_log of sql/sql_table.cc); when it is, we should reconsider the moment of writing this log record (before or after op, under THR_LOCK_maria or not...), how to use it in Recovery. For now it can serve to apply logs to a backup so we sync it. */ if (unlikely(translog_write_record(&lsn, LOGREC_REDO_RENAME_TABLE, &dummy_transaction_object, NULL, (translog_size_t)(old_name_len + new_name_len), sizeof(log_array)/sizeof(log_array[0]), log_array, NULL, NULL) || translog_flush(lsn))) { maria_close(info); DBUG_RETURN(1); } /* store LSN into file, needed for Recovery to not be confused if a RENAME happened (applying REDOs to the wrong table). */ if (_ma_update_state_lsns(share, lsn, share->state.create_trid, TRUE, TRUE)) { maria_close(info); DBUG_RETURN(1); } } _ma_reset_state(info); maria_close(info); forced_rename: /* This code is written so that it should be possible to re-run a failed rename (even if there is a server crash in between the renames) and complete it. */ fn_format(from,old_name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); fn_format(to,new_name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); if (mysql_file_rename_with_symlink(key_file_kfile, from, to, MYF(MY_WME | sync_dir))) index_file_rename_error= my_errno; fn_format(from,old_name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); fn_format(to,new_name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); if (mysql_file_rename_with_symlink(key_file_dfile, from, to, MYF(MY_WME | sync_dir))) data_file_rename_error= my_errno; if (data_file_rename_error && data_file_rename_error != ENOENT && !ddl_recovery) { /* Now we have a renamed index file and a non-renamed data file, try to undo a successful rename of the index file. */ if (!index_file_rename_error) { fn_format(from, old_name, "", MARIA_NAME_IEXT, MYF(MY_UNPACK_FILENAME|MY_APPEND_EXT)); fn_format(to, new_name, "", MARIA_NAME_IEXT, MYF(MY_UNPACK_FILENAME|MY_APPEND_EXT)); mysql_file_rename_with_symlink(key_file_kfile, to, from, MYF(MY_WME | sync_dir)); } } DBUG_RETURN(data_file_rename_error ? data_file_rename_error: index_file_rename_error); }