MDEV-17794 Do not assign persistent ID for temporary tables

InnoDB in MySQL 5.7 introduced two new parameters to the function
dict_hdr_get_new_id(), to allow redo logging to be disabled when
assigning identifiers to temporary tables or during the
backup-unfriendly TRUNCATE TABLE that was replaced in MariaDB
by MDEV-13564.

Now that MariaDB 10.4.0 removed the crash recovery code for the
backup-unfriendly TRUNCATE, we can revert dict_hdr_get_new_id()
to be used only for persistent data structures.

dict_table_assign_new_id(): Remove. This was a simple 2-line function
that was called from few places.

dict_table_open_on_id_low(): Declare in the only file where it
is called.

dict_sys_t::temp_id_hash: A separate lookup table for temporary tables.
Table names will be in the common dict_sys_t::table_hash.

dict_sys_t::get_temporary_table_id(): Assign a temporary table ID.

dict_sys_t::get_table(): Look up a persistent table.

dict_sys_t::get_temporary_table(): Look up a temporary table.

dict_sys_t::temp_table_id: The sequence of temporary table identifiers.
Starts from DICT_HDR_FIRST_ID, so that we can continue to simply compare
dict_table_t::id to a few constants for the persistent hard-coded
data dictionary tables.

undo_node_t::state: Distinguish temporary and persistent tables.

lock_check_dict_lock(), lock_get_table_id(): Assert that there cannot
be locks on temporary tables.

row_rec_to_index_entry_impl(): Assert that there cannot be metadata
records on temporary tables.

row_undo_ins_parse_undo_rec(): Distinguish temporary and persistent tables.
Move some assertions from the only caller. Return whether the table was
found.

row_undo_ins(): Add some assertions.

row_undo_mod_clust(), row_undo_mod(): Do not assign node->state.
Let row_undo() do that.

row_undo_mod_parse_undo_rec(): Distinguish temporary and persistent tables.
Move some assertions from the only caller. Return whether the table was
found.

row_undo_try_truncate(): Renamed and simplified from trx_roll_try_truncate().

row_undo_rec_get(): Replaces trx_roll_pop_top_rec_of_trx() and
trx_roll_pop_top_rec(). Fetch an undo log record, and assign undo->state
accordingly.

trx_undo_truncate_end(): Acquire the rseg->mutex only for the minimum
required duration, and release it between mini-transactions.
This commit is contained in:
Marko Mäkelä 2018-11-22 15:36:50 +02:00
parent 23ff318d03
commit 4be0855cf5
25 changed files with 450 additions and 550 deletions

View File

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, MariaDB Corporation.
Copyright (c) 2016, 2018, 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
@ -64,52 +64,14 @@ dict_hdr_get_new_id(
(not assigned if NULL) */
index_id_t* index_id, /*!< out: index id
(not assigned if NULL) */
ulint* space_id, /*!< out: space id
ulint* space_id) /*!< out: space id
(not assigned if NULL) */
const dict_table_t* table, /*!< in: table */
bool disable_redo) /*!< in: if true and table
object is NULL
then disable-redo */
{
dict_hdr_t* dict_hdr;
ib_id_t id;
mtr_t mtr;
mtr_start(&mtr);
if (table) {
if (table->is_temporary()) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
} else if (disable_redo) {
/* In non-read-only mode we need to ensure that space-id header
page is written to disk else if page is removed from buffer
cache and re-loaded it would assign temporary tablespace id
to another tablespace.
This is not a case with read-only mode as there is no new object
that is created except temporary tablespace. */
mtr.set_log_mode(srv_read_only_mode
? MTR_LOG_NONE : MTR_LOG_NO_REDO);
}
/* Server started and let's say space-id = x
- table created with file-per-table
- space-id = x + 1
- crash
Case 1: If it was redo logged then we know that it will be
restored to x + 1
Case 2: if not redo-logged
Header will have the old space-id = x
This is OK because on restart there is no object with
space id = x + 1
Case 3:
space-id = x (on start)
space-id = x+1 (temp-table allocation) - no redo logging
space-id = x+2 (non-temp-table allocation), this get's
redo logged.
If there is a crash there will be only 2 entries
x (original) and x+2 (new) and disk hdr will be updated
to reflect x + 2 entry.
We cannot allocate the same space id to different objects. */
dict_hdr = dict_hdr_get(&mtr);
if (table_id) {

View File

@ -352,10 +352,12 @@ dict_build_table_def_step(
{
ut_ad(mutex_own(&dict_sys->mutex));
dict_table_t* table = node->table;
trx_t* trx = thr_get_trx(thr);
ut_ad(!table->is_temporary());
ut_ad(!table->space);
ut_ad(table->space_id == ULINT_UNDEFINED);
dict_table_assign_new_id(table, thr_get_trx(thr));
dict_hdr_get_new_id(&table->id, NULL, NULL);
trx->table_id = table->id;
/* Always set this bit for all new created tables */
DICT_TF2_FLAG_SET(table, DICT_TF2_FTS_AUX_HEX_NAME);
@ -368,8 +370,6 @@ dict_build_table_def_step(
ut_ad(DICT_TF_GET_ZIP_SSIZE(table->flags) == 0
|| dict_table_has_atomic_blobs(table));
trx_t* trx = thr_get_trx(thr);
ut_ad(trx->table_id);
mtr_t mtr;
trx_undo_t* undo = trx->rsegs.m_redo.undo;
if (undo && !undo->table_id
@ -397,7 +397,7 @@ dict_build_table_def_step(
}
/* Get a new tablespace ID */
ulint space_id;
dict_hdr_get_new_id(NULL, NULL, &space_id, table, false);
dict_hdr_get_new_id(NULL, NULL, &space_id);
DBUG_EXECUTE_IF(
"ib_create_table_fail_out_of_space_ids",
@ -745,7 +745,7 @@ dict_build_index_def_step(
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0)
|| dict_index_is_clust(index));
dict_hdr_get_new_id(NULL, &index->id, NULL, table, false);
dict_hdr_get_new_id(NULL, &index->id, NULL);
/* Inherit the space id from the table; we store all indexes of a
table in the same tablespace */
@ -785,7 +785,7 @@ dict_build_index_def(
ut_ad((UT_LIST_GET_LEN(table->indexes) > 0)
|| dict_index_is_clust(index));
dict_hdr_get_new_id(NULL, &index->id, NULL, table, false);
dict_hdr_get_new_id(NULL, &index->id, NULL);
/* Note that the index was created by this transaction. */
index->trx_id = trx->id;
@ -2376,15 +2376,3 @@ dict_delete_tablespace_and_datafiles(
return(err);
}
/** Assign a new table ID and put it into the table cache and the transaction.
@param[in,out] table Table that needs an ID
@param[in,out] trx Transaction */
void
dict_table_assign_new_id(
dict_table_t* table,
trx_t* trx)
{
dict_hdr_get_new_id(&table->id, NULL, NULL, table, false);
trx->table_id = table->id;
}

View File

@ -406,6 +406,27 @@ dict_table_stats_unlock(
}
}
/** Open a persistent table.
@param[in] table_id persistent table identifier
@param[in] ignore_err errors to ignore
@param[in] cached_only whether to skip loading
@return persistent table
@retval NULL if not found */
static dict_table_t* dict_table_open_on_id_low(
table_id_t table_id,
dict_err_ignore_t ignore_err,
bool cached_only)
{
dict_table_t* table = dict_sys->get_table(table_id);
if (!table && !cached_only) {
table = dict_load_table_on_id(table_id, ignore_err);
}
return table;
}
/**********************************************************************//**
Try to drop any indexes after an aborted index creation.
This can also be after a server kill during DROP INDEX. */
@ -1084,20 +1105,19 @@ dict_init(void)
dict_operation_lock = static_cast<rw_lock_t*>(
ut_zalloc_nokey(sizeof(*dict_operation_lock)));
dict_sys = static_cast<dict_sys_t*>(ut_zalloc_nokey(sizeof(*dict_sys)));
dict_sys = new (ut_zalloc_nokey(sizeof(*dict_sys))) dict_sys_t();
UT_LIST_INIT(dict_sys->table_LRU, &dict_table_t::table_LRU);
UT_LIST_INIT(dict_sys->table_non_LRU, &dict_table_t::table_LRU);
mutex_create(LATCH_ID_DICT_SYS, &dict_sys->mutex);
dict_sys->table_hash = hash_create(
buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
const ulint hash_size = buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE);
dict_sys->table_id_hash = hash_create(
buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
dict_sys->table_hash = hash_create(hash_size);
dict_sys->table_id_hash = hash_create(hash_size);
dict_sys->temp_id_hash = hash_create(hash_size);
rw_lock_create(dict_operation_lock_key,
dict_operation_lock, SYNC_DICT_OPERATION);
@ -1257,8 +1277,7 @@ dict_table_add_system_columns(
}
/** Add the table definition to the data dictionary cache */
void
dict_table_t::add_to_cache()
void dict_table_t::add_to_cache()
{
ut_ad(dict_lru_validate());
ut_ad(mutex_own(&dict_sys->mutex));
@ -1266,7 +1285,6 @@ dict_table_t::add_to_cache()
cached = TRUE;
ulint fold = ut_fold_string(name.m_name);
ulint id_fold = ut_fold_ull(id);
/* Look for a table with the same name: error if such exists */
{
@ -1284,32 +1302,31 @@ dict_table_t::add_to_cache()
ut_ad(table2 == NULL);
#endif /* UNIV_DEBUG */
}
HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold,
this);
/* Look for a table with the same id: error if such exists */
hash_table_t* id_hash = is_temporary()
? dict_sys->temp_id_hash : dict_sys->table_id_hash;
const ulint id_fold = ut_fold_ull(id);
{
dict_table_t* table2;
HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold,
HASH_SEARCH(id_hash, id_hash, id_fold,
dict_table_t*, table2, ut_ad(table2->cached),
table2->id == id);
ut_a(table2 == NULL);
#ifdef UNIV_DEBUG
/* Look for the same table pointer with a different id */
HASH_SEARCH_ALL(id_hash, dict_sys->table_id_hash,
HASH_SEARCH_ALL(id_hash, id_hash,
dict_table_t*, table2, ut_ad(table2->cached),
table2 == this);
ut_ad(table2 == NULL);
#endif /* UNIV_DEBUG */
HASH_INSERT(dict_table_t, id_hash, id_hash, id_fold, this);
}
/* Add table to hash table of tables */
HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold,
this);
/* Add table to hash table of tables based on table id */
HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold,
this);
if (can_be_evicted) {
UT_LIST_ADD_FIRST(dict_sys->table_LRU, this);
} else {
@ -1955,6 +1972,7 @@ dict_table_change_id_in_cache(
ut_ad(table);
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
ut_ad(!table->is_temporary());
/* Remove the table from the hash table of id's */
@ -2012,8 +2030,10 @@ void dict_table_remove_from_cache(dict_table_t* table, bool lru, bool keep)
HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash,
ut_fold_string(table->name.m_name), table);
HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash,
ut_fold_ull(table->id), table);
hash_table_t* id_hash = table->is_temporary()
? dict_sys->temp_id_hash : dict_sys->table_id_hash;
const ulint id_fold = ut_fold_ull(table->id);
HASH_DELETE(dict_table_t, id_hash, id_hash, id_fold, table);
/* Remove table from LRU or non-LRU list. */
if (table->can_be_evicted) {
@ -6535,17 +6555,17 @@ dict_resize()
/* all table entries are in table_LRU and table_non_LRU lists */
hash_table_free(dict_sys->table_hash);
hash_table_free(dict_sys->table_id_hash);
hash_table_free(dict_sys->temp_id_hash);
dict_sys->table_hash = hash_create(
buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
dict_sys->table_id_hash = hash_create(
buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE));
const ulint hash_size = buf_pool_get_curr_size()
/ (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE);
dict_sys->table_hash = hash_create(hash_size);
dict_sys->table_id_hash = hash_create(hash_size);
dict_sys->temp_id_hash = hash_create(hash_size);
for (table = UT_LIST_GET_FIRST(dict_sys->table_LRU); table;
table = UT_LIST_GET_NEXT(table_LRU, table)) {
ut_ad(!table->is_temporary());
ulint fold = ut_fold_string(table->name.m_name);
ulint id_fold = ut_fold_ull(table->id);
@ -6564,8 +6584,10 @@ dict_resize()
HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash,
fold, table);
HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash,
id_fold, table);
hash_table_t* id_hash = table->is_temporary()
? dict_sys->temp_id_hash : dict_sys->table_id_hash;
HASH_INSERT(dict_table_t, id_hash, id_hash, id_fold, table);
}
mutex_exit(&dict_sys->mutex);
@ -6588,7 +6610,7 @@ dict_close(void)
/* Free the hash elements. We don't remove them from the table
because we are going to destroy the table anyway. */
for (ulint i = 0; i < hash_get_n_cells(dict_sys->table_id_hash); i++) {
for (ulint i = 0; i < hash_get_n_cells(dict_sys->table_hash); i++) {
dict_table_t* table;
table = static_cast<dict_table_t*>(
@ -6609,6 +6631,7 @@ dict_close(void)
/* The elements are the same instance as in dict_sys->table_hash,
therefore we don't delete the individual elements. */
hash_table_free(dict_sys->table_id_hash);
hash_table_free(dict_sys->temp_id_hash);
mutex_exit(&dict_sys->mutex);
mutex_free(&dict_sys->mutex);

View File

@ -31,13 +31,13 @@ Created 10/25/1995 Heikki Tuuri
#include "buf0buf.h"
#include "dict0boot.h"
#include "dict0dict.h"
#include "dict0load.h"
#include "fsp0file.h"
#include "fsp0fsp.h"
#include "hash0hash.h"
#include "log0log.h"
#include "log0recv.h"
#include "mach0data.h"
#include "mem0mem.h"
#include "mtr0log.h"
#include "os0file.h"
#include "page0zip.h"

View File

@ -11030,9 +11030,8 @@ err_col:
dict_table_add_system_columns(table, heap);
if (table->is_temporary()) {
/* Get a new table ID. FIXME: Make this a private
sequence, not shared with persistent tables! */
dict_table_assign_new_id(table, m_trx);
m_trx->table_id = table->id
= dict_sys->get_temporary_table_id();
ut_ad(dict_tf_get_rec_format(table->flags)
!= REC_FORMAT_COMPRESSED);
table->space_id = SRV_TMP_SPACE_ID;

View File

@ -5228,9 +5228,10 @@ inline dberr_t dict_table_t::reassign_id(trx_t* trx)
{
DBUG_ASSERT(instant);
ut_ad(magic_n == DICT_TABLE_MAGIC_N);
ut_ad(!is_temporary());
table_id_t new_id;
dict_hdr_get_new_id(&new_id, NULL, NULL, NULL, false);
dict_hdr_get_new_id(&new_id, NULL, NULL);
pars_info_t* pinfo = pars_info_create();
pars_info_add_ull_literal(pinfo, "old", id);

View File

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 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
@ -26,8 +27,6 @@ Created 4/18/1996 Heikki Tuuri
#ifndef dict0boot_h
#define dict0boot_h
#include "univ.i"
#include "mtr0mtr.h"
#include "mtr0log.h"
#include "ut0byte.h"
@ -53,12 +52,8 @@ dict_hdr_get_new_id(
(not assigned if NULL) */
index_id_t* index_id, /*!< out: index id
(not assigned if NULL) */
ulint* space_id, /*!< out: space id
ulint* space_id); /*!< out: space id
(not assigned if NULL) */
const dict_table_t* table, /*!< in: table */
bool disable_redo); /*!< in: if true and table
object is NULL
then disable-redo */
/**********************************************************************//**
Writes the current value of the row id counter to the dictionary header file
page. */
@ -127,13 +122,6 @@ dict_is_sys_table(
/* The following is a secondary index on SYS_TABLES */
#define DICT_TABLE_IDS_ID 5
#define DICT_HDR_FIRST_ID 10 /* the ids for tables etc. start
from this number, except for basic
system tables and their above defined
indexes; ibuf tables and indexes are
assigned as the id the number
DICT_IBUF_ID_MIN plus the space id */
/* The offset of the dictionary header on the page */
#define DICT_HDR FSEG_PAGE_DATA

View File

@ -69,14 +69,6 @@ dict_create_table_step(
/*===================*/
que_thr_t* thr); /*!< in: query thread */
/** Assign a new table ID and put it into the table cache and the transaction.
@param[in,out] table Table that needs an ID
@param[in,out] trx Transaction */
void
dict_table_assign_new_id(
dict_table_t* table,
trx_t* trx);
/***********************************************************//**
Creates an index. This is a high-level function used in SQL execution
graphs.

View File

@ -28,7 +28,6 @@ Created 1/8/1996 Heikki Tuuri
#ifndef dict0dict_h
#define dict0dict_h
#include "univ.i"
#include "data0data.h"
#include "data0type.h"
#include "dict0mem.h"
@ -42,14 +41,16 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0byte.h"
#include "ut0mem.h"
#include "ut0rnd.h"
#include <deque>
#include "fsp0fsp.h"
#include "dict0pagecompress.h"
#include "sync0rw.h"
#include <atomic>
extern bool innodb_table_stats_not_found;
extern bool innodb_index_stats_not_found;
#include "sync0rw.h"
/** the first table or index ID for other than hard-coded system tables */
#define DICT_HDR_FIRST_ID 10
/********************************************************************//**
Get the database name length in a table name.
@return database name length */
@ -1572,8 +1573,10 @@ struct dict_sys_t{
the log records */
hash_table_t* table_hash; /*!< hash table of the tables, based
on name */
hash_table_t* table_id_hash; /*!< hash table of the tables, based
on id */
/** hash table of persistent table IDs */
hash_table_t* table_id_hash;
/** hash table of temporary table IDs */
hash_table_t* temp_id_hash;
dict_table_t* sys_tables; /*!< SYS_TABLES table */
dict_table_t* sys_columns; /*!< SYS_COLUMNS table */
dict_table_t* sys_indexes; /*!< SYS_INDEXES table */
@ -1587,6 +1590,52 @@ struct dict_sys_t{
UT_LIST_BASE_NODE_T(dict_table_t)
table_non_LRU; /*!< List of tables that can't be
evicted from the cache */
/** @return a new temporary table ID */
table_id_t get_temporary_table_id() {
return temp_table_id.fetch_add(1, std::memory_order_relaxed);
}
/** Look up a temporary table.
@param id temporary table ID
@return temporary table
@retval NULL if the table does not exist
(should only happen during the rollback of CREATE...SELECT) */
dict_table_t* get_temporary_table(table_id_t id)
{
ut_ad(mutex_own(&mutex));
dict_table_t* table;
ulint fold = ut_fold_ull(id);
HASH_SEARCH(id_hash, temp_id_hash, fold, dict_table_t*, table,
ut_ad(table->cached), table->id == id);
if (UNIV_LIKELY(table != NULL)) {
DBUG_ASSERT(table->is_temporary());
DBUG_ASSERT(table->id >= DICT_HDR_FIRST_ID);
table->acquire();
}
return table;
}
/** Look up a persistent table.
@param id table ID
@return table
@retval NULL if not cached */
dict_table_t* get_table(table_id_t id)
{
ut_ad(mutex_own(&mutex));
dict_table_t* table;
ulint fold = ut_fold_ull(id);
HASH_SEARCH(id_hash, table_id_hash, fold, dict_table_t*, table,
ut_ad(table->cached), table->id == id);
DBUG_ASSERT(!table || !table->is_temporary());
return table;
}
dict_sys_t() : temp_table_id(DICT_HDR_FIRST_ID) {}
private:
/** the sequence of temporary table IDs */
std::atomic<table_id_t> temp_table_id;
};
/** dummy index for ROW_FORMAT=REDUNDANT supremum and infimum records */

View File

@ -24,13 +24,9 @@ Data dictionary system
Created 1/8/1996 Heikki Tuuri
***********************************************************************/
#include "data0type.h"
#include "dict0load.h"
#include "rem0types.h"
#include "fsp0fsp.h"
#include "srv0srv.h"
#include "sync0rw.h"
#include "fsp0sysspace.h"
#include "dict0pagecompress.h"
/*********************************************************************//**
Gets the minimum number of bytes per character.

View File

@ -47,18 +47,6 @@ dict_table_check_if_in_cache_low(
/*=============================*/
const char* table_name); /*!< in: table name */
/**********************************************************************//**
Returns a table object based on table id.
@return table, NULL if does not exist */
UNIV_INLINE
dict_table_t*
dict_table_open_on_id_low(
/*=====================*/
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err, /*!< in: errors to ignore
when loading the table */
ibool open_only_if_in_cache);
#include "dict0priv.ic"
#endif /* dict0priv.h */

View File

@ -25,7 +25,6 @@ Created Wed 13 Oct 2010 16:10:14 EST Sunny Bains
#include "dict0dict.h"
#include "dict0load.h"
#include "dict0priv.h"
/**********************************************************************//**
Gets a table; loads it to the dictionary cache if necessary. A low-level
@ -63,40 +62,6 @@ dict_table_get_low(
return(table);
}
/**********************************************************************//**
Returns a table object based on table id.
@return table, NULL if does not exist */
UNIV_INLINE
dict_table_t*
dict_table_open_on_id_low(
/*======================*/
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err, /*!< in: errors to ignore
when loading the table */
ibool open_only_if_in_cache)
{
dict_table_t* table;
ulint fold;
ut_ad(mutex_own(&dict_sys->mutex));
/* Look for the table name in the hash table */
fold = ut_fold_ull(table_id);
HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold,
dict_table_t*, table, ut_ad(table->cached),
table->id == table_id);
if (table == NULL && !open_only_if_in_cache) {
table = dict_load_table_on_id(table_id, ignore_err);
}
ut_ad(!table || table->cached);
/* TODO: should get the type information from MySQL */
return(table);
}
/**********************************************************************//**
Checks if a table is in the dictionary cache.
@return table, NULL if not found */

View File

@ -1389,6 +1389,7 @@ rec_get_converted_size(
} else if (index->table->id == DICT_INDEXES_ID) {
/* The column SYS_INDEXES.MERGE_THRESHOLD was
instantly added in MariaDB 10.2.2 (MySQL 5.7). */
ut_ad(!index->table->is_temporary());
ut_ad(index->n_fields == DICT_NUM_FIELDS__SYS_INDEXES);
ut_ad(dtuple->n_fields == DICT_NUM_FIELDS__SYS_INDEXES
|| dtuple->n_fields

View File

@ -86,17 +86,20 @@ that index record. */
enum undo_exec {
UNDO_NODE_FETCH_NEXT = 1, /*!< we should fetch the next
undo log record */
UNDO_NODE_INSERT, /*!< undo a fresh insert of a
row to a table */
UNDO_NODE_MODIFY /*!< undo a modify operation
(DELETE or UPDATE) on a row
of a table */
/** rollback an insert into persistent table */
UNDO_INSERT_PERSISTENT,
/** rollback an update (or delete) in a persistent table */
UNDO_UPDATE_PERSISTENT,
/** rollback an insert into temporary table */
UNDO_INSERT_TEMPORARY,
/** rollback an update (or delete) in a temporary table */
UNDO_UPDATE_TEMPORARY,
};
/** Undo node structure */
struct undo_node_t{
que_common_t common; /*!< node type: QUE_NODE_UNDO */
enum undo_exec state; /*!< node execution state */
undo_exec state; /*!< rollback execution state */
trx_t* trx; /*!< trx for which undo is done */
roll_ptr_t roll_ptr;/*!< roll pointer to undo log record */
trx_undo_rec_t* undo_rec;/*!< undo log record */

View File

@ -53,16 +53,6 @@ trx_savept_take(
/*============*/
trx_t* trx); /*!< in: transaction */
/** Get the last undo log record of a transaction (for rollback).
@param[in,out] trx transaction
@param[out] roll_ptr DB_ROLL_PTR to the undo record
@param[in,out] heap memory heap for allocation
@return undo log record copied to heap
@retval NULL if none left or the roll_limit (savepoint) was reached */
trx_undo_rec_t*
trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap)
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Report progress when rolling back a row of a recovered transaction. */
void trx_roll_report_progress();
/*******************************************************************//**

View File

@ -180,9 +180,7 @@ trx_undo_free_last_page(trx_undo_t* undo, mtr_t* mtr)
@param[in,out] undo undo log
@param[in] limit all undo logs after this limit will be discarded
@param[in] is_temp whether this is temporary undo log */
void
trx_undo_truncate_end(trx_undo_t* undo, undo_no_t limit, bool is_temp)
MY_ATTRIBUTE((nonnull));
void trx_undo_truncate_end(trx_undo_t& undo, undo_no_t limit, bool is_temp);
/** Truncate the head of an undo log.
NOTE that only whole pages are freed; the header page is not

View File

@ -4253,6 +4253,7 @@ lock_check_dict_lock(
const lock_t* lock) /*!< in: lock to check */
{
if (lock_get_type_low(lock) == LOCK_REC) {
ut_ad(!lock->index->table->is_temporary());
/* Check if the transcation locked a record
in a system table in X mode. It should have set
@ -4266,9 +4267,8 @@ lock_check_dict_lock(
} else {
ut_ad(lock_get_type_low(lock) & LOCK_TABLE);
const dict_table_t* table;
table = lock->un_member.tab_lock.table;
const dict_table_t* table = lock->un_member.tab_lock.table;
ut_ad(!table->is_temporary());
/* Check if the transcation locked a system table
in IX mode. It should have set the dict_op code
@ -6127,10 +6127,8 @@ lock_get_table_id(
/*==============*/
const lock_t* lock) /*!< in: lock */
{
dict_table_t* table;
table = lock_get_table(lock);
dict_table_t* table = lock_get_table(lock);
ut_ad(!table->is_temporary());
return(table->id);
}

View File

@ -28,6 +28,7 @@ Created 2012-02-08 by Sunny Bains.
#include "btr0pcur.h"
#include "que0que.h"
#include "dict0boot.h"
#include "dict0load.h"
#include "ibuf0ibuf.h"
#include "pars0pars.h"
#include "row0sel.h"

View File

@ -2948,7 +2948,7 @@ row_mysql_table_id_reassign(
dberr_t err;
pars_info_t* info = pars_info_create();
dict_hdr_get_new_id(new_id, NULL, NULL, table, false);
dict_hdr_get_new_id(new_id, NULL, NULL);
pars_info_add_ull_literal(info, "old_id", table->id);
pars_info_add_ull_literal(info, "new_id", *new_id);

View File

@ -725,6 +725,7 @@ row_rec_to_index_entry_impl(
ut_ad(heap != NULL);
ut_ad(index != NULL);
ut_ad(!mblob || index->is_primary());
ut_ad(!mblob || !index->table->is_temporary());
ut_ad(!mblob || !dict_index_is_spatial(index));
compile_time_assert(!mblob || metadata);
compile_time_assert(mblob <= 2);
@ -759,7 +760,8 @@ row_rec_to_index_entry_impl(
|| rec_len == dict_index_get_n_fields(index) + uint(mblob == 1)
/* a record for older SYS_INDEXES table
(missing merge_threshold column) is acceptable. */
|| (index->table->id == DICT_INDEXES_ID
|| (!index->table->is_temporary()
&& index->table->id == DICT_INDEXES_ID
&& rec_len == dict_index_get_n_fields(index) - 1));
ulint i;

View File

@ -80,8 +80,19 @@ row_undo_ins_remove_clust_rec(
if (index->table->is_temporary()) {
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
mtr.set_log_mode(MTR_LOG_NO_REDO);
ut_ad(!dict_index_is_online_ddl(index));
ut_ad(index->table->id >= DICT_HDR_FIRST_ID);
online = false;
} else {
index->set_modified(mtr);
online = dict_index_is_online_ddl(index);
if (online) {
ut_ad(node->trx->dict_operation_lock_mode
!= RW_X_LATCH);
ut_ad(node->table->id != DICT_INDEXES_ID);
ut_ad(node->table->id != DICT_COLUMNS_ID);
mtr_s_lock(dict_index_get_lock(index), &mtr);
}
}
/* This is similar to row_undo_mod_clust(). The DDL thread may
@ -90,14 +101,6 @@ row_undo_ins_remove_clust_rec(
purged. However, we can log the removal out of sync with the
B-tree modification. */
online = dict_index_is_online_ddl(index);
if (online) {
ut_ad(node->trx->dict_operation_lock_mode
!= RW_X_LATCH);
ut_ad(node->table->id != DICT_INDEXES_ID);
mtr_s_lock(dict_index_get_lock(index), &mtr);
}
success = btr_pcur_restore_position(
online
? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
@ -119,21 +122,19 @@ row_undo_ins_remove_clust_rec(
rec, index, NULL, true, ULINT_UNDEFINED, &heap);
row_log_table_delete(rec, index, offsets, NULL);
mem_heap_free(heap);
}
} else {
switch (node->table->id) {
case DICT_INDEXES_ID:
ut_ad(!online);
ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
ut_ad(node->trx->dict_operation_lock_mode
== RW_X_LATCH);
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
dict_drop_index_tree(
btr_pcur_get_rec(&node->pcur), &(node->pcur), &mtr);
dict_drop_index_tree(btr_pcur_get_rec(&node->pcur),
&node->pcur, &mtr);
mtr.commit();
mtr.start();
success = btr_pcur_restore_position(
BTR_MODIFY_LEAF, &node->pcur, &mtr);
ut_a(success);
@ -146,7 +147,8 @@ row_undo_ins_remove_clust_rec(
completed. At this point, any corresponding operation
to the metadata record will have been rolled back. */
ut_ad(!online);
ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
ut_ad(node->trx->dict_operation_lock_mode
== RW_X_LATCH);
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
const rec_t* rec = btr_pcur_get_rec(&node->pcur);
if (rec_get_n_fields_old(rec)
@ -161,6 +163,7 @@ row_undo_ins_remove_clust_rec(
}
node->trx->evict_table(mach_read_from_8(data));
}
}
if (btr_cur_optimistic_delete(btr_cur, 0, &mtr)) {
err = DB_SUCCESS;
@ -363,14 +366,10 @@ retry:
return(err);
}
/***********************************************************//**
Parses the row reference and other info in a fresh insert undo record. */
static
void
row_undo_ins_parse_undo_rec(
/*========================*/
undo_node_t* node, /*!< in/out: row undo node */
ibool dict_locked) /*!< in: TRUE if own dict_sys->mutex */
/** Parse an insert undo record.
@param[in,out] node row rollback state
@param[in] dict_locked whether the data dictionary cache is locked */
static bool row_undo_ins_parse_undo_rec(undo_node_t* node, bool dict_locked)
{
dict_index_t* clust_index;
byte* ptr;
@ -379,18 +378,28 @@ row_undo_ins_parse_undo_rec(
ulint dummy;
bool dummy_extern;
ut_ad(node);
ut_ad(node->state == UNDO_INSERT_PERSISTENT
|| node->state == UNDO_INSERT_TEMPORARY);
ut_ad(node->trx->in_rollback);
ut_ad(trx_undo_roll_ptr_is_insert(node->roll_ptr));
ptr = trx_undo_rec_get_pars(node->undo_rec, &node->rec_type, &dummy,
&dummy_extern, &undo_no, &table_id);
node->update = NULL;
node->table = dict_table_open_on_id(
table_id, dict_locked, DICT_TABLE_OP_NORMAL);
if (node->state == UNDO_INSERT_PERSISTENT) {
node->table = dict_table_open_on_id(table_id, dict_locked,
DICT_TABLE_OP_NORMAL);
} else if (!dict_locked) {
mutex_enter(&dict_sys->mutex);
node->table = dict_sys->get_temporary_table(table_id);
mutex_exit(&dict_sys->mutex);
} else {
node->table = dict_sys->get_temporary_table(table_id);
}
/* Skip the UNDO if we can't find the table or the .ibd file. */
if (UNIV_UNLIKELY(node->table == NULL)) {
return;
if (!node->table) {
return false;
}
switch (node->rec_type) {
@ -429,6 +438,7 @@ close_table:
connection, instead of doing this rollback. */
dict_table_close(node->table, dict_locked, FALSE);
node->table = NULL;
return false;
} else {
ut_ad(!node->table->skip_alter_undo);
clust_index = dict_table_get_first_index(node->table);
@ -460,6 +470,8 @@ close_table:
goto close_table;
}
}
return true;
}
/***************************************************************//**
@ -536,18 +548,10 @@ row_undo_ins(
que_thr_t* thr) /*!< in: query thread */
{
dberr_t err;
ibool dict_locked;
bool dict_locked = node->trx->dict_operation_lock_mode == RW_X_LATCH;
ut_ad(node->state == UNDO_NODE_INSERT);
ut_ad(node->trx->in_rollback);
ut_ad(trx_undo_roll_ptr_is_insert(node->roll_ptr));
dict_locked = node->trx->dict_operation_lock_mode == RW_X_LATCH;
row_undo_ins_parse_undo_rec(node, dict_locked);
if (node->table == NULL) {
return(DB_SUCCESS);
if (!row_undo_ins_parse_undo_rec(node, dict_locked)) {
return DB_SUCCESS;
}
/* Iterate over all the indexes and undo the insert.*/
@ -570,27 +574,20 @@ row_undo_ins(
break;
}
/* fall through */
case TRX_UNDO_INSERT_METADATA:
log_free_check();
if (node->table->id == DICT_INDEXES_ID) {
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
ut_ad(!node->table->is_temporary());
if (!dict_locked) {
mutex_enter(&dict_sys->mutex);
}
}
// FIXME: We need to update the dict_index_t::space and
// page number fields too.
err = row_undo_ins_remove_clust_rec(node);
if (node->table->id == DICT_INDEXES_ID
&& !dict_locked) {
if (!dict_locked) {
mutex_exit(&dict_sys->mutex);
}
} else {
err = row_undo_ins_remove_clust_rec(node);
}
if (err == DB_SUCCESS && node->table->stat_initialized) {
/* Not protected by dict_table_stats_lock() for
@ -609,6 +606,12 @@ row_undo_ins(
node->table, node->trx->mysql_thd);
}
}
break;
case TRX_UNDO_INSERT_METADATA:
log_free_check();
ut_ad(!node->table->is_temporary());
err = row_undo_ins_remove_clust_rec(node);
}
dict_table_close(node->table, dict_locked, FALSE);

View File

@ -500,8 +500,6 @@ mtr_commit_exit:
btr_pcur_commit_specify_mtr(pcur, &mtr);
func_exit:
node->state = UNDO_NODE_FETCH_NEXT;
if (offsets_heap) {
mem_heap_free(offsets_heap);
}
@ -1203,14 +1201,10 @@ row_undo_mod_upd_exist_sec(
return(err);
}
/***********************************************************//**
Parses the row reference and other info in a modify undo log record. */
static MY_ATTRIBUTE((nonnull))
void
row_undo_mod_parse_undo_rec(
/*========================*/
undo_node_t* node, /*!< in: row undo node */
ibool dict_locked) /*!< in: TRUE if own dict_sys->mutex */
/** Parse an update undo record.
@param[in,out] node row rollback state
@param[in] dict_locked whether the data dictionary cache is locked */
static bool row_undo_mod_parse_undo_rec(undo_node_t* node, bool dict_locked)
{
dict_index_t* clust_index;
byte* ptr;
@ -1223,19 +1217,28 @@ row_undo_mod_parse_undo_rec(
ulint cmpl_info;
bool dummy_extern;
ut_ad(node->state == UNDO_UPDATE_PERSISTENT
|| node->state == UNDO_UPDATE_TEMPORARY);
ut_ad(node->trx->in_rollback);
ut_ad(!trx_undo_roll_ptr_is_insert(node->roll_ptr));
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
&dummy_extern, &undo_no, &table_id);
node->rec_type = type;
node->table = dict_table_open_on_id(
table_id, dict_locked, DICT_TABLE_OP_NORMAL);
if (node->state == UNDO_UPDATE_PERSISTENT) {
node->table = dict_table_open_on_id(table_id, dict_locked,
DICT_TABLE_OP_NORMAL);
} else if (!dict_locked) {
mutex_enter(&dict_sys->mutex);
node->table = dict_sys->get_temporary_table(table_id);
mutex_exit(&dict_sys->mutex);
} else {
node->table = dict_sys->get_temporary_table(table_id);
}
/* TODO: other fixes associated with DROP TABLE + rollback in the
same table by another user */
if (node->table == NULL) {
/* Table was dropped */
return;
if (!node->table) {
return false;
}
ut_ad(!node->table->skip_alter_undo);
@ -1253,7 +1256,7 @@ close_table:
connection, instead of doing this rollback. */
dict_table_close(node->table, dict_locked, FALSE);
node->table = NULL;
return;
return false;
}
clust_index = dict_table_get_first_index(node->table);
@ -1324,6 +1327,8 @@ close_table:
(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)
? NULL : ptr);
}
return true;
}
/***********************************************************//**
@ -1336,27 +1341,12 @@ row_undo_mod(
que_thr_t* thr) /*!< in: query thread */
{
dberr_t err;
ibool dict_locked;
ut_ad(node != NULL);
ut_ad(thr != NULL);
ut_ad(node->state == UNDO_NODE_MODIFY);
ut_ad(node->trx->in_rollback);
ut_ad(!trx_undo_roll_ptr_is_insert(node->roll_ptr));
dict_locked = thr_get_trx(thr)->dict_operation_lock_mode == RW_X_LATCH;
ut_ad(thr_get_trx(thr) == node->trx);
const bool dict_locked = node->trx->dict_operation_lock_mode
== RW_X_LATCH;
row_undo_mod_parse_undo_rec(node, dict_locked);
if (node->table == NULL) {
/* It is already undone, or will be undone by another query
thread, or table was dropped */
node->state = UNDO_NODE_FETCH_NEXT;
return(DB_SUCCESS);
if (!row_undo_mod_parse_undo_rec(node, dict_locked)) {
return DB_SUCCESS;
}
node->index = dict_table_get_first_index(node->table);

View File

@ -217,7 +217,8 @@ row_undo_search_clust_to_pcur(
log, first mark them DATA_MISSING. So we will know if the
value gets updated */
if (node->table->n_v_cols
&& node->state != UNDO_NODE_INSERT
&& (node->state == UNDO_UPDATE_PERSISTENT
|| node->state == UNDO_UPDATE_TEMPORARY)
&& !(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
for (ulint i = 0;
i < dict_table_get_n_v_cols(node->table); i++) {
@ -253,6 +254,149 @@ func_exit:
return(found);
}
/** Try to truncate the undo logs.
@param[in,out] trx transaction */
static void row_undo_try_truncate(trx_t* trx)
{
if (trx_undo_t* undo = trx->rsegs.m_redo.undo) {
ut_ad(undo->rseg == trx->rsegs.m_redo.rseg);
trx_undo_truncate_end(*undo, trx->undo_no, false);
}
if (trx_undo_t* undo = trx->rsegs.m_noredo.undo) {
ut_ad(undo->rseg == trx->rsegs.m_noredo.rseg);
trx_undo_truncate_end(*undo, trx->undo_no, true);
}
}
/** Get the latest undo log record for rollback.
@param[in,out] node rollback context
@return whether an undo log record was fetched */
static bool row_undo_rec_get(undo_node_t* node)
{
trx_t* trx = node->trx;
if (trx->pages_undone) {
trx->pages_undone = 0;
row_undo_try_truncate(trx);
}
trx_undo_t* undo = NULL;
trx_undo_t* insert = trx->rsegs.m_redo.old_insert;
trx_undo_t* update = trx->rsegs.m_redo.undo;
trx_undo_t* temp = trx->rsegs.m_noredo.undo;
const undo_no_t limit = trx->roll_limit;
ut_ad(!insert || !update || insert->empty() || update->empty()
|| insert->top_undo_no != update->top_undo_no);
ut_ad(!insert || !temp || insert->empty() || temp->empty()
|| insert->top_undo_no != temp->top_undo_no);
ut_ad(!update || !temp || update->empty() || temp->empty()
|| update->top_undo_no != temp->top_undo_no);
if (UNIV_LIKELY_NULL(insert)
&& !insert->empty() && limit <= insert->top_undo_no) {
undo = insert;
}
if (update && !update->empty() && update->top_undo_no >= limit) {
if (!undo) {
undo = update;
} else if (undo->top_undo_no < update->top_undo_no) {
undo = update;
}
}
if (temp && !temp->empty() && temp->top_undo_no >= limit) {
if (!undo) {
undo = temp;
} else if (undo->top_undo_no < temp->top_undo_no) {
undo = temp;
}
}
if (undo == NULL) {
row_undo_try_truncate(trx);
/* Mark any ROLLBACK TO SAVEPOINT completed, so that
if the transaction object is committed and reused
later, we will default to a full ROLLBACK. */
trx->roll_limit = 0;
trx->in_rollback = false;
return false;
}
ut_ad(!undo->empty());
ut_ad(limit <= undo->top_undo_no);
node->roll_ptr = trx_undo_build_roll_ptr(
false, undo->rseg->id, undo->top_page_no, undo->top_offset);
mtr_t mtr;
mtr.start();
page_t* undo_page = trx_undo_page_get_s_latched(
page_id_t(undo->rseg->space->id, undo->top_page_no), &mtr);
ulint offset = undo->top_offset;
trx_undo_rec_t* prev_rec = trx_undo_get_prev_rec(
undo_page + offset, undo->hdr_page_no, undo->hdr_offset,
true, &mtr);
if (prev_rec == NULL) {
undo->top_undo_no = IB_ID_MAX;
ut_ad(undo->empty());
} else {
page_t* prev_rec_page = page_align(prev_rec);
if (prev_rec_page != undo_page) {
trx->pages_undone++;
}
undo->top_page_no = page_get_page_no(prev_rec_page);
undo->top_offset = ulint(prev_rec - prev_rec_page);
undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec);
ut_ad(!undo->empty());
}
{
const trx_undo_rec_t* undo_rec = undo_page + offset;
node->undo_rec = trx_undo_rec_copy(undo_rec, node->heap);
}
mtr.commit();
switch (trx_undo_rec_get_type(node->undo_rec)) {
case TRX_UNDO_INSERT_METADATA:
/* This record type was introduced in MDEV-11369
instant ADD COLUMN, which was implemented after
MDEV-12288 removed the insert_undo log. There is no
instant ADD COLUMN for temporary tables. Therefore,
this record can only be present in the main undo log. */
ut_ad(undo == update);
/* fall through */
case TRX_UNDO_RENAME_TABLE:
ut_ad(undo == insert || undo == update);
/* fall through */
case TRX_UNDO_INSERT_REC:
ut_ad(undo == insert || undo == update || undo == temp);
node->roll_ptr |= 1ULL << ROLL_PTR_INSERT_FLAG_POS;
node->state = undo == temp
? UNDO_INSERT_TEMPORARY : UNDO_INSERT_PERSISTENT;
break;
default:
ut_ad(undo == update || undo == temp);
node->state = undo == temp
? UNDO_UPDATE_TEMPORARY : UNDO_UPDATE_PERSISTENT;
break;
}
trx->undo_no = node->undo_no = trx_undo_rec_get_undo_no(
node->undo_rec);
return true;
}
/***********************************************************//**
Fetches an undo log record and does the undo for the recorded operation.
If none left, or a partial rollback completed, returns control to the
@ -265,23 +409,12 @@ row_undo(
undo_node_t* node, /*!< in: row undo node */
que_thr_t* thr) /*!< in: query thread */
{
trx_t* trx = node->trx;
ut_ad(trx->in_rollback);
ut_ad(node->trx->in_rollback);
if (node->state == UNDO_NODE_FETCH_NEXT) {
node->undo_rec = trx_roll_pop_top_rec_of_trx(
trx, &node->roll_ptr, node->heap);
if (!node->undo_rec) {
if (node->state == UNDO_NODE_FETCH_NEXT && !row_undo_rec_get(node)) {
/* Rollback completed for this query thread */
thr->run_node = que_node_get_parent(node);
return(DB_SUCCESS);
}
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
node->state = trx_undo_roll_ptr_is_insert(node->roll_ptr)
? UNDO_NODE_INSERT : UNDO_NODE_MODIFY;
return DB_SUCCESS;
}
/* Prevent DROP TABLE etc. while we are rolling back this row.
@ -289,31 +422,33 @@ row_undo(
then we already have dict_operation_lock locked in x-mode. Do not
try to lock again, because that would cause a hang. */
trx_t* trx = node->trx;
const bool locked_data_dict = (trx->dict_operation_lock_mode == 0);
if (locked_data_dict) {
row_mysql_freeze_data_dictionary(trx);
}
dberr_t err;
if (node->state == UNDO_NODE_INSERT) {
switch (node->state) {
case UNDO_INSERT_PERSISTENT:
case UNDO_INSERT_TEMPORARY:
err = row_undo_ins(node, thr);
node->state = UNDO_NODE_FETCH_NEXT;
} else {
ut_ad(node->state == UNDO_NODE_MODIFY);
break;
case UNDO_UPDATE_PERSISTENT:
case UNDO_UPDATE_TEMPORARY:
err = row_undo_mod(node, thr);
break;
case UNDO_NODE_FETCH_NEXT:
ut_ad(!"wrong state");
}
if (locked_data_dict) {
row_mysql_unfreeze_data_dictionary(trx);
}
/* Do some cleanup */
node->state = UNDO_NODE_FETCH_NEXT;
btr_pcur_close(&(node->pcur));
mem_heap_empty(node->heap);

View File

@ -44,10 +44,6 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0trx.h"
#include "trx0undo.h"
/** This many pages must be undone before a truncate is tried within
rollback */
static const ulint TRX_ROLL_TRUNC_THRESHOLD = 1;
/** true if trx_rollback_all_recovered() thread is active */
bool trx_rollback_is_active;
@ -874,175 +870,6 @@ DECLARE_THREAD(trx_rollback_all_recovered)(void*)
OS_THREAD_DUMMY_RETURN;
}
/** Try to truncate the undo logs.
@param[in,out] trx transaction */
static
void
trx_roll_try_truncate(trx_t* trx)
{
trx->pages_undone = 0;
undo_no_t undo_no = trx->undo_no;
if (trx_undo_t* undo = trx->rsegs.m_redo.undo) {
ut_ad(undo->rseg == trx->rsegs.m_redo.rseg);
mutex_enter(&undo->rseg->mutex);
trx_undo_truncate_end(undo, undo_no, false);
mutex_exit(&undo->rseg->mutex);
}
if (trx_undo_t* undo = trx->rsegs.m_noredo.undo) {
ut_ad(undo->rseg == trx->rsegs.m_noredo.rseg);
mutex_enter(&undo->rseg->mutex);
trx_undo_truncate_end(undo, undo_no, true);
mutex_exit(&undo->rseg->mutex);
}
#ifdef WITH_WSREP_OUT
if (wsrep_on(trx->mysql_thd)) {
trx->lock.was_chosen_as_deadlock_victim = FALSE;
}
#endif /* WITH_WSREP */
}
/***********************************************************************//**
Pops the topmost undo log record in a single undo log and updates the info
about the topmost record in the undo log memory struct.
@return undo log record, the page s-latched */
static
trx_undo_rec_t*
trx_roll_pop_top_rec(
/*=================*/
trx_t* trx, /*!< in: transaction */
trx_undo_t* undo, /*!< in: undo log */
mtr_t* mtr) /*!< in: mtr */
{
page_t* undo_page = trx_undo_page_get_s_latched(
page_id_t(undo->rseg->space->id, undo->top_page_no), mtr);
ulint offset = undo->top_offset;
trx_undo_rec_t* prev_rec = trx_undo_get_prev_rec(
undo_page + offset, undo->hdr_page_no, undo->hdr_offset,
true, mtr);
if (prev_rec == NULL) {
undo->top_undo_no = IB_ID_MAX;
ut_ad(undo->empty());
} else {
page_t* prev_rec_page = page_align(prev_rec);
if (prev_rec_page != undo_page) {
trx->pages_undone++;
}
undo->top_page_no = page_get_page_no(prev_rec_page);
undo->top_offset = ulint(prev_rec - prev_rec_page);
undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec);
ut_ad(!undo->empty());
}
return(undo_page + offset);
}
/** Get the last undo log record of a transaction (for rollback).
@param[in,out] trx transaction
@param[out] roll_ptr DB_ROLL_PTR to the undo record
@param[in,out] heap memory heap for allocation
@return undo log record copied to heap
@retval NULL if none left or the roll_limit (savepoint) was reached */
trx_undo_rec_t*
trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap)
{
if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) {
trx_roll_try_truncate(trx);
}
trx_undo_t* undo = NULL;
trx_undo_t* insert = trx->rsegs.m_redo.old_insert;
trx_undo_t* update = trx->rsegs.m_redo.undo;
trx_undo_t* temp = trx->rsegs.m_noredo.undo;
const undo_no_t limit = trx->roll_limit;
ut_ad(!insert || !update || insert->empty() || update->empty()
|| insert->top_undo_no != update->top_undo_no);
ut_ad(!insert || !temp || insert->empty() || temp->empty()
|| insert->top_undo_no != temp->top_undo_no);
ut_ad(!update || !temp || update->empty() || temp->empty()
|| update->top_undo_no != temp->top_undo_no);
if (UNIV_LIKELY_NULL(insert)
&& !insert->empty() && limit <= insert->top_undo_no) {
undo = insert;
}
if (update && !update->empty() && update->top_undo_no >= limit) {
if (!undo) {
undo = update;
} else if (undo->top_undo_no < update->top_undo_no) {
undo = update;
}
}
if (temp && !temp->empty() && temp->top_undo_no >= limit) {
if (!undo) {
undo = temp;
} else if (undo->top_undo_no < temp->top_undo_no) {
undo = temp;
}
}
if (undo == NULL) {
trx_roll_try_truncate(trx);
/* Mark any ROLLBACK TO SAVEPOINT completed, so that
if the transaction object is committed and reused
later, we will default to a full ROLLBACK. */
trx->roll_limit = 0;
trx->in_rollback = false;
return(NULL);
}
ut_ad(!undo->empty());
ut_ad(limit <= undo->top_undo_no);
*roll_ptr = trx_undo_build_roll_ptr(
false, undo->rseg->id, undo->top_page_no, undo->top_offset);
mtr_t mtr;
mtr.start();
trx_undo_rec_t* undo_rec = trx_roll_pop_top_rec(trx, undo, &mtr);
const undo_no_t undo_no = trx_undo_rec_get_undo_no(undo_rec);
switch (trx_undo_rec_get_type(undo_rec)) {
case TRX_UNDO_INSERT_METADATA:
/* This record type was introduced in MDEV-11369
instant ADD COLUMN, which was implemented after
MDEV-12288 removed the insert_undo log. There is no
instant ADD COLUMN for temporary tables. Therefore,
this record can only be present in the main undo log. */
ut_ad(undo == update);
/* fall through */
case TRX_UNDO_RENAME_TABLE:
ut_ad(undo == insert || undo == update);
/* fall through */
case TRX_UNDO_INSERT_REC:
ut_ad(undo == insert || undo == update || undo == temp);
*roll_ptr |= 1ULL << ROLL_PTR_INSERT_FLAG_POS;
break;
default:
ut_ad(undo == update || undo == temp);
break;
}
trx->undo_no = undo_no;
trx_undo_rec_t* undo_rec_copy = trx_undo_rec_copy(undo_rec, heap);
mtr.commit();
return(undo_rec_copy);
}
/****************************************************************//**
Builds an undo 'query' graph for a transaction. The actual rollback is
performed by executing this query graph like a query subprocedure call.

View File

@ -888,41 +888,46 @@ trx_undo_free_last_page(trx_undo_t* undo, mtr_t* mtr)
@param[in,out] undo undo log
@param[in] limit all undo logs after this limit will be discarded
@param[in] is_temp whether this is temporary undo log */
void
trx_undo_truncate_end(trx_undo_t* undo, undo_no_t limit, bool is_temp)
void trx_undo_truncate_end(trx_undo_t& undo, undo_no_t limit, bool is_temp)
{
ut_ad(mutex_own(&undo->rseg->mutex));
ut_ad(is_temp == !undo->rseg->is_persistent());
mtr_t mtr;
ut_ad(is_temp == !undo.rseg->is_persistent());
for (;;) {
mtr_t mtr;
mtr.start();
if (is_temp) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
trx_undo_rec_t* trunc_here = NULL;
mutex_enter(&undo.rseg->mutex);
page_t* undo_page = trx_undo_page_get(
page_id_t(undo->rseg->space->id, undo->last_page_no),
page_id_t(undo.rseg->space->id, undo.last_page_no),
&mtr);
trx_undo_rec_t* rec = trx_undo_page_get_last_rec(
undo_page, undo->hdr_page_no, undo->hdr_offset);
undo_page, undo.hdr_page_no, undo.hdr_offset);
while (rec) {
if (trx_undo_rec_get_undo_no(rec) >= limit) {
/* Truncate at least this record off, maybe
more */
trunc_here = rec;
} else {
goto function_exit;
if (trx_undo_rec_get_undo_no(rec) < limit) {
goto func_exit;
}
/* Truncate at least this record off, maybe more */
trunc_here = rec;
rec = trx_undo_page_get_prev_rec(rec,
undo->hdr_page_no,
undo->hdr_offset);
undo.hdr_page_no,
undo.hdr_offset);
}
if (undo->last_page_no == undo->hdr_page_no) {
function_exit:
if (undo.last_page_no != undo.hdr_page_no) {
trx_undo_free_last_page(&undo, &mtr);
mutex_exit(&undo.rseg->mutex);
mtr.commit();
continue;
}
func_exit:
mutex_exit(&undo.rseg->mutex);
if (trunc_here) {
mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR
+ TRX_UNDO_PAGE_FREE,
@ -933,10 +938,6 @@ function_exit:
mtr.commit();
return;
}
trx_undo_free_last_page(undo, &mtr);
mtr.commit();
}
}
/** Truncate the head of an undo log.