mirror of
https://github.com/MariaDB/server.git
synced 2025-01-19 13:32:33 +01:00
baddc4e91c
innodb-5.1-ss1318 innodb-5.1-ss1330 innodb-5.1-ss1332 innodb-5.1-ss1340 Fixes: - Bug #21409: Incorrect result returned when in READ-COMMITTED with query_cache ON At low transaction isolation levels we let each consistent read set its own snapshot. - Bug #23666: strange Innodb_row_lock_time_% values in show status; also millisecs wrong On Windows ut_usectime returns secs and usecs relative to the UNIX epoch (which is Jan, 1 1970). - Bug #25494: LATEST DEADLOCK INFORMATION is not always cleared lock_deadlock_recursive(): When the search depth or length is exceeded, rewind lock_latest_err_file and display the two transactions at the point of aborting the search. - Bug #25927: Foreign key with ON DELETE SET NULL on NOT NULL can crash server Prevent ALTER TABLE ... MODIFY ... NOT NULL on columns for which there is a foreign key constraint ON ... SET NULL. - Bug #26835: Repeatable corruption of utf8-enabled tables inside InnoDB The bug could be reproduced as follows: Define a table so that the first column of the clustered index is a VARCHAR or a UTF-8 CHAR in a collation where sequences of bytes of differing length are considered equivalent. Insert and delete a record. Before the delete-marked record is purged, insert another record whose first column is of different length but equivalent to the first record. Under certain conditions, the insertion can be incorrectly performed as update-in-place. Likewise, an operation that could be done as update-in-place can unnecessarily be performed as delete and insert, but that would not cause corruption but merely degraded performance.
398 lines
9.6 KiB
Text
398 lines
9.6 KiB
Text
/******************************************************
|
|
Database log
|
|
|
|
(c) 1995 Innobase Oy
|
|
|
|
Created 12/9/1995 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "os0file.h"
|
|
#include "mach0data.h"
|
|
#include "mtr0mtr.h"
|
|
|
|
/**********************************************************
|
|
Checks by parsing that the catenated log segment for a single mtr is
|
|
consistent. */
|
|
|
|
ibool
|
|
log_check_log_recs(
|
|
/*===============*/
|
|
byte* buf, /* in: pointer to the start of the log segment
|
|
in the log_sys->buf log buffer */
|
|
ulint len, /* in: segment length in bytes */
|
|
dulint buf_start_lsn); /* in: buffer start lsn */
|
|
|
|
/****************************************************************
|
|
Gets a log block flush bit. */
|
|
UNIV_INLINE
|
|
ibool
|
|
log_block_get_flush_bit(
|
|
/*====================*/
|
|
/* out: TRUE if this block was the first
|
|
to be written in a log flush */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
if (LOG_BLOCK_FLUSH_BIT_MASK
|
|
& mach_read_from_4(log_block + LOG_BLOCK_HDR_NO)) {
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets the log block flush bit. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_flush_bit(
|
|
/*====================*/
|
|
byte* log_block, /* in: log block */
|
|
ibool val) /* in: value to set */
|
|
{
|
|
ulint field;
|
|
|
|
field = mach_read_from_4(log_block + LOG_BLOCK_HDR_NO);
|
|
|
|
if (val) {
|
|
field = field | LOG_BLOCK_FLUSH_BIT_MASK;
|
|
} else {
|
|
field = field & ~LOG_BLOCK_FLUSH_BIT_MASK;
|
|
}
|
|
|
|
mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, field);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets a log block number stored in the header. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_get_hdr_no(
|
|
/*=================*/
|
|
/* out: log block number stored in the block
|
|
header */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
return(~LOG_BLOCK_FLUSH_BIT_MASK
|
|
& mach_read_from_4(log_block + LOG_BLOCK_HDR_NO));
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets the log block number stored in the header; NOTE that this must be set
|
|
before the flush bit! */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_hdr_no(
|
|
/*=================*/
|
|
byte* log_block, /* in: log block */
|
|
ulint n) /* in: log block number: must be > 0 and
|
|
< LOG_BLOCK_FLUSH_BIT_MASK */
|
|
{
|
|
ut_ad(n > 0);
|
|
ut_ad(n < LOG_BLOCK_FLUSH_BIT_MASK);
|
|
|
|
mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, n);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets a log block data length. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_get_data_len(
|
|
/*===================*/
|
|
/* out: log block data length measured as a
|
|
byte offset from the block start */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
return(mach_read_from_2(log_block + LOG_BLOCK_HDR_DATA_LEN));
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets the log block data length. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_data_len(
|
|
/*===================*/
|
|
byte* log_block, /* in: log block */
|
|
ulint len) /* in: data length */
|
|
{
|
|
mach_write_to_2(log_block + LOG_BLOCK_HDR_DATA_LEN, len);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets a log block first mtr log record group offset. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_get_first_rec_group(
|
|
/*==========================*/
|
|
/* out: first mtr log record group byte offset
|
|
from the block start, 0 if none */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
return(mach_read_from_2(log_block + LOG_BLOCK_FIRST_REC_GROUP));
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets the log block first mtr log record group offset. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_first_rec_group(
|
|
/*==========================*/
|
|
byte* log_block, /* in: log block */
|
|
ulint offset) /* in: offset, 0 if none */
|
|
{
|
|
mach_write_to_2(log_block + LOG_BLOCK_FIRST_REC_GROUP, offset);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets a log block checkpoint number field (4 lowest bytes). */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_get_checkpoint_no(
|
|
/*========================*/
|
|
/* out: checkpoint no (4 lowest bytes) */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
return(mach_read_from_4(log_block + LOG_BLOCK_CHECKPOINT_NO));
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets a log block checkpoint number field (4 lowest bytes). */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_checkpoint_no(
|
|
/*========================*/
|
|
byte* log_block, /* in: log block */
|
|
dulint no) /* in: checkpoint no */
|
|
{
|
|
mach_write_to_4(log_block + LOG_BLOCK_CHECKPOINT_NO,
|
|
ut_dulint_get_low(no));
|
|
}
|
|
|
|
/****************************************************************
|
|
Converts a lsn to a log block number. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_convert_lsn_to_no(
|
|
/*========================*/
|
|
/* out: log block number, it is > 0 and <= 1G */
|
|
dulint lsn) /* in: lsn of a byte within the block */
|
|
{
|
|
ulint no;
|
|
|
|
no = ut_dulint_get_low(lsn) / OS_FILE_LOG_BLOCK_SIZE;
|
|
no += (ut_dulint_get_high(lsn) % OS_FILE_LOG_BLOCK_SIZE)
|
|
* 2 * (0x80000000UL / OS_FILE_LOG_BLOCK_SIZE);
|
|
|
|
no = no & 0x3FFFFFFFUL;
|
|
|
|
return(no + 1);
|
|
}
|
|
|
|
/****************************************************************
|
|
Calculates the checksum for a log block. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_calc_checksum(
|
|
/*====================*/
|
|
/* out: checksum */
|
|
byte* block) /* in: log block */
|
|
{
|
|
ulint sum;
|
|
ulint sh;
|
|
ulint i;
|
|
|
|
sum = 1;
|
|
sh = 0;
|
|
|
|
for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; i++) {
|
|
sum = sum & 0x7FFFFFFFUL;
|
|
sum += (((ulint)(*(block + i))) << sh) + (ulint)(*(block + i));
|
|
sh++;
|
|
if (sh > 24) {
|
|
sh = 0;
|
|
}
|
|
}
|
|
|
|
return(sum);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets a log block checksum field value. */
|
|
UNIV_INLINE
|
|
ulint
|
|
log_block_get_checksum(
|
|
/*===================*/
|
|
/* out: checksum */
|
|
byte* log_block) /* in: log block */
|
|
{
|
|
return(mach_read_from_4(log_block + OS_FILE_LOG_BLOCK_SIZE
|
|
- LOG_BLOCK_CHECKSUM));
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets a log block checksum field value. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_set_checksum(
|
|
/*===================*/
|
|
byte* log_block, /* in: log block */
|
|
ulint checksum) /* in: checksum */
|
|
{
|
|
mach_write_to_4(log_block + OS_FILE_LOG_BLOCK_SIZE
|
|
- LOG_BLOCK_CHECKSUM,
|
|
checksum);
|
|
}
|
|
|
|
/****************************************************************
|
|
Initializes a log block in the log buffer. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_init(
|
|
/*===========*/
|
|
byte* log_block, /* in: pointer to the log buffer */
|
|
dulint lsn) /* in: lsn within the log block */
|
|
{
|
|
ulint no;
|
|
|
|
ut_ad(mutex_own(&(log_sys->mutex)));
|
|
|
|
no = log_block_convert_lsn_to_no(lsn);
|
|
|
|
log_block_set_hdr_no(log_block, no);
|
|
|
|
log_block_set_data_len(log_block, LOG_BLOCK_HDR_SIZE);
|
|
log_block_set_first_rec_group(log_block, 0);
|
|
}
|
|
|
|
/****************************************************************
|
|
Initializes a log block in the log buffer in the old format, where there
|
|
was no checksum yet. */
|
|
UNIV_INLINE
|
|
void
|
|
log_block_init_in_old_format(
|
|
/*=========================*/
|
|
byte* log_block, /* in: pointer to the log buffer */
|
|
dulint lsn) /* in: lsn within the log block */
|
|
{
|
|
ulint no;
|
|
|
|
ut_ad(mutex_own(&(log_sys->mutex)));
|
|
|
|
no = log_block_convert_lsn_to_no(lsn);
|
|
|
|
log_block_set_hdr_no(log_block, no);
|
|
mach_write_to_4(log_block + OS_FILE_LOG_BLOCK_SIZE
|
|
- LOG_BLOCK_CHECKSUM, no);
|
|
log_block_set_data_len(log_block, LOG_BLOCK_HDR_SIZE);
|
|
log_block_set_first_rec_group(log_block, 0);
|
|
}
|
|
|
|
/****************************************************************
|
|
Writes to the log the string given. The log must be released with
|
|
log_release. */
|
|
UNIV_INLINE
|
|
dulint
|
|
log_reserve_and_write_fast(
|
|
/*=======================*/
|
|
/* out: end lsn of the log record, ut_dulint_zero if
|
|
did not succeed */
|
|
byte* str, /* in: string */
|
|
ulint len, /* in: string length */
|
|
dulint* start_lsn,/* out: start lsn of the log record */
|
|
ibool* success)/* out: TRUE if success */
|
|
{
|
|
log_t* log = log_sys;
|
|
ulint data_len;
|
|
dulint lsn;
|
|
|
|
*success = TRUE;
|
|
|
|
mutex_enter(&(log->mutex));
|
|
|
|
data_len = len + log->buf_free % OS_FILE_LOG_BLOCK_SIZE;
|
|
|
|
if (data_len >= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) {
|
|
|
|
/* The string does not fit within the current log block
|
|
or the log block would become full */
|
|
|
|
*success = FALSE;
|
|
|
|
mutex_exit(&(log->mutex));
|
|
|
|
return(ut_dulint_zero);
|
|
}
|
|
|
|
*start_lsn = log->lsn;
|
|
|
|
ut_memcpy(log->buf + log->buf_free, str, len);
|
|
|
|
log_block_set_data_len(ut_align_down(log->buf + log->buf_free,
|
|
OS_FILE_LOG_BLOCK_SIZE),
|
|
data_len);
|
|
#ifdef UNIV_LOG_DEBUG
|
|
log->old_buf_free = log->buf_free;
|
|
log->old_lsn = log->lsn;
|
|
#endif
|
|
log->buf_free += len;
|
|
|
|
ut_ad(log->buf_free <= log->buf_size);
|
|
|
|
lsn = ut_dulint_add(log->lsn, len);
|
|
|
|
log->lsn = lsn;
|
|
|
|
#ifdef UNIV_LOG_DEBUG
|
|
log_check_log_recs(log->buf + log->old_buf_free,
|
|
log->buf_free - log->old_buf_free, log->old_lsn);
|
|
#endif
|
|
return(lsn);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Releases the log mutex. */
|
|
UNIV_INLINE
|
|
void
|
|
log_release(void)
|
|
/*=============*/
|
|
{
|
|
mutex_exit(&(log_sys->mutex));
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets the current lsn. */
|
|
UNIV_INLINE
|
|
dulint
|
|
log_get_lsn(void)
|
|
/*=============*/
|
|
/* out: current lsn */
|
|
{
|
|
dulint lsn;
|
|
|
|
mutex_enter(&(log_sys->mutex));
|
|
|
|
lsn = log_sys->lsn;
|
|
|
|
mutex_exit(&(log_sys->mutex));
|
|
|
|
return(lsn);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Checks if there is need for a log buffer flush or a new checkpoint, and does
|
|
this if yes. Any database operation should call this when it has modified
|
|
more than about 4 pages. NOTE that this function may only be called when the
|
|
OS thread owns no synchronization objects except the dictionary mutex. */
|
|
UNIV_INLINE
|
|
void
|
|
log_free_check(void)
|
|
/*================*/
|
|
{
|
|
/* ut_ad(sync_thread_levels_empty()); */
|
|
|
|
if (log_sys->check_flush_or_checkpoint) {
|
|
|
|
log_check_margins();
|
|
}
|
|
}
|