424 lines
13 KiB
C++
424 lines
13 KiB
C++
/*****************************************************************************
|
|
|
|
Copyright (c) 2016, 2019, Oracle and/or its affiliates. All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License, version 2.0, as published by the
|
|
Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including but not
|
|
limited to OpenSSL) that is licensed under separate terms, as designated in a
|
|
particular file or component or in included license documentation. The authors
|
|
of MySQL hereby grant you an additional permission to link the program and
|
|
your derivative works with the separately licensed software that they have
|
|
included with MySQL.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*****************************************************************************/
|
|
|
|
#include "lob0del.h"
|
|
#include "lob0first.h"
|
|
#include "lob0index.h"
|
|
#include "lob0inf.h"
|
|
#include "lob0lob.h"
|
|
#include "row0upd.h"
|
|
#include "trx0purge.h"
|
|
#include "trx0rec.h"
|
|
#include "zlob0first.h"
|
|
#include "zlob0index.h"
|
|
#include "zlob0read.h"
|
|
|
|
namespace lob {
|
|
|
|
/** Rollback from undo log information.
|
|
@param[in] ctx the delete operation context.
|
|
@param[in] index the clustered index to which LOB belongs.
|
|
@param[in] ref the LOB reference object.
|
|
@param[in] uf the update vector of concerned field. */
|
|
static void rollback_from_undolog(DeleteContext *ctx, dict_index_t *index,
|
|
ref_t &ref, const upd_field_t *uf) {
|
|
DBUG_TRACE;
|
|
|
|
trx_t *trx = nullptr;
|
|
|
|
dberr_t err = apply_undolog(ctx->get_mtr(), trx, index, ref, uf);
|
|
ut_a(err == DB_SUCCESS);
|
|
}
|
|
|
|
/** Rollback modification of a uncompressed LOB.
|
|
@param[in] ctx the delete operation context information.
|
|
@param[in] index clustered index in which LOB is present
|
|
@param[in] trxid the transaction that is being rolled back.
|
|
@param[in] undo_no during rollback to savepoint, rollback only
|
|
upto this undo number.
|
|
@param[in] ref reference to LOB that is being rolled back.
|
|
@param[in] rec_type undo record type.
|
|
@param[in] uf update vector of the concerned field. */
|
|
static void rollback(DeleteContext *ctx, dict_index_t *index, trx_id_t trxid,
|
|
undo_no_t undo_no, ref_t &ref, ulint rec_type,
|
|
const upd_field_t *uf) {
|
|
DBUG_TRACE;
|
|
|
|
ut_ad(ctx->m_rollback);
|
|
|
|
if (uf != nullptr && uf->lob_diffs != nullptr && uf->lob_diffs->size() > 0) {
|
|
/* Undo log contains changes done to the LOB. This must have
|
|
been a small change done to LOB. Apply the undo log on the
|
|
LOB.*/
|
|
rollback_from_undolog(ctx, index, ref, uf);
|
|
return;
|
|
}
|
|
|
|
mtr_t *mtr = ctx->get_mtr();
|
|
|
|
page_no_t first_page_no = ref.page_no();
|
|
page_id_t page_id(ref.space_id(), first_page_no);
|
|
page_size_t page_size(dict_table_page_size(index->table));
|
|
|
|
first_page_t first(mtr, index);
|
|
first.load_x(page_id, page_size);
|
|
|
|
flst_base_node_t *flst = first.index_list();
|
|
fil_addr_t node_loc = flst_get_first(flst, mtr);
|
|
|
|
while (!fil_addr_is_null(node_loc)) {
|
|
flst_node_t *node = first.addr2ptr_x(node_loc);
|
|
index_entry_t cur_entry(node, mtr, index);
|
|
|
|
if (cur_entry.can_rollback(trxid, undo_no)) {
|
|
node_loc = cur_entry.make_old_version_current(index, trxid, first);
|
|
|
|
} else {
|
|
node_loc = cur_entry.get_next();
|
|
}
|
|
}
|
|
|
|
if (rec_type == TRX_UNDO_INSERT_REC || first.is_empty()) {
|
|
if (dict_index_is_online_ddl(index)) {
|
|
row_log_table_blob_free(index, ref.page_no());
|
|
}
|
|
|
|
first.free_all_index_pages();
|
|
first.dealloc();
|
|
|
|
} else {
|
|
#ifdef UNIV_DEBUG
|
|
const ulint lob_size = ref.length();
|
|
fil_addr_t first_node_loc = flst_get_first(flst, mtr);
|
|
ut_ad(validate_size(lob_size, index, first_node_loc, mtr));
|
|
#endif /* UNIV_DEBUG */
|
|
}
|
|
|
|
ref.set_page_no(FIL_NULL, mtr);
|
|
ref.set_length(0, mtr);
|
|
|
|
DBUG_EXECUTE_IF("crash_endof_lob_rollback", DBUG_SUICIDE(););
|
|
}
|
|
|
|
/** Rollback modification of a compressed LOB.
|
|
@param[in] ctx the delete operation context information.
|
|
@param[in] index clustered index in which LOB is present
|
|
@param[in] trxid the transaction that is being rolled back.
|
|
@param[in] undo_no during rollback to savepoint, rollback only
|
|
upto this undo number.
|
|
@param[in] ref reference to LOB that is purged.
|
|
@param[in] rec_type undo record type. */
|
|
static void z_rollback(DeleteContext *ctx, dict_index_t *index, trx_id_t trxid,
|
|
undo_no_t undo_no, ref_t &ref, ulint rec_type) {
|
|
ut_ad(ctx->m_rollback);
|
|
const ulint commit_freq = 1;
|
|
ulint n_entries = 0;
|
|
|
|
mtr_t local_mtr;
|
|
mtr_start(&local_mtr);
|
|
|
|
page_no_t first_page_no = ref.page_no();
|
|
page_id_t page_id(ref.space_id(), first_page_no);
|
|
page_size_t page_size(dict_table_page_size(index->table));
|
|
|
|
z_first_page_t first(&local_mtr, index);
|
|
first.load_x(page_id, page_size);
|
|
|
|
flst_base_node_t *flst = first.index_list();
|
|
fil_addr_t node_loc = flst_get_first(flst, &local_mtr);
|
|
|
|
while (!fil_addr_is_null(node_loc)) {
|
|
flst_node_t *node = first.addr2ptr_x(node_loc);
|
|
z_index_entry_t cur_entry(node, &local_mtr, index);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
ulint idx_len = first.get_index_list_length();
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
if (cur_entry.can_rollback(trxid, undo_no)) {
|
|
node_loc = cur_entry.make_old_version_current(index, trxid, first);
|
|
|
|
n_entries++;
|
|
|
|
} else {
|
|
node_loc = cur_entry.get_next();
|
|
}
|
|
|
|
if ((n_entries % commit_freq == 0) && !first.is_empty()) {
|
|
mtr_commit(&local_mtr);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
if (idx_len == 1) {
|
|
DBUG_EXECUTE_IF("crash_middle_zlob_rollback", DBUG_SUICIDE(););
|
|
}
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
mtr_start(&local_mtr);
|
|
first.load_x(page_id, page_size);
|
|
}
|
|
}
|
|
|
|
if (rec_type == TRX_UNDO_INSERT_REC || first.is_empty()) {
|
|
if (dict_index_is_online_ddl(index)) {
|
|
row_log_table_blob_free(index, ref.page_no());
|
|
}
|
|
|
|
/* Ensure that the btr mtr is not holding any x-latch on the first page of
|
|
* LOB. */
|
|
ut_ad(!mtr_is_block_fix(ctx->get_mtr(), first.get_block(),
|
|
MTR_MEMO_PAGE_X_FIX, index->table));
|
|
|
|
first.free_all_frag_node_pages();
|
|
first.free_all_index_pages();
|
|
first.dealloc();
|
|
|
|
} else {
|
|
ut_ad(first.validate());
|
|
}
|
|
|
|
ut_ad(ctx->get_page_zip() != nullptr);
|
|
ref.set_page_no(FIL_NULL, 0);
|
|
ref.set_length(0, 0);
|
|
ctx->x_latch_rec_page(&local_mtr);
|
|
ctx->zblob_write_blobref(ctx->m_field_no, &local_mtr);
|
|
|
|
mtr_commit(&local_mtr);
|
|
|
|
DBUG_EXECUTE_IF("crash_endof_zlob_rollback", DBUG_SUICIDE(););
|
|
}
|
|
|
|
/** Purge a compressed LOB.
|
|
@param[in] ctx the delete operation context information.
|
|
@param[in] index clustered index in which LOB is present
|
|
@param[in] trxid the transaction that is being purged.
|
|
@param[in] undo_no during rollback to savepoint, purge only upto
|
|
this undo number.
|
|
@param[in] ref reference to LOB that is purged.
|
|
@param[in] rec_type undo record type. */
|
|
static void z_purge(DeleteContext *ctx, dict_index_t *index, trx_id_t trxid,
|
|
undo_no_t undo_no, ref_t &ref, ulint rec_type) {
|
|
const bool is_rollback = ctx->m_rollback;
|
|
|
|
if (is_rollback) {
|
|
z_rollback(ctx, index, trxid, undo_no, ref, rec_type);
|
|
return;
|
|
}
|
|
|
|
mtr_t *mtr = ctx->get_mtr();
|
|
|
|
page_no_t first_page_no = ref.page_no();
|
|
page_id_t page_id(ref.space_id(), first_page_no);
|
|
|
|
z_first_page_t first(mtr, index);
|
|
first.load_x(first_page_no);
|
|
|
|
ut_ad(first.validate());
|
|
|
|
trx_id_t last_trx_id = first.get_last_trx_id();
|
|
undo_no_t last_undo_no = first.get_last_trx_undo_no();
|
|
ut_ad(first.get_page_type() == FIL_PAGE_TYPE_ZLOB_FIRST);
|
|
|
|
flst_base_node_t *flst = first.index_list();
|
|
flst_base_node_t *free_list = first.free_list();
|
|
fil_addr_t node_loc = flst_get_first(flst, mtr);
|
|
|
|
z_index_entry_t cur_entry(mtr, index);
|
|
|
|
while (!fil_addr_is_null(node_loc)) {
|
|
flst_node_t *node = first.addr2ptr_x(node_loc);
|
|
cur_entry.reset(node);
|
|
|
|
flst_base_node_t *vers = cur_entry.get_versions_list();
|
|
fil_addr_t ver_loc = flst_get_first(vers, mtr);
|
|
|
|
/* Scan the older versions. */
|
|
while (!fil_addr_is_null(ver_loc)) {
|
|
flst_node_t *ver_node = first.addr2ptr_x(ver_loc);
|
|
z_index_entry_t vers_entry(ver_node, mtr, index);
|
|
|
|
if (vers_entry.can_be_purged(trxid, undo_no)) {
|
|
ver_loc =
|
|
vers_entry.purge_version(index, trxid, first, vers, free_list);
|
|
} else {
|
|
ver_loc = vers_entry.get_next();
|
|
}
|
|
}
|
|
|
|
node_loc = cur_entry.get_next();
|
|
cur_entry.reset(nullptr);
|
|
}
|
|
|
|
bool ok_to_free_2 = (rec_type == TRX_UNDO_UPD_EXIST_REC) &&
|
|
!first.can_be_partially_updated() &&
|
|
(last_trx_id == trxid) && (last_undo_no == undo_no);
|
|
|
|
if (rec_type == TRX_UNDO_DEL_MARK_REC || ok_to_free_2) {
|
|
if (dict_index_is_online_ddl(index)) {
|
|
row_log_table_blob_free(index, ref.page_no());
|
|
}
|
|
|
|
first.free_all_frag_node_pages();
|
|
first.free_all_index_pages();
|
|
first.dealloc();
|
|
|
|
} else {
|
|
ut_ad(first.validate());
|
|
}
|
|
|
|
if (ctx->get_page_zip() != nullptr) {
|
|
ref.set_page_no(FIL_NULL, 0);
|
|
ref.set_length(0, 0);
|
|
ctx->zblob_write_blobref(ctx->m_field_no, mtr);
|
|
} else {
|
|
/* Note that page_zip will be NULL in
|
|
row_purge_upd_exist_or_extern(). */
|
|
ref.set_page_no(FIL_NULL, mtr);
|
|
ref.set_length(0, mtr);
|
|
}
|
|
}
|
|
|
|
/** Purge an uncompressed LOB.
|
|
@param[in] ctx the delete operation context information.
|
|
@param[in] index clustered index in which LOB is present
|
|
@param[in] trxid the transaction that is being purged.
|
|
@param[in] undo_no during rollback to savepoint, purge only upto
|
|
this undo number.
|
|
@param[in] ref reference to LOB that is purged.
|
|
@param[in] rec_type undo record type.
|
|
@param[in] uf the update vector for the field. */
|
|
void purge(DeleteContext *ctx, dict_index_t *index, trx_id_t trxid,
|
|
undo_no_t undo_no, ref_t ref, ulint rec_type,
|
|
const upd_field_t *uf) {
|
|
DBUG_TRACE;
|
|
|
|
mtr_t *mtr = ctx->get_mtr();
|
|
const bool is_rollback = ctx->m_rollback;
|
|
|
|
if (ref.is_null()) {
|
|
/* In the rollback, we may encounter a clustered index
|
|
record with some unwritten off-page columns. There is
|
|
nothing to free then. */
|
|
ut_a(ctx->m_rollback);
|
|
return;
|
|
}
|
|
|
|
if (!ref.is_owner() || ref.page_no() == FIL_NULL || ref.length() == 0 ||
|
|
(ctx->m_rollback && ref.is_inherited())) {
|
|
return;
|
|
}
|
|
|
|
if (!is_rollback && uf != nullptr && uf->lob_diffs != nullptr &&
|
|
uf->lob_diffs->size() > 0) {
|
|
/* Undo record contains LOB diffs. So purge shouldn't look
|
|
at the LOB. */
|
|
return;
|
|
}
|
|
|
|
space_id_t space_id = ref.space_id();
|
|
|
|
/* The current entry - it is the latest version. */
|
|
index_entry_t cur_entry(mtr, index);
|
|
|
|
page_no_t first_page_no = ref.page_no();
|
|
page_id_t page_id(space_id, first_page_no);
|
|
page_size_t page_size(dict_table_page_size(index->table));
|
|
page_type_t page_type =
|
|
first_page_t::get_page_type(index, page_id, page_size);
|
|
|
|
if (page_type == FIL_PAGE_TYPE_ZBLOB || page_type == FIL_PAGE_TYPE_BLOB ||
|
|
page_type == FIL_PAGE_SDI_BLOB || page_type == FIL_PAGE_SDI_ZBLOB) {
|
|
lob::Deleter free_blob(*ctx);
|
|
free_blob.destroy();
|
|
return;
|
|
}
|
|
|
|
if (page_type == FIL_PAGE_TYPE_ZLOB_FIRST) {
|
|
z_purge(ctx, index, trxid, undo_no, ref, rec_type);
|
|
return;
|
|
}
|
|
|
|
first_page_t first(mtr, index);
|
|
first.load_x(page_id, page_size);
|
|
|
|
ut_a(page_type == FIL_PAGE_TYPE_LOB_FIRST);
|
|
|
|
if (is_rollback) {
|
|
rollback(ctx, index, trxid, undo_no, ref, rec_type, uf);
|
|
return;
|
|
}
|
|
|
|
trx_id_t last_trx_id = first.get_last_trx_id();
|
|
undo_no_t last_undo_no = first.get_last_trx_undo_no();
|
|
|
|
flst_base_node_t *flst = first.index_list();
|
|
flst_base_node_t *free_list = first.free_list();
|
|
fil_addr_t node_loc = flst_get_first(flst, mtr);
|
|
|
|
while (!fil_addr_is_null(node_loc)) {
|
|
flst_node_t *node = first.addr2ptr_x(node_loc);
|
|
cur_entry.reset(node);
|
|
|
|
flst_base_node_t *vers = cur_entry.get_versions_list();
|
|
fil_addr_t ver_loc = flst_get_first(vers, mtr);
|
|
|
|
/* Scan the older versions. */
|
|
while (!fil_addr_is_null(ver_loc)) {
|
|
flst_node_t *ver_node = first.addr2ptr_x(ver_loc);
|
|
index_entry_t vers_entry(ver_node, mtr, index);
|
|
|
|
if (vers_entry.can_be_purged(trxid, undo_no)) {
|
|
ver_loc = vers_entry.purge_version(index, trxid, vers, free_list);
|
|
} else {
|
|
ver_loc = vers_entry.get_next();
|
|
}
|
|
}
|
|
|
|
node_loc = cur_entry.get_next();
|
|
cur_entry.reset(nullptr);
|
|
}
|
|
|
|
bool ok_to_free = (rec_type == TRX_UNDO_UPD_EXIST_REC) &&
|
|
!first.can_be_partially_updated() &&
|
|
(last_trx_id == trxid) && (last_undo_no == undo_no);
|
|
|
|
if (rec_type == TRX_UNDO_DEL_MARK_REC || ok_to_free) {
|
|
ut_ad(first.get_page_type() == FIL_PAGE_TYPE_LOB_FIRST);
|
|
|
|
if (dict_index_is_online_ddl(index)) {
|
|
row_log_table_blob_free(index, ref.page_no());
|
|
}
|
|
first.free_all_data_pages();
|
|
|
|
first.free_all_index_pages();
|
|
first.dealloc();
|
|
}
|
|
|
|
ref.set_page_no(FIL_NULL, mtr);
|
|
ref.set_length(0, mtr);
|
|
}
|
|
|
|
} /* namespace lob */
|