1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-28750 : Assertion `trans_safe || !updated || thd->transaction->stmt.modified_non_trans_table' failed in virtual bool multi_update::send_eof()

Problem was that in multi-table update that contains tables that are not
transactional and tables that are transactional error handling does not
work correctly if write set size is limited. Because, Galera
replication for MyISAM is experimental it is better to avoid
multi-table updates in this case.

Fixed by not allowing multi-table updates if statement contains
non transactional and transactional tables and write set size
is limited.
This commit is contained in:
Jan Lindström
2026-03-23 09:43:00 +02:00
committed by Jan Lindström
parent e78756fe20
commit 1f34996880
8 changed files with 282 additions and 2 deletions

View File

@@ -0,0 +1,112 @@
connection node_2;
connection node_1;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1 SET t1.a=t1.a * 2;
UPDATE ti AS t1 SET t1.a=t1.a * 2;
UPDATE tm AS t1 SET t1.a=t1.a * 2;
UPDATE ti AS t1 SET t1.a=t1.a * 2;
ERROR HY000: Some non-transactional changed tables couldn't be rolled back
SHOW WARNINGS;
Level Code Message
Error 1196 Some non-transactional changed tables couldn't be rolled back
Error 1180 wsrep_max_ws_rows exceeded
Error 1030 Got error 1180 "Unknown error 1180" from storage engine InnoDB
COMMIT;
SELECT * FROM ti;
a b id
SELECT * from tm;
a b
8 NULL
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
SET sql_mode=only_full_group_by;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
ERROR HY000: Galera replication not supported
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
ERROR HY000: Galera replication not supported
COMMIT;
SELECT * FROM ti;
a b id
1 2 4
SELECT * from tm;
a b
2 NULL
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
SET sql_mode=only_full_group_by;
INSERT INTO ti VALUES (1,2,4);
SET GLOBAL wsrep_max_ws_rows=2;
ERROR HY000: WSREP transaction is already active
set GLOBAL wsrep_max_ws_size=2047;
ERROR HY000: WSREP transaction is already active
COMMIT;
SELECT * FROM ti;
a b id
1 2 4
SELECT * from tm;
a b
2 NULL
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=InnoDB;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
ERROR HY000: wsrep_max_ws_rows exceeded
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
COMMIT;
SELECT * FROM ti;
a b id
SELECT * from tm;
a b
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=0;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
COMMIT;
SELECT * FROM ti;
a b id
4 2 4
SELECT * from tm;
a b
8 NULL
DROP TABLE ti,tm;
CREATE TABLE t1 (col INT);
INSERT INTO t1 VALUES (0xF4AB);
CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=mrg_myisam UNION=(t1) insert_method=FIRST;
SET GLOBAL wsrep_max_ws_rows=1;
DROP TABLES t1;
INSERT INTO t1 VALUES (6373);
CREATE TABLE t2 ENGINE=heap SELECT * FROM t1;
DROP TABLE t1, t2;
SET default_storage_engine="HEAP";
CREATE TABLE t1 (f1 BIGINT);
SET GLOBAL wsrep_max_ws_rows = 1;
INSERT INTO t1 VALUES (NOW()),(NOW()),(NOW());
SELECT COUNT(*) AS EXPECT_3 FROM t1;
EXPECT_3
3
DROP TABLE t1;
CREATE TABLE t1 (i INT) ENGINE=MyISAM DEFAULT CHARSET=utf8 SELECT 1 as i;
DROP TABLE t1;
SET GLOBAL wsrep_max_ws_rows=0;

View File

@@ -125,6 +125,7 @@ CREATE TABLE t1(c1 INT)ENGINE = INNODB;
SET GLOBAL wsrep_max_ws_rows= DEFAULT;
INSERT INTO t1 VALUES(1);
INSERT INTO t1 SELECT * FROM t1;
COMMIT;
SET GLOBAL wsrep_max_ws_rows= 1;
ALTER TABLE t1 CHANGE COLUMN c1 c1 BIGINT;
connection node_2;

View File

@@ -0,0 +1,104 @@
--source include/galera_cluster.inc
#
# MDEV-28750 Assertion `trans_safe || !updated || thd->transaction->stmt.modified_non_trans_table' failed in virtual bool multi_update::send_eof()
#
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1 SET t1.a=t1.a * 2;
UPDATE ti AS t1 SET t1.a=t1.a * 2;
UPDATE tm AS t1 SET t1.a=t1.a * 2;
--error ER_WARNING_NOT_COMPLETE_ROLLBACK
UPDATE ti AS t1 SET t1.a=t1.a * 2;
SHOW WARNINGS;
COMMIT;
SELECT * FROM ti;
SELECT * from tm;
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
SET sql_mode=only_full_group_by;
INSERT INTO ti VALUES (1,2,4);
--error ER_GALERA_REPLICATION_NOT_SUPPORTED
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
--error ER_GALERA_REPLICATION_NOT_SUPPORTED
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
COMMIT;
SELECT * FROM ti;
SELECT * from tm;
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
SET sql_mode=only_full_group_by;
INSERT INTO ti VALUES (1,2,4);
--error ER_WRONG_ARGUMENTS
SET GLOBAL wsrep_max_ws_rows=2;
--error ER_WRONG_ARGUMENTS
set GLOBAL wsrep_max_ws_size=2047;
COMMIT;
SELECT * FROM ti;
SELECT * from tm;
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=InnoDB;
SET GLOBAL wsrep_max_ws_rows=2;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
--error ER_ERROR_DURING_COMMIT
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
COMMIT;
SELECT * FROM ti;
SELECT * from tm;
DROP TABLE ti,tm;
CREATE TABLE ti (a INT UNSIGNED, b SMALLINT, id BIGINT NOT NULL, KEY(b), PRIMARY KEY(id)) ENGINE=InnoDB;
CREATE TABLE tm (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=MyISAM;
SET GLOBAL wsrep_max_ws_rows=0;
START TRANSACTION;
INSERT INTO tm SET b=NULL, a=2;
INSERT INTO ti VALUES (1,2,4);
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
UPDATE tm AS t1, ti AS t2 SET t1.a=t1.a * 2, t2.a=t2.a * 2;
COMMIT;
SELECT * FROM ti;
SELECT * from tm;
DROP TABLE ti,tm;
#
# MDEV-25548 Assertion `transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table' failed.
#
CREATE TABLE t1 (col INT);
INSERT INTO t1 VALUES (0xF4AB);
CREATE TEMPORARY TABLE t1 (c1 INT) ENGINE=mrg_myisam UNION=(t1) insert_method=FIRST;
SET GLOBAL wsrep_max_ws_rows=1;
DROP TABLES t1;
INSERT INTO t1 VALUES (6373);
CREATE TABLE t2 ENGINE=heap SELECT * FROM t1;
DROP TABLE t1, t2;
SET default_storage_engine="HEAP";
CREATE TABLE t1 (f1 BIGINT);
SET GLOBAL wsrep_max_ws_rows = 1;
INSERT INTO t1 VALUES (NOW()),(NOW()),(NOW());
SELECT COUNT(*) AS EXPECT_3 FROM t1;
DROP TABLE t1;
CREATE TABLE t1 (i INT) ENGINE=MyISAM DEFAULT CHARSET=utf8 SELECT 1 as i;
DROP TABLE t1;
SET GLOBAL wsrep_max_ws_rows=0;

View File

@@ -159,6 +159,7 @@ CREATE TABLE t1(c1 INT)ENGINE = INNODB;
SET GLOBAL wsrep_max_ws_rows= DEFAULT;
INSERT INTO t1 VALUES(1);
INSERT INTO t1 SELECT * FROM t1;
COMMIT;
SET GLOBAL wsrep_max_ws_rows= 1;
ALTER TABLE t1 CHANGE COLUMN c1 c1 BIGINT;

View File

@@ -46,6 +46,11 @@
#include "sql_insert.h" // For vers_insert_history_row() that may be
// needed for System Versioning.
#ifdef WITH_WSREP
#include "wsrep_mysqld.h" // wsrep_max_ws_rows, wsrep_max_ws_size
#include "wsrep_binlog.h" // WSREP_MAX_WS_SIZE
#endif
/**
True if the table's input and output record buffers are comparable using
compare_record(TABLE*).
@@ -2029,6 +2034,36 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
if (select_lex->vers_setup_conds(thd, table_list))
DBUG_RETURN(1);
#ifdef WITH_WSREP
if (WSREP(thd))
{
bool transactional= false;
bool non_trans= false;
for (TABLE_LIST *tablel= table_list; tablel; tablel= tablel->next_global)
{
TABLE *table= tablel->table;
if (table->file->has_transactions_and_rollback())
transactional= true;
else
non_trans= true;
}
/* In multi-table update Galera does not support update to both
transactional and non-transactional engines if write-set
size is limited. */
bool limited = (wsrep_max_ws_rows || wsrep_max_ws_size != WSREP_MAX_WS_SIZE);
if (transactional && non_trans && limited)
{
my_error(ER_GALERA_REPLICATION_NOT_SUPPORTED, MYF(0));
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GALERA_REPLICATION_NOT_SUPPORTED,
"Galera does not support multi-table update",
" to both transactional and non-transactional engines"
" if write-set size is limited.");
DBUG_RETURN(1);
}
}
#endif /* WITH_WSREP */
res= mysql_select(thd,
table_list, total_list, conds,
select_lex->order_list.elements,

View File

@@ -6215,7 +6215,7 @@ static Sys_var_charptr Sys_wsrep_start_position (
CMD_LINE(REQUIRED_ARG),
DEFAULT(WSREP_START_POSITION_ZERO),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_start_position_check),
ON_CHECK(wsrep_start_position_check),
ON_UPDATE(wsrep_start_position_update));
static Sys_var_ulong Sys_wsrep_max_ws_size (
@@ -6228,7 +6228,9 @@ static Sys_var_ulong Sys_wsrep_max_ws_size (
static Sys_var_ulong Sys_wsrep_max_ws_rows (
"wsrep_max_ws_rows", "Max number of rows in write set",
GLOBAL_VAR(wsrep_max_ws_rows), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1048576), DEFAULT(0), BLOCK_SIZE(1));
VALID_RANGE(0, 1048576), DEFAULT(0),
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_max_ws_rows_check), ON_UPDATE(0));
static Sys_var_charptr Sys_wsrep_notify_cmd(
"wsrep_notify_cmd", "",

View File

@@ -1,4 +1,5 @@
/* Copyright 2008-2025 Codership Oy <http://www.codership.com>
Copyright 2025-2026 MariaDB plc <http://www.mariadb.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1005,6 +1006,11 @@ bool wsrep_max_ws_size_check(sys_var *self, THD* thd, set_var* var)
my_message(ER_WRONG_ARGUMENTS, "WSREP (galera) not started", MYF(0));
return true;
}
if (thd->wsrep_trx().active())
{
my_message(ER_WRONG_ARGUMENTS, "WSREP transaction is active", MYF(0));
return true;
}
return false;
}
@@ -1231,3 +1237,20 @@ bool wsrep_slave_threads_check (sys_var *self, THD* thd, set_var* var)
return false;
}
bool wsrep_max_ws_rows_check(sys_var *self, THD* thd, set_var* var)
{
unsigned long long max_rows= (unsigned long long)var->save_result.ulonglong_value;
// Default 0 is always allowed
if (max_rows == 0)
return false;
// Note that we allow changing this even when WSREP is not on
if (thd->wsrep_trx().active())
{
my_message(ER_WRONG_ARGUMENTS, "WSREP transaction is active", MYF(0));
return true;
}
return false;
}

View File

@@ -1,4 +1,5 @@
/* Copyright (C) 2013-2025 Codership Oy <info@codership.com>
Copyright (C) 2025-2026 MariaDB plc <http://www.mariadb.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -101,6 +102,7 @@ extern bool wsrep_trx_fragment_unit_update UPDATE_ARGS;
extern bool wsrep_max_ws_size_check CHECK_ARGS;
extern bool wsrep_max_ws_size_update UPDATE_ARGS;
extern bool wsrep_max_ws_rows_check CHECK_ARGS;
extern bool wsrep_reject_queries_update UPDATE_ARGS;