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,47 +122,47 @@ 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->rec_type == TRX_UNDO_INSERT_REC);
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->rec_type == TRX_UNDO_INSERT_REC);
dict_drop_index_tree(btr_pcur_get_rec(&node->pcur),
&node->pcur, &mtr);
mtr.commit();
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);
break;
case DICT_COLUMNS_ID:
/* This is rolling back an INSERT into SYS_COLUMNS.
If it was part of an instant ALTER TABLE operation, we
must evict the table definition, so that it can be
reloaded after the dictionary operation has been
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->rec_type == TRX_UNDO_INSERT_REC);
const rec_t* rec = btr_pcur_get_rec(&node->pcur);
if (rec_get_n_fields_old(rec)
!= DICT_NUM_FIELDS__SYS_COLUMNS) {
mtr.start();
success = btr_pcur_restore_position(
BTR_MODIFY_LEAF, &node->pcur, &mtr);
ut_a(success);
break;
case DICT_COLUMNS_ID:
/* This is rolling back an INSERT into SYS_COLUMNS.
If it was part of an instant ALTER TABLE operation, we
must evict the table definition, so that it can be
reloaded after the dictionary operation has been
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->rec_type == TRX_UNDO_INSERT_REC);
const rec_t* rec = btr_pcur_get_rec(&node->pcur);
if (rec_get_n_fields_old(rec)
!= DICT_NUM_FIELDS__SYS_COLUMNS) {
break;
}
ulint len;
const byte* data = rec_get_nth_field_old(
rec, DICT_FLD__SYS_COLUMNS__TABLE_ID, &len);
if (len != 8) {
break;
}
node->trx->evict_table(mach_read_from_8(data));
}
ulint len;
const byte* data = rec_get_nth_field_old(
rec, DICT_FLD__SYS_COLUMNS__TABLE_ID, &len);
if (len != 8) {
break;
}
node->trx->evict_table(mach_read_from_8(data));
}
if (btr_cur_optimistic_delete(btr_cur, 0, &mtr)) {
@ -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,26 +574,19 @@ 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) {
mutex_exit(&dict_sys->mutex);
err = row_undo_ins_remove_clust_rec(node);
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) {
@ -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) {
/* 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;
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;
}
/* 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,54 +888,55 @@ 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 (trunc_here) {
mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR
+ TRX_UNDO_PAGE_FREE,
ulint(trunc_here - undo_page),
MLOG_2BYTES, &mtr);
}
if (undo.last_page_no != undo.hdr_page_no) {
trx_undo_free_last_page(&undo, &mtr);
mutex_exit(&undo.rseg->mutex);
mtr.commit();
return;
continue;
}
func_exit:
mutex_exit(&undo.rseg->mutex);
if (trunc_here) {
mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR
+ TRX_UNDO_PAGE_FREE,
ulint(trunc_here - undo_page),
MLOG_2BYTES, &mtr);
}
trx_undo_free_last_page(undo, &mtr);
mtr.commit();
return;
}
}