From b5222205b22c6b5d730e5a1d6906c7847a5481de Mon Sep 17 00:00:00 2001 From: "songhuaxiong.shx" Date: Wed, 25 Mar 2026 10:16:27 +0800 Subject: [PATCH] Fix bug#115136 --- .../suite/innodb/r/innodb_bug115136.result | 54 ++++++++++++++ .../suite/innodb/t/innodb_bug115136.test | 71 +++++++++++++++++++ storage/innobase/ddl/ddl0ddl.cc | 11 +++ 3 files changed, 136 insertions(+) create mode 100644 mysql-test/suite/innodb/r/innodb_bug115136.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug115136.test diff --git a/mysql-test/suite/innodb/r/innodb_bug115136.result b/mysql-test/suite/innodb/r/innodb_bug115136.result new file mode 100644 index 00000000..5e9a0865 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug115136.result @@ -0,0 +1,54 @@ +SET @ori_parallel_read_threads = @@innodb_parallel_read_threads; +SET @ori_ddl_threads = @@innodb_ddl_threads; +SET @ori_ddl_buffer_size = @@innodb_ddl_buffer_size; +CREATE TABLE a(a INT, b INT, primary key(a)); +SET innodb_parallel_read_threads = 16; +SET innodb_ddl_threads = 16; +CREATE PROCEDURE insert_into_tables(IN num INTEGER) +BEGIN +DECLARE x INT; +SET x = 1; +while x < num do +INSERT INTO `a` (`a`, `b`) VALUES (x, x); +SET x = x + 1; +END while; +END$$ +CALL insert_into_tables(10000); +# +# Test 1: Small ddl_buffer_size to trigger buffer overflow during DDL +# +SET innodb_ddl_buffer_size = 65536; +ALTER TABLE a ADD COLUMN `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD KEY (`id`); +# No rows should have id=0 (Jakub's reported issue with incomplete fix) +SELECT COUNT(*) FROM a WHERE id = 0; +COUNT(*) +0 +# Max(id) should be same with MAX(a) and MAX(b) +SELECT MAX(a), MAX(b), MAX(id) FROM a; +MAX(a) MAX(b) MAX(id) +9999 9999 9999 +# No id values should be skipped (original bug) +SELECT b FROM a WHERE b NOT IN (SELECT id FROM a); +b +# +# Test 2: Large ddl_buffer_size (no buffer overflow, baseline test) +# +SET innodb_ddl_buffer_size = 104857600; +ALTER TABLE a DROP COLUMN id, DROP index id; +ALTER TABLE a ADD COLUMN `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD KEY (`id`); +# No rows should have id=0 +SELECT COUNT(*) FROM a WHERE id = 0; +COUNT(*) +0 +# Max(id) should be same with MAX(a) and MAX(b) +SELECT MAX(a), MAX(b), MAX(id) FROM a; +MAX(a) MAX(b) MAX(id) +9999 9999 9999 +# No id values should be skipped +SELECT b FROM a WHERE b NOT IN (SELECT id FROM a); +b +DROP TABLE a; +DROP PROCEDURE insert_into_tables; +SET innodb_parallel_read_threads = @ori_parallel_read_threads; +SET innodb_ddl_threads = @ori_ddl_threads; +SET innodb_ddl_buffer_size = @ori_ddl_buffer_size; diff --git a/mysql-test/suite/innodb/t/innodb_bug115136.test b/mysql-test/suite/innodb/t/innodb_bug115136.test new file mode 100644 index 00000000..b87abe24 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug115136.test @@ -0,0 +1,71 @@ +# Bug#115136: Skip value when ADD an auto_increment COLUMN +# +# When adding an auto_increment COLUMN via ALTER TABLE, if the DDL buffer +# overflows during the operation, row_build_w_add_vcol() recreates the +# tuple FROM the add_cols template, which resets the previously computed +# auto_increment value. This causes either: +# - Skipped auto_increment values (original bug) +# - Rows getting id=0 (with incomplete fix) + +SET @ori_parallel_read_threads = @@innodb_parallel_read_threads; +SET @ori_ddl_threads = @@innodb_ddl_threads; +SET @ori_ddl_buffer_size = @@innodb_ddl_buffer_size; + +CREATE TABLE a(a INT, b INT, primary key(a)); +SET innodb_parallel_read_threads = 16; +SET innodb_ddl_threads = 16; + +delimiter $$; +CREATE PROCEDURE insert_into_tables(IN num INTEGER) + BEGIN + DECLARE x INT; + SET x = 1; + while x < num do + INSERT INTO `a` (`a`, `b`) VALUES (x, x); + SET x = x + 1; + END while; +END$$ +delimiter ;$$ + +CALL insert_into_tables(10000); + +--echo # +--echo # Test 1: Small ddl_buffer_size to trigger buffer overflow during DDL +--echo # +SET innodb_ddl_buffer_size = 65536; + +ALTER TABLE a ADD COLUMN `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD KEY (`id`); + +--echo # No rows should have id=0 (Jakub's reported issue with incomplete fix) +SELECT COUNT(*) FROM a WHERE id = 0; + +--echo # Max(id) should be same with MAX(a) and MAX(b) +SELECT MAX(a), MAX(b), MAX(id) FROM a; + +--echo # No id values should be skipped (original bug) +SELECT b FROM a WHERE b NOT IN (SELECT id FROM a); + +--echo # +--echo # Test 2: Large ddl_buffer_size (no buffer overflow, baseline test) +--echo # +SET innodb_ddl_buffer_size = 104857600; + +ALTER TABLE a DROP COLUMN id, DROP index id; + +ALTER TABLE a ADD COLUMN `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ADD KEY (`id`); + +--echo # No rows should have id=0 +SELECT COUNT(*) FROM a WHERE id = 0; + +--echo # Max(id) should be same with MAX(a) and MAX(b) +SELECT MAX(a), MAX(b), MAX(id) FROM a; + +--echo # No id values should be skipped +SELECT b FROM a WHERE b NOT IN (SELECT id FROM a); + +# Cleanup +DROP TABLE a; +DROP PROCEDURE insert_into_tables; +SET innodb_parallel_read_threads = @ori_parallel_read_threads; +SET innodb_ddl_threads = @ori_ddl_threads; +SET innodb_ddl_buffer_size = @ori_ddl_buffer_size; diff --git a/storage/innobase/ddl/ddl0ddl.cc b/storage/innobase/ddl/ddl0ddl.cc index 7d4a72c9..f769b83b 100644 --- a/storage/innobase/ddl/ddl0ddl.cc +++ b/storage/innobase/ddl/ddl0ddl.cc @@ -497,6 +497,17 @@ dberr_t Row::build(ddl::Context &ctx, dict_index_t *index, mem_heap_t *heap, size_t type) noexcept { ut_ad(rec_offs_any_null_extern(index, m_rec, m_offsets) == nullptr); + if (type == ROW_COPY_DATA) { + ut_ad(m_ptr != nullptr); + for (ulint i = 0; i < dtuple_get_n_fields(m_ptr); ++i) { + dfield_dup(dtuple_get_nth_field(m_ptr, i), heap); + } + for (ulint i = 0; i < dtuple_get_n_v_fields(m_ptr); ++i) { + dfield_dup(dtuple_get_nth_v_field(m_ptr, i), heap); + } + return DB_SUCCESS; + } + /* Build a row based on the clustered index. */ m_ptr = row_build_w_add_vcol(type, index, m_rec, m_offsets, ctx.m_new_table, -- 2.19.1.6.gb485710b