From 85bcc7d26315604a9cd67a64a0ad197dfcda93fc Mon Sep 17 00:00:00 2001 From: Daniele Sciascia Date: Tue, 19 Nov 2024 16:56:21 +0100 Subject: [PATCH 01/12] 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 02/12] 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 03/12] 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 04/12] 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 05/12] 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 06/12] 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 07/12] 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 673936173f00cb3a08f6416e921f580c5b58b82d Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Wed, 4 Dec 2024 13:08:28 +0100 Subject: [PATCH 08/12] 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 09/12] 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 10/12] 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 11/12] 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 12/12] 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; }