/***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2021, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. 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 *****************************************************************************/ /********************************************************************//** @file srv/srv0start.cc Starts the InnoDB database server Created 2/16/1996 Heikki Tuuri *************************************************************************/ #include "my_global.h" #include "mysqld.h" #include "mysql/psi/mysql_stage.h" #include "mysql/psi/psi.h" #include "row0ftsort.h" #include "ut0mem.h" #include "mem0mem.h" #include "data0data.h" #include "data0type.h" #include "dict0dict.h" #include "buf0buf.h" #include "buf0dblwr.h" #include "buf0dump.h" #include "os0file.h" #include "os0thread.h" #include "fil0fil.h" #include "fil0crypt.h" #include "fsp0fsp.h" #include "rem0rec.h" #include "mtr0mtr.h" #include "log0crypt.h" #include "log0recv.h" #include "page0page.h" #include "page0cur.h" #include "trx0trx.h" #include "trx0sys.h" #include "btr0btr.h" #include "btr0cur.h" #include "rem0rec.h" #include "ibuf0ibuf.h" #include "srv0start.h" #include "srv0srv.h" #include "btr0defragment.h" #include "mysql/service_wsrep.h" /* wsrep_recovery */ #include "trx0rseg.h" #include "buf0flu.h" #include "buf0rea.h" #include "dict0boot.h" #include "dict0load.h" #include "dict0stats_bg.h" #include "que0que.h" #include "lock0lock.h" #include "trx0roll.h" #include "trx0purge.h" #include "lock0lock.h" #include "pars0pars.h" #include "btr0sea.h" #include "rem0cmp.h" #include "dict0crea.h" #include "row0ins.h" #include "row0sel.h" #include "row0upd.h" #include "row0row.h" #include "row0mysql.h" #include "btr0pcur.h" #include "os0event.h" #include "zlib.h" #include "ut0crc32.h" /** We are prepared for a situation that we have this many threads waiting for a semaphore inside InnoDB. srv_start() sets the value. */ ulint srv_max_n_threads; /** Log sequence number at shutdown */ lsn_t srv_shutdown_lsn; /** TRUE if a raw partition is in use */ ibool srv_start_raw_disk_in_use; /** Number of IO threads to use */ uint srv_n_file_io_threads; /** UNDO tablespaces starts with space id. */ ulint srv_undo_space_id_start; /** TRUE if the server is being started, before rolling back any incomplete transactions */ bool srv_startup_is_before_trx_rollback_phase; /** TRUE if the server is being started */ bool srv_is_being_started; /** TRUE if SYS_TABLESPACES is available for lookups */ bool srv_sys_tablespaces_open; /** TRUE if the server was successfully started */ bool srv_was_started; /** The original value of srv_log_file_size (innodb_log_file_size) */ static ulonglong srv_log_file_size_requested; /** whether srv_start() has been called */ static bool srv_start_has_been_called; /** Whether any undo log records can be generated */ UNIV_INTERN bool srv_undo_sources; #ifdef UNIV_DEBUG /** InnoDB system tablespace to set during recovery */ UNIV_INTERN uint srv_sys_space_size_debug; /** whether redo log file have been created at startup */ UNIV_INTERN bool srv_log_file_created; #endif /* UNIV_DEBUG */ /** whether some background threads that create redo log have been started */ static bool srv_started_redo; /** At a shutdown this value climbs from SRV_SHUTDOWN_NONE to SRV_SHUTDOWN_CLEANUP and then to SRV_SHUTDOWN_LAST_PHASE, and so on */ enum srv_shutdown_t srv_shutdown_state = SRV_SHUTDOWN_NONE; /** Name of srv_monitor_file */ static char* srv_monitor_file_name; std::unique_ptr<tpool::timer> srv_master_timer; /** */ #define SRV_MAX_N_PENDING_SYNC_IOS 100 #ifdef UNIV_PFS_THREAD /* Keys to register InnoDB threads with performance schema */ mysql_pfs_key_t thread_pool_thread_key; #endif /* UNIV_PFS_THREAD */ #ifdef HAVE_PSI_STAGE_INTERFACE /** Array of all InnoDB stage events for monitoring activities via performance schema. */ static PSI_stage_info* srv_stages[] = { &srv_stage_alter_table_end, &srv_stage_alter_table_insert, &srv_stage_alter_table_log_index, &srv_stage_alter_table_log_table, &srv_stage_alter_table_merge_sort, &srv_stage_alter_table_read_pk_internal_sort, &srv_stage_buffer_pool_load, }; #endif /* HAVE_PSI_STAGE_INTERFACE */ /*********************************************************************//** Check if a file can be opened in read-write mode. @return true if it doesn't exist or can be opened in rw mode. */ static bool srv_file_check_mode( /*================*/ const char* name) /*!< in: filename to check */ { os_file_stat_t stat; memset(&stat, 0x0, sizeof(stat)); dberr_t err = os_file_get_status( name, &stat, true, srv_read_only_mode); if (err == DB_FAIL) { ib::error() << "os_file_get_status() failed on '" << name << "'. Can't determine file permissions."; return(false); } else if (err == DB_SUCCESS) { /* Note: stat.rw_perm is only valid of files */ if (stat.type == OS_FILE_TYPE_FILE) { if (!stat.rw_perm) { const char* mode = srv_read_only_mode ? "read" : "read-write"; ib::error() << name << " can't be opened in " << mode << " mode."; return(false); } } else { /* Not a regular file, bail out. */ ib::error() << "'" << name << "' not a regular file."; return(false); } } else { /* This is OK. If the file create fails on RO media, there is nothing we can do. */ ut_a(err == DB_NOT_FOUND); } return(true); } /** Initial number of the redo log file */ static const char INIT_LOG_FILE0[]= "101"; /** Creates log file. @param[in] create_new_db whether the database is being initialized @param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value @param[out] logfile0 name of the log file @return DB_SUCCESS or error code */ static dberr_t create_log_file(bool create_new_db, lsn_t lsn, std::string& logfile0) { if (srv_read_only_mode) { ib::error() << "Cannot create log file in read-only mode"; return DB_READ_ONLY; } if (!log_set_capacity(srv_log_file_size_requested)) { return(DB_ERROR); } /* Crashing after deleting the first file should be recoverable. The buffer pool was clean, and we can simply create log file from the scratch. */ DBUG_EXECUTE_IF("innodb_log_abort_6", delete_log_file("0"); return DB_ERROR;); for (size_t i = 0; i < 102; i++) { delete_log_file(std::to_string(i).c_str()); } DBUG_PRINT("ib_log", ("After innodb_log_abort_6")); DBUG_ASSERT(!buf_pool.any_io_pending()); DBUG_EXECUTE_IF("innodb_log_abort_7", return DB_ERROR;); DBUG_PRINT("ib_log", ("After innodb_log_abort_7")); logfile0 = get_log_file_path(LOG_FILE_NAME_PREFIX) .append(INIT_LOG_FILE0); bool ret; pfs_os_file_t file = os_file_create( innodb_log_file_key, logfile0.c_str(), OS_FILE_CREATE|OS_FILE_ON_ERROR_NO_EXIT, OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret); if (!ret) { ib::error() << "Cannot create " << logfile0; return DB_ERROR; } ib::info() << "Setting log file " << logfile0 << " size to " << srv_log_file_size << " bytes"; ret = os_file_set_size(logfile0.c_str(), file, srv_log_file_size); if (!ret) { os_file_close(file); ib::error() << "Cannot set log file " << logfile0 << " size to " << srv_log_file_size << " bytes"; return DB_ERROR; } ret = os_file_close(file); ut_a(ret); DBUG_EXECUTE_IF("innodb_log_abort_8", return(DB_ERROR);); DBUG_PRINT("ib_log", ("After innodb_log_abort_8")); /* We did not create the first log file initially as LOG_FILE_NAME, so that crash recovery cannot find it until it has been completed and renamed. */ log_sys.log.create(); log_sys.log.open_file(logfile0); if (!fil_system.sys_space->open(create_new_db)) { return DB_ERROR; } /* Create a log checkpoint. */ mysql_mutex_lock(&log_sys.mutex); if (log_sys.is_encrypted() && !log_crypt_init()) { return DB_ERROR; } ut_d(recv_no_log_write = false); lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE); log_sys.set_lsn(lsn + LOG_BLOCK_HDR_SIZE); log_sys.log.set_lsn(lsn); log_sys.log.set_lsn_offset(LOG_FILE_HDR_SIZE); log_sys.buf_next_to_write = 0; log_sys.write_lsn = lsn; log_sys.next_checkpoint_no = 0; log_sys.last_checkpoint_lsn = 0; memset(log_sys.buf, 0, srv_log_buffer_size); log_block_init(log_sys.buf, lsn); log_block_set_first_rec_group(log_sys.buf, LOG_BLOCK_HDR_SIZE); memset(log_sys.flush_buf, 0, srv_log_buffer_size); log_sys.buf_free = LOG_BLOCK_HDR_SIZE; log_sys.log.write_header_durable(lsn); ut_ad(srv_startup_is_before_trx_rollback_phase); if (create_new_db) { srv_startup_is_before_trx_rollback_phase = false; } /* Enable checkpoints in buf_flush_page_cleaner(). */ recv_sys.recovery_on = false; mysql_mutex_unlock(&log_sys.mutex); log_make_checkpoint(); log_buffer_flush_to_disk(); return DB_SUCCESS; } /** Rename the first redo log file. @param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value @param[in,out] logfile0 name of the first log file @return error code @retval DB_SUCCESS on successful operation */ MY_ATTRIBUTE((warn_unused_result)) static dberr_t create_log_file_rename(lsn_t lsn, std::string &logfile0) { ut_ad(!srv_log_file_created); ut_d(srv_log_file_created= true); DBUG_EXECUTE_IF("innodb_log_abort_9", return (DB_ERROR);); DBUG_PRINT("ib_log", ("After innodb_log_abort_9")); /* Rename the first log file, now that a log checkpoint has been created. */ auto new_name = get_log_file_path(); ib::info() << "Renaming log file " << logfile0 << " to " << new_name; mysql_mutex_lock(&log_sys.mutex); ut_ad(logfile0.size() == 2 + new_name.size()); logfile0= new_name; dberr_t err= log_sys.log.rename(std::move(new_name)); mysql_mutex_unlock(&log_sys.mutex); DBUG_EXECUTE_IF("innodb_log_abort_10", err= DB_ERROR;); if (err == DB_SUCCESS) ib::info() << "New log file created, LSN=" << lsn; return err; } /** Create an undo tablespace file @param[in] name file name @return DB_SUCCESS or error code */ static dberr_t srv_undo_tablespace_create(const char* name) { pfs_os_file_t fh; bool ret; dberr_t err = DB_SUCCESS; os_file_create_subdirs_if_needed(name); fh = os_file_create( innodb_data_file_key, name, srv_read_only_mode ? OS_FILE_OPEN : OS_FILE_CREATE, OS_FILE_NORMAL, OS_DATA_FILE, srv_read_only_mode, &ret); if (!ret) { if (os_file_get_last_error(false) != OS_FILE_ALREADY_EXISTS #ifdef UNIV_AIX /* AIX 5.1 after security patch ML7 may have errno set to 0 here, which causes our function to return 100; work around that AIX problem */ && os_file_get_last_error(false) != 100 #endif /* UNIV_AIX */ ) { ib::error() << "Can't create UNDO tablespace " << name; } err = DB_ERROR; } else if (srv_read_only_mode) { ib::info() << name << " opened in read-only mode"; } else { /* We created the data file and now write it full of zeros */ ib::info() << "Data file " << name << " did not exist: new to" " be created"; ib::info() << "Setting file " << name << " size to " << (SRV_UNDO_TABLESPACE_SIZE_IN_PAGES >> (20 - srv_page_size_shift)) << " MB"; ib::info() << "Database physically writes the file full: " << "wait..."; if (!os_file_set_size(name, fh, os_offset_t {SRV_UNDO_TABLESPACE_SIZE_IN_PAGES} << srv_page_size_shift)) { ib::error() << "Unable to allocate " << name; err = DB_ERROR; } os_file_close(fh); } return(err); } /* Validate the number of undo opened undo tablespace and user given undo tablespace @return DB_SUCCESS if it is valid */ static dberr_t srv_validate_undo_tablespaces() { /* If the user says that there are fewer than what we find we tolerate that discrepancy but not the inverse. Because there could be unused undo tablespaces for future use. */ if (srv_undo_tablespaces > srv_undo_tablespaces_open) { ib::error() << "Expected to open innodb_undo_tablespaces=" << srv_undo_tablespaces << " but was able to find only " << srv_undo_tablespaces_open; return DB_ERROR; } else if (srv_undo_tablespaces_open > 0) { ib::info() << "Opened " << srv_undo_tablespaces_open << " undo tablespaces"; if (srv_undo_tablespaces == 0) ib::warn() << "innodb_undo_tablespaces=0 disables" " dedicated undo log tablespaces"; } return DB_SUCCESS; } /** @return the number of active undo tablespaces (except system tablespace) */ static ulint trx_rseg_get_n_undo_tablespaces() { std::set<uint32_t> space_ids; mtr_t mtr; mtr.start(); if (const buf_block_t *sys_header= trx_sysf_get(&mtr, false)) for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) if (trx_sysf_rseg_get_page_no(sys_header, rseg_id) != FIL_NULL) if (uint32_t space= trx_sysf_rseg_get_space(sys_header, rseg_id)) space_ids.insert(space); mtr.commit(); return space_ids.size(); } /** Open an undo tablespace. @param[in] create whether undo tablespaces are being created @param[in] name tablespace file name @param[in] i undo tablespace count @return undo tablespace identifier @retval 0 on failure */ static ulint srv_undo_tablespace_open(bool create, const char* name, ulint i) { bool success; char undo_name[sizeof "innodb_undo000"]; ulint space_id= 0; ulint fsp_flags= 0; if (create) { space_id= srv_undo_space_id_start + i; snprintf(undo_name, sizeof(undo_name), "innodb_undo%03u", static_cast<unsigned>(space_id)); switch (srv_checksum_algorithm) { case SRV_CHECKSUM_ALGORITHM_FULL_CRC32: case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32: fsp_flags= FSP_FLAGS_FCRC32_MASK_MARKER | FSP_FLAGS_FCRC32_PAGE_SSIZE(); break; default: fsp_flags= FSP_FLAGS_PAGE_SSIZE(); } } pfs_os_file_t fh= os_file_create(innodb_data_file_key, name, OS_FILE_OPEN | OS_FILE_ON_ERROR_NO_EXIT | OS_FILE_ON_ERROR_SILENT, OS_FILE_AIO, OS_DATA_FILE, srv_read_only_mode, &success); if (!success) return 0; os_offset_t size= os_file_get_size(fh); ut_a(size != os_offset_t(-1)); if (!create) { page_t *page= static_cast<byte*>(aligned_malloc(srv_page_size, srv_page_size)); dberr_t err= os_file_read(IORequestRead, fh, page, 0, srv_page_size); if (err != DB_SUCCESS) { err_exit: ib::error() << "Unable to read first page of file " << name; aligned_free(page); return err; } uint32_t id= mach_read_from_4(FIL_PAGE_SPACE_ID + page); if (id == 0 || id >= SRV_SPACE_ID_UPPER_BOUND || memcmp_aligned<2>(FIL_PAGE_SPACE_ID + page, FSP_HEADER_OFFSET + FSP_SPACE_ID + page, 4)) { ib::error() << "Inconsistent tablespace ID in file " << name; err= DB_CORRUPTION; goto err_exit; } fsp_flags= mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page); if (buf_page_is_corrupted(false, page, fsp_flags)) { ib::error() << "Checksum mismatch in the first page of file " << name; err= DB_CORRUPTION; goto err_exit; } space_id= id; snprintf(undo_name, sizeof undo_name, "innodb_undo%03u", id); aligned_free(page); } /* Load the tablespace into InnoDB's internal data structures. */ /* We set the biggest space id to the undo tablespace because InnoDB hasn't opened any other tablespace apart from the system tablespace. */ fil_set_max_space_id_if_bigger(space_id); fil_space_t *space= fil_space_t::create(undo_name, space_id, fsp_flags, FIL_TYPE_TABLESPACE, NULL); ut_a(fil_validate()); ut_a(space); fil_node_t *file= space->add(name, fh, 0, false, true); mutex_enter(&fil_system.mutex); if (create) { space->set_sizes(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES); space->size= file->size= uint32_t(size >> srv_page_size_shift); } else if (!file->read_page0()) { os_file_close(file->handle); file->handle= OS_FILE_CLOSED; ut_a(fil_system.n_open > 0); fil_system.n_open--; } mutex_exit(&fil_system.mutex); return space_id; } /** Check if undo tablespaces and redo log files exist before creating a new system tablespace @retval DB_SUCCESS if all undo and redo logs are not found @retval DB_ERROR if any undo and redo logs are found */ static dberr_t srv_check_undo_redo_logs_exists() { bool ret; pfs_os_file_t fh; char name[OS_FILE_MAX_PATH]; /* Check if any undo tablespaces exist */ for (ulint i = 1; i <= srv_undo_tablespaces; ++i) { snprintf( name, sizeof(name), "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i); fh = os_file_create( innodb_data_file_key, name, OS_FILE_OPEN_RETRY | OS_FILE_ON_ERROR_NO_EXIT | OS_FILE_ON_ERROR_SILENT, OS_FILE_NORMAL, OS_DATA_FILE, srv_read_only_mode, &ret); if (ret) { os_file_close(fh); ib::error() << "undo tablespace '" << name << "' exists." " Creating system tablespace with existing undo" " tablespaces is not supported. Please delete" " all undo tablespaces before creating new" " system tablespace."; return(DB_ERROR); } } /* Check if redo log file exists */ auto logfilename = get_log_file_path(); fh = os_file_create(innodb_log_file_key, logfilename.c_str(), OS_FILE_OPEN_RETRY | OS_FILE_ON_ERROR_NO_EXIT | OS_FILE_ON_ERROR_SILENT, OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret); if (ret) { os_file_close(fh); ib::error() << "redo log file '" << logfilename << "' exists. Creating system tablespace with" " existing redo log file is not recommended." " Please delete redo log file before" " creating new system tablespace."; return DB_ERROR; } return(DB_SUCCESS); } static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, ulint n_undo) { /* Open all the undo tablespaces that are currently in use. If we fail to open any of these it is a fatal error. The tablespace ids should be contiguous. It is a fatal error because they are required for recovery and are referenced by the UNDO logs (a.k.a RBS). */ ulint prev_id= create_new_db ? srv_undo_space_id_start - 1 : 0; for (ulint i= 0; i < n_undo; ++i) { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof name, "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i + 1); ulint space_id= srv_undo_tablespace_open(create_new_db, name, i); if (!space_id) { if (!create_new_db) break; ib::error() << "Unable to open create tablespace '" << name << "'."; return DB_ERROR; } /* Should be no gaps in undo tablespace ids. */ ut_a(!i || prev_id + 1 == space_id); prev_id= space_id; /* Note the first undo tablespace id in case of no active undo tablespace. */ if (0 == srv_undo_tablespaces_open++) srv_undo_space_id_start= space_id; } /* Open any extra unused undo tablespaces. These must be contiguous. We stop at the first failure. These are undo tablespaces that are not in use and therefore not required by recovery. We only check that there are no gaps. */ for (ulint i= prev_id + 1; i < srv_undo_space_id_start + TRX_SYS_N_RSEGS; ++i) { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof(name), "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i); if (!srv_undo_tablespace_open(create_new_db, name, i)) break; ++srv_undo_tablespaces_open; } return srv_validate_undo_tablespaces(); } /** Open the configured number of dedicated undo tablespaces. @param[in] create_new_db whether the database is being initialized @return DB_SUCCESS or error code */ dberr_t srv_undo_tablespaces_init(bool create_new_db) { srv_undo_tablespaces_open= 0; ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL); if (srv_undo_tablespaces == 1) srv_undo_tablespaces= 0; /* Create the undo spaces only if we are creating a new instance. We don't allow creating of new undo tablespaces in an existing instance (yet). */ if (create_new_db) { srv_undo_space_id_start= 1; DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;); for (ulint i= 0; i < srv_undo_tablespaces; ++i) { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof name, "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i + 1); if (dberr_t err= srv_undo_tablespace_create(name)) { ib::error() << "Could not create undo tablespace '" << name << "'."; return err; } } } /* Get the tablespace ids of all the undo segments excluding the system tablespace (0). If we are creating a new instance then we build the undo_tablespace_ids ourselves since they don't already exist. */ srv_undo_tablespaces_active= srv_undo_tablespaces; ulint n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP || srv_operation == SRV_OPERATION_RESTORE_DELTA) ? srv_undo_tablespaces : TRX_SYS_N_RSEGS; if (dberr_t err= srv_all_undo_tablespaces_open(create_new_db, n_undo)) return err; /* Initialize srv_undo_space_id_start=0 when there are no dedicated undo tablespaces. */ if (srv_undo_tablespaces_open == 0) srv_undo_space_id_start= 0; if (create_new_db) { mtr_t mtr; for (ulint i= 0; i < srv_undo_tablespaces; ++i) { mtr.start(); fsp_header_init(fil_space_get(srv_undo_space_id_start + i), SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); mtr.commit(); } } return DB_SUCCESS; } /** Create the temporary file tablespace. @param[in] create_new_db whether we are creating a new database @return DB_SUCCESS or error code. */ static dberr_t srv_open_tmp_tablespace(bool create_new_db) { ulint sum_of_new_sizes; /* Will try to remove if there is existing file left-over by last unclean shutdown */ srv_tmp_space.set_sanity_check_status(true); srv_tmp_space.delete_files(); srv_tmp_space.set_ignore_read_only(true); ib::info() << "Creating shared tablespace for temporary tables"; bool create_new_temp_space; srv_tmp_space.set_space_id(SRV_TMP_SPACE_ID); dberr_t err = srv_tmp_space.check_file_spec( &create_new_temp_space, 12 * 1024 * 1024); if (err == DB_FAIL) { ib::error() << "The innodb_temporary" " data file must be writable!"; err = DB_ERROR; } else if (err != DB_SUCCESS) { ib::error() << "Could not create the shared innodb_temporary."; } else if ((err = srv_tmp_space.open_or_create( true, create_new_db, &sum_of_new_sizes, NULL)) != DB_SUCCESS) { ib::error() << "Unable to create the shared innodb_temporary"; } else if (fil_system.temp_space->open(true)) { /* Initialize the header page */ mtr_t mtr; mtr.start(); mtr.set_log_mode(MTR_LOG_NO_REDO); fsp_header_init(fil_system.temp_space, srv_tmp_space.get_sum_of_sizes(), &mtr); mtr.commit(); } else { /* This file was just opened in the code above! */ ib::error() << "The innodb_temporary" " data file cannot be re-opened" " after check_file_spec() succeeded!"; err = DB_ERROR; } return(err); } /** Shutdown background threads, except the page cleaner. */ static void srv_shutdown_threads() { ut_ad(!srv_undo_sources); srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; lock_sys.timeout_timer.reset(); srv_master_timer.reset(); if (purge_sys.enabled()) { srv_purge_shutdown(); } if (srv_n_fil_crypt_threads) { fil_crypt_set_thread_cnt(0); } } #ifdef UNIV_DEBUG # define srv_init_abort(_db_err) \ srv_init_abort_low(create_new_db, __FILE__, __LINE__, _db_err) #else # define srv_init_abort(_db_err) \ srv_init_abort_low(create_new_db, _db_err) #endif /* UNIV_DEBUG */ /** Innobase start-up aborted. Perform cleanup actions. @param[in] create_new_db TRUE if new db is being created @param[in] file File name @param[in] line Line number @param[in] err Reason for aborting InnoDB startup @return DB_SUCCESS or error code. */ MY_ATTRIBUTE((warn_unused_result, nonnull)) static dberr_t srv_init_abort_low( bool create_new_db, #ifdef UNIV_DEBUG const char* file, unsigned line, #endif /* UNIV_DEBUG */ dberr_t err) { ut_ad(srv_is_being_started); if (create_new_db) { ib::error() << "Database creation was aborted" #ifdef UNIV_DEBUG " at " << innobase_basename(file) << "[" << line << "]" #endif /* UNIV_DEBUG */ " with error " << err << ". You may need" " to delete the ibdata1 file before trying to start" " up again."; } else { ib::error() << "Plugin initialization aborted" #ifdef UNIV_DEBUG " at " << innobase_basename(file) << "[" << line << "]" #endif /* UNIV_DEBUG */ " with error " << err; } srv_shutdown_bg_undo_sources(); srv_shutdown_threads(); return(err); } /** Prepare to delete the redo log file. Flush the dirty pages from all the buffer pools. Flush the redo log buffer to the redo log file. @param[in] old_exists old redo log file exists @return lsn upto which data pages have been flushed. */ static lsn_t srv_prepare_to_delete_redo_log_file(bool old_exists) { DBUG_ENTER("srv_prepare_to_delete_redo_log_file"); /* Disable checkpoints in the page cleaner. */ ut_ad(!recv_sys.recovery_on); recv_sys.recovery_on= true; /* Clean the buffer pool. */ buf_flush_sync(); if (log_sys.log.subformat != 2) srv_log_file_size= 0; DBUG_EXECUTE_IF("innodb_log_abort_1", DBUG_RETURN(0);); DBUG_PRINT("ib_log", ("After innodb_log_abort_1")); mysql_mutex_lock(&log_sys.mutex); const bool latest_format= (log_sys.log.format & ~log_t::FORMAT_ENCRYPTED) == log_t::FORMAT_10_5; lsn_t flushed_lsn= log_sys.get_lsn(); if (latest_format) { fil_names_clear(flushed_lsn, false); flushed_lsn= log_sys.get_lsn(); } { const char *msg; if (!latest_format || srv_log_file_size == 0) { msg= "Upgrading redo log: "; same_size: ib::info() << msg << srv_log_file_size_requested << " bytes; LSN=" << flushed_lsn; } else if (old_exists && srv_log_file_size == srv_log_file_size_requested) { msg= srv_encrypt_log ? "Encrypting redo log: " : "Removing redo log encryption: "; goto same_size; } else { if (srv_encrypt_log == (my_bool)log_sys.is_encrypted()) msg= srv_encrypt_log ? "Resizing encrypted" : "Resizing"; else msg= srv_encrypt_log ? "Encrypting and resizing" : "Removing encryption and resizing"; ib::info() << msg << " redo log from " << srv_log_file_size << " to " << srv_log_file_size_requested << " bytes; LSN=" << flushed_lsn; } } mysql_mutex_unlock(&log_sys.mutex); if (flushed_lsn != log_sys.get_flushed_lsn()) { log_write_up_to(flushed_lsn, false); log_sys.log.flush(); } ut_ad(flushed_lsn == log_sys.get_lsn()); ut_ad(!buf_pool.any_io_pending()); DBUG_RETURN(flushed_lsn); } /** Tries to locate LOG_FILE_NAME and check it's size, etc @param[out] log_file_found returns true here if correct file was found @return dberr_t with DB_SUCCESS or some error */ static dberr_t find_and_check_log_file(bool &log_file_found) { log_file_found= false; auto logfile0= get_log_file_path(); os_file_stat_t stat_info; const dberr_t err= os_file_get_status(logfile0.c_str(), &stat_info, false, srv_read_only_mode); auto is_operation_restore= []() -> bool { return srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE_EXPORT; }; if (err == DB_NOT_FOUND) { if (is_operation_restore()) return DB_NOT_FOUND; return DB_SUCCESS; } if (stat_info.type != OS_FILE_TYPE_FILE) return DB_SUCCESS; if (!srv_file_check_mode(logfile0.c_str())) return DB_ERROR; const os_offset_t size= stat_info.size; ut_a(size != (os_offset_t) -1); if (size % OS_FILE_LOG_BLOCK_SIZE) { ib::error() << "Log file " << logfile0 << " size " << size << " is not a multiple of " << OS_FILE_LOG_BLOCK_SIZE << " bytes"; return DB_ERROR; } if (size == 0 && is_operation_restore()) { /* Tolerate an empty LOG_FILE_NAME from a previous run of mariabackup --prepare. */ return DB_NOT_FOUND; } /* The first log file must consist of at least the following 512-byte pages: header, checkpoint page 1, empty, checkpoint page 2, redo log page(s). Mariabackup --prepare would create an empty LOG_FILE_NAME. Tolerate it. */ if (size != 0 && size <= OS_FILE_LOG_BLOCK_SIZE * 4) { ib::error() << "Log file " << logfile0 << " size " << size << " is too small"; return DB_ERROR; } srv_log_file_size= size; log_file_found= true; return DB_SUCCESS; } /** Start InnoDB. @param[in] create_new_db whether to create a new database @return DB_SUCCESS or error code */ dberr_t srv_start(bool create_new_db) { lsn_t flushed_lsn; dberr_t err = DB_SUCCESS; bool srv_log_file_found = true; mtr_t mtr; ut_ad(srv_operation == SRV_OPERATION_NORMAL || srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE_EXPORT); if (srv_force_recovery) { ib::info() << "!!! innodb_force_recovery is set to " << srv_force_recovery << " !!!"; } if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) { srv_read_only_mode = true; } high_level_read_only = srv_read_only_mode || srv_force_recovery > SRV_FORCE_NO_IBUF_MERGE || srv_sys_space.created_new_raw(); srv_started_redo = false; compile_time_assert(sizeof(ulint) == sizeof(void*)); #ifdef UNIV_DEBUG ib::info() << "!!!!!!!! UNIV_DEBUG switched on !!!!!!!!!"; #endif #ifdef UNIV_IBUF_DEBUG ib::info() << "!!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!"; #endif ib::info() << MUTEX_TYPE; ib::info() << "Compressed tables use zlib " ZLIB_VERSION #ifdef UNIV_ZIP_DEBUG " with validation" #endif /* UNIV_ZIP_DEBUG */ ; #ifdef UNIV_ZIP_COPY ib::info() << "and extra copying"; #endif /* UNIV_ZIP_COPY */ /* Since InnoDB does not currently clean up all its internal data structures in MySQL Embedded Server Library server_end(), we print an error message if someone tries to start up InnoDB a second time during the process lifetime. */ if (srv_start_has_been_called) { ib::error() << "Startup called second time" " during the process lifetime." " In the MySQL Embedded Server Library" " you cannot call server_init() more than" " once during the process lifetime."; } srv_start_has_been_called = true; srv_is_being_started = true; /* Register performance schema stages before any real work has been started which may need to be instrumented. */ mysql_stage_register("innodb", srv_stages, static_cast<int>(UT_ARR_SIZE(srv_stages))); /* Set the maximum number of threads which can wait for a semaphore inside InnoDB: this is the 'sync wait array' size */ srv_max_n_threads = 1 /* io_ibuf_thread */ + 1 /* io_log_thread */ + 1 /* srv_print_monitor_task */ + 1 /* srv_purge_coordinator_thread */ + 1 /* buf_dump_thread */ + 1 /* dict_stats_thread */ + 1 /* fts_optimize_thread */ + 1 /* trx_rollback_all_recovered */ + 128 /* added as margin, for use of InnoDB Memcached etc. */ + 1/* buf_flush_page_cleaner */ + max_connections + srv_n_read_io_threads + srv_n_write_io_threads + srv_n_purge_threads /* FTS Parallel Sort */ + fts_sort_pll_degree * FTS_NUM_AUX_INDEX * max_connections; srv_boot(); ib::info() << my_crc32c_implementation(); if (!srv_read_only_mode) { mutex_create(LATCH_ID_SRV_MONITOR_FILE, &srv_monitor_file_mutex); if (srv_innodb_status) { srv_monitor_file_name = static_cast<char*>( ut_malloc_nokey( strlen(fil_path_to_mysql_datadir) + 20 + sizeof "/innodb_status.")); sprintf(srv_monitor_file_name, "%s/innodb_status." ULINTPF, fil_path_to_mysql_datadir, static_cast<ulint> (IF_WIN(GetCurrentProcessId(), getpid()))); srv_monitor_file = my_fopen(srv_monitor_file_name, O_RDWR|O_TRUNC|O_CREAT, MYF(MY_WME)); if (!srv_monitor_file) { ib::error() << "Unable to create " << srv_monitor_file_name << ": " << strerror(errno); if (err == DB_SUCCESS) { err = DB_ERROR; } } } else { srv_monitor_file_name = NULL; srv_monitor_file = os_file_create_tmpfile(); if (!srv_monitor_file && err == DB_SUCCESS) { err = DB_ERROR; } } mutex_create(LATCH_ID_SRV_MISC_TMPFILE, &srv_misc_tmpfile_mutex); srv_misc_tmpfile = os_file_create_tmpfile(); if (!srv_misc_tmpfile && err == DB_SUCCESS) { err = DB_ERROR; } } if (err != DB_SUCCESS) { return(srv_init_abort(err)); } srv_n_file_io_threads = srv_n_read_io_threads + srv_n_write_io_threads; if (!srv_read_only_mode) { /* Add the log and ibuf IO threads. */ srv_n_file_io_threads += 2; } else { ib::info() << "Disabling background log and ibuf IO write" << " threads."; } ut_a(srv_n_file_io_threads <= SRV_MAX_N_IO_THREADS); if (os_aio_init()) { ib::error() << "Cannot initialize AIO sub-system"; return(srv_init_abort(DB_ERROR)); } #ifdef LINUX_NATIVE_AIO if (srv_use_native_aio) { ib::info() << "Using Linux native AIO"; } #endif fil_system.create(srv_file_per_table ? 50000 : 5000); ib::info() << "Initializing buffer pool, total size = " << srv_buf_pool_size << ", chunk size = " << srv_buf_pool_chunk_unit; if (buf_pool.create()) { ib::error() << "Cannot allocate memory for the buffer pool"; return(srv_init_abort(DB_ERROR)); } ib::info() << "Completed initialization of buffer pool"; #ifdef UNIV_DEBUG /* We have observed deadlocks with a 5MB buffer pool but the actual lower limit could very well be a little higher. */ if (srv_buf_pool_size <= 5 * 1024 * 1024) { ib::info() << "Small buffer pool size (" << srv_buf_pool_size / 1024 / 1024 << "M), the flst_validate() debug function can cause a" << " deadlock if the buffer pool fills up."; } #endif /* UNIV_DEBUG */ log_sys.create(); recv_sys.create(); lock_sys.create(srv_lock_table_size); if (!srv_read_only_mode) { buf_flush_page_cleaner_init(); ut_ad(buf_page_cleaner_is_active); } srv_startup_is_before_trx_rollback_phase = true; /* Check if undo tablespaces and redo log files exist before creating a new system tablespace */ if (create_new_db) { err = srv_check_undo_redo_logs_exists(); if (err != DB_SUCCESS) { return(srv_init_abort(DB_ERROR)); } recv_sys.debug_free(); } /* Open or create the data files. */ ulint sum_of_new_sizes; err = srv_sys_space.open_or_create( false, create_new_db, &sum_of_new_sizes, &flushed_lsn); switch (err) { case DB_SUCCESS: break; case DB_CANNOT_OPEN_FILE: ib::error() << "Could not open or create the system tablespace. If" " you tried to add new data files to the system" " tablespace, and it failed here, you should now" " edit innodb_data_file_path in my.cnf back to what" " it was, and remove the new ibdata files InnoDB" " created in this failed attempt. InnoDB only wrote" " those files full of zeros, but did not yet use" " them in any way. But be careful: do not remove" " old data files which contain your precious data!"; /* fall through */ default: /* Other errors might come from Datafile::validate_first_page() */ return(srv_init_abort(err)); } srv_log_file_size_requested = srv_log_file_size; if (innodb_encrypt_temporary_tables && !log_crypt_init()) { return srv_init_abort(DB_ERROR); } std::string logfile0; bool create_new_log = create_new_db; if (create_new_db) { flushed_lsn = log_sys.get_lsn(); log_sys.set_flushed_lsn(flushed_lsn); err = create_log_file(true, flushed_lsn, logfile0); if (err != DB_SUCCESS) { for (Tablespace::const_iterator i = srv_sys_space.begin(); i != srv_sys_space.end(); i++) { os_file_delete(innodb_data_file_key, i->filepath()); } return(srv_init_abort(err)); } } else { srv_log_file_size = 0; bool log_file_found; if (dberr_t err = find_and_check_log_file(log_file_found)) { if (err == DB_NOT_FOUND) { return DB_SUCCESS; } return srv_init_abort(err); } create_new_log = srv_log_file_size == 0; if (create_new_log) { if (flushed_lsn < lsn_t(1000)) { ib::error() << "Cannot create log file because" " data files are corrupt or the" " database was not shut down cleanly" " after creating the data files."; return srv_init_abort(DB_ERROR); } srv_log_file_size = srv_log_file_size_requested; err = create_log_file(false, flushed_lsn, logfile0); if (err == DB_SUCCESS) { err = create_log_file_rename(flushed_lsn, logfile0); } if (err != DB_SUCCESS) { return(srv_init_abort(err)); } /* Suppress the message about crash recovery. */ flushed_lsn = log_sys.get_lsn(); goto file_checked; } srv_log_file_found = log_file_found; log_sys.log.open_file(get_log_file_path()); log_sys.log.create(); if (!log_set_capacity(srv_log_file_size_requested)) { return(srv_init_abort(DB_ERROR)); } /* Enable checkpoints in the page cleaner. */ recv_sys.recovery_on = false; } file_checked: /* Open log file and data files in the systemtablespace: we keep them open until database shutdown */ ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); err = fil_system.sys_space->open(create_new_db) ? srv_undo_tablespaces_init(create_new_db) : DB_ERROR; /* If the force recovery is set very high then we carry on regardless of all errors. Basically this is fingers crossed mode. */ if (err != DB_SUCCESS && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { return(srv_init_abort(err)); } /* Initialize objects used by dict stats gathering thread, which can also be used by recovery if it tries to drop some table */ if (!srv_read_only_mode) { dict_stats_init(); } trx_sys.create(); if (create_new_db) { ut_ad(!srv_read_only_mode); mtr_start(&mtr); ut_ad(fil_system.sys_space->id == 0); compile_time_assert(TRX_SYS_SPACE == 0); compile_time_assert(IBUF_SPACE_ID == 0); fsp_header_init(fil_system.sys_space, uint32_t(sum_of_new_sizes), &mtr); ulint ibuf_root = btr_create( DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space, DICT_IBUF_ID_MIN, nullptr, &mtr); mtr_commit(&mtr); if (ibuf_root == FIL_NULL) { return(srv_init_abort(DB_ERROR)); } ut_ad(ibuf_root == IBUF_TREE_ROOT_PAGE_NO); /* To maintain backward compatibility we create only the first rollback segment before the double write buffer. All the remaining rollback segments will be created later, after the double write buffer has been created. */ trx_sys_create_sys_pages(); err = trx_lists_init_at_db_start(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } err = dict_create(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } buf_flush_sync(); flushed_lsn = log_sys.get_lsn(); err = fil_write_flushed_lsn(flushed_lsn); if (err == DB_SUCCESS) { err = create_log_file_rename(flushed_lsn, logfile0); } if (err != DB_SUCCESS) { return(srv_init_abort(err)); } } else { /* Suppress warnings in fil_space_t::create() for files that are being read before dict_boot() has recovered DICT_HDR_MAX_SPACE_ID. */ fil_system.space_id_reuse_warned = true; /* We always try to do a recovery, even if the database had been shut down normally: this is the normal startup path */ err = create_new_log ? DB_SUCCESS : recv_recovery_from_checkpoint_start(flushed_lsn); recv_sys.close_files(); recv_sys.dblwr.pages.clear(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } switch (srv_operation) { case SRV_OPERATION_NORMAL: case SRV_OPERATION_RESTORE_EXPORT: /* Initialize the change buffer. */ err = dict_boot(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } /* fall through */ case SRV_OPERATION_RESTORE: /* This must precede recv_sys.apply(true). */ srv_undo_tablespaces_active = trx_rseg_get_n_undo_tablespaces(); err = srv_validate_undo_tablespaces(); if (err != DB_SUCCESS) { return srv_init_abort(err); } if (srv_operation == SRV_OPERATION_RESTORE) { break; } err = trx_lists_init_at_db_start(); if (err != DB_SUCCESS) { return srv_init_abort(err); } break; case SRV_OPERATION_RESTORE_DELTA: case SRV_OPERATION_BACKUP: ut_ad("wrong mariabackup mode" == 0); } if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { /* Apply the hashed log records to the respective file pages, for the last batch of recv_group_scan_log_recs(). */ recv_sys.apply(true); if (recv_sys.found_corrupt_log || recv_sys.found_corrupt_fs) { return(srv_init_abort(DB_CORRUPTION)); } DBUG_PRINT("ib_log", ("apply completed")); if (recv_needed_recovery) { trx_sys_print_mysql_binlog_offset(); } } fil_system.space_id_reuse_warned = false; if (!srv_read_only_mode) { const ulint flags = FSP_FLAGS_PAGE_SSIZE(); for (ulint id = 0; id <= srv_undo_tablespaces; id++) { if (fil_space_t* space = fil_space_get(id)) { fsp_flags_try_adjust(space, flags); } } if (sum_of_new_sizes > 0) { /* New data file(s) were added */ mtr.start(); mtr.x_lock_space(fil_system.sys_space, __FILE__, __LINE__); buf_block_t* block = buf_page_get( page_id_t(0, 0), 0, RW_SX_LATCH, &mtr); ulint size = mach_read_from_4( FSP_HEADER_OFFSET + FSP_SIZE + block->frame); ut_ad(size == fil_system.sys_space ->size_in_header); size += sum_of_new_sizes; mtr.write<4>(*block, FSP_HEADER_OFFSET + FSP_SIZE + block->frame, size); fil_system.sys_space->size_in_header = uint32_t(size); mtr.commit(); /* Immediately write the log record about increased tablespace size to disk, so that it is durable even if mysqld would crash quickly */ log_buffer_flush_to_disk(); } } #ifdef UNIV_DEBUG { mtr.start(); buf_block_t* block = buf_page_get(page_id_t(0, 0), 0, RW_S_LATCH, &mtr); ut_ad(mach_read_from_4(FSP_SIZE + FSP_HEADER_OFFSET + block->frame) == fil_system.sys_space->size_in_header); mtr.commit(); } #endif const ulint tablespace_size_in_header = fil_system.sys_space->size_in_header; const ulint sum_of_data_file_sizes = srv_sys_space.get_sum_of_sizes(); /* Compare the system tablespace file size to what is stored in FSP_SIZE. In srv_sys_space.open_or_create() we already checked that the file sizes match the innodb_data_file_path specification. */ if (srv_read_only_mode || sum_of_data_file_sizes == tablespace_size_in_header) { /* Do not complain about the size. */ } else if (!srv_sys_space.can_auto_extend_last_file() || sum_of_data_file_sizes < tablespace_size_in_header) { ib::error() << "Tablespace size stored in header is " << tablespace_size_in_header << " pages, but the sum of data file sizes is " << sum_of_data_file_sizes << " pages"; if (srv_force_recovery == 0 && sum_of_data_file_sizes < tablespace_size_in_header) { ib::error() << "Cannot start InnoDB. The tail of" " the system tablespace is" " missing. Have you edited" " innodb_data_file_path in my.cnf" " in an inappropriate way, removing" " data files from there?" " You can set innodb_force_recovery=1" " in my.cnf to force" " a startup if you are trying to" " recover a badly corrupt database."; return(srv_init_abort(DB_ERROR)); } } recv_sys.debug_free(); if (srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE_EXPORT) { /* After applying the redo log from SRV_OPERATION_BACKUP, flush the changes to the data files and truncate or delete the log. Unless --export is specified, no further change to InnoDB files is needed. */ ut_ad(srv_force_recovery <= SRV_FORCE_IGNORE_CORRUPT); ut_ad(recv_no_log_write); err = fil_write_flushed_lsn(log_sys.get_lsn()); DBUG_ASSERT(!buf_pool.any_io_pending()); log_sys.log.close_file(); if (err == DB_SUCCESS) { bool trunc = srv_operation == SRV_OPERATION_RESTORE; if (!trunc) { delete_log_file("0"); } else { auto logfile0 = get_log_file_path(); /* Truncate the first log file. */ fclose(fopen(logfile0.c_str(), "w")); } } return(err); } /* Upgrade or resize or rebuild the redo logs before generating any dirty pages, so that the old redo log file will not be written to. */ if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) { /* Completely ignore the redo log. */ } else if (srv_read_only_mode) { /* Leave the redo log alone. */ } else if (srv_log_file_size_requested == srv_log_file_size && srv_log_file_found && log_sys.log.format == (srv_encrypt_log ? log_t::FORMAT_ENC_10_5 : log_t::FORMAT_10_5) && log_sys.log.subformat == 2) { /* No need to add or remove encryption, upgrade, downgrade, or resize. */ } else { /* Prepare to delete the old redo log file */ flushed_lsn = srv_prepare_to_delete_redo_log_file( srv_log_file_found); DBUG_EXECUTE_IF("innodb_log_abort_1", return(srv_init_abort(DB_ERROR));); /* Prohibit redo log writes from any other threads until creating a log checkpoint at the end of create_log_file(). */ ut_d(recv_no_log_write = true); DBUG_ASSERT(!buf_pool.any_io_pending()); DBUG_EXECUTE_IF("innodb_log_abort_3", return(srv_init_abort(DB_ERROR));); DBUG_PRINT("ib_log", ("After innodb_log_abort_3")); /* Stamp the LSN to the data files. */ err = fil_write_flushed_lsn(flushed_lsn); DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;); DBUG_PRINT("ib_log", ("After innodb_log_abort_4")); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } /* Close the redo log file, so that we can replace it */ log_sys.log.close_file(); DBUG_EXECUTE_IF("innodb_log_abort_5", return(srv_init_abort(DB_ERROR));); DBUG_PRINT("ib_log", ("After innodb_log_abort_5")); ib::info() << "Starting to delete and rewrite log file."; srv_log_file_size = srv_log_file_size_requested; err = create_log_file(false, flushed_lsn, logfile0); if (err == DB_SUCCESS) { err = create_log_file_rename(flushed_lsn, logfile0); } if (err != DB_SUCCESS) { return(srv_init_abort(err)); } } } ut_ad(err == DB_SUCCESS); ut_a(sum_of_new_sizes != ULINT_UNDEFINED); /* Create the doublewrite buffer to a new tablespace */ if (!srv_read_only_mode && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO && !buf_dblwr.create()) { return(srv_init_abort(DB_ERROR)); } /* Here the double write buffer has already been created and so any new rollback segments will be allocated after the double write buffer. The default segment should already exist. We create the new segments only if it's a new database or the database was shutdown cleanly. */ /* Note: When creating the extra rollback segments during an upgrade we violate the latching order, even if the change buffer is empty. We make an exception in sync0sync.cc and check srv_is_being_started for that violation. It cannot create a deadlock because we are still running in single threaded mode essentially. Only the IO threads should be running at this stage. */ if (!trx_sys_create_rsegs()) { return(srv_init_abort(DB_ERROR)); } if (!create_new_db) { ut_ad(high_level_read_only || srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE); /* Validate a few system page types that were left uninitialized before MySQL or MariaDB 5.5. */ if (!high_level_read_only && !fil_system.sys_space->full_crc32()) { buf_block_t* block; mtr.start(); /* Bitmap page types will be reset in buf_dblwr_check_block() without redo logging. */ block = buf_page_get( page_id_t(IBUF_SPACE_ID, FSP_IBUF_HEADER_PAGE_NO), 0, RW_X_LATCH, &mtr); fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); /* Already MySQL 3.23.53 initialized FSP_IBUF_TREE_ROOT_PAGE_NO to FIL_PAGE_INDEX. No need to reset that one. */ block = buf_page_get( page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO), 0, RW_X_LATCH, &mtr); fil_block_check_type(*block, FIL_PAGE_TYPE_TRX_SYS, &mtr); block = buf_page_get( page_id_t(TRX_SYS_SPACE, FSP_FIRST_RSEG_PAGE_NO), 0, RW_X_LATCH, &mtr); fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); block = buf_page_get( page_id_t(TRX_SYS_SPACE, FSP_DICT_HDR_PAGE_NO), 0, RW_X_LATCH, &mtr); fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr); mtr.commit(); } /* Roll back any recovered data dictionary transactions, so that the data dictionary tables will be free of any locks. The data dictionary latch should guarantee that there is at most one data dictionary transaction active at a time. */ if (!high_level_read_only && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) { /* If the following call is ever removed, the first-time ha_innobase::open() must hold (or acquire and release) a table lock that conflicts with trx_resurrect_table_locks(), to ensure that any recovered incomplete ALTER TABLE will have been rolled back. Otherwise, dict_table_t::instant could be cleared by rollback invoking dict_index_t::clear_instant_alter() while open table handles exist in client connections. */ trx_rollback_recovered(false); } /* FIXME: Skip the following if srv_read_only_mode, while avoiding "Allocated tablespace ID" warnings. */ if (srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE) { /* Open or Create SYS_TABLESPACES and SYS_DATAFILES so that tablespace names and other metadata can be found. */ err = dict_create_or_check_sys_tablespace(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } /* The following call is necessary for the insert buffer to work with multiple tablespaces. We must know the mapping between space id's and .ibd file names. In a crash recovery, we check that the info in data dictionary is consistent with what we already know about space id's from the calls to fil_ibd_load(). In a normal startup, we create the space objects for every table in the InnoDB data dictionary that has an .ibd file. We also determine the maximum tablespace id used. */ dict_check_tablespaces_and_store_max_id(); } if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO && !srv_read_only_mode) { /* Drop partially created indexes. */ row_merge_drop_temp_indexes(); /* Drop garbage tables. */ row_mysql_drop_garbage_tables(); /* Drop any auxiliary tables that were not dropped when the parent table was dropped. This can happen if the parent table was dropped but the server crashed before the auxiliary tables were dropped. */ fts_drop_orphaned_tables(); /* Rollback incomplete non-DDL transactions */ trx_rollback_is_active = true; os_thread_create(trx_rollback_all_recovered); } } srv_startup_is_before_trx_rollback_phase = false; if (!srv_read_only_mode) { /* timer task which watches the timeouts for lock waits */ lock_sys.timeout_timer.reset(srv_thread_pool->create_timer( lock_wait_timeout_task)); DBUG_EXECUTE_IF("innodb_skip_monitors", goto skip_monitors;); /* Create the task which warns of long semaphore waits */ srv_start_periodic_timer(srv_monitor_timer, srv_monitor_task, SRV_MONITOR_INTERVAL); #ifndef DBUG_OFF skip_monitors: #endif ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN || !purge_sys.enabled()); if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { srv_undo_sources = true; /* Create the dict stats gathering task */ dict_stats_start(); /* Create the thread that will optimize the FULLTEXT search index subsystem. */ fts_optimize_init(); } } /* Create the SYS_FOREIGN and SYS_FOREIGN_COLS system tables */ err = dict_create_or_check_foreign_constraint_tables(); if (err == DB_SUCCESS) { err = dict_create_or_check_sys_tablespace(); if (err == DB_SUCCESS) { err = dict_create_or_check_sys_virtual(); } } switch (err) { case DB_SUCCESS: break; case DB_READ_ONLY: if (srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) { break; } ib::error() << "Cannot create system tables in read-only mode"; /* fall through */ default: return(srv_init_abort(err)); } if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL) { /* Initialize the innodb_temporary tablespace and keep it open until shutdown. */ err = srv_open_tmp_tablespace(create_new_db); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } trx_temp_rseg_create(); if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { srv_start_periodic_timer(srv_master_timer, srv_master_callback, 1000); } } if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL && srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { srv_init_purge_tasks(); purge_sys.coordinator_startup(); srv_wake_purge_thread_if_not_active(); } srv_is_being_started = false; if (srv_print_verbose_log) { ib::info() << INNODB_VERSION_STR << " started; log sequence number " << recv_sys.recovered_lsn << "; transaction id " << trx_sys.get_max_trx_id(); } if (srv_force_recovery == 0) { /* In the change buffer we may have even bigger tablespace id's, because we may have dropped those tablespaces, but the buffered records have not been cleaned yet. */ ibuf_update_max_tablespace_id(); } if (!srv_read_only_mode) { if (create_new_db) { srv_buffer_pool_load_at_startup = FALSE; } #ifdef WITH_WSREP /* Create the dump/load thread only when not running with --wsrep-recover. */ if (!get_wsrep_recovery()) { #endif /* WITH_WSREP */ /* Start buffer pool dump/load task */ buf_load_at_startup(); #ifdef WITH_WSREP } else { ib::warn() << "Skipping buffer pool dump/restore during " "wsrep recovery."; } #endif /* WITH_WSREP */ /* Create thread(s) that handles key rotation. This is needed already here as log_preflush_pool_modified_pages will flush dirty pages and that might need e.g. fil_crypt_threads_event. */ fil_system_enter(); fil_crypt_threads_init(); fil_system_exit(); /* Initialize online defragmentation. */ btr_defragment_init(); srv_started_redo = true; } return(DB_SUCCESS); } /** Shut down background threads that can generate undo log. */ void srv_shutdown_bg_undo_sources() { srv_shutdown_state = SRV_SHUTDOWN_INITIATED; if (srv_undo_sources) { ut_ad(!srv_read_only_mode); fts_optimize_shutdown(); dict_stats_shutdown(); while (row_get_background_drop_list_len_low()) { srv_inc_activity_count(); os_thread_yield(); } srv_undo_sources = false; } } /** Shutdown purge to make sure that there is no possibility that we call any plugin code (e.g., audit) inside virtual column computation. */ void innodb_preshutdown() { static bool first_time= true; if (!first_time) return; first_time= false; if (srv_read_only_mode) return; if (!srv_fast_shutdown && srv_operation == SRV_OPERATION_NORMAL) { /* Because a slow shutdown must empty the change buffer, we had better prevent any further changes from being buffered. */ innodb_change_buffering= 0; if (trx_sys.is_initialised()) while (trx_sys.any_active_transactions()) os_thread_sleep(1000); } srv_shutdown_bg_undo_sources(); srv_purge_shutdown(); if (srv_n_fil_crypt_threads) fil_crypt_set_thread_cnt(0); } /** Shut down InnoDB. */ void innodb_shutdown() { innodb_preshutdown(); ut_ad(!srv_undo_sources); switch (srv_operation) { case SRV_OPERATION_BACKUP: case SRV_OPERATION_RESTORE_DELTA: break; case SRV_OPERATION_RESTORE: case SRV_OPERATION_RESTORE_EXPORT: mysql_mutex_lock(&buf_pool.flush_list_mutex); srv_shutdown_state = SRV_SHUTDOWN_CLEANUP; while (buf_page_cleaner_is_active) { pthread_cond_signal(&buf_pool.do_flush_list); my_cond_wait(&buf_pool.done_flush_list, &buf_pool.flush_list_mutex.m_mutex); } mysql_mutex_unlock(&buf_pool.flush_list_mutex); break; case SRV_OPERATION_NORMAL: /* Shut down the persistent files. */ logs_empty_and_mark_files_at_shutdown(); } os_aio_free(); fil_space_t::close_all(); /* Exit any remaining threads. */ ut_ad(!buf_page_cleaner_is_active); srv_shutdown_threads(); if (srv_monitor_file) { my_fclose(srv_monitor_file, MYF(MY_WME)); srv_monitor_file = 0; if (srv_monitor_file_name) { unlink(srv_monitor_file_name); ut_free(srv_monitor_file_name); } } if (srv_misc_tmpfile) { my_fclose(srv_misc_tmpfile, MYF(MY_WME)); srv_misc_tmpfile = 0; } ut_ad(dict_sys.is_initialised() || !srv_was_started); ut_ad(trx_sys.is_initialised() || !srv_was_started); ut_ad(buf_dblwr.is_initialised() || !srv_was_started || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); ut_ad(lock_sys.is_initialised() || !srv_was_started); ut_ad(log_sys.is_initialised() || !srv_was_started); ut_ad(ibuf.index || !srv_was_started); dict_stats_deinit(); if (srv_started_redo) { ut_ad(!srv_read_only_mode); /* srv_shutdown_bg_undo_sources() already invoked fts_optimize_shutdown(); dict_stats_shutdown(); */ fil_crypt_threads_cleanup(); btr_defragment_shutdown(); } /* This must be disabled before closing the buffer pool and closing the data dictionary. */ #ifdef BTR_CUR_HASH_ADAPT if (dict_sys.is_initialised()) { btr_search_disable(); } #endif /* BTR_CUR_HASH_ADAPT */ ibuf_close(); log_sys.close(); purge_sys.close(); trx_sys.close(); buf_dblwr.close(); lock_sys.close(); trx_pool_close(); if (!srv_read_only_mode) { mutex_free(&srv_monitor_file_mutex); mutex_free(&srv_misc_tmpfile_mutex); } dict_sys.close(); btr_search_sys_free(); row_mysql_close(); srv_free(); fil_system.close(); pars_lexer_close(); recv_sys.close(); ut_ad(buf_pool.is_initialised() || !srv_was_started); buf_pool.close(); sync_check_close(); srv_sys_space.shutdown(); if (srv_tmp_space.get_sanity_check_status()) { if (fil_system.temp_space) { fil_system.temp_space->close(); } srv_tmp_space.delete_files(); } srv_tmp_space.shutdown(); #ifdef WITH_INNODB_DISALLOW_WRITES os_event_destroy(srv_allow_writes_event); #endif /* WITH_INNODB_DISALLOW_WRITES */ if (srv_was_started && srv_print_verbose_log) { ib::info() << "Shutdown completed; log sequence number " << srv_shutdown_lsn << "; transaction id " << trx_sys.get_max_trx_id(); } srv_thread_pool_end(); srv_started_redo = false; srv_was_started = false; srv_start_has_been_called = false; } /** Get the meta-data filename from the table name for a single-table tablespace. @param[in] table table object @param[out] filename filename @param[in] max_len filename max length */ void srv_get_meta_data_filename( dict_table_t* table, char* filename, ulint max_len) { ulint len; char* path; /* Make sure the data_dir_path is set. */ dict_get_and_save_data_dir_path(table, false); if (DICT_TF_HAS_DATA_DIR(table->flags)) { ut_a(table->data_dir_path); path = fil_make_filepath( table->data_dir_path, table->name.m_name, CFG, true); } else { path = fil_make_filepath(NULL, table->name.m_name, CFG, false); } ut_a(path); len = strlen(path); ut_a(max_len >= len); strcpy(filename, path); ut_free(path); }