diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 23a485dc5ed..bb1635b8621 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1,4 +1,29 @@ drop table if exists t1, t2; +# +# Bug#55458: Partitioned MyISAM table gets crashed by multi-table update +# +CREATE TABLE t1 ( +`id` int NOT NULL, +`user_num` int DEFAULT NULL, +PRIMARY KEY (`id`) +) ENGINE=MyISAM CHARSET=latin1; +INSERT INTO t1 VALUES (1,8601); +INSERT INTO t1 VALUES (2,8601); +INSERT INTO t1 VALUES (3,8601); +INSERT INTO t1 VALUES (4,8601); +CREATE TABLE t2 ( +`id` int(11) NOT NULL, +`user_num` int DEFAULT NULL, +`name` varchar(64) NOT NULL, +PRIMARY KEY (`id`) +) ENGINE=MyISAM CHARSET=latin1 +PARTITION BY HASH (id) +PARTITIONS 2; +INSERT INTO t2 VALUES (1,8601,'John'); +INSERT INTO t2 VALUES (2,8601,'JS'); +INSERT INTO t2 VALUES (3,8601,'John S'); +UPDATE t1, t2 SET t2.name = 'John Smith' WHERE t1.user_num = t2.user_num; +DROP TABLE t1, t2; CREATE TABLE t1 (a INT, b INT) PARTITION BY LIST (a) SUBPARTITION BY HASH (b) diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 22124cc1847..95d702ee6b8 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -14,6 +14,33 @@ drop table if exists t1, t2; --enable_warnings +--echo # +--echo # Bug#55458: Partitioned MyISAM table gets crashed by multi-table update +--echo # +CREATE TABLE t1 ( + `id` int NOT NULL, + `user_num` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM CHARSET=latin1; +INSERT INTO t1 VALUES (1,8601); +INSERT INTO t1 VALUES (2,8601); +INSERT INTO t1 VALUES (3,8601); +INSERT INTO t1 VALUES (4,8601); +CREATE TABLE t2 ( + `id` int(11) NOT NULL, + `user_num` int DEFAULT NULL, + `name` varchar(64) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM CHARSET=latin1 +PARTITION BY HASH (id) +PARTITIONS 2; +INSERT INTO t2 VALUES (1,8601,'John'); +INSERT INTO t2 VALUES (2,8601,'JS'); +INSERT INTO t2 VALUES (3,8601,'John S'); + +UPDATE t1, t2 SET t2.name = 'John Smith' WHERE t1.user_num = t2.user_num; + +DROP TABLE t1, t2; # # Bug#48276: can't add column if subpartition exists CREATE TABLE t1 (a INT, b INT) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index a87c7fbf7b8..3d3ae33be4b 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -232,6 +232,7 @@ void ha_partition::init_handler_variables() m_innodb= FALSE; m_extra_cache= FALSE; m_extra_cache_size= 0; + m_extra_prepare_for_update= FALSE; m_handler_status= handler_not_initialized; m_low_byte_first= 1; m_part_field_array= NULL; @@ -5549,9 +5550,20 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_PREPARE_FOR_RENAME: DBUG_RETURN(prepare_for_rename()); break; + case HA_EXTRA_PREPARE_FOR_UPDATE: + DBUG_ASSERT(m_extra_cache); + /* + Needs to be run on the first partition in the range now, and + later in late_extra_cache, when switching to a new partition to scan. + */ + m_extra_prepare_for_update= TRUE; + if (m_part_spec.start_part != NO_CURRENT_PART_ID) + { + VOID(m_file[m_part_spec.start_part]->extra(HA_EXTRA_PREPARE_FOR_UPDATE)); + } + break; case HA_EXTRA_NORMAL: case HA_EXTRA_QUICK: - case HA_EXTRA_PREPARE_FOR_UPDATE: case HA_EXTRA_FORCE_REOPEN: case HA_EXTRA_PREPARE_FOR_DROP: case HA_EXTRA_FLUSH_CACHE: @@ -5578,6 +5590,7 @@ int ha_partition::extra(enum ha_extra_function operation) { m_extra_cache= FALSE; m_extra_cache_size= 0; + m_extra_prepare_for_update= FALSE; DBUG_RETURN(loop_extra(operation)); } case HA_EXTRA_IGNORE_NO_KEY: @@ -5705,6 +5718,7 @@ int ha_partition::extra_opt(enum ha_extra_function operation, ulong cachesize) void ha_partition::prepare_extra_cache(uint cachesize) { DBUG_ENTER("ha_partition::prepare_extra_cache()"); + DBUG_PRINT("info", ("cachesize %u", cachesize)); m_extra_cache= TRUE; m_extra_cache_size= cachesize; @@ -5793,14 +5807,21 @@ void ha_partition::late_extra_cache(uint partition_id) { handler *file; DBUG_ENTER("ha_partition::late_extra_cache"); + DBUG_PRINT("info", ("extra_cache %u partid %u size %u", m_extra_cache, + partition_id, m_extra_cache_size)); - if (!m_extra_cache) + if (!m_extra_cache && !m_extra_prepare_for_update) DBUG_VOID_RETURN; file= m_file[partition_id]; if (m_extra_cache_size == 0) VOID(file->extra(HA_EXTRA_CACHE)); else VOID(file->extra_opt(HA_EXTRA_CACHE, m_extra_cache_size)); + if (m_extra_prepare_for_update) + { + DBUG_ASSERT(m_extra_cache); + VOID(file->extra(HA_EXTRA_PREPARE_FOR_UPDATE)); + } DBUG_VOID_RETURN; } @@ -5821,7 +5842,7 @@ void ha_partition::late_extra_no_cache(uint partition_id) handler *file; DBUG_ENTER("ha_partition::late_extra_no_cache"); - if (!m_extra_cache) + if (!m_extra_cache && !m_extra_prepare_for_update) DBUG_VOID_RETURN; file= m_file[partition_id]; VOID(file->extra(HA_EXTRA_NO_CACHE)); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index e3dc7d17c6d..013341e4df3 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -154,6 +154,8 @@ private: */ bool m_extra_cache; uint m_extra_cache_size; + /* The same goes for HA_EXTRA_PREPARE_FOR_UPDATE */ + bool m_extra_prepare_for_update; void init_handler_variables(); /*