polardbxengine/sql/rpl_transaction_write_set_c...

229 lines
7.4 KiB
C++

/* Copyright (c) 2014, 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 "sql/rpl_transaction_write_set_ctx.h"
#include <stddef.h>
#include <utility>
#include "m_string.h"
#include "my_dbug.h"
#include "mysql/psi/mysql_mutex.h"
#include "mysql/service_mysql_alloc.h"
#include "mysql/service_rpl_transaction_write_set.h" // Transaction_write_set
#include "sql/current_thd.h" // current_thd
#include "sql/debug_sync.h" // debug_sync_set_action
#include "sql/mysqld_thd_manager.h" // Global_THD_manager
#include "sql/psi_memory_key.h"
#include "sql/sql_class.h" // THD
#include "sql/transaction_info.h"
Rpl_transaction_write_set_ctx::Rpl_transaction_write_set_ctx()
: m_has_missing_keys(false), m_has_related_foreign_keys(false) {
DBUG_TRACE;
/*
In order to speed-up small transactions write-set extraction,
we preallocate 12 elements.
12 is a sufficient number to hold write-sets for single
statement transactions, even on tables with foreign keys.
*/
write_set.reserve(12);
}
void Rpl_transaction_write_set_ctx::add_write_set(uint64 hash) {
DBUG_TRACE;
write_set.push_back(hash);
}
std::vector<uint64> *Rpl_transaction_write_set_ctx::get_write_set() {
DBUG_TRACE;
return &write_set;
}
void Rpl_transaction_write_set_ctx::clear_write_set() {
DBUG_TRACE;
write_set.clear();
savepoint.clear();
savepoint_list.clear();
m_has_missing_keys = m_has_related_foreign_keys = false;
}
void Rpl_transaction_write_set_ctx::set_has_missing_keys() {
DBUG_TRACE;
m_has_missing_keys = true;
}
bool Rpl_transaction_write_set_ctx::get_has_missing_keys() {
DBUG_TRACE;
return m_has_missing_keys;
}
void Rpl_transaction_write_set_ctx::set_has_related_foreign_keys() {
DBUG_TRACE;
m_has_related_foreign_keys = true;
}
bool Rpl_transaction_write_set_ctx::get_has_related_foreign_keys() {
DBUG_TRACE;
return m_has_related_foreign_keys;
}
/**
Implementation of service_rpl_transaction_write_set, see
@file include/mysql/service_rpl_transaction_write_set.h
*/
Transaction_write_set *get_transaction_write_set(unsigned long m_thread_id) {
DBUG_TRACE;
THD *thd = nullptr;
Transaction_write_set *result_set = nullptr;
Find_thd_with_id find_thd_with_id(m_thread_id);
thd = Global_THD_manager::get_instance()->find_thd(&find_thd_with_id);
if (thd) {
Rpl_transaction_write_set_ctx *transaction_write_set_ctx =
thd->get_transaction()->get_transaction_write_set_ctx();
int write_set_size = transaction_write_set_ctx->get_write_set()->size();
if (write_set_size == 0) {
mysql_mutex_unlock(&thd->LOCK_thd_data);
return nullptr;
}
result_set = (Transaction_write_set *)my_malloc(
key_memory_write_set_extraction, sizeof(Transaction_write_set), MYF(0));
result_set->write_set_size = write_set_size;
result_set->write_set = (unsigned long long *)my_malloc(
key_memory_write_set_extraction,
write_set_size * sizeof(unsigned long long), MYF(0));
int result_set_index = 0;
for (std::vector<uint64>::iterator it =
transaction_write_set_ctx->get_write_set()->begin();
it != transaction_write_set_ctx->get_write_set()->end(); ++it) {
uint64 temp = *it;
result_set->write_set[result_set_index++] = temp;
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
}
return result_set;
}
void Rpl_transaction_write_set_ctx::add_savepoint(char *name) {
DBUG_TRACE;
std::string identifier(name);
DBUG_EXECUTE_IF("transaction_write_set_savepoint_clear_on_commit_rollback", {
DBUG_ASSERT(savepoint.size() == 0);
DBUG_ASSERT(write_set.size() == 0);
DBUG_ASSERT(savepoint_list.size() == 0);
});
DBUG_EXECUTE_IF("transaction_write_set_savepoint_level",
DBUG_ASSERT(savepoint.size() == 0););
std::map<std::string, size_t>::iterator it;
/*
Savepoint with the same name, the old savepoint is deleted and a new one
is set
*/
if ((it = savepoint.find(name)) != savepoint.end()) savepoint.erase(it);
savepoint.insert(
std::pair<std::string, size_t>(identifier, write_set.size()));
DBUG_EXECUTE_IF(
"transaction_write_set_savepoint_add_savepoint",
DBUG_ASSERT(savepoint.find(identifier)->second == write_set.size()););
}
void Rpl_transaction_write_set_ctx::del_savepoint(char *name) {
DBUG_TRACE;
std::string identifier(name);
DBUG_EXECUTE_IF("transaction_write_set_savepoint_block_before_release", {
const char act[] = "now wait_for signal.unblock_release";
DBUG_ASSERT(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act)));
});
savepoint.erase(identifier);
}
void Rpl_transaction_write_set_ctx::rollback_to_savepoint(char *name) {
DBUG_TRACE;
size_t position = 0;
std::string identifier(name);
std::map<std::string, size_t>::iterator elem;
if ((elem = savepoint.find(identifier)) != savepoint.end()) {
DBUG_ASSERT(elem->second <= write_set.size());
DBUG_EXECUTE_IF("transaction_write_set_savepoint_block_before_rollback", {
const char act[] = "now wait_for signal.unblock_rollback";
DBUG_ASSERT(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act)));
});
position = elem->second;
// Remove all savepoints created after the savepoint identifier given as
// parameter
std::map<std::string, size_t>::iterator it = savepoint.begin();
while (it != savepoint.end()) {
if (it->second > position)
savepoint.erase(it++);
else
++it;
}
/*
We need to check that:
- starting index of the range we want to erase does exist.
- write_set size have elements to be removed
*/
if (write_set.size() > 0 && position < write_set.size()) {
// Clear all elements after savepoint
write_set.erase(write_set.begin() + position, write_set.end());
}
DBUG_EXECUTE_IF("transaction_write_set_savepoint_add_savepoint",
DBUG_ASSERT(write_set.size() == 1););
DBUG_EXECUTE_IF("transaction_write_set_size_2",
DBUG_ASSERT(write_set.size() == 2););
}
}
void Rpl_transaction_write_set_ctx::reset_savepoint_list() {
DBUG_TRACE;
savepoint_list.push_back(savepoint);
savepoint.clear();
}
void Rpl_transaction_write_set_ctx::restore_savepoint_list() {
DBUG_TRACE;
if (!savepoint_list.empty()) {
savepoint = savepoint_list.back();
savepoint_list.pop_back();
}
}