diff options
Diffstat (limited to 'storage/xtradb/log/log0recv.cc')
-rw-r--r-- | storage/xtradb/log/log0recv.cc | 3817 |
1 files changed, 0 insertions, 3817 deletions
diff --git a/storage/xtradb/log/log0recv.cc b/storage/xtradb/log/log0recv.cc deleted file mode 100644 index 2f43a1a42a8..00000000000 --- a/storage/xtradb/log/log0recv.cc +++ /dev/null @@ -1,3817 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2017, MariaDB Corporation. - -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 log/log0recv.cc -Recovery - -Created 9/20/1997 Heikki Tuuri -*******************************************************/ - -// First include (the generated) my_config.h, to get correct platform defines. -#include "my_config.h" -#include <stdio.h> // Solaris/x86 header file bug - -#include <vector> -#include <my_service_manager.h> - -#include "log0recv.h" - -#ifdef UNIV_NONINL -#include "log0recv.ic" -#endif - -#include "log0crypt.h" - -#include "config.h" -#ifdef HAVE_ALLOCA_H -#include "alloca.h" -#elif defined(HAVE_MALLOC_H) -#include "malloc.h" -#endif - -#include "mem0mem.h" -#include "buf0buf.h" -#include "buf0flu.h" -#include "mtr0mtr.h" -#include "mtr0log.h" -#include "page0cur.h" -#include "page0zip.h" -#include "btr0btr.h" -#include "btr0cur.h" -#include "ibuf0ibuf.h" -#include "trx0undo.h" -#include "trx0rec.h" -#include "fil0fil.h" -#include "fil0crypt.h" -#ifndef UNIV_HOTBACKUP -# include "buf0rea.h" -# include "srv0srv.h" -# include "srv0start.h" -# include "trx0roll.h" -# include "row0merge.h" -# include "sync0sync.h" -#else /* !UNIV_HOTBACKUP */ - - -/** This is set to FALSE if the backup was originally taken with the -mysqlbackup --include regexp option: then we do not want to create tables in -directories which were not included */ -UNIV_INTERN ibool recv_replay_file_ops = TRUE; -#endif /* !UNIV_HOTBACKUP */ - -/** Log records are stored in the hash table in chunks at most of this size; -this must be less than UNIV_PAGE_SIZE as it is stored in the buffer pool */ -#define RECV_DATA_BLOCK_SIZE (MEM_MAX_ALLOC_IN_BUF - sizeof(recv_data_t)) - -/** Read-ahead area in applying log records to file pages */ -#define RECV_READ_AHEAD_AREA 32 - -/** The recovery system */ -UNIV_INTERN recv_sys_t* recv_sys; -/** TRUE when applying redo log records during crash recovery; FALSE -otherwise. Note that this is FALSE while a background thread is -rolling back incomplete transactions. */ -UNIV_INTERN ibool recv_recovery_on; - -#ifndef UNIV_HOTBACKUP -/** TRUE when recv_init_crash_recovery() has been called. */ -UNIV_INTERN ibool recv_needed_recovery; -# ifdef UNIV_DEBUG -/** TRUE if writing to the redo log (mtr_commit) is forbidden. -Protected by log_sys->mutex. */ -UNIV_INTERN ibool recv_no_log_write = FALSE; -# endif /* UNIV_DEBUG */ - -/** TRUE if buf_page_is_corrupted() should check if the log sequence -number (FIL_PAGE_LSN) is in the future. Initially FALSE, and set by -recv_recovery_from_checkpoint_start_func(). */ -UNIV_INTERN ibool recv_lsn_checks_on; - -/** There are two conditions under which we scan the logs, the first -is normal startup and the second is when we do a recovery from an -archive. -This flag is set if we are doing a scan from the last checkpoint during -startup. If we find log entries that were written after the last checkpoint -we know that the server was not cleanly shutdown. We must then initialize -the crash recovery environment before attempting to store these entries in -the log hash table. */ -static ibool recv_log_scan_is_startup_type; - -/** If the following is TRUE, the buffer pool file pages must be invalidated -after recovery and no ibuf operations are allowed; this becomes TRUE if -the log record hash table becomes too full, and log records must be merged -to file pages already before the recovery is finished: in this case no -ibuf operations are allowed, as they could modify the pages read in the -buffer pool before the pages have been recovered to the up-to-date state. - -TRUE means that recovery is running and no operations on the log files -are allowed yet: the variable name is misleading. */ -UNIV_INTERN ibool recv_no_ibuf_operations; -/** TRUE when the redo log is being backed up */ -# define recv_is_making_a_backup FALSE -/** TRUE when recovering from a backed up redo log file */ -# define recv_is_from_backup FALSE -#else /* !UNIV_HOTBACKUP */ -# define recv_needed_recovery FALSE -/** TRUE when the redo log is being backed up */ -UNIV_INTERN ibool recv_is_making_a_backup = FALSE; -/** TRUE when recovering from a backed up redo log file */ -UNIV_INTERN ibool recv_is_from_backup = FALSE; -# define buf_pool_get_curr_size() (5 * 1024 * 1024) -#endif /* !UNIV_HOTBACKUP */ - -/** The type of the previous parsed redo log record */ -static ulint recv_previous_parsed_rec_type; -/** The offset of the previous parsed redo log record */ -static ulint recv_previous_parsed_rec_offset; -/** The 'multi' flag of the previous parsed redo log record */ -static ulint recv_previous_parsed_rec_is_multi; - -/** Maximum page number encountered in the redo log */ -UNIV_INTERN ulint recv_max_parsed_page_no; - -/** This many frames must be left free in the buffer pool when we scan -the log and store the scanned log records in the buffer pool: we will -use these free frames to read in pages when we start applying the -log records to the database. -This is the default value. If the actual size of the buffer pool is -larger than 10 MB we'll set this value to 512. */ -UNIV_INTERN ulint recv_n_pool_free_frames; - -/** The maximum lsn we see for a page during the recovery process. If this -is bigger than the lsn we are able to scan up to, that is an indication that -the recovery failed and the database may be corrupt. */ -UNIV_INTERN lsn_t recv_max_page_lsn; - -#ifdef UNIV_PFS_THREAD -UNIV_INTERN mysql_pfs_key_t trx_rollback_clean_thread_key; -#endif /* UNIV_PFS_THREAD */ - -#ifdef UNIV_PFS_MUTEX -UNIV_INTERN mysql_pfs_key_t recv_sys_mutex_key; -#endif /* UNIV_PFS_MUTEX */ - -#ifndef UNIV_HOTBACKUP -# ifdef UNIV_PFS_THREAD -UNIV_INTERN mysql_pfs_key_t recv_writer_thread_key; -# endif /* UNIV_PFS_THREAD */ - -# ifdef UNIV_PFS_MUTEX -UNIV_INTERN mysql_pfs_key_t recv_writer_mutex_key; -# endif /* UNIV_PFS_MUTEX */ - -/** Flag indicating if recv_writer thread is active. */ -static volatile bool recv_writer_thread_active; -UNIV_INTERN os_thread_t recv_writer_thread_handle = 0; -#endif /* !UNIV_HOTBACKUP */ - -/* prototypes */ - -#ifndef UNIV_HOTBACKUP -/*******************************************************//** -Initialize crash recovery environment. Can be called iff -recv_needed_recovery == FALSE. */ -static -void -recv_init_crash_recovery(void); -/*===========================*/ -#endif /* !UNIV_HOTBACKUP */ - -/********************************************************//** -Creates the recovery system. */ -UNIV_INTERN -void -recv_sys_create(void) -/*=================*/ -{ - if (recv_sys != NULL) { - - return; - } - - recv_sys = static_cast<recv_sys_t*>(mem_zalloc(sizeof(*recv_sys))); - - mutex_create(recv_sys_mutex_key, &recv_sys->mutex, SYNC_RECV); - -#ifndef UNIV_HOTBACKUP - mutex_create(recv_writer_mutex_key, &recv_sys->writer_mutex, - SYNC_LEVEL_VARYING); -#endif /* !UNIV_HOTBACKUP */ - - recv_sys->heap = NULL; - recv_sys->addr_hash = NULL; -} - -/********************************************************//** -Release recovery system mutexes. */ -UNIV_INTERN -void -recv_sys_close(void) -/*================*/ -{ - if (recv_sys != NULL) { - if (recv_sys->addr_hash != NULL) { - hash_table_free(recv_sys->addr_hash); - } - - if (recv_sys->heap != NULL) { - mem_heap_free(recv_sys->heap); - } - - if (recv_sys->buf != NULL) { - ut_free(recv_sys->buf); - } - - if (recv_sys->last_block_buf_start != NULL) { - mem_free(recv_sys->last_block_buf_start); - } - -#ifndef UNIV_HOTBACKUP - ut_ad(!recv_writer_thread_active); - mutex_free(&recv_sys->writer_mutex); -#endif /* !UNIV_HOTBACKUP */ - - mutex_free(&recv_sys->mutex); - - mem_free(recv_sys); - recv_sys = NULL; - } -} - -/********************************************************//** -Frees the recovery system memory. */ -UNIV_INTERN -void -recv_sys_mem_free(void) -/*===================*/ -{ - if (recv_sys != NULL) { - if (recv_sys->addr_hash != NULL) { - hash_table_free(recv_sys->addr_hash); - } - - if (recv_sys->heap != NULL) { - mem_heap_free(recv_sys->heap); - } - - if (recv_sys->buf != NULL) { - ut_free(recv_sys->buf); - } - - if (recv_sys->last_block_buf_start != NULL) { - mem_free(recv_sys->last_block_buf_start); - } - - mem_free(recv_sys); - recv_sys = NULL; - } -} - -#ifndef UNIV_HOTBACKUP -/************************************************************ -Reset the state of the recovery system variables. */ -UNIV_INTERN -void -recv_sys_var_init(void) -/*===================*/ -{ - recv_lsn_checks_on = FALSE; - - recv_n_pool_free_frames = 256; - - recv_recovery_on = FALSE; - - recv_needed_recovery = FALSE; - - recv_lsn_checks_on = FALSE; - - recv_log_scan_is_startup_type = FALSE; - - recv_no_ibuf_operations = FALSE; - - recv_previous_parsed_rec_type = 999999; - - recv_previous_parsed_rec_offset = 0; - - recv_previous_parsed_rec_is_multi = 0; - - recv_max_parsed_page_no = 0; - - recv_n_pool_free_frames = 256; - - recv_max_page_lsn = 0; -} - -/******************************************************************//** -recv_writer thread tasked with flushing dirty pages from the buffer -pools. -@return a dummy parameter */ -extern "C" UNIV_INTERN -os_thread_ret_t -DECLARE_THREAD(recv_writer_thread)( -/*===============================*/ - void* arg MY_ATTRIBUTE((unused))) - /*!< in: a dummy parameter required by - os_thread_create */ -{ - my_thread_init(); - ut_ad(!srv_read_only_mode); - -#ifdef UNIV_PFS_THREAD - pfs_register_thread(recv_writer_thread_key); -#endif /* UNIV_PFS_THREAD */ - -#ifdef UNIV_DEBUG_THREAD_CREATION - fprintf(stderr, "InnoDB: recv_writer thread running, id %lu\n", - os_thread_pf(os_thread_get_curr_id())); -#endif /* UNIV_DEBUG_THREAD_CREATION */ - - while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { - - os_thread_sleep(100000); - - mutex_enter(&recv_sys->writer_mutex); - - if (!recv_recovery_on) { - mutex_exit(&recv_sys->writer_mutex); - break; - } - - /* Flush pages from end of LRU if required */ - buf_flush_LRU_tail(); - - mutex_exit(&recv_sys->writer_mutex); - } - - recv_writer_thread_active = false; - - my_thread_end(); - /* We count the number of threads in os_thread_exit(). - A created thread should always use that to exit and not - use return() to exit. */ - os_thread_exit(NULL); - - OS_THREAD_DUMMY_RETURN; -} -#endif /* !UNIV_HOTBACKUP */ - -/************************************************************ -Inits the recovery system for a recovery operation. */ -UNIV_INTERN -void -recv_sys_init( -/*==========*/ - ulint available_memory) /*!< in: available memory in bytes */ -{ - if (recv_sys->heap != NULL) { - - return; - } - -#ifndef UNIV_HOTBACKUP - mutex_enter(&(recv_sys->mutex)); - - recv_sys->heap = mem_heap_create_typed(256, - MEM_HEAP_FOR_RECV_SYS); -#else /* !UNIV_HOTBACKUP */ - recv_sys->heap = mem_heap_create(256); - recv_is_from_backup = TRUE; -#endif /* !UNIV_HOTBACKUP */ - - /* Set appropriate value of recv_n_pool_free_frames. */ - if (buf_pool_get_curr_size() >= (10 * 1024 * 1024)) { - /* Buffer pool of size greater than 10 MB. */ - recv_n_pool_free_frames = 512; - } - - recv_sys->buf = static_cast<byte*>(ut_malloc(RECV_PARSING_BUF_SIZE)); - recv_sys->len = 0; - recv_sys->recovered_offset = 0; - - recv_sys->addr_hash = hash_create(available_memory / 512); - recv_sys->n_addrs = 0; - - recv_sys->apply_log_recs = FALSE; - recv_sys->apply_batch_on = FALSE; - - recv_sys->last_block_buf_start = static_cast<byte*>( - mem_alloc(2 * OS_FILE_LOG_BLOCK_SIZE)); - - recv_sys->last_block = static_cast<byte*>(ut_align( - recv_sys->last_block_buf_start, OS_FILE_LOG_BLOCK_SIZE)); - - recv_sys->found_corrupt_log = FALSE; - recv_sys->progress_time = ut_time(); - - recv_max_page_lsn = 0; - - /* Call the constructor for recv_sys_t::dblwr member */ - new (&recv_sys->dblwr) recv_dblwr_t(); - - mutex_exit(&(recv_sys->mutex)); -} - -/** Empty a fully processed hash table. */ -static -void -recv_sys_empty_hash() -{ - ut_ad(mutex_own(&(recv_sys->mutex))); - ut_a(recv_sys->n_addrs == 0); - - hash_table_free(recv_sys->addr_hash); - mem_heap_empty(recv_sys->heap); - - recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 512); -} - -#ifndef UNIV_HOTBACKUP -# ifndef UNIV_LOG_DEBUG -/********************************************************//** -Frees the recovery system. */ -static -void -recv_sys_debug_free(void) -/*=====================*/ -{ - mutex_enter(&(recv_sys->mutex)); - - hash_table_free(recv_sys->addr_hash); - mem_heap_free(recv_sys->heap); - ut_free(recv_sys->buf); - mem_free(recv_sys->last_block_buf_start); - - recv_sys->buf = NULL; - recv_sys->heap = NULL; - recv_sys->addr_hash = NULL; - recv_sys->last_block_buf_start = NULL; - - mutex_exit(&(recv_sys->mutex)); -} -# endif /* UNIV_LOG_DEBUG */ - -# ifdef UNIV_LOG_ARCHIVE -/********************************************************//** -Truncates possible corrupted or extra records from a log group. */ -static -void -recv_truncate_group( -/*================*/ - log_group_t* group, /*!< in: log group */ - lsn_t recovered_lsn, /*!< in: recovery succeeded up to this - lsn */ - lsn_t limit_lsn, /*!< in: this was the limit for - recovery */ - lsn_t checkpoint_lsn, /*!< in: recovery was started from this - checkpoint */ - lsn_t archived_lsn) /*!< in: the log has been archived up to - this lsn */ -{ - lsn_t start_lsn; - lsn_t end_lsn; - lsn_t finish_lsn1; - lsn_t finish_lsn2; - lsn_t finish_lsn; - - if (archived_lsn == LSN_MAX) { - /* Checkpoint was taken in the NOARCHIVELOG mode */ - archived_lsn = checkpoint_lsn; - } - - finish_lsn1 = ut_uint64_align_down(archived_lsn, - OS_FILE_LOG_BLOCK_SIZE) - + log_group_get_capacity(group); - - finish_lsn2 = ut_uint64_align_up(recovered_lsn, - OS_FILE_LOG_BLOCK_SIZE) - + recv_sys->last_log_buf_size; - - if (limit_lsn != LSN_MAX) { - /* We do not know how far we should erase log records: erase - as much as possible */ - - finish_lsn = finish_lsn1; - } else { - /* It is enough to erase the length of the log buffer */ - finish_lsn = finish_lsn1 < finish_lsn2 - ? finish_lsn1 : finish_lsn2; - } - - ut_a(RECV_SCAN_SIZE <= log_sys->buf_size); - - memset(log_sys->buf, 0, RECV_SCAN_SIZE); - - start_lsn = ut_uint64_align_down(recovered_lsn, - OS_FILE_LOG_BLOCK_SIZE); - - if (start_lsn != recovered_lsn) { - /* Copy the last incomplete log block to the log buffer and - edit its data length: */ - lsn_t diff = recovered_lsn - start_lsn; - - ut_a(diff <= 0xFFFFUL); - - ut_memcpy(log_sys->buf, recv_sys->last_block, - OS_FILE_LOG_BLOCK_SIZE); - log_block_set_data_len(log_sys->buf, (ulint) diff); - } - - if (start_lsn >= finish_lsn) { - - return; - } - - for (;;) { - ulint len; - - end_lsn = start_lsn + RECV_SCAN_SIZE; - - if (end_lsn > finish_lsn) { - - end_lsn = finish_lsn; - } - - len = (ulint) (end_lsn - start_lsn); - - log_group_write_buf(group, log_sys->buf, len, start_lsn, 0); - if (end_lsn >= finish_lsn) { - - return; - } - - memset(log_sys->buf, 0, RECV_SCAN_SIZE); - - start_lsn = end_lsn; - } -} - -/********************************************************//** -Copies the log segment between group->recovered_lsn and recovered_lsn from the -most up-to-date log group to group, so that it contains the latest log data. */ -static -void -recv_copy_group( -/*============*/ - log_group_t* up_to_date_group, /*!< in: the most up-to-date log - group */ - log_group_t* group, /*!< in: copy to this log - group */ - lsn_t recovered_lsn) /*!< in: recovery succeeded up - to this lsn */ -{ - lsn_t start_lsn; - lsn_t end_lsn; - - if (group->scanned_lsn >= recovered_lsn) { - - return; - } - - ut_a(RECV_SCAN_SIZE <= log_sys->buf_size); - - start_lsn = ut_uint64_align_down(group->scanned_lsn, - OS_FILE_LOG_BLOCK_SIZE); - for (;;) { - ulint len; - - end_lsn = start_lsn + RECV_SCAN_SIZE; - - if (end_lsn > recovered_lsn) { - end_lsn = ut_uint64_align_up(recovered_lsn, - OS_FILE_LOG_BLOCK_SIZE); - } - - log_group_read_log_seg(LOG_RECOVER, log_sys->buf, - up_to_date_group, start_lsn, end_lsn, - FALSE); - - len = (ulint) (end_lsn - start_lsn); - - log_group_write_buf(group, log_sys->buf, len, start_lsn, 0); - - if (end_lsn >= recovered_lsn) { - - return; - } - - start_lsn = end_lsn; - } -} -# endif /* UNIV_LOG_ARCHIVE */ - -/********************************************************//** -Copies a log segment from the most up-to-date log group to the other log -groups, so that they all contain the latest log data. Also writes the info -about the latest checkpoint to the groups, and inits the fields in the group -memory structs to up-to-date values. */ -static -void -recv_synchronize_groups( -/*====================*/ -#ifdef UNIV_LOG_ARCHIVE - log_group_t* up_to_date_group /*!< in: the most up-to-date - log group */ -#endif - ) -{ - lsn_t start_lsn; - lsn_t end_lsn; - lsn_t recovered_lsn; - - recovered_lsn = recv_sys->recovered_lsn; - - /* Read the last recovered log block to the recovery system buffer: - the block is always incomplete */ - - start_lsn = ut_uint64_align_down(recovered_lsn, - OS_FILE_LOG_BLOCK_SIZE); - end_lsn = ut_uint64_align_up(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE); - - ut_a(start_lsn != end_lsn); - - log_group_read_log_seg(LOG_RECOVER, recv_sys->last_block, -#ifdef UNIV_LOG_ARCHIVE - up_to_date_group, -#else /* UNIV_LOG_ARCHIVE */ - UT_LIST_GET_FIRST(log_sys->log_groups), -#endif /* UNIV_LOG_ARCHIVE */ - start_lsn, end_lsn, FALSE); - - for (log_group_t* group = UT_LIST_GET_FIRST(log_sys->log_groups); - group; - group = UT_LIST_GET_NEXT(log_groups, group)) { -#ifdef UNIV_LOG_ARCHIVE - if (group != up_to_date_group) { - - /* Copy log data if needed */ - - recv_copy_group(group, up_to_date_group, - recovered_lsn); - } -#endif /* UNIV_LOG_ARCHIVE */ - /* Update the fields in the group struct to correspond to - recovered_lsn */ - - log_group_set_fields(group, recovered_lsn); - ut_a(log_sys); - - } - /* Copy the checkpoint info to the groups; remember that we have - incremented checkpoint_no by one, and the info will not be written - over the max checkpoint info, thus making the preservation of max - checkpoint info on disk certain */ - - log_groups_write_checkpoint_info(); - - mutex_exit(&(log_sys->mutex)); - - /* Wait for the checkpoint write to complete */ - rw_lock_s_lock(&(log_sys->checkpoint_lock)); - rw_lock_s_unlock(&(log_sys->checkpoint_lock)); - - mutex_enter(&(log_sys->mutex)); -} -#endif /* !UNIV_HOTBACKUP */ - -/***********************************************************************//** -Checks the consistency of the checkpoint info -@return TRUE if ok */ -ibool -recv_check_cp_is_consistent( -/*========================*/ - const byte* buf) /*!< in: buffer containing checkpoint info */ -{ - ulint fold; - - fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1); - - if ((fold & 0xFFFFFFFFUL) != mach_read_from_4( - buf + LOG_CHECKPOINT_CHECKSUM_1)) { - return(FALSE); - } - - fold = ut_fold_binary(buf + LOG_CHECKPOINT_LSN, - LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN); - - if ((fold & 0xFFFFFFFFUL) != mach_read_from_4( - buf + LOG_CHECKPOINT_CHECKSUM_2)) { - return(FALSE); - } - - return(TRUE); -} - -#ifndef UNIV_HOTBACKUP -/********************************************************//** -Looks for the maximum consistent checkpoint from the log groups. -@return error code or DB_SUCCESS */ -MY_ATTRIBUTE((nonnull, warn_unused_result)) -dberr_t -recv_find_max_checkpoint( -/*=====================*/ - log_group_t** max_group, /*!< out: max group */ - ulint* max_field) /*!< out: LOG_CHECKPOINT_1 or - LOG_CHECKPOINT_2 */ -{ - log_group_t* group; - ib_uint64_t max_no; - ib_uint64_t checkpoint_no; - ulint field; - byte* buf; - - group = UT_LIST_GET_FIRST(log_sys->log_groups); - - max_no = 0; - *max_group = NULL; - *max_field = 0; - - buf = log_sys->checkpoint_buf; - - while (group) { - - ulint log_hdr_log_block_size; - - group->state = LOG_GROUP_CORRUPTED; - - /* Assert that we can reuse log_sys->checkpoint_buf to read the - part of the header that contains the log block size. */ - ut_ad(LOG_FILE_OS_FILE_LOG_BLOCK_SIZE + 4 - < OS_FILE_LOG_BLOCK_SIZE); - - fil_io(OS_FILE_READ | OS_FILE_LOG, true, group->space_id, 0, - 0, 0, OS_FILE_LOG_BLOCK_SIZE, - log_sys->checkpoint_buf, NULL, NULL); - log_hdr_log_block_size - = mach_read_from_4(log_sys->checkpoint_buf - + LOG_FILE_OS_FILE_LOG_BLOCK_SIZE); - if (log_hdr_log_block_size == 0) { - /* 0 means default value */ - log_hdr_log_block_size = 512; - } - if (UNIV_UNLIKELY(log_hdr_log_block_size - != srv_log_block_size)) { - fprintf(stderr, - "InnoDB: Error: The block size of ib_logfile " - "%lu is not equal to innodb_log_block_size " - "%lu.\n" - "InnoDB: Error: Suggestion - Recreate log " - "files.\n", - log_hdr_log_block_size, srv_log_block_size); - return(DB_ERROR); - } - - for (field = LOG_CHECKPOINT_1; field <= LOG_CHECKPOINT_2; - field += LOG_CHECKPOINT_2 - LOG_CHECKPOINT_1) { - - log_group_read_checkpoint_info(group, field); - - if (!recv_check_cp_is_consistent(buf)) { -#ifdef UNIV_DEBUG - if (log_debug_writes) { - fprintf(stderr, - "InnoDB: Checkpoint in group" - " %lu at %lu invalid, %lu\n", - (ulong) group->id, - (ulong) field, - (ulong) mach_read_from_4( - buf - + LOG_CHECKPOINT_CHECKSUM_1)); - - } -#endif /* UNIV_DEBUG */ - goto not_consistent; - } - - group->state = LOG_GROUP_OK; - - group->lsn = mach_read_from_8( - buf + LOG_CHECKPOINT_LSN); - group->lsn_offset = mach_read_from_4( - buf + LOG_CHECKPOINT_OFFSET_LOW32); - group->lsn_offset |= ((lsn_t) mach_read_from_4( - buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32; - checkpoint_no = mach_read_from_8( - buf + LOG_CHECKPOINT_NO); - - if (!log_crypt_read_checkpoint_buf(buf)) { - return DB_ERROR; - } - -#ifdef UNIV_DEBUG - if (log_debug_writes) { - fprintf(stderr, - "InnoDB: Checkpoint number %lu" - " found in group %lu\n", - (ulong) checkpoint_no, - (ulong) group->id); - } -#endif /* UNIV_DEBUG */ - - if (checkpoint_no >= max_no) { - *max_group = group; - *max_field = field; - max_no = checkpoint_no; - } - -not_consistent: - ; - } - - group = UT_LIST_GET_NEXT(log_groups, group); - } - - if (*max_group == NULL) { - - fprintf(stderr, - "InnoDB: No valid checkpoint found.\n" - "InnoDB: A downgrade from MariaDB 10.2.2" - " or later is not supported.\n" - "InnoDB: If this error appears when you are" - " creating an InnoDB database,\n" - "InnoDB: the problem may be that during" - " an earlier attempt you managed\n" - "InnoDB: to create the InnoDB data files," - " but log file creation failed.\n" - "InnoDB: If that is the case, please refer to\n" - "InnoDB: " REFMAN "error-creating-innodb.html\n"); - return(DB_ERROR); - } - - return(DB_SUCCESS); -} -#else /* !UNIV_HOTBACKUP */ -/*******************************************************************//** -Reads the checkpoint info needed in hot backup. -@return TRUE if success */ -UNIV_INTERN -ibool -recv_read_checkpoint_info_for_backup( -/*=================================*/ - const byte* hdr, /*!< in: buffer containing the log group - header */ - lsn_t* lsn, /*!< out: checkpoint lsn */ - lsn_t* offset, /*!< out: checkpoint offset in the log group */ - lsn_t* cp_no, /*!< out: checkpoint number */ - lsn_t* first_header_lsn) - /*!< out: lsn of of the start of the - first log file */ -{ - ulint max_cp = 0; - ib_uint64_t max_cp_no = 0; - const byte* cp_buf; - - cp_buf = hdr + LOG_CHECKPOINT_1; - - if (recv_check_cp_is_consistent(cp_buf)) { - max_cp_no = mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO); - max_cp = LOG_CHECKPOINT_1; - } - - cp_buf = hdr + LOG_CHECKPOINT_2; - - if (recv_check_cp_is_consistent(cp_buf)) { - if (mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO) > max_cp_no) { - max_cp = LOG_CHECKPOINT_2; - } - } - - if (max_cp == 0) { - return(FALSE); - } - - cp_buf = hdr + max_cp; - - *lsn = mach_read_from_8(cp_buf + LOG_CHECKPOINT_LSN); - *offset = mach_read_from_4( - cp_buf + LOG_CHECKPOINT_OFFSET_LOW32); - *offset |= ((lsn_t) mach_read_from_4( - cp_buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32; - - *cp_no = mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO); - - *first_header_lsn = mach_read_from_8(hdr + LOG_FILE_START_LSN); - - return(TRUE); -} -#endif /* !UNIV_HOTBACKUP */ - -/******************************************************//** -Checks the 4-byte checksum to the trailer checksum field of a log -block. We also accept a log block in the old format before -InnoDB-3.23.52 where the checksum field contains the log block number. -@return TRUE if ok, or if the log block may be in the format of InnoDB -version predating 3.23.52 */ -UNIV_INTERN -ibool -log_block_checksum_is_ok_or_old_format( -/*===================================*/ - const byte* block, /*!< in: pointer to a log block */ - bool print_err) /*!< in print if error found */ -{ -#ifdef UNIV_LOG_DEBUG - return(TRUE); -#endif /* UNIV_LOG_DEBUG */ - - ulint block_checksum = log_block_get_checksum(block); - - if (UNIV_LIKELY(srv_log_checksum_algorithm == - SRV_CHECKSUM_ALGORITHM_NONE || - log_block_calc_checksum(block) == block_checksum)) { - - return(TRUE); - } - - if (srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32 || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_NONE) { - - const char* algo = NULL; - - ib_logf(IB_LOG_LEVEL_ERROR, - "log block checksum mismatch: expected " ULINTPF ", " - "calculated checksum " ULINTPF, - block_checksum, - log_block_calc_checksum(block)); - - if (block_checksum == LOG_NO_CHECKSUM_MAGIC) { - - algo = "none"; - } else if (block_checksum == - log_block_calc_checksum_crc32(block)) { - - algo = "crc32"; - } else if (block_checksum == - log_block_calc_checksum_innodb(block)) { - - algo = "innodb"; - } - - if (algo) { - - const char* current_algo; - - current_algo = buf_checksum_algorithm_name( - (srv_checksum_algorithm_t) - srv_log_checksum_algorithm); - - ib_logf(IB_LOG_LEVEL_ERROR, - "current InnoDB log checksum type: %s, " - "detected log checksum type: %s", - current_algo, - algo); - } - - ib_logf(IB_LOG_LEVEL_FATAL, - "STRICT method was specified for innodb_log_checksum, " - "so we intentionally assert here."); - } - - ut_ad(srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_CRC32 || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_INNODB); - - if (block_checksum == LOG_NO_CHECKSUM_MAGIC || - block_checksum == log_block_calc_checksum_crc32(block) || - block_checksum == log_block_calc_checksum_innodb(block)) { - - return(TRUE); - } - - if (log_block_get_hdr_no(block) == block_checksum) { - - /* We assume the log block is in the format of - InnoDB version < 3.23.52 and the block is ok */ -#if 0 - fprintf(stderr, - "InnoDB: Scanned old format < InnoDB-3.23.52" - " log block number %lu\n", - log_block_get_hdr_no(block)); -#endif - return(TRUE); - } - - if (print_err) { - fprintf(stderr, "BROKEN: block: %lu checkpoint: %lu %.8lx %.8lx\n", - log_block_get_hdr_no(block), - log_block_get_checkpoint_no(block), - log_block_calc_checksum(block), - log_block_get_checksum(block)); - } - - return(FALSE); -} - -#ifdef UNIV_HOTBACKUP -/*******************************************************************//** -Scans the log segment and n_bytes_scanned is set to the length of valid -log scanned. */ -UNIV_INTERN -void -recv_scan_log_seg_for_backup( -/*=========================*/ - byte* buf, /*!< in: buffer containing log data */ - ulint buf_len, /*!< in: data length in that buffer */ - lsn_t* scanned_lsn, /*!< in/out: lsn of buffer start, - we return scanned lsn */ - ulint* scanned_checkpoint_no, - /*!< in/out: 4 lowest bytes of the - highest scanned checkpoint number so - far */ - ulint* n_bytes_scanned)/*!< out: how much we were able to - scan, smaller than buf_len if log - data ended here */ -{ - ulint data_len; - byte* log_block; - ulint no; - - *n_bytes_scanned = 0; - - for (log_block = buf; log_block < buf + buf_len; - log_block += OS_FILE_LOG_BLOCK_SIZE) { - - no = log_block_get_hdr_no(log_block); - -#if 0 - fprintf(stderr, "Log block header no %lu\n", no); -#endif - - if (no != log_block_convert_lsn_to_no(*scanned_lsn) - || !log_block_checksum_is_ok_or_old_format(log_block)) { -#if 0 - fprintf(stderr, - "Log block n:o %lu, scanned lsn n:o %lu\n", - no, log_block_convert_lsn_to_no(*scanned_lsn)); -#endif - /* Garbage or an incompletely written log block */ - - log_block += OS_FILE_LOG_BLOCK_SIZE; -#if 0 - fprintf(stderr, - "Next log block n:o %lu\n", - log_block_get_hdr_no(log_block)); -#endif - break; - } - - if (*scanned_checkpoint_no > 0 - && log_block_get_checkpoint_no(log_block) - < *scanned_checkpoint_no - && *scanned_checkpoint_no - - log_block_get_checkpoint_no(log_block) - > 0x80000000UL) { - - /* Garbage from a log buffer flush which was made - before the most recent database recovery */ -#if 0 - fprintf(stderr, - "Scanned cp n:o %lu, block cp n:o %lu\n", - *scanned_checkpoint_no, - log_block_get_checkpoint_no(log_block)); -#endif - break; - } - - data_len = log_block_get_data_len(log_block); - - *scanned_checkpoint_no - = log_block_get_checkpoint_no(log_block); - *scanned_lsn += data_len; - - *n_bytes_scanned += data_len; - - if (data_len < OS_FILE_LOG_BLOCK_SIZE) { - /* Log data ends here */ - -#if 0 - fprintf(stderr, "Log block data len %lu\n", - data_len); -#endif - break; - } - } -} -#endif /* UNIV_HOTBACKUP */ - -/*******************************************************************//** -Tries to parse a single log record body and also applies it to a page if -specified. File ops are parsed, but not applied in this function. -@return log record end, NULL if not a complete record */ -static -byte* -recv_parse_or_apply_log_rec_body( -/*=============================*/ - byte type, /*!< in: type */ - byte* ptr, /*!< in: pointer to a buffer */ - byte* end_ptr,/*!< in: pointer to the buffer end */ - buf_block_t* block, /*!< in/out: buffer block or NULL; if - not NULL, then the log record is - applied to the page, and the log - record should be complete then */ - mtr_t* mtr, /*!< in: mtr or NULL; should be non-NULL - if and only if block is non-NULL */ - ulint space_id) - /*!< in: tablespace id obtained by - parsing initial log record */ -{ - dict_index_t* index = NULL; - page_t* page; - page_zip_des_t* page_zip; -#ifdef UNIV_DEBUG - ulint page_type; -#endif /* UNIV_DEBUG */ - - ut_ad(!block == !mtr); - - if (block) { - page = block->frame; - page_zip = buf_block_get_page_zip(block); - ut_d(page_type = fil_page_get_type(page)); - } else { - page = NULL; - page_zip = NULL; - ut_d(page_type = FIL_PAGE_TYPE_ALLOCATED); - } - - switch (type) { -#ifdef UNIV_LOG_LSN_DEBUG - case MLOG_LSN: - /* The LSN is checked in recv_parse_log_rec(). */ - break; -#endif /* UNIV_LOG_LSN_DEBUG */ - case MLOG_1BYTE: case MLOG_2BYTES: case MLOG_4BYTES: case MLOG_8BYTES: - /* Note that crypt data can be set to empty page */ - ptr = mlog_parse_nbytes(type, ptr, end_ptr, page, page_zip); - break; - case MLOG_REC_INSERT: case MLOG_COMP_REC_INSERT: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_REC_INSERT, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = page_cur_parse_insert_rec(FALSE, ptr, end_ptr, - block, index, mtr); - } - break; - case MLOG_REC_CLUST_DELETE_MARK: case MLOG_COMP_REC_CLUST_DELETE_MARK: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_REC_CLUST_DELETE_MARK, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = btr_cur_parse_del_mark_set_clust_rec( - ptr, end_ptr, page, page_zip, index); - } - break; - case MLOG_COMP_REC_SEC_DELETE_MARK: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - /* This log record type is obsolete, but we process it for - backward compatibility with MySQL 5.0.3 and 5.0.4. */ - ut_a(!page || page_is_comp(page)); - ut_a(!page_zip); - ptr = mlog_parse_index(ptr, end_ptr, TRUE, &index); - if (!ptr) { - break; - } - /* Fall through */ - case MLOG_REC_SEC_DELETE_MARK: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - ptr = btr_cur_parse_del_mark_set_sec_rec(ptr, end_ptr, - page, page_zip); - break; - case MLOG_REC_UPDATE_IN_PLACE: case MLOG_COMP_REC_UPDATE_IN_PLACE: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_REC_UPDATE_IN_PLACE, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = btr_cur_parse_update_in_place(ptr, end_ptr, page, - page_zip, index); - } - break; - case MLOG_LIST_END_DELETE: case MLOG_COMP_LIST_END_DELETE: - case MLOG_LIST_START_DELETE: case MLOG_COMP_LIST_START_DELETE: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_LIST_END_DELETE - || type == MLOG_COMP_LIST_START_DELETE, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = page_parse_delete_rec_list(type, ptr, end_ptr, - block, index, mtr); - } - break; - case MLOG_LIST_END_COPY_CREATED: case MLOG_COMP_LIST_END_COPY_CREATED: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_LIST_END_COPY_CREATED, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = page_parse_copy_rec_list_to_created_page( - ptr, end_ptr, block, index, mtr); - } - break; - case MLOG_PAGE_REORGANIZE: - case MLOG_COMP_PAGE_REORGANIZE: - case MLOG_ZIP_PAGE_REORGANIZE: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type != MLOG_PAGE_REORGANIZE, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = btr_parse_page_reorganize( - ptr, end_ptr, index, - type == MLOG_ZIP_PAGE_REORGANIZE, - block, mtr); - } - break; - case MLOG_PAGE_CREATE: case MLOG_COMP_PAGE_CREATE: - /* Allow anything in page_type when creating a page. */ - ut_a(!page_zip); - ptr = page_parse_create(ptr, end_ptr, - type == MLOG_COMP_PAGE_CREATE, - block, mtr); - break; - case MLOG_UNDO_INSERT: - ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); - ptr = trx_undo_parse_add_undo_rec(ptr, end_ptr, page); - break; - case MLOG_UNDO_ERASE_END: - ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); - ptr = trx_undo_parse_erase_page_end(ptr, end_ptr, page, mtr); - break; - case MLOG_UNDO_INIT: - /* Allow anything in page_type when creating a page. */ - ptr = trx_undo_parse_page_init(ptr, end_ptr, page, mtr); - break; - case MLOG_UNDO_HDR_DISCARD: - ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); - ptr = trx_undo_parse_discard_latest(ptr, end_ptr, page, mtr); - break; - case MLOG_UNDO_HDR_CREATE: - case MLOG_UNDO_HDR_REUSE: - ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); - ptr = trx_undo_parse_page_header(type, ptr, end_ptr, - page, mtr); - break; - case MLOG_REC_MIN_MARK: case MLOG_COMP_REC_MIN_MARK: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - /* On a compressed page, MLOG_COMP_REC_MIN_MARK - will be followed by MLOG_COMP_REC_DELETE - or MLOG_ZIP_WRITE_HEADER(FIL_PAGE_PREV, FIL_NULL) - in the same mini-transaction. */ - ut_a(type == MLOG_COMP_REC_MIN_MARK || !page_zip); - ptr = btr_parse_set_min_rec_mark( - ptr, end_ptr, type == MLOG_COMP_REC_MIN_MARK, - page, mtr); - break; - case MLOG_REC_DELETE: case MLOG_COMP_REC_DELETE: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, - type == MLOG_COMP_REC_DELETE, - &index))) { - ut_a(!page - || (ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table)); - ptr = page_cur_parse_delete_rec(ptr, end_ptr, - block, index, mtr); - } - break; - case MLOG_IBUF_BITMAP_INIT: - /* Allow anything in page_type when creating a page. */ - ptr = ibuf_parse_bitmap_init(ptr, end_ptr, block, mtr); - break; - case MLOG_INIT_FILE_PAGE: - /* Allow anything in page_type when creating a page. */ - ptr = fsp_parse_init_file_page(ptr, end_ptr, block); - break; - case MLOG_WRITE_STRING: - /* Allow setting crypt_data also for empty page */ - ptr = mlog_parse_string(ptr, end_ptr, page, page_zip); - break; - case MLOG_FILE_RENAME: - /* Do not rerun file-based log entries if this is - IO completion from a page read. */ - if (page == NULL) { - ptr = fil_op_log_parse_or_replay(ptr, end_ptr, type, - (recv_recovery_is_on() - ? space_id : 0), 0); - } - break; - case MLOG_FILE_CREATE: - case MLOG_FILE_DELETE: - case MLOG_FILE_CREATE2: - /* Do not rerun file-based log entries if this is - IO completion from a page read. */ - if (page == NULL) { - ptr = fil_op_log_parse_or_replay(ptr, end_ptr, - type, 0, 0); - } - break; - case MLOG_ZIP_WRITE_NODE_PTR: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - ptr = page_zip_parse_write_node_ptr(ptr, end_ptr, - page, page_zip); - break; - case MLOG_ZIP_WRITE_BLOB_PTR: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - ptr = page_zip_parse_write_blob_ptr(ptr, end_ptr, - page, page_zip); - break; - case MLOG_ZIP_WRITE_HEADER: - ut_ad(!page || page_type == FIL_PAGE_INDEX); - ptr = page_zip_parse_write_header(ptr, end_ptr, - page, page_zip); - break; - case MLOG_ZIP_PAGE_COMPRESS: - /* Allow anything in page_type when creating a page. */ - ptr = page_zip_parse_compress(ptr, end_ptr, - page, page_zip); - break; - case MLOG_ZIP_PAGE_COMPRESS_NO_DATA: - if (NULL != (ptr = mlog_parse_index( - ptr, end_ptr, TRUE, &index))) { - - ut_a(!page || ((ibool)!!page_is_comp(page) - == dict_table_is_comp(index->table))); - ptr = page_zip_parse_compress_no_data( - ptr, end_ptr, page, page_zip, index); - } - break; - case MLOG_FILE_WRITE_CRYPT_DATA: - dberr_t err; - ptr = const_cast<byte*>(fil_parse_write_crypt_data(ptr, end_ptr, block, &err)); - - if (err != DB_SUCCESS) { - recv_sys->found_corrupt_log = TRUE; - } - break; - default: - ptr = NULL; - recv_sys->found_corrupt_log = TRUE; - } - - if (index) { - dict_table_t* table = index->table; - - dict_mem_index_free(index); - dict_mem_table_free(table); - } - - return(ptr); -} - -/*********************************************************************//** -Calculates the fold value of a page file address: used in inserting or -searching for a log record in the hash table. -@return folded value */ -UNIV_INLINE -ulint -recv_fold( -/*======*/ - ulint space, /*!< in: space */ - ulint page_no)/*!< in: page number */ -{ - return(ut_fold_ulint_pair(space, page_no)); -} - -/*********************************************************************//** -Calculates the hash value of a page file address: used in inserting or -searching for a log record in the hash table. -@return folded value */ -UNIV_INLINE -ulint -recv_hash( -/*======*/ - ulint space, /*!< in: space */ - ulint page_no)/*!< in: page number */ -{ - return(hash_calc_hash(recv_fold(space, page_no), recv_sys->addr_hash)); -} - -/*********************************************************************//** -Gets the hashed file address struct for a page. -@return file address struct, NULL if not found from the hash table */ -static -recv_addr_t* -recv_get_fil_addr_struct( -/*=====================*/ - ulint space, /*!< in: space id */ - ulint page_no)/*!< in: page number */ -{ - recv_addr_t* recv_addr; - - for (recv_addr = static_cast<recv_addr_t*>( - HASH_GET_FIRST(recv_sys->addr_hash, - recv_hash(space, page_no))); - recv_addr != 0; - recv_addr = static_cast<recv_addr_t*>( - HASH_GET_NEXT(addr_hash, recv_addr))) { - - if (recv_addr->space == space - && recv_addr->page_no == page_no) { - - return(recv_addr); - } - } - - return(NULL); -} - -/*******************************************************************//** -Adds a new log record to the hash table of log records. */ -static -void -recv_add_to_hash_table( -/*===================*/ - byte type, /*!< in: log record type */ - ulint space, /*!< in: space id */ - ulint page_no, /*!< in: page number */ - byte* body, /*!< in: log record body */ - byte* rec_end, /*!< in: log record end */ - lsn_t start_lsn, /*!< in: start lsn of the mtr */ - lsn_t end_lsn) /*!< in: end lsn of the mtr */ -{ - recv_t* recv; - ulint len; - recv_data_t* recv_data; - recv_data_t** prev_field; - recv_addr_t* recv_addr; - - if (fil_tablespace_deleted_or_being_deleted_in_mem(space, -1)) { - /* The tablespace does not exist any more: do not store the - log record */ - - return; - } - - len = rec_end - body; - - recv = static_cast<recv_t*>( - mem_heap_alloc(recv_sys->heap, sizeof(recv_t))); - - recv->type = type; - recv->len = rec_end - body; - recv->start_lsn = start_lsn; - recv->end_lsn = end_lsn; - - recv_addr = recv_get_fil_addr_struct(space, page_no); - - if (recv_addr == NULL) { - recv_addr = static_cast<recv_addr_t*>( - mem_heap_alloc(recv_sys->heap, sizeof(recv_addr_t))); - - recv_addr->space = space; - recv_addr->page_no = page_no; - recv_addr->state = RECV_NOT_PROCESSED; - - UT_LIST_INIT(recv_addr->rec_list); - - HASH_INSERT(recv_addr_t, addr_hash, recv_sys->addr_hash, - recv_fold(space, page_no), recv_addr); - recv_sys->n_addrs++; -#if 0 - fprintf(stderr, "Inserting log rec for space %lu, page %lu\n", - space, page_no); -#endif - } - - UT_LIST_ADD_LAST(rec_list, recv_addr->rec_list, recv); - - prev_field = &(recv->data); - - /* Store the log record body in chunks of less than UNIV_PAGE_SIZE: - recv_sys->heap grows into the buffer pool, and bigger chunks could not - be allocated */ - - while (rec_end > body) { - - len = rec_end - body; - - if (len > RECV_DATA_BLOCK_SIZE) { - len = RECV_DATA_BLOCK_SIZE; - } - - recv_data = static_cast<recv_data_t*>( - mem_heap_alloc(recv_sys->heap, - sizeof(recv_data_t) + len)); - - *prev_field = recv_data; - - memcpy(recv_data + 1, body, len); - - prev_field = &(recv_data->next); - - body += len; - } - - *prev_field = NULL; -} - -/*********************************************************************//** -Copies the log record body from recv to buf. */ -static -void -recv_data_copy_to_buf( -/*==================*/ - byte* buf, /*!< in: buffer of length at least recv->len */ - recv_t* recv) /*!< in: log record */ -{ - recv_data_t* recv_data; - ulint part_len; - ulint len; - - len = recv->len; - recv_data = recv->data; - - while (len > 0) { - if (len > RECV_DATA_BLOCK_SIZE) { - part_len = RECV_DATA_BLOCK_SIZE; - } else { - part_len = len; - } - - ut_memcpy(buf, ((byte*) recv_data) + sizeof(recv_data_t), - part_len); - buf += part_len; - len -= part_len; - - recv_data = recv_data->next; - } -} - -/************************************************************************//** -Applies the hashed log records to the page, if the page lsn is less than the -lsn of a log record. This can be called when a buffer page has just been -read in, or also for a page already in the buffer pool. */ -UNIV_INTERN -void -recv_recover_page_func( -/*===================*/ -#ifndef UNIV_HOTBACKUP - ibool just_read_in, - /*!< in: TRUE if the i/o handler calls - this for a freshly read page */ -#endif /* !UNIV_HOTBACKUP */ - buf_block_t* block) /*!< in/out: buffer block */ -{ - page_t* page; - page_zip_des_t* page_zip; - recv_addr_t* recv_addr; - recv_t* recv; - byte* buf; - lsn_t start_lsn; - lsn_t end_lsn; - lsn_t page_lsn; - lsn_t page_newest_lsn; - ibool modification_to_page; -#ifndef UNIV_HOTBACKUP - ibool success; -#endif /* !UNIV_HOTBACKUP */ - mtr_t mtr; - - mutex_enter(&(recv_sys->mutex)); - - if (recv_sys->apply_log_recs == FALSE) { - - /* Log records should not be applied now */ - - mutex_exit(&(recv_sys->mutex)); - - return; - } - - recv_addr = recv_get_fil_addr_struct(buf_block_get_space(block), - buf_block_get_page_no(block)); - - if ((recv_addr == NULL) - /* bugfix: http://bugs.mysql.com/bug.php?id=44140 */ - || (recv_addr->state == RECV_BEING_READ && !just_read_in) - || (recv_addr->state == RECV_BEING_PROCESSED) - || (recv_addr->state == RECV_PROCESSED)) { - - mutex_exit(&(recv_sys->mutex)); - - return; - } - -#if 0 - fprintf(stderr, "Recovering space %lu, page %lu\n", - buf_block_get_space(block), buf_block_get_page_no(block)); -#endif - - recv_addr->state = RECV_BEING_PROCESSED; - - mutex_exit(&(recv_sys->mutex)); - - mtr_start(&mtr); - mtr_set_log_mode(&mtr, MTR_LOG_NONE); - - page = block->frame; - page_zip = buf_block_get_page_zip(block); - -#ifndef UNIV_HOTBACKUP - if (just_read_in) { - /* Move the ownership of the x-latch on the page to - this OS thread, so that we can acquire a second - x-latch on it. This is needed for the operations to - the page to pass the debug checks. */ - - rw_lock_x_lock_move_ownership(&block->lock); - } - - success = buf_page_get_known_nowait(RW_X_LATCH, block, - BUF_KEEP_OLD, - __FILE__, __LINE__, - &mtr); - ut_a(success); - - buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); -#endif /* !UNIV_HOTBACKUP */ - - /* Read the newest modification lsn from the page */ - page_lsn = mach_read_from_8(page + FIL_PAGE_LSN); - -#ifndef UNIV_HOTBACKUP - /* It may be that the page has been modified in the buffer - pool: read the newest modification lsn there */ - - page_newest_lsn = buf_page_get_newest_modification(&block->page); - - if (page_newest_lsn) { - - page_lsn = page_newest_lsn; - } -#else /* !UNIV_HOTBACKUP */ - /* In recovery from a backup we do not really use the buffer pool */ - page_newest_lsn = 0; -#endif /* !UNIV_HOTBACKUP */ - - modification_to_page = FALSE; - start_lsn = end_lsn = 0; - - recv = UT_LIST_GET_FIRST(recv_addr->rec_list); - - while (recv) { - end_lsn = recv->end_lsn; - - if (recv->len > RECV_DATA_BLOCK_SIZE) { - /* We have to copy the record body to a separate - buffer */ - - buf = static_cast<byte*>(mem_alloc(recv->len)); - - recv_data_copy_to_buf(buf, recv); - } else { - buf = ((byte*)(recv->data)) + sizeof(recv_data_t); - } - - if (recv->type == MLOG_INIT_FILE_PAGE) { - page_lsn = page_newest_lsn; - - memset(FIL_PAGE_LSN + page, 0, 8); - memset(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM - + page, 0, 8); - - if (page_zip) { - memset(FIL_PAGE_LSN + page_zip->data, 0, 8); - } - } - - if (recv->start_lsn >= page_lsn) { - - lsn_t end_lsn; - - if (!modification_to_page) { - - modification_to_page = TRUE; - start_lsn = recv->start_lsn; - } - - DBUG_PRINT("ib_log", - ("apply " LSN_PF ": %u len %u " - "page %u:%u", recv->start_lsn, - (unsigned) recv->type, - (unsigned) recv->len, - (unsigned) recv_addr->space, - (unsigned) recv_addr->page_no)); - - recv_parse_or_apply_log_rec_body(recv->type, buf, - buf + recv->len, - block, &mtr, - recv_addr->space); - - end_lsn = recv->start_lsn + recv->len; - mach_write_to_8(FIL_PAGE_LSN + page, end_lsn); - mach_write_to_8(UNIV_PAGE_SIZE - - FIL_PAGE_END_LSN_OLD_CHKSUM - + page, end_lsn); - - if (page_zip) { - mach_write_to_8(FIL_PAGE_LSN - + page_zip->data, end_lsn); - } - } - - if (recv->len > RECV_DATA_BLOCK_SIZE) { - mem_free(buf); - } - - recv = UT_LIST_GET_NEXT(rec_list, recv); - } - -#ifdef UNIV_ZIP_DEBUG - if (fil_page_get_type(page) == FIL_PAGE_INDEX) { - page_zip_des_t* page_zip = buf_block_get_page_zip(block); - - ut_a(!page_zip - || page_zip_validate_low(page_zip, page, NULL, FALSE)); - } -#endif /* UNIV_ZIP_DEBUG */ - -#ifndef UNIV_HOTBACKUP - if (modification_to_page) { - ut_a(block); - - log_flush_order_mutex_enter(); - buf_flush_recv_note_modification(block, start_lsn, end_lsn); - log_flush_order_mutex_exit(); - } -#endif /* !UNIV_HOTBACKUP */ - - /* Make sure that committing mtr does not change the modification - lsn values of page */ - - mtr.modifications = FALSE; - - mtr_commit(&mtr); - - ib_time_t time = ut_time(); - - mutex_enter(&(recv_sys->mutex)); - - if (recv_max_page_lsn < page_lsn) { - recv_max_page_lsn = page_lsn; - } - - recv_addr->state = RECV_PROCESSED; - - ut_a(recv_sys->n_addrs > 0); - if (ulint n = --recv_sys->n_addrs) { - if (recv_sys->report(time)) { - ib_logf(IB_LOG_LEVEL_INFO, - "To recover: " ULINTPF " pages from log", n); - service_manager_extend_timeout( - INNODB_EXTEND_TIMEOUT_INTERVAL, "To recover: " ULINTPF " pages from log", n); - } - } - - mutex_exit(&recv_sys->mutex); -} - -#ifndef UNIV_HOTBACKUP -/*******************************************************************//** -Reads in pages which have hashed log records, from an area around a given -page number. -@return number of pages found */ -static -ulint -recv_read_in_area( -/*==============*/ - ulint space, /*!< in: space */ - ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ - ulint page_no)/*!< in: page number */ -{ - recv_addr_t* recv_addr; - ulint page_nos[RECV_READ_AHEAD_AREA]; - ulint low_limit; - ulint n; - - low_limit = page_no - (page_no % RECV_READ_AHEAD_AREA); - - n = 0; - - for (page_no = low_limit; page_no < low_limit + RECV_READ_AHEAD_AREA; - page_no++) { - recv_addr = recv_get_fil_addr_struct(space, page_no); - - if (recv_addr && !buf_page_peek(space, page_no)) { - - mutex_enter(&(recv_sys->mutex)); - - if (recv_addr->state == RECV_NOT_PROCESSED) { - recv_addr->state = RECV_BEING_READ; - - page_nos[n] = page_no; - - n++; - } - - mutex_exit(&(recv_sys->mutex)); - } - } - - buf_read_recv_pages(FALSE, space, zip_size, page_nos, n); - return(n); -} - -/** Apply the hash table of stored log records to persistent data pages. -@param[in] last_batch whether the change buffer merge will be - performed as part of the operation */ -UNIV_INTERN -void -recv_apply_hashed_log_recs(bool last_batch) -{ - for (;;) { - mutex_enter(&recv_sys->mutex); - - if (!recv_sys->apply_batch_on) { - break; - } - - if (recv_sys->found_corrupt_log) { - mutex_exit(&recv_sys->mutex); - return; - } - - mutex_exit(&recv_sys->mutex); - os_thread_sleep(500000); - } - - ut_ad(!last_batch == mutex_own(&log_sys->mutex)); - - if (!last_batch) { - recv_no_ibuf_operations = TRUE; - } - - if (ulint n = recv_sys->n_addrs) { - const char* msg = last_batch - ? "Starting final batch to recover " - : "Starting a batch to recover "; - ib_logf(IB_LOG_LEVEL_INFO, - "%s" ULINTPF " pages from redo log", msg, n); - sd_notifyf(0, "STATUS=%s" ULINTPF " pages from redo log", - msg, n); - } - - recv_sys->apply_log_recs = TRUE; - recv_sys->apply_batch_on = TRUE; - - for (ulint i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) { - for (recv_addr_t* recv_addr = static_cast<recv_addr_t*>( - HASH_GET_FIRST(recv_sys->addr_hash, i)); - recv_addr; - recv_addr = static_cast<recv_addr_t*>( - HASH_GET_NEXT(addr_hash, recv_addr))) { - - ulint space = recv_addr->space; - ulint zip_size = fil_space_get_zip_size(space); - ulint page_no = recv_addr->page_no; - - if (recv_addr->state == RECV_NOT_PROCESSED) { - mutex_exit(&recv_sys->mutex); - - if (buf_page_peek(space, page_no)) { - mtr_t mtr; - mtr_start(&mtr); - buf_block_t* block = buf_page_get( - space, zip_size, page_no, - RW_X_LATCH, &mtr); - buf_block_dbg_add_level( - block, SYNC_NO_ORDER_CHECK); - - recv_recover_page(FALSE, block); - mtr_commit(&mtr); - } else { - recv_read_in_area(space, zip_size, - page_no); - } - - mutex_enter(&recv_sys->mutex); - } - } - } - - /* Wait until all the pages have been processed */ - - while (recv_sys->n_addrs != 0) { - - mutex_exit(&(recv_sys->mutex)); - - if (recv_sys->found_corrupt_log) { - return; - } - - os_thread_sleep(500000); - - mutex_enter(&(recv_sys->mutex)); - } - - if (!last_batch) { - bool success; - - /* Flush all the file pages to disk and invalidate them in - the buffer pool */ - - ut_d(recv_no_log_write = TRUE); - mutex_exit(&(recv_sys->mutex)); - mutex_exit(&(log_sys->mutex)); - - /* Stop the recv_writer thread from issuing any LRU - flush batches. */ - mutex_enter(&recv_sys->writer_mutex); - - /* Wait for any currently run batch to end. */ - buf_flush_wait_LRU_batch_end(); - - success = buf_flush_list(ULINT_MAX, LSN_MAX, NULL); - - ut_a(success); - - buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); - - buf_pool_invalidate(); - - /* Allow batches from recv_writer thread. */ - mutex_exit(&recv_sys->writer_mutex); - - mutex_enter(&(log_sys->mutex)); - mutex_enter(&(recv_sys->mutex)); - ut_d(recv_no_log_write = srv_apply_log_only); - - recv_no_ibuf_operations = FALSE; - } - - recv_sys->apply_log_recs = FALSE; - recv_sys->apply_batch_on = FALSE; - - recv_sys_empty_hash(); - - mutex_exit(&recv_sys->mutex); -} -#else /* !UNIV_HOTBACKUP */ -/*******************************************************************//** -Applies log records in the hash table to a backup. */ -UNIV_INTERN -void -recv_apply_log_recs_for_backup(void) -/*================================*/ -{ - recv_addr_t* recv_addr; - ulint n_hash_cells; - buf_block_t* block; - ulint actual_size; - ibool success; - ulint error; - ulint i; - - recv_sys->apply_log_recs = TRUE; - recv_sys->apply_batch_on = TRUE; - - block = back_block1; - - n_hash_cells = hash_get_n_cells(recv_sys->addr_hash); - - for (i = 0; i < n_hash_cells; i++) { - /* The address hash table is externally chained */ - recv_addr = hash_get_nth_cell(recv_sys->addr_hash, i)->node; - - while (recv_addr != NULL) { - - ulint zip_size - = fil_space_get_zip_size(recv_addr->space); - - if (zip_size == ULINT_UNDEFINED) { -#if 0 - fprintf(stderr, - "InnoDB: Warning: cannot apply" - " log record to" - " tablespace %lu page %lu,\n" - "InnoDB: because tablespace with" - " that id does not exist.\n", - recv_addr->space, recv_addr->page_no); -#endif - recv_addr->state = RECV_PROCESSED; - - ut_a(recv_sys->n_addrs); - recv_sys->n_addrs--; - - goto skip_this_recv_addr; - } - - /* We simulate a page read made by the buffer pool, to - make sure the recovery apparatus works ok. We must init - the block. */ - - buf_page_init_for_backup_restore( - recv_addr->space, recv_addr->page_no, - zip_size, block); - - /* Extend the tablespace's last file if the page_no - does not fall inside its bounds; we assume the last - file is auto-extending, and mysqlbackup copied the file - when it still was smaller */ - - success = fil_extend_space_to_desired_size( - &actual_size, - recv_addr->space, recv_addr->page_no + 1); - if (!success) { - fprintf(stderr, - "InnoDB: Fatal error: cannot extend" - " tablespace %u to hold %u pages\n", - recv_addr->space, recv_addr->page_no); - - exit(1); - } - - /* Read the page from the tablespace file using the - fil0fil.cc routines */ - - if (zip_size) { - error = fil_io(OS_FILE_READ, true, - recv_addr->space, zip_size, - recv_addr->page_no, 0, zip_size, - block->page.zip.data, NULL, 0, 0, false); - if (error == DB_SUCCESS - && !buf_zip_decompress(block, TRUE)) { - exit(1); - } - } else { - error = fil_io(OS_FILE_READ, true, - recv_addr->space, 0, - recv_addr->page_no, 0, - UNIV_PAGE_SIZE, - block->frame, NULL, 0, 0, false); - } - - if (error != DB_SUCCESS) { - fprintf(stderr, - "InnoDB: Fatal error: cannot read" - " from tablespace" - " %lu page number %lu\n", - (ulong) recv_addr->space, - (ulong) recv_addr->page_no); - - exit(1); - } - - /* Apply the log records to this page */ - recv_recover_page(FALSE, block); - - /* Write the page back to the tablespace file using the - fil0fil.cc routines */ - - buf_flush_init_for_writing( - block->frame, buf_block_get_page_zip(block), - mach_read_from_8(block->frame + FIL_PAGE_LSN)); - - if (zip_size) { - error = fil_io(OS_FILE_WRITE, true, - recv_addr->space, zip_size, - recv_addr->page_no, 0, - zip_size, - block->page.zip.data, NULL, 0, 0, false); - } else { - error = fil_io(OS_FILE_WRITE, true, - recv_addr->space, 0, - recv_addr->page_no, 0, - UNIV_PAGE_SIZE, - block->frame, NULL, 0, - block->latest_modification, - block->encrypt_later); - } -skip_this_recv_addr: - recv_addr = HASH_GET_NEXT(addr_hash, recv_addr); - } - } - sd_notify(0, "STATUS=InnoDB: Apply batch for backup completed"); - - recv_sys_empty_hash(); -} -#endif /* !UNIV_HOTBACKUP */ - -/*******************************************************************//** -Tries to parse a single log record and returns its length. -@return length of the record, or 0 if the record was not complete */ -UNIV_INTERN -ulint -recv_parse_log_rec( -/*===============*/ - byte* ptr, /*!< in: pointer to a buffer */ - byte* end_ptr,/*!< in: pointer to the buffer end */ - byte* type, /*!< out: type */ - ulint* space, /*!< out: space id */ - ulint* page_no,/*!< out: page number */ - byte** body) /*!< out: log record body start */ -{ - byte* new_ptr; - - *body = NULL; - - if (ptr == end_ptr) { - - return(0); - } - - if (*ptr == MLOG_MULTI_REC_END) { - - *type = *ptr; - - return(1); - } - - if (*ptr == MLOG_DUMMY_RECORD) { - *type = *ptr; - - *space = ULINT_UNDEFINED - 1; /* For debugging */ - - return(1); - } - - new_ptr = mlog_parse_initial_log_record(ptr, end_ptr, type, space, - page_no); - *body = new_ptr; - - if (UNIV_UNLIKELY(!new_ptr)) { - - return(0); - } - -#ifdef UNIV_LOG_LSN_DEBUG - if (*type == MLOG_LSN) { - lsn_t lsn = (lsn_t) *space << 32 | *page_no; -# ifdef UNIV_LOG_DEBUG - ut_a(lsn == log_sys->old_lsn); -# else /* UNIV_LOG_DEBUG */ - ut_a(lsn == recv_sys->recovered_lsn); -# endif /* UNIV_LOG_DEBUG */ - } -#endif /* UNIV_LOG_LSN_DEBUG */ - - byte* old_ptr = new_ptr; - new_ptr = recv_parse_or_apply_log_rec_body(*type, new_ptr, end_ptr, - NULL, NULL, *space); - if (UNIV_UNLIKELY(new_ptr == NULL)) { - - return(0); - } - - if (*page_no == 0 && *type == MLOG_4BYTES - && mach_read_from_2(old_ptr) == FSP_HEADER_OFFSET + FSP_SIZE) { - ulint size; - mach_parse_compressed(old_ptr + 2, end_ptr, &size); - fil_space_set_recv_size(*space, size); - } - - if (*page_no > recv_max_parsed_page_no) { - recv_max_parsed_page_no = *page_no; - } - - return(new_ptr - ptr); -} - -/*******************************************************//** -Calculates the new value for lsn when more data is added to the log. */ -UNIV_INTERN -lsn_t -recv_calc_lsn_on_data_add( -/*======================*/ - lsn_t lsn, /*!< in: old lsn */ - ib_uint64_t len) /*!< in: this many bytes of data is - added, log block headers not included */ -{ - ulint frag_len; - ib_uint64_t lsn_len; - - frag_len = (lsn % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_HDR_SIZE; - ut_ad(frag_len < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE - - LOG_BLOCK_TRL_SIZE); - lsn_len = len; - lsn_len += (lsn_len + frag_len) - / (OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE - - LOG_BLOCK_TRL_SIZE) - * (LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE); - - return(lsn + lsn_len); -} - -#ifdef UNIV_LOG_DEBUG -/*******************************************************//** -Checks that the parser recognizes incomplete initial segments of a log -record as incomplete. */ -static -void -recv_check_incomplete_log_recs( -/*===========================*/ - byte* ptr, /*!< in: pointer to a complete log record */ - ulint len) /*!< in: length of the log record */ -{ - ulint i; - byte type; - ulint space; - ulint page_no; - byte* body; - - for (i = 0; i < len; i++) { - ut_a(0 == recv_parse_log_rec(ptr, ptr + i, &type, &space, - &page_no, &body)); - } -} -#endif /* UNIV_LOG_DEBUG */ - -/*******************************************************//** -Prints diagnostic info of corrupt log. */ -static -void -recv_report_corrupt_log( -/*====================*/ - byte* ptr, /*!< in: pointer to corrupt log record */ - byte type, /*!< in: type of the record */ - ulint space, /*!< in: space id, this may also be garbage */ - ulint page_no)/*!< in: page number, this may also be garbage */ -{ - fprintf(stderr, - "InnoDB: ############### CORRUPT LOG RECORD FOUND\n" - "InnoDB: Log record type %lu, space id %lu, page number %lu\n" - "InnoDB: Log parsing proceeded successfully up to " LSN_PF "\n" - "InnoDB: Previous log record type %lu, is multi %lu\n" - "InnoDB: Recv offset %lu, prev %lu\n", - (ulong) type, (ulong) space, (ulong) page_no, - recv_sys->recovered_lsn, - (ulong) recv_previous_parsed_rec_type, - (ulong) recv_previous_parsed_rec_is_multi, - (ulong) (ptr - recv_sys->buf), - (ulong) recv_previous_parsed_rec_offset); - - if ((ulint)(ptr - recv_sys->buf + 100) - > recv_previous_parsed_rec_offset - && (ulint)(ptr - recv_sys->buf + 100 - - recv_previous_parsed_rec_offset) - < 200000) { - fputs("InnoDB: Hex dump of corrupt log starting" - " 100 bytes before the start\n" - "InnoDB: of the previous log rec,\n" - "InnoDB: and ending 100 bytes after the start" - " of the corrupt rec:\n", - stderr); - - ut_print_buf(stderr, - recv_sys->buf - + recv_previous_parsed_rec_offset - 100, - ptr - recv_sys->buf + 200 - - recv_previous_parsed_rec_offset); - putc('\n', stderr); - } - -#ifndef UNIV_HOTBACKUP - if (!srv_force_recovery) { - fputs("InnoDB: Set innodb_force_recovery" - " to ignore this error.\n", stderr); - } -#endif /* !UNIV_HOTBACKUP */ - - fputs("InnoDB: WARNING: the log file may have been corrupt and it\n" - "InnoDB: is possible that the log scan did not proceed\n" - "InnoDB: far enough in recovery! Please run CHECK TABLE\n" - "InnoDB: on your InnoDB tables to check that they are ok!\n" - "InnoDB: If mysqld crashes after this recovery, look at\n" - "InnoDB: " REFMAN "forcing-innodb-recovery.html\n" - "InnoDB: about forcing recovery.\n", stderr); - - fflush(stderr); -} - -/*******************************************************//** -Parses log records from a buffer and stores them to a hash table to wait -merging to file pages. -@return currently always returns FALSE */ -static -ibool -recv_parse_log_recs( -/*================*/ - ibool store_to_hash, /*!< in: TRUE if the records should be stored - to the hash table; this is set to FALSE if just - debug checking is needed */ - dberr_t* err) /*!< out: DB_SUCCESS if successfull, - DB_ERROR if parsing fails. */ -{ - byte* ptr; - byte* end_ptr; - ulint single_rec; - ulint len; - ulint total_len; - lsn_t new_recovered_lsn; - lsn_t old_lsn; - byte type; - ulint space; - ulint page_no; - byte* body; - ulint n_recs; - - ut_ad(mutex_own(&(log_sys->mutex))); - ut_ad(recv_sys->parse_start_lsn != 0); -loop: - ptr = recv_sys->buf + recv_sys->recovered_offset; - - end_ptr = recv_sys->buf + recv_sys->len; - - if (ptr == end_ptr) { - - return(FALSE); - } - - single_rec = (ulint)*ptr & MLOG_SINGLE_REC_FLAG; - - if (single_rec || *ptr == MLOG_DUMMY_RECORD) { - /* The mtr only modified a single page, or this is a file op */ - - old_lsn = recv_sys->recovered_lsn; - - /* Try to parse a log record, fetching its type, space id, - page no, and a pointer to the body of the log record */ - - len = recv_parse_log_rec(ptr, end_ptr, &type, &space, - &page_no, &body); - - if (len == 0 || recv_sys->found_corrupt_log) { - if (recv_sys->found_corrupt_log) { - - recv_report_corrupt_log(ptr, - type, space, page_no); - } - - return(FALSE); - } - - new_recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len); - - if (new_recovered_lsn > recv_sys->scanned_lsn) { - /* The log record filled a log block, and we require - that also the next log block should have been scanned - in */ - - return(FALSE); - } - - recv_previous_parsed_rec_type = (ulint) type; - recv_previous_parsed_rec_offset = recv_sys->recovered_offset; - recv_previous_parsed_rec_is_multi = 0; - - recv_sys->recovered_offset += len; - recv_sys->recovered_lsn = new_recovered_lsn; - - DBUG_PRINT("ib_log", - ("scan " LSN_PF ": log rec %u len %u " - "page %u:%u", old_lsn, - (unsigned) type, (unsigned) len, - (unsigned) space, (unsigned) page_no)); - - if (type == MLOG_DUMMY_RECORD) { - /* Do nothing */ - - } else if (!store_to_hash) { - /* In debug checking, update a replicate page - according to the log record, and check that it - becomes identical with the original page */ -#ifdef UNIV_LOG_DEBUG - recv_check_incomplete_log_recs(ptr, len); -#endif/* UNIV_LOG_DEBUG */ - - } else if (type == MLOG_FILE_CREATE - || type == MLOG_FILE_CREATE2 - || type == MLOG_FILE_RENAME - || type == MLOG_FILE_DELETE) { - ut_a(space); -#ifdef UNIV_HOTBACKUP - if (recv_replay_file_ops) { - - /* In mysqlbackup --apply-log, replay an .ibd - file operation, if possible; note that - fil_path_to_mysql_datadir is set in mysqlbackup - to point to the datadir we should use there */ - - if (NULL == fil_op_log_parse_or_replay( - body, end_ptr, type, - space, page_no)) { - fprintf(stderr, - "InnoDB: Error: file op" - " log record of type %lu" - " space %lu not complete in\n" - "InnoDB: the replay phase." - " Path %s\n", - (ulint) type, space, - (char*)(body + 2)); - - *err = DB_ERROR; - return(FALSE); - } - } -#endif - /* In normal mysqld crash recovery we do not try to - replay file operations */ -#ifdef UNIV_LOG_LSN_DEBUG - } else if (type == MLOG_LSN) { - /* Do not add these records to the hash table. - The page number and space id fields are misused - for something else. */ -#endif /* UNIV_LOG_LSN_DEBUG */ - } else { - recv_add_to_hash_table(type, space, page_no, body, - ptr + len, old_lsn, - recv_sys->recovered_lsn); - } - } else { - /* Check that all the records associated with the single mtr - are included within the buffer */ - - total_len = 0; - n_recs = 0; - - for (;;) { - len = recv_parse_log_rec(ptr, end_ptr, &type, &space, - &page_no, &body); - if (len == 0 || recv_sys->found_corrupt_log) { - - if (recv_sys->found_corrupt_log) { - - recv_report_corrupt_log( - ptr, type, space, page_no); - } - - return(FALSE); - } - - recv_previous_parsed_rec_type = (ulint) type; - recv_previous_parsed_rec_offset - = recv_sys->recovered_offset + total_len; - recv_previous_parsed_rec_is_multi = 1; - -#ifdef UNIV_LOG_DEBUG - if ((!store_to_hash) && (type != MLOG_MULTI_REC_END)) { - recv_check_incomplete_log_recs(ptr, len); - } -#endif /* UNIV_LOG_DEBUG */ - - DBUG_PRINT("ib_log", - ("scan " LSN_PF ": multi-log rec %u " - "len %u page %u:%u", - recv_sys->recovered_lsn, - (unsigned) type, (unsigned) len, - (unsigned) space, (unsigned) page_no)); - - total_len += len; - n_recs++; - - ptr += len; - - if (type == MLOG_MULTI_REC_END) { - - /* Found the end mark for the records */ - - break; - } - } - - new_recovered_lsn = recv_calc_lsn_on_data_add( - recv_sys->recovered_lsn, total_len); - - if (new_recovered_lsn > recv_sys->scanned_lsn) { - /* The log record filled a log block, and we require - that also the next log block should have been scanned - in */ - - return(FALSE); - } - - /* Add all the records to the hash table */ - - ptr = recv_sys->buf + recv_sys->recovered_offset; - - for (;;) { - old_lsn = recv_sys->recovered_lsn; - len = recv_parse_log_rec(ptr, end_ptr, &type, &space, - &page_no, &body); - if (recv_sys->found_corrupt_log) { - - recv_report_corrupt_log(ptr, - type, space, page_no); - } - - ut_a(len != 0); - ut_a(0 == ((ulint)*ptr & MLOG_SINGLE_REC_FLAG)); - - recv_sys->recovered_offset += len; - recv_sys->recovered_lsn - = recv_calc_lsn_on_data_add(old_lsn, len); - if (type == MLOG_MULTI_REC_END) { - - /* Found the end mark for the records */ - - break; - } - - if (store_to_hash -#ifdef UNIV_LOG_LSN_DEBUG - && type != MLOG_LSN -#endif /* UNIV_LOG_LSN_DEBUG */ - ) { - recv_add_to_hash_table(type, space, page_no, - body, ptr + len, - old_lsn, - new_recovered_lsn); - } - - ptr += len; - } - } - - goto loop; -} - -/*******************************************************//** -Adds data from a new log block to the parsing buffer of recv_sys if -recv_sys->parse_start_lsn is non-zero. -@return TRUE if more data added */ -static -ibool -recv_sys_add_to_parsing_buf( -/*========================*/ - const byte* log_block, /*!< in: log block */ - lsn_t scanned_lsn) /*!< in: lsn of how far we were able - to find data in this log block */ -{ - ulint more_len; - ulint data_len; - ulint start_offset; - ulint end_offset; - - ut_ad(scanned_lsn >= recv_sys->scanned_lsn); - - if (!recv_sys->parse_start_lsn) { - /* Cannot start parsing yet because no start point for - it found */ - - return(FALSE); - } - - data_len = log_block_get_data_len(log_block); - - if (recv_sys->parse_start_lsn >= scanned_lsn) { - - return(FALSE); - - } else if (recv_sys->scanned_lsn >= scanned_lsn) { - - return(FALSE); - - } else if (recv_sys->parse_start_lsn > recv_sys->scanned_lsn) { - more_len = (ulint) (scanned_lsn - recv_sys->parse_start_lsn); - } else { - more_len = (ulint) (scanned_lsn - recv_sys->scanned_lsn); - } - - if (more_len == 0) { - - return(FALSE); - } - - ut_ad(data_len >= more_len); - - start_offset = data_len - more_len; - - if (start_offset < LOG_BLOCK_HDR_SIZE) { - start_offset = LOG_BLOCK_HDR_SIZE; - } - - end_offset = data_len; - - if (end_offset > OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { - end_offset = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; - } - - ut_ad(start_offset <= end_offset); - - if (start_offset < end_offset) { - ut_memcpy(recv_sys->buf + recv_sys->len, - log_block + start_offset, end_offset - start_offset); - - recv_sys->len += end_offset - start_offset; - - ut_a(recv_sys->len <= RECV_PARSING_BUF_SIZE); - } - - return(TRUE); -} - -/*******************************************************//** -Moves the parsing buffer data left to the buffer start. */ -static -void -recv_sys_justify_left_parsing_buf(void) -/*===================================*/ -{ - ut_memmove(recv_sys->buf, recv_sys->buf + recv_sys->recovered_offset, - recv_sys->len - recv_sys->recovered_offset); - - recv_sys->len -= recv_sys->recovered_offset; - - recv_sys->recovered_offset = 0; -} - -/*******************************************************//** -Scans log from a buffer and stores new log data to the parsing buffer. -Parses and hashes the log records if new data found. Unless -UNIV_HOTBACKUP is defined, this function will apply log records -automatically when the hash table becomes full. -@return TRUE if limit_lsn has been reached, or not able to scan any -more in this log group */ -UNIV_INTERN -ibool -recv_scan_log_recs( -/*===============*/ - ulint available_memory,/*!< in: we let the hash table of recs - to grow to this size, at the maximum */ - ibool store_to_hash, /*!< in: TRUE if the records should be - stored to the hash table; this is set - to FALSE if just debug checking is - needed */ - const byte* buf, /*!< in: buffer containing a log - segment or garbage */ - ulint len, /*!< in: buffer length */ - lsn_t start_lsn, /*!< in: buffer start lsn */ - lsn_t* contiguous_lsn, /*!< in/out: it is known that all log - groups contain contiguous log data up - to this lsn */ - lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to - this lsn */ - dberr_t* err) /*!< out: error code or DB_SUCCESS */ -{ - const byte* log_block; - ulint no; - lsn_t scanned_lsn; - ibool finished; - ulint data_len; - ibool more_data; - bool maybe_encrypted=false; - - ut_ad(start_lsn % OS_FILE_LOG_BLOCK_SIZE == 0); - ut_ad(len % OS_FILE_LOG_BLOCK_SIZE == 0); - ut_ad(len >= OS_FILE_LOG_BLOCK_SIZE); - ut_a(store_to_hash <= TRUE); - - finished = FALSE; - - log_block = buf; - scanned_lsn = start_lsn; - more_data = FALSE; - *err = DB_SUCCESS; - - do { - log_crypt_err_t log_crypt_err; - - no = log_block_get_hdr_no(log_block); - /* - fprintf(stderr, "Log block header no %lu\n", no); - - fprintf(stderr, "Scanned lsn no %lu\n", - log_block_convert_lsn_to_no(scanned_lsn)); - */ - - if (no != log_block_convert_lsn_to_no(scanned_lsn) - || !log_block_checksum_is_ok_or_old_format(log_block, true)) { - - if (no == log_block_convert_lsn_to_no(scanned_lsn) - && !log_block_checksum_is_ok_or_old_format( - log_block, true)) { - fprintf(stderr, - "InnoDB: Log block no %lu at" - " lsn " LSN_PF " has\n" - "InnoDB: ok header, but checksum field" - " contains %lu, should be %lu\n", - (ulong) no, - scanned_lsn, - (ulong) log_block_get_checksum( - log_block), - (ulong) log_block_calc_checksum( - log_block)); - } - - maybe_encrypted = log_crypt_block_maybe_encrypted(log_block, - &log_crypt_err); - - /* Garbage or an incompletely written log block */ - - /* Print checkpoint encryption keys if present */ - log_crypt_print_checkpoint_keys(log_block); - finished = TRUE; - - if (maybe_encrypted) { - /* Log block maybe encrypted finish processing*/ - log_crypt_print_error(log_crypt_err); - *err = DB_ERROR; - return (TRUE); - } - - /* Stop if we encounter a garbage log block */ - if (!srv_force_recovery) { - fputs("InnoDB: Set innodb_force_recovery" - " to ignore this error.\n", stderr); - *err = DB_ERROR; - return (TRUE); - } - - break; - - } - - if (log_block_get_flush_bit(log_block)) { - /* This block was a start of a log flush operation: - we know that the previous flush operation must have - been completed for all log groups before this block - can have been flushed to any of the groups. Therefore, - we know that log data is contiguous up to scanned_lsn - in all non-corrupt log groups. */ - - if (scanned_lsn > *contiguous_lsn) { - *contiguous_lsn = scanned_lsn; - } - } - - data_len = log_block_get_data_len(log_block); - - if ((store_to_hash || (data_len == OS_FILE_LOG_BLOCK_SIZE)) - && scanned_lsn + data_len > recv_sys->scanned_lsn - && (recv_sys->scanned_checkpoint_no > 0) - && (log_block_get_checkpoint_no(log_block) - < recv_sys->scanned_checkpoint_no) - && (recv_sys->scanned_checkpoint_no - - log_block_get_checkpoint_no(log_block) - > 0x80000000UL)) { - - /* Garbage from a log buffer flush which was made - before the most recent database recovery */ - - finished = TRUE; -#ifdef UNIV_LOG_DEBUG - /* This is not really an error, but currently - we stop here in the debug version: */ - - *err = DB_ERROR; - return (TRUE); -#endif - break; - } - - if (!recv_sys->parse_start_lsn - && (log_block_get_first_rec_group(log_block) > 0)) { - - /* We found a point from which to start the parsing - of log records */ - - recv_sys->parse_start_lsn = scanned_lsn - + log_block_get_first_rec_group(log_block); - recv_sys->scanned_lsn = recv_sys->parse_start_lsn; - recv_sys->recovered_lsn = recv_sys->parse_start_lsn; - } - - scanned_lsn += data_len; - - if (scanned_lsn > recv_sys->scanned_lsn) { - - /* We have found more entries. If this scan is - of startup type, we must initiate crash recovery - environment before parsing these log records. */ - -#ifndef UNIV_HOTBACKUP - if (recv_log_scan_is_startup_type - && !recv_needed_recovery) { - if (!srv_read_only_mode) { - ib_logf(IB_LOG_LEVEL_INFO, - "Starting crash recovery from " - "checkpoint LSN=" LSN_PF, - recv_sys->scanned_lsn); - - recv_init_crash_recovery(); - } else { - ib_logf(IB_LOG_LEVEL_ERROR, - "innodb_read_only prevents" - " crash recovery"); - recv_needed_recovery = TRUE; - return(TRUE); - } - } -#endif /* !UNIV_HOTBACKUP */ - - /* We were able to find more log data: add it to the - parsing buffer if parse_start_lsn is already - non-zero */ - - if (recv_sys->len + 4 * OS_FILE_LOG_BLOCK_SIZE - >= RECV_PARSING_BUF_SIZE) { - fprintf(stderr, - "InnoDB: Error: log parsing" - " buffer overflow." - " Recovery may have failed!\n"); - - recv_sys->found_corrupt_log = TRUE; - -#ifndef UNIV_HOTBACKUP - if (!srv_force_recovery) { - fputs("InnoDB: Set" - " innodb_force_recovery" - " to ignore this error.\n", - stderr); - *err = DB_ERROR; - return (TRUE); - } -#endif /* !UNIV_HOTBACKUP */ - - } else if (!recv_sys->found_corrupt_log) { - more_data = recv_sys_add_to_parsing_buf( - log_block, scanned_lsn); - } - - recv_sys->scanned_lsn = scanned_lsn; - recv_sys->scanned_checkpoint_no - = log_block_get_checkpoint_no(log_block); - } - - if (data_len < OS_FILE_LOG_BLOCK_SIZE) { - /* Log data for this group ends here */ - - finished = TRUE; - break; - } else { - log_block += OS_FILE_LOG_BLOCK_SIZE; - } - } while (log_block < buf + len && !finished); - - *group_scanned_lsn = scanned_lsn; - - if (more_data && !recv_sys->found_corrupt_log) { - /* Try to parse more log records */ - - recv_parse_log_recs(store_to_hash, err); - - if (*err != DB_SUCCESS) { - return (TRUE); - } - -#ifndef UNIV_HOTBACKUP - if (store_to_hash - && mem_heap_get_size(recv_sys->heap) > available_memory) { - - /* Hash table of log records has grown too big: - empty it; FALSE means no ibuf operations - allowed, as we cannot add new records to the - log yet: they would be produced by ibuf - operations */ - - recv_apply_hashed_log_recs(false); - } -#endif /* !UNIV_HOTBACKUP */ - - if (recv_sys->recovered_offset > RECV_PARSING_BUF_SIZE / 4) { - /* Move parsing buffer data to the buffer start */ - - recv_sys_justify_left_parsing_buf(); - } - } - - return(finished); -} - -#ifndef UNIV_HOTBACKUP -/*******************************************************//** -Scans log from a buffer and stores new log data to the parsing buffer. Parses -and hashes the log records if new data found. */ -static -void -recv_group_scan_log_recs( -/*=====================*/ - log_group_t* group, /*!< in: log group */ - lsn_t* contiguous_lsn, /*!< in/out: it is known that all log - groups contain contiguous log data up - to this lsn */ - lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to - this lsn */ - dberr_t* err) /*!< out: error code or DB_SUCCESS */ -{ - ibool finished; - lsn_t start_lsn; - lsn_t end_lsn; - - finished = FALSE; - *err = DB_SUCCESS; - - start_lsn = *contiguous_lsn; - - while (!finished) { - end_lsn = start_lsn + RECV_SCAN_SIZE; - - log_group_read_log_seg(LOG_RECOVER, log_sys->buf, - group, start_lsn, end_lsn, FALSE); - - finished = recv_scan_log_recs( - (buf_pool_get_n_pages() - - (recv_n_pool_free_frames * srv_buf_pool_instances)) - * UNIV_PAGE_SIZE, - TRUE, log_sys->buf, RECV_SCAN_SIZE, - start_lsn, contiguous_lsn, group_scanned_lsn, - err); - - if (*err != DB_SUCCESS) { - break; - } - - start_lsn = end_lsn; - } - -#ifdef UNIV_DEBUG - if (log_debug_writes) { - fprintf(stderr, - "InnoDB: Scanned group %lu up to" - " log sequence number " LSN_PF "\n", - (ulong) group->id, - *group_scanned_lsn); - } -#endif /* UNIV_DEBUG */ -} - -/*******************************************************//** -Initialize crash recovery environment. Can be called iff -recv_needed_recovery == FALSE. */ -static -void -recv_init_crash_recovery(void) -/*==========================*/ -{ - ut_ad(!srv_read_only_mode); - ut_a(!recv_needed_recovery); - - recv_needed_recovery = TRUE; - - fil_load_single_table_tablespaces(); - - /* If we are using the doublewrite method, we will - check if there are half-written pages in data files, - and restore them from the doublewrite buffer if - possible */ - - service_manager_extend_timeout( - INNODB_EXTEND_TIMEOUT_INTERVAL, "Starting Innodb crash recovery"); - - if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { - buf_dblwr_process(); - - /* Spawn the background thread to flush dirty pages - from the buffer pools. */ - recv_writer_thread_active = true; - recv_writer_thread_handle = os_thread_create( - recv_writer_thread, 0, 0); - } -} - -/** Recovers from a checkpoint. When this function returns, the database is able -to start processing of new user transactions, but the function -recv_recovery_from_checkpoint_finish should be called later to complete -the recovery and free the resources used in it. -@param[in] type LOG_CHECKPOINT or LOG_ARCHIVE -@param[in] limit_lsn recover up to this lsn if possible -@param[in] flushed_lsn flushed lsn from first data file -@return error code or DB_SUCCESS */ -UNIV_INTERN -dberr_t -recv_recovery_from_checkpoint_start_func( -#ifdef UNIV_LOG_ARCHIVE - ulint type, - lsn_t limit_lsn, -#endif /* UNIV_LOG_ARCHIVE */ - lsn_t flushed_lsn) -{ - log_group_t* group; - log_group_t* max_cp_group; - ulint max_cp_field; - lsn_t checkpoint_lsn; - ib_uint64_t checkpoint_no; - lsn_t group_scanned_lsn = 0; - lsn_t contiguous_lsn; -#ifdef UNIV_LOG_ARCHIVE - log_group_t* up_to_date_group; - lsn_t archived_lsn; -#endif /* UNIV_LOG_ARCHIVE */ - byte* buf; - byte* log_hdr_buf; - byte* log_hdr_buf_base = reinterpret_cast<byte *> - (alloca(LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE)); - dberr_t err; - - /* Initialize red-black tree for fast insertions into the - flush_list during recovery process. */ - buf_flush_init_flush_rbt(); - - ut_when_dtor<recv_dblwr_t> tmp(recv_sys->dblwr); - - log_hdr_buf = static_cast<byte *> - (ut_align(log_hdr_buf_base, OS_FILE_LOG_BLOCK_SIZE)); - -#ifdef UNIV_LOG_ARCHIVE - ut_ad(type != LOG_CHECKPOINT || limit_lsn == LSN_MAX); -/** TRUE when recovering from a checkpoint */ -# define TYPE_CHECKPOINT (type == LOG_CHECKPOINT) -/** Recover up to this log sequence number */ -# define LIMIT_LSN limit_lsn -#else /* UNIV_LOG_ARCHIVE */ -/** TRUE when recovering from a checkpoint */ -# define TYPE_CHECKPOINT 1 -/** Recover up to this log sequence number */ -# define LIMIT_LSN LSN_MAX -#endif /* UNIV_LOG_ARCHIVE */ - - if (srv_force_recovery >= SRV_FORCE_NO_LOG_REDO) { - - ib_logf(IB_LOG_LEVEL_INFO, - "The user has set SRV_FORCE_NO_LOG_REDO on, " - "skipping log redo"); - - return(DB_SUCCESS); - } - - recv_recovery_on = TRUE; - - recv_sys->limit_lsn = LIMIT_LSN; - - mutex_enter(&(log_sys->mutex)); - - /* Look for the latest checkpoint from any of the log groups */ - - err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field); - - if (err != DB_SUCCESS) { - - mutex_exit(&(log_sys->mutex)); - - return(err); - } - - log_group_read_checkpoint_info(max_cp_group, max_cp_field); - - buf = log_sys->checkpoint_buf; - - checkpoint_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_LSN); - checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO); -#ifdef UNIV_LOG_ARCHIVE - archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN); -#endif /* UNIV_LOG_ARCHIVE */ - - /* Read the first log file header to print a note if this is - a recovery from a restored InnoDB Hot Backup */ - - fil_io(OS_FILE_READ | OS_FILE_LOG, true, max_cp_group->space_id, 0, - 0, 0, LOG_FILE_HDR_SIZE, - log_hdr_buf, max_cp_group, 0); - - if (0 == ut_memcmp(log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, - (byte*)"ibbackup", (sizeof "ibbackup") - 1)) { - - if (srv_read_only_mode) { - - ib_logf(IB_LOG_LEVEL_ERROR, - "Cannot restore from mysqlbackup, InnoDB " - "running in read-only mode!"); - - return(DB_ERROR); - } - - /* This log file was created by mysqlbackup --restore: print - a note to the user about it */ - - ib_logf(IB_LOG_LEVEL_INFO, - "The log file was created by mysqlbackup --apply-log " - "at %s. The following crash recovery is part of a " - "normal restore.", - log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP); - - /* Wipe over the label now */ - - memset(log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, - ' ', 4); - /* Write to the log file to wipe over the label */ - fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, - max_cp_group->space_id, 0, - 0, 0, OS_FILE_LOG_BLOCK_SIZE, - log_hdr_buf, max_cp_group, 0); - } - -#ifdef UNIV_LOG_ARCHIVE - group = UT_LIST_GET_FIRST(log_sys->log_groups); - - while (group) { - log_checkpoint_get_nth_group_info(buf, group->id, - &(group->archived_file_no)); - - log_archived_get_offset(group, group->archived_file_no, - archived_lsn, &(group->archived_offset)); - - group = UT_LIST_GET_NEXT(log_groups, group); - } -#endif /* UNIV_LOG_ARCHIVE */ - - if (TYPE_CHECKPOINT) { - /* Start reading the log groups from the checkpoint lsn up. The - variable contiguous_lsn contains an lsn up to which the log is - known to be contiguously written to all log groups. */ - recv_sys->parse_start_lsn = checkpoint_lsn; - recv_sys->scanned_lsn = checkpoint_lsn; - recv_sys->scanned_checkpoint_no = 0; - recv_sys->recovered_lsn = checkpoint_lsn; - srv_start_lsn = checkpoint_lsn; - } - - contiguous_lsn = ut_uint64_align_down(recv_sys->scanned_lsn, - OS_FILE_LOG_BLOCK_SIZE); -#ifdef UNIV_LOG_ARCHIVE - if (TYPE_CHECKPOINT) { - up_to_date_group = max_cp_group; - } else { - ulint capacity; - dberr_t err; - - /* Try to recover the remaining part from logs: first from - the logs of the archived group */ - - group = recv_sys->archive_group; - capacity = log_group_get_capacity(group); - - if (recv_sys->scanned_lsn > checkpoint_lsn + capacity - || checkpoint_lsn > recv_sys->scanned_lsn + capacity) { - - mutex_exit(&(log_sys->mutex)); - - /* The group does not contain enough log: probably - an archived log file was missing or corrupt */ - - return(DB_ERROR); - } - - recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn, &err); - - if (err != DB_SUCCESS || recv_sys->scanned_lsn < checkpoint_lsn) { - - mutex_exit(&(log_sys->mutex)); - - /* The group did not contain enough log: an archived - log file was missing or invalid, or the log group - was corrupt */ - - return(DB_ERROR); - } - - group->scanned_lsn = group_scanned_lsn; - up_to_date_group = group; - } -#endif /* UNIV_LOG_ARCHIVE */ - - ut_ad(RECV_SCAN_SIZE <= log_sys->buf_size); - - group = UT_LIST_GET_FIRST(log_sys->log_groups); - -#ifdef UNIV_LOG_ARCHIVE - if ((type == LOG_ARCHIVE) && (group == recv_sys->archive_group)) { - group = UT_LIST_GET_NEXT(log_groups, group); - } -#endif /* UNIV_LOG_ARCHIVE */ - - /* Set the flag to publish that we are doing startup scan. */ - recv_log_scan_is_startup_type = TYPE_CHECKPOINT; - while (group) { -#ifdef UNIV_LOG_ARCHIVE - lsn_t old_scanned_lsn = recv_sys->scanned_lsn; -#endif /* UNIV_LOG_ARCHIVE */ - dberr_t err = DB_SUCCESS; - - recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn, &err); - - if (err != DB_SUCCESS) { - return (err); - } - - group->scanned_lsn = group_scanned_lsn; - -#ifdef UNIV_LOG_ARCHIVE - if (old_scanned_lsn < group_scanned_lsn) { - /* We found a more up-to-date group */ - - up_to_date_group = group; - } - - if ((type == LOG_ARCHIVE) - && (group == recv_sys->archive_group)) { - group = UT_LIST_GET_NEXT(log_groups, group); - } -#endif /* UNIV_LOG_ARCHIVE */ - - group = UT_LIST_GET_NEXT(log_groups, group); - } - - /* Done with startup scan. Clear the flag. */ - recv_log_scan_is_startup_type = FALSE; - - if (srv_read_only_mode && recv_needed_recovery) { - return(DB_READ_ONLY); - } - - if (TYPE_CHECKPOINT) { - /* NOTE: we always do a 'recovery' at startup, but only if - there is something wrong we will print a message to the - user about recovery: */ - - if (checkpoint_lsn != flushed_lsn) { - if (!recv_needed_recovery) { - ib_logf(IB_LOG_LEVEL_INFO, - "The log sequence number " - LSN_PF - " in ibdata file do not match" - " the log sequence number " - LSN_PF - " in the ib_logfiles!", - flushed_lsn, - checkpoint_lsn); - - if (!srv_read_only_mode) { - recv_init_crash_recovery(); - } else { - ib_logf(IB_LOG_LEVEL_ERROR, - "Can't initiate database " - "recovery, running " - "in read-only-mode."); - return(DB_READ_ONLY); - } - } - } - } - - /* We currently have only one log group */ - if (group_scanned_lsn < checkpoint_lsn - || group_scanned_lsn < recv_max_page_lsn) { - ib_logf(IB_LOG_LEVEL_ERROR, - "We scanned the log up to " - LSN_PF ". A checkpoint was at " LSN_PF - " and the maximum LSN on a database page was " LSN_PF - ". It is possible that the database is now corrupt!", - group_scanned_lsn, checkpoint_lsn, recv_max_page_lsn); - } - - if (recv_sys->recovered_lsn < checkpoint_lsn) { - - mutex_exit(&(log_sys->mutex)); - - if (recv_sys->recovered_lsn >= LIMIT_LSN) { - - return(DB_SUCCESS); - } - - /* No harm in trying to do RO access. */ - if (!srv_read_only_mode) { - return (DB_READ_ONLY); - } - - return(DB_ERROR); - } - - /* Synchronize the uncorrupted log groups to the most up-to-date log - group; we also copy checkpoint info to groups */ - - log_sys->next_checkpoint_lsn = checkpoint_lsn; - log_sys->next_checkpoint_no = checkpoint_no + 1; - /* here the checkpoint info is written without any redo logging ongoing - * and next_checkpoint_no is updated directly hence no +1 */ - log_crypt_set_ver_and_key(log_sys->next_checkpoint_no); - -#ifdef UNIV_LOG_ARCHIVE - log_sys->archived_lsn = archived_lsn; - - recv_synchronize_groups(up_to_date_group); -#else /* UNIV_LOG_ARCHIVE */ - recv_synchronize_groups(); -#endif /* UNIV_LOG_ARCHIVE */ - - if (!recv_needed_recovery) { - ut_a(checkpoint_lsn == recv_sys->recovered_lsn); - } else { - srv_start_lsn = recv_sys->recovered_lsn; - } - - log_sys->lsn = recv_sys->recovered_lsn; - - ut_memcpy(log_sys->buf, recv_sys->last_block, OS_FILE_LOG_BLOCK_SIZE); - - log_sys->buf_free = (ulint) log_sys->lsn % OS_FILE_LOG_BLOCK_SIZE; - log_sys->buf_next_to_write = log_sys->buf_free; - log_sys->written_to_some_lsn = log_sys->lsn; - log_sys->written_to_all_lsn = log_sys->lsn; - - log_sys->last_checkpoint_lsn = checkpoint_lsn; - - MONITOR_SET(MONITOR_LSN_CHECKPOINT_AGE, - log_sys->lsn - log_sys->last_checkpoint_lsn); - - log_sys->next_checkpoint_no = checkpoint_no + 1; - log_crypt_set_ver_and_key(log_sys->next_checkpoint_no); - -#ifdef UNIV_LOG_ARCHIVE - if (archived_lsn == LSN_MAX) { - - log_sys->archiving_state = LOG_ARCH_OFF; - } -#endif /* UNIV_LOG_ARCHIVE */ - - mutex_enter(&recv_sys->mutex); - - recv_sys->apply_log_recs = TRUE; - - mutex_exit(&recv_sys->mutex); - - mutex_exit(&log_sys->mutex); - - recv_lsn_checks_on = TRUE; - - /* The database is now ready to start almost normal processing of user - transactions: transaction rollbacks and the application of the log - records in the hash table can be run in background. */ - - return(DB_SUCCESS); - -#undef TYPE_CHECKPOINT -#undef LIMIT_LSN -} - -/********************************************************//** -Completes recovery from a checkpoint. */ -UNIV_INTERN -void -recv_recovery_from_checkpoint_finish(void) -/*======================================*/ -{ - if (recv_needed_recovery) { - trx_sys_print_mysql_master_log_pos(); - trx_sys_print_mysql_binlog_offset(); - } - - if (recv_sys->found_corrupt_log) { - - fprintf(stderr, - "InnoDB: WARNING: the log file may have been" - " corrupt and it\n" - "InnoDB: is possible that the log scan or parsing" - " did not proceed\n" - "InnoDB: far enough in recovery. Please run" - " CHECK TABLE\n" - "InnoDB: on your InnoDB tables to check that" - " they are ok!\n" - "InnoDB: It may be safest to recover your" - " InnoDB database from\n" - "InnoDB: a backup!\n"); - } - - /* Make sure that the recv_writer thread is done. This is - required because it grabs various mutexes and we want to - ensure that when we enable sync_order_checks there is no - mutex currently held by any thread. */ - mutex_enter(&recv_sys->writer_mutex); - - /* Free the resources of the recovery system */ - recv_recovery_on = FALSE; - - /* By acquring the mutex we ensure that the recv_writer thread - won't trigger any more LRU batchtes. Now wait for currently - in progress batches to finish. */ - buf_flush_wait_LRU_batch_end(); - - mutex_exit(&recv_sys->writer_mutex); - - ulint count = 0; - while (recv_writer_thread_active) { - ++count; - os_thread_sleep(100000); - if (srv_print_verbose_log && count > 600) { - ib_logf(IB_LOG_LEVEL_INFO, - "Waiting for recv_writer to " - "finish flushing of buffer pool"); - count = 0; - } - } - -#ifdef __WIN__ - if (recv_writer_thread_handle) { - CloseHandle(recv_writer_thread_handle); - recv_writer_thread_handle = 0; - } -#endif /* __WIN__ */ - -#ifndef UNIV_LOG_DEBUG - recv_sys_debug_free(); -#endif - /* Free up the flush_rbt. */ - buf_flush_free_flush_rbt(); - - /* 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 (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO - && !srv_apply_log_only) { - trx_rollback_or_clean_recovered(FALSE); - } -} - -/********************************************************//** -Initiates the rollback of active transactions. */ -UNIV_INTERN -void -recv_recovery_rollback_active(void) -/*===============================*/ -{ -#ifdef UNIV_SYNC_DEBUG - /* Wait for a while so that created threads have time to suspend - themselves before we switch the latching order checks on */ - os_thread_sleep(1000000); - - ut_ad(!recv_writer_thread_active); - - /* Switch latching order checks on in sync0sync.cc */ - sync_order_checks_on = TRUE; -#endif - /* We can't start any (DDL) transactions if UNDO logging - has been disabled, additionally disable ROLLBACK of recovered - user transactions. */ - if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO - && !srv_read_only_mode) { - - /* Drop partially created indexes. */ - row_merge_drop_temp_indexes(); - /* Drop temporary tables. */ - row_mysql_drop_temp_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 the uncommitted transactions which have no user - session */ - - trx_rollback_or_clean_is_active = true; - os_thread_create(trx_rollback_or_clean_all_recovered, 0, 0); - } -} - -/******************************************************//** -Resets the logs. The contents of log files will be lost! */ -UNIV_INTERN -void -recv_reset_logs( -/*============*/ -#ifdef UNIV_LOG_ARCHIVE - ulint arch_log_no, /*!< in: next archived log file number */ - ibool new_logs_created,/*!< in: TRUE if resetting logs - is done at the log creation; - FALSE if it is done after - archive recovery */ -#endif /* UNIV_LOG_ARCHIVE */ - lsn_t lsn) /*!< in: reset to this lsn - rounded up to be divisible by - OS_FILE_LOG_BLOCK_SIZE, after - which we add - LOG_BLOCK_HDR_SIZE */ -{ - log_group_t* group; - - ut_ad(mutex_own(&(log_sys->mutex))); - - log_sys->lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE); - - group = UT_LIST_GET_FIRST(log_sys->log_groups); - - while (group) { - group->lsn = log_sys->lsn; - group->lsn_offset = LOG_FILE_HDR_SIZE; -#ifdef UNIV_LOG_ARCHIVE - group->archived_file_no = arch_log_no; - group->archived_offset = 0; - - if (!new_logs_created) { - recv_truncate_group(group, group->lsn, group->lsn, - group->lsn, group->lsn); - } -#endif /* UNIV_LOG_ARCHIVE */ - - group = UT_LIST_GET_NEXT(log_groups, group); - } - - log_sys->buf_next_to_write = 0; - log_sys->written_to_some_lsn = log_sys->lsn; - log_sys->written_to_all_lsn = log_sys->lsn; - - log_sys->next_checkpoint_no = 0; - log_sys->last_checkpoint_lsn = 0; - -#ifdef UNIV_LOG_ARCHIVE - log_sys->archived_lsn = log_sys->lsn; -#endif /* UNIV_LOG_ARCHIVE */ - - log_sys->tracked_lsn = log_sys->lsn; - - memset(log_sys->buf, 0, log_sys->buf_size); - log_block_init(log_sys->buf, log_sys->lsn); - log_block_set_first_rec_group(log_sys->buf, LOG_BLOCK_HDR_SIZE); - - log_sys->buf_free = LOG_BLOCK_HDR_SIZE; - log_sys->lsn += LOG_BLOCK_HDR_SIZE; - - MONITOR_SET(MONITOR_LSN_CHECKPOINT_AGE, - (log_sys->lsn - log_sys->last_checkpoint_lsn)); - - mutex_exit(&(log_sys->mutex)); - - /* Reset the checkpoint fields in logs */ - - log_make_checkpoint_at(LSN_MAX, TRUE); - - mutex_enter(&(log_sys->mutex)); -} -#endif /* !UNIV_HOTBACKUP */ - -#ifdef UNIV_HOTBACKUP -/******************************************************//** -Creates new log files after a backup has been restored. */ -UNIV_INTERN -void -recv_reset_log_files_for_backup( -/*============================*/ - const char* log_dir, /*!< in: log file directory path */ - ulint n_log_files, /*!< in: number of log files */ - lsn_t log_file_size, /*!< in: log file size */ - lsn_t lsn) /*!< in: new start lsn, must be - divisible by OS_FILE_LOG_BLOCK_SIZE */ -{ - os_file_t log_file; - ibool success; - byte* buf; - ulint i; - ulint log_dir_len; - char name[5000]; - static const char ib_logfile_basename[] = "ib_logfile"; - - log_dir_len = strlen(log_dir); - /* full path name of ib_logfile consists of log dir path + basename - + number. This must fit in the name buffer. - */ - ut_a(log_dir_len + strlen(ib_logfile_basename) + 11 < sizeof(name)); - - buf = ut_malloc(LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); - memset(buf, '\0', LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); - - for (i = 0; i < n_log_files; i++) { - - sprintf(name, "%s%s%lu", log_dir, - ib_logfile_basename, (ulong) i); - - log_file = os_file_create_simple(innodb_file_log_key, - name, OS_FILE_CREATE, - OS_FILE_READ_WRITE, - &success); - if (!success) { - fprintf(stderr, - "InnoDB: Cannot create %s. Check that" - " the file does not exist yet.\n", name); - - exit(1); - } - - fprintf(stderr, - "Setting log file size to %llu\n", - log_file_size); - - success = os_file_set_size(name, log_file, log_file_size); - - if (!success) { - fprintf(stderr, - "InnoDB: Cannot set %s size to %llu\n", - name, log_file_size); - exit(1); - } - - os_file_flush(log_file); - os_file_close(log_file); - } - - /* We pretend there is a checkpoint at lsn + LOG_BLOCK_HDR_SIZE */ - - log_reset_first_header_and_checkpoint(buf, lsn); - - log_block_init_in_old_format(buf + LOG_FILE_HDR_SIZE, lsn); - log_block_set_first_rec_group(buf + LOG_FILE_HDR_SIZE, - LOG_BLOCK_HDR_SIZE); - sprintf(name, "%s%s%lu", log_dir, ib_logfile_basename, (ulong)0); - - log_file = os_file_create_simple(innodb_file_log_key, - name, OS_FILE_OPEN, - OS_FILE_READ_WRITE, &success); - if (!success) { - fprintf(stderr, "InnoDB: Cannot open %s.\n", name); - - exit(1); - } - - os_file_write(name, log_file, buf, 0, - LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); - os_file_flush(log_file); - os_file_close(log_file); - - ut_free(buf); -} -#endif /* UNIV_HOTBACKUP */ - -/******************************************************//** -Checks the 4-byte checksum to the trailer checksum field of a log -block. We also accept a log block in the old format before -InnoDB-3.23.52 where the checksum field contains the log block number. -@return TRUE if ok, or if the log block may be in the format of InnoDB -version predating 3.23.52 */ -UNIV_INTERN -ibool -log_block_checksum_is_ok_or_old_format( -/*===================================*/ - const byte* block) /*!< in: pointer to a log block */ -{ -#ifdef UNIV_LOG_DEBUG - return(TRUE); -#endif /* UNIV_LOG_DEBUG */ - - ulint block_checksum = log_block_get_checksum(block); - - if (UNIV_LIKELY(srv_log_checksum_algorithm == - SRV_CHECKSUM_ALGORITHM_NONE || - log_block_calc_checksum(block) == block_checksum)) { - - return(TRUE); - } - - if (srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_CRC32 || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_INNODB || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_STRICT_NONE) { - - const char* algo = NULL; - - ib_logf(IB_LOG_LEVEL_ERROR, - "log block checksum mismatch: expected " ULINTPF ", " - "calculated checksum " ULINTPF, - block_checksum, - log_block_calc_checksum(block)); - - if (block_checksum == LOG_NO_CHECKSUM_MAGIC) { - - algo = "none"; - } else if (block_checksum == - log_block_calc_checksum_crc32(block)) { - - algo = "crc32"; - } else if (block_checksum == - log_block_calc_checksum_innodb(block)) { - - algo = "innodb"; - } - - if (algo) { - - const char* current_algo; - - current_algo = buf_checksum_algorithm_name( - (srv_checksum_algorithm_t) - srv_log_checksum_algorithm); - - ib_logf(IB_LOG_LEVEL_ERROR, - "current InnoDB log checksum type: %s, " - "detected log checksum type: %s", - current_algo, - algo); - } - - ib_logf(IB_LOG_LEVEL_FATAL, - "STRICT method was specified for innodb_log_checksum, " - "so we intentionally assert here."); - } - - ut_ad(srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_CRC32 || - srv_log_checksum_algorithm == SRV_CHECKSUM_ALGORITHM_INNODB); - - if (block_checksum == LOG_NO_CHECKSUM_MAGIC || - block_checksum == log_block_calc_checksum_crc32(block) || - block_checksum == log_block_calc_checksum_innodb(block)) { - - return(TRUE); - } - - if (log_block_get_hdr_no(block) == block_checksum) { - - /* We assume the log block is in the format of - InnoDB version < 3.23.52 and the block is ok */ -#if 0 - fprintf(stderr, - "InnoDB: Scanned old format < InnoDB-3.23.52" - " log block number %lu\n", - log_block_get_hdr_no(block)); -#endif - return(TRUE); - } - - return(FALSE); -} - -void recv_dblwr_t::add(byte* page) -{ - pages.push_back(page); -} - -byte* recv_dblwr_t::find_page(ulint space_id, ulint page_no) -{ - std::vector<byte*> matches; - byte* result = 0; - - for (std::list<byte*>::iterator i = pages.begin(); - i != pages.end(); ++i) { - - if ((page_get_space_id(*i) == space_id) - && (page_get_page_no(*i) == page_no)) { - matches.push_back(*i); - } - } - - if (matches.size() == 1) { - result = matches[0]; - } else if (matches.size() > 1) { - - lsn_t max_lsn = 0; - lsn_t page_lsn = 0; - - for (std::vector<byte*>::iterator i = matches.begin(); - i != matches.end(); ++i) { - - page_lsn = mach_read_from_8(*i + FIL_PAGE_LSN); - - if (page_lsn > max_lsn) { - max_lsn = page_lsn; - result = *i; - } - } - } - - return(result); -} |