From 998a625d006db4e740db4faf821ab5bbd2f4a2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 29 Nov 2024 14:16:11 +0200 Subject: [PATCH 01/33] Clean up recv_sys.pages bookkeeping Instead of repurposing buf_page_t::access_time for state()==MEMORY blocks that are part of recv_sys.pages, let us define an anonymous union around buf_page_t::hash. In this way, we will be able to declare access_time private. Reviewed by: Vladislav Lesin --- storage/innobase/include/buf0buf.h | 26 ++++++++++++++------------ storage/innobase/log/log0recv.cc | 24 ++++++++++++------------ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 08b2125c7aa..d50c63579e3 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -513,8 +513,19 @@ public: // FIXME: fix fil_iterate() /** Page id. Protected by buf_pool.page_hash.lock_get() when the page is in buf_pool.page_hash. */ page_id_t id_; - /** buf_pool.page_hash link; protected by buf_pool.page_hash.lock_get() */ - buf_page_t *hash; + union { + /** for in_file(): buf_pool.page_hash link; + protected by buf_pool.page_hash.lock_get() */ + buf_page_t *hash; + /** for state()==MEMORY that are part of recv_sys.pages and + protected by recv_sys.mutex */ + struct { + /** number of recv_sys.pages entries stored in the block */ + uint16_t used_records; + /** the offset of the next free record */ + uint16_t free_offset; + }; + }; private: /** log sequence number of the START of the log entry written of the oldest modification to this block which has not yet been written @@ -605,16 +616,7 @@ public: /* @} */ Atomic_counter access_time; /*!< time of first access, or 0 if the block was never accessed - in the buffer pool. - - For state() == MEMORY - blocks, this field can be repurposed - for something else. - - When this field counts log records - and bytes allocated for recv_sys.pages, - the field is protected by - recv_sys_t::mutex. */ + in the buffer pool. */ buf_page_t() : id_{0} { static_assert(NOT_USED == 0, "compatibility"); diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 47c757f0f79..2c9c93a42fa 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1486,6 +1486,7 @@ void recv_sys_t::clear() { buf_block_t *prev_block= UT_LIST_GET_PREV(unzip_LRU, block); ut_ad(block->page.state() == buf_page_t::MEMORY); + block->page.hash= nullptr; UT_LIST_REMOVE(blocks, block); MEM_MAKE_ADDRESSABLE(block->page.frame, srv_page_size); buf_block_free(block); @@ -1534,14 +1535,11 @@ inline void recv_sys_t::free(const void *data) buf_block_t *block= &chunk->blocks[offs]; ut_ad(block->page.frame == page_align(data)); ut_ad(block->page.state() == buf_page_t::MEMORY); - ut_ad(static_cast(block->page.access_time - 1) < - srv_page_size); - unsigned a= block->page.access_time; - ut_ad(a >= 1U << 16); - a-= 1U << 16; - block->page.access_time= a; - if (!(a >> 16)) + ut_ad(uint16_t(block->page.free_offset - 1) < srv_page_size); + ut_ad(block->page.used_records); + if (!--block->page.used_records) { + block->page.hash= nullptr; UT_LIST_REMOVE(blocks, block); MEM_MAKE_ADDRESSABLE(block->page.frame, srv_page_size); buf_block_free(block); @@ -2250,7 +2248,7 @@ bool recv_sys_t::add(map::iterator it, lsn_t start_lsn, lsn_t lsn, ut_ad(tail->lsn == lsn); block= UT_LIST_GET_LAST(blocks); ut_ad(block); - const size_t used= static_cast(block->page.access_time - 1) + 1; + const size_t used= uint16_t(block->page.free_offset - 1) + 1; ut_ad(used >= ALIGNMENT); const byte *end= const_cast(tail)->end(); if (!((reinterpret_cast(end + len) ^ @@ -2271,7 +2269,7 @@ append: ut_ad(new_used > used); if (new_used > srv_page_size) break; - block->page.access_time= (block->page.access_time & ~0U << 16) | + block->page.free_offset= ut_calc_align(static_cast(new_used), ALIGNMENT); goto append; } @@ -2286,7 +2284,8 @@ append: block= add_block(); if (UNIV_UNLIKELY(!block)) return true; - block->page.access_time= 1U << 16 | + block->page.used_records= 1; + block->page.free_offset= ut_calc_align(static_cast(size), ALIGNMENT); static_assert(ut_is_2pow(ALIGNMENT), "ALIGNMENT must be a power of 2"); UT_LIST_ADD_FIRST(blocks, block); @@ -2296,7 +2295,7 @@ append: } else { - size_t free_offset= static_cast(block->page.access_time); + size_t free_offset= block->page.free_offset; ut_ad(!ut_2pow_remainder(free_offset, ALIGNMENT)); if (UNIV_UNLIKELY(!free_offset)) { @@ -2309,7 +2308,8 @@ append: if (free_offset > srv_page_size) goto create_block; - block->page.access_time= ((block->page.access_time >> 16) + 1) << 16 | + block->page.used_records++; + block->page.free_offset= ut_calc_align(static_cast(free_offset), ALIGNMENT); MEM_MAKE_ADDRESSABLE(block->page.frame + free_offset - size, size); buf= block->page.frame + free_offset - size; From 507323abe6d0cf61abb608b87b25fb5f6141aff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 29 Nov 2024 14:16:34 +0200 Subject: [PATCH 02/33] Cleanup: Remove duplicated code buf_block_alloc(): Define as an alias in buf0lru.h, which defines the underlying buf_LRU_get_free_block(). buf_block_free(): Define as an alias of the non-inline function buf_pool.free_block(block). Reviewed by: Vladislav Lesin --- storage/innobase/btr/btr0sea.cc | 1 + storage/innobase/buf/buf0flu.cc | 1 + storage/innobase/include/buf0buf.h | 12 +----------- storage/innobase/include/buf0buf.inl | 20 -------------------- storage/innobase/include/buf0lru.h | 11 +++++++---- storage/innobase/mem/mem0mem.cc | 1 + 6 files changed, 11 insertions(+), 35 deletions(-) diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index fc30f4121d1..7203809f036 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -27,6 +27,7 @@ Created 2/17/1996 Heikki Tuuri #include "btr0sea.h" #ifdef BTR_CUR_HASH_ADAPT #include "buf0buf.h" +#include "buf0lru.h" #include "page0page.h" #include "page0cur.h" #include "btr0cur.h" diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index df85feb603a..a513f2499f6 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -31,6 +31,7 @@ Created 11/11/1995 Heikki Tuuri #include #include "buf0flu.h" +#include "buf0lru.h" #include "buf0buf.h" #include "buf0checksum.h" #include "buf0dblwr.h" diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index d50c63579e3..cb44cafa673 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -143,17 +143,7 @@ operator<<( #ifndef UNIV_INNOCHECKSUM # define buf_pool_get_curr_size() srv_buf_pool_curr_size - -/** Allocate a buffer block. -@return own: the allocated block, state()==MEMORY */ -inline buf_block_t *buf_block_alloc(); -/********************************************************************//** -Frees a buffer block which does not contain a file page. */ -UNIV_INLINE -void -buf_block_free( -/*===========*/ - buf_block_t* block); /*!< in, own: block to be freed */ +# define buf_block_free(block) buf_pool.free_block(block) #define buf_page_get(ID, SIZE, LA, MTR) \ buf_page_get_gen(ID, SIZE, LA, NULL, BUF_GET, MTR) diff --git a/storage/innobase/include/buf0buf.inl b/storage/innobase/include/buf0buf.inl index a72ff61161f..85bd10b5e88 100644 --- a/storage/innobase/include/buf0buf.inl +++ b/storage/innobase/include/buf0buf.inl @@ -75,26 +75,6 @@ inline bool buf_page_peek_if_too_old(const buf_page_t *bpage) } } -/** Allocate a buffer block. -@return own: the allocated block, in state BUF_BLOCK_MEMORY */ -inline buf_block_t *buf_block_alloc() -{ - return buf_LRU_get_free_block(false); -} - -/********************************************************************//** -Frees a buffer block which does not contain a file page. */ -UNIV_INLINE -void -buf_block_free( -/*===========*/ - buf_block_t* block) /*!< in, own: block to be freed */ -{ - mysql_mutex_lock(&buf_pool.mutex); - buf_LRU_block_free_non_file_page(block); - mysql_mutex_unlock(&buf_pool.mutex); -} - /********************************************************************//** Increments the modify clock of a frame by 1. The caller must (1) own the buf_pool mutex and block bufferfix count has to be zero, (2) or own an x-lock diff --git a/storage/innobase/include/buf0lru.h b/storage/innobase/include/buf0lru.h index 53a5f136fd2..ac4f36066a4 100644 --- a/storage/innobase/include/buf0lru.h +++ b/storage/innobase/include/buf0lru.h @@ -81,14 +81,17 @@ we put it to free list to be used. buf_block_t* buf_LRU_get_free_block(bool have_mutex) MY_ATTRIBUTE((malloc,warn_unused_result)); +#define buf_block_alloc() buf_LRU_get_free_block(false) + /** @return whether the unzip_LRU list should be used for evicting a victim instead of the general LRU list */ bool buf_LRU_evict_from_unzip_LRU(); -/** Puts a block back to the free list. -@param[in] block block; not containing a file page */ -void -buf_LRU_block_free_non_file_page(buf_block_t* block); +/** Free a buffer block which does not contain a file page, +while holding buf_pool.mutex. +@param block block to be put to buf_pool.free */ +void buf_LRU_block_free_non_file_page(buf_block_t *block); + /******************************************************************//** Adds a block to the LRU list. Please make sure that the page_size is already set when invoking the function, so that we can get correct diff --git a/storage/innobase/mem/mem0mem.cc b/storage/innobase/mem/mem0mem.cc index 5e8587bfea6..8a342275a46 100644 --- a/storage/innobase/mem/mem0mem.cc +++ b/storage/innobase/mem/mem0mem.cc @@ -26,6 +26,7 @@ Created 6/9/1994 Heikki Tuuri #include "mem0mem.h" #include "buf0buf.h" +#include "buf0lru.h" #include "srv0srv.h" #include From 1a9011d273e0330c8242ad8857f0519bf0aa2ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 29 Nov 2024 15:12:20 +0200 Subject: [PATCH 03/33] MDEV-35525: Index corruption in reverse scans btr_cur_t::search_leaf(): In the BTR_SEARCH_PREV and BTR_MODIFY_PREV modes, reset the previous search status before invoking page_cur_search_with_match(). Otherwise, we the search could invoke in a totally wrong subtree. This fixes a regression that was introduced in commit de4030e4d49805a7ded5c0bfee01cc3fd7623522 (MDEV-30400). --- storage/innobase/btr/btr0cur.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index a5ffc825c20..6a57f2dc307 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -1660,6 +1660,7 @@ release_tree: ut_ad(block_savepoint + 2 == mtr->get_savepoint()); if (ret < 0) { + up_match= 0, low_match= 0, up_bytes= 0, low_bytes= 0; /* While our latch on the level-2 page prevents splits or merges of this level-1 block, other threads may have modified it due to splitting or merging some level-0 (leaf) From 01cc92e098be51798d80bb8f3e1d61dc47979d63 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sun, 1 Dec 2024 13:52:41 +0400 Subject: [PATCH 04/33] MDEV-34700 Connect SQLite3 MTR test fails due to various charset/collation related output changes Re-recorded test results with the COLLATE clause, according to MDEV-29446. --- storage/connect/mysql-test/connect/r/odbc_sqlite3.result | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/connect/mysql-test/connect/r/odbc_sqlite3.result b/storage/connect/mysql-test/connect/r/odbc_sqlite3.result index bce69227073..0a241305a62 100644 --- a/storage/connect/mysql-test/connect/r/odbc_sqlite3.result +++ b/storage/connect/mysql-test/connect/r/odbc_sqlite3.result @@ -2,14 +2,14 @@ Table Create Table t1 CREATE TABLE `t1` ( `Description` char(128) NOT NULL, `Attributes` varchar(256) DEFAULT NULL -) ENGINE=CONNECT DEFAULT CHARSET=latin1 `TABLE_TYPE`='ODBC' `CATFUNC`='Drivers' +) ENGINE=CONNECT DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci `TABLE_TYPE`='ODBC' `CATFUNC`='Drivers' SET NAMES utf8; CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=ODBC CONNECTION='Driver=SQLite3 ODBC Driver;Database=MTR_SUITE_DIR/std_data/test.sqlite3;NoWCHAR=yes' CHARSET=utf8 DATA_CHARSET=utf8;; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `a` varchar(64) DEFAULT NULL -) ENGINE=CONNECT DEFAULT CHARSET=utf8 CONNECTION='Driver=SQLite3 ODBC Driver;Database=MTR_SUITE_DIR/std_data/test.sqlite3;NoWCHAR=yes' `TABLE_TYPE`='ODBC' `DATA_CHARSET`='utf8' +) ENGINE=CONNECT DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci CONNECTION='Driver=SQLite3 ODBC Driver;Database=MTR_SUITE_DIR/std_data/test.sqlite3;NoWCHAR=yes' `TABLE_TYPE`='ODBC' `DATA_CHARSET`='utf8' SELECT * FROM t1; a test1 @@ -21,8 +21,8 @@ CREATE TABLE t2 AS SELECT * FROM t1; SHOW CREATE TABLE t2; Table Create Table t2 CREATE TABLE `t2` ( - `a` varchar(64) CHARACTER SET utf8 DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 + `a` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci SELECT * FROM t2; a test1 From ecf2e131bd5f17f8ed97c50cfe8dc13623a763d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 2 Dec 2024 11:04:38 +0200 Subject: [PATCH 05/33] MDEV-31174 fixup: clang++-20 -Wnontrivial-memcall --- sql/item_create.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/item_create.h b/sql/item_create.h index 25a75a22163..166e173b3c2 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -304,7 +304,7 @@ class Native_functions_hash: public HASH public: Native_functions_hash() { - bzero(this, sizeof(*this)); + bzero((void*) this, sizeof(*this)); } ~Native_functions_hash() { From 3de412fbe884a85b2ec7ae56fee47152c008e6c0 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 19 Nov 2024 12:44:42 +0400 Subject: [PATCH 06/33] MDEV-25593 Assertion `0' failed in Type_handler_temporal_result::Item_get_date on double EXECUTE When binding to NULL, DEFAULT or IGNORE from an Item value, Item_param did not change m_type_handler, so its value remained from the previous bind. Thid led to DBUG_ASSERTs in Item_param::get_date() and Timestamp_or_zero_datetime_native_null. Fix: Set Item_param::m_type_handler to &type_handler_null when binding from an Item returning NULL. This patch also fixes MDEV-35427. --- mysql-test/main/ps_11bugs.result | 55 +++++++++++++++++++ mysql-test/main/ps_11bugs.test | 38 +++++++++++++ .../mysql-test/type_inet/type_inet6_ps.result | 17 ++++++ .../mysql-test/type_inet/type_inet6_ps.test | 16 ++++++ sql/item.cc | 12 +++- sql/item.h | 10 ++-- sql/sql_prepare.cc | 4 +- 7 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 plugin/type_inet/mysql-test/type_inet/type_inet6_ps.result create mode 100644 plugin/type_inet/mysql-test/type_inet/type_inet6_ps.test diff --git a/mysql-test/main/ps_11bugs.result b/mysql-test/main/ps_11bugs.result index 3bc7039583b..87a3487141b 100644 --- a/mysql-test/main/ps_11bugs.result +++ b/mysql-test/main/ps_11bugs.result @@ -191,3 +191,58 @@ Warnings: Warning 1292 Truncated incorrect INTEGER value: '+ -><()~*:""&|' SET @@sort_buffer_size=DEFAULT; End of 5.0 tests. +# Start of 10.5 tests +# +# MDEV-25593 Assertion `0' failed in Type_handler_temporal_result::Item_get_date on double EXECUTE +# +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30'); +PREPARE stmt FROM 'SELECT ? AS c1'; +EXECUTE stmt USING current_timestamp; +c1 +2001-01-01 10:20:30 +EXECUTE stmt USING @unknown; +c1 +NULL +DEALLOCATE PREPARE stmt; +PREPARE stmt FROM 'SELECT ? AS c1 FROM DUAL'; +EXECUTE stmt USING current_time; +c1 +10:20:30 +EXECUTE stmt USING DEFAULT; +c1 +NULL +DEALLOCATE PREPARE stmt; +PREPARE stmt FROM 'SELECT ? AS c1 FROM DUAL'; +EXECUTE stmt USING current_time; +c1 +10:20:30 +EXECUTE stmt USING IGNORE; +c1 +NULL +DEALLOCATE PREPARE stmt; +PREPARE stmt FROM "SELECT DATE('') between''AND ? AS c1"; +EXECUTE stmt USING current_time; +c1 +1 +Warnings: +Warning 1292 Truncated incorrect datetime value: '' +Warning 1292 Truncated incorrect datetime value: '' +EXECUTE stmt USING @unknown; +c1 +NULL +Warnings: +Warning 1292 Truncated incorrect datetime value: '' +Warning 1292 Truncated incorrect datetime value: '' +DEALLOCATE PREPARE stmt; +PREPARE stmt FROM 'SELECT CONCAT(UNIX_TIMESTAMP(?)) AS c1'; +EXECUTE stmt USING CURRENT_TIMESTAMP; +c1 +978344430 +EXECUTE stmt USING @unknown; +c1 +NULL +DEALLOCATE PREPARE stmt; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# End of 10.5 tests diff --git a/mysql-test/main/ps_11bugs.test b/mysql-test/main/ps_11bugs.test index 1888de060fc..7a0843e9352 100644 --- a/mysql-test/main/ps_11bugs.test +++ b/mysql-test/main/ps_11bugs.test @@ -217,3 +217,41 @@ SELECT LEFT("12345", @@ft_boolean_syntax); SET @@sort_buffer_size=DEFAULT; --echo End of 5.0 tests. + + +--echo # Start of 10.5 tests + +--echo # +--echo # MDEV-25593 Assertion `0' failed in Type_handler_temporal_result::Item_get_date on double EXECUTE +--echo # + +SET time_zone='+00:00'; +SET timestamp=UNIX_TIMESTAMP('2001-01-01 10:20:30'); +PREPARE stmt FROM 'SELECT ? AS c1'; +EXECUTE stmt USING current_timestamp; +EXECUTE stmt USING @unknown; +DEALLOCATE PREPARE stmt; + +PREPARE stmt FROM 'SELECT ? AS c1 FROM DUAL'; +EXECUTE stmt USING current_time; +EXECUTE stmt USING DEFAULT; +DEALLOCATE PREPARE stmt; + +PREPARE stmt FROM 'SELECT ? AS c1 FROM DUAL'; +EXECUTE stmt USING current_time; +EXECUTE stmt USING IGNORE; +DEALLOCATE PREPARE stmt; + +PREPARE stmt FROM "SELECT DATE('') between''AND ? AS c1"; +EXECUTE stmt USING current_time; +EXECUTE stmt USING @unknown; +DEALLOCATE PREPARE stmt; + +PREPARE stmt FROM 'SELECT CONCAT(UNIX_TIMESTAMP(?)) AS c1'; +EXECUTE stmt USING CURRENT_TIMESTAMP; +EXECUTE stmt USING @unknown; +DEALLOCATE PREPARE stmt; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + +--echo # End of 10.5 tests diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.result b/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.result new file mode 100644 index 00000000000..4458f5e2f75 --- /dev/null +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.result @@ -0,0 +1,17 @@ +# Start of 10.5 tests +# +# MDEV-35427 Assertion `is_null() >= item->null_value' failed in Timestamp_or_zero_datetime_native_null::Timestamp_or_zero_datetime_native_null on EXECUTE +# +SET time_zone='+00:00'; +SET timestamp=1000000000; +PREPARE s FROM 'SELECT CONCAT (UNIX_TIMESTAMP(?))'; +EXECUTE s USING CAST('::' AS INET6); +CONCAT (UNIX_TIMESTAMP(?)) +999993600 +EXECUTE s USING NULL; +CONCAT (UNIX_TIMESTAMP(?)) +NULL +DEALLOCATE PREPARE s; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; +# End of 10.5 tests diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.test b/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.test new file mode 100644 index 00000000000..4251e820e17 --- /dev/null +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_ps.test @@ -0,0 +1,16 @@ +--echo # Start of 10.5 tests + +--echo # +--echo # MDEV-35427 Assertion `is_null() >= item->null_value' failed in Timestamp_or_zero_datetime_native_null::Timestamp_or_zero_datetime_native_null on EXECUTE +--echo # + +SET time_zone='+00:00'; +SET timestamp=1000000000; +PREPARE s FROM 'SELECT CONCAT (UNIX_TIMESTAMP(?))'; +EXECUTE s USING CAST('::' AS INET6); +EXECUTE s USING NULL; +DEALLOCATE PREPARE s; +SET timestamp=DEFAULT; +SET time_zone=DEFAULT; + +--echo # End of 10.5 tests diff --git a/sql/item.cc b/sql/item.cc index dc27feb759a..dd9a9063e55 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4426,6 +4426,7 @@ bool Item_param::set_from_item(THD *thd, Item *item) if (item->null_value) { set_null(); + set_handler(&type_handler_null); DBUG_RETURN(false); } else @@ -4443,7 +4444,10 @@ bool Item_param::set_from_item(THD *thd, Item *item) DBUG_RETURN(set_value(thd, item, &tmp, h)); } else + { set_null(); + set_handler(&type_handler_null); + } DBUG_RETURN(0); } @@ -4963,7 +4967,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src) } -void Item_param::set_default() +void Item_param::set_default(bool set_type_handler_null) { m_is_settable_routine_parameter= false; state= DEFAULT_VALUE; @@ -4976,13 +4980,17 @@ void Item_param::set_default() can misbehave (e.g. crash on asserts). */ null_value= true; + if (set_type_handler_null) + set_handler(&type_handler_null); } -void Item_param::set_ignore() +void Item_param::set_ignore(bool set_type_handler_null) { m_is_settable_routine_parameter= false; state= IGNORE_VALUE; null_value= true; + if (set_type_handler_null) + set_handler(&type_handler_null); } /** diff --git a/sql/item.h b/sql/item.h index 204f98b7677..8a13c9ea85a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -4154,8 +4154,8 @@ public: int save_in_field(Field *field, bool no_conversions) override; - void set_default(); - void set_ignore(); + void set_default(bool set_type_handler_null); + void set_ignore(bool set_type_handler_null); void set_null(); void set_int(longlong i, uint32 max_length_arg); void set_double(double i); @@ -6695,7 +6695,7 @@ public: { // It should not be possible to have "EXECUTE .. USING DEFAULT(a)" DBUG_ASSERT(0); - param->set_default(); + param->set_default(true); return false; } table_map used_tables() const override; @@ -6826,7 +6826,7 @@ public: } bool save_in_param(THD *, Item_param *param) override { - param->set_default(); + param->set_default(true); return false; } Item *do_get_copy(THD *thd) const override @@ -6860,7 +6860,7 @@ public: } bool save_in_param(THD *, Item_param *param) override { - param->set_ignore(); + param->set_ignore(true); return false; } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 8647db0e766..3d7dda16643 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -899,10 +899,10 @@ static bool insert_bulk_params(Prepared_statement *stmt, param->set_null(); break; case STMT_INDICATOR_DEFAULT: - param->set_default(); + param->set_default(false); break; case STMT_INDICATOR_IGNORE: - param->set_ignore(); + param->set_ignore(false); break; default: DBUG_ASSERT(0); From 5c86f3df33dc972acfc3665025c25d6430887569 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 29 Nov 2024 11:45:01 +0100 Subject: [PATCH 07/33] MDEV-35522: MariaDB Audit does not detect all DCLs forms when masking password 1. skip OR REPLACE (to make it filter_query_type made recursive) 2. skip SET STATEMENT ... FOR before checking statements with passwords --- .../plugins/r/server_audit_pwd_mask.result | 11 ++ .../plugins/t/server_audit_pwd_mask.test | 13 ++ plugin/server_audit/server_audit.c | 170 ++++++++++++------ 3 files changed, 142 insertions(+), 52 deletions(-) diff --git a/mysql-test/suite/plugins/r/server_audit_pwd_mask.result b/mysql-test/suite/plugins/r/server_audit_pwd_mask.result index 7cf3b05bcf1..ce90230f1ea 100644 --- a/mysql-test/suite/plugins/r/server_audit_pwd_mask.result +++ b/mysql-test/suite/plugins/r/server_audit_pwd_mask.result @@ -1,3 +1,6 @@ +# +# MDEV-35507 and MDEV-35522 +# install plugin ed25519 soname 'auth_ed25519'; install plugin server_audit soname 'server_audit'; set global server_audit_file_path='server_audit.log'; @@ -6,17 +9,25 @@ set global server_audit_logging=on; # unsafe to log passwords (pwd-123) CREATE USER u1 IDENTIFIED BY 'pwd_123'; create user u2 IDENTIFIED VIA ed25519 USING PASSWORD('pwd_123'); +CREATE OR REPLACE USER u1 IDENTIFIED BY 'pwd_123'; SET PASSWORD FOR u1 = PASSWORD('pwd_123'); ALTER USER u1 IDENTIFIED BY 'pwd_123'; +ALTER USER if exists u1 IDENTIFIED BY 'pwd_123'; +SET STATEMENT max_statement_time=10 FOR ALTER USER u1 IDENTIFIED BY 'pwd_123'; alter user u2 identified VIA ed25519 USING password('pwd_123'); GRANT ALL ON test TO u1 IDENTIFIED BY "pwd_123"; GRANT ALL ON test TO u1 identified VIA ed25519 as password('pwd_123') or ed25519 using password('pwd_123'); +CREATE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); +CREATE OR REPLACE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); +CREATE OR REPLACE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); # pattern should not be found NOT FOUND /pwd_123/ in server_audit.log # pattern should not be found # cleaunup +DROP SERVER s1; DROP USER u1; DROP USER u2; set global server_audit_logging=off; UNINSTALL PLUGIN ed25519; UNINSTALL PLUGIN server_audit; +# end of 10.5 tests diff --git a/mysql-test/suite/plugins/t/server_audit_pwd_mask.test b/mysql-test/suite/plugins/t/server_audit_pwd_mask.test index af6425b2035..b393e22d11e 100644 --- a/mysql-test/suite/plugins/t/server_audit_pwd_mask.test +++ b/mysql-test/suite/plugins/t/server_audit_pwd_mask.test @@ -13,6 +13,10 @@ if (!$AUTH_ED25519_SO) { let $MYSQLD_DATADIR= `SELECT @@datadir`; let SEARCH_FILE= $MYSQLD_DATADIR/server_audit.log; +--echo # +--echo # MDEV-35507 and MDEV-35522 +--echo # + install plugin ed25519 soname 'auth_ed25519'; install plugin server_audit soname 'server_audit'; @@ -25,17 +29,24 @@ set global server_audit_logging=on; CREATE USER u1 IDENTIFIED BY 'pwd_123'; create user u2 IDENTIFIED VIA ed25519 USING PASSWORD('pwd_123'); +CREATE OR REPLACE USER u1 IDENTIFIED BY 'pwd_123'; SET PASSWORD FOR u1 = PASSWORD('pwd_123'); ALTER USER u1 IDENTIFIED BY 'pwd_123'; +ALTER USER if exists u1 IDENTIFIED BY 'pwd_123'; +SET STATEMENT max_statement_time=10 FOR ALTER USER u1 IDENTIFIED BY 'pwd_123'; alter user u2 identified VIA ed25519 USING password('pwd_123'); GRANT ALL ON test TO u1 IDENTIFIED BY "pwd_123"; GRANT ALL ON test TO u1 identified VIA ed25519 as password('pwd_123') or ed25519 using password('pwd_123'); +CREATE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); +CREATE OR REPLACE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); +CREATE OR REPLACE SERVER s1 FOREIGN DATA WRAPPER mariadb OPTIONS ( PASSWORD "pwd_123"); --let SEARCH_PATTERN=pwd_123 --echo # pattern should not be found --source include/search_pattern_in_file.inc --echo # pattern should not be found --echo # cleaunup +DROP SERVER s1; DROP USER u1; DROP USER u2; set global server_audit_logging=off; @@ -44,3 +55,5 @@ set global server_audit_logging=off; UNINSTALL PLUGIN ed25519; UNINSTALL PLUGIN server_audit; --enable_warnings + +--echo # end of 10.5 tests diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 2de27215ed9..0f1a53183a3 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -818,6 +818,7 @@ enum sa_keywords SQLCOM_TRUNCATE, SQLCOM_QUERY_ADMIN, SQLCOM_DCL, + SQLCOM_FOUND=-1, }; struct sa_keyword @@ -829,30 +830,87 @@ struct sa_keyword }; -struct sa_keyword xml_word= {3, "XML", 0, SQLCOM_NOTHING}; -struct sa_keyword user_word= {4, "USER", 0, SQLCOM_NOTHING}; -struct sa_keyword data_word= {4, "DATA", 0, SQLCOM_NOTHING}; -struct sa_keyword server_word= {6, "SERVER", 0, SQLCOM_NOTHING}; -struct sa_keyword master_word= {6, "MASTER", 0, SQLCOM_NOTHING}; -struct sa_keyword password_word= {8, "PASSWORD", 0, SQLCOM_NOTHING}; -struct sa_keyword function_word= {8, "FUNCTION", 0, SQLCOM_NOTHING}; -struct sa_keyword statement_word= {9, "STATEMENT", 0, SQLCOM_NOTHING}; -struct sa_keyword procedure_word= {9, "PROCEDURE", 0, SQLCOM_NOTHING}; +struct sa_keyword xml_word[]= +{ + {3, "XML", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword user_word[]= +{ + {4, "USER", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword data_word[]= +{ + {4, "DATA", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword server_word[]= +{ + {6, "SERVER", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword master_word[]= +{ + {6, "MASTER", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword password_word[]= +{ + {8, "PASSWORD", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword function_word[]= +{ + {8, "FUNCTION", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword statement_word[]= +{ + {9, "STATEMENT", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword procedure_word[]= +{ + {9, "PROCEDURE", 0, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword replace_user_word[]= +{ + {7, "REPLACE", user_word, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword or_replace_user_word[]= +{ + {2, "OR", replace_user_word, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword replace_server_word[]= +{ + {7, "REPLACE", server_word, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; +struct sa_keyword or_replace_server_word[]= +{ + {2, "OR", replace_server_word, SQLCOM_FOUND}, + {0, NULL, 0, SQLCOM_NOTHING} +}; struct sa_keyword keywords_to_skip[]= { - {3, "SET", &statement_word, SQLCOM_QUERY_ADMIN}, - {0, NULL, 0, SQLCOM_DDL} + {3, "SET", statement_word, SQLCOM_QUERY_ADMIN}, + {0, NULL, 0, SQLCOM_NOTHING} }; struct sa_keyword not_ddl_keywords[]= { - {4, "DROP", &user_word, SQLCOM_DCL}, - {6, "CREATE", &user_word, SQLCOM_DCL}, - {6, "RENAME", &user_word, SQLCOM_DCL}, - {0, NULL, 0, SQLCOM_DDL} + {4, "DROP", user_word, SQLCOM_DCL}, + {6, "CREATE", user_word, SQLCOM_DCL}, + {6, "CREATE", or_replace_user_word, SQLCOM_DCL}, + {6, "RENAME", user_word, SQLCOM_DCL}, + {0, NULL, 0, SQLCOM_NOTHING} }; @@ -863,7 +921,7 @@ struct sa_keyword ddl_keywords[]= {6, "CREATE", 0, SQLCOM_DDL}, {6, "RENAME", 0, SQLCOM_DDL}, {8, "TRUNCATE", 0, SQLCOM_DDL}, - {0, NULL, 0, SQLCOM_DDL} + {0, NULL, 0, SQLCOM_NOTHING} }; @@ -871,15 +929,15 @@ struct sa_keyword dml_keywords[]= { {2, "DO", 0, SQLCOM_DML}, {4, "CALL", 0, SQLCOM_DML}, - {4, "LOAD", &data_word, SQLCOM_DML}, - {4, "LOAD", &xml_word, SQLCOM_DML}, + {4, "LOAD", data_word, SQLCOM_DML}, + {4, "LOAD", xml_word, SQLCOM_DML}, {6, "DELETE", 0, SQLCOM_DML}, {6, "INSERT", 0, SQLCOM_DML}, {6, "SELECT", 0, SQLCOM_DML}, {6, "UPDATE", 0, SQLCOM_DML}, {7, "HANDLER", 0, SQLCOM_DML}, {7, "REPLACE", 0, SQLCOM_DML}, - {0, NULL, 0, SQLCOM_DML} + {0, NULL, 0, SQLCOM_NOTHING} }; @@ -887,38 +945,41 @@ struct sa_keyword dml_no_select_keywords[]= { {2, "DO", 0, SQLCOM_DML}, {4, "CALL", 0, SQLCOM_DML}, - {4, "LOAD", &data_word, SQLCOM_DML}, - {4, "LOAD", &xml_word, SQLCOM_DML}, + {4, "LOAD", data_word, SQLCOM_DML}, + {4, "LOAD", xml_word, SQLCOM_DML}, {6, "DELETE", 0, SQLCOM_DML}, {6, "INSERT", 0, SQLCOM_DML}, {6, "UPDATE", 0, SQLCOM_DML}, {7, "HANDLER", 0, SQLCOM_DML}, {7, "REPLACE", 0, SQLCOM_DML}, - {0, NULL, 0, SQLCOM_DML} + {0, NULL, 0, SQLCOM_NOTHING} }; struct sa_keyword dcl_keywords[]= { - {6, "CREATE", &user_word, SQLCOM_DCL}, - {4, "DROP", &user_word, SQLCOM_DCL}, - {6, "RENAME", &user_word, SQLCOM_DCL}, + {6, "CREATE", user_word, SQLCOM_DCL}, + {6, "CREATE", or_replace_user_word, SQLCOM_DCL}, + {4, "DROP", user_word, SQLCOM_DCL}, + {6, "RENAME", user_word, SQLCOM_DCL}, {5, "GRANT", 0, SQLCOM_DCL}, {6, "REVOKE", 0, SQLCOM_DCL}, - {3, "SET", &password_word, SQLCOM_DCL}, - {0, NULL, 0, SQLCOM_DDL} + {3, "SET", password_word, SQLCOM_DCL}, + {0, NULL, 0, SQLCOM_NOTHING} }; struct sa_keyword passwd_keywords[]= { - {3, "SET", &password_word, SQLCOM_SET_OPTION}, - {5, "ALTER", &server_word, SQLCOM_ALTER_SERVER}, - {5, "ALTER", &user_word, SQLCOM_ALTER_USER}, + {3, "SET", password_word, SQLCOM_SET_OPTION}, + {5, "ALTER", server_word, SQLCOM_ALTER_SERVER}, + {5, "ALTER", user_word, SQLCOM_ALTER_USER}, {5, "GRANT", 0, SQLCOM_GRANT}, - {6, "CREATE", &user_word, SQLCOM_CREATE_USER}, - {6, "CREATE", &server_word, SQLCOM_CREATE_SERVER}, - {6, "CHANGE", &master_word, SQLCOM_CHANGE_MASTER}, + {6, "CREATE", user_word, SQLCOM_CREATE_USER}, + {6, "CREATE", or_replace_user_word, SQLCOM_CREATE_USER}, + {6, "CREATE", server_word, SQLCOM_CREATE_SERVER}, + {6, "CREATE", or_replace_server_word, SQLCOM_CREATE_SERVER}, + {6, "CHANGE", master_word, SQLCOM_CHANGE_MASTER}, {0, NULL, 0, SQLCOM_NOTHING} }; @@ -1732,7 +1793,7 @@ static int filter_query_type(const char *query, struct sa_keyword *kwd) query++; } - qwe_in_list= 0; + qwe_in_list= SQLCOM_NOTHING; if (!(len= get_next_word(query, fword))) goto not_in_list; query+= len+1; @@ -1750,8 +1811,7 @@ static int filter_query_type(const char *query, struct sa_keyword *kwd) query++; nlen= get_next_word(query, nword); } - if (l_keywords->next->length != nlen || - strncmp(l_keywords->next->wd, nword, nlen) != 0) + if (filter_query_type(query, l_keywords->next) == SQLCOM_NOTHING) goto do_loop; } @@ -1766,6 +1826,25 @@ not_in_list: return qwe_in_list; } +static const char *skip_set_statement(const char *query) +{ + if (filter_query_type(query, keywords_to_skip)) + { + char fword[MAX_KEYWORD + 1]; + int len; + do + { + len= get_next_word(query, fword); + query+= len ? len : 1; + if (len == 3 && strncmp(fword, "FOR", 3) == 0) + break; + } while (*query); + + if (*query == 0) + return 0; + } + return query; +} static int log_statement_ex(const struct connection_info *cn, time_t ev_time, unsigned long thd_id, @@ -1809,21 +1888,8 @@ static int log_statement_ex(const struct connection_info *cn, { const char *orig_query= query; - if (filter_query_type(query, keywords_to_skip)) - { - char fword[MAX_KEYWORD + 1]; - int len; - do - { - len= get_next_word(query, fword); - query+= len ? len : 1; - if (len == 3 && strncmp(fword, "FOR", 3) == 0) - break; - } while (*query); - - if (*query == 0) - return 0; - } + if ((query= skip_set_statement(query)) == SQLCOM_NOTHING) + return 0; if (events & EVENT_QUERY_DDL) { @@ -1879,7 +1945,7 @@ do_log_query: if (query_log_limit > 0 && uh_buffer_size > query_log_limit+2) uh_buffer_size= query_log_limit+2; - switch (filter_query_type(query, passwd_keywords)) + switch (filter_query_type(skip_set_statement(query), passwd_keywords)) { case SQLCOM_GRANT: case SQLCOM_CREATE_USER: From d0fcac44501b93593219ef2210032d06458d5410 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 29 Nov 2024 11:12:10 +1100 Subject: [PATCH 08/33] MDEV-35422 Fix spider group by handler trying to use fake group by fields This is a fixup of MDEV-26345 commit 77ed235d50bd9b1480f26d18ea0b70ca7480af23. In MDEV-26345 the spider group by handler was updated so that it uses the item_ptr fields of Query::group_by and Query::order_by, instead of item. This was and is because the call to join->set_items_ref_array(join->items1) during the execution stage, just before the execution replaces the order-by / group-by item arrays with Item_temptable_field. Spider traverses the item tree during the group by handler (gbh) creation at the end of the optimization stage, and decides a gbh could handle the execution of the query. Basically spider gbh can handle the execution if it can construct a well-formed query, executes on the data node, and store the results in the correct places. If so, it will create one, otherwise it will return NULL and the execution will use the usual handler (ha_spider instead of spider_group_by_handler). To that end, the general principle is the items checked for creation should be the same items later used for query construciton. Since in MDEV-26345 we changed to use the item_ptr field instead of item field of order-by and group-by in query construction, in this patch we do the same for the gbh creation. The item_ptr field could be the uninitialised NULL value during the gbh creation. This is because the optimizer may replace a DISTINCT with a GROUP BY, which only happens if the original GROUP BY is empty. It creates the artificial GROUP BY by calling create_distinct_group(), which creates the corresponding ORDER object with item field aligning with somewhere in ref_pointer_array, but leaving item_ptr to be NULL. When spider finds out that item_ptr is NULL, it knows there's some optimizer skullduggery and it is passed a query different from the original. Without a clear contract between the server layer and the gbh, it is better to be safe than sorry and not create the gbh in this case. Also add a check and error reporting for the unlikely case of item_ptr changing from non-NULL at gbh construction to NULL at execution to prevent server crash. Also, we remove a check added in MDEV-29480 of order by items being aggregate functions. That check was added with the premise that spider was including auxiliary SELECT items which is referenced by ORDER BY items. This premise was no longer true since MDEV-26345, and caused problems such as MDEV-29546, which was fixed by MDEV-26345. --- .../spider/bugfix/r/mdev_34659.result | 7 ++++++ .../spider/bugfix/r/mdev_35422.result | 24 +++++++++++++++++++ .../spider/bugfix/t/mdev_34659.test | 2 ++ .../spider/bugfix/t/mdev_35422.test | 21 ++++++++++++++++ storage/spider/spd_db_mysql.cc | 24 +++++++++++++++++++ storage/spider/spd_group_by_handler.cc | 14 ++++++----- 6 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_35422.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_35422.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_34659.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_34659.result index e77939396b0..e0f8b25d3c1 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_34659.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_34659.result @@ -16,6 +16,13 @@ SELECT * FROM t2 ORDER BY CAST(c AS INET6); c 456 123 +SELECT * FROM t2 GROUP BY CAST(c AS char(60)); +c +123 +456 +SELECT * FROM t2 GROUP BY CAST(c AS INET6); +c +456 DROP TABLE t1,t2; drop server srv; for master_1 diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_35422.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_35422.result new file mode 100644 index 00000000000..1d19fe84cac --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_35422.result @@ -0,0 +1,24 @@ +for master_1 +for child2 +for child3 +set spider_same_server_link= 1; +CREATE SERVER srv FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); +create table t2 (c varchar(10)); +create table t1 (c varchar(10)) ENGINE=Spider +COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"'; +insert into t1 values ('abc'), ('abd'), ('abcd'), ('abc'); +SELECT DISTINCT c FROM t1; +c +abc +abd +abcd +SELECT DISTINCT c FROM t1 WHERE (c LIKE 'abc%'); +c +abc +abcd +drop table t1, t2; +drop server srv; +for master_1 +for child2 +for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_34659.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_34659.test index 3b481d2277f..3b1dc206866 100644 --- a/storage/spider/mysql-test/spider/bugfix/t/mdev_34659.test +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_34659.test @@ -13,6 +13,8 @@ CREATE TABLE t2 (c INT) ENGINE=Spider COMMENT='WRAPPER "mysql",SRV "srv",TABLE " insert into t2 values (456), (123); SELECT * FROM t2 ORDER BY CAST(c AS char(60)); SELECT * FROM t2 ORDER BY CAST(c AS INET6); +SELECT * FROM t2 GROUP BY CAST(c AS char(60)); +SELECT * FROM t2 GROUP BY CAST(c AS INET6); # Cleanup DROP TABLE t1,t2; drop server srv; diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_35422.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_35422.test new file mode 100644 index 00000000000..dc89bc88edc --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_35422.test @@ -0,0 +1,21 @@ +--disable_query_log +--disable_result_log +--source ../../t/test_init.inc +--enable_result_log +--enable_query_log +set spider_same_server_link= 1; +evalp CREATE SERVER srv FOREIGN DATA WRAPPER mysql +OPTIONS (SOCKET "$MASTER_1_MYSOCK", DATABASE 'test',user 'root'); +create table t2 (c varchar(10)); +create table t1 (c varchar(10)) ENGINE=Spider +COMMENT='WRAPPER "mysql", srv "srv",TABLE "t2"'; +insert into t1 values ('abc'), ('abd'), ('abcd'), ('abc'); +SELECT DISTINCT c FROM t1; +SELECT DISTINCT c FROM t1 WHERE (c LIKE 'abc%'); +drop table t1, t2; +drop server srv; +--disable_query_log +--disable_result_log +--source ../../t/test_deinit.inc +--enable_result_log +--enable_query_log diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index 4b232c18166..5fd215de341 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -14066,6 +14066,11 @@ int spider_mbase_handler::append_group_by_part( DBUG_RETURN(error_num); } +/* + Append the GROUP BY part. + + Only used by the group by handler for query construction. +*/ int spider_mbase_handler::append_group_by( ORDER *order, spider_string *str, @@ -14084,6 +14089,13 @@ int spider_mbase_handler::append_group_by( str->q_append(SPIDER_SQL_GROUP_STR, SPIDER_SQL_GROUP_LEN); for (; order; order = order->next) { + /* + This is not expected to happen, as NULL check was performed + at the creation of the group by handler, and any NULL item_ptr + would have resulted in the gbh not being created. + */ + if (!order->item_ptr) + DBUG_RETURN(ER_INTERNAL_ERROR); if ((error_num = spider_db_print_item_type(order->item_ptr, NULL, spider, str, alias, alias_length, dbton_id, use_fields, fields))) { @@ -14123,6 +14135,11 @@ int spider_mbase_handler::append_order_by_part( DBUG_RETURN(error_num); } +/* + Append the ORDER BY part. + + Only used by the group by handler for query construction. +*/ int spider_mbase_handler::append_order_by( ORDER *order, spider_string *str, @@ -14141,6 +14158,13 @@ int spider_mbase_handler::append_order_by( str->q_append(SPIDER_SQL_ORDER_STR, SPIDER_SQL_ORDER_LEN); for (; order; order = order->next) { + /* + This is not expected to happen, as NULL check was performed + at the creation of the group by handler, and any NULL item_ptr + would have resulted in the gbh not being created. + */ + if (!order->item_ptr) + DBUG_RETURN(ER_INTERNAL_ERROR); if ((error_num = spider_db_print_item_type(order->item_ptr, NULL, spider, str, alias, alias_length, dbton_id, use_fields, fields))) { diff --git a/storage/spider/spd_group_by_handler.cc b/storage/spider/spd_group_by_handler.cc index e90129e6113..0c62e6cf7eb 100644 --- a/storage/spider/spd_group_by_handler.cc +++ b/storage/spider/spd_group_by_handler.cc @@ -1622,8 +1622,10 @@ group_by_handler *spider_create_group_by_handler( { for (order = query->group_by; order; order = order->next) { - if (spider_db_print_item_type((*order->item), NULL, spider, NULL, NULL, 0, - roop_count, TRUE, fields_arg)) + if (order->item_ptr == NULL || + spider_db_print_item_type(order->item_ptr, NULL, spider, + NULL, NULL, 0, roop_count, TRUE, + fields_arg)) { DBUG_PRINT("info",("spider dbton_id=%d can't create group by", roop_count)); spider_clear_bit(dbton_bitmap, roop_count); @@ -1640,10 +1642,10 @@ group_by_handler *spider_create_group_by_handler( { for (order = query->order_by; order; order = order->next) { - if ((*order->item)->type() == Item::SUM_FUNC_ITEM) - continue; - if (spider_db_print_item_type((*order->item), NULL, spider, NULL, NULL, 0, - roop_count, TRUE, fields_arg)) + if (order->item_ptr == NULL || + spider_db_print_item_type(order->item_ptr, NULL, spider, + NULL, NULL, 0, roop_count, TRUE, + fields_arg)) { DBUG_PRINT("info",("spider dbton_id=%d can't create order by", roop_count)); spider_clear_bit(dbton_bitmap, roop_count); From bf3e16eb817cf16e8a9d4a606b54930b67eccd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 21 Nov 2024 12:23:31 +0200 Subject: [PATCH 09/33] MDEV-35467 : WSREP: read_completion_condition(): shutdown while in init () Ignore expected warning from Galera library when connections are shutdown. Signed-off-by: Julius Goryavsky --- mysql-test/suite/galera/r/galera_wan.result | 7 +++++++ mysql-test/suite/galera/t/galera_wan.test | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/mysql-test/suite/galera/r/galera_wan.result b/mysql-test/suite/galera/r/galera_wan.result index 21cd5258590..3904c1dc517 100644 --- a/mysql-test/suite/galera/r/galera_wan.result +++ b/mysql-test/suite/galera/r/galera_wan.result @@ -35,3 +35,10 @@ SELECT VARIABLE_VALUE LIKE '%gmcast.segment = 3%' FROM INFORMATION_SCHEMA.GLOBAL VARIABLE_VALUE LIKE '%gmcast.segment = 3%' 1 DROP TABLE t1; +connection node_1; +call mtr.add_suppression("WSREP: read_completion_condition.*"); +call mtr.add_suppression("WSREP: read_handler.*"); +disconnect node_3; +disconnect node_4; +disconnect node_2; +disconnect node_1; diff --git a/mysql-test/suite/galera/t/galera_wan.test b/mysql-test/suite/galera/t/galera_wan.test index 73be33fa33d..ab2346ebe94 100644 --- a/mysql-test/suite/galera/t/galera_wan.test +++ b/mysql-test/suite/galera/t/galera_wan.test @@ -40,3 +40,12 @@ SELECT COUNT(*) AS EXPECT_1 FROM t1; SELECT VARIABLE_VALUE LIKE '%gmcast.segment = 3%' FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME = 'wsrep_provider_options'; DROP TABLE t1; + +--connection node_1 +call mtr.add_suppression("WSREP: read_completion_condition.*"); +call mtr.add_suppression("WSREP: read_handler.*"); + +--disconnect node_3 +--disconnect node_4 + +--source include/galera_end.inc From af50783fcd3b8c9b6278dfbcee3d1ecfcbbb8ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 21 Nov 2024 13:22:49 +0200 Subject: [PATCH 10/33] MDEV-35471 : Sporadic failures in the galera_pc_recovery mtr test For some reason InnoDB was disabled at --wsrep-recover call. Added --loose-innodb to command like to make sure InnoDB is enabled. Signed-off-by: Julius Goryavsky --- mysql-test/suite/galera/disabled.def | 1 - mysql-test/suite/galera/t/galera_pc_recovery.test | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 759fc38be74..16aaf89259f 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -10,7 +10,6 @@ # ############################################################################## -galera_pc_recovery : MDEV-25199 cluster fails to start up galera_sequences : MDEV-32561 WSREP FSM failure: no such a transition REPLICATING -> COMMITTED galera_concurrent_ctas : MDEV-32779 galera_concurrent_ctas: assertion in the galera::ReplicatorSMM::finish_cert() galera_as_slave_replay : MDEV-32780 galera_as_slave_replay: assertion in the wsrep::transaction::before_rollback() diff --git a/mysql-test/suite/galera/t/galera_pc_recovery.test b/mysql-test/suite/galera/t/galera_pc_recovery.test index 0fd9c8ab3d9..4489db63a91 100644 --- a/mysql-test/suite/galera/t/galera_pc_recovery.test +++ b/mysql-test/suite/galera/t/galera_pc_recovery.test @@ -33,8 +33,8 @@ SELECT COUNT(*) = 1 FROM t1; # Perform --wsrep-recover and preserve the positions into variables by placing them in $MYSQL_TMP_DIR/galera_wsrep_start_position.inc and then --source'ing it ---exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.1.log > $MYSQL_TMP_DIR/galera_wsrep_recover.1.log 2>&1 ---exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.2.log > $MYSQL_TMP_DIR/galera_wsrep_recover.2.log 2>&1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --loose-innodb --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.1.log > $MYSQL_TMP_DIR/galera_wsrep_recover.1.log 2>&1 +--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --loose-innodb --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.2.log > $MYSQL_TMP_DIR/galera_wsrep_recover.2.log 2>&1 --perl use strict; From f219fb84893e9837ce09a8811929650347aea4f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 7 Nov 2024 09:04:16 +0200 Subject: [PATCH 11/33] MDEV-35355 : Galera test failure on galera_sr.mysql-wsrep-features#165 Problem was that in DeadlockChecker::trx_rollback() we hold lock_sys before we call wsrep_handle_SR_rollback() where THD::LOCK_thd_data (and some cases THD::LOCK_thd_kill) are acquired. This is against current mutex ordering rules. However, acquiring THD::LOCK_thd_data is not necessary because we always are in victim_thd context, either client session is rolling back or rollbacker thread should be in control. Therefore, we should always use wsrep_thd_self_abort() and then no additional mutexes are required. Fixed by removing locking of THD::LOCK_thd_data and using only wsrep_thd_self_abort(). In debug builds added assertions to verify that we are always in victim_thd context. This fix is for MariaDB 10.5 and we already have a test case that sporadically fail in Jenkins before this fix. Signed-off-by: Julius Goryavsky --- sql/service_wsrep.cc | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index c131e3c7fb5..6aab1bec99b 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -186,42 +186,29 @@ extern "C" my_bool wsrep_thd_is_SR(const THD *thd) return thd && thd->wsrep_cs().transaction().is_streaming(); } -extern "C" void wsrep_handle_SR_rollback(THD *bf_thd, +extern "C" void wsrep_handle_SR_rollback(THD *bf_thd __attribute__((unused)), THD *victim_thd) { + /* + We should always be in victim_thd context, either client session is + rolling back or rollbacker thread should be in control. + */ DBUG_ASSERT(victim_thd); + DBUG_ASSERT(current_thd == victim_thd); DBUG_ASSERT(wsrep_thd_is_SR(victim_thd)); - if (!victim_thd || !wsrep_on(bf_thd)) return; - wsrep_thd_LOCK(victim_thd); + /* Defensive measure to avoid crash in production. */ + if (!victim_thd) return; - WSREP_DEBUG("handle rollback, for deadlock: thd %llu trx_id %" PRIu64 " frags %zu conf %s", + WSREP_DEBUG("Handle SR rollback, for deadlock: thd %llu trx_id %" PRIu64 " frags %zu conf %s", victim_thd->thread_id, victim_thd->wsrep_trx_id(), victim_thd->wsrep_sr().fragments_certified(), wsrep_thd_transaction_state_str(victim_thd)); - /* Note: do not store/reset globals before wsrep_bf_abort() call - to avoid losing BF thd context. */ - if (!(bf_thd && bf_thd != victim_thd)) - { - DEBUG_SYNC(victim_thd, "wsrep_before_SR_rollback"); - } - if (bf_thd) - { - wsrep_bf_abort(bf_thd, victim_thd); - } - else - { - wsrep_thd_self_abort(victim_thd); - } + DEBUG_SYNC(victim_thd, "wsrep_before_SR_rollback"); - wsrep_thd_UNLOCK(victim_thd); - - if (bf_thd) - { - wsrep_store_threadvars(bf_thd); - } + wsrep_thd_self_abort(victim_thd); } extern "C" my_bool wsrep_thd_bf_abort(THD *bf_thd, THD *victim_thd, From 8a32ae5d6dfc9b6f7c97fe76fcaf1f2d6f510168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Tue, 5 Nov 2024 09:24:09 +0200 Subject: [PATCH 12/33] MDEV-32779 : galera_concurrent_ctas: assertion in the galera::ReplicatorSMM::finish_cert() I could not reproduce reported assertion. However, I could reporoduce test failure because missing wait_condition and error in test case. Added missing wait_condition and fixed error in test case to make test stable. Signed-off-by: Julius Goryavsky --- .../galera/r/galera_concurrent_ctas.result | 17 +++++++++++++++++ .../t/galera_concurrent_ctas.combinations | 4 ++++ .../suite/galera/t/galera_concurrent_ctas.test | 17 ++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera/t/galera_concurrent_ctas.combinations diff --git a/mysql-test/suite/galera/r/galera_concurrent_ctas.result b/mysql-test/suite/galera/r/galera_concurrent_ctas.result index 933f1d2a98e..a25e367bf74 100644 --- a/mysql-test/suite/galera/r/galera_concurrent_ctas.result +++ b/mysql-test/suite/galera/r/galera_concurrent_ctas.result @@ -3,6 +3,7 @@ connection node_1; connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1; connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connect node_2b, 127.0.0.1, root, , test, $NODE_MYPORT_2; connection node_1; SET DEBUG_SYNC = 'wsrep_create_table_as_select WAIT_FOR continue'; CREATE table t1 as SELECT SLEEP(0.1);; @@ -17,14 +18,30 @@ connection node_1b; SET SESSION debug_sync = 'now WAIT_FOR sync.wsrep_apply_cb_reached'; # Signal first CTAS to continue and wait until CTAS has executed SET DEBUG_SYNC= 'now SIGNAL continue'; +connection node_2b; +# Wait first CTAS to replicate +SELECT * FROM t1; +SLEEP(0.2) +0 +connection node_1b; SET GLOBAL debug_dbug= ''; SET DEBUG_SYNC = 'now SIGNAL signal.wsrep_apply_cb'; connection node_2a; connection node_1b; SET DEBUG_SYNC= 'RESET'; connection node_2; +SELECT * FROM t1; +SLEEP(0.2) +0 connection node_1; +SELECT * FROM t1; +SLEEP(0.2) +0 DROP TABLE t1; +disconnect node_1a; +disconnect node_1b; +disconnect node_2a; +disconnect node_2b; disconnect node_2; disconnect node_1; # End of test diff --git a/mysql-test/suite/galera/t/galera_concurrent_ctas.combinations b/mysql-test/suite/galera/t/galera_concurrent_ctas.combinations new file mode 100644 index 00000000000..1eeb8fb4614 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_concurrent_ctas.combinations @@ -0,0 +1,4 @@ +[binlogoff] + +[binlogon] +log-bin diff --git a/mysql-test/suite/galera/t/galera_concurrent_ctas.test b/mysql-test/suite/galera/t/galera_concurrent_ctas.test index e22ac811c8b..ed579f19a0d 100644 --- a/mysql-test/suite/galera/t/galera_concurrent_ctas.test +++ b/mysql-test/suite/galera/t/galera_concurrent_ctas.test @@ -9,6 +9,7 @@ --connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 --connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1 --connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 +--connect node_2b, 127.0.0.1, root, , test, $NODE_MYPORT_2 --connection node_1 # @@ -48,6 +49,14 @@ SET SESSION debug_sync = 'now WAIT_FOR sync.wsrep_apply_cb_reached'; SET DEBUG_SYNC= 'now SIGNAL continue'; --let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE STATE = 'Creating table CREATE table t1 as SELECT SLEEP(0.1)' --source include/wait_condition.inc + +--connection node_2b +--echo # Wait first CTAS to replicate +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1' +--source include/wait_condition.inc +SELECT * FROM t1; + +--connection node_1b # # Release second CTAS and cleanup # @@ -74,12 +83,18 @@ SET DEBUG_SYNC= 'RESET'; # --connection node_2 --reap +SELECT * FROM t1; --connection node_1 ---error 0,ER_TABLE_EXISTS_ERROR,ER_QUERY_INTERRUPTED +--error 0,ER_QUERY_INTERRUPTED,ER_LOCK_DEADLOCK --reap +SELECT * FROM t1; DROP TABLE t1; +--disconnect node_1a +--disconnect node_1b +--disconnect node_2a +--disconnect node_2b --source include/galera_end.inc --echo # End of test From c20f09ddcfc5136664b71ddab37f5a1496cf54cf Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:42 +0300 Subject: [PATCH 13/33] Dtrace cmake fix for clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there are GCC-incompatible compiler flags dtrace fails like this: gcc: error: unrecognized command-line option ‘-fno-limit-debug-info’ gcc: error: unrecognized command-line option ‘-mbranches-within-32B-boundaries’ "gcc .dtrace-temp.3fd6bacf.c" failed Usage /usr/bin/dtrace [--help] [-h | -G] [-C [-I]] -s File.d [-o ] --- cmake/dtrace.cmake | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake index dae3b56b584..9d3d3cd50c5 100644 --- a/cmake/dtrace.cmake +++ b/cmake/dtrace.cmake @@ -125,16 +125,20 @@ FUNCTION(DTRACE_INSTRUMENT target) WORKING_DIRECTORY ${objdir} ) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux") - # dtrace on Linux runs gcc and uses flags from environment - SET(CFLAGS_SAVED $ENV{CFLAGS}) - SET(ENV{CFLAGS} ${CMAKE_C_FLAGS}) + IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # dtrace on Linux runs gcc and uses flags from environment + SET(CFLAGS_SAVED $ENV{CFLAGS}) + SET(ENV{CFLAGS} ${CMAKE_C_FLAGS}) + ENDIF() SET(outfile "${CMAKE_BINARY_DIR}/probes_mysql.o") # Systemtap object EXECUTE_PROCESS( COMMAND ${DTRACE} -G -s ${CMAKE_SOURCE_DIR}/include/probes_mysql.d.base -o ${outfile} ) - SET(ENV{CFLAGS} ${CFLAGS_SAVED}) + IF (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + SET(ENV{CFLAGS} ${CFLAGS_SAVED}) + ENDIF() ENDIF() # Do not try to extend the library if we have not built the .o file From 3835437eb8155740040340c214dbdd0979951b37 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:42 +0300 Subject: [PATCH 14/33] MDEV-15330 Cleanup: load_data.test removed --- .../suite/versioning/r/load_data.result | 10 ------- mysql-test/suite/versioning/r/replace.result | 11 ++++++++ mysql-test/suite/versioning/t/load_data.test | 16 ----------- mysql-test/suite/versioning/t/replace.test | 28 +++++++++++++++++++ 4 files changed, 39 insertions(+), 26 deletions(-) delete mode 100644 mysql-test/suite/versioning/r/load_data.result delete mode 100644 mysql-test/suite/versioning/t/load_data.test diff --git a/mysql-test/suite/versioning/r/load_data.result b/mysql-test/suite/versioning/r/load_data.result deleted file mode 100644 index 1fcde73e565..00000000000 --- a/mysql-test/suite/versioning/r/load_data.result +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE t1 (a INT, b INT, c INT, vc INT AS (c), UNIQUE(a), UNIQUE(b)) WITH SYSTEM VERSIONING; -INSERT IGNORE INTO t1 (a,b,c) VALUES (1,2,3); -SELECT a, b, c FROM t1 INTO OUTFILE '15330.data'; -Warnings: -Warning 1287 ' INTO FROM...' instead -LOAD DATA INFILE '15330.data' IGNORE INTO TABLE t1 (a,b,c); -Warnings: -Warning 1062 Duplicate entry '1' for key 'a' -LOAD DATA INFILE '15330.data' REPLACE INTO TABLE t1 (a,b,c); -DROP TABLE t1; diff --git a/mysql-test/suite/versioning/r/replace.result b/mysql-test/suite/versioning/r/replace.result index 57a992cce49..8ac4047c5ff 100644 --- a/mysql-test/suite/versioning/r/replace.result +++ b/mysql-test/suite/versioning/r/replace.result @@ -61,3 +61,14 @@ connection con1; replace into t1 values (1),(2); ERROR 23000: Duplicate entry '1' for key 'PRIMARY' drop table t1; +# +# MDEV-15330 Server crash or assertion `table->insert_values' failure in write_record upon LOAD DATA +# +create table t1 (a int, b int, c int, vc int as (c), unique(a), unique(b)) with system versioning; +insert ignore into t1 (a,b,c) values (1,2,3); +select a, b, c into outfile '15330.data' from t1; +load data infile '15330.data' ignore into table t1 (a,b,c); +Warnings: +Warning 1062 Duplicate entry '1' for key 'a' +load data infile '15330.data' replace into table t1 (a,b,c); +drop table t1; diff --git a/mysql-test/suite/versioning/t/load_data.test b/mysql-test/suite/versioning/t/load_data.test deleted file mode 100644 index aa4e2192113..00000000000 --- a/mysql-test/suite/versioning/t/load_data.test +++ /dev/null @@ -1,16 +0,0 @@ -# -# MDEV-15330 Server crash or assertion `table->insert_values' failure in write_record upon LOAD DATA -# -CREATE TABLE t1 (a INT, b INT, c INT, vc INT AS (c), UNIQUE(a), UNIQUE(b)) WITH SYSTEM VERSIONING; -INSERT IGNORE INTO t1 (a,b,c) VALUES (1,2,3); - ---disable_cursor_protocol ---disable_ps2_protocol -SELECT a, b, c FROM t1 INTO OUTFILE '15330.data'; ---enable_ps2_protocol ---enable_cursor_protocol -LOAD DATA INFILE '15330.data' IGNORE INTO TABLE t1 (a,b,c); -LOAD DATA INFILE '15330.data' REPLACE INTO TABLE t1 (a,b,c); - -# Cleanup -DROP TABLE t1; diff --git a/mysql-test/suite/versioning/t/replace.test b/mysql-test/suite/versioning/t/replace.test index 83489f4a4b9..d69eebd1b9c 100644 --- a/mysql-test/suite/versioning/t/replace.test +++ b/mysql-test/suite/versioning/t/replace.test @@ -77,4 +77,32 @@ replace into t1 values (1),(2); drop table t1; +--echo # +--echo # MDEV-15330 Server crash or assertion `table->insert_values' failure in write_record upon LOAD DATA +--echo # +if ($default_engine == MEMORY) +{ + --disable_query_log + set default_storage_engine= myisam; + --enable_query_log +} +create table t1 (a int, b int, c int, vc int as (c), unique(a), unique(b)) with system versioning; +insert ignore into t1 (a,b,c) values (1,2,3); + +--disable_cursor_protocol +--disable_ps2_protocol +select a, b, c into outfile '15330.data' from t1; +--enable_ps2_protocol +--enable_cursor_protocol +load data infile '15330.data' ignore into table t1 (a,b,c); +load data infile '15330.data' replace into table t1 (a,b,c); +--let $datadir=`select @@datadir` +--remove_file $datadir/test/15330.data + +# cleanup +drop table t1; +--disable_query_log +eval set default_storage_engine= $default_engine; +--enable_query_log + --source suite/versioning/common_finish.inc From 55b599320503659fb2c5671bc894f66954c4beea Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:42 +0300 Subject: [PATCH 15/33] Cleanup: make_keypart_map inline for easier debugging. --- sql/handler.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index fc3d00d43d5..ad1cbaff5b7 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2980,12 +2980,19 @@ uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map); bitmap with first N+1 bits set (keypart_map for a key prefix of [0..N] keyparts) */ -#define make_keypart_map(N) (((key_part_map)2 << (N)) - 1) +inline key_part_map make_keypart_map(uint N) +{ + return ((key_part_map)2 << (N)) - 1; +} + /* bitmap with first N bits set (keypart_map for a key prefix of [0..N-1] keyparts) */ -#define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1) +inline key_part_map make_prev_keypart_map(uint N) +{ + return ((key_part_map)1 << (N)) - 1; +} /** Base class to be used by handlers different shares */ From 27c25ceedbde07649d282fe0859defc7f724473c Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:42 +0300 Subject: [PATCH 16/33] MDEV-31030 Assertion `!error' failed in ha_partition::update_row on UPDATE Removing wrong assertion as division by zero was caused by valid input. --- mysql-test/main/partition.result | 20 ++++++++++++++++++++ mysql-test/main/partition.test | 16 ++++++++++++++++ sql/ha_partition.cc | 5 ++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/partition.result b/mysql-test/main/partition.result index e18273f4fe9..8cf64927293 100644 --- a/mysql-test/main/partition.result +++ b/mysql-test/main/partition.result @@ -2857,3 +2857,23 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY t3 ref_or_null a1,b a1 10 func,test.t2.a 198 Using where; Full scan on NULL key set optimizer_switch=@tmp_os; drop table t1,t2,t3; +# +# MDEV-31030 Assertion `!error' failed in ha_partition::update_row on UPDATE +# +create table t (c int) +partition by list (1 div c) ( +partition p0 values in (null), +partition p values in (1)); +insert ignore into t values (0), (1), (0); +Warnings: +Warning 1365 Division by 0 +Warning 1365 Division by 0 +update t set c= 2; +ERROR HY000: Table has no partition for value 0 +update ignore t set c= 3; +select * from t; +c +0 +0 +1 +drop table t; diff --git a/mysql-test/main/partition.test b/mysql-test/main/partition.test index b3ea7f5c274..b9e055783ca 100644 --- a/mysql-test/main/partition.test +++ b/mysql-test/main/partition.test @@ -3068,3 +3068,19 @@ set optimizer_switch=@tmp_os; drop table t1,t2,t3; +--echo # +--echo # MDEV-31030 Assertion `!error' failed in ha_partition::update_row on UPDATE +--echo # + +create table t (c int) +partition by list (1 div c) ( + partition p0 values in (null), + partition p values in (1)); +insert ignore into t values (0), (1), (0); +--error ER_NO_PARTITION_FOR_GIVEN_VALUE +update t set c= 2; +--disable_warnings +update ignore t set c= 3; +--enable_warnings +select * from t; +drop table t; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index ed7fb7fe5f9..2a06ca4300e 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4591,7 +4591,10 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data) Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol, so this is not supported for this engine. */ - error= get_part_for_buf(old_data, m_rec0, m_part_info, &old_part_id); + { + Abort_on_warning_instant_set old_abort_on_warning(thd, 0); + error= get_part_for_buf(old_data, m_rec0, m_part_info, &old_part_id); + } DBUG_ASSERT(!error); DBUG_ASSERT(old_part_id == m_last_part); DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), old_part_id)); From 13f93da1f60f6f51ebe9a1f3e3fcfd6caa54c07f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:43 +0300 Subject: [PATCH 17/33] MDEV-33783 CREATE SERVER segfaults on wrong mysql.servers Do basic checking of mysql.servers compatibility. --- mysql-test/main/servers.result | 14 +++++++ mysql-test/main/servers.test | 15 +++++++ sql/sql_servers.cc | 77 ++++++++++++++++++++++------------ 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/mysql-test/main/servers.result b/mysql-test/main/servers.result index 585f0f62af0..c466967a0db 100644 --- a/mysql-test/main/servers.result +++ b/mysql-test/main/servers.result @@ -24,3 +24,17 @@ SELECT * FROM mysql.servers; Server_name Host Db Username Password Port Socket Wrapper Owner s1 3306 bar mysql DROP SERVER s1; +# +# MDEV-33783 CREATE SERVER segfaults on wrong mysql.servers +# +create server s1 foreign data wrapper foo options(user 'a'); +alter server s1 options(host 'server.example.org'); +rename table mysql.servers to mysql.servers_save; +create table mysql.servers (x int); +alter server s1 options(host 'server.example.org'); +ERROR HY000: The foreign server name you are trying to reference does not exist. Data source error: s1 +create server s2 foreign data wrapper foo options(user 'a'); +ERROR HY000: Can't read record in system table +drop table mysql.servers; +rename table mysql.servers_save to mysql.servers; +drop server s1; diff --git a/mysql-test/main/servers.test b/mysql-test/main/servers.test index 645206c376e..6aaaa356c1d 100644 --- a/mysql-test/main/servers.test +++ b/mysql-test/main/servers.test @@ -22,3 +22,18 @@ DROP SERVER s1; CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS(SOCKET 'bar'); SELECT * FROM mysql.servers; DROP SERVER s1; + +--echo # +--echo # MDEV-33783 CREATE SERVER segfaults on wrong mysql.servers +--echo # +create server s1 foreign data wrapper foo options(user 'a'); +alter server s1 options(host 'server.example.org'); +rename table mysql.servers to mysql.servers_save; +create table mysql.servers (x int); +--error ER_FOREIGN_SERVER_DOESNT_EXIST +alter server s1 options(host 'server.example.org'); +--error ER_CANT_FIND_SYSTEM_REC +create server s2 foreign data wrapper foo options(user 'a'); +drop table mysql.servers; +rename table mysql.servers_save to mysql.servers; +drop server s1; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 8350b9dba9b..59a4679847b 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -462,7 +462,7 @@ get_server_from_table_to_cache(TABLE *table) RETURN VALUES 0 - no error - other - error code + other - ER_ error code */ static int @@ -546,15 +546,19 @@ insert_server_record_into_cache(FOREIGN_SERVER *server) advance of insertion into the mysql.servers table RETURN VALUE - VOID - + 0 - no errors + >0 - ER_ error code */ -static void +static int store_server_fields(TABLE *table, FOREIGN_SERVER *server) { table->use_all_columns(); + + if (table->s->fields < 9) + return ER_CANT_FIND_SYSTEM_REC; + /* "server" has already been prepped by prepare_server_struct_for_<> so, all we need to do is check if the value is set (> -1 for port) @@ -563,30 +567,43 @@ store_server_fields(TABLE *table, FOREIGN_SERVER *server) have changed will be set. If an insert, then all will be set, even if with empty strings */ - if (server->host) + if (server->host && table->field[1]->store(server->host, - (uint) strlen(server->host), system_charset_info); - if (server->db) + (uint) strlen(server->host), system_charset_info)) + goto err; + if (server->db && table->field[2]->store(server->db, - (uint) strlen(server->db), system_charset_info); - if (server->username) + (uint) strlen(server->db), system_charset_info)) + goto err; + if (server->username && table->field[3]->store(server->username, - (uint) strlen(server->username), system_charset_info); - if (server->password) + (uint) strlen(server->username), system_charset_info)) + goto err; + if (server->password && table->field[4]->store(server->password, - (uint) strlen(server->password), system_charset_info); - if (server->port > -1) - table->field[5]->store(server->port); - - if (server->socket) + (uint) strlen(server->password), system_charset_info)) + goto err; + if (server->port > -1 && + table->field[5]->store(server->port)) + goto err; + if (server->socket && table->field[6]->store(server->socket, - (uint) strlen(server->socket), system_charset_info); - if (server->scheme) + (uint) strlen(server->socket), system_charset_info)) + goto err; + if (server->scheme && table->field[7]->store(server->scheme, - (uint) strlen(server->scheme), system_charset_info); - if (server->owner) + (uint) strlen(server->scheme), system_charset_info)) + goto err; + if (server->owner && table->field[8]->store(server->owner, - (uint) strlen(server->owner), system_charset_info); + (uint) strlen(server->owner), system_charset_info)) + goto err; + return 0; + +err: + THD *thd= table->in_use; + DBUG_ASSERT(thd->is_error()); + return thd->get_stmt_da()->get_sql_errno(); } /* @@ -608,7 +625,7 @@ store_server_fields(TABLE *table, FOREIGN_SERVER *server) RETURN VALUE 0 - no errors - >0 - error code + >0 - ER_ error code */ @@ -642,7 +659,8 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server) error= 1; } /* store each field to be inserted */ - store_server_fields(table, server); + if ((error= store_server_fields(table, server))) + DBUG_RETURN(error); DBUG_PRINT("info",("record for server '%s' not found!", server->server_name)); @@ -972,9 +990,15 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) table->use_all_columns(); /* set the field that's the PK to the value we're looking for */ - table->field[0]->store(server->server_name, + if (table->field[0]->store(server->server_name, server->server_name_length, - system_charset_info); + system_charset_info)) + { + DBUG_ASSERT(0); /* Protected by servers_cache */ + THD *thd= table->in_use; + DBUG_ASSERT(thd->is_error()); + return thd->get_stmt_da()->get_sql_errno(); + } if (unlikely((error= table->file->ha_index_read_idx_map(table->record[0], 0, @@ -992,7 +1016,8 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) { /* ok, so we can update since the record exists in the table */ store_record(table,record[1]); - store_server_fields(table, server); + if ((error= store_server_fields(table, server))) + goto end; if (unlikely((error=table->file->ha_update_row(table->record[1], table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME)) From b8ad202da1336550e6987a9c673d1a23e4b26e7b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 3 Dec 2024 13:49:43 +0300 Subject: [PATCH 18/33] MDEV-34770 UBSAN: runtime error: load of address 0x... with insufficient space for an object of type 'uchar' in sys_vars.inl Disable UBSAN for global_var_ptr()/session_var_ptr() (none of "undefined" suboptions for gcc-13 worked). --- sql/set_var.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/set_var.h b/sql/set_var.h index c8da3c1c169..416993e5c34 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -247,9 +247,11 @@ protected: Typically it's the same as session_value_ptr(), but it's different, for example, for ENUM, that is printed as a string, but stored as a number. */ + __attribute__((no_sanitize("undefined"))) uchar *session_var_ptr(THD *thd) const { return ((uchar*)&(thd->variables)) + offset; } + __attribute__((no_sanitize("undefined"))) uchar *global_var_ptr() const { return ((uchar*)&global_system_variables) + offset; } From 85bcc7d26315604a9cd67a64a0ad197dfcda93fc Mon Sep 17 00:00:00 2001 From: Daniele Sciascia Date: Tue, 19 Nov 2024 16:56:21 +0100 Subject: [PATCH 19/33] MDEV-35446 Sporadic failure of galera.galera_insert_multi Test failed sporadically when --ps-protocol was enabled: a transaction that was BF aborted on COMMIT would succeed instead of reporting the expected deadlock error. The reason for the failure was that, depending on timing, the transaction was BF aborted while the COMMIT statement was being prepared through a COM_STMT_PREPARE command. In the failing cases, the transaction was BF aborted after COM_STMT_PREPARE had already disabled the diagnostics area of the client. Attempt to override the deadlock error towards the end of dispatch_command() would be skipped, resulting in a successful COMMIT even if the transaction is aborted. This bug affected the following MTR tests: - galera_insert_multi - galera_nopk_unicode Signed-off-by: Julius Goryavsky --- mysql-test/suite/galera/r/MDEV-35446.result | 22 ++++++++ mysql-test/suite/galera/t/MDEV-35446.cnf | 4 ++ mysql-test/suite/galera/t/MDEV-35446.test | 57 +++++++++++++++++++++ sql/sql_parse.cc | 7 ++- 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera/r/MDEV-35446.result create mode 100644 mysql-test/suite/galera/t/MDEV-35446.cnf create mode 100644 mysql-test/suite/galera/t/MDEV-35446.test diff --git a/mysql-test/suite/galera/r/MDEV-35446.result b/mysql-test/suite/galera/r/MDEV-35446.result new file mode 100644 index 00000000000..30f3b2d591c --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-35446.result @@ -0,0 +1,22 @@ +connection node_2; +connection node_1; +connect bf_trx, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connect victim_trx, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connect node_2_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_2; +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; +connection victim_trx; +START TRANSACTION; +INSERT INTO t1 VALUES (2), (1); +connection node_2_ctrl; +SET GLOBAL debug_dbug = '+d,sync.wsrep_apply_cb'; +connection bf_trx; +INSERT INTO t1 VALUES (1), (2); +connection node_2_ctrl; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; +SET GLOBAL debug_dbug = ''; +connection victim_trx; +SET DEBUG_SYNC = "wsrep_at_dispatch_end_before_result SIGNAL signal.wsrep_apply_cb WAIT_FOR bf_abort"; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/MDEV-35446.cnf b/mysql-test/suite/galera/t/MDEV-35446.cnf new file mode 100644 index 00000000000..ee365a18340 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-35446.cnf @@ -0,0 +1,4 @@ +!include ../galera_2nodes.cnf + +[mysqltest] +ps-protocol diff --git a/mysql-test/suite/galera/t/MDEV-35446.test b/mysql-test/suite/galera/t/MDEV-35446.test new file mode 100644 index 00000000000..49b82adcd01 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-35446.test @@ -0,0 +1,57 @@ +# +# MDEV-35446 +# +# Test BF abort of a transaction under PS protocol, after +# a statement is prepared (and the diagnostics area is +# disabled). +# + +--source include/have_debug_sync.inc +--source include/galera_cluster.inc + +# +# Setup: bf_trx executes in node_1 and will BF abort victim_trx. +# +--connect bf_trx, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connect victim_trx, 127.0.0.1, root, , test, $NODE_MYPORT_2 +--connect node_2_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_2 + +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB; + +--connection victim_trx +START TRANSACTION; +INSERT INTO t1 VALUES (2), (1); + +--connection node_2_ctrl +SET GLOBAL debug_dbug = '+d,sync.wsrep_apply_cb'; + +--connection bf_trx +INSERT INTO t1 VALUES (1), (2); + +--connection node_2_ctrl +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; +SET GLOBAL debug_dbug = ''; + +# +# COMMIT the victim_trx and expect a deadlock error. +# Here we park the client in a sync point at the end of prepare +# command (COM_STMT_PREPARE), where the diagnostics area of the +# client has already been disabled. The client signals the +# applier to continue and will be BF aborted. +# If bug is present, the transaction is aborted, but the COMMIT +# statement succeeds (instead of returning deadlock error). +# +--connection victim_trx + +--disable_ps_protocol +SET DEBUG_SYNC = "wsrep_at_dispatch_end_before_result SIGNAL signal.wsrep_apply_cb WAIT_FOR bf_abort"; +--enable_ps_protocol + +--error ER_LOCK_DEADLOCK +COMMIT; + +# +# Cleanup +# +SET DEBUG_SYNC = 'RESET'; +DROP TABLE t1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0ed938eb63e..e7699a3f464 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1165,7 +1165,8 @@ static bool wsrep_command_no_result(char command) { return (command == COM_STMT_FETCH || command == COM_STMT_SEND_LONG_DATA || - command == COM_STMT_CLOSE); + command == COM_STMT_CLOSE || + command == COM_STMT_PREPARE); } #endif /* WITH_WSREP */ #ifndef EMBEDDED_LIBRARY @@ -2439,6 +2440,10 @@ dispatch_end: { WSREP_DEBUG("THD is killed at dispatch_end"); } + if (thd->lex->sql_command != SQLCOM_SET_OPTION) + { + DEBUG_SYNC(thd, "wsrep_at_dispatch_end_before_result"); + } wsrep_after_command_before_result(thd); if (wsrep_current_error(thd) && !wsrep_command_no_result(command)) { From c772344510a010c6eba6865a45ebb48c973f9993 Mon Sep 17 00:00:00 2001 From: Daniele Sciascia Date: Fri, 29 Nov 2024 14:50:14 +0100 Subject: [PATCH 20/33] Allow mysqltest to run COMMIT statement under --ps-protocol Fix the regular expression that determines which statements can use the Prepared Statement API, when --ps-protocol is used. The current regular expression allows COMMIT only if it is followed by a whitespace. Meaning that statement "COMMIT ;" is allowed to run with prepared statements, while "COMMIT;" is not. Fix the filter so that both are allowed. Signed-off-by: Julius Goryavsky --- client/mysqltest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 3d820834b67..71025814fcf 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -9620,7 +9620,7 @@ void init_re(void) //"[[:space:]]*CALL[[:space:]]|" // XXX run_query_stmt doesn't read multiple result sets "[[:space:]]*CHANGE[[:space:]]|" "[[:space:]]*CHECKSUM[[:space:]]|" - "[[:space:]]*COMMIT[[:space:]]|" + "[[:space:]]*COMMIT[[:space:]]*|" "[[:space:]]*COMPOUND[[:space:]]|" "[[:space:]]*CREATE[[:space:]]+DATABASE[[:space:]]|" "[[:space:]]*CREATE[[:space:]]+INDEX[[:space:]]|" From a2575a0703406f633659d9b9c8e0ff9750c888bf Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Thu, 21 Nov 2024 16:54:51 +0200 Subject: [PATCH 21/33] MDEV-35465 Async replication stops working on Galera async replica node when parallel replication is enabled Parallel slave failed to retry in retry_event_group() with error WSREP: Parallel slave worker failed at wsrep_before_command() hook Fix wsrep transaction cleanup/restart in retry_event_group() to properly clean up previous transaction by calling wsrep_after_statement(). Also move call to reset error after call to wsrep_after_statement() to make sure that it remains effective. Add a MTR test galera_as_slave_parallel_retry to reproduce the error when the fix is not present. Other issues which were detected when testing with sysbench: Check if parallel slave is killed for retry before waiting for prior commits in THD::wsrep_parallel_slave_wait_for_prior_commit(). This is required with slave-parallel-mode=optimistic to avoid deadlock when a slave later in commit order manages to reach prepare phase before a lock conflict is detected. Suppress wsrep applier specific warning for slave threads. Signed-off-by: Julius Goryavsky --- .../r/galera_as_slave_parallel_retry.result | 22 ++++++++ .../t/galera_as_slave_parallel_retry.cnf | 10 ++++ .../t/galera_as_slave_parallel_retry.test | 52 +++++++++++++++++++ sql/log_event_server.cc | 2 +- sql/rpl_parallel.cc | 29 +++++++---- sql/rpl_parallel.h | 1 + sql/wsrep_mysqld.cc | 11 ++-- 7 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_as_slave_parallel_retry.result create mode 100644 mysql-test/suite/galera/t/galera_as_slave_parallel_retry.cnf create mode 100644 mysql-test/suite/galera/t/galera_as_slave_parallel_retry.test diff --git a/mysql-test/suite/galera/r/galera_as_slave_parallel_retry.result b/mysql-test/suite/galera/r/galera_as_slave_parallel_retry.result new file mode 100644 index 00000000000..f292a4d48e5 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_as_slave_parallel_retry.result @@ -0,0 +1,22 @@ +connection node_2; +connection node_1; +connect master, 127.0.0.1, root, , test, $NODE_MYPORT_3; +connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1; +START SLAVE; +connection master; +CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB; +connection node_1; +SET GLOBAL debug_dbug = '+d,rpl_parallel_simulate_temp_err_xid,sync.wsrep_retry_event_group'; +connection master; +INSERT INTO t1 VALUES (1); +connection node_1_ctrl; +SET debug_sync = 'now WAIT_FOR sync.wsrep_retry_event_group_reached'; +SET GLOBAL debug_dbug = ''; +SET debug_sync = 'now SIGNAL signal.wsrep_retry_event_group'; +connection node_1; +SET debug_sync = 'RESET'; +connection master; +DROP TABLE t1; +connection node_1; +STOP SLAVE; diff --git a/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.cnf b/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.cnf new file mode 100644 index 00000000000..59e4c5ffba2 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.cnf @@ -0,0 +1,10 @@ +!include ../galera_2nodes_as_slave.cnf + +[mysqld] +log-bin=mysqld-bin +log-slave-updates +binlog-format=ROW + +[mysqld.1] +slave-parallel-threads=2 +slave-parallel-mode=optimistic diff --git a/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.test b/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.test new file mode 100644 index 00000000000..0d499f22fcd --- /dev/null +++ b/mysql-test/suite/galera/t/galera_as_slave_parallel_retry.test @@ -0,0 +1,52 @@ +# MDEV-35465 Async replication stops working on Galera async replica node +# when parallel replication is enabled + +--source include/have_innodb.inc +--source include/have_log_bin.inc +--source include/galera_cluster.inc +--source include/have_debug_sync.inc + +# Node 3 is not a Galera node, use it as a master +--connect master, 127.0.0.1, root, , test, $NODE_MYPORT_3 +--connect node_1_ctrl, 127.0.0.1, root, , test, $NODE_MYPORT_1 + +--connection node_1 +--disable_query_log +--eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_USER='root', MASTER_PORT=$NODE_MYPORT_3; +--enable_query_log +START SLAVE; + +--connection master +CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB; + +--connection node_1 +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1' +--source include/wait_condition.inc + +# +--let debug_dbug_orig = `SELECT @@GLOBAL.debug_dbug` +SET GLOBAL debug_dbug = '+d,rpl_parallel_simulate_temp_err_xid,sync.wsrep_retry_event_group'; + +--connection master +INSERT INTO t1 VALUES (1); + +--connection node_1_ctrl +SET debug_sync = 'now WAIT_FOR sync.wsrep_retry_event_group_reached'; +--eval SET GLOBAL debug_dbug = '$debug_dbug_orig' +SET debug_sync = 'now SIGNAL signal.wsrep_retry_event_group'; + +--let $wait_condition = SELECT COUNT(*) = 1 FROM t1; +--source include/wait_condition.inc + + +--connection node_1 +SET debug_sync = 'RESET'; + +--connection master +DROP TABLE t1; + +--connection node_1 +--let $wait_condition = SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1' +--source include/wait_condition.inc + +STOP SLAVE; diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 90d4c9c2406..9f7fea18075 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -5511,7 +5511,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) if (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))) { #ifdef WITH_WSREP - if (WSREP(thd)) + if (WSREP(thd) && !thd->slave_thread) { WSREP_WARN("BF applier thread=%lu failed to open_and_lock_tables for " "%s, fatal: %d " diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index ca469e3b6c7..935269a2ed2 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -134,7 +134,7 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev) asynchronously, we need to be sure they will be completed before starting a new transaction. Otherwise the new transaction might suffer a spurious kill. */ -static void +void wait_for_pending_deadlock_kill(THD *thd, rpl_group_info *rgi) { PSI_stage_info old_stage; @@ -831,8 +831,12 @@ do_retry: err= 0; errmsg= NULL; #ifdef WITH_WSREP - thd->wsrep_cs().reset_error(); - WSREP_DEBUG("retrying async replication event"); + DBUG_EXECUTE_IF("sync.wsrep_retry_event_group", { + const char act[]= "now " + "SIGNAL sync.wsrep_retry_event_group_reached " + "WAIT_FOR signal.wsrep_retry_event_group"; + debug_sync_set_action(thd, STRING_WITH_LEN(act)); + };); #endif /* WITH_WSREP */ /* @@ -981,15 +985,20 @@ do_retry: */ thd->reset_killed(); #ifdef WITH_WSREP - if (wsrep_before_command(thd)) + if (WSREP(thd)) { - WSREP_WARN("Parallel slave worker failed at wsrep_before_command() hook"); - err= 1; - goto err; + /* Exec after statement hook to make sure that the failed transaction + * gets cleared and reset error state. */ + if (wsrep_after_statement(thd)) + { + WSREP_WARN("Parallel slave worker failed at wsrep_after_statement() hook"); + err= 1; + goto err; + } + thd->wsrep_cs().reset_error(); + wsrep_start_trx_if_not_started(thd); + WSREP_DEBUG("parallel slave retry, after trx start"); } - wsrep_start_trx_if_not_started(thd); - WSREP_DEBUG("parallel slave retry, after trx start"); - #endif /* WITH_WSREP */ strmake_buf(log_name, ir->name); if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0) diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index 66b04ca4590..1d0b2e18d95 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -380,6 +380,7 @@ struct rpl_parallel { extern struct rpl_parallel_thread_pool global_rpl_thread_pool; +extern void wait_for_pending_deadlock_kill(THD *thd, rpl_group_info *rgi); extern int rpl_parallel_resize_pool_if_no_slaves(void); extern int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool); extern int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 6fd6250ca24..4a2c439cb43 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -3426,11 +3426,16 @@ enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit) bool THD::wsrep_parallel_slave_wait_for_prior_commit() { - if (rgi_slave && rgi_slave->is_parallel_exec && wait_for_prior_commit()) + if (rgi_slave && rgi_slave->is_parallel_exec) { - return 1; + wait_for_pending_deadlock_kill(this, rgi_slave); + if (rgi_slave->killed_for_retry) { + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return true; + } + return wait_for_prior_commit(); } - return 0; + return false; } /***** callbacks for wsrep service ************/ From 818c84ad459e17622210a3f9bbf277777d26adfe Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Tue, 3 Dec 2024 15:08:25 +0100 Subject: [PATCH 22/33] galera mtr tests: post-fix changes to test suite --- mysql-test/suite/galera/disabled.def | 1 - mysql-test/suite/galera/t/galera_pc_recovery.test | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 16aaf89259f..cbb42331792 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -11,6 +11,5 @@ ############################################################################## galera_sequences : MDEV-32561 WSREP FSM failure: no such a transition REPLICATING -> COMMITTED -galera_concurrent_ctas : MDEV-32779 galera_concurrent_ctas: assertion in the galera::ReplicatorSMM::finish_cert() galera_as_slave_replay : MDEV-32780 galera_as_slave_replay: assertion in the wsrep::transaction::before_rollback() galera_slave_replay : MDEV-32780 galera_as_slave_replay: assertion in the wsrep::transaction::before_rollback() diff --git a/mysql-test/suite/galera/t/galera_pc_recovery.test b/mysql-test/suite/galera/t/galera_pc_recovery.test index 4489db63a91..631054103a3 100644 --- a/mysql-test/suite/galera/t/galera_pc_recovery.test +++ b/mysql-test/suite/galera/t/galera_pc_recovery.test @@ -33,8 +33,8 @@ SELECT COUNT(*) = 1 FROM t1; # Perform --wsrep-recover and preserve the positions into variables by placing them in $MYSQL_TMP_DIR/galera_wsrep_start_position.inc and then --source'ing it ---exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --loose-innodb --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.1.log > $MYSQL_TMP_DIR/galera_wsrep_recover.1.log 2>&1 ---exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --wsrep-recover --loose-innodb --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.2.log > $MYSQL_TMP_DIR/galera_wsrep_recover.2.log 2>&1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --innodb --wsrep-recover --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.1.log > $MYSQL_TMP_DIR/galera_wsrep_recover.1.log 2>&1 +--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --innodb --wsrep-recover --log-error=$MYSQL_TMP_DIR/galera_wsrep_recover.2.log > $MYSQL_TMP_DIR/galera_wsrep_recover.2.log 2>&1 --perl use strict; From 8e9aa9c6b09541830234f17fe615d0879b1af3cb Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 4 Dec 2024 12:58:00 +0200 Subject: [PATCH 23/33] Fix MariadDB to compile with gcc 7.5.0 gcc 7.5.0 does not understand __attribute__((no_sanitize("undefined")) I moved the usage of this attribute from sql/set_var.h to include/my_attribute.h and created a macro for it depending on compiler used. --- include/my_attribute.h | 8 +++++++- sql/set_var.h | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/my_attribute.h b/include/my_attribute.h index c8e980702b6..e88947fb142 100644 --- a/include/my_attribute.h +++ b/include/my_attribute.h @@ -70,5 +70,11 @@ # endif /* GNUC >= 3.1 */ #endif - +/* gcc 7.5.0 does not support __attribute__((no_sanitize("undefined")) */ +#if defined(__clang__) +#define __attribute_no_sanitize__ __attribute__((no_sanitize("undefined"))) +#else +#define __attribute_no_sanitize__ #endif + +#endif /* _my_attribute_h */ diff --git a/sql/set_var.h b/sql/set_var.h index 416993e5c34..483027e57d7 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -26,6 +26,7 @@ #endif #include +#include class sys_var; class set_var; @@ -247,11 +248,11 @@ protected: Typically it's the same as session_value_ptr(), but it's different, for example, for ENUM, that is printed as a string, but stored as a number. */ - __attribute__((no_sanitize("undefined"))) + __attribute_no_sanitize__ uchar *session_var_ptr(THD *thd) const { return ((uchar*)&(thd->variables)) + offset; } - __attribute__((no_sanitize("undefined"))) + __attribute_no_sanitize__ uchar *global_var_ptr() const { return ((uchar*)&global_system_variables) + offset; } From 2bf9f0d422444ad867ba81e8180fada6c519db08 Mon Sep 17 00:00:00 2001 From: Jason Cu Date: Thu, 16 Nov 2023 15:34:53 -0800 Subject: [PATCH 24/33] MDEV-32395: update_depend_map_for_order: SEGV at /mariadb-11.3.0/sql/sql_select.cc:16583 MDEV-32329 (patch) pushdown from having into where: Server crashes at sub_select When generating an Item_equal with a Item_ref that refers to a field outside of a subselect, remove_item_direct_ref() causes the dependency (depended_from) on the outer select to be lost, which causes trouble for code downstream that can no longer determine the scope of the Item. Not calling remove_item_direct_ref() retains the Item's dependency. Test cases from MDEV-32395 and MDEV-32329 are included. Some fixes from other developers: Monty: - Fixed wrong code in Item_equal::create_pushable_equalities() that could cause wrong item to be used if there was no matching items. Daniel Black: - Added test cases from MDEV-32329 Igor Babaev: - Provided fix for removing call to remove_item_direct_ref() in eliminate_item_equal() MDEV-32395: update_depend_map_for_order: SEGV at /mariadb-11.3.0/sql/sql_select.cc:16583 Include test cases from MDEV-32329. --- mysql-test/main/derived_cond_pushdown.result | 54 ++++++++++++++++++++ mysql-test/main/derived_cond_pushdown.test | 52 +++++++++++++++++++ sql/item.h | 14 ----- sql/item_cmpfunc.cc | 9 ++-- sql/sql_select.cc | 4 +- 5 files changed, 112 insertions(+), 21 deletions(-) diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result index ca63e138fe7..63d57f2d1a1 100644 --- a/mysql-test/main/derived_cond_pushdown.result +++ b/mysql-test/main/derived_cond_pushdown.result @@ -19078,4 +19078,58 @@ f a c 0 0 -1 DROP VIEW v; DROP TABLE t1; +# +# MDEV-32395: update_depend_map_for_order: SEGV at +# /mariadb-11.3.0/sql/sql_select.cc:16583 +# +create table t1 (c1 int); +insert into t1 values (1), (2); +create table t2 (c2 int); +insert into t2 values (1), (2); +create table t3 (c3 int); +insert into t3 values (1), (2); +set statement optimizer_switch='condition_pushdown_for_derived=off,condition_pushdown_for_subquery=off,condition_pushdown_from_having=off' for +select t1.c1 as a from t2, t1 where t1.c1=t2.c2 +order by (select c3 from t3 group by a having a=2); +a +1 +2 +drop table t1, t2, t3; +# Test case 2 +CREATE TABLE t1 ( x BOOLEAN NOT NULL ); +INSERT INTO t1 ( x ) VALUES ( 1 ) ; +UPDATE t1 SET x = 1 WHERE x = 1 ; +INSERT INTO t1 ( x ) VALUES ( 1 ) , ( x IN ( SELECT x FROM ( SELECT ( SELECT EXISTS ( SELECT * FROM ( SELECT DISTINCT ( - CASE WHEN x = 1 THEN 1 ELSE x + 1 END >= x IS NOT NULL = 1 AND x = 1 ) OR x = x OR x = 'x' FROM t1 AS x GROUP BY x ) AS x WHERE 1 / x GROUP BY x HAVING ( 1 = 1 AND x = 1 ) ) FROM t1 GROUP BY EXISTS ( SELECT 1 ) ) FROM t1 UNION SELECT x FROM t1 ) AS x ) ) ; +DROP TABLE t1; +# Test case 3 +CREATE TABLE t0 ( c6 INT , c21 INT ) ; +INSERT INTO t0 VALUES ( 55 , -95 ) , ( 9 , 90 ) ; +ALTER TABLE t0 ADD COLUMN c37 INT AFTER c6 ; +INSERT INTO t0 VALUES ( ) , ( ) ; +SELECT t0 . c6 AS c42 FROM ( SELECT t0 . c6 = TRIM( TRAILING FROM 96 ) SOUNDS LIKE CONVERT ( t0 . c6 , UNSIGNED ) >> PI ( ) AS c49 FROM t0 ) AS t1 JOIN t0 ON RTRIM ( - RAND ( -66 ) BETWEEN FIND_IN_SET ( 20 , UNHEX ( -80 ) IS NULL OR IF ( 85 , -83 , -113 ) ) AND -125 ) / EXP ( c21 ) = t1 . c49 ORDER BY c42 , ( c42 + ( SELECT c21 AS c61 FROM t0 WHERE t0 . c37 >= -19.601384 = RAND ( ) / TRIM( t0 . c21 FROM 'C@rG3D(#9*17(a.,rV' ) = -106 GROUP BY c21 , c42 HAVING c42 = -73 LIMIT 1 ) ) ; +c42 +9 +9 +55 +55 +drop table t0; +# +# MDEV-32329 pushdown from having into where: Server crashes at sub_select +# +WITH RECURSIVE cte AS ( SELECT 1 as x UNION SELECT x FROM cte) +SELECT ( SELECT 1 FROM ( SELECT 1 FROM cte) dt GROUP BY x HAVING x= 1 ) +FROM cte +GROUP BY 1 ; +( SELECT 1 FROM ( SELECT 1 FROM cte) dt GROUP BY x HAVING x= 1 ) +1 +# Test case 2 +WITH +cte1 AS ( SELECT 1 as x UNION SELECT 1), +cte2 AS ( SELECT 1 as x UNION SELECT 1) +SELECT +( SELECT 1 FROM ( SELECT 1 FROM cte1) dt GROUP BY x HAVING x= 1 ) +FROM cte2 +GROUP BY 1 ; +( SELECT 1 FROM ( SELECT 1 FROM cte1) dt GROUP BY x HAVING x= 1 ) +1 # End of 10.5 tests diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test index 4e91bcaa372..9a09e398b97 100644 --- a/mysql-test/main/derived_cond_pushdown.test +++ b/mysql-test/main/derived_cond_pushdown.test @@ -4217,5 +4217,57 @@ SELECT * FROM v,t1 WHERE f = DEFAULT(c); DROP VIEW v; DROP TABLE t1; +--echo # +--echo # MDEV-32395: update_depend_map_for_order: SEGV at +--echo # /mariadb-11.3.0/sql/sql_select.cc:16583 +--echo # + +create table t1 (c1 int); +insert into t1 values (1), (2); +create table t2 (c2 int); +insert into t2 values (1), (2); +create table t3 (c3 int); +insert into t3 values (1), (2); + +set statement optimizer_switch='condition_pushdown_for_derived=off,condition_pushdown_for_subquery=off,condition_pushdown_from_having=off' for +select t1.c1 as a from t2, t1 where t1.c1=t2.c2 + order by (select c3 from t3 group by a having a=2); +drop table t1, t2, t3; + +--echo # Test case 2 + +CREATE TABLE t1 ( x BOOLEAN NOT NULL ); +INSERT INTO t1 ( x ) VALUES ( 1 ) ; +UPDATE t1 SET x = 1 WHERE x = 1 ; +INSERT INTO t1 ( x ) VALUES ( 1 ) , ( x IN ( SELECT x FROM ( SELECT ( SELECT EXISTS ( SELECT * FROM ( SELECT DISTINCT ( - CASE WHEN x = 1 THEN 1 ELSE x + 1 END >= x IS NOT NULL = 1 AND x = 1 ) OR x = x OR x = 'x' FROM t1 AS x GROUP BY x ) AS x WHERE 1 / x GROUP BY x HAVING ( 1 = 1 AND x = 1 ) ) FROM t1 GROUP BY EXISTS ( SELECT 1 ) ) FROM t1 UNION SELECT x FROM t1 ) AS x ) ) ; +DROP TABLE t1; + +--echo # Test case 3 + +CREATE TABLE t0 ( c6 INT , c21 INT ) ; +INSERT INTO t0 VALUES ( 55 , -95 ) , ( 9 , 90 ) ; +ALTER TABLE t0 ADD COLUMN c37 INT AFTER c6 ; +INSERT INTO t0 VALUES ( ) , ( ) ; +SELECT t0 . c6 AS c42 FROM ( SELECT t0 . c6 = TRIM( TRAILING FROM 96 ) SOUNDS LIKE CONVERT ( t0 . c6 , UNSIGNED ) >> PI ( ) AS c49 FROM t0 ) AS t1 JOIN t0 ON RTRIM ( - RAND ( -66 ) BETWEEN FIND_IN_SET ( 20 , UNHEX ( -80 ) IS NULL OR IF ( 85 , -83 , -113 ) ) AND -125 ) / EXP ( c21 ) = t1 . c49 ORDER BY c42 , ( c42 + ( SELECT c21 AS c61 FROM t0 WHERE t0 . c37 >= -19.601384 = RAND ( ) / TRIM( t0 . c21 FROM 'C@rG3D(#9*17(a.,rV' ) = -106 GROUP BY c21 , c42 HAVING c42 = -73 LIMIT 1 ) ) ; +drop table t0; + +--echo # +--echo # MDEV-32329 pushdown from having into where: Server crashes at sub_select +--echo # + +WITH RECURSIVE cte AS ( SELECT 1 as x UNION SELECT x FROM cte) + SELECT ( SELECT 1 FROM ( SELECT 1 FROM cte) dt GROUP BY x HAVING x= 1 ) + FROM cte + GROUP BY 1 ; + +--echo # Test case 2 + +WITH + cte1 AS ( SELECT 1 as x UNION SELECT 1), + cte2 AS ( SELECT 1 as x UNION SELECT 1) +SELECT + ( SELECT 1 FROM ( SELECT 1 FROM cte1) dt GROUP BY x HAVING x= 1 ) +FROM cte2 + GROUP BY 1 ; --echo # End of 10.5 tests diff --git a/sql/item.h b/sql/item.h index 8a13c9ea85a..147b12dfa8b 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2511,12 +2511,6 @@ public: */ virtual void under_not(Item_func_not * upper __attribute__((unused))) {}; - /* - If Item_field is wrapped in Item_direct_wrep remove this Item_direct_ref - wrapper. - */ - virtual Item *remove_item_direct_ref() { return this; } - void register_in(THD *thd); @@ -5758,11 +5752,6 @@ public: With_sum_func_cache* get_with_sum_func_cache() override { return this; } Item *field_transformer_for_having_pushdown(THD *thd, uchar *arg) override { return (*ref)->field_transformer_for_having_pushdown(thd, arg); } - Item *remove_item_direct_ref() override - { - *ref= (*ref)->remove_item_direct_ref(); - return this; - } }; @@ -5810,8 +5799,6 @@ public: Ref_Type ref_type() override { return DIRECT_REF; } Item *do_get_copy(THD *thd) const override { return get_item_copy(thd, this); } - Item *remove_item_direct_ref() override - { return (*ref)->remove_item_direct_ref(); } }; @@ -6194,7 +6181,6 @@ public: { return get_item_copy(thd, this); } Item *field_transformer_for_having_pushdown(THD *, uchar *) override { return this; } - Item *remove_item_direct_ref() override { return this; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e72e825a04d..0df3e031799 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -7823,10 +7823,11 @@ bool Item_equal::create_pushable_equalities(THD *thd, while ((item=it++)) { - left_item= item; - if (checker && !((item->*checker) (arg))) - continue; - break; + if (!checker || ((item->*checker)(arg))) + { + left_item= item; + break; + } } if (!left_item) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 33004a42685..d75a3bbcd4c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16266,9 +16266,7 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels, */ Item *head_item= (!item_const && current_sjm && current_sjm_head != field_item) ? current_sjm_head: head; - eq_item= new (thd->mem_root) Item_func_eq(thd, - field_item->remove_item_direct_ref(), - head_item->remove_item_direct_ref()); + eq_item= new (thd->mem_root) Item_func_eq(thd, field_item, head_item); if (!eq_item || eq_item->set_cmp_func(thd)) return 0; From aa9d5aea48efc6848711c2f4aae299de7db50882 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 4 Dec 2024 15:39:10 +0300 Subject: [PATCH 25/33] MDEV-34770 GCC warning fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvm-bintar-trusty-x86: warning: ‘no_sanitize’ attribute directive ignored [-Wattributes] The attribute is supported since gcc 8. --- include/my_attribute.h | 12 ++++++++---- sql/set_var.h | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/my_attribute.h b/include/my_attribute.h index e88947fb142..9bfb6ab50be 100644 --- a/include/my_attribute.h +++ b/include/my_attribute.h @@ -71,10 +71,14 @@ #endif /* gcc 7.5.0 does not support __attribute__((no_sanitize("undefined")) */ -#if defined(__clang__) -#define __attribute_no_sanitize__ __attribute__((no_sanitize("undefined"))) -#else -#define __attribute_no_sanitize__ +#ifndef ATTRIBUTE_NO_UBSAN +# if (GCC_VERSION >= 8000) || defined(__clang__) +# define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize("undefined"))) +# elif (GCC_VERSION >= 6001) +# define ATTRIBUTE_NO_UBSAN __attribute__((no_sanitize_undefined)) +# else +# define ATTRIBUTE_NO_UBSAN +# endif #endif #endif /* _my_attribute_h */ diff --git a/sql/set_var.h b/sql/set_var.h index 483027e57d7..58b23e14bf7 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -248,11 +248,11 @@ protected: Typically it's the same as session_value_ptr(), but it's different, for example, for ENUM, that is printed as a string, but stored as a number. */ - __attribute_no_sanitize__ + ATTRIBUTE_NO_UBSAN uchar *session_var_ptr(THD *thd) const { return ((uchar*)&(thd->variables)) + offset; } - __attribute_no_sanitize__ + ATTRIBUTE_NO_UBSAN uchar *global_var_ptr() const { return ((uchar*)&global_system_variables) + offset; } From aca72b326ae584851e5d755ff0b5fdb89e05c2e7 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 26 Nov 2024 09:37:28 +1100 Subject: [PATCH 26/33] MDEV-34815 SIGILL error when executing mariadbd compiled for RISC-V with Clang RISC-V and Clang produce rdcycle for __builtin_readcyclecounter. Since Linux kernel 6.6 this is a privileged instruction not available to userspace programs. The use of __builtin_readcyclecounter is excluded from RISCV falling back to the rdtime/rdtimeh instructions provided in MDEV-33435. Thanks Alexander Richardson for noting it should be linux only in the code and noting FreeBSD RISC-V permits rdcycle. Author: BINSZ on JIRA --- include/my_rdtsc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/my_rdtsc.h b/include/my_rdtsc.h index 45e91d228f1..70be851dac0 100644 --- a/include/my_rdtsc.h +++ b/include/my_rdtsc.h @@ -97,7 +97,7 @@ C_MODE_START /** A cycle timer. - On clang we use __builtin_readcyclecounter(), except for AARCH64. + On clang we use __builtin_readcyclecounter(), except for AARCH64 and RISC-V. On other compilers: On IA-32 and AMD64, we use the RDTSC instruction. @@ -152,7 +152,7 @@ C_MODE_START */ static inline ulonglong my_timer_cycles(void) { -# if __has_builtin(__builtin_readcyclecounter) && !defined (__aarch64__) +# if __has_builtin(__builtin_readcyclecounter) && !defined (__aarch64__) && !(defined(__linux__) && defined(__riscv)) #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_AARCH64 return __builtin_readcyclecounter(); # elif defined _M_IX86 || defined _M_X64 || defined __i386__ || defined __x86_64__ From 673936173f00cb3a08f6416e921f580c5b58b82d Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Wed, 4 Dec 2024 13:08:28 +0100 Subject: [PATCH 27/33] Make sql_acl_getsort.ic named in line with other files i.e. sql_acl_getsort.inl --- sql/sql_acl.cc | 2 +- sql/{sql_acl_getsort.ic => sql_acl_getsort.inl} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename sql/{sql_acl_getsort.ic => sql_acl_getsort.inl} (100%) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f59df5df37a..949e9825dde 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -78,7 +78,7 @@ const char *safe_vio_type_name(Vio *vio) return vio_type_name(vio_type(vio), &unused); } -#include "sql_acl_getsort.ic" +#include "sql_acl_getsort.inl" static LEX_CSTRING native_password_plugin_name= { STRING_WITH_LEN("mysql_native_password") diff --git a/sql/sql_acl_getsort.ic b/sql/sql_acl_getsort.inl similarity index 100% rename from sql/sql_acl_getsort.ic rename to sql/sql_acl_getsort.inl From daea59a81d0529c362489c1249b3e1236e34f663 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 28 Nov 2024 11:11:03 +0100 Subject: [PATCH 28/33] MDEV-31761: mariadb-binlog prints fractional timestamp part incorrectly Fractional part < 100000 microseconds was printed without leading zeros, causing such timestamps to be applied incorrectly in mariadb-binlog | mysql Signed-off-by: Kristian Nielsen --- mysql-test/main/mysqlbinlog.result | 28 ++++++++++++++++++++++++ mysql-test/main/mysqlbinlog.test | 34 ++++++++++++++++++++++++++++++ sql/log_event_client.cc | 7 ++++-- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/mysqlbinlog.result b/mysql-test/main/mysqlbinlog.result index c6d9ef97229..4e0f570c899 100644 --- a/mysql-test/main/mysqlbinlog.result +++ b/mysql-test/main/mysqlbinlog.result @@ -1286,3 +1286,31 @@ ERROR: Bad syntax in rewrite-db: empty FROM db ERROR: Bad syntax in rewrite-db: empty FROM db +# +# MDEV-31761: mariadb-binlog prints fractional timestamp part incorrectly +# +SET SESSION binlog_format= MIXED; +RESET MASTER; +SET time_zone= '+02:00'; +CREATE TABLE t (a INT, +b TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)); +set SESSION timestamp= 1689978980.012345; +INSERT INTO t (a) VALUES (1); +SELECT * from t; +a b +1 2023-07-22 00:36:20.012345 +FLUSH BINARY LOGS; +SET SESSION timestamp= 1689978980.567890; +SET SESSION binlog_format= ROW; +UPDATE t SET a = 2; +FLUSH BINARY LOGS; +SET SESSION binlog_format= STATEMENT; +DROP TABLE t; +SELECT * FROM t; +a b +1 2023-07-22 00:36:20.012345 +SELECT * FROM t; +a b +2 2023-07-22 00:36:20.567890 +DROP TABLE t; +SET time_zone= default; diff --git a/mysql-test/main/mysqlbinlog.test b/mysql-test/main/mysqlbinlog.test index 22a85393a35..0e5fd9efb70 100644 --- a/mysql-test/main/mysqlbinlog.test +++ b/mysql-test/main/mysqlbinlog.test @@ -637,3 +637,37 @@ FLUSH LOGS; --exec $MYSQL_BINLOG --rewrite-db=" ->" --short-form $MYSQLD_DATADIR/master-bin.000001 2>&1 --exec $MYSQL_BINLOG --rewrite-db=" test -> foo " --short-form $MYSQLD_DATADIR/master-bin.000001 > /dev/null 2> $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn + + +--echo # +--echo # MDEV-31761: mariadb-binlog prints fractional timestamp part incorrectly +--echo # + +SET SESSION binlog_format= MIXED; + +RESET MASTER; +SET time_zone= '+02:00'; +CREATE TABLE t (a INT, + b TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)); +set SESSION timestamp= 1689978980.012345; +INSERT INTO t (a) VALUES (1); +SELECT * from t; +FLUSH BINARY LOGS; +SET SESSION timestamp= 1689978980.567890; +SET SESSION binlog_format= ROW; +UPDATE t SET a = 2; +FLUSH BINARY LOGS; +SET SESSION binlog_format= STATEMENT; + +# Replay to see that timestamps are applied correctly. +# The bug was that leading zeros on the fractional part were not included in +# the mysqlbinlog output, so 1689978980.012345 was applied as 1689978980.12345. + +DROP TABLE t; +--let $datadir= `select @@datadir` +--exec $MYSQL_BINLOG $datadir/master-bin.000001 | $MYSQL test +SELECT * FROM t; +--exec $MYSQL_BINLOG $datadir/master-bin.000002 | $MYSQL test +SELECT * FROM t; +DROP TABLE t; +SET time_zone= default; diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 11fabbbca39..720cc5ab611 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -1851,8 +1851,11 @@ bool Query_log_event::print_query_header(IO_CACHE* file, end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); if (when_sec_part && when_sec_part <= TIME_MAX_SECOND_PART) { - *end++= '.'; - end=int10_to_str(when_sec_part, end, 10); + char buff2[1 + 6 + 1]; + /* Ensure values < 100000 are printed with leading zeros, MDEV-31761. */ + snprintf(buff2, sizeof(buff2), ".%06lu", when_sec_part); + DBUG_ASSERT(strlen(buff2) == 1 + 6); + end= strmov(end, buff2); } end= strmov(end, print_event_info->delimiter); *end++='\n'; From 5a3a16154f773a901b90188fcd2cfaaf0015af89 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 28 Nov 2024 11:54:26 +0100 Subject: [PATCH 29/33] MDEV-35514: Too much mtr output from analyze: sync_with_master Limit SHOW BINLOG/RELAYLOG EVENTS in show_rpl_debug_info.inc to 200 lines. Reviewed-by: Daniel Black Signed-off-by: Kristian Nielsen --- mysql-test/include/show_rpl_debug_info.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/include/show_rpl_debug_info.inc b/mysql-test/include/show_rpl_debug_info.inc index 1b2b7360970..a27ea83038c 100644 --- a/mysql-test/include/show_rpl_debug_info.inc +++ b/mysql-test/include/show_rpl_debug_info.inc @@ -86,12 +86,12 @@ while ($_rpl_server) --echo **** SHOW BINLOG EVENTS on $CURRENT_CONNECTION **** let $binlog_name= query_get_value("SHOW MASTER STATUS", File, 1); --echo binlog_name = '$binlog_name' - eval SHOW BINLOG EVENTS IN '$binlog_name'; + eval SHOW BINLOG EVENTS IN '$binlog_name' LIMIT 200; --echo --echo **** SHOW RELAYLOG EVENTS on $CURRENT_CONNECTION **** let $relaylog_name= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1); --echo relaylog_name = '$relaylog_name' - eval SHOW RELAYLOG EVENTS IN '$relaylog_name'; + eval SHOW RELAYLOG EVENTS IN '$relaylog_name' LIMIT 200; --let $_rpl_is_first_server= 0 From b4fde50b1fbef0de3ba7b519cbd6539d23c46e4d Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 29 Nov 2024 13:05:41 +0100 Subject: [PATCH 30/33] MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION The partitioning error handling code was looking at thd->lex->alter_info.partition_flags in non-alter-table cases, in which cases the value is stale and contains whatever was set by any earlier ALTER TABLE. This could cause the wrong error code to be generated, which then in some cases can cause replication to break with "different errorcode" error. Signed-off-by: Kristian Nielsen --- .../suite/rpl/include/rpl_partition.inc | 28 +++++++++++++++++++ .../suite/rpl/r/rpl_partition_archive.result | 18 ++++++++++++ .../suite/rpl/r/rpl_partition_innodb.result | 17 +++++++++++ .../suite/rpl/r/rpl_partition_memory.result | 17 +++++++++++ .../suite/rpl/r/rpl_partition_myisam.result | 17 +++++++++++ sql/ha_partition.cc | 3 +- .../rpl/r/rpl_partition_tokudb.result | 17 +++++++++++ 7 files changed, 116 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/include/rpl_partition.inc b/mysql-test/suite/rpl/include/rpl_partition.inc index 9f16f769f54..509ae2d88a8 100644 --- a/mysql-test/suite/rpl/include/rpl_partition.inc +++ b/mysql-test/suite/rpl/include/rpl_partition.inc @@ -95,6 +95,33 @@ SELECT * FROM test.regular_tbl ORDER BY fkid LIMIT 2; --replace_column 2 date-time 3 USER 4 UUID SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; + +--echo *** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +--connection master +eval CREATE TABLE t1 (a INT) +ENGINE=$engine_type +PARTITION BY LIST(a) ( + PARTITION p0 VALUES IN (9, NULL), + PARTITION p1 VALUES IN (8, 2, 7), + PARTITION p2 VALUES IN (6, 4, 5), + PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; + +# This failed statement leaves ALTER_PARTITION_TRUNCATE set in +# thd->lex->alter_info.partition_flags +--error ER_NO_SUCH_TABLE +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; + +# The bug was that the code would wrongly look at the (now stale) value of +# thd->lex->alter_info.partition_flags and give the wrong error code +# ER_WRONG_PARTITION_NAME. +--error ER_NO_PARTITION_FOR_GIVEN_VALUE +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); + +--sync_slave_with_master + + ###### CLEAN UP SECTION ############## connection master; @@ -102,3 +129,4 @@ DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; diff --git a/mysql-test/suite/rpl/r/rpl_partition_archive.result b/mysql-test/suite/rpl/r/rpl_partition_archive.result index 4dfd38bcbc6..5c0374bd5af 100644 --- a/mysql-test/suite/rpl/r/rpl_partition_archive.result +++ b/mysql-test/suite/rpl/r/rpl_partition_archive.result @@ -140,8 +140,26 @@ SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; id dt user uuidf fkid filler 1 date-time USER UUID 300 Partitioned table! Going to test replication for MySQL 2 date-time USER UUID 299 Partitioned table! Going to test replication for MySQL +*** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +connection master; +CREATE TABLE t1 (a INT) +ENGINE='Archive' +PARTITION BY LIST(a) ( +PARTITION p0 VALUES IN (9, NULL), +PARTITION p1 VALUES IN (8, 2, 7), +PARTITION p2 VALUES IN (6, 4, 5), +PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; +ERROR 42S02: Table 'test.non_existent' doesn't exist +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); +ERROR HY000: Table has no partition for value 9 +connection slave; +connection master; DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_partition_innodb.result b/mysql-test/suite/rpl/r/rpl_partition_innodb.result index 4b717d8b46c..599d0edf414 100644 --- a/mysql-test/suite/rpl/r/rpl_partition_innodb.result +++ b/mysql-test/suite/rpl/r/rpl_partition_innodb.result @@ -142,9 +142,26 @@ SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; id dt user uuidf fkid filler 1 date-time USER UUID 300 Partitioned table! Going to test replication for MySQL 2 date-time USER UUID 299 Partitioned table! Going to test replication for MySQL +*** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +connection master; +CREATE TABLE t1 (a INT) +ENGINE='InnoDB' +PARTITION BY LIST(a) ( +PARTITION p0 VALUES IN (9, NULL), +PARTITION p1 VALUES IN (8, 2, 7), +PARTITION p2 VALUES IN (6, 4, 5), +PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; +ERROR 42S02: Table 'test.non_existent' doesn't exist +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); +ERROR HY000: Table has no partition for value 9 +connection slave; connection master; DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_partition_memory.result b/mysql-test/suite/rpl/r/rpl_partition_memory.result index d37973b0d80..291fdfa33fd 100644 --- a/mysql-test/suite/rpl/r/rpl_partition_memory.result +++ b/mysql-test/suite/rpl/r/rpl_partition_memory.result @@ -142,9 +142,26 @@ SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; id dt user uuidf fkid filler 1 date-time USER UUID 300 Partitioned table! Going to test replication for MySQL 2 date-time USER UUID 299 Partitioned table! Going to test replication for MySQL +*** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +connection master; +CREATE TABLE t1 (a INT) +ENGINE='Memory' +PARTITION BY LIST(a) ( +PARTITION p0 VALUES IN (9, NULL), +PARTITION p1 VALUES IN (8, 2, 7), +PARTITION p2 VALUES IN (6, 4, 5), +PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; +ERROR 42S02: Table 'test.non_existent' doesn't exist +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); +ERROR HY000: Table has no partition for value 9 +connection slave; connection master; DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_partition_myisam.result b/mysql-test/suite/rpl/r/rpl_partition_myisam.result index 57c06a7cbec..678548ddb34 100644 --- a/mysql-test/suite/rpl/r/rpl_partition_myisam.result +++ b/mysql-test/suite/rpl/r/rpl_partition_myisam.result @@ -142,9 +142,26 @@ SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; id dt user uuidf fkid filler 1 date-time USER UUID 300 Partitioned table! Going to test replication for MySQL 2 date-time USER UUID 299 Partitioned table! Going to test replication for MySQL +*** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +connection master; +CREATE TABLE t1 (a INT) +ENGINE='MyISAM' +PARTITION BY LIST(a) ( +PARTITION p0 VALUES IN (9, NULL), +PARTITION p1 VALUES IN (8, 2, 7), +PARTITION p2 VALUES IN (6, 4, 5), +PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; +ERROR 42S02: Table 'test.non_existent' doesn't exist +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); +ERROR HY000: Table has no partition for value 9 +connection slave; connection master; DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; include/rpl_end.inc diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 2a06ca4300e..197f4fa51d6 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -10143,7 +10143,8 @@ void ha_partition::print_error(int error, myf errflag) /* Should probably look for my own errors first */ if ((error == HA_ERR_NO_PARTITION_FOUND) && - ! (thd->lex->alter_info.partition_flags & ALTER_PARTITION_TRUNCATE)) + ! (thd->lex->sql_command == SQLCOM_ALTER_TABLE && + (thd->lex->alter_info.partition_flags & ALTER_PARTITION_TRUNCATE))) { m_part_info->print_no_partition_found(table, errflag); DBUG_VOID_RETURN; diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_partition_tokudb.result b/storage/tokudb/mysql-test/rpl/r/rpl_partition_tokudb.result index 168a5e41b28..e8585e50611 100644 --- a/storage/tokudb/mysql-test/rpl/r/rpl_partition_tokudb.result +++ b/storage/tokudb/mysql-test/rpl/r/rpl_partition_tokudb.result @@ -142,9 +142,26 @@ SELECT * FROM test.regular_tbl ORDER BY fkid DESC LIMIT 2; id dt user uuidf fkid filler 1 date-time USER UUID 300 Partitioned table! Going to test replication for MySQL 2 date-time USER UUID 299 Partitioned table! Going to test replication for MySQL +*** MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION +connection master; +CREATE TABLE t1 (a INT) +ENGINE=TokuDB; +PARTITION BY LIST(a) ( +PARTITION p0 VALUES IN (9, NULL), +PARTITION p1 VALUES IN (8, 2, 7), +PARTITION p2 VALUES IN (6, 4, 5), +PARTITION p3 VALUES IN (3, 1, 0) +); +ALTER TABLE t1 DROP PARTITION p0; +ALTER TABLE non_existent TRUNCATE PARTITION p1,p2; +ERROR 42S02: Table 'test.non_existent' doesn't exist +INSERT INTO t1 PARTITION (p1,p2,p3) VALUES (0),(9); +ERROR HY000: Table has no partition for value 9 +connection slave; connection master; DROP PROCEDURE test.proc_norm; DROP PROCEDURE test.proc_byrange; DROP TABLE test.regular_tbl; DROP TABLE test.byrange_tbl; +DROP TABLE test.t1; include/rpl_end.inc From 2ab10fbec2dc086f3486c356064fed4d4c74ca7d Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 3 Dec 2024 15:02:06 +0100 Subject: [PATCH 31/33] MDEV-24959: ER_BINLOG_ROW_LOGGING_FAILED (1534: Writing one row to the row-based binary log failed) The Write_rows_log_event originally allocated the m_rows_buf up-front, and thus is_valid() checks that the buffer is allocated correctly. But at some point this was changed to allocate the buffer lazily on demand. This means that a a valid event can now have m_rows_buf==NULL. The is_valid() code was not changed, and thus is_valid() could return false on a valid event. This caused a bug for REPLACE INTO t() VALUES(), () which generates a write_rows event with no after image; then the m_rows_buf was never allocated and is_valid() incorrectly returned false, causing an error in some other parts of the code. Also fix a couple of missing special cases in the code for mysqlbinlog to correctly decode (in comments) row events with missing after image. Signed-off-by: Kristian Nielsen --- mysql-test/main/mysqlbinlog.result | 18 ++++++++++++ mysql-test/main/mysqlbinlog.test | 45 ++++++++++++++++++++++++++++++ sql/log_event.h | 2 +- sql/log_event_client.cc | 21 ++++++++++++-- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/mysqlbinlog.result b/mysql-test/main/mysqlbinlog.result index 4e0f570c899..5bde2b05a98 100644 --- a/mysql-test/main/mysqlbinlog.result +++ b/mysql-test/main/mysqlbinlog.result @@ -1314,3 +1314,21 @@ a b 2 2023-07-22 00:36:20.567890 DROP TABLE t; SET time_zone= default; +# +# MDEV-24959: ER_BINLOG_ROW_LOGGING_FAILED (1534: Writing one row to the row-based binary log failed) +# +SET SESSION binlog_format= ROW; +SET SESSION binlog_row_image= MINIMAL; +RESET MASTER; +CREATE TABLE t1 (a INT NOT NULL DEFAULT 0 PRIMARY KEY); +REPLACE INTO t1 () VALUES (),(); +DROP TABLE t1; +FLUSH BINARY LOGS; +SET SESSION binlog_format= STATEMENT; +SET SESSION binlog_row_image= default; +FOUND 1 /Number of rows: 2/ in mdev24959_1.txt +FOUND 1 /DROP TABLE/ in mdev24959_1.txt +FOUND 1 /Number of rows: 2/ in mdev24959_2.txt +FOUND 1 /DROP TABLE/ in mdev24959_2.txt +FOUND 1 /INSERT INTO .* VALUES/ in mdev24959_2.txt +FOUND 1 /SET /[*] no columns [*]// in mdev24959_2.txt diff --git a/mysql-test/main/mysqlbinlog.test b/mysql-test/main/mysqlbinlog.test index 0e5fd9efb70..ceb6ff2eee6 100644 --- a/mysql-test/main/mysqlbinlog.test +++ b/mysql-test/main/mysqlbinlog.test @@ -671,3 +671,48 @@ SELECT * FROM t; SELECT * FROM t; DROP TABLE t; SET time_zone= default; + + +--echo # +--echo # MDEV-24959: ER_BINLOG_ROW_LOGGING_FAILED (1534: Writing one row to the row-based binary log failed) +--echo # + +SET SESSION binlog_format= ROW; +SET SESSION binlog_row_image= MINIMAL; + +RESET MASTER; +CREATE TABLE t1 (a INT NOT NULL DEFAULT 0 PRIMARY KEY); +REPLACE INTO t1 () VALUES (),(); +DROP TABLE t1; +FLUSH BINARY LOGS; +SET SESSION binlog_format= STATEMENT; +SET SESSION binlog_row_image= default; + +--exec $MYSQL_BINLOG --base64-output=decode-rows $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mdev24959_1.txt +--exec $MYSQL_BINLOG --base64-output=decode-rows --verbose $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mdev24959_2.txt + +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mdev24959_1.txt +--let SEARCH_ABORT= NOT FOUND +--let SEARCH_PATTERN= Number of rows: 2 +--source include/search_pattern_in_file.inc + +# There was a bug that mysqlbinlog would get an error while decoding the +# update rows event with no after image and abort the dump; test that now +# the dump is complete and includes the final DROP TABLE. +--let SEARCH_PATTERN= DROP TABLE +--source include/search_pattern_in_file.inc + +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mdev24959_2.txt +--let SEARCH_PATTERN= Number of rows: 2 +--source include/search_pattern_in_file.inc + +--let SEARCH_PATTERN= DROP TABLE +--source include/search_pattern_in_file.inc + +--let SEARCH_PATTERN= INSERT INTO .* VALUES +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= SET /[*] no columns [*]/ +--source include/search_pattern_in_file.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mdev24959_1.txt +--remove_file $MYSQLTEST_VARDIR/tmp/mdev24959_2.txt diff --git a/sql/log_event.h b/sql/log_event.h index a869c6e04f1..0a1d6502932 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -5086,7 +5086,7 @@ public: */ bool is_valid() const override { - return m_rows_buf && m_cols.bitmap; + return m_cols.bitmap; } uint m_row_count; /* The number of rows added to the event */ diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 720cc5ab611..a04776e6404 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -1384,6 +1384,13 @@ void Rows_log_event::count_row_events(PRINT_EVENT_INFO *print_event_info) switch (general_type_code) { case WRITE_ROWS_EVENT: + /* + A write rows event containing no after image (can happen for REPLACE + INTO t() VALUES ()), count this correctly as 1 row and no 0. + */ + if (unlikely(m_rows_buf == m_rows_end)) + print_event_info->row_events++; + /* Fall through. */ case DELETE_ROWS_EVENT: row_events= 1; break; @@ -1509,6 +1516,7 @@ bool Rows_log_event::print_verbose(IO_CACHE *file, /* If the write rows event contained no values for the AI */ if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end))) { + print_event_info->row_events++; if (my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n", map->get_db_name(), map->get_table_name())) goto err; @@ -1542,9 +1550,16 @@ bool Rows_log_event::print_verbose(IO_CACHE *file, /* Print the second image (for UPDATE only) */ if (sql_clause2) { - if (!(length= print_verbose_one_row(file, td, print_event_info, - &m_cols_ai, value, - (const uchar*) sql_clause2))) + /* If the update rows event contained no values for the AI */ + if (unlikely(bitmap_is_clear_all(&m_cols_ai))) + { + length= (bitmap_bits_set(&m_cols_ai) + 7) / 8; + if (my_b_printf(file, "### SET /* no columns */\n")) + goto err; + } + else if (!(length= print_verbose_one_row(file, td, print_event_info, + &m_cols_ai, value, + (const uchar*) sql_clause2))) goto err; value+= length; } From d959acbbf84546d283a1b441530667ea41ebb180 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 25 Oct 2024 20:20:10 +0200 Subject: [PATCH 32/33] MDEV-34049: Parallel access to temptable in different domain_id in parallel replication Disallow changing @@gtid_domain_id while a temporary table is open in STATEMENT or MIXED binlog mode. Otherwise, a slave may try to replicate events refering to the same temporary table in parallel, using domain-based out-of-order parallel replication. This is not valid, temporary tables are only available for use within a single thread at a time. One concrete consequence seen from this bug was a ROLLBACK on an InnoDB temporary table running in one domain in parallel with DROP TEMPORARY TABLE in another domain, causing an assertion inside InnoDB: InnoDB: Failing assertion: table->get_ref_count() == 0 in dict_sys_t::remove. Use an existing error code that's somewhat close to the real issue (ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO), to not add a new error code in a GA release. When this is merged to the next GA release, we could optionally introduce a new and more precise error code for an attempt to change the domain_id while temporary tables are open. Reviewed-by: Brandon Nesterenko Signed-off-by: Kristian Nielsen --- .../suite/rpl/r/rpl_gtid_errorhandling.result | 36 +++++++++++++++-- .../suite/rpl/t/rpl_gtid_errorhandling.test | 40 +++++++++++++++++++ sql/sys_vars.cc | 25 +++++++++++- 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result index 54156685806..5e26bdb0032 100644 --- a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result +++ b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result @@ -60,6 +60,34 @@ ROLLBACK; SELECT * FROM t1 ORDER BY a; a 1 +SET @old_mode= @@SESSION.binlog_format; +SET SESSION binlog_format= row; +SET SESSION gtid_domain_id= 200; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +SET SESSION gtid_domain_id= 0; +BEGIN; +INSERT INTO t2 VALUES (200); +INSERT INTO t1 SELECT * FROM t2; +COMMIT; +SET SESSION gtid_domain_id= 201; +SET SESSION gtid_domain_id= 0; +DELETE FROM t1 WHERE a=200; +SET SESSION gtid_domain_id= 202; +DROP TEMPORARY TABLE t2; +SET SESSION binlog_format= mixed; +SET SESSION gtid_domain_id= 0; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); +SET SESSION gtid_domain_id= 0; +SET SESSION gtid_domain_id= 204; +ERROR HY000: Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction +SET SESSION binlog_format=statement; +INSERT INTO t2 VALUES (2); +SET SESSION gtid_domain_id= 205; +ERROR HY000: Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction +DROP TEMPORARY TABLE t2; +SET SESSION gtid_domain_id= @old_domain; +SET SESSION binlog_format= @old_mode; *** Test requesting an explicit GTID position that conflicts with newer GTIDs of our own in the binlog. *** connection slave; include/stop_slave.inc @@ -83,16 +111,16 @@ ERROR 25000: You are not allowed to execute this command in a transaction ROLLBACK; SET GLOBAL gtid_strict_mode= 1; SET GLOBAL gtid_slave_pos = "0-1-1"; -ERROR HY000: Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +ERROR HY000: Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-17. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos SET GLOBAL gtid_slave_pos = ""; -ERROR HY000: Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +ERROR HY000: Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-17. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos SET GLOBAL gtid_strict_mode= 0; SET GLOBAL gtid_slave_pos = "0-1-1"; Warnings: -Warning 1947 Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +Warning 1947 Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-17. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos SET GLOBAL gtid_slave_pos = ""; Warnings: -Warning 1948 Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +Warning 1948 Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-17. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos RESET MASTER; SET GLOBAL gtid_slave_pos = "0-1-1"; START SLAVE; diff --git a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test index eec7a275e03..aa67778b603 100644 --- a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test @@ -68,6 +68,46 @@ SELECT * FROM t1 ORDER BY a; ROLLBACK; SELECT * FROM t1 ORDER BY a; +# MDEV-34049: Parallel access to temptable in different domain_id in parallel replication +# +# Temporary tables must be prevented from being accessed from multiple threads +# at the same time in parallel replication. Withon one domain_id, this is done +# by running wait_for_prior_commit() before accessing a temporary table. To +# prevent the same temporary table from being accessed in parallel from two +# domains in out-of-order parallel replication, an error must be raised on +# attempt to change the gtid_domain_id while temporary tables are in use in +# a session and binlogged. In row-based binlogging, temporary tables are not +# binlogged, so gtid_domain_id can be freely changed. +SET @old_mode= @@SESSION.binlog_format; +SET SESSION binlog_format= row; +SET SESSION gtid_domain_id= 200; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +SET SESSION gtid_domain_id= 0; +BEGIN; +INSERT INTO t2 VALUES (200); +INSERT INTO t1 SELECT * FROM t2; +COMMIT; +SET SESSION gtid_domain_id= 201; +SET SESSION gtid_domain_id= 0; +DELETE FROM t1 WHERE a=200; +SET SESSION gtid_domain_id= 202; +DROP TEMPORARY TABLE t2; + +SET SESSION binlog_format= mixed; +SET SESSION gtid_domain_id= 0; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); +SET SESSION gtid_domain_id= 0; +--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO +SET SESSION gtid_domain_id= 204; +SET SESSION binlog_format=statement; +INSERT INTO t2 VALUES (2); +--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO +SET SESSION gtid_domain_id= 205; +DROP TEMPORARY TABLE t2; +SET SESSION gtid_domain_id= @old_domain; +SET SESSION binlog_format= @old_mode; + --echo *** Test requesting an explicit GTID position that conflicts with newer GTIDs of our own in the binlog. *** --connection slave diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index cb35386f883..2eda3ce9c8f 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1806,12 +1806,33 @@ Sys_pseudo_thread_id( static bool check_gtid_domain_id(sys_var *self, THD *thd, set_var *var) { - if (var->type != OPT_GLOBAL && - error_if_in_trans_or_substatement(thd, + if (var->type != OPT_GLOBAL) + { + if (error_if_in_trans_or_substatement(thd, ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO, ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO)) return true; + /* + All binlogged statements on a temporary table must be binlogged in the + same domain_id; it is not safe to run them in parallel in different + domains, temporary table must be exclusive to a single thread. + In row-based binlogging, temporary tables do not end up in the binlog, + so there is no such issue. + ToDo: When merging to next (non-GA) release, introduce a more specific + error that describes that the problem is changing gtid_domain_id with + open temporary tables in statement/mixed binlogging mode; it is not + really due to doing it inside a "transaction". + */ + if (thd->has_thd_temporary_tables() && + !thd->is_current_stmt_binlog_format_row() && + var->save_result.ulonglong_value != thd->variables.gtid_domain_id) + { + my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO, + MYF(0)); + return true; + } + } return false; } From 7372ecc396517839a1546a8c1dd9192711922ef1 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 28 Nov 2024 10:24:28 +0100 Subject: [PATCH 33/33] Restore the THD state correctly in parallel replication If both do_gco_wait() and do_ftwrl_wait() had to wait, the state was not restored correctly. Signed-off-by: Kristian Nielsen --- sql/rpl_parallel.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 3669ed3768e..25bc31638d8 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -500,7 +500,8 @@ do_ftwrl_wait(rpl_group_info *rgi, { thd->set_time_for_next_stage(); thd->ENTER_COND(&entry->COND_parallel_entry, &entry->LOCK_parallel_entry, - &stage_waiting_for_ftwrl, old_stage); + &stage_waiting_for_ftwrl, + (*did_enter_cond ? nullptr : old_stage)); *did_enter_cond= true; do {