From 464055fe655d70e7191f4bd9f5709bc64b26b942 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Tue, 1 Oct 2024 18:35:39 +0300
Subject: [PATCH] MDEV-34078 Memory leak in InnoDB purge with 32-column PRIMARY
 KEY

row_purge_reset_trx_id(): Reserve large enough offsets for accomodating
the maximum width PRIMARY KEY followed by DB_TRX_ID,DB_ROLL_PTR.

Reviewed by: Thirunarayanan Balathandayuthapani
---
 mysql-test/suite/innodb/r/purge.result | 23 ++++++++++++++++++++
 mysql-test/suite/innodb/t/purge.test   | 29 +++++++++++++++++++++++++-
 storage/innobase/row/row0purge.cc      |  2 +-
 3 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/mysql-test/suite/innodb/r/purge.result b/mysql-test/suite/innodb/r/purge.result
index a71d0afdcbe..5eedbc4ad9d 100644
--- a/mysql-test/suite/innodb/r/purge.result
+++ b/mysql-test/suite/innodb/r/purge.result
@@ -116,6 +116,29 @@ t12963823	CREATE TABLE `t12963823` (
   KEY `ndx_o` (`o`(500)),
   KEY `ndx_p` (`p`(500))
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci ROW_FORMAT=DYNAMIC
+BEGIN NOT ATOMIC
+DECLARE c TEXT DEFAULT(SELECT CONCAT('CREATE TABLE t1(c',
+GROUP_CONCAT(seq SEPARATOR
+' INT DEFAULT 0, c'),
+' INT DEFAULT 0, PRIMARY KEY(c',
+GROUP_CONCAT(seq SEPARATOR ', c'),
+')) ENGINE=InnoDB;') FROM seq_1_to_33);
+EXECUTE IMMEDIATE c;
+END;
+$$
+ERROR 42000: Too many key parts specified; max 32 parts allowed
+BEGIN NOT ATOMIC
+DECLARE c TEXT DEFAULT(SELECT CONCAT('CREATE TABLE t1(c',
+GROUP_CONCAT(seq SEPARATOR
+' INT DEFAULT 0, c'),
+' INT DEFAULT 0, PRIMARY KEY(c',
+GROUP_CONCAT(seq SEPARATOR ', c'),
+')) ENGINE=InnoDB;') FROM seq_1_to_32);
+EXECUTE IMMEDIATE c;
+END;
+$$
+INSERT INTO t1() VALUES();
 InnoDB		0 transactions not purged
+DROP TABLE t1;
 DROP TABLE t1_purge, t2_purge, t3_purge, t4_purge, t12637786, t12963823;
 SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency;
diff --git a/mysql-test/suite/innodb/t/purge.test b/mysql-test/suite/innodb/t/purge.test
index 63312e50fd8..3caa3f065aa 100644
--- a/mysql-test/suite/innodb/t/purge.test
+++ b/mysql-test/suite/innodb/t/purge.test
@@ -1,5 +1,6 @@
 --source include/have_innodb.inc
 --source include/have_innodb_16k.inc
+--source include/have_sequence.inc
 
 SET @save_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
 SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
@@ -110,8 +111,34 @@ CREATE INDEX ndx_n ON t12963823 (n(500));
 CREATE INDEX ndx_o ON t12963823 (o(500));
 CREATE INDEX ndx_p ON t12963823 (p(500));
 SHOW CREATE TABLE t12963823;
-# We need to activate the purge thread before DROP TABLE.
 
+DELIMITER $$;
+--error ER_TOO_MANY_KEY_PARTS
+BEGIN NOT ATOMIC
+  DECLARE c TEXT DEFAULT(SELECT CONCAT('CREATE TABLE t1(c',
+                                       GROUP_CONCAT(seq SEPARATOR
+                                       ' INT DEFAULT 0, c'),
+                                       ' INT DEFAULT 0, PRIMARY KEY(c',
+                                       GROUP_CONCAT(seq SEPARATOR ', c'),
+                                       ')) ENGINE=InnoDB;') FROM seq_1_to_33);
+  EXECUTE IMMEDIATE c;
+END;
+$$
+BEGIN NOT ATOMIC
+  DECLARE c TEXT DEFAULT(SELECT CONCAT('CREATE TABLE t1(c',
+                                       GROUP_CONCAT(seq SEPARATOR
+                                       ' INT DEFAULT 0, c'),
+                                       ' INT DEFAULT 0, PRIMARY KEY(c',
+                                       GROUP_CONCAT(seq SEPARATOR ', c'),
+                                       ')) ENGINE=InnoDB;') FROM seq_1_to_32);
+  EXECUTE IMMEDIATE c;
+END;
+$$
+DELIMITER ;$$
+INSERT INTO t1() VALUES();
+
+# We need to activate the purge thread before DROP TABLE.
 -- source include/wait_all_purged.inc
+DROP TABLE t1;
 DROP TABLE t1_purge, t2_purge, t3_purge, t4_purge, t12637786, t12963823;
 SET GLOBAL innodb_purge_rseg_truncate_frequency=@save_frequency;
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index 6942f5b7af8..f9b30b6a0a9 100644
--- a/storage/innobase/row/row0purge.cc
+++ b/storage/innobase/row/row0purge.cc
@@ -678,7 +678,7 @@ static void row_purge_reset_trx_id(purge_node_t* node, mtr_t* mtr)
 		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 + 2];
+		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,
-- 
2.30.9