lists.mariadb.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

commits

Thread Start a new thread
Threads by month
  • ----- 2025 -----
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
commits@lists.mariadb.org

  • 14603 discussions
[PATCH] MDEV-33239: mysqlbinlog always stops at timestamp 0xffffffff
by Kristian Nielsen 30 Nov '24

30 Nov '24
Do not use the magic value 0xffffffff as meaning "no --stop-datetime option specified", as this is a valid timestamp value. Use an explicit boolean flag instead. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- client/mysqlbinlog.cc | 10 ++++++---- mysql-test/main/mysqlbinlog.result | 19 +++++++++++++++++++ mysql-test/main/mysqlbinlog.test | 22 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 812cde74759..5af5ca95c75 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -159,7 +159,8 @@ static Domain_gtid_event_filter *domain_id_gtid_filter= NULL; static Server_gtid_event_filter *server_id_gtid_filter= NULL; static char *start_datetime_str, *stop_datetime_str; -static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX; +static my_time_t start_datetime= 0, stop_datetime= 0; +static bool stop_datetime_given= false; static ulonglong rec_count= 0; static MYSQL* mysql = NULL; static const char* dirname_for_local_load= 0; @@ -1027,7 +1028,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, if (ev_type != ROTATE_EVENT && is_server_id_excluded(ev->server_id)) goto end; } - if ((ev->when >= stop_datetime) + if ((stop_datetime_given && ev->when >= stop_datetime) || (pos >= stop_position_mot)) { /* end the program */ @@ -2120,6 +2121,7 @@ get_one_option(const struct my_option *opt, const char *argument, break; case OPT_STOP_DATETIME: stop_datetime= convert_str_to_timestamp(stop_datetime_str); + stop_datetime_given= true; break; case OPT_BASE64_OUTPUT_MODE: int val; @@ -3193,7 +3195,7 @@ static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, Emit a warning in the event that we finished processing input before reaching the boundary indicated by --stop-datetime. */ - if (stop_datetime != MY_TIME_T_MAX && + if (stop_datetime_given && stop_datetime > last_ev_when) { retval = OK_STOP; @@ -3300,7 +3302,7 @@ int main(int argc, char** argv) if (stop_position != (ulonglong)(~(my_off_t)0)) warning("The --stop-position option is ignored in raw mode"); - if (stop_datetime != MY_TIME_T_MAX) + if (stop_datetime_given) warning("The --stop-datetime option is ignored in raw mode"); result_file= 0; if (result_file_name) diff --git a/mysql-test/main/mysqlbinlog.result b/mysql-test/main/mysqlbinlog.result index c7b354105c2..8181a76244c 100644 --- a/mysql-test/main/mysqlbinlog.result +++ b/mysql-test/main/mysqlbinlog.result @@ -1300,4 +1300,23 @@ ERROR: Bad syntax in rewrite-db. Expected syntax is FROM->TO. ERROR: Bad syntax in rewrite-db. Expected syntax is FROM->TO. ERROR: Bad syntax in rewrite-db. Expected syntax is FROM->TO. ERROR: Bad syntax in rewrite-db. Expected syntax is FROM->TO. +# +# MDEV-33239: mysqlbinlog always stops at timestamp 0xffffffff +# +RESET MASTER; +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1); +SET @@timestamp= 0 + 0xffffffff; +INSERT INTO t VALUES (2); +SELECT * FROM t ORDER BY a; +a +1 +2 +FLUSH BINARY LOGS; +DROP TABLE t; +SELECT * FROM t ORDER BY a; +a +1 +2 +DROP TABLE t; ALTER DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci; diff --git a/mysql-test/main/mysqlbinlog.test b/mysql-test/main/mysqlbinlog.test index 3beea178b78..4922a9b0422 100644 --- a/mysql-test/main/mysqlbinlog.test +++ b/mysql-test/main/mysqlbinlog.test @@ -641,4 +641,26 @@ FLUSH LOGS; --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-33239: mysqlbinlog always stops at timestamp 0xffffffff +--echo # + +RESET MASTER; + +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1); +SET @@timestamp= 0 + 0xffffffff; +INSERT INTO t VALUES (2); +SELECT * FROM t ORDER BY a; + +FLUSH BINARY LOGS; +DROP TABLE t; + +# The bug was that mysqlbinlog would stop before the event with timestamp +# 0xffffffff, so the second insert would be missing from the table. +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 | $MYSQL test +SELECT * FROM t ORDER BY a; +DROP TABLE t; + + --source include/test_db_charset_restore.inc -- 2.39.5
1 0
0 0
[PATCH] MDEV-13831: Assertion on event group missing XID/COMMIT event
by Kristian Nielsen 29 Nov '24

29 Nov '24
The assertion occurred in the SQL thread if an event group was incompletely written, missing the end XID or COMMIT event, and immediately followed by a new event group. This could also lead to the incomplete event group being committed, and with the wrong GTID. Fix by rolling back any active transaction from a prior event group when applying the following GTID event. Getting an incomplete event like this is somewhat rare to happen. If the server crashes in the middle of writing an event group, the server restart will write a new format description event, which makes the slave roll back the partial event group. But presumably it could happen if the master experiences temporary write errors in the binlog, like intermittent disk full for example. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- mysql-test/suite/rpl/r/rpl_mdev13831.result | 29 +++++++++++++++ mysql-test/suite/rpl/t/rpl_mdev13831.test | 41 +++++++++++++++++++++ sql/log_event_server.cc | 15 +++++++- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/rpl/r/rpl_mdev13831.result create mode 100644 mysql-test/suite/rpl/t/rpl_mdev13831.test diff --git a/mysql-test/suite/rpl/r/rpl_mdev13831.result b/mysql-test/suite/rpl/r/rpl_mdev13831.result new file mode 100644 index 00000000000..0f0f25e6def --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_mdev13831.result @@ -0,0 +1,29 @@ +include/master-slave.inc +[connection master] +*** MDEV-13831: Assertion on event group missing XID/COMMIT event +connection slave; +include/stop_slave.inc +CHANGE MASTER TO Master_use_gtid= No; +include/start_slave.inc +connection master; +SET @old_legacy= @@GLOBAL.binlog_legacy_event_pos; +SET GLOBAL binlog_legacy_event_pos= 1; +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1); +connection master1; +SET @old_dbug= @@SESSION.debug_dbug; +SET SESSION debug_dbug = '+d,fail_binlog_write_1'; +UPDATE t1 SET a = 2; +ERROR HY000: Error writing file 'master-bin' (errno: 28 "No space left on device") +SET debug_dbug= @old_dbug; +DROP TEMPORARY TABLE t1; +ERROR 42S02: Unknown table 'test.t1' +connection master; +CREATE TEMPORARY TABLE t1 (i INT) ENGINE=InnoDB; +connection slave; +connection master; +SET GLOBAL binlog_legacy_event_pos= @old_legacy; +CALL mtr.add_suppression("Error writing file.*No space left on device"); +DROP TEMPORARY TABLE t1; +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_mdev13831.test b/mysql-test/suite/rpl/t/rpl_mdev13831.test new file mode 100644 index 00000000000..5b86e21cda0 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_mdev13831.test @@ -0,0 +1,41 @@ +--source include/have_debug.inc +--source include/have_innodb.inc +--source include/have_binlog_format_mixed.inc +--source include/master-slave.inc + +--echo *** MDEV-13831: Assertion on event group missing XID/COMMIT event + +--connection slave +--source include/stop_slave.inc +# Use non-GTID mode. In GTID mode, the IO thread will fail if it sees an +# incomplete event group after MDEV-27697 patch. +CHANGE MASTER TO Master_use_gtid= No; +--source include/start_slave.inc + +--connection master +# The dbug injection below is only active in legacy mode. +SET @old_legacy= @@GLOBAL.binlog_legacy_event_pos; +SET GLOBAL binlog_legacy_event_pos= 1; +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1); + +--connection master1 +SET @old_dbug= @@SESSION.debug_dbug; +SET SESSION debug_dbug = '+d,fail_binlog_write_1'; +--error ER_ERROR_ON_WRITE +UPDATE t1 SET a = 2; +SET debug_dbug= @old_dbug; +--error ER_BAD_TABLE_ERROR +DROP TEMPORARY TABLE t1; + +--connection master +CREATE TEMPORARY TABLE t1 (i INT) ENGINE=InnoDB; + +--sync_slave_with_master + +--connection master +SET GLOBAL binlog_legacy_event_pos= @old_legacy; +CALL mtr.add_suppression("Error writing file.*No space left on device"); +DROP TEMPORARY TABLE t1; +DROP TABLE t1; +--source include/rpl_end.inc diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 84da9ab17aa..10cd47bbd62 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -3094,7 +3094,20 @@ static char gtid_begin_string[] = "BEGIN"; int Gtid_log_event::do_apply_event(rpl_group_info *rgi) { + Relay_log_info *rli= rgi->rli; ulonglong bits= thd->variables.option_bits; + + if (unlikely(thd->transaction->all.ha_list || (bits & OPTION_GTID_BEGIN))) + { + rli->report(WARNING_LEVEL, 0, NULL, + "Rolling back unfinished transaction (no COMMIT " + "or ROLLBACK in relay log). This indicates a corrupt binlog " + "on the master, possibly caused by disk full or other write " + "error."); + rgi->cleanup_context(thd, 1); + bits= thd->variables.option_bits; + } + thd->variables.server_id= this->server_id; thd->variables.gtid_domain_id= this->domain_id; thd->variables.gtid_seq_no= this->seq_no; @@ -3113,7 +3126,7 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi) DBUG_ASSERT((bits & OPTION_GTID_BEGIN) == 0); - Master_info *mi=rgi->rli->mi; + Master_info *mi= rli->mi; switch (flags2 & (FL_DDL | FL_TRANSACTIONAL)) { case FL_TRANSACTIONAL: -- 2.39.5
1 0
0 0
[PATCH] MDEV-5798: Wrong errorcode for missing partition after TRUNCATE PARTITION
by Kristian Nielsen 29 Nov '24

29 Nov '24
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 <knielsen(a)knielsen-hq.org> --- .../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 ed7fb7fe5f9..e94357f0d45 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -10140,7 +10140,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 -- 2.39.5
1 0
0 0
[PATCH] MDEV-11176: FTWRL confusing state about "worker thread pool"
by Kristian Nielsen 29 Nov '24

29 Nov '24
The FLUSH TABLE WITH READ LOCK briefly set the state (in PROCESSLIST) to "Waiting while replication worker thread pool is busy", even if there was nothing to wait for. This is somewhat confusing on a server that might not even have any replication configured, let alone replication workers. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- sql/rpl_parallel.cc | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index b5e1bb96c1e..4d3f12e4116 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -551,6 +551,7 @@ pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd) { PSI_stage_info old_stage; int res= 0; + bool did_enter_cond= false; /* Wait here while the queue is busy. This is done to make FLUSH TABLES WITH @@ -567,24 +568,28 @@ pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd) */ DBUG_EXECUTE_IF("mark_busy_mdev_22370",my_sleep(1000000);); mysql_mutex_lock(&pool->LOCK_rpl_thread_pool); - if (thd) - { - thd->set_time_for_next_stage(); - thd->ENTER_COND(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool, - &stage_waiting_for_rpl_thread_pool, &old_stage); - } - while (pool->busy) + if (pool->busy) { - if (thd && unlikely(thd->check_killed())) + if (thd) { - res= 1; - break; + thd->set_time_for_next_stage(); + thd->ENTER_COND(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool, + &stage_waiting_for_rpl_thread_pool, &old_stage); + did_enter_cond= true; } - mysql_cond_wait(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool); + do + { + if (thd && unlikely(thd->check_killed())) + { + res= 1; + break; + } + mysql_cond_wait(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool); + } while (pool->busy); } if (!res) pool->busy= true; - if (thd) + if (did_enter_cond) thd->EXIT_COND(&old_stage); else mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool); -- 2.39.5
1 0
0 0
[PATCH] MDEV-31756: WAIT/NOWAIT in DDL makes binary logs difficult or impossible to replay
by Kristian Nielsen 29 Nov '24

29 Nov '24
Remove any WAIT <n> or NOWAIT option from the query before binlogging DDL. Otherwise applying the event with mysqlbinlog | mysql may fail on NOWAIT if there is temporarily a lock on the table during the apply (while no such lock was there when the original query ran and was binlogged). Such locks can occur even without user control, since InnoDB background tasks can sometimes hold such locks for a short while. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- .../suite/binlog/r/binlog_ddl_nowait.result | 64 +++++++++++++++ .../suite/binlog/t/binlog_ddl_nowait.test | 77 +++++++++++++++++++ sql/sql_lex.cc | 26 +++++-- sql/sql_lex.h | 19 +++++ sql/sql_table.cc | 57 +++++++++++++- sql/sql_yacc.yy | 62 ++++++++++++--- 6 files changed, 286 insertions(+), 19 deletions(-) create mode 100644 mysql-test/suite/binlog/r/binlog_ddl_nowait.result create mode 100644 mysql-test/suite/binlog/t/binlog_ddl_nowait.test diff --git a/mysql-test/suite/binlog/r/binlog_ddl_nowait.result b/mysql-test/suite/binlog/r/binlog_ddl_nowait.result new file mode 100644 index 00000000000..86712fb533a --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_ddl_nowait.result @@ -0,0 +1,64 @@ +*** MDEV-31756: WAIT/NOWAIT in DDL makes binary logs difficult or impossible to replay +connection default; +RESET MASTER; +CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM; +ALTER TABLE t1 WAIT +0.0 ADD b INT; +ALTER TABLE t1 WAIT +4.3 ADD c INT; +INSERT INTO t1(a,b) VALUES (1,1), (2,1), (3,2), (4,3), (5, 5), (6, 8), (7, 13); +CREATE INDEX b_idx ON t1(b) WAIT 0; +OPTIMIZE TABLE t1 NOWAIT; +Table Op Msg_type Msg_text +test.t1 optimize status OK +TRUNCATE TABLE t1 NOWAIT; +DROP INDEX b_idx ON t1 WAIT 0; +RENAME TABLE t1 NOWAIT TO t2; +DROP TABLE t2 WAIT 1; +connection default; +FLUSH BINARY LOGS; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +connect con1,localhost,root,,; +INSERT INTO t1 VALUES (0*SLEEP(0.2)); +connection default; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +connection con1; +INSERT INTO t1(a) VALUES (0*SLEEP(0.2) + 100); +connection default; +Table Op Msg_type Msg_text +test.t1 optimize status OK +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b_idx` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +connection con1; +INSERT INTO t1(a) VALUES (0*SLEEP(0.2) + 200); +connection default; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci +connection con1; +disconnect con1; +connection default; +NOT FOUND /WAIT/ in mdev31756.text +DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/binlog_ddl_nowait.test b/mysql-test/suite/binlog/t/binlog_ddl_nowait.test new file mode 100644 index 00000000000..b2b5a4ec2b6 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_ddl_nowait.test @@ -0,0 +1,77 @@ +# DDL binlogging is the same regardless of binlog format +--source include/have_binlog_format_mixed.inc + +--echo *** MDEV-31756: WAIT/NOWAIT in DDL makes binary logs difficult or impossible to replay + +--connection default +--let $datadir= `select @@datadir` +RESET MASTER; + +--let $file= query_get_value(SHOW MASTER STATUS, File, 1) +--let $pos0= query_get_value(SHOW MASTER STATUS, Position, 1) +CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM; +--let $pos1= query_get_value(SHOW MASTER STATUS, Position, 1) +ALTER TABLE t1 WAIT +0.0 ADD b INT; +ALTER TABLE t1 WAIT +4.3 ADD c INT; +INSERT INTO t1(a,b) VALUES (1,1), (2,1), (3,2), (4,3), (5, 5), (6, 8), (7, 13); +--let $pos2= query_get_value(SHOW MASTER STATUS, Position, 1) +CREATE INDEX b_idx ON t1(b) WAIT 0; +OPTIMIZE TABLE t1 NOWAIT; +TRUNCATE TABLE t1 NOWAIT; +--let $pos3= query_get_value(SHOW MASTER STATUS, Position, 1) +DROP INDEX b_idx ON t1 WAIT 0; +--let $pos4= query_get_value(SHOW MASTER STATUS, Position, 1) +RENAME TABLE t1 NOWAIT TO t2; +DROP TABLE t2 WAIT 1; + + +# Test that we can mysqlbinlog|mysql the DDL even though it has to wait for +# the table lock for a short while. +# +# This is very prone to races of course, but that is ok. The case we want to +# test is when mysqbinlog|mysql runs while the table lock is held, and that +# will be the case "most" of the time. If we race and the lock is not held, +# it just means the test is ineffective, it will still pass. + +--connection default +FLUSH BINARY LOGS; +--exec $MYSQL_BINLOG --start-position=$pos0 --stop-position=$pos1 $datadir/master-bin.000001 | $MYSQL test +SHOW CREATE TABLE t1; + +connect (con1,localhost,root,,); +send INSERT INTO t1 VALUES (0*SLEEP(0.2)); + +--connection default +--exec $MYSQL_BINLOG --start-position=$pos1 --stop-position=$pos2 $datadir/master-bin.000001 | $MYSQL test +SHOW CREATE TABLE t1; + +--connection con1 +reap; +send INSERT INTO t1(a) VALUES (0*SLEEP(0.2) + 100); + +--connection default +--exec $MYSQL_BINLOG --start-position=$pos2 --stop-position=$pos3 $datadir/master-bin.000001 | $MYSQL test +SHOW CREATE TABLE t1; + +--connection con1 +reap; +send INSERT INTO t1(a) VALUES (0*SLEEP(0.2) + 200); + +--connection default +--exec $MYSQL_BINLOG --start-position=$pos3 --stop-position=$pos4 $datadir/master-bin.000001 | $MYSQL test +SHOW CREATE TABLE t1; + +--connection con1 +reap; +--disconnect con1 +--connection default + +# Check that there is no WAIT <n> or NOWAIT in binlogged queries. +--exec $MYSQL_BINLOG $datadir/master-bin.000001 >$MYSQL_TMP_DIR/mdev31756.text +--let SEARCH_FILE= $MYSQL_TMP_DIR/mdev31756.text +--let SEARCH_PATTERN= WAIT +--let SEARCH_ABORT= FOUND +--source include/search_pattern_in_file.inc +--remove_file $MYSQL_TMP_DIR/mdev31756.text + +DROP TABLE t1; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 0131068a530..75ec0d9dffe 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1557,6 +1557,19 @@ LEX_CSTRING Lex_input_stream::get_token(uint skip, uint length) } +Lex_string_with_pos_st +Lex_input_stream::get_token_with_pos(uint skip, uint length) +{ + LEX_CSTRING tmp; + Lex_string_with_pos_st out; + out.start_pos= m_tok_start + skip - get_buf(); + tmp= get_token(skip, length); + out.str= tmp.str; + out.length= tmp.length; + return out; +} + + static size_t my_unescape(CHARSET_INFO *cs, char *to, const char *str, const char *end, int sep, bool backslash_escapes) @@ -2193,7 +2206,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) if ((yyLength() >= 3) && !ident_map[c]) { /* skip '0x' */ - yylval->lex_str= get_token(2, yyLength() - 2); + yylval->lex_str_with_pos= get_token_with_pos(2, yyLength() - 2); return (HEX_NUM); } yyUnget(); @@ -2233,7 +2246,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) { yySkip(); while (my_isdigit(cs, yyGet())) ; - yylval->lex_str= get_token(0, yyLength()); + yylval->lex_str_with_pos= get_token_with_pos(0, yyLength()); return(FLOAT_NUM); } } @@ -2272,8 +2285,9 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) - the number is either not followed by a dot at all, or - the number is followed by a double dot as in: FOR i IN 1..10 */ - yylval->lex_str= get_token(0, yyLength()); - return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length); + yylval->lex_str_with_pos= get_token_with_pos(0, yyLength()); + return int_token(yylval->lex_str_with_pos.str, + (uint) yylval->lex_str_with_pos.length); } // fall through case MY_LEX_REAL: // Incomplete real number @@ -2287,10 +2301,10 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) if (!my_isdigit(cs, c)) return ABORT_SYM; // No digit after sign while (my_isdigit(cs, yyGet())) ; - yylval->lex_str= get_token(0, yyLength()); + yylval->lex_str_with_pos= get_token_with_pos(0, yyLength()); return(FLOAT_NUM); } - yylval->lex_str= get_token(0, yyLength()); + yylval->lex_str_with_pos= get_token_with_pos(0, yyLength()); return(DECIMAL_NUM); case MY_LEX_HEX_NUMBER: // Found x'hexstring' diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 780ad3d9e16..0c17480bf3b 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -103,6 +103,12 @@ struct Lex_string_with_metadata_st: public LEX_CSTRING }; +struct Lex_string_with_pos_st: public LEX_CSTRING +{ + uint start_pos; +}; + + /* Used to store identifiers in the client character set. Points to a query fragment. @@ -2863,6 +2869,7 @@ class Lex_input_stream int find_keyword(Lex_ident_cli_st *str, uint len, bool function) const; int find_keyword_qualified_special_func(Lex_ident_cli_st *str, uint len) const; LEX_CSTRING get_token(uint skip, uint length); + Lex_string_with_pos_st get_token_with_pos(uint skip, uint length); int scan_ident_start(THD *thd, Lex_ident_cli_st *str); int scan_ident_middle(THD *thd, Lex_ident_cli_st *str, CHARSET_INFO **cs, my_lex_states *); @@ -3540,6 +3547,12 @@ struct LEX: public Query_tables_list */ uint table_count_update; + /* + Used to remember a character position during parsing, eg. the end of + ulong_num for ddl_wait_nowait_end_offset. + */ + uint last_lex_end_pos; + uint8 describe; /* A flag that indicates what kinds of derived tables are present in the @@ -3585,15 +3598,21 @@ struct LEX: public Query_tables_list keyword_delayed_begin_offset is the offset to the beginning of the DELAYED keyword in INSERT DELAYED statement. keyword_delayed_end_offset is the offset to the character right after the DELAYED keyword. + + Similarly, ddl_wait_nowait_begin_offset and ddl_wait_nowait_end_offset mark + the start and end of the "NOWAIT" or "WAIT <number>" (including last + character of <number>) option for ALTER TABLE and similar statements. */ union { const char *stmt_definition_begin; uint keyword_delayed_begin_offset; + uint ddl_wait_nowait_begin_offset; }; union { const char *stmt_definition_end; uint keyword_delayed_end_offset; + uint ddl_wait_nowait_end_offset; }; /** diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 748ae999087..a007f7689e6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -996,6 +996,23 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) } +/** + Create a new query string for DDL with the WAIT <n> or NOWAIT option removed. + This option should not be in the binlog, as a query that succeeded on the + master _must_ also succeed on the slave, even if it needs to wait. +*/ +static int +create_stmt_without_nowait(THD *thd, String *buf) +{ + if (buf->append(thd->query(), thd->query_length()) || + buf->replace(thd->lex->ddl_wait_nowait_begin_offset, + thd->lex->ddl_wait_nowait_end_offset - + thd->lex->ddl_wait_nowait_begin_offset, NULL, 0)) + return 1; + return 0; +} + + /* SYNOPSIS write_bin_log() @@ -1035,9 +1052,43 @@ int write_bin_log(THD *thd, bool clear_error, } else errcode= query_error_code(thd, TRUE); - error= thd->binlog_query(THD::STMT_QUERY_TYPE, - query, query_length, is_trans, FALSE, FALSE, - errcode) > 0; + + /* Remove any NOWAIT or WAIT <n> from DDL. */ + String log_query; + switch(thd->lex->sql_command) + { + case SQLCOM_TRUNCATE: + case SQLCOM_DROP_INDEX: + case SQLCOM_RENAME_TABLE: + case SQLCOM_OPTIMIZE: + case SQLCOM_ALTER_TABLE: + case SQLCOM_CREATE_INDEX: + /* DROP TABLE is binlogged with a rewritten query, so omitted here. */ + if (thd->lex->ddl_wait_nowait_begin_offset > 0) + { + if (create_stmt_without_nowait(thd, &log_query)) + { + sql_print_error("Event Error: An error occurred while creating query " + "string for DDL with NOWAIT/WAIT removed, before " + "writing it into binary log."); + error= 1; + } + else + { + query= log_query.c_ptr(); + query_length= log_query.length(); + } + } + break; + + default: + ; /* Nothing */ + } + + if (!error) + error= thd->binlog_query(THD::STMT_QUERY_TYPE, + query, query_length, is_trans, FALSE, FALSE, + errcode) > 0; thd_proc_info(thd, 0); } return error; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 85717713325..75be19cb4c8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -201,6 +201,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)() /* structs */ LEX_CSTRING lex_str; + Lex_string_with_pos_st lex_str_with_pos; Lex_ident_cli_st kwd; Lex_ident_cli_st ident_cli; Lex_ident_sys_st ident_sys; @@ -1316,9 +1317,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %right INTO %type <lex_str> - DECIMAL_NUM FLOAT_NUM NUM LONG_NUM - HEX_NUM HEX_STRING - LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text + HEX_STRING + LEX_HOSTNAME field_ident select_alias ident_or_text TEXT_STRING_sys TEXT_STRING_literal key_cache_name sp_opt_label BIN_NUM TEXT_STRING_filesystem @@ -1326,6 +1326,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); sp_block_label sp_control_label opt_place opt_db udt_name +%type <lex_str_with_pos> + DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM ULONGLONG_NUM + %type <ident_sys> IDENT_sys ident_func @@ -12747,12 +12750,41 @@ int_num: ; ulong_num: - opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } - | HEX_NUM { $$= strtoul($1.str, (char**) 0, 16); } - | opt_plus LONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } - | opt_plus ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } - | opt_plus DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } - | opt_plus FLOAT_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + opt_plus NUM + { + int error; + $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); + Lex->last_lex_end_pos= $2.start_pos + (uint)$2.length; + } + | HEX_NUM + { + $$= strtoul($1.str, (char**) 0, 16); + Lex->last_lex_end_pos= $1.start_pos + (uint)$1.length; + } + | opt_plus LONG_NUM + { + int error; + $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); + Lex->last_lex_end_pos= $2.start_pos + (uint)$2.length; + } + | opt_plus ULONGLONG_NUM + { + int error; + $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); + Lex->last_lex_end_pos= $2.start_pos + (uint)$2.length; + } + | opt_plus DECIMAL_NUM + { + int error; + $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); + Lex->last_lex_end_pos= $2.start_pos + (uint)$2.length; + } + | opt_plus FLOAT_NUM + { + int error; + $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); + Lex->last_lex_end_pos= $2.start_pos + (uint)$2.length; + } ; real_ulong_num: @@ -17116,15 +17148,25 @@ lock: opt_lock_wait_timeout: /* empty */ - {} + { + LEX *lex= Lex; + lex->ddl_wait_nowait_begin_offset= 0; + lex->ddl_wait_nowait_end_offset= 0; + } | WAIT_SYM ulong_num { + LEX *lex= Lex; + lex->ddl_wait_nowait_begin_offset= (uint)($1.pos() - thd->query()); + lex->ddl_wait_nowait_end_offset= lex->last_lex_end_pos; if (unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), $2)) || unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), $2))) MYSQL_YYABORT; } | NOWAIT_SYM { + LEX *lex= Lex; + lex->ddl_wait_nowait_begin_offset= (uint)($1.pos() - thd->query()); + lex->ddl_wait_nowait_end_offset= (uint)($1.end() - thd->query()); if (unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), 0)) || unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), 0))) MYSQL_YYABORT; -- 2.39.5
1 0
0 0
[PATCH] MDEV-31761: mariadb-binlog prints fractional timestamp part incorrectly
by Kristian Nielsen 28 Nov '24

28 Nov '24
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 <knielsen(a)knielsen-hq.org> --- mysql-test/main/mysqlbinlog.result | 26 ++++++++++++++++++++++++ mysql-test/main/mysqlbinlog.test | 32 ++++++++++++++++++++++++++++++ sql/log_event_client.cc | 7 +++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/mysqlbinlog.result b/mysql-test/main/mysqlbinlog.result index c6d9ef97229..76ded3d31f8 100644 --- a/mysql-test/main/mysqlbinlog.result +++ b/mysql-test/main/mysqlbinlog.result @@ -1286,3 +1286,29 @@ ERROR: Bad syntax in rewrite-db: empty FROM db ERROR: Bad syntax in rewrite-db: empty FROM db +# +# MDEV-31761: Timestamp is written into binary log incorrectly +# +SET SESSION binlog_format= MIXED; +RESET MASTER; +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; diff --git a/mysql-test/main/mysqlbinlog.test b/mysql-test/main/mysqlbinlog.test index 22a85393a35..1747d55d29d 100644 --- a/mysql-test/main/mysqlbinlog.test +++ b/mysql-test/main/mysqlbinlog.test @@ -637,3 +637,35 @@ 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: Timestamp is written into binary log incorrectly +--echo # + +SET SESSION binlog_format= MIXED; + +RESET MASTER; +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; 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'; -- 2.39.5
1 0
0 0
[PATCH] Restore the THD state correctly in parallel replication
by Kristian Nielsen 28 Nov '24

28 Nov '24
If both do_gco_wait() and do_ftwrl_wait() had to wait, the state was not restored correctly. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- 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 e59c1de7a7c..553557638f6 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 { -- 2.39.5
1 0
0 0
[PATCH] MDEV-34049: Parallel access to temptable in different domain_id in parallel replication
by Kristian Nielsen 25 Oct '24

25 Oct '24
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. 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. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- .../suite/rpl/r/rpl_gtid_errorhandling.result | 36 ++++++++++++++++--- .../suite/rpl/t/rpl_gtid_errorhandling.test | 31 ++++++++++++++++ sql/sys_vars.cc | 27 ++++++++++++-- 3 files changed, 87 insertions(+), 7 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..e3ca6df1a6e 100644 --- a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test @@ -68,6 +68,37 @@ 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 +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 115bbdf499b..2ef1e404f85 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1808,12 +1808,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; } -- 2.39.5
1 0
0 0
[PATCH] MDEV-29744: Fix incorrect locking order of LOCK_log/LOCK_commit_ordered and LOCK_global_system_variables
by Kristian Nielsen 25 Oct '24

25 Oct '24
The LOCK_global_system_variables must not be held when taking mutexes such as LOCK_commit_ordered and LOCK_log, as this causes inconsistent mutex locking order that can theoretically cause the server to deadlock. To avoid this, temporarily release LOCK_global_system_variables in two system variable update functions, like it is done in many other places. Enforce the correct locking order at server startup, to more easily catch (in debug builds) any remaining wrong orders that may be hidden elsewhere in the code. Note that when this is merged to 11.4, similar unlock/lock of LOCK_global_system_variables must be added in update_binlog_space_limit() as is done in binlog_checksum_update() and fix_max_binlog_size(), as this is a new function added in 11.4 that also needs the same fix. Tests will fail with wrong mutex order until this is done. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> fixup_locking_order Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- sql/log.cc | 5 +++++ sql/sys_vars.cc | 2 ++ 2 files changed, 7 insertions(+) diff --git a/sql/log.cc b/sql/log.cc index 512be2e2a6d..ecb177a34fb 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3595,6 +3595,9 @@ void MYSQL_BIN_LOG::init_pthread_objects() mysql_mutex_init(m_key_LOCK_binlog_end_pos, &LOCK_binlog_end_pos, MY_MUTEX_INIT_SLOW); + + /* Fix correct mutex order to catch violations quicker (MDEV-35197). */ + mysql_mutex_record_order(&LOCK_log, &LOCK_global_system_variables); } @@ -11753,6 +11756,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var, bool check_purge= false; ulong UNINIT_VAR(prev_binlog_id); + mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_lock(mysql_bin_log.get_log_lock()); if(mysql_bin_log.is_open()) { @@ -11771,6 +11775,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var, mysql_mutex_unlock(mysql_bin_log.get_log_lock()); if (check_purge) mysql_bin_log.checkpoint_and_purge(prev_binlog_id); + mysql_mutex_lock(&LOCK_global_system_variables); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index cb35386f883..115bbdf499b 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1660,7 +1660,9 @@ Sys_max_binlog_stmt_cache_size( static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type) { + mysql_mutex_unlock(&LOCK_global_system_variables); mysql_bin_log.set_max_size(max_binlog_size); + mysql_mutex_lock(&LOCK_global_system_variables); return false; } static Sys_var_on_access_global<Sys_var_ulong, -- 2.39.5
1 0
0 0
[PATCH] MDEV-35197: Fix incorrect locking order of LOCK_log/LOCK_commit_ordered and LOCK_global_system_variables
by Kristian Nielsen 22 Oct '24

22 Oct '24
The LOCK_global_system_variables must not be held when taking mutexes such as LOCK_commit_ordered and LOCK_log, as this causes inconsistent mutex locking order that can theoretically cause the server to deadlock. To avoid this, temporarily release LOCK_global_system_variables in two system variable update functions, like it is done in many other places. Enforce the correct locking order at server startup, to more easily catch (in debug builds) any remaining wrong orders that may be hidden elsewhere in the code. Signed-off-by: Kristian Nielsen <knielsen(a)knielsen-hq.org> --- sql/log.cc | 8 ++++++++ sql/sys_vars.cc | 2 ++ 2 files changed, 10 insertions(+) diff --git a/sql/log.cc b/sql/log.cc index 512be2e2a6d..70fe33f9272 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -3595,6 +3595,12 @@ void MYSQL_BIN_LOG::init_pthread_objects() mysql_mutex_init(m_key_LOCK_binlog_end_pos, &LOCK_binlog_end_pos, MY_MUTEX_INIT_SLOW); + + /* Fix correct mutex order to catch violations quicker (MDEV-35197). */ + mysql_mutex_lock(&LOCK_log); + mysql_mutex_lock(&LOCK_global_system_variables); + mysql_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_unlock(&LOCK_log); } @@ -11753,6 +11759,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var, bool check_purge= false; ulong UNINIT_VAR(prev_binlog_id); + mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_lock(mysql_bin_log.get_log_lock()); if(mysql_bin_log.is_open()) { @@ -11771,6 +11778,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var, mysql_mutex_unlock(mysql_bin_log.get_log_lock()); if (check_purge) mysql_bin_log.checkpoint_and_purge(prev_binlog_id); + mysql_mutex_lock(&LOCK_global_system_variables); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index cb35386f883..115bbdf499b 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1660,7 +1660,9 @@ Sys_max_binlog_stmt_cache_size( static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type) { + mysql_mutex_unlock(&LOCK_global_system_variables); mysql_bin_log.set_max_size(max_binlog_size); + mysql_mutex_lock(&LOCK_global_system_variables); return false; } static Sys_var_on_access_global<Sys_var_ulong, -- 2.39.2
1 0
0 0
  • ← Newer
  • 1
  • 2
  • 3
  • 4
  • 5
  • ...
  • 1461
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.