Merge 10.6 into 10.9

This commit is contained in:
Marko Mäkelä 2023-06-07 14:32:46 +03:00
commit 878a86f276
19 changed files with 429 additions and 202 deletions

View file

@ -86,7 +86,7 @@ sanity_checks() {
datadir=`mariadbd_get_param datadir`
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks=`LC_ALL=C BLOCKSIZE= df --output=avail "$datadir" | tail -n 1`
df_available_blocks="$(LC_ALL=C BLOCKSIZE='' df --output=avail "$datadir" | tail -n 1)"
if [ "$df_available_blocks" -lt "4096" ]; then
log_failure_msg "$0: ERROR: The partition with $datadir is too full!"
echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER

View file

@ -223,14 +223,23 @@ then
mkdir -Z $mysql_datadir
fi
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks=`LC_ALL=C BLOCKSIZE= df --output=avail "$datadir" | tail -n 1`
if [ "$df_available_blocks" -lt "4096" ]
# Check if MariaDB datadir is available if not fails.
# There should be symlink or directory available or something will fail.
if [ -d "$mysql_datadir" ] || [ -L "$mysql_datadir" ]
then
echo "ERROR: There's not enough space in $mysql_datadir/" 1>&2
db_stop
exit 1
# As preset blocksize of GNU df is 1024 then available bytes is $df_available_blocks * 1024
# 4096 blocks is then lower than 4 MB
df_available_blocks="$(LC_ALL=C BLOCKSIZE='' df --output=avail "$mysql_datadir" | tail -n 1)"
if [ "$df_available_blocks" -lt "4096" ]
then
echo "ERROR: There's not enough space in $mysql_datadir/" 1>&2
db_stop
exit 1
fi
else
echo "ERROR: There's no directory or symlink available: $mysql_datadir/" 1>&2
db_stop
exit 1
fi
# Since the home directory was created before putting the user into

View file

@ -26,4 +26,60 @@ UPDATE mysql.innodb_table_stats SET last_update=NULL WHERE table_name='t1';
XA END 'test';
XA ROLLBACK 'test';
DROP TABLE t1;
#
# MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
#
#
# Testing a non-default format: Field_timestamp0 - UINT4 based
#
SET @@global.mysql56_temporal_format=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp /* mariadb-5.3 */ NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp /* mariadb-5.3 */ NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
#
# Now as the table t1 is dropped, expect no statistics
#
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
database_name table_name last_update n_rows clustered_index_size sum_of_other_index_sizes
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
database_name table_name index_name last_update stat_name stat_value sample_size stat_description
#
# Testing with the default format: Field_timestampf - BINARY(4) based with the UNSIGNED_FLAG
#
SET @@global.mysql56_temporal_format=1;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update timestamp NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
# End of 10.6 tests

View file

@ -28,4 +28,57 @@ XA END 'test';
XA ROLLBACK 'test';
DROP TABLE t1;
--echo #
--echo # MDEV-30483 After upgrade to 10.6 from Mysql 5.7 seeing "InnoDB: Column last_update in table mysql.innodb_table_stats is BINARY(4) NOT NULL but should be INT UNSIGNED NOT NULL"
--echo #
# The following tests demonstrate that these columns:
# - innodb_table_stats.last_update
# - innodb_index_stats.last_update
# have sane values close to NOW(), rather than any garbage,
# with all TIMESTAMP formats.
--echo #
--echo # Testing a non-default format: Field_timestamp0 - UINT4 based
--echo #
SET @@global.mysql56_temporal_format=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo #
--echo # Now as the table t1 is dropped, expect no statistics
--echo #
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
--echo #
--echo # Testing with the default format: Field_timestampf - BINARY(4) based with the UNSIGNED_FLAG
--echo #
SET @@global.mysql56_temporal_format=1;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo # End of 10.6 tests

View file

@ -0,0 +1,26 @@
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1))ENGINE=InnoDB
PARTITION BY LIST(f1) (
PARTITION p1 VALUES in (1, 2, 3),
PARTITION p2 VALUES in (4, 5, 6));
INSERT INTO t1 VALUES(1, 1), (1, 1), (6, 1);
connect con1,localhost,root,,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect con2,localhost,root,,,;
SET DEBUG_SYNC="innodb_rollback_inplace_alter_table SIGNAL default_resume WAIT_FOR alter_resume";
ALTER TABLE t1 ADD UNIQUE INDEX(f1);
connection default;
set DEBUG_SYNC="now WAIT_FOR default_resume";
SET DEBUG_SYNC="innodb_row_update_for_mysql_begin SIGNAL alter_resume WAIT_FOR alter_finish";
DELETE FROM t1;
connection con2;
ERROR 23000: Duplicate entry '1' for key 'f1_2'
SET DEBUG_SYNC="now SIGNAL alter_finish";
connection default;
connection con1;
commit;
connection default;
disconnect con1;
disconnect con2;
InnoDB 0 transactions not purged
drop table t1;
SET DEBUG_SYNC=reset;

View file

@ -0,0 +1 @@
--innodb_purge_threads=1

View file

@ -0,0 +1,37 @@
--source include/have_innodb.inc
--source include/have_partition.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
CREATE TABLE t1(f1 INT, f2 INT, INDEX(f1))ENGINE=InnoDB
PARTITION BY LIST(f1) (
PARTITION p1 VALUES in (1, 2, 3),
PARTITION p2 VALUES in (4, 5, 6));
INSERT INTO t1 VALUES(1, 1), (1, 1), (6, 1);
connect(con1,localhost,root,,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect(con2,localhost,root,,,);
SET DEBUG_SYNC="innodb_rollback_inplace_alter_table SIGNAL default_resume WAIT_FOR alter_resume";
send ALTER TABLE t1 ADD UNIQUE INDEX(f1);
connection default;
set DEBUG_SYNC="now WAIT_FOR default_resume";
SET DEBUG_SYNC="innodb_row_update_for_mysql_begin SIGNAL alter_resume WAIT_FOR alter_finish";
send DELETE FROM t1;
connection con2;
--error ER_DUP_ENTRY
reap;
SET DEBUG_SYNC="now SIGNAL alter_finish";
connection default;
reap;
connection con1;
commit;
connection default;
disconnect con1;
disconnect con2;
--source ../../innodb/include/wait_all_purged.inc
drop table t1;
SET DEBUG_SYNC=reset;

View file

@ -13,7 +13,7 @@ innodb_table_stats CREATE TABLE `innodb_table_stats` (
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
Table Create Table
innodb_table_stats CREATE TABLE `innodb_table_stats` (
@ -25,7 +25,7 @@ innodb_table_stats CREATE TABLE `innodb_table_stats` (
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_index_stats;
Table Create Table
innodb_index_stats CREATE TABLE `innodb_index_stats` (
@ -44,7 +44,7 @@ CREATE TABLE t1 (a INT, KEY(a)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (10);
DROP TABLE t1;
SET @@global.innodb_stats_persistent=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
Table Create Table
innodb_table_stats CREATE TABLE `innodb_table_stats` (
@ -56,7 +56,7 @@ innodb_table_stats CREATE TABLE `innodb_table_stats` (
`sum_of_other_index_sizes` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`database_name`,`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_index_stats;
Table Create Table
innodb_index_stats CREATE TABLE `innodb_index_stats` (
@ -71,3 +71,38 @@ innodb_index_stats CREATE TABLE `innodb_index_stats` (
PRIMARY KEY (`database_name`,`table_name`,`index_name`,`stat_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin STATS_PERSISTENT=0
SET @@global.innodb_stats_persistent=1;
#
# Testing MySQL-5.6-alike Field_timestampf: BINARY(4) based, without UNSIGNED_FLAG
#
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update type_mysql_timestamp NO current_timestamp() on update current_timestamp()
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
Field Type Null Key Default Extra
last_update type_mysql_timestamp NO current_timestamp() on update current_timestamp()
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
TIMESTAMPDIFF(DAY,last_update,now())<=1
1
DROP TABLE t1;
#
# Now as the table t1 is dropped, expect no statistics
#
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
database_name table_name last_update n_rows clustered_index_size sum_of_other_index_sizes
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
database_name table_name index_name last_update stat_name stat_value sample_size stat_description
#
# Restore the structure of the tables
#
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();

View file

@ -6,9 +6,9 @@
SET @@global.innodb_stats_persistent=0;
SHOW CREATE TABLE mysql.innodb_table_stats;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_index_stats;
SET @@global.innodb_stats_persistent=1;
@ -17,8 +17,46 @@ INSERT INTO t1 VALUES (10);
DROP TABLE t1;
SET @@global.innodb_stats_persistent=0;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP;
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_table_stats;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP;
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW CREATE TABLE mysql.innodb_index_stats;
SET @@global.innodb_stats_persistent=1;
# The following test demonstrate that these columns:
# - innodb_table_stats.last_update
# - innodb_index_stats.last_update
# have sane values close to NOW(), rather than any garbage,
# with MySQL-alike Field_timestampf
--echo #
--echo # Testing MySQL-5.6-alike Field_timestampf: BINARY(4) based, without UNSIGNED_FLAG
--echo #
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TYPE_MYSQL_TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
SHOW COLUMNS FROM mysql.innodb_table_stats LIKE 'last_update';
SHOW COLUMNS FROM mysql.innodb_index_stats LIKE 'last_update';
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=1;
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT TIMESTAMPDIFF(DAY,last_update,now())<=1 FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
DROP TABLE t1;
--echo #
--echo # Now as the table t1 is dropped, expect no statistics
--echo #
SELECT * FROM mysql.innodb_table_stats
WHERE database_name='test' AND table_name='t1';
SELECT * FROM mysql.innodb_index_stats
WHERE database_name='test' AND table_name='t1' AND stat_name='size';
--echo #
--echo # Restore the structure of the tables
--echo #
ALTER TABLE mysql.innodb_table_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();
ALTER TABLE mysql.innodb_index_stats MODIFY last_update TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp();

View file

@ -2140,6 +2140,8 @@ void buf_page_free(fil_space_t *space, uint32_t page, mtr_t *mtr)
}
block->page.lock.x_lock();
if (block->page.is_ibuf_exist())
ibuf_merge_or_delete_for_page(nullptr, page_id, block->page.zip_size());
#ifdef BTR_CUR_HASH_ADAPT
if (block->index)
btr_search_drop_page_hash_index(block, false);

View file

@ -2438,6 +2438,7 @@ static void buf_flush_page_cleaner()
else if (buf_pool.ran_out())
{
buf_pool.page_cleaner_set_idle(false);
buf_pool.get_oldest_modification(0);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
n= srv_max_io_capacity;
mysql_mutex_lock(&buf_pool.mutex);

View file

@ -1955,8 +1955,8 @@ err_exit:
FIL_TYPE_TABLESPACE,
crypt_data, mode, true)) {
fil_node_t* node = space->add(path, file, size, false, true);
mysql_mutex_unlock(&fil_system.mutex);
IF_WIN(node->find_metadata(), node->find_metadata(file, true));
mysql_mutex_unlock(&fil_system.mutex);
mtr.start();
mtr.set_named_space(space);
ut_a(fsp_header_init(space, size, &mtr) == DB_SUCCESS);

View file

@ -2364,6 +2364,7 @@ tablespace_deleted:
}
const ulint zip_size = s->zip_size(), size = s->size;
s->x_lock();
s->release();
mtr_t mtr;
@ -2381,13 +2382,17 @@ tablespace_deleted:
|| !page_is_leaf(block->page.frame);
mtr.commit();
if (err == DB_TABLESPACE_DELETED) {
s->x_unlock();
goto tablespace_deleted;
}
if (!remove) {
s->x_unlock();
continue;
}
}
s->x_unlock();
if (srv_shutdown_state == SRV_SHUTDOWN_NONE
|| srv_fast_shutdown) {
continue;
@ -2416,7 +2421,7 @@ tablespace_deleted:
/* Prevent an infinite loop, by removing entries from
the change buffer in the case the bitmap bits were
wrongly clear even though buffered changes exist. */
ibuf_delete_recs(page_id_t(space_ids[i], page_nos[i]));
ibuf_delete_recs(page_id_t(space_id, page_nos[i]));
}
}
@ -4194,25 +4199,26 @@ dberr_t ibuf_merge_or_delete_for_page(buf_block_t *block,
ibuf_mtr_commit(&mtr);
if (bitmap_bits
&& DB_SUCCESS
if (!bitmap_bits) {
done:
/* No changes are buffered for this page. */
space->release();
return DB_SUCCESS;
}
if (!block
|| DB_SUCCESS
== fseg_page_is_allocated(space, page_id.page_no())) {
ibuf_mtr_start(&mtr);
mtr.set_named_space(space);
ibuf_reset_bitmap(block, page_id, zip_size, &mtr);
ibuf_mtr_commit(&mtr);
bitmap_bits = 0;
if (!block
|| btr_page_get_index_id(block->page.frame)
!= DICT_IBUF_ID_MIN + IBUF_SPACE_ID) {
ibuf_delete_recs(page_id);
}
}
if (!bitmap_bits) {
/* No changes are buffered for this page. */
space->release();
return DB_SUCCESS;
goto done;
}
}

View file

@ -216,14 +216,6 @@ buf_block_t*
trx_undo_assign_low(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
mtr_t *mtr, dberr_t *err)
MY_ATTRIBUTE((nonnull, warn_unused_result));
/******************************************************************//**
Sets the state of the undo log segment at a transaction finish.
@return undo log segment header page, x-latched */
buf_block_t*
trx_undo_set_state_at_finish(
/*=========================*/
trx_undo_t* undo, /*!< in: undo log memory copy */
mtr_t* mtr); /*!< in: mtr */
/** Set the state of the undo log segment at a XA PREPARE or XA ROLLBACK.
@param[in,out] trx transaction

View file

@ -2491,7 +2491,7 @@ inline
recv_sys_t::parse_mtr_result recv_sys_t::parse(source &l, bool if_exists)
noexcept
{
restart:
restart:
#ifndef SUX_LOCK_GENERIC
ut_ad(log_sys.latch.is_write_locked() ||
srv_operation == SRV_OPERATION_BACKUP ||
@ -3279,9 +3279,6 @@ set_start_lsn:
|| recv_sys.is_corrupt_log()) && !srv_force_recovery) {
if (init) {
init->created = false;
if (space || block->page.id().page_no()) {
block->page.lock.x_lock_recursive();
}
}
mtr.discard_modifications();
@ -3847,10 +3844,12 @@ void recv_sys_t::apply(bool last_batch)
if (fil_space_t *space = fil_space_get(id + srv_undo_space_id_start))
{
ut_ad(UT_LIST_GET_LEN(space->chain) == 1);
ut_ad(space->recv_size >= t.pages);
fil_node_t *file= UT_LIST_GET_FIRST(space->chain);
ut_ad(file->is_open());
os_file_truncate(file->name, file->handle,
os_offset_t{t.pages} << srv_page_size_shift, true);
os_offset_t{space->recv_size} <<
srv_page_size_shift, true);
}
}
}

View file

@ -214,14 +214,14 @@ row_ins_sec_index_entry_by_modify(
made to the clustered index, and completed the
secondary index creation before we got here. In this
case, the change would already be there. The CREATE
INDEX should be waiting for a MySQL meta-data lock
upgrade at least until this INSERT or UPDATE
returns. After that point, set_committed(true)
would be invoked in commit_inplace_alter_table(). */
INDEX should be in wait_while_table_is_used() at least
until this INSERT or UPDATE returns. After that point,
set_committed(true) would be invoked in
commit_inplace_alter_table(). */
ut_a(update->n_fields == 0);
ut_a(!cursor->index()->is_committed());
ut_ad(!dict_index_is_online_ddl(cursor->index()));
return(DB_SUCCESS);
return cursor->index()->is_committed()
? DB_CORRUPTION : DB_SUCCESS;
}
if (mode == BTR_MODIFY_LEAF) {

View file

@ -615,6 +615,8 @@ row_purge_del_mark(
const auto type= node->index->type;
if (type & (DICT_FTS | DICT_CORRUPT))
continue;
if (node->index->online_status > ONLINE_INDEX_CREATION)
continue;
if (UNIV_UNLIKELY(DICT_VIRTUAL & type) && !node->index->is_committed() &&
node->index->has_new_v_col())
continue;
@ -767,6 +769,11 @@ row_purge_upd_exist_or_extern_func(
continue;
}
if (node->index->online_status
> ONLINE_INDEX_CREATION) {
continue;
}
if (row_upd_changes_ord_field_binary(node->index, node->update,
thr, NULL, NULL)) {
/* Build the older version of the index entry */

View file

@ -246,127 +246,121 @@ Remove the undo log segment from the rseg slot if it is too big for reuse.
void
trx_purge_add_undo_to_history(const trx_t* trx, trx_undo_t*& undo, mtr_t* mtr)
{
DBUG_PRINT("trx", ("commit(" TRX_ID_FMT "," TRX_ID_FMT ")",
trx->id, trx_id_t{trx->rw_trx_hash_element->no}));
ut_ad(undo == trx->rsegs.m_redo.undo);
trx_rseg_t* rseg = trx->rsegs.m_redo.rseg;
ut_ad(undo->rseg == rseg);
buf_block_t* rseg_header = rseg->get(mtr, nullptr);
/* We are in transaction commit; we cannot return an error. If the
database is corrupted, it is better to crash it than to
intentionally violate ACID by committing something that is known to
be corrupted. */
ut_ad(rseg_header);
buf_block_t* undo_page = trx_undo_set_state_at_finish(
undo, mtr);
trx_ulogf_t* undo_header = undo_page->page.frame
+ undo->hdr_offset;
DBUG_PRINT("trx", ("commit(" TRX_ID_FMT "," TRX_ID_FMT ")",
trx->id, trx_id_t{trx->rw_trx_hash_element->no}));
ut_ad(undo->id < TRX_RSEG_N_SLOTS);
ut_ad(undo == trx->rsegs.m_redo.undo);
trx_rseg_t *rseg= trx->rsegs.m_redo.rseg;
ut_ad(undo->rseg == rseg);
buf_block_t *rseg_header= rseg->get(mtr, nullptr);
/* We are in transaction commit; we cannot return an error. If the
database is corrupted, it is better to crash it than to
intentionally violate ACID by committing something that is known to
be corrupted. */
ut_ad(rseg_header);
buf_block_t *undo_page=
buf_page_get(page_id_t(rseg->space->id, undo->hdr_page_no), 0,
RW_X_LATCH, mtr);
/* This function is invoked during transaction commit, which is not
allowed to fail. If we get a corrupted undo header, we will crash here. */
ut_a(undo_page);
trx_ulogf_t *undo_header= undo_page->page.frame + undo->hdr_offset;
ut_ad(mach_read_from_2(undo_header + TRX_UNDO_NEEDS_PURGE) <= 1);
ut_ad(rseg->needs_purge > trx->id);
ut_ad(mach_read_from_2(undo_header + TRX_UNDO_NEEDS_PURGE) <= 1);
ut_ad(rseg->needs_purge > trx->id);
if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT
+ rseg_header->page.frame))) {
/* This database must have been upgraded from
before MariaDB 10.3.5. */
trx_rseg_format_upgrade(rseg_header, mtr);
}
if (rseg->last_page_no == FIL_NULL)
{
rseg->last_page_no= undo->hdr_page_no;
rseg->set_last_commit(undo->hdr_offset, trx->rw_trx_hash_element->no);
}
if (undo->state != TRX_UNDO_CACHED) {
/* The undo log segment will not be reused */
ut_a(undo->id < TRX_RSEG_N_SLOTS);
static_assert(FIL_NULL == 0xffffffff, "");
mtr->memset(rseg_header,
TRX_RSEG + TRX_RSEG_UNDO_SLOTS
+ undo->id * TRX_RSEG_SLOT_SIZE, 4, 0xff);
rseg->history_size++;
uint32_t hist_size = mach_read_from_4(
TRX_RSEG_HISTORY_SIZE + TRX_RSEG
+ rseg_header->page.frame);
if (UNIV_UNLIKELY(mach_read_from_4(TRX_RSEG + TRX_RSEG_FORMAT +
rseg_header->page.frame)))
/* This database must have been upgraded from before MariaDB 10.3.5. */
trx_rseg_format_upgrade(rseg_header, mtr);
ut_ad(undo->size == flst_get_len(TRX_UNDO_SEG_HDR
+ TRX_UNDO_PAGE_LIST
+ undo_page->page.frame));
uint16_t undo_state;
mtr->write<4>(*rseg_header, TRX_RSEG + TRX_RSEG_HISTORY_SIZE
+ rseg_header->page.frame,
hist_size + undo->size);
mtr->write<8>(*rseg_header, TRX_RSEG + TRX_RSEG_MAX_TRX_ID
+ rseg_header->page.frame,
trx_sys.get_max_trx_id());
}
if (undo->size == 1 &&
TRX_UNDO_PAGE_REUSE_LIMIT >
mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE +
undo_page->page.frame))
{
undo->state= undo_state= TRX_UNDO_CACHED;
UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
}
else
{
ut_ad(undo->size == flst_get_len(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST +
undo_page->page.frame));
/* The undo log segment will not be reused */
static_assert(FIL_NULL == 0xffffffff, "");
mtr->memset(rseg_header, TRX_RSEG + TRX_RSEG_UNDO_SLOTS +
undo->id * TRX_RSEG_SLOT_SIZE, 4, 0xff);
uint32_t hist_size= mach_read_from_4(TRX_RSEG_HISTORY_SIZE + TRX_RSEG +
rseg_header->page.frame);
mtr->write<4>(*rseg_header, TRX_RSEG + TRX_RSEG_HISTORY_SIZE +
rseg_header->page.frame, hist_size + undo->size);
mtr->write<8>(*rseg_header, TRX_RSEG + TRX_RSEG_MAX_TRX_ID +
rseg_header->page.frame, trx_sys.get_max_trx_id());
ut_free(undo);
undo_state= TRX_UNDO_TO_PURGE;
}
/* After the purge thread has been given permission to exit,
we may roll back transactions (trx->undo_no==0)
in THD::cleanup() invoked from unlink_thd() in fast shutdown,
or in trx_rollback_recovered() in slow shutdown.
undo= nullptr;
Before any transaction-generating background threads or the
purge have been started, we can
start transactions in row_merge_drop_temp_indexes(),
and roll back recovered transactions.
/* After the purge thread has been given permission to exit,
we may roll back transactions (trx->undo_no==0)
in THD::cleanup() invoked from unlink_thd() in fast shutdown,
or in trx_rollback_recovered() in slow shutdown.
Arbitrary user transactions may be executed when all the undo log
related background processes (including purge) are disabled due to
innodb_force_recovery=2 or innodb_force_recovery=3.
DROP TABLE may be executed at any innodb_force_recovery level.
Before any transaction-generating background threads or the purge
have been started, we can start transactions in
row_merge_drop_temp_indexes(), and roll back recovered transactions.
During fast shutdown, we may also continue to execute
user transactions. */
ut_ad(srv_undo_sources
|| trx->undo_no == 0
|| (!purge_sys.enabled()
&& (srv_is_being_started
|| trx_rollback_is_active
|| srv_force_recovery >= SRV_FORCE_NO_BACKGROUND))
|| srv_fast_shutdown);
Arbitrary user transactions may be executed when all the undo log
related background processes (including purge) are disabled due to
innodb_force_recovery=2 or innodb_force_recovery=3. DROP TABLE may
be executed at any innodb_force_recovery level.
#ifdef WITH_WSREP
if (wsrep_is_wsrep_xid(&trx->xid)) {
trx_rseg_update_wsrep_checkpoint(rseg_header, &trx->xid, mtr);
}
During fast shutdown, we may also continue to execute user
transactions. */
ut_ad(srv_undo_sources || trx->undo_no == 0 ||
(!purge_sys.enabled() &&
(srv_is_being_started ||
trx_rollback_is_active ||
srv_force_recovery >= SRV_FORCE_NO_BACKGROUND)) ||
srv_fast_shutdown);
#ifdef WITH_WSREP
if (wsrep_is_wsrep_xid(&trx->xid))
trx_rseg_update_wsrep_checkpoint(rseg_header, &trx->xid, mtr);
#endif
if (trx->mysql_log_file_name && *trx->mysql_log_file_name) {
/* Update the latest MySQL binlog name and offset info
in rollback segment header if MySQL binlogging is on
or the database server is a MySQL replication save. */
trx_rseg_update_binlog_offset(rseg_header, trx, mtr);
}
if (trx->mysql_log_file_name && *trx->mysql_log_file_name)
/* Update the latest binlog name and offset if log_bin=ON or this
is a replica. */
trx_rseg_update_binlog_offset(rseg_header, trx, mtr);
/* Add the log as the first in the history list */
/* Add the log as the first in the history list */
/* We are in transaction commit; we cannot return an error
when detecting corruption. It is better to crash the server
than to intentionally violate ACID by committing something
that is known to be corrupted. */
ut_a(flst_add_first(rseg_header, TRX_RSEG + TRX_RSEG_HISTORY, undo_page,
static_cast<uint16_t>(undo->hdr_offset
+ TRX_UNDO_HISTORY_NODE),
mtr) == DB_SUCCESS);
/* We are in transaction commit; we cannot return an error
when detecting corruption. It is better to crash the server
than to intentionally violate ACID by committing something
that is known to be corrupted. */
ut_a(flst_add_first(rseg_header, TRX_RSEG + TRX_RSEG_HISTORY, undo_page,
uint16_t(page_offset(undo_header) +
TRX_UNDO_HISTORY_NODE), mtr) == DB_SUCCESS);
mtr->write<8,mtr_t::MAYBE_NOP>(*undo_page,
undo_header + TRX_UNDO_TRX_NO,
trx->rw_trx_hash_element->no);
mtr->write<2,mtr_t::MAYBE_NOP>(*undo_page, undo_header
+ TRX_UNDO_NEEDS_PURGE, 1U);
if (rseg->last_page_no == FIL_NULL) {
rseg->last_page_no = undo->hdr_page_no;
rseg->set_last_commit(undo->hdr_offset,
trx->rw_trx_hash_element->no);
}
rseg->history_size++;
if (undo->state == TRX_UNDO_CACHED) {
UT_LIST_ADD_FIRST(rseg->undo_cached, undo);
} else {
ut_ad(undo->state == TRX_UNDO_TO_PURGE);
ut_free(undo);
}
undo = NULL;
mtr->write<2>(*undo_page, TRX_UNDO_SEG_HDR + TRX_UNDO_STATE +
undo_page->page.frame, undo_state);
mtr->write<8,mtr_t::MAYBE_NOP>(*undo_page, undo_header + TRX_UNDO_TRX_NO,
trx->rw_trx_hash_element->no);
mtr->write<2,mtr_t::MAYBE_NOP>(*undo_page, undo_header +
TRX_UNDO_NEEDS_PURGE, 1U);
}
/** Free an undo log segment.
@ -685,6 +679,7 @@ not_free:
mtr_t mtr;
mtr.start();
mtr.x_lock_space(&space);
const auto space_id= space.id;
/* Lock all modified pages of the tablespace.
@ -694,8 +689,8 @@ not_free:
mini-transaction commit and the server was killed, then
discarding the to-be-trimmed pages without flushing would
break crash recovery. */
rescan:
mysql_mutex_lock(&buf_pool.flush_list_mutex);
for (buf_page_t *bpage= UT_LIST_GET_LAST(buf_pool.flush_list); bpage; )
{
ut_ad(bpage->oldest_modification());
@ -703,46 +698,47 @@ not_free:
buf_page_t *prev= UT_LIST_GET_PREV(list, bpage);
if (bpage->id().space() == space.id &&
bpage->oldest_modification() != 1)
if (bpage->oldest_modification() > 2 && bpage->id().space() == space_id)
{
ut_ad(bpage->frame);
auto block= reinterpret_cast<buf_block_t*>(bpage);
if (!bpage->lock.x_lock_try())
bpage->fix();
{
rescan:
/* Let buf_pool_t::release_freed_page() proceed. */
/* Try to acquire an exclusive latch while the cache line is
fresh after fix(). */
const bool got_lock{bpage->lock.x_lock_try()};
buf_pool.flush_hp.set(prev);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
mysql_mutex_lock(&buf_pool.mutex);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
mysql_mutex_unlock(&buf_pool.mutex);
bpage= UT_LIST_GET_LAST(buf_pool.flush_list);
continue;
if (!got_lock)
bpage->lock.x_lock();
}
buf_pool.flush_hp.set(prev);
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
#ifdef BTR_CUR_HASH_ADAPT
ut_ad(!block->index); /* There is no AHI on undo tablespaces. */
/* There is no AHI on undo tablespaces. */
ut_ad(!reinterpret_cast<buf_block_t*>(bpage)->index);
#endif
bpage->fix();
ut_ad(!bpage->is_io_fixed());
mysql_mutex_lock(&buf_pool.flush_list_mutex);
ut_ad(bpage->id().space() == space_id);
if (bpage->oldest_modification() > 1)
if (bpage->oldest_modification() > 2)
{
mtr.memo_push(reinterpret_cast<buf_block_t*>(bpage),
MTR_MEMO_PAGE_X_FIX);
mysql_mutex_lock(&buf_pool.flush_list_mutex);
ut_ad(bpage->oldest_modification() > 2);
bpage->reset_oldest_modification();
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
}
else
{
bpage->unfix();
bpage->lock.x_unlock();
mysql_mutex_lock(&buf_pool.flush_list_mutex);
}
if (prev != buf_pool.flush_hp.get())
/* Rescan, because we may have lost the position. */
{
mysql_mutex_unlock(&buf_pool.flush_list_mutex);
goto rescan;
}
}
bpage= prev;

View file

@ -1463,37 +1463,6 @@ template buf_block_t*
trx_undo_assign_low<true>(trx_t *trx, trx_rseg_t *rseg, trx_undo_t **undo,
mtr_t *mtr, dberr_t *err);
/******************************************************************//**
Sets the state of the undo log segment at a transaction finish.
@return undo log segment header page, x-latched */
buf_block_t*
trx_undo_set_state_at_finish(
/*=========================*/
trx_undo_t* undo, /*!< in: undo log memory copy */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(undo->id < TRX_RSEG_N_SLOTS);
ut_ad(undo->rseg->is_persistent());
buf_block_t *block=
buf_page_get(page_id_t(undo->rseg->space->id, undo->hdr_page_no), 0,
RW_X_LATCH, mtr);
/* This function is invoked during transaction commit, which is not
allowed to fail. If we get a corrupted undo header, we will crash here. */
ut_a(block);
const uint16_t state = undo->size == 1 &&
TRX_UNDO_PAGE_REUSE_LIMIT >
mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE +
block->page.frame)
? TRX_UNDO_CACHED
: TRX_UNDO_TO_PURGE;
undo->state= state;
mtr->write<2>(*block, TRX_UNDO_SEG_HDR + TRX_UNDO_STATE + block->page.frame,
state);
return block;
}
/** Set the state of the undo log segment at a XA PREPARE or XA ROLLBACK.
@param[in,out] trx transaction
@param[in,out] undo undo log