mariadb/storage/xtradb/trx/trx0sys.cc

1525 lines
39 KiB
C++
Raw Normal View History

2009-03-26 07:11:11 +01:00
/*****************************************************************************
2017-05-16 13:16:11 +02:00
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
2009-03-26 07:11:11 +01:00
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, Suite 500, Boston, MA 02110-1335 USA
2009-03-26 07:11:11 +01:00
*****************************************************************************/
2009-09-07 12:22:53 +02:00
/**************************************************//**
2013-12-16 15:38:05 +01:00
@file trx/trx0sys.cc
Transaction system
Created 3/26/1996 Heikki Tuuri
*******************************************************/
#include "trx0sys.h"
#ifdef UNIV_NONINL
#include "trx0sys.ic"
#endif
2015-12-13 10:13:18 +01:00
#ifdef UNIV_HOTBACKUP
#include "fsp0types.h"
#else /* !UNIV_HOTBACKUP */
#include "fsp0fsp.h"
2009-09-07 12:22:53 +02:00
#include "mtr0log.h"
#include "mtr0log.h"
#include "trx0trx.h"
#include "trx0rseg.h"
#include "trx0undo.h"
#include "srv0srv.h"
2011-07-14 21:22:41 +02:00
#include "srv0start.h"
#include "trx0purge.h"
#include "log0log.h"
2010-01-06 13:00:14 +01:00
#include "log0recv.h"
#include "os0file.h"
2010-01-06 13:00:14 +01:00
#include "read0read.h"
#ifdef WITH_WSREP
#include "ha_prototypes.h" /* wsrep_is_wsrep_xid() */
#endif /* */
#include <mysql/service_wsrep.h>
2009-09-07 12:22:53 +02:00
/** The file format tag structure with id and name. */
2013-12-16 15:38:05 +01:00
struct file_format_t {
2009-09-07 12:22:53 +02:00
ulint id; /*!< id of the file format */
const char* name; /*!< text representation of the
file format */
2013-12-16 15:38:05 +01:00
ib_mutex_t mutex; /*!< covers changes to the above
fields */
};
2009-09-07 12:22:53 +02:00
/** The transaction system */
UNIV_INTERN trx_sys_t* trx_sys = NULL;
2009-09-07 12:22:53 +02:00
/** In a MySQL replication slave, in crash recovery we store the master log
file name and position here. */
/* @{ */
/** Master binlog file name */
2013-12-16 15:38:05 +01:00
UNIV_INTERN char trx_sys_mysql_master_log_name[TRX_SYS_MYSQL_LOG_NAME_LEN];
2009-09-07 12:22:53 +02:00
/** Master binlog file position. We have successfully got the updates
up to this position. -1 means that no crash recovery was needed, or
there was no master log position info inside InnoDB.*/
UNIV_INTERN ib_int64_t trx_sys_mysql_master_log_pos = -1;
2009-09-07 12:22:53 +02:00
/* @} */
2009-09-07 12:22:53 +02:00
/** If this MySQL server uses binary logging, after InnoDB has been inited
and if it has done a crash recovery, we store the binlog file name and position
2009-09-07 12:22:53 +02:00
here. */
/* @{ */
/** Binlog file name */
UNIV_INTERN char trx_sys_mysql_bin_log_name[TRX_SYS_MYSQL_LOG_NAME_LEN];
2009-09-07 12:22:53 +02:00
/** Binlog file position, or -1 if unknown */
UNIV_INTERN ib_int64_t trx_sys_mysql_bin_log_pos = -1;
2009-09-07 12:22:53 +02:00
/* @} */
#endif /* !UNIV_HOTBACKUP */
2009-09-07 12:22:53 +02:00
/** List of animal names representing file format. */
static const char* file_format_name_map[] = {
"Antelope",
"Barracuda",
"Cheetah",
"Dragon",
"Elk",
"Fox",
"Gazelle",
"Hornet",
"Impala",
"Jaguar",
"Kangaroo",
"Leopard",
"Moose",
"Nautilus",
"Ocelot",
"Porpoise",
"Quail",
"Rabbit",
"Shark",
"Tiger",
"Urchin",
"Viper",
"Whale",
"Xenops",
"Yak",
"Zebra"
};
2009-09-07 12:22:53 +02:00
/** The number of elements in the file format name array. */
2008-12-03 06:06:00 +01:00
static const ulint FILE_FORMAT_NAME_N
= sizeof(file_format_name_map) / sizeof(file_format_name_map[0]);
2011-07-14 21:22:41 +02:00
#ifdef UNIV_PFS_MUTEX
/* Key to register the mutex with performance schema */
UNIV_INTERN mysql_pfs_key_t file_format_max_mutex_key;
2013-12-16 15:38:05 +01:00
UNIV_INTERN mysql_pfs_key_t trx_sys_mutex_key;
#endif /* UNIV_PFS_RWLOCK */
2011-07-14 21:22:41 +02:00
2012-09-05 10:44:23 +02:00
#ifndef UNIV_HOTBACKUP
#ifdef UNIV_DEBUG
/* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
2013-07-16 14:55:47 +02:00
UNIV_INTERN uint trx_rseg_n_slots_debug = 0;
#endif
2009-09-07 12:22:53 +02:00
/** This is used to track the maximum file format id known to InnoDB. It's
2011-07-14 21:22:41 +02:00
updated via SET GLOBAL innodb_file_format_max = 'x' or when we open
or create a table. */
static file_format_t file_format_max;
2013-12-16 15:38:05 +01:00
#ifdef UNIV_DEBUG
2009-09-07 12:22:53 +02:00
/****************************************************************//**
2013-12-16 15:38:05 +01:00
Checks whether a trx is in one of rw_trx_list or ro_trx_list.
2009-09-07 12:22:53 +02:00
@return TRUE if is in */
UNIV_INTERN
ibool
trx_in_trx_list(
/*============*/
2013-12-16 15:38:05 +01:00
const trx_t* in_trx) /*!< in: transaction */
{
2013-12-16 15:38:05 +01:00
const trx_t* trx;
trx_list_t* trx_list;
2013-12-16 15:38:05 +01:00
/* Non-locking autocommits should not hold any locks. */
assert_trx_in_list(in_trx);
2013-12-16 15:38:05 +01:00
trx_list = in_trx->read_only
? &trx_sys->ro_trx_list : &trx_sys->rw_trx_list;
2013-12-16 15:38:05 +01:00
ut_ad(mutex_own(&trx_sys->mutex));
2013-12-16 15:38:05 +01:00
ut_ad(trx_assert_started(in_trx));
2013-12-16 15:38:05 +01:00
for (trx = UT_LIST_GET_FIRST(*trx_list);
trx != NULL && trx != in_trx;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
2013-12-16 15:38:05 +01:00
assert_trx_in_list(trx);
ut_ad(trx->read_only == (trx_list == &trx_sys->ro_trx_list));
}
2013-12-16 15:38:05 +01:00
return(trx != NULL);
}
2013-12-16 15:38:05 +01:00
#endif /* UNIV_DEBUG */
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Writes the value of max_trx_id to the file based trx system header. */
UNIV_INTERN
void
trx_sys_flush_max_trx_id(void)
/*==========================*/
{
mtr_t mtr;
2013-12-16 15:38:05 +01:00
trx_sysf_t* sys_header;
#ifndef WITH_WSREP
/* wsrep_fake_trx_id violates this assert
* Copied from trx_sys_get_new_trx_id
*/
2013-12-16 15:38:05 +01:00
ut_ad(mutex_own(&trx_sys->mutex));
#endif /* WITH_WSREP */
2013-12-16 15:38:05 +01:00
if (!srv_read_only_mode) {
mtr_start(&mtr);
2013-12-16 15:38:05 +01:00
sys_header = trx_sysf_get(&mtr);
2013-12-16 15:38:05 +01:00
mlog_write_ull(
sys_header + TRX_SYS_TRX_ID_STORE,
trx_sys->max_trx_id, &mtr);
mtr_commit(&mtr);
}
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Updates the offset information about the end of the MySQL binlog entry
which corresponds to the transaction just being committed. In a MySQL
replication slave updates the latest master binlog position up to which
replication has proceeded. */
UNIV_INTERN
void
trx_sys_update_mysql_binlog_offset(
/*===============================*/
2013-12-16 15:38:05 +01:00
const char* file_name,/*!< in: MySQL log file name */
2009-09-07 12:22:53 +02:00
ib_int64_t offset, /*!< in: position in that log file */
ulint field, /*!< in: offset of the MySQL log info field in
the trx sys header */
#ifdef WITH_WSREP
trx_sysf_t* sys_header, /*!< in: trx sys header */
#endif /* WITH_WSREP */
2009-09-07 12:22:53 +02:00
mtr_t* mtr) /*!< in: mtr */
{
#ifndef WITH_WSREP
2013-12-16 15:38:05 +01:00
trx_sysf_t* sys_header;
#endif /* !WITH_WSREP */
2013-12-16 15:38:05 +01:00
if (ut_strlen(file_name) >= TRX_SYS_MYSQL_LOG_NAME_LEN) {
/* We cannot fit the name to the 512 bytes we have reserved */
2013-12-16 15:38:05 +01:00
return;
}
#ifndef WITH_WSREP
2013-12-16 15:38:05 +01:00
sys_header = trx_sysf_get(mtr);
#endif /* !WITH_WSREP */
2013-12-16 15:38:05 +01:00
if (mach_read_from_4(sys_header + field
+ TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
!= TRX_SYS_MYSQL_LOG_MAGIC_N) {
mlog_write_ulint(sys_header + field
+ TRX_SYS_MYSQL_LOG_MAGIC_N_FLD,
TRX_SYS_MYSQL_LOG_MAGIC_N,
MLOG_4BYTES, mtr);
}
if (0 != strcmp((char*) (sys_header + field + TRX_SYS_MYSQL_LOG_NAME),
file_name)) {
mlog_write_string(sys_header + field
+ TRX_SYS_MYSQL_LOG_NAME,
(byte*) file_name, 1 + ut_strlen(file_name),
mtr);
}
if (mach_read_from_4(sys_header + field
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH) > 0
|| (offset >> 32) > 0) {
mlog_write_ulint(sys_header + field
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH,
(ulint)(offset >> 32),
MLOG_4BYTES, mtr);
}
mlog_write_ulint(sys_header + field
+ TRX_SYS_MYSQL_LOG_OFFSET_LOW,
(ulint)(offset & 0xFFFFFFFFUL),
MLOG_4BYTES, mtr);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Stores the MySQL binlog offset info in the trx system header if
the magic number shows it valid, and print the info to stderr */
UNIV_INTERN
void
trx_sys_print_mysql_binlog_offset(void)
/*===================================*/
{
trx_sysf_t* sys_header;
mtr_t mtr;
ulint trx_sys_mysql_bin_log_pos_high;
ulint trx_sys_mysql_bin_log_pos_low;
mtr_start(&mtr);
sys_header = trx_sysf_get(&mtr);
if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
!= TRX_SYS_MYSQL_LOG_MAGIC_N) {
mtr_commit(&mtr);
return;
}
trx_sys_mysql_bin_log_pos_high = mach_read_from_4(
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH);
trx_sys_mysql_bin_log_pos_low = mach_read_from_4(
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_LOW);
trx_sys_mysql_bin_log_pos
2013-12-16 15:38:05 +01:00
= (((ib_int64_t) trx_sys_mysql_bin_log_pos_high) << 32)
+ (ib_int64_t) trx_sys_mysql_bin_log_pos_low;
ut_memcpy(trx_sys_mysql_bin_log_name,
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_NAME, TRX_SYS_MYSQL_LOG_NAME_LEN);
fprintf(stderr,
"InnoDB: Last MySQL binlog file position %lu %lu,"
" file name %s\n",
trx_sys_mysql_bin_log_pos_high, trx_sys_mysql_bin_log_pos_low,
trx_sys_mysql_bin_log_name);
mtr_commit(&mtr);
}
#ifdef WITH_WSREP
#ifdef UNIV_DEBUG
static long long trx_sys_cur_xid_seqno = -1;
static unsigned char trx_sys_cur_xid_uuid[16];
/** Read WSREP XID seqno */
static inline long long read_wsrep_xid_seqno(const XID* xid)
{
long long seqno;
memcpy(&seqno, xid->data + 24, sizeof(long long));
return seqno;
}
/** Read WSREP XID UUID */
static inline void read_wsrep_xid_uuid(const XID* xid, unsigned char* buf)
{
memcpy(buf, xid->data + 8, 16);
}
#endif /* UNIV_DEBUG */
/** Update WSREP XID info in sys_header of TRX_SYS_PAGE_NO = 5.
@param[in] xid Transaction XID
@param[in,out] sys_header sys_header
@param[in] mtr minitransaction */
void
trx_sys_update_wsrep_checkpoint(
const XID* xid,
trx_sysf_t* sys_header,
mtr_t* mtr)
{
#ifdef UNIV_DEBUG
if (xid->formatID != -1
&& mach_read_from_4(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_MAGIC_N_FLD)
== TRX_SYS_WSREP_XID_MAGIC_N) {
/* Check that seqno is monotonically increasing */
unsigned char xid_uuid[16];
long long xid_seqno = read_wsrep_xid_seqno(xid);
read_wsrep_xid_uuid(xid, xid_uuid);
if (!memcmp(xid_uuid, trx_sys_cur_xid_uuid, 8)) {
ut_ad(xid_seqno > trx_sys_cur_xid_seqno);
trx_sys_cur_xid_seqno = xid_seqno;
} else {
memcpy(trx_sys_cur_xid_uuid, xid_uuid, 16);
}
trx_sys_cur_xid_seqno = xid_seqno;
}
#endif /* UNIV_DEBUG */
ut_ad(xid && mtr);
ut_a(xid->formatID == -1 || wsrep_is_wsrep_xid((const XID *)xid));
2014-01-25 10:02:49 +01:00
if (mach_read_from_4(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_MAGIC_N_FLD)
!= TRX_SYS_WSREP_XID_MAGIC_N) {
mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_MAGIC_N_FLD,
TRX_SYS_WSREP_XID_MAGIC_N,
MLOG_4BYTES, mtr);
}
mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_FORMAT,
(int)xid->formatID,
MLOG_4BYTES, mtr);
mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_GTRID_LEN,
(int)xid->gtrid_length,
MLOG_4BYTES, mtr);
mlog_write_ulint(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_BQUAL_LEN,
(int)xid->bqual_length,
MLOG_4BYTES, mtr);
mlog_write_string(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_DATA,
(const unsigned char*) xid->data,
XIDDATASIZE, mtr);
}
/** Read WSREP XID from sys_header of TRX_SYS_PAGE_NO = 5.
@param[out] xid Transaction XID
@retval true if found, false if not */
bool
trx_sys_read_wsrep_checkpoint(XID* xid)
{
trx_sysf_t* sys_header;
mtr_t mtr;
ulint magic;
ut_ad(xid);
mtr_start(&mtr);
sys_header = trx_sysf_get(&mtr);
if ((magic = mach_read_from_4(sys_header + TRX_SYS_WSREP_XID_INFO
+ TRX_SYS_WSREP_XID_MAGIC_N_FLD))
!= TRX_SYS_WSREP_XID_MAGIC_N) {
memset(xid, 0, sizeof(*xid));
xid->formatID = -1;
trx_sys_update_wsrep_checkpoint(xid, sys_header, &mtr);
mtr_commit(&mtr);
return false;
}
2014-01-25 10:02:49 +01:00
xid->formatID = (int)mach_read_from_4(
sys_header
+ TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_FORMAT);
xid->gtrid_length = (int)mach_read_from_4(
sys_header
+ TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_GTRID_LEN);
xid->bqual_length = (int)mach_read_from_4(
sys_header
+ TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_BQUAL_LEN);
ut_memcpy(xid->data,
sys_header + TRX_SYS_WSREP_XID_INFO + TRX_SYS_WSREP_XID_DATA,
XIDDATASIZE);
mtr_commit(&mtr);
return true;
}
#endif /* WITH_WSREP */
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
2013-12-16 15:38:05 +01:00
Prints to stderr the MySQL master log offset info in the trx system header if
the magic number shows it valid. */
UNIV_INTERN
void
trx_sys_print_mysql_master_log_pos(void)
/*====================================*/
{
trx_sysf_t* sys_header;
mtr_t mtr;
mtr_start(&mtr);
sys_header = trx_sysf_get(&mtr);
if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
!= TRX_SYS_MYSQL_LOG_MAGIC_N) {
mtr_commit(&mtr);
return;
}
fprintf(stderr,
"InnoDB: In a MySQL replication slave the last"
" master binlog file\n"
2013-12-16 15:38:05 +01:00
"InnoDB: position %lu %lu, file name %s\n",
(ulong) mach_read_from_4(sys_header
+ TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
(ulong) mach_read_from_4(sys_header
+ TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_LOW),
sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_NAME);
2012-11-21 23:24:18 +01:00
/* Copy the master log position info to global variables we can
2013-12-16 15:38:05 +01:00
use in ha_innobase.cc to initialize glob_mi to right values */
2013-12-16 15:38:05 +01:00
ut_memcpy(trx_sys_mysql_master_log_name,
sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_NAME,
TRX_SYS_MYSQL_LOG_NAME_LEN);
trx_sys_mysql_master_log_pos
= (((ib_int64_t) mach_read_from_4(
sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH)) << 32)
+ ((ib_int64_t) mach_read_from_4(
sys_header + TRX_SYS_MYSQL_MASTER_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_LOW));
mtr_commit(&mtr);
}
2009-09-07 12:22:53 +02:00
/****************************************************************//**
Looks for a free slot for a rollback segment in the trx system file copy.
@return slot index or ULINT_UNDEFINED if not found */
UNIV_INTERN
ulint
trx_sysf_rseg_find_free(
/*====================*/
2009-09-07 12:22:53 +02:00
mtr_t* mtr) /*!< in: mtr */
{
ulint i;
2013-12-16 15:38:05 +01:00
trx_sysf_t* sys_header;
sys_header = trx_sysf_get(mtr);
for (i = 0; i < TRX_SYS_N_RSEGS; i++) {
2013-12-16 15:38:05 +01:00
ulint page_no;
page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr);
if (page_no == FIL_NULL) {
return(i);
}
}
return(ULINT_UNDEFINED);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Creates the file page for the transaction system. This function is called only
at the database creation, before trx_sys_init. */
static
void
trx_sysf_create(
/*============*/
2009-09-07 12:22:53 +02:00
mtr_t* mtr) /*!< in: mtr */
{
trx_sysf_t* sys_header;
ulint slot_no;
buf_block_t* block;
page_t* page;
ulint page_no;
2011-07-14 21:22:41 +02:00
byte* ptr;
ulint len;
ut_ad(mtr);
/* Note that below we first reserve the file space x-latch, and
then enter the kernel: we must do it in this order to conform
to the latching order rules. */
mtr_x_lock(fil_space_get_latch(TRX_SYS_SPACE, NULL), mtr);
/* Create the trx sys file block in a new allocated file segment */
block = fseg_create(TRX_SYS_SPACE, 0, TRX_SYS + TRX_SYS_FSEG_HEADER,
mtr);
buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);
2008-12-03 06:06:00 +01:00
ut_a(buf_block_get_page_no(block) == TRX_SYS_PAGE_NO);
page = buf_block_get_frame(block);
mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS,
MLOG_2BYTES, mtr);
/* Reset the doublewrite buffer magic number to zero so that we
know that the doublewrite buffer has not yet been created (this
suppresses a Valgrind warning) */
mlog_write_ulint(page + TRX_SYS_DOUBLEWRITE
+ TRX_SYS_DOUBLEWRITE_MAGIC, 0, MLOG_4BYTES, mtr);
sys_header = trx_sysf_get(mtr);
/* Start counting transaction ids from number 1 up */
2011-07-14 21:22:41 +02:00
mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1);
2011-07-14 21:22:41 +02:00
/* Reset the rollback segment slots. Old versions of InnoDB
define TRX_SYS_N_RSEGS as 256 (TRX_SYS_OLD_N_RSEGS) and expect
that the whole array is initialized. */
ptr = TRX_SYS_RSEGS + sys_header;
len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS)
* TRX_SYS_RSEG_SLOT_SIZE;
memset(ptr, 0xff, len);
ptr += len;
ut_a(ptr <= page + (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END));
2011-07-14 21:22:41 +02:00
/* Initialize all of the page. This part used to be uninitialized. */
memset(ptr, 0, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page - ptr);
2011-07-14 21:22:41 +02:00
mlog_log_string(sys_header, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
+ page - sys_header, mtr);
/* Create the first rollback segment in the SYSTEM tablespace */
2011-07-14 21:22:41 +02:00
slot_no = trx_sysf_rseg_find_free(mtr);
page_no = trx_rseg_header_create(TRX_SYS_SPACE, 0, ULINT_MAX, slot_no,
mtr);
2013-12-16 15:38:05 +01:00
ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID);
2011-07-14 21:22:41 +02:00
ut_a(page_no == FSP_FIRST_RSEG_PAGE_NO);
}
2011-07-14 21:22:41 +02:00
/*****************************************************************//**
Compare two trx_rseg_t instances on last_trx_no. */
static
int
trx_rseg_compare_last_trx_no(
/*=========================*/
const void* p1, /*!< in: elem to compare */
const void* p2) /*!< in: elem to compare */
{
ib_int64_t cmp;
const rseg_queue_t* rseg_q1 = (const rseg_queue_t*) p1;
const rseg_queue_t* rseg_q2 = (const rseg_queue_t*) p2;
cmp = rseg_q1->trx_no - rseg_q2->trx_no;
if (cmp < 0) {
return(-1);
} else if (cmp > 0) {
return(1);
}
return(0);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Creates and initializes the central memory structures for the transaction
2013-12-16 15:38:05 +01:00
system. This is called when the database is started.
@return min binary heap of rsegs to purge */
UNIV_INTERN
2013-12-16 15:38:05 +01:00
ib_bh_t*
trx_sys_init_at_db_start(void)
/*==========================*/
{
2013-12-16 15:38:05 +01:00
mtr_t mtr;
ib_bh_t* ib_bh;
trx_sysf_t* sys_header;
2011-07-14 21:22:41 +02:00
ib_uint64_t rows_to_undo = 0;
const char* unit = "";
2011-07-14 21:22:41 +02:00
/* We create the min binary heap here and pass ownership to
purge when we init the purge sub-system. Purge is responsible
for freeing the binary heap. */
ib_bh = ib_bh_create(
trx_rseg_compare_last_trx_no,
sizeof(rseg_queue_t), TRX_SYS_N_RSEGS);
2013-12-16 15:38:05 +01:00
mtr_start(&mtr);
2013-05-08 09:52:54 +02:00
/* Allocate the trx descriptors array */
2013-12-16 15:38:05 +01:00
trx_sys->descriptors = static_cast<trx_id_t*>(
ut_malloc(sizeof(trx_id_t) *
TRX_DESCR_ARRAY_INITIAL_SIZE));
2013-05-08 09:52:54 +02:00
trx_sys->descr_n_max = TRX_DESCR_ARRAY_INITIAL_SIZE;
trx_sys->descr_n_used = 0;
2013-07-16 14:55:47 +02:00
srv_descriptors_memory = TRX_DESCR_ARRAY_INITIAL_SIZE *
sizeof(trx_id_t);
2013-05-08 09:52:54 +02:00
sys_header = trx_sysf_get(&mtr);
2013-12-16 15:38:05 +01:00
if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
trx_rseg_array_init(sys_header, ib_bh, &mtr);
}
/* VERY important: after the database is started, max_trx_id value is
divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the 'if' in
trx_sys_get_new_trx_id will evaluate to TRUE when the function
is first time called, and the value for trx id will be written
to the disk-based header! Thus trx id values will not overlap when
the database is repeatedly started! */
2011-07-14 21:22:41 +02:00
trx_sys->max_trx_id = 2 * TRX_SYS_TRX_ID_WRITE_MARGIN
+ ut_uint64_align_up(mach_read_from_8(sys_header
+ TRX_SYS_TRX_ID_STORE),
TRX_SYS_TRX_ID_WRITE_MARGIN);
2013-12-16 15:38:05 +01:00
ut_d(trx_sys->rw_max_trx_id = trx_sys->max_trx_id);
UT_LIST_INIT(trx_sys->mysql_trx_list);
2013-12-16 15:38:05 +01:00
trx_dummy_sess = sess_open();
2013-12-16 15:38:05 +01:00
trx_lists_init_at_db_start();
2013-12-16 15:38:05 +01:00
/* This S lock is not strictly required, it is here only to satisfy
the debug code (assertions). We are still running in single threaded
bootstrap mode. */
2013-12-16 15:38:05 +01:00
mutex_enter(&trx_sys->mutex);
2013-12-16 15:38:05 +01:00
ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
2013-12-16 15:38:05 +01:00
if (UT_LIST_GET_LEN(trx_sys->rw_trx_list) > 0) {
const trx_t* trx;
2013-12-16 15:38:05 +01:00
for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
ut_ad(trx->is_recovered);
assert_trx_in_rw_list(trx);
if (trx_state_eq(trx, TRX_STATE_ACTIVE)) {
rows_to_undo += trx->undo_no;
}
}
if (rows_to_undo > 1000000000) {
unit = "M";
rows_to_undo = rows_to_undo / 1000000;
}
fprintf(stderr,
"InnoDB: %lu transaction(s) which must be"
" rolled back or cleaned up\n"
"InnoDB: in total %lu%s row operations to undo\n",
2013-12-16 15:38:05 +01:00
(ulong) UT_LIST_GET_LEN(trx_sys->rw_trx_list),
(ulong) rows_to_undo, unit);
fprintf(stderr, "InnoDB: Trx id counter is " TRX_ID_FMT "\n",
2013-12-16 15:38:05 +01:00
trx_sys->max_trx_id);
}
2013-12-16 15:38:05 +01:00
mutex_exit(&trx_sys->mutex);
2013-12-16 15:38:05 +01:00
UT_LIST_INIT(trx_sys->view_list);
mtr_commit(&mtr);
2013-12-16 15:38:05 +01:00
return(ib_bh);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
2013-12-16 15:38:05 +01:00
Creates the trx_sys instance and initializes ib_bh and mutex. */
UNIV_INTERN
void
trx_sys_create(void)
/*================*/
2013-12-16 15:38:05 +01:00
{
ut_ad(trx_sys == NULL);
trx_sys = static_cast<trx_sys_t*>(mem_zalloc(sizeof(*trx_sys)));
mutex_create(trx_sys_mutex_key, &trx_sys->mutex, SYNC_TRX_SYS);
}
/*****************************************************************//**
Creates and initializes the transaction system at the database creation. */
UNIV_INTERN
void
trx_sys_create_sys_pages(void)
/*==========================*/
{
mtr_t mtr;
mtr_start(&mtr);
trx_sysf_create(&mtr);
mtr_commit(&mtr);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Update the file format tag.
@return always TRUE */
static
ibool
trx_sys_file_format_max_write(
/*==========================*/
2009-09-07 12:22:53 +02:00
ulint format_id, /*!< in: file format id */
const char** name) /*!< out: max file format name, can
be NULL */
{
mtr_t mtr;
byte* ptr;
buf_block_t* block;
2011-07-14 21:22:41 +02:00
ib_uint64_t tag_value;
mtr_start(&mtr);
block = buf_page_get(
TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr);
file_format_max.id = format_id;
file_format_max.name = trx_sys_file_format_id_to_name(format_id);
ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG;
2011-07-14 21:22:41 +02:00
tag_value = format_id + TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
if (name) {
2008-12-03 06:06:00 +01:00
*name = file_format_max.name;
}
2011-07-14 21:22:41 +02:00
mlog_write_ull(ptr, tag_value, &mtr);
mtr_commit(&mtr);
return(TRUE);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Read the file format tag.
@return the file format or ULINT_UNDEFINED if not set. */
static
ulint
trx_sys_file_format_max_read(void)
/*==============================*/
{
mtr_t mtr;
const byte* ptr;
const buf_block_t* block;
2011-07-14 21:22:41 +02:00
ib_id_t file_format_id;
/* Since this is called during the startup phase it's safe to
read the value without a covering mutex. */
mtr_start(&mtr);
block = buf_page_get(
TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr);
ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG;
file_format_id = mach_read_from_8(ptr);
mtr_commit(&mtr);
2011-07-14 21:22:41 +02:00
file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
2011-07-14 21:22:41 +02:00
if (file_format_id >= FILE_FORMAT_NAME_N) {
2008-12-03 06:06:00 +01:00
/* Either it has never been tagged, or garbage in it. */
return(ULINT_UNDEFINED);
}
2011-07-14 21:22:41 +02:00
return((ulint) file_format_id);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Get the name representation of the file format from its id.
@return pointer to the name */
UNIV_INTERN
const char*
trx_sys_file_format_id_to_name(
/*===========================*/
2009-09-07 12:22:53 +02:00
const ulint id) /*!< in: id of the file format */
{
ut_a(id < FILE_FORMAT_NAME_N);
return(file_format_name_map[id]);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Check for the max file format tag stored on disk. Note: If max_format_id
2013-12-16 15:38:05 +01:00
is == UNIV_FORMAT_MAX + 1 then we only print a warning.
2009-09-07 12:22:53 +02:00
@return DB_SUCCESS or error code */
UNIV_INTERN
2013-12-16 15:38:05 +01:00
dberr_t
trx_sys_file_format_max_check(
/*==========================*/
2009-09-07 12:22:53 +02:00
ulint max_format_id) /*!< in: max format id to check */
{
ulint format_id;
/* Check the file format in the tablespace. Do not try to
recover if the file format is not supported by the engine
unless forced by the user. */
format_id = trx_sys_file_format_max_read();
2008-12-03 06:06:00 +01:00
if (format_id == ULINT_UNDEFINED) {
/* Format ID was not set. Set it to minimum possible
value. */
2013-12-16 15:38:05 +01:00
format_id = UNIV_FORMAT_MIN;
2008-12-03 06:06:00 +01:00
}
2013-12-16 15:38:05 +01:00
ib_logf(IB_LOG_LEVEL_INFO,
"Highest supported file format is %s.",
trx_sys_file_format_id_to_name(UNIV_FORMAT_MAX));
2013-12-16 15:38:05 +01:00
if (format_id > UNIV_FORMAT_MAX) {
ut_a(format_id < FILE_FORMAT_NAME_N);
2013-12-16 15:38:05 +01:00
ib_logf(max_format_id <= UNIV_FORMAT_MAX
? IB_LOG_LEVEL_ERROR : IB_LOG_LEVEL_WARN,
"The system tablespace is in a file "
"format that this version doesn't support - %s.",
trx_sys_file_format_id_to_name(format_id));
2013-12-16 15:38:05 +01:00
if (max_format_id <= UNIV_FORMAT_MAX) {
return(DB_ERROR);
}
}
format_id = (format_id > max_format_id) ? format_id : max_format_id;
/* We don't need a mutex here, as this function should only
be called once at start up. */
file_format_max.id = format_id;
file_format_max.name = trx_sys_file_format_id_to_name(format_id);
return(DB_SUCCESS);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Set the file format id unconditionally except if it's already the
2009-09-07 12:22:53 +02:00
same value.
@return TRUE if value updated */
UNIV_INTERN
ibool
trx_sys_file_format_max_set(
/*========================*/
2009-09-07 12:22:53 +02:00
ulint format_id, /*!< in: file format id */
const char** name) /*!< out: max file format name or
2008-12-03 06:06:00 +01:00
NULL if not needed. */
{
ibool ret = FALSE;
2013-12-16 15:38:05 +01:00
ut_a(format_id <= UNIV_FORMAT_MAX);
mutex_enter(&file_format_max.mutex);
/* Only update if not already same value. */
if (format_id != file_format_max.id) {
ret = trx_sys_file_format_max_write(format_id, name);
}
mutex_exit(&file_format_max.mutex);
return(ret);
}
2009-09-07 12:22:53 +02:00
/********************************************************************//**
2008-12-03 06:06:00 +01:00
Tags the system table space with minimum format id if it has not been
tagged yet.
WARNING: This function is only called during the startup and AFTER the
redo log application during recovery has finished. */
UNIV_INTERN
void
trx_sys_file_format_tag_init(void)
/*==============================*/
{
ulint format_id;
format_id = trx_sys_file_format_max_read();
/* If format_id is not set then set it to the minimum. */
if (format_id == ULINT_UNDEFINED) {
2013-12-16 15:38:05 +01:00
trx_sys_file_format_max_set(UNIV_FORMAT_MIN, NULL);
2008-12-03 06:06:00 +01:00
}
}
2009-09-07 12:22:53 +02:00
/********************************************************************//**
2008-12-03 06:06:00 +01:00
Update the file format tag in the system tablespace only if the given
2009-09-07 12:22:53 +02:00
format id is greater than the known max id.
@return TRUE if format_id was bigger than the known max id */
UNIV_INTERN
ibool
2008-12-03 06:06:00 +01:00
trx_sys_file_format_max_upgrade(
/*============================*/
2009-09-07 12:22:53 +02:00
const char** name, /*!< out: max file format name */
ulint format_id) /*!< in: file format identifier */
{
ibool ret = FALSE;
ut_a(name);
ut_a(file_format_max.name != NULL);
2013-12-16 15:38:05 +01:00
ut_a(format_id <= UNIV_FORMAT_MAX);
mutex_enter(&file_format_max.mutex);
if (format_id > file_format_max.id) {
ret = trx_sys_file_format_max_write(format_id, name);
}
mutex_exit(&file_format_max.mutex);
return(ret);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Get the name representation of the file format from its id.
@return pointer to the max format name */
UNIV_INTERN
const char*
trx_sys_file_format_max_get(void)
/*=============================*/
{
return(file_format_max.name);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Initializes the tablespace tag system. */
UNIV_INTERN
void
trx_sys_file_format_init(void)
/*==========================*/
{
2011-07-14 21:22:41 +02:00
mutex_create(file_format_max_mutex_key,
&file_format_max.mutex, SYNC_FILE_FORMAT_TAG);
/* We don't need a mutex here, as this function should only
be called once at start up. */
2013-12-16 15:38:05 +01:00
file_format_max.id = UNIV_FORMAT_MIN;
file_format_max.name = trx_sys_file_format_id_to_name(
file_format_max.id);
}
2009-09-07 12:22:53 +02:00
/*****************************************************************//**
Closes the tablespace tag system. */
UNIV_INTERN
void
trx_sys_file_format_close(void)
/*===========================*/
{
/* Does nothing at the moment */
}
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
/*********************************************************************
Creates the rollback segments.
@return number of rollback segments that are active. */
2011-07-14 21:22:41 +02:00
UNIV_INTERN
2013-12-16 15:38:05 +01:00
ulint
trx_sys_create_rsegs(
2011-07-14 21:22:41 +02:00
/*=================*/
2013-12-16 15:38:05 +01:00
ulint n_spaces, /*!< number of tablespaces for UNDO logs */
ulint n_rsegs) /*!< number of rollback segments to create */
2011-07-14 21:22:41 +02:00
{
mtr_t mtr;
2013-12-16 15:38:05 +01:00
ulint n_used;
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
ut_a(n_spaces < TRX_SYS_N_RSEGS);
ut_a(n_rsegs <= TRX_SYS_N_RSEGS);
2011-07-14 21:22:41 +02:00
2014-02-26 16:25:11 +01:00
if (srv_read_only_mode) {
2013-12-16 15:38:05 +01:00
return(ULINT_UNDEFINED);
}
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
/* This is executed in single-threaded mode therefore it is not
necessary to use the same mtr in trx_rseg_create(). n_used cannot
change while the function is executing. */
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
mtr_start(&mtr);
n_used = trx_sysf_rseg_find_free(&mtr);
2011-07-14 21:22:41 +02:00
mtr_commit(&mtr);
2013-12-16 15:38:05 +01:00
if (n_used == ULINT_UNDEFINED) {
n_used = TRX_SYS_N_RSEGS;
}
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
/* Do not create additional rollback segments if innodb_force_recovery
has been set and the database was not shutdown cleanly. */
if (!srv_force_recovery && !recv_needed_recovery && n_used < n_rsegs) {
2011-07-14 21:22:41 +02:00
ulint i;
2013-12-16 15:38:05 +01:00
ulint new_rsegs = n_rsegs - n_used;
for (i = 0; i < new_rsegs; ++i) {
2017-05-16 13:16:11 +02:00
ulint space_id;
space_id = (n_spaces == 0) ? 0
: (srv_undo_space_id_start + i % n_spaces);
2011-07-14 21:22:41 +02:00
2017-05-16 13:16:11 +02:00
/* Tablespace 0 is the system tablespace. */
if (trx_rseg_create(space_id) != NULL) {
2013-12-16 15:38:05 +01:00
++n_used;
2011-07-14 21:22:41 +02:00
} else {
break;
}
}
}
2013-12-16 15:38:05 +01:00
ib_logf(IB_LOG_LEVEL_INFO,
"%lu rollback segment(s) are active.", n_used);
return(n_used);
2011-07-14 21:22:41 +02:00
}
2009-09-07 12:22:53 +02:00
#else /* !UNIV_HOTBACKUP */
/*****************************************************************//**
Prints to stderr the MySQL binlog info in the system header if the
magic number shows it valid. */
UNIV_INTERN
void
trx_sys_print_mysql_binlog_offset_from_page(
/*========================================*/
const byte* page) /*!< in: buffer containing the trx
system header page, i.e., page number
TRX_SYS_PAGE_NO in the tablespace */
{
const trx_sysf_t* sys_header;
sys_header = page + TRX_SYS;
if (mach_read_from_4(sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_MAGIC_N_FLD)
== TRX_SYS_MYSQL_LOG_MAGIC_N) {
fprintf(stderr,
2014-09-11 10:15:01 +02:00
"mysqlbackup: Last MySQL binlog file position %lu %lu,"
2009-09-07 12:22:53 +02:00
" file name %s\n",
(ulong) mach_read_from_4(
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_HIGH),
(ulong) mach_read_from_4(
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_OFFSET_LOW),
sys_header + TRX_SYS_MYSQL_LOG_INFO
+ TRX_SYS_MYSQL_LOG_NAME);
}
}
/*****************************************************************//**
Reads the file format id from the first system table space file.
Even if the call succeeds and returns TRUE, the returned format id
may be ULINT_UNDEFINED signalling that the format id was not present
in the data file.
@return TRUE if call succeeds */
UNIV_INTERN
ibool
trx_sys_read_file_format_id(
/*========================*/
const char *pathname, /*!< in: pathname of the first system
table space file */
ulint *format_id) /*!< out: file format of the system table
space */
{
os_file_t file;
ibool success;
byte buf[UNIV_PAGE_SIZE * 2];
page_t* page = ut_align(buf, UNIV_PAGE_SIZE);
const byte* ptr;
2011-07-14 21:22:41 +02:00
ib_id_t file_format_id;
2009-09-07 12:22:53 +02:00
*format_id = ULINT_UNDEFINED;
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
file = os_file_create_simple_no_error_handling(
2011-07-14 21:22:41 +02:00
innodb_file_data_key,
2009-09-07 12:22:53 +02:00
pathname,
OS_FILE_OPEN,
OS_FILE_READ_ONLY,
&success
);
if (!success) {
/* The following call prints an error message */
2013-12-16 15:38:05 +01:00
os_file_get_last_error(true);
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
ut_print_timestamp(stderr);
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
fprintf(stderr,
2014-09-11 10:15:01 +02:00
" mysqlbackup: Error: trying to read system "
"tablespace file format,\n"
" mysqlbackup: but could not open the tablespace "
2013-12-16 15:38:05 +01:00
"file %s!\n", pathname);
2009-09-07 12:22:53 +02:00
return(FALSE);
}
/* Read the page on which file format is stored */
success = os_file_read_no_error_handling(
2013-12-16 15:38:05 +01:00
file, page, TRX_SYS_PAGE_NO * UNIV_PAGE_SIZE, UNIV_PAGE_SIZE);
2009-09-07 12:22:53 +02:00
if (!success) {
/* The following call prints an error message */
2013-12-16 15:38:05 +01:00
os_file_get_last_error(true);
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
ut_print_timestamp(stderr);
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
fprintf(stderr,
2014-09-11 10:15:01 +02:00
" mysqlbackup: Error: trying to read system "
"tablespace file format,\n"
" mysqlbackup: but failed to read the tablespace "
2013-12-16 15:38:05 +01:00
"file %s!\n", pathname);
2009-09-07 12:22:53 +02:00
os_file_close(file);
return(FALSE);
}
os_file_close(file);
/* get the file format from the page */
ptr = page + TRX_SYS_FILE_FORMAT_TAG;
file_format_id = mach_read_from_8(ptr);
2011-07-14 21:22:41 +02:00
file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N;
2009-09-07 12:22:53 +02:00
2011-07-14 21:22:41 +02:00
if (file_format_id >= FILE_FORMAT_NAME_N) {
2009-09-07 12:22:53 +02:00
/* Either it has never been tagged, or garbage in it. */
return(TRUE);
}
2011-07-14 21:22:41 +02:00
*format_id = (ulint) file_format_id;
2009-09-07 12:22:53 +02:00
return(TRUE);
}
/*****************************************************************//**
Reads the file format id from the given per-table data file.
@return TRUE if call succeeds */
UNIV_INTERN
ibool
trx_sys_read_pertable_file_format_id(
/*=================================*/
const char *pathname, /*!< in: pathname of a per-table
datafile */
ulint *format_id) /*!< out: file format of the per-table
data file */
{
os_file_t file;
ibool success;
byte buf[UNIV_PAGE_SIZE * 2];
page_t* page = ut_align(buf, UNIV_PAGE_SIZE);
const byte* ptr;
ib_uint32_t flags;
*format_id = ULINT_UNDEFINED;
2011-07-14 21:22:41 +02:00
2009-09-07 12:22:53 +02:00
file = os_file_create_simple_no_error_handling(
2011-07-14 21:22:41 +02:00
innodb_file_data_key,
2009-09-07 12:22:53 +02:00
pathname,
OS_FILE_OPEN,
OS_FILE_READ_ONLY,
&success
);
if (!success) {
/* The following call prints an error message */
2013-12-16 15:38:05 +01:00
os_file_get_last_error(true);
2009-09-07 12:22:53 +02:00
ut_print_timestamp(stderr);
2013-12-16 15:38:05 +01:00
2009-09-07 12:22:53 +02:00
fprintf(stderr,
2014-09-11 10:15:01 +02:00
" mysqlbackup: Error: trying to read per-table "
2013-12-16 15:38:05 +01:00
"tablespace format,\n"
2014-09-11 10:15:01 +02:00
" mysqlbackup: but could not open the tablespace "
2013-12-16 15:38:05 +01:00
"file %s!\n", pathname);
2009-09-07 12:22:53 +02:00
return(FALSE);
}
/* Read the first page of the per-table datafile */
2013-12-16 15:38:05 +01:00
success = os_file_read_no_error_handling(file, page, 0, UNIV_PAGE_SIZE);
2009-09-07 12:22:53 +02:00
if (!success) {
/* The following call prints an error message */
2013-12-16 15:38:05 +01:00
os_file_get_last_error(true);
2009-09-07 12:22:53 +02:00
ut_print_timestamp(stderr);
2013-12-16 15:38:05 +01:00
2009-09-07 12:22:53 +02:00
fprintf(stderr,
2014-09-11 10:15:01 +02:00
" mysqlbackup: Error: trying to per-table data file "
2013-12-16 15:38:05 +01:00
"format,\n"
2014-09-11 10:15:01 +02:00
" mysqlbackup: but failed to read the tablespace "
2013-12-16 15:38:05 +01:00
"file %s!\n", pathname);
2009-09-07 12:22:53 +02:00
os_file_close(file);
return(FALSE);
}
os_file_close(file);
/* get the file format from the page */
ptr = page + 54;
flags = mach_read_from_4(ptr);
2015-12-13 10:13:18 +01:00
if (!fsp_flags_is_valid(flags) {
2009-09-07 12:22:53 +02:00
/* bad tablespace flags */
return(FALSE);
}
2015-12-13 10:13:18 +01:00
*format_id = FSP_FLAGS_GET_POST_ANTELOPE(flags);
return(TRUE);
2009-09-07 12:22:53 +02:00
}
/*****************************************************************//**
Get the name representation of the file format from its id.
@return pointer to the name */
UNIV_INTERN
const char*
trx_sys_file_format_id_to_name(
/*===========================*/
const ulint id) /*!< in: id of the file format */
{
if (!(id < FILE_FORMAT_NAME_N)) {
/* unknown id */
2013-12-16 15:38:05 +01:00
return("Unknown");
2009-09-07 12:22:53 +02:00
}
return(file_format_name_map[id]);
}
#endif /* !UNIV_HOTBACKUP */
2010-01-06 13:00:14 +01:00
#ifndef UNIV_HOTBACKUP
2010-01-06 13:00:14 +01:00
/*********************************************************************
Shutdown/Close the transaction system. */
UNIV_INTERN
void
trx_sys_close(void)
/*===============*/
{
2013-12-16 15:38:05 +01:00
ulint i;
2011-07-14 21:22:41 +02:00
trx_t* trx;
2010-01-06 13:00:14 +01:00
read_view_t* view;
ut_ad(trx_sys != NULL);
2011-07-14 21:22:41 +02:00
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
2010-01-06 13:00:14 +01:00
/* Check that all read views are closed except read view owned
by a purge. */
2013-12-16 15:38:05 +01:00
mutex_enter(&trx_sys->mutex);
2010-01-06 13:00:14 +01:00
if (UT_LIST_GET_LEN(trx_sys->view_list) > 1) {
fprintf(stderr,
"InnoDB: Error: all read views were not closed"
" before shutdown:\n"
"InnoDB: %lu read views open \n",
UT_LIST_GET_LEN(trx_sys->view_list) - 1);
}
2013-12-16 15:38:05 +01:00
mutex_exit(&trx_sys->mutex);
2010-01-06 13:00:14 +01:00
sess_close(trx_dummy_sess);
trx_dummy_sess = NULL;
trx_purge_sys_close();
/* Free the double write data structures. */
if (buf_dblwr) {
buf_dblwr_free();
}
2010-01-06 13:00:14 +01:00
2011-07-14 21:22:41 +02:00
/* Only prepared transactions may be left in the system. Free them. */
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx
|| srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| (IS_XTRABACKUP() && srv_apply_log_only));
2011-07-14 21:22:41 +02:00
2013-12-16 15:38:05 +01:00
while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) {
2011-07-14 21:22:41 +02:00
trx_free_prepared(trx);
}
2010-01-06 13:00:14 +01:00
/* There can't be any active transactions. */
2013-12-16 15:38:05 +01:00
for (i = 0; i < TRX_SYS_N_RSEGS; ++i) {
trx_rseg_t* rseg;
2010-01-06 13:00:14 +01:00
2013-12-16 15:38:05 +01:00
rseg = trx_sys->rseg_array[i];
2010-01-06 13:00:14 +01:00
2013-12-16 15:38:05 +01:00
if (rseg != NULL) {
trx_rseg_mem_free(rseg);
} else {
break;
}
2010-01-06 13:00:14 +01:00
}
view = UT_LIST_GET_FIRST(trx_sys->view_list);
while (view != NULL) {
read_view_t* prev_view = view;
view = UT_LIST_GET_NEXT(view_list, prev_view);
/* Views are allocated from the trx_sys->global_read_view_heap.
So, we simply remove the element here. */
UT_LIST_REMOVE(view_list, trx_sys->view_list, prev_view);
}
if (!IS_XTRABACKUP() || !srv_apply_log_only) {
ut_a(UT_LIST_GET_LEN(trx_sys->view_list) == 0);
ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == 0);
ut_a(UT_LIST_GET_LEN(trx_sys->mysql_trx_list) == 0);
}
2010-01-06 13:00:14 +01:00
2013-12-16 15:38:05 +01:00
mutex_free(&trx_sys->mutex);
2013-05-08 09:52:54 +02:00
ut_ad(trx_sys->descr_n_used == 0);
ut_free(trx_sys->descriptors);
2010-01-06 13:00:14 +01:00
mem_free(trx_sys);
trx_sys = NULL;
}
2013-12-16 15:38:05 +01:00
2017-03-04 19:49:14 +01:00
/** @brief Convert an undo log to TRX_UNDO_PREPARED state on shutdown.
If any prepared ACTIVE transactions exist, and their rollback was
prevented by innodb_force_recovery, we convert these transactions to
XA PREPARE state in the main-memory data structures, so that shutdown
will proceed normally. These transactions will again recover as ACTIVE
on the next restart, and they will be rolled back unless
innodb_force_recovery prevents it again.
@param[in] trx transaction
@param[in,out] undo undo log to convert to TRX_UNDO_PREPARED */
static
void
trx_undo_fake_prepared(
const trx_t* trx,
trx_undo_t* undo)
{
ut_ad(srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
ut_ad(trx->is_recovered);
if (undo != NULL) {
ut_ad(undo->state == TRX_UNDO_ACTIVE);
undo->state = TRX_UNDO_PREPARED;
}
}
2013-12-16 15:38:05 +01:00
/*********************************************************************
Check if there are any active (non-prepared) transactions.
@return total number of active transactions or 0 if none */
UNIV_INTERN
ulint
trx_sys_any_active_transactions(void)
/*=================================*/
{
if (IS_XTRABACKUP() && srv_apply_log_only) {
return(0);
}
2013-12-16 15:38:05 +01:00
mutex_enter(&trx_sys->mutex);
2017-03-04 19:49:14 +01:00
ulint total_trx = UT_LIST_GET_LEN(trx_sys->mysql_trx_list);
if (total_trx == 0) {
total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
ut_a(total_trx >= trx_sys->n_prepared_trx);
if (total_trx > trx_sys->n_prepared_trx
&& srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) {
for (trx_t* trx = UT_LIST_GET_FIRST(
trx_sys->rw_trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
if (!trx_state_eq(trx, TRX_STATE_ACTIVE)
|| !trx->is_recovered) {
continue;
}
/* This was a recovered transaction
whose rollback was disabled by
the innodb_force_recovery setting.
Pretend that it is in XA PREPARE
state so that shutdown will work. */
trx_undo_fake_prepared(
trx, trx->insert_undo);
trx_undo_fake_prepared(
trx, trx->update_undo);
trx->state = TRX_STATE_PREPARED;
trx_sys->n_prepared_trx++;
trx_sys->n_prepared_recovered_trx++;
}
}
2013-12-16 15:38:05 +01:00
2017-03-04 19:49:14 +01:00
ut_a(total_trx >= trx_sys->n_prepared_trx);
total_trx -= trx_sys->n_prepared_trx;
}
2013-12-16 15:38:05 +01:00
mutex_exit(&trx_sys->mutex);
return(total_trx);
}
#ifdef UNIV_DEBUG
/*************************************************************//**
Validate the trx_list_t.
@return TRUE if valid. */
static
ibool
trx_sys_validate_trx_list_low(
/*===========================*/
trx_list_t* trx_list) /*!< in: &trx_sys->ro_trx_list
or &trx_sys->rw_trx_list */
{
const trx_t* trx;
const trx_t* prev_trx = NULL;
ut_ad(mutex_own(&trx_sys->mutex));
ut_ad(trx_list == &trx_sys->ro_trx_list
|| trx_list == &trx_sys->rw_trx_list);
for (trx = UT_LIST_GET_FIRST(*trx_list);
trx != NULL;
prev_trx = trx, trx = UT_LIST_GET_NEXT(trx_list, prev_trx)) {
assert_trx_in_list(trx);
ut_ad(trx->read_only == (trx_list == &trx_sys->ro_trx_list));
ut_a(prev_trx == NULL || prev_trx->id > trx->id);
}
return(TRUE);
}
/*************************************************************//**
Validate the trx_sys_t::ro_trx_list and trx_sys_t::rw_trx_list.
@return TRUE if lists are valid. */
UNIV_INTERN
ibool
trx_sys_validate_trx_list(void)
/*===========================*/
{
ut_ad(mutex_own(&trx_sys->mutex));
ut_a(trx_sys_validate_trx_list_low(&trx_sys->ro_trx_list));
ut_a(trx_sys_validate_trx_list_low(&trx_sys->rw_trx_list));
return(TRUE);
}
#endif /* UNIV_DEBUG */
#endif /* !UNIV_HOTBACKUP */