/***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2009, Percona Inc. Copyright (c) 2013, 2022, MariaDB Corporation. 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 "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 "zlib.h" #include "log.h" /** 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; /** UNDO tablespaces starts with space id. */ uint32_t 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 the server was successfully started */ bool srv_was_started; /** whether srv_start() has been called */ static bool srv_start_has_been_called; /** Whether any undo log records can be generated */ bool srv_undo_sources; /** innodb_encrypt_log */ my_bool srv_encrypt_log; #ifdef UNIV_DEBUG /** InnoDB system tablespace to set during recovery */ uint srv_sys_space_size_debug; /** whether redo log file have been created at startup */ 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 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 */ /** Delete any garbage log files */ static void delete_log_files() { for (size_t i= 1; i < 102; i++) delete_log_file(std::to_string(i).c_str()); } /** Creates log file. @param create_new_db whether the database is being initialized @param lsn log sequence number @param 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) { ut_ad(!srv_read_only_mode); /* We will retain ib_logfile0 until we have written a new logically empty log as ib_logfile101 and atomically renamed it to ib_logfile0 in log_t::rename_resized(). */ delete_log_files(); ut_ad(!os_aio_pending_reads()); ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); ut_ad(!buf_pool.get_oldest_modification(0)); ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); /* os_aio_pending_writes() may hold here if some write_io_callback() did not release the slot yet. However, the page write itself must have completed, because the buf_pool.flush_list is empty. In debug builds, we wait for this to happen, hoping to get a hung process if this assumption does not hold. */ ut_d(os_aio_wait_until_no_pending_writes(false)); log_sys.latch.wr_lock(SRW_LOCK_CALL); log_sys.set_capacity(); std::string logfile0{get_log_file_path("ib_logfile101")}; bool ret; os_file_t file{ os_file_create_func(logfile0.c_str(), OS_FILE_CREATE, OS_FILE_NORMAL, OS_LOG_FILE, false, &ret) }; if (!ret) { sql_print_error("InnoDB: Cannot create %.*s", int(logfile0.size()), logfile0.data()); err_exit: log_sys.latch.wr_unlock(); return DB_ERROR; } ret = os_file_set_size(logfile0.c_str(), file, srv_log_file_size); if (!ret) { ib::error() << "Cannot set log file " << logfile0 << " size to " << ib::bytes_iec{srv_log_file_size}; close_and_exit: os_file_close_func(file); goto err_exit; } log_sys.set_latest_format(srv_encrypt_log); if (!log_sys.attach(file, srv_log_file_size)) { goto close_and_exit; } if (!fil_system.sys_space->open(create_new_db)) { goto err_exit; } /* Create a log checkpoint. */ if (log_sys.is_encrypted() && !log_crypt_init()) { goto err_exit; } ut_d(recv_no_log_write = false); log_sys.create(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; log_sys.latch.wr_unlock(); log_make_checkpoint(); log_buffer_flush_to_disk(); return DB_SUCCESS; } /** Rename the redo log file after resizing. @return whether an error occurred */ bool log_t::resize_rename() noexcept { std::string old_name{get_log_file_path("ib_logfile101")}; std::string new_name{get_log_file_path()}; if (IF_WIN(MoveFileEx(old_name.c_str(), new_name.c_str(), MOVEFILE_REPLACE_EXISTING), !rename(old_name.c_str(), new_name.c_str()))) return false; sql_print_error("InnoDB: Failed to rename log from %.*s to %.*s (error %d)", int(old_name.size()), old_name.data(), int(new_name.size()), new_name.data(), IF_WIN(int(GetLastError()), errno)); return true; } /** 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 _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 ) { 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 " << ib::bytes_iec{SRV_UNDO_TABLESPACE_SIZE_IN_PAGES << srv_page_size_shift}; 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); } inline dberr_t trx_sys_t::reset_page(mtr_t *mtr) { dberr_t err= DB_SUCCESS; buf_block_t *sys_header= buf_page_get_gen( page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO), 0, RW_X_LATCH, nullptr, BUF_GET, mtr, &err); if (!sys_header) return err; const bool dblwr_enabled= mach_read_from_4(TRX_SYS_DOUBLEWRITE_MAGIC + TRX_SYS_DOUBLEWRITE + sys_header->page.frame) == TRX_SYS_DOUBLEWRITE_MAGIC_N; char doublewrite[TRX_SYS_DOUBLEWRITE_BLOCK2 + 4]; memcpy(doublewrite, TRX_SYS_DOUBLEWRITE + sys_header->page.frame, sizeof doublewrite); fsp_init_file_page(fil_system.sys_space, sys_header, mtr); mtr->write<2>(*sys_header, FIL_PAGE_TYPE + sys_header->page.frame, FIL_PAGE_TYPE_TRX_SYS); mtr->write<4>(*sys_header, TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_PAGE_NO + sys_header->page.frame, FSP_FIRST_RSEG_PAGE_NO); mtr->memset(sys_header, TRX_SYS + TRX_SYS_RSEGS + TRX_SYS_RSEG_SLOT_SIZE, 254 * TRX_SYS_RSEG_SLOT_SIZE, 0xff); static_assert(TRX_SYS_RSEG_SLOT_SIZE == 8, ""); if (dblwr_enabled) { mtr->memcpy( *sys_header, sys_header->page.frame + TRX_SYS_DOUBLEWRITE, doublewrite, sizeof doublewrite); mtr->memmove( *sys_header, TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE + TRX_SYS_DOUBLEWRITE_REPEAT, TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE, 12); memcpy( sys_header->page.frame + TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE + TRX_SYS_DOUBLEWRITE_REPEAT, sys_header->page.frame + TRX_SYS_DOUBLEWRITE + FSEG_HEADER_SIZE, 12); } return DB_SUCCESS; } /** Delete the old undo tablespaces present in the undo log directory */ static dberr_t srv_undo_delete_old_tablespaces() { /* Delete the old undo tablespaces*/ for (uint32_t i= 0; i < srv_undo_tablespaces_open; ++i) fil_close_tablespace(srv_undo_space_id_start + i); DBUG_EXECUTE_IF("after_deleting_old_undo_abort", return DB_ERROR;); /* Do checkpoint to get rid of old undo log tablespaces redo logs */ log_make_checkpoint(); DBUG_EXECUTE_IF("after_deleting_old_undo_success", return DB_ERROR;); return DB_SUCCESS; } /** Recreate the undo log tablespaces */ ATTRIBUTE_COLD static dberr_t srv_undo_tablespaces_reinit() { mtr_t mtr; dberr_t err; buf_block_t *first_rseg_hdr; uint32_t latest_space_id; mtr.start(); buf_block_t *dict_hdr= buf_page_get_gen( page_id_t(DICT_HDR_SPACE, DICT_HDR_PAGE_NO), 0, RW_X_LATCH, nullptr, BUF_GET, &mtr, &err); if (!dict_hdr) goto func_exit; /* Assign the new space id for the first undo tablespace */ latest_space_id= mach_read_from_4( DICT_HDR + DICT_HDR_MAX_SPACE_ID + dict_hdr->page.frame); if (latest_space_id + srv_undo_tablespaces > SRV_SPACE_ID_UPPER_BOUND) { err= DB_ERROR; sql_print_error("InnoDB: Running out of tablespace id"); goto func_exit; } first_rseg_hdr= buf_page_get_gen(trx_sys.rseg_array[0].page_id(), 0, RW_X_LATCH, nullptr, BUF_GET, &mtr, &err); if (!first_rseg_hdr) goto func_exit; if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT + first_rseg_hdr->page.frame))) trx_rseg_format_upgrade(first_rseg_hdr, &mtr); mtr.write<8,mtr_t::MAYBE_NOP>(*first_rseg_hdr, TRX_RSEG + TRX_RSEG_MAX_TRX_ID + first_rseg_hdr->page.frame, trx_sys.get_max_trx_id() - 1); /* Reset TRX_SYS page */ err= trx_sys.reset_page(&mtr); if (err) goto func_exit; if (srv_undo_tablespaces_open == 0) { /* Free the system rollback segment */ for (ulint i= 1; i < TRX_SYS_N_RSEGS; i++) { trx_rseg_t *rseg= &trx_sys.rseg_array[i]; if (rseg->space != fil_system.sys_space) continue; buf_block_t *block= buf_page_get_gen( rseg->page_id(), 0, RW_X_LATCH, nullptr, BUF_GET, &mtr); if (!block) break; while (!fseg_free_step(TRX_RSEG + TRX_RSEG_FSEG_HEADER + block->page.frame, &mtr)); } } for (ulint rseg_id= 1; rseg_id < TRX_SYS_N_RSEGS; rseg_id++) { trx_rseg_t *rseg= &trx_sys.rseg_array[rseg_id]; rseg->destroy(); rseg->init(nullptr, FIL_NULL); } if (*trx_sys.recovered_binlog_filename #ifdef WITH_WSREP || !trx_sys.recovered_wsrep_xid.is_null() #endif /* WITH_WSREP */ ) { /* Update binlog offset, binlog file name & wsrep xid in system tablespace rollback segment */ if (*trx_sys.recovered_binlog_filename) { ut_d(const size_t len = strlen(trx_sys.recovered_binlog_filename) + 1); ut_ad(len > 1); ut_ad(len <= TRX_RSEG_BINLOG_NAME_LEN); trx_rseg_update_binlog_offset( first_rseg_hdr, trx_sys.recovered_binlog_filename, trx_sys.recovered_binlog_offset, &mtr); } #ifdef WITH_WSREP if (!trx_sys.recovered_wsrep_xid.is_null()) trx_rseg_update_wsrep_checkpoint( first_rseg_hdr, &trx_sys.recovered_wsrep_xid, &mtr); #endif /* WITH_WSREP */ } dict_hdr->page.fix(); mtr.commit(); DBUG_EXECUTE_IF("after_rseg_reset_abort", log_write_up_to(mtr.commit_lsn(), true); dict_hdr->page.unfix(); return DB_ERROR;); sql_print_information( "InnoDB: Reinitializing innodb_undo_tablespaces= %u from %u", srv_undo_tablespaces, srv_undo_tablespaces_open); /* Delete the old undo tablespaces */ err= srv_undo_delete_old_tablespaces(); if (err) { dict_hdr->page.unfix(); return err; } mtr.start(); dict_hdr->page.lock.x_lock(); mtr.memo_push(dict_hdr, MTR_MEMO_PAGE_X_FIX); if (srv_undo_tablespaces == 0) { srv_undo_space_id_start= 0; srv_undo_tablespaces_open= 0; goto func_exit; } srv_undo_space_id_start= latest_space_id; if (fil_assign_new_space_id(&srv_undo_space_id_start)) mtr.write<4>(*dict_hdr, DICT_HDR + DICT_HDR_MAX_SPACE_ID + dict_hdr->page.frame, srv_undo_space_id_start); /* Re-create the new undo tablespaces */ err= srv_undo_tablespaces_init(true, &mtr); func_exit: mtr.commit(); DBUG_EXECUTE_IF("after_reinit_undo_abort", log_write_up_to(mtr.commit_lsn(), true); err= DB_ERROR;); if (err == DB_SUCCESS) { /* Usually, recovery must work no matter when log_checkpoints are triggered. This is a special case, because this code is executed as part of InnoDB startup. Backup requires that the server has been started up, backup should never observe the log records that were written in mtr and also srv_undo_tablespaces_init() initializes the undo tablespace start id based on page0 content before reading the redo log */ log_make_checkpoint(); DBUG_EXECUTE_IF("after_reinit_undo_success", err= DB_ERROR;); srv_undo_tablespaces_active= srv_undo_tablespaces; } return err; } /** Reinitialize the undo tablespaces when there is no undo log left to purge/rollback and validate the number of undo opened undo tablespace and user given undo tablespace @return DB_SUCCESS if it is valid */ static dberr_t srv_undo_tablespaces_reinitialize() { /* Re-create the undo tablespaces if it has no undo logs left to purge/rollback */ if (srv_undo_tablespaces != srv_undo_tablespaces_open && trx_sys.is_undo_empty()) return srv_undo_tablespaces_reinit(); /* 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) { sql_print_warning("InnoDB: Cannot change innodb_undo_tablespaces=%u " "because previous shutdown was not with " "innodb_fast_shutdown=0", srv_undo_tablespaces); srv_undo_tablespaces= srv_undo_tablespaces_open; } else if (srv_undo_tablespaces_open > 0) sql_print_information("InnoDB: Opened " UINT32PF " undo tablespaces", srv_undo_tablespaces_open); return DB_SUCCESS; } /** @return the number of active undo tablespaces (except system tablespace) */ static uint32_t trx_rseg_get_n_undo_tablespaces() { std::set 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 static_cast(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 if file doesn't exist @retval ~0U if page0 is corrupted */ static uint32_t srv_undo_tablespace_open(bool create, const char* name, uint32_t i) { bool success; uint32_t space_id= 0; uint32_t fsp_flags= 0; if (create) { space_id= srv_undo_space_id_start + i; 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_SILENT, OS_FILE_AIO, OS_DATA_FILE, srv_read_only_mode, &success); if (!success) return 0; ulint n_retries = 5; os_offset_t size= os_file_get_size(fh); ut_a(size != os_offset_t(-1)); if (!create) { page_t *page= static_cast(aligned_malloc(srv_page_size, srv_page_size)); undo_retry: if (os_file_read(IORequestRead, fh, page, 0, srv_page_size, nullptr) != DB_SUCCESS) { err_exit: if (n_retries && srv_operation == SRV_OPERATION_BACKUP) { sql_print_information("InnoDB: Retrying to read undo " "tablespace %s", name); n_retries--; goto undo_retry; } ib::error() << "Unable to read first page of file " << name; aligned_free(page); return ~0U; } DBUG_EXECUTE_IF("undo_space_read_fail", goto err_exit;); 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; goto err_exit; } space_id= id; fsp_flags= mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page); if (buf_page_is_corrupted(false, page, fsp_flags)) { sql_print_error("InnoDB: Checksum mismatch in the first page of file %s", name); if (recv_sys.dblwr.restore_first_page(space_id, name, fh)) goto err_exit; } 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); mysql_mutex_lock(&fil_system.mutex); fil_space_t *space= fil_space_t::create(space_id, fsp_flags, FIL_TYPE_TABLESPACE, nullptr, FIL_ENCRYPTION_DEFAULT, true); ut_ad(space); fil_node_t *file= space->add(name, fh, 0, false, true); 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--; } mysql_mutex_unlock(&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; 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/undo%03zu", srv_undo_dir, i); fh = os_file_create_func( name, OS_FILE_OPEN_RETRY_SILENT, OS_FILE_NORMAL, OS_DATA_FILE, srv_read_only_mode, &ret); if (ret) { os_file_close_func(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_func(logfilename.c_str(), OS_FILE_OPEN_RETRY_SILENT, OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode, &ret); if (ret) { os_file_close_func(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_undo, uint32_t 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). */ uint32_t prev_id= create_new_undo ? srv_undo_space_id_start - 1 : 0; for (uint32_t i= 0; i < n_undo; ++i) { char name[OS_FILE_MAX_PATH]; snprintf(name, sizeof name, "%s/undo%03u", srv_undo_dir, i + 1); uint32_t space_id= srv_undo_tablespace_open(create_new_undo, name, i); switch (space_id) { case ~0U: return DB_CORRUPTION; case 0: if (!create_new_undo) goto unused_undo; sql_print_error("InnoDB: Unable to open create tablespace '%s'.", name); return DB_ERROR; default: /* 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. */ unused_undo: for (uint32_t 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/undo%03u", srv_undo_dir, i); uint32_t space_id= srv_undo_tablespace_open(create_new_undo, name, i); if (!space_id || space_id == ~0U) break; if (0 == srv_undo_tablespaces_open++) srv_undo_space_id_start= space_id; } return DB_SUCCESS; } /** Open the configured number of dedicated undo tablespaces. @param[in] create_new_undo whether the undo tablespaces has to be created @param[in,out] mtr mini-transaction @return DB_SUCCESS or error code */ dberr_t srv_undo_tablespaces_init(bool create_new_undo, mtr_t *mtr) { srv_undo_tablespaces_open= 0; ut_ad(!create_new_undo || mtr); ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS); ut_a(!create_new_undo || srv_operation <= SRV_OPERATION_EXPORT_RESTORED); 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_undo) { 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/undo%03zu", srv_undo_dir, 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; uint32_t n_undo= (create_new_undo || 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_undo, 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_undo) { for (uint32_t i= 0; i < srv_undo_tablespaces; ++i) { dberr_t err= fsp_header_init(fil_space_get(srv_undo_space_id_start + i), SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, mtr); if (err) return err; } } 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); 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)) != 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); err = fsp_header_init(fil_system.temp_space, srv_tmp_space.get_sum_of_sizes(), &mtr); mtr.commit(); if (err == DB_SUCCESS) { err = trx_temp_rseg_create(&mtr); } } 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_master_timer.reset(); srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; if (purge_sys.enabled()) { srv_purge_shutdown(); } if (srv_n_fil_crypt_threads) { fil_crypt_set_thread_cnt(0); } } /** Shut down background threads that can generate undo log. */ static 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(); srv_undo_sources= false; } } #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 if (srv_operation == SRV_OPERATION_NORMAL) { 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. @return lsn upto which data pages have been flushed. */ static lsn_t srv_prepare_to_delete_redo_log_file() { DBUG_ENTER("srv_prepare_to_delete_redo_log_file"); ut_ad(recv_sys.recovery_on); /* Clean the buffer pool. */ buf_flush_sync(); DBUG_EXECUTE_IF("innodb_log_abort_1", DBUG_RETURN(0);); DBUG_PRINT("ib_log", ("After innodb_log_abort_1")); log_sys.latch.wr_lock(SRW_LOCK_CALL); const bool latest_format{log_sys.is_latest()}; lsn_t flushed_lsn{log_sys.get_lsn()}; if (latest_format && !(log_sys.file_size & 4095) && flushed_lsn != log_sys.next_checkpoint_lsn + (log_sys.is_encrypted() ? SIZE_OF_FILE_CHECKPOINT + 8 : SIZE_OF_FILE_CHECKPOINT)) { fil_names_clear(flushed_lsn); flushed_lsn= log_sys.get_lsn(); } { const char *msg; if (!latest_format) { msg= "Upgrading redo log: "; same_size: ib::info() << msg << ib::bytes_iec(srv_log_file_size) << "; LSN=" << flushed_lsn; } else if (srv_log_file_size == log_sys.file_size) { 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 " << ib::bytes_iec{log_sys.file_size} << " to " << ib::bytes_iec{srv_log_file_size} << "; LSN=" << flushed_lsn; } } log_sys.latch.wr_unlock(); log_write_up_to(flushed_lsn, false); ut_ad(flushed_lsn == log_sys.get_lsn()); ut_ad(!os_aio_pending_reads()); ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); ut_ad(!buf_pool.get_oldest_modification(0)); ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); ut_d(os_aio_wait_until_no_pending_writes(false)); DBUG_RETURN(flushed_lsn); } static tpool::task_group rollback_all_recovered_group(1); static tpool::task rollback_all_recovered_task(trx_rollback_all_recovered, nullptr, &rollback_all_recovered_group); /** 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) { dberr_t err = DB_SUCCESS; mtr_t mtr; ut_ad(srv_operation <= SRV_OPERATION_RESTORE_EXPORT || 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; } } if (srv_read_only_mode) { sql_print_information("InnoDB: Started in read only mode"); srv_use_doublewrite_buf = false; } high_level_read_only = srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN || 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() << "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 MariaDB 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(UT_ARR_SIZE(srv_stages))); srv_boot(); ib::info() << my_crc32c_implementation(); if (!srv_read_only_mode) { mysql_mutex_init(srv_monitor_file_mutex_key, &srv_monitor_file_mutex, nullptr); mysql_mutex_init(srv_misc_tmpfile_mutex_key, &srv_misc_tmpfile_mutex, nullptr); } if (!srv_read_only_mode) { if (srv_innodb_status) { srv_monitor_file_name = static_cast( 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 (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; } } 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)); } if (srv_read_only_mode) { ib::info() << "Disabling background log and ibuf IO write" << " 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 #ifdef HAVE_URING if (srv_use_native_aio) { ib::info() << "Using liburing"; } #endif fil_system.create(srv_file_per_table ? 50000 : 5000); ib::info() << "Initializing buffer pool, total size = " << ib::bytes_iec{srv_buf_pool_size} << ", chunk size = " << ib::bytes_iec{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 (" << ib::bytes_iec{srv_buf_pool_size} << "), the flst_validate() debug function can cause a" << " deadlock if the buffer pool fills up."; } #endif /* UNIV_DEBUG */ if (!log_sys.create()) { return srv_init_abort(DB_ERROR); } recv_sys.create(); lock_sys.create(srv_lock_table_size); srv_startup_is_before_trx_rollback_phase = true; if (!srv_read_only_mode) { buf_flush_page_cleaner_init(); ut_ad(buf_page_cleaner_is_active); } if (innodb_encrypt_temporary_tables && !log_crypt_init()) { return srv_init_abort(DB_ERROR); } /* 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(); } else { err = recv_recovery_read_checkpoint(); if (err != DB_SUCCESS) { return srv_init_abort(err); } } /* 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); 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 be flagged by Datafile::validate_first_page() */ return srv_init_abort(err); } if (create_new_db) { lsn_t flushed_lsn = log_sys.init_lsn(); err = create_log_file(true, flushed_lsn); if (err != DB_SUCCESS) { for (const Datafile &file: srv_sys_space) { os_file_delete(innodb_data_file_key, file.filepath()); } return srv_init_abort(err); } srv_undo_space_id_start = 1; } /* Open data files in the system tablespace: we keep them open until database shutdown */ ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug); if (fil_system.sys_space->open(create_new_db)) { mtr_t mtr; mtr.start(); err= srv_undo_tablespaces_init(create_new_db, &mtr); mtr.commit(); } else { err= 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); ut_a(fsp_header_init(fil_system.sys_space, uint32_t(sum_of_new_sizes), &mtr) == DB_SUCCESS); ulint ibuf_root = btr_create( DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space, DICT_IBUF_ID_MIN, nullptr, &mtr, &err); mtr_commit(&mtr); if (ibuf_root == FIL_NULL) { return srv_init_abort(err); } 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. */ err = trx_sys_create_sys_pages(&mtr); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } err = dict_create(); if (err != DB_SUCCESS) { return(srv_init_abort(err)); } buf_flush_sync(); ut_ad(!srv_log_file_created); ut_d(srv_log_file_created= true); if (log_sys.resize_rename()) { return(srv_init_abort(DB_ERROR)); } } 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 = recv_recovery_from_checkpoint_start(); 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_EXPORT_RESTORED: 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(); if (srv_operation != SRV_OPERATION_RESTORE) { dict_sys.load_sys_tables(); } 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: case SRV_OPERATION_BACKUP_NO_DEFER: 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(). Since it may generate huge batch of threadpool tasks, for read io task group, scale down thread creation rate by temporarily restricting tpool concurrency. */ srv_thread_pool->set_concurrency(srv_n_read_io_threads); mysql_mutex_lock(&recv_sys.mutex); recv_sys.apply(true); mysql_mutex_unlock(&recv_sys.mutex); srv_thread_pool->set_concurrency(); if (recv_sys.is_corrupt_log() || recv_sys.is_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_operation > SRV_OPERATION_EXPORT_RESTORED) { ut_ad(srv_operation == SRV_OPERATION_RESTORE_EXPORT || srv_operation == SRV_OPERATION_RESTORE); 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 (log_sys.file_size == srv_log_file_size && log_sys.format == (srv_encrypt_log ? log_t::FORMAT_ENC_10_8 : log_t::FORMAT_10_8)) { /* No need to add or remove encryption, upgrade, or resize. */ delete_log_files(); } else { /* Prepare to delete the old redo log file */ const lsn_t lsn{srv_prepare_to_delete_redo_log_file()}; 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); ut_ad(!os_aio_pending_reads()); ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex)); ut_ad(!buf_pool.get_oldest_modification(0)); ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); /* os_aio_pending_writes() may hold here if some write_io_callback() did not release the slot yet. However, the page write itself must have completed, because the buf_pool.flush_list is empty. In debug builds, we wait for this to happen, hoping to get a hung process if this assumption does not hold. */ ut_d(os_aio_wait_until_no_pending_writes(false)); /* Close the redo log file, so that we can replace it */ log_sys.close_file(); DBUG_EXECUTE_IF("innodb_log_abort_5", return(srv_init_abort(DB_ERROR));); DBUG_PRINT("ib_log", ("After innodb_log_abort_5")); err = create_log_file(false, lsn); if (err == DB_SUCCESS && log_sys.resize_rename()) { err = DB_ERROR; } if (err != DB_SUCCESS) { return(srv_init_abort(err)); } } recv_sys.debug_free(); if (!srv_read_only_mode) { const uint32_t flags = FSP_FLAGS_PAGE_SSIZE(); for (uint32_t id = srv_undo_space_id_start; 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); buf_block_t* block = buf_page_get( page_id_t(0, 0), 0, RW_SX_LATCH, &mtr); /* The first page of the system tablespace should already have been successfully accessed earlier during startup. */ ut_a(block); ulint size = mach_read_from_4( FSP_HEADER_OFFSET + FSP_SIZE + block->page.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->page.frame, size); fil_system.sys_space->size_in_header = uint32_t(size); mtr.commit(); log_write_up_to(mtr.commit_lsn(), true); } } #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->page.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)); } } } 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)); } /* Recreate the undo tablespaces */ if (!high_level_read_only) { err = srv_undo_tablespaces_reinitialize(); if (err) { return srv_init_abort(err); } } srv_undo_tablespaces = srv_undo_tablespaces_open; /* 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. 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_UNDO_LOG_SCAN); /* 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); if (UNIV_UNLIKELY(!block)) { corrupted_old_page: mtr.commit(); return srv_init_abort(DB_CORRUPTION); } 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); if (UNIV_UNLIKELY(!block)) { goto corrupted_old_page; } 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); if (UNIV_UNLIKELY(!block)) { goto corrupted_old_page; } 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); if (UNIV_UNLIKELY(!block)) { goto corrupted_old_page; } 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); } if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { /* The following call is necessary for the change buffer to work with multiple tablespaces. We must know the mapping between space id's and .ibd file names. We also determine the maximum tablespace id used. */ dict_check_tablespaces_and_store_max_id(nullptr); } if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO && !srv_read_only_mode) { /* Drop partially created indexes. */ row_merge_drop_temp_indexes(); /* Rollback incomplete non-DDL transactions */ trx_rollback_is_active = true; srv_thread_pool->submit_task(&rollback_all_recovered_task); } } srv_startup_is_before_trx_rollback_phase = false; if (!srv_read_only_mode) { 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(); } } err = dict_sys.create_or_check_sys_tables(); 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_EXPORT_RESTORED) { /* 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)); } if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) { srv_start_periodic_timer(srv_master_timer, srv_master_callback, 1000); } } srv_is_being_started = false; if (srv_print_verbose_log) { sql_print_information("InnoDB: " "log sequence number " LSN_PF #ifdef HAVE_PMEM "%s" #endif "; transaction id " TRX_ID_FMT, recv_sys.lsn, #ifdef HAVE_PMEM log_sys.is_pmem() ? " (memory-mapped)" : "", #endif 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_cond. */ fil_crypt_threads_init(); /* Initialize online defragmentation. */ btr_defragment_init(); srv_started_redo = true; } return(DB_SUCCESS); } /** 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_EXPORT_RESTORED) { /* Because a slow shutdown must empty the change buffer, we had better prevent any further changes from being buffered. */ innodb_change_buffering= 0; if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO && srv_was_started) while (trx_sys.any_active_transactions()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } 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: case SRV_OPERATION_BACKUP_NO_DEFER: 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: case SRV_OPERATION_EXPORT_RESTORED: /* 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_created() || !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 || !innodb_change_buffering || !srv_was_started || srv_force_recovery >= SRV_FORCE_NO_DDL_UNDO); 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) { mysql_mutex_destroy(&srv_monitor_file_mutex); mysql_mutex_destroy(&srv_misc_tmpfile_mutex); } dict_sys.close(); btr_search_sys_free(); srv_free(); fil_system.close(); pars_lexer_close(); recv_sys.close(); ut_ad(buf_pool.is_initialised() || !srv_was_started); buf_pool.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(); if (srv_stats.pages_page_compression_error) ib::warn() << "Page compression errors: " << srv_stats.pages_page_compression_error; 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); const char* data_dir_path = DICT_TF_HAS_DATA_DIR(table->flags) ? table->data_dir_path : nullptr; ut_ad(!DICT_TF_HAS_DATA_DIR(table->flags) || data_dir_path); path = fil_make_filepath(data_dir_path, table->name, CFG, data_dir_path != nullptr); ut_a(path); len = strlen(path); ut_a(max_len >= len); strcpy(filename, path); ut_free(path); }