Commit e1013725 authored by Sergei Golubchik's avatar Sergei Golubchik

Merge branch 'github/10.5' into bb-10.5-release

parents 5018b998 23c53df4
Branches unavailable
Tags unavailable
No related merge requests found
MYSQL_VERSION_MAJOR=10
MYSQL_VERSION_MINOR=5
MYSQL_VERSION_PATCH=4
MYSQL_VERSION_PATCH=5
SERVER_MATURITY=stable
# Tests for delete with INNODB
#
# MDEV-22187: SIGSEGV in ha_innobase::cmp_ref on DELETE
#
SET @save_sort_buffer_size= @@sort_buffer_size;
SET sort_buffer_size=1024;
CREATE TABLE t1(c1 CHAR(255) PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (0), ('a'), ('b');
ANALYZE TABLE t1 PERSISTENT FOR ALL;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
SELECT * FROM t1;
c1
0
a
b
EXPLAIN DELETE b FROM t1 AS a JOIN t1 AS b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE a index NULL PRIMARY 255 NULL 3 Using index
1 SIMPLE b ALL NULL NULL NULL NULL 3
DELETE b FROM t1 AS a JOIN t1 AS b;
SELECT * FROM t1;
c1
SET sort_buffer_size=@save_sort_buffer_size;
DROP TABLE t1;
--source include/have_innodb.inc
--source include/have_sequence.inc
--echo # Tests for delete with INNODB
--echo #
--echo # MDEV-22187: SIGSEGV in ha_innobase::cmp_ref on DELETE
--echo #
SET @save_sort_buffer_size= @@sort_buffer_size;
SET sort_buffer_size=1024;
CREATE TABLE t1(c1 CHAR(255) PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (0), ('a'), ('b');
ANALYZE TABLE t1 PERSISTENT FOR ALL;
SELECT * FROM t1;
EXPLAIN DELETE b FROM t1 AS a JOIN t1 AS b;
DELETE b FROM t1 AS a JOIN t1 AS b;
SELECT * FROM t1;
SET sort_buffer_size=@save_sort_buffer_size;
DROP TABLE t1;
CREATE TEMPORARY SEQUENCE seq_1;
XA START '3';
CREATE TEMPORARY TABLE tmp_1(c INT);
XA END '3';
XA PREPARE '3';
DROP TEMPORARY TABLE tmp_1;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
ALTER TABLE tmp_1 DROP COLUMN c;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
DROP TEMPORARY SEQUENCE seq_1;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
ALTER SEQUENCE seq_1 INCREMENT BY 1;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
CREATE TEMPORARY TABLE tmp_2(c INT);
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
CREATE TEMPORARY SEQUENCE seq_2;
ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state
XA ROLLBACK '3';
# Proof of correct logging incl empty XA-PREPARE
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY SEQUENCE seq_1
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE tmp_1(c INT)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # XA START X'33',X'',1 GTID #-#-#
master-bin.000001 # Query # # XA END X'33',X'',1
master-bin.000001 # XA_prepare # # XA PREPARE X'33',X'',1
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # XA ROLLBACK X'33',X'',1
# The test verifies execution and binary logging of user XA that produce empty
# XA-PREPARE group of events.
--source include/have_binlog_format_mixed.inc
--source include/have_innodb.inc
# MDEV-22420 DDL on temporary object is prohibited when XA is in prepare state
# Temporary sequnce may not be created within a transaction
CREATE TEMPORARY SEQUENCE seq_1;
XA START '3';
CREATE TEMPORARY TABLE tmp_1(c INT);
XA END '3';
XA PREPARE '3';
--error ER_XAER_RMFAIL
DROP TEMPORARY TABLE tmp_1;
--error ER_XAER_RMFAIL
ALTER TABLE tmp_1 DROP COLUMN c;
--error ER_XAER_RMFAIL
DROP TEMPORARY SEQUENCE seq_1;
--error ER_XAER_RMFAIL
ALTER SEQUENCE seq_1 INCREMENT BY 1;
--error ER_XAER_RMFAIL
CREATE TEMPORARY TABLE tmp_2(c INT);
--error ER_XAER_RMFAIL
CREATE TEMPORARY SEQUENCE seq_2;
# Cleanup
XA ROLLBACK '3';
--echo # Proof of correct logging incl empty XA-PREPARE
--source include/show_binlog_events.inc
......@@ -39,7 +39,7 @@ SELECT COUNT(*) = 0 FROM t1;
--connection node_2
--sleep 1
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'Init' AND INFO = 'FLUSH TABLES WITH READ LOCK'
--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE (STATE = 'Init' OR STATE = 'starting') AND INFO = 'FLUSH TABLES WITH READ LOCK'
--source include/wait_condition.inc
--source include/galera_clear_sync_point.inc
......
......@@ -200,6 +200,7 @@ fi
# New filter - exclude everything except dirs (schemas) and innodb files
FILTER="-f '- /lost+found'
-f '- /.zfs'
-f '- /.fseventsd'
-f '- /.Trashes'
-f '+ /wsrep_sst_binlog.tar'
......@@ -357,7 +358,7 @@ EOF
[ "$OS" = "Linux" ] && count=$(grep -c processor /proc/cpuinfo)
[ "$OS" = "Darwin" -o "$OS" = "FreeBSD" ] && count=$(sysctl -n hw.ncpu)
find . -maxdepth 1 -mindepth 1 -type d -not -name "lost+found" \
find . -maxdepth 1 -mindepth 1 -type d -not -name "lost+found" -not -name ".zfs" \
-print0 | xargs -I{} -0 -P $count \
rsync ${STUNNEL:+--rsh="$STUNNEL"} \
--owner --group --perms --links --specials \
......@@ -440,6 +441,7 @@ timeout = 300
$SILENT
[$MODULE]
path = $WSREP_SST_OPT_DATA
exclude = .zfs
[$MODULE-log_dir]
path = $WSREP_LOG_DIR
[$MODULE-data_dir]
......
......@@ -3737,13 +3737,16 @@ static void report_cut_value_error(THD *thd, uint row_count, const char *fname)
{
size_t fn_len= strlen(fname);
char *fname_upper= (char *) my_alloca(fn_len + 1);
fname_upper[fn_len]= 0;
for (; fn_len; fn_len--)
fname_upper[fn_len-1]= my_toupper(&my_charset_latin1, fname[fn_len-1]);
if (!fname_upper)
fname_upper= (char*) fname; // Out of memory
else
memcpy(fname_upper, fname, fn_len+1);
my_caseup_str(&my_charset_latin1, fname_upper);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_CUT_VALUE_GROUP_CONCAT,
ER_THD(thd, ER_CUT_VALUE_GROUP_CONCAT),
row_count, fname_upper);
my_afree(fname_upper);
}
......
This diff is collapsed.
#ifndef MDL_H
#define MDL_H
/* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2020, MariaDB
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
......@@ -16,6 +17,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "sql_plist.h"
#include "ilist.h"
#include <my_sys.h>
#include <m_string.h>
#include <mysql_com.h>
......@@ -685,7 +687,7 @@ class MDL_wait_for_subgraph
threads/contexts.
*/
class MDL_ticket : public MDL_wait_for_subgraph
class MDL_ticket : public MDL_wait_for_subgraph, public ilist_node<>
{
public:
/**
......@@ -694,15 +696,9 @@ class MDL_ticket : public MDL_wait_for_subgraph
*/
MDL_ticket *next_in_context;
MDL_ticket **prev_in_context;
/**
Pointers for participating in the list of satisfied/pending requests
for the lock. Externally accessible.
*/
MDL_ticket *next_in_lock;
MDL_ticket **prev_in_lock;
public:
#ifdef WITH_WSREP
void wsrep_report(bool debug);
void wsrep_report(bool debug) const;
#endif /* WITH_WSREP */
bool has_pending_conflicting_lock() const;
......
......@@ -3565,8 +3565,11 @@ void LEX::print(String *str, enum_query_type query_type)
value->print(str, query_type);
}
str->append(STRING_WITH_LEN(" WHERE "));
sel->where->print(str, query_type);
if (sel->where)
{
str->append(STRING_WITH_LEN(" WHERE "));
sel->where->print(str, query_type);
}
if (sel->order_list.elements)
{
......
......@@ -4890,6 +4890,8 @@ mysql_execute_command(THD *thd)
}
else
{
if (thd->transaction->xid_state.check_has_uncommitted_xa())
goto error;
status_var_decrement(thd->status_var.com_stat[lex->sql_command]);
status_var_increment(thd->status_var.com_drop_tmp_table);
......
/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
Copyright (c) 2020, MariaDB
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
......@@ -2368,7 +2369,7 @@ void wsrep_to_isolation_end(THD *thd)
*/
void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
MDL_ticket *ticket,
const MDL_ticket *ticket,
const MDL_key *key)
{
/* Fallback to the non-wsrep behaviour */
......
/* Copyright 2008-2017 Codership Oy <http://www.codership.com>
Copyright (c) 2020, MariaDB
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
......@@ -527,7 +528,7 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr);
extern void
wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
MDL_ticket *ticket,
const MDL_ticket *ticket,
const MDL_key *key);
enum wsrep_thread_type {
......
......@@ -537,22 +537,6 @@ btr_page_alloc_low(
seg_header, hint_page_no, file_direction,
true, mtr, init_mtr);
#ifdef UNIV_DEBUG_SCRUBBING
if (block != NULL) {
fprintf(stderr,
"alloc %lu:%lu to index: %lu root: %lu\n",
buf_block_get_page_no(block),
buf_block_get_space(block),
index->id,
dict_index_get_page(index));
} else {
fprintf(stderr,
"failed alloc index: %lu root: %lu\n",
index->id,
dict_index_get_page(index));
}
#endif /* UNIV_DEBUG_SCRUBBING */
return block;
}
......
......@@ -2063,6 +2063,40 @@ inline void buf_pool_t::write_unlock_all_page_hash()
old_page_hash->write_unlock_all();
}
namespace
{
struct find_interesting_trx
{
void operator()(const trx_t &trx)
{
if (trx.state == TRX_STATE_NOT_STARTED)
return;
if (trx.mysql_thd == nullptr)
return;
if (withdraw_started <= trx.start_time)
return;
if (!found)
{
ib::warn() << "The following trx might hold "
"the blocks in buffer pool to "
"be withdrawn. Buffer pool "
"resizing can complete only "
"after all the transactions "
"below release the blocks.";
found= true;
}
lock_trx_print_wait_and_mvcc_state(stderr, &trx, current_time);
}
bool &found;
time_t withdraw_started;
time_t current_time;
};
} // namespace
/** Resize from srv_buf_pool_old_size to srv_buf_pool_size. */
inline void buf_pool_t::resize()
......@@ -2160,30 +2194,9 @@ inline void buf_pool_t::resize()
}
lock_mutex_enter();
mutex_enter(&trx_sys.mutex);
bool found = false;
for (trx_t* trx = UT_LIST_GET_FIRST(trx_sys.trx_list);
trx != NULL;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
if (trx->state != TRX_STATE_NOT_STARTED
&& trx->mysql_thd != NULL
&& withdraw_started > trx->start_time) {
if (!found) {
ib::warn() <<
"The following trx might hold"
" the blocks in buffer pool to"
" be withdrawn. Buffer pool"
" resizing can complete only"
" after all the transactions"
" below release the blocks.";
found = true;
}
lock_trx_print_wait_and_mvcc_state(
stderr, trx, current_time);
}
}
mutex_exit(&trx_sys.mutex);
trx_sys.trx_list.for_each(find_interesting_trx{
found, withdraw_started, current_time});
lock_mutex_exit();
withdraw_started = current_time;
......
......@@ -4824,23 +4824,23 @@ static void innobase_kill_query(handlerton*, THD *thd, enum thd_kill_levels)
DBUG_VOID_RETURN;
#endif /* WITH_WSREP */
lock_mutex_enter();
mutex_enter(&trx_sys.mutex);
trx_sys.trx_list.freeze();
trx_mutex_enter(trx);
/* It is possible that innobase_close_connection() is concurrently
being executed on our victim. Even if the trx object is later
reused for another client connection or a background transaction,
its trx->mysql_thd will differ from our thd.
trx_t::state changes are protected by trx_t::mutex, and
trx_sys.trx_list is protected by trx_sys.mutex, in
both trx_create() and trx_free().
trx_sys.trx_list is thread-safe. It's freezed to 'protect'
trx_t. However, trx_t::commit_in_memory() changes a trx_t::state
of autocommit non-locking transactions without any protection.
At this point, trx may have been reallocated for another client
connection, or for a background operation. In that case, either
trx_t::state or trx_t::mysql_thd should not match our expectations. */
bool cancel= trx->mysql_thd == thd && trx->state == TRX_STATE_ACTIVE &&
!trx->lock.was_chosen_as_deadlock_victim;
mutex_exit(&trx_sys.mutex);
trx_sys.trx_list.unfreeze();
if (!cancel);
else if (lock_t *lock= trx->lock.wait_lock)
lock_cancel_waiting_and_release(lock);
......
......@@ -502,7 +502,8 @@ struct fil_space_t
last_freed_lsn= lsn;
}
/** Clear all freed ranges */
/** Clear all freed ranges for undo tablespace when InnoDB
encounters TRIM redo log record */
void clear_freed_ranges()
{
std::lock_guard<std::mutex> freed_lock(freed_range_mutex);
......
......@@ -511,6 +511,7 @@ inline void mtr_t::memcpy(const buf_block_t &b, void *dest, const void *str,
@param[in,out] b buffer page */
inline void mtr_t::init(buf_block_t *b)
{
ut_ad(!m_freed_pages);
b->page.status= buf_page_t::INIT_ON_FLUSH;
if (m_log_mode != MTR_LOG_ALL)
......
......@@ -580,16 +580,11 @@ struct mtr_t {
void add_freed_offset(page_id_t id)
{
ut_ad(m_user_space == NULL || id.space() == m_user_space->id);
m_freed_ranges.add_value(id.page_no());
if (!m_freed_pages)
m_freed_pages= new range_set();
m_freed_pages->add_value(id.page_no());
}
/** Clear the freed pages */
void clear_freed_ranges()
{
m_freed_ranges.clear();
m_freed_in_system_tablespace= 0;
m_trim_pages= false;
}
private:
/** Log a write of a byte string to a page.
@param block buffer page
......@@ -685,7 +680,7 @@ struct mtr_t {
lsn_t m_commit_lsn;
/** set of freed page ids */
range_set m_freed_ranges;
range_set *m_freed_pages= nullptr;
};
#include "mtr0mtr.ic"
......
......@@ -41,8 +41,7 @@ Created 3/26/1996 Heikki Tuuri
#ifdef WITH_WSREP
#include "trx0xa.h"
#endif /* WITH_WSREP */
typedef UT_LIST_BASE_NODE_T(trx_t) trx_ut_list_t;
#include "ilist.h"
/** Checks if a page address is the trx sys header page.
@param[in] page_id page id
......@@ -803,6 +802,49 @@ class rw_trx_hash_t
}
};
class thread_safe_trx_ilist_t
{
public:
void create() { mutex_create(LATCH_ID_TRX_SYS, &mutex); }
void close() { mutex_free(&mutex); }
bool empty() const
{
mutex_enter(&mutex);
auto result= trx_list.empty();
mutex_exit(&mutex);
return result;
}
void push_front(trx_t &trx)
{
mutex_enter(&mutex);
trx_list.push_front(trx);
mutex_exit(&mutex);
}
void remove(trx_t &trx)
{
mutex_enter(&mutex);
trx_list.remove(trx);
mutex_exit(&mutex);
}
template <typename Callable> void for_each(Callable &&callback) const
{
mutex_enter(&mutex);
for (const auto &trx : trx_list)
callback(trx);
mutex_exit(&mutex);
}
void freeze() const { mutex_enter(&mutex); }
void unfreeze() const { mutex_exit(&mutex); }
private:
alignas(CACHE_LINE_SIZE) mutable TrxSysMutex mutex;
alignas(CACHE_LINE_SIZE) ilist<trx_t> trx_list;
};
/** The transaction system central memory data structure. */
class trx_sys_t
......@@ -833,11 +875,8 @@ class trx_sys_t
*/
MY_ALIGNED(CACHE_LINE_SIZE) Atomic_counter<uint32_t> rseg_history_len;
/** Mutex protecting trx_list AND NOTHING ELSE. */
MY_ALIGNED(CACHE_LINE_SIZE) mutable TrxSysMutex mutex;
/** List of all transactions. */
MY_ALIGNED(CACHE_LINE_SIZE) trx_ut_list_t trx_list;
thread_safe_trx_ilist_t trx_list;
MY_ALIGNED(CACHE_LINE_SIZE)
/** Temporary rollback segments */
......@@ -978,7 +1017,6 @@ class trx_sys_t
void snapshot_ids(trx_t *caller_trx, trx_ids_t *ids, trx_id_t *max_trx_id,
trx_id_t *min_trx_no)
{
ut_ad(!mutex_own(&mutex));
snapshot_ids_arg arg(ids);
while ((arg.m_id= get_rw_trx_hash_version()) != get_max_trx_id())
......@@ -1075,9 +1113,7 @@ class trx_sys_t
*/
void register_trx(trx_t *trx)
{
mutex_enter(&mutex);
UT_LIST_ADD_FIRST(trx_list, trx);
mutex_exit(&mutex);
trx_list.push_front(*trx);
}
......@@ -1088,9 +1124,7 @@ class trx_sys_t
*/
void deregister_trx(trx_t *trx)
{
mutex_enter(&mutex);
UT_LIST_REMOVE(trx_list, trx);
mutex_exit(&mutex);
trx_list.remove(*trx);
}
......@@ -1109,14 +1143,11 @@ class trx_sys_t
{
size_t count= 0;
mutex_enter(&mutex);
for (const trx_t *trx= UT_LIST_GET_FIRST(trx_list); trx;
trx= UT_LIST_GET_NEXT(trx_list, trx))
{
if (trx->read_view.is_open())
trx_list.for_each([&count](const trx_t &trx) {
if (trx.read_view.is_open())
++count;
}
mutex_exit(&mutex);
});
return count;
}
......
......@@ -35,6 +35,7 @@ Created 3/26/1996 Heikki Tuuri
#include "ut0vec.h"
#include "fts0fts.h"
#include "read0types.h"
#include "ilist.h"
#include <vector>
#include <set>
......@@ -713,7 +714,7 @@ struct trx_rsegs_t {
trx_temp_undo_t m_noredo;
};
struct trx_t {
struct trx_t : ilist_node<> {
private:
/**
Count of references.
......@@ -909,10 +910,6 @@ struct trx_t {
/*!< how many tables the current SQL
statement uses, except those
in consistent read */
/*------------------------------*/
UT_LIST_NODE_T(trx_t) trx_list; /*!< list of all transactions;
protected by trx_sys.mutex */
/*------------------------------*/
dberr_t error_state; /*!< 0 if no error, otherwise error
number; NOTE That ONLY the thread
doing the transaction is allowed to
......
......@@ -4654,15 +4654,14 @@ struct lock_print_info
purge_trx(purge_sys.query ? purge_sys.query->trx : NULL)
{}
void operator()(const trx_t* trx) const
void operator()(const trx_t &trx) const
{
ut_ad(mutex_own(&trx_sys.mutex));
if (UNIV_UNLIKELY(trx == purge_trx))
if (UNIV_UNLIKELY(&trx == purge_trx))
return;
lock_trx_print_wait_and_mvcc_state(file, trx, now);
lock_trx_print_wait_and_mvcc_state(file, &trx, now);
if (trx->will_lock && srv_print_innodb_lock_monitor)
lock_trx_print_locks(file, trx);
if (trx.will_lock && srv_print_innodb_lock_monitor)
lock_trx_print_locks(file, &trx);
}
FILE* const file;
......@@ -4682,11 +4681,8 @@ lock_print_info_all_transactions(
ut_ad(lock_mutex_own());
fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
const time_t now = time(NULL);
mutex_enter(&trx_sys.mutex);
ut_list_map(trx_sys.trx_list, lock_print_info(file, now));
mutex_exit(&trx_sys.mutex);
trx_sys.trx_list.for_each(lock_print_info(file, time(nullptr)));
lock_mutex_exit();
ut_ad(lock_validate());
......
......@@ -354,12 +354,8 @@ struct mtr_write_log_t {
/** Start a mini-transaction. */
void mtr_t::start()
{
#ifdef HAVE_valgrind_or_MSAN
char m_freed_ranges_vbits[sizeof m_freed_ranges];
#endif
MEM_GET_VBITS(&m_freed_ranges, m_freed_ranges_vbits, sizeof m_freed_ranges);
UNIV_MEM_INVALID(this, sizeof *this);
MEM_SET_VBITS(&m_freed_ranges, m_freed_ranges_vbits, sizeof m_freed_ranges);
UNIV_MEM_VALID(&m_freed_pages, sizeof(m_freed_pages));
ut_d(m_start= true);
ut_d(m_commit= false);
......@@ -378,6 +374,7 @@ void mtr_t::start()
m_user_space= nullptr;
m_commit_lsn= 0;
m_freed_in_system_tablespace= m_trim_pages= false;
ut_ad(!m_freed_pages);
}
/** Release the resources */
......@@ -387,7 +384,6 @@ inline void mtr_t::release_resources()
ut_d(m_memo.for_each_block_in_reverse(CIterate<DebugCheck>()));
m_log.erase();
m_memo.erase();
clear_freed_ranges();
ut_d(m_commit= true);
}
......@@ -420,8 +416,9 @@ void mtr_t::commit()
to insert into the flush list. */
log_mutex_exit();
if (!m_freed_ranges.empty())
if (m_freed_pages)
{
ut_ad(!m_freed_pages->empty());
fil_space_t *freed_space= m_user_space;
/* Get the freed tablespace in case of predefined tablespace */
if (!freed_space)
......@@ -434,14 +431,15 @@ void mtr_t::commit()
/* Update the last freed lsn */
freed_space->update_last_freed_lsn(m_commit_lsn);
for (const auto &range : m_freed_ranges)
freed_space->add_free_range(range);
}
if (is_trim_pages())
{
ut_ad(m_user_space != nullptr);
m_user_space->clear_freed_ranges();
if (!is_trim_pages())
for (const auto &range : *m_freed_pages)
freed_space->add_free_range(range);
else
freed_space->clear_freed_ranges();
delete m_freed_pages;
m_freed_pages= nullptr;
/* Reset of m_trim_pages and m_freed_in_system_tablespace
happens in mtr_t::start() */
}
m_memo.for_each_block_in_reverse(CIterate<const ReleaseBlocks>
......@@ -472,7 +470,7 @@ void mtr_t::commit_files(lsn_t checkpoint_lsn)
ut_ad(!m_made_dirty);
ut_ad(m_memo.size() == 0);
ut_ad(!srv_read_only_mode);
ut_ad(m_freed_ranges.empty());
ut_ad(!m_freed_pages);
ut_ad(!m_freed_in_system_tablespace);
if (checkpoint_lsn) {
......
......@@ -245,10 +245,8 @@ void ReadView::open(trx_t *trx)
void trx_sys_t::clone_oldest_view(ReadViewBase *view) const
{
view->snapshot(nullptr);
mutex_enter(&mutex);
/* Find oldest view. */
for (const trx_t *trx= UT_LIST_GET_FIRST(trx_list); trx;
trx= UT_LIST_GET_NEXT(trx_list, trx))
trx->read_view.append_to(view);
mutex_exit(&mutex);
trx_list.for_each([view](const trx_t &trx) {
trx.read_view.append_to(view);
});
}
......@@ -1220,19 +1220,13 @@ static void fetch_data_into_cache(trx_i_s_cache_t *cache)
trx_i_s_cache_clear(cache);
/* Capture the state of transactions */
mutex_enter(&trx_sys.mutex);
for (const trx_t *trx= UT_LIST_GET_FIRST(trx_sys.trx_list);
trx != NULL;
trx= UT_LIST_GET_NEXT(trx_list, trx))
{
if (trx_is_started(trx) && trx != purge_sys.query->trx)
trx_sys.trx_list.for_each([cache](const trx_t &trx) {
if (!cache->is_truncated && trx_is_started(&trx) &&
&trx != purge_sys.query->trx)
{
fetch_data_into_cache_low(cache, trx);
if (cache->is_truncated)
break;
}
}
mutex_exit(&trx_sys.mutex);
fetch_data_into_cache_low(cache, &trx);
}
});
cache->is_truncated= false;
}
......
......@@ -205,8 +205,7 @@ trx_sys_t::create()
ut_ad(this == &trx_sys);
ut_ad(!is_initialised());
m_initialised = true;
mutex_create(LATCH_ID_TRX_SYS, &mutex);
UT_LIST_INIT(trx_list, &trx_t::trx_list);
trx_list.create();
rseg_history_len= 0;
rw_trx_hash.init();
......@@ -320,8 +319,8 @@ trx_sys_t::close()
}
}
ut_a(UT_LIST_GET_LEN(trx_list) == 0);
mutex_free(&mutex);
ut_a(trx_list.empty());
trx_list.close();
m_initialised = false;
}
......@@ -330,15 +329,11 @@ ulint trx_sys_t::any_active_transactions()
{
uint32_t total_trx= 0;
mutex_enter(&mutex);
for (trx_t* trx= UT_LIST_GET_FIRST(trx_sys.trx_list);
trx != NULL;
trx= UT_LIST_GET_NEXT(trx_list, trx))
{
if (trx->state == TRX_STATE_COMMITTED_IN_MEMORY ||
(trx->state == TRX_STATE_ACTIVE && trx->id))
trx_sys.trx_list.for_each([&total_trx](const trx_t &trx) {
if (trx.state == TRX_STATE_COMMITTED_IN_MEMORY ||
(trx.state == TRX_STATE_ACTIVE && trx.id))
total_trx++;
}
mutex_exit(&mutex);
});
return total_trx;
}
......@@ -977,8 +977,9 @@ trx_start_low(
ut_a(trx->lock.table_locks.empty());
/* No other thread can access this trx object through rw_trx_hash,
still it can be found through trx_sys.trx_list, which means state
change must be protected by e.g. trx->mutex.
still it can be found through trx_sys.trx_list. Sometimes it's
possible to indirectly protect trx_t::state by freezing
trx_sys.trx_list.
For now we update it without mutex protection, because original code
did it this way. It has to be reviewed and fixed properly. */
......@@ -1343,9 +1344,9 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
/* This state change is not protected by any mutex, therefore
there is an inherent race here around state transition during
printouts. We ignore this race for the sake of efficiency.
However, the trx_sys_t::mutex will protect the trx_t instance
and it cannot be removed from the trx_list and freed
without first acquiring the trx_sys_t::mutex. */
However, the freezing of trx_sys.trx_list will protect the trx_t
instance and it cannot be removed from the trx_list and freed
without first unfreezing trx_list. */
ut_ad(trx_state_eq(this, TRX_STATE_ACTIVE));
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment