From 99e18bbb5a04dd19e8fdbcdac806eea8d1795732 Mon Sep 17 00:00:00 2001 From: Mohammad Tafzeel Shams Date: Tue, 7 Jul 2026 16:58:55 +0530 Subject: [PATCH] MDEV-39795: Assertion `n_reserved > 0' failed Problem: ======== 1. Assertion `n_reserved > 0` failed in fseg_create(): fsp_reserve_free_extents() has a special condition for small tablespaces where it reserves individual pages instead of full extents. In such cases, n_reserved can be 0 even when the reservation succeeds, causing the assertion ut_ad(n_reserved > 0) to fail incorrectly. The code was checking n_reserved to determine whether a reservation had already been attempted, but this logic breaks for small tablespaces where pages, rather than extents, are reserved. 2. Encryption metadata not cleared for compressed-only pages: buf_page_encrypt() only cleared encryption-related metadata fields (key-version and crypt-checksum) when the page was neither encrypted nor compressed. However, these fields should also be cleared when page_compressed is true but encrypted is false, to avoid leaving stale encryption metadata in compressed-only pages. Solution: ========= buf_page_encrypt(): Refactored the early-return logic. Encryption metadata fields are now cleared whenever encrypted is false, regardless of page_compressed. The function returns early only when both !encrypted and !page_compressed. fseg_create(): Reintroduced a boolean variable `reserved` to track whether fsp_reserve_free_extents() has been attempted (removed as part of MDEV-38419 | c7313da), replacing assertion `n_reserved > 0`. Added an early return when DB_DECRYPTION_FAILED is encountered during inode allocation. my_error_innodb(): Added handling for DB_DECRYPTION_FAILED to report decryption errors to the user through ER_GET_ERRMSG. --- ...nnodb_encrypt_compression_algorithm.result | 34 +++++++++++++++++++ .../innodb_encrypt_compression_algorithm.test | 29 ++++++++++++++++ storage/innobase/buf/buf0flu.cc | 7 ++-- storage/innobase/fsp/fsp0fsp.cc | 13 ++++--- storage/innobase/handler/handler0alter.cc | 4 +++ 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 mysql-test/suite/encryption/r/innodb_encrypt_compression_algorithm.result create mode 100644 mysql-test/suite/encryption/t/innodb_encrypt_compression_algorithm.test diff --git a/mysql-test/suite/encryption/r/innodb_encrypt_compression_algorithm.result b/mysql-test/suite/encryption/r/innodb_encrypt_compression_algorithm.result new file mode 100644 index 0000000000000..c1a54593571ed --- /dev/null +++ b/mysql-test/suite/encryption/r/innodb_encrypt_compression_algorithm.result @@ -0,0 +1,34 @@ +# +# MDEV 39795 : Assertion `n_reserved > 0' failed +# +SET GLOBAL innodb_encrypt_tables = ON; +SET GLOBAL innodb_compression_algorithm = 'none'; +CREATE TABLE t1 ( +id INT AUTO_INCREMENT PRIMARY KEY, +col1 INT, +col2 INT, +col3 INT +) ENGINE=InnoDB ROW_FORMAT = Compact PAGE_COMPRESSED=1; +INSERT INTO t1 (col1, col2, col3) VALUES (1, 10, 100); +FLUSH TABLES t1 FOR EXPORT; +# restart +ALTER TABLE t1 ADD INDEX idx_col1 (col1); +FLUSH TABLES t1 FOR EXPORT; +# restart +ALTER TABLE t1 ADD INDEX idx_col2_col3 (col2, col3); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `col1` int(11) DEFAULT NULL, + `col2` int(11) DEFAULT NULL, + `col3` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_col1` (`col1`), + KEY `idx_col2_col3` (`col2`,`col3`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci ROW_FORMAT=COMPACT `PAGE_COMPRESSED`=1 +SELECT * FROM t1; +id col1 col2 col3 +1 1 10 100 +# Cleanup +DROP TABLE t1; diff --git a/mysql-test/suite/encryption/t/innodb_encrypt_compression_algorithm.test b/mysql-test/suite/encryption/t/innodb_encrypt_compression_algorithm.test new file mode 100644 index 0000000000000..9d7bd1f123bf4 --- /dev/null +++ b/mysql-test/suite/encryption/t/innodb_encrypt_compression_algorithm.test @@ -0,0 +1,29 @@ +-- source include/have_innodb.inc +-- source include/have_file_key_management_plugin.inc + +--echo # +--echo # MDEV 39795 : Assertion `n_reserved > 0' failed +--echo # + +SET GLOBAL innodb_encrypt_tables = ON; +SET GLOBAL innodb_compression_algorithm = 'none'; +CREATE TABLE t1 ( + id INT AUTO_INCREMENT PRIMARY KEY, + col1 INT, + col2 INT, + col3 INT +) ENGINE=InnoDB ROW_FORMAT = Compact PAGE_COMPRESSED=1; +INSERT INTO t1 (col1, col2, col3) VALUES (1, 10, 100); +FLUSH TABLES t1 FOR EXPORT; + +--source include/restart_mysqld.inc +ALTER TABLE t1 ADD INDEX idx_col1 (col1); +FLUSH TABLES t1 FOR EXPORT; + +--source include/restart_mysqld.inc +ALTER TABLE t1 ADD INDEX idx_col2_col3 (col2, col3); +SHOW CREATE TABLE t1; +SELECT * FROM t1; + +--echo # Cleanup +DROP TABLE t1; diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index b0ee82e03e06b..658858bbc0111 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -658,9 +658,9 @@ static byte *buf_page_encrypt(fil_space_t *space, buf_page_t *bpage, byte *s, const bool full_crc32= space->full_crc32(); - if (!encrypted && !page_compressed) + if (!encrypted) { - /* No need to encrypt or compress. Clear key-version & crypt-checksum. */ + /* No need to encrypt. Clear key-version & crypt-checksum. */ static_assert(FIL_PAGE_FCRC32_KEY_VERSION % 4 == 0, "alignment"); static_assert(FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION % 4 == 2, "not perfect alignment"); @@ -668,7 +668,8 @@ static byte *buf_page_encrypt(fil_space_t *space, buf_page_t *bpage, byte *s, memset_aligned<4>(s + FIL_PAGE_FCRC32_KEY_VERSION, 0, 4); else memset_aligned<2>(s + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION, 0, 8); - return s; + if (!page_compressed) + return s; } static_assert(FIL_PAGE_FCRC32_END_LSN % 4 == 0, "alignment"); diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index a36143c3bccdd..e3d23227a8245 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1684,6 +1684,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, fseg_inode_t* inode; ib_id_t seg_id; uint32_t n_reserved = 0; + bool reserved = false; DBUG_ENTER("fseg_create"); @@ -1707,20 +1708,24 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, inode_alloc: inode = fsp_alloc_seg_inode(space, header, &iblock, mtr, err); + if (*err == DB_DECRYPTION_FAILED) + DBUG_RETURN(nullptr); if (!inode) { block = nullptr; reserve_extent: - if (!has_done_reservation && !n_reserved) { + if (!has_done_reservation && !reserved) { *err = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_NORMAL, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) { DBUG_RETURN(nullptr); } - ut_ad(n_reserved > 0); - /* Extents reserved successfully. So - try allocating the page or inode */ + /* Extents (or pages for small table) + reserved successfully. + See fsp_reserve_free_extents() + So try allocating the page or inode */ + reserved = true; if (inode) { goto page_alloc; } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 6f214d43000dd..09748fa5ecb04 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -918,6 +918,10 @@ my_error_innodb( case DB_CORRUPTION: my_error(ER_NOT_KEYFILE, MYF(0), table); break; + case DB_DECRYPTION_FAILED: + my_error(ER_GET_ERRMSG, MYF(0), error, + ut_strerr(error), "InnoDB"); + break; case DB_TOO_BIG_RECORD: { /* Note that in page0zip.ic page_zip_rec_needs_ext() rec_size is limited to COMPRESSED_REC_MAX_DATA_SIZE (16K) or