Merge 10.11 into 11.4

This commit is contained in:
Marko Mäkelä 2025-09-09 14:09:10 +03:00
commit 7e76a58e0b
10 changed files with 33 additions and 244 deletions

View file

@ -1,51 +0,0 @@
SET @save_stats_persistent = @@GLOBAL.innodb_stats_persistent;
SET GLOBAL innodb_stats_persistent = 0;
#
# MDEV-12288 Reset DB_TRX_ID when the history is removed,
# to speed up MVCC
#
CREATE TABLE t1(a INT PRIMARY KEY, b INT NOT NULL)
ROW_FORMAT=REDUNDANT ENGINE=InnoDB;
InnoDB 0 transactions not purged
connect prevent_purge,localhost,root;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
INSERT INTO t1 VALUES(1,2),(3,4);
ALTER TABLE t1 ADD COLUMN c INT;
UPDATE t1 SET b=-3 WHERE a=3;
connect con1,localhost,root;
BEGIN;
UPDATE t1 SET b=4 WHERE a=3;
disconnect prevent_purge;
connection default;
SET GLOBAL innodb_max_purge_lag_wait=1;
connection con1;
ROLLBACK;
disconnect con1;
connection default;
InnoDB 0 transactions not purged
FLUSH TABLE t1 FOR EXPORT;
Clustered index root page contents:
N_RECS=3; LEVEL=0
header=0x0100000300c6 (a=0x696e66696d756d00)
header=0x1000200b0087 (a=0x80000000,
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
b=0x80000000,
c=NULL(4 bytes))
header=0x0000100900a6 (a=0x80000001,
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
b=0x80000002)
header=0x000018090074 (a=0x80000003,
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
b=0x7ffffffd)
header=0x040008030000 (a=0x73757072656d756d00)
UNLOCK TABLES;
SELECT * FROM t1;
a b c
1 2 NULL
3 -3 NULL
DROP TABLE t1;
SET GLOBAL innodb_stats_persistent = @save_stats_persistent;

View file

@ -12,8 +12,8 @@ SYS_TABLES clustered index root page (8):
N_RECS=8; LEVEL=0; INDEX_ID=0x0000000000000001
header=0x01000003008d (NAME=0x696e66696d756d00)
header=0x0000101500d5 (NAME='SYS_FOREIGN',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x000000000000000b,
N_COLS=0x00000004,
TYPE=0x00000001,
@ -22,8 +22,8 @@ header=0x0000101500d5 (NAME='SYS_FOREIGN',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000000)
header=0x000018150122 (NAME='SYS_FOREIGN_COLS',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x000000000000000c,
N_COLS=0x00000004,
TYPE=0x00000001,
@ -32,8 +32,8 @@ header=0x000018150122 (NAME='SYS_FOREIGN_COLS',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000000)
header=0x0000201501ae (NAME='SYS_VIRTUAL',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x000000000000000d,
N_COLS=0x00000003,
TYPE=0x00000001,
@ -42,8 +42,8 @@ header=0x0000201501ae (NAME='SYS_VIRTUAL',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000000)
header=0x0400301501f2 (NAME='test/tc',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x000000000000000f,
N_COLS=0x80000001,
TYPE=0x00000001,
@ -52,8 +52,8 @@ header=0x0400301501f2 (NAME='test/tc',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000002)
header=0x00003815027a (NAME='test/td',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x0000000000000010,
N_COLS=0x80000001,
TYPE=0x00000021,
@ -62,8 +62,8 @@ header=0x00003815027a (NAME='test/td',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000003)
header=0x00004815016a (NAME='test/tp',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x0000000000000012,
N_COLS=0x80000001,
TYPE=0x000009a1,
@ -72,8 +72,8 @@ header=0x00004815016a (NAME='test/tp',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000005)
header=0x000028150236 (NAME='test/tr',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x000000000000000e,
N_COLS=0x00000001,
TYPE=0x00000001,
@ -82,8 +82,8 @@ header=0x000028150236 (NAME='test/tr',
CLUSTER_NAME=NULL(0 bytes),
SPACE=0x00000001)
header=0x000040150074 (NAME='test/tz',
DB_TRX_ID=0x000000000000,
DB_ROLL_PTR=0x80000000000000,
DB_TRX_ID=0xXXXXXXXXXXXX,
DB_ROLL_PTR=0xXXXXXXXXXXXXXX,
ID=0x0000000000000011,
N_COLS=0x80000001,
TYPE=0x00000023,
@ -155,6 +155,7 @@ BEGIN;
INSERT INTO tr VALUES(1);
INSERT INTO tc VALUES(1);
INSERT INTO td VALUES(1);
INSERT INTO tz VALUES(1);
INSERT INTO tp VALUES(1);
ROLLBACK;
SELECT * FROM tr;

View file

@ -1,83 +0,0 @@
--source include/innodb_page_size.inc
SET @save_stats_persistent = @@GLOBAL.innodb_stats_persistent;
SET GLOBAL innodb_stats_persistent = 0;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
let MYSQLD_DATADIR=`select @@datadir`;
--echo #
--echo # MDEV-12288 Reset DB_TRX_ID when the history is removed,
--echo # to speed up MVCC
--echo #
CREATE TABLE t1(a INT PRIMARY KEY, b INT NOT NULL)
ROW_FORMAT=REDUNDANT ENGINE=InnoDB;
--source include/wait_all_purged.inc
--connect (prevent_purge,localhost,root)
START TRANSACTION WITH CONSISTENT SNAPSHOT;
--connection default
INSERT INTO t1 VALUES(1,2),(3,4);
ALTER TABLE t1 ADD COLUMN c INT;
UPDATE t1 SET b=-3 WHERE a=3;
--connect (con1,localhost,root)
BEGIN;
# For purgeable records, we must record DB_TRX_ID=0 in the undo log!
UPDATE t1 SET b=4 WHERE a=3;
--disconnect prevent_purge
--connection default
# Initiate a full purge, which should reset the DB_TRX_ID except for a=3.
SET GLOBAL innodb_max_purge_lag_wait=1;
# Initiate a ROLLBACK of the update, which should reset the DB_TRX_ID for a=3.
--connection con1
ROLLBACK;
--disconnect con1
--connection default
# Reset the DB_TRX_ID for the hidden ADD COLUMN metadata record.
--source include/wait_all_purged.inc
FLUSH TABLE t1 FOR EXPORT;
# The following is based on innodb.table_flags:
--perl
use strict;
my $ps= $ENV{INNODB_PAGE_SIZE};
my $file= "$ENV{MYSQLD_DATADIR}/test/t1.ibd";
open(FILE, "<", $file) || die "Unable to open $file\n";
my $page;
print "Clustered index root page contents:\n";
sysseek(FILE, 3*$ps, 0) || die "Unable to seek $file";
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
print "N_RECS=", unpack("n", substr($page,38+16,2));
print "; LEVEL=", unpack("n", substr($page,38+26,2)), "\n";
my @fields=qw(a DB_TRX_ID DB_ROLL_PTR b c);
for (my $offset= 0x65; $offset;
$offset= unpack("n", substr($page,$offset-2,2)))
{
print "header=0x", unpack("H*",substr($page,$offset-6,6)), " (";
my $n_fields= unpack("n", substr($page,$offset-4,2)) >> 1 & 0x3ff;
my $start= 0;
my $name;
for (my $i= 0; $i < $n_fields; $i++) {
my $end= unpack("C", substr($page, $offset-7-$i, 1));
print ",\n " if $i;
print "$fields[$i]=";
if ($end & 0x80) {
print "NULL(", ($end & 0x7f) - $start, " bytes)"
} else {
print "0x", unpack("H*", substr($page,$offset+$start,$end-$start))
}
$start= $end & 0x7f;
}
print ")\n";
}
close(FILE) || die "Unable to close $file\n";
EOF
UNLOCK TABLES;
SELECT * FROM t1;
DROP TABLE t1;
SET GLOBAL innodb_stats_persistent = @save_stats_persistent;

View file

@ -89,7 +89,9 @@ for (my $offset= 0x65; $offset;
my $end= unpack("C", substr($page, $offset-7-$i, 1));
print ",\n " if $i;
print "$fields[$i]=";
if ($end & 0x80) {
if ($i == 1 || $i == 2) {
print "0x", "XX" x ($end-$start);
} elsif ($end & 0x80) {
print "NULL(", ($end & 0x7f) - $start, " bytes)"
} elsif ($n_fields > 1 && $i == 0) {
$name= substr($page,$offset+$start,$end-$start);
@ -210,10 +212,7 @@ BEGIN;
INSERT INTO tr VALUES(1);
INSERT INTO tc VALUES(1);
INSERT INTO td VALUES(1);
# We cannot access tz, because due to our fiddling of the NO_ROLLBACK flag,
# it now has a record with DB_TRX_ID=0, which is invalid for
# transactional tables until MDEV-12288 is implemented.
# INSERT INTO tz VALUES(1);
INSERT INTO tz VALUES(1);
INSERT INTO tp VALUES(1);
ROLLBACK;

View file

@ -638,7 +638,8 @@ public:
{
/** The largest encountered transaction identifier for which no
transaction was observed to be active. This is a cache to speed up
trx_sys_t::find_same_or_older().
trx_sys_t::find_same_or_older() as well as to elide some calls to
trx_sys_t::find().
This will be zero-initialized in Pool::Pool() and not initialized
when a transaction object in the pool is freed and reused. The

View file

@ -5629,8 +5629,10 @@ func_exit:
? lock_clust_rec_some_has_impl(rec, index, offsets)
: 0;
if (trx_t *impl_trx = impl_trx_id
? trx_sys.find(current_trx(), impl_trx_id, false)
trx_t *trx= current_trx();
if (trx_t *impl_trx = impl_trx_id > (trx ? trx->max_inactive_id : 0)
? trx_sys.find(trx, impl_trx_id, false)
: 0) {
/* impl_trx could have been committed before we
acquire its mutex, but not thereafter. */
@ -6215,7 +6217,7 @@ lock_rec_convert_impl_to_expl(
trx_id = lock_clust_rec_some_has_impl(rec, index, offsets);
if (trx_id == 0) {
if (trx_id <= caller_trx->max_inactive_id) {
return nullptr;
}
if (UNIV_UNLIKELY(trx_id == caller_trx->id)) {

View file

@ -2413,8 +2413,7 @@ end_of_index:
} else if (rec_trx_id < trx->id) {
/* Reset the DB_TRX_ID,DB_ROLL_PTR of old rows
for which history is not going to be
available after the rebuild operation.
This essentially mimics row_purge_reset_trx_id(). */
available after the rebuild operation. */
row->fields[new_trx_id_col].data
= const_cast<byte*>(reset_trx_id);
row->fields[new_trx_id_col + 1].data

View file

@ -1032,78 +1032,6 @@ bool row_purge_del_mark(purge_node_t *node)
return result;
}
/** Reset DB_TRX_ID, DB_ROLL_PTR of a clustered index record
whose old history can no longer be observed.
@param[in,out] node purge node
@param[in,out] mtr mini-transaction (will be started and committed) */
static void row_purge_reset_trx_id(purge_node_t* node, mtr_t* mtr)
{
/* Reset DB_TRX_ID, DB_ROLL_PTR for old records. */
mtr->start();
if (row_purge_reposition_pcur(BTR_MODIFY_LEAF, node, mtr)) {
dict_index_t* index = dict_table_get_first_index(
node->table);
ulint trx_id_pos = index->n_uniq ? index->n_uniq : 1;
rec_t* rec = btr_pcur_get_rec(&node->pcur);
mem_heap_t* heap = NULL;
/* Reserve enough offsets for the PRIMARY KEY and 2 columns
so that we can access DB_TRX_ID, DB_ROLL_PTR. */
rec_offs offsets_[REC_OFFS_HEADER_SIZE + MAX_REF_PARTS + 3];
rec_offs_init(offsets_);
rec_offs* offsets = rec_get_offsets(
rec, index, offsets_, index->n_core_fields,
trx_id_pos + 2, &heap);
ut_ad(heap == NULL);
ut_ad(dict_index_get_nth_field(index, trx_id_pos)
->col->mtype == DATA_SYS);
ut_ad(dict_index_get_nth_field(index, trx_id_pos)
->col->prtype == (DATA_TRX_ID | DATA_NOT_NULL));
ut_ad(dict_index_get_nth_field(index, trx_id_pos + 1)
->col->mtype == DATA_SYS);
ut_ad(dict_index_get_nth_field(index, trx_id_pos + 1)
->col->prtype == (DATA_ROLL_PTR | DATA_NOT_NULL));
/* Only update the record if DB_ROLL_PTR matches (the
record has not been modified after this transaction
became purgeable) */
if (node->roll_ptr
== row_get_rec_roll_ptr(rec, index, offsets)) {
ut_ad(!rec_get_deleted_flag(
rec, rec_offs_comp(offsets))
|| rec_is_alter_metadata(rec, *index));
DBUG_LOG("purge", "reset DB_TRX_ID="
<< ib::hex(row_get_rec_trx_id(
rec, index, offsets)));
index->set_modified(*mtr);
buf_block_t* block = btr_pcur_get_block(&node->pcur);
if (UNIV_LIKELY_NULL(block->page.zip.data)) {
page_zip_write_trx_id_and_roll_ptr(
block, rec, offsets, trx_id_pos,
0, 1ULL << ROLL_PTR_INSERT_FLAG_POS,
mtr);
} else {
ulint len;
byte* ptr = rec_get_nth_field(
rec, offsets, trx_id_pos, &len);
ut_ad(len == DATA_TRX_ID_LEN);
size_t offs = ptr - block->page.frame;
mtr->memset(block, offs, DATA_TRX_ID_LEN, 0);
offs += DATA_TRX_ID_LEN;
mtr->write<1,mtr_t::MAYBE_NOP>(
*block, block->page.frame + offs,
0x80U);
mtr->memset(block, offs + 1,
DATA_ROLL_PTR_LEN - 1, 0);
}
}
}
mtr->commit();
}
/***********************************************************//**
Purges an update of an existing record. Also purges an update of a delete
marked record if that record contained an externally stored field. */
@ -1233,8 +1161,6 @@ skip_secondaries:
mtr.commit();
}
}
row_purge_reset_trx_id(node, &mtr);
}
#ifdef UNIV_DEBUG
@ -1530,8 +1456,6 @@ row_purge_record_func(
/* fall through */
default:
if (!updated_extern) {
mtr_t mtr;
row_purge_reset_trx_id(node, &mtr);
break;
}
/* fall through */

View file

@ -6384,16 +6384,13 @@ rec_loop:
ULINT_UNDEFINED, &heap);
goto next_rec;
}
else if (!rec_deleted && !rec_trx_id);
else if (!rec_deleted);
else if (!check_table_extended_view.changes_visible(rec_trx_id));
else if (prebuilt->autoinc_error == DB_SUCCESS)
{
const char *msg= rec_deleted
? "Unpurged clustered index record"
: "Clustered index record with stale history";
ib::warn w;
w << msg << " in table " << index->table->name << ": "
w << "Unpurged clustered index record in table "
<< index->table->name << ": "
<< rec_offsets_print(rec, offsets);
prebuilt->autoinc_error= DB_MISSING_HISTORY;
push_warning_printf(prebuilt->trx->mysql_thd,

View file

@ -123,7 +123,7 @@ row_vers_impl_x_locked_low(
ULINT_UNDEFINED, &heap);
trx_id = row_get_rec_trx_id(clust_rec, clust_index, clust_offsets);
if (trx_id == 0) {
if (trx_id <= caller_trx->max_inactive_id) {
/* The transaction history was already purged. */
mem_heap_free(heap);
DBUG_RETURN(0);