Skip to content

Commit 42af3f2

Browse files
committed
MDEV-36484 Atomic DDL: Assertion `!param->got_error' failed upon unsuccessful multi-RENAME TABLE
When executing an atomic sequence of RENAME operations, such as:     RENAME TABLE t1 TO t2, t3 TO t4, ... any failure in the sequence triggers a rollback of previously completed renames to preserve atomicity. However, when an error occurs, `my_error()` is invoked immediately, which sets the `thd->is_error()` flag. This premature flag setting causes the rollback logic to misinterpret the thread state, leading to incorrect reversion behavior and assertion failures. To address this, the error is now captured and deferred — stored during the failure and only passed to `my_error()` after the DDL reversion is complete. This ensures the error state is not prematurely set, allowing the rollback logic to execute safely and correctly.
1 parent 127f28a commit 42af3f2

File tree

5 files changed

+192
-57
lines changed

5 files changed

+192
-57
lines changed

include/my_sys.h

+9
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,13 @@ typedef struct st_io_cache /* Used when caching files */
502502
size_t alloced_buffer;
503503
} IO_CACHE;
504504

505+
typedef struct Prepared_error
506+
{
507+
uint code;
508+
char message[MYSYS_ERRMSG_SIZE];
509+
myf flags;
510+
} MY_PREPARED_ERROR;
511+
505512
typedef void (*my_error_reporter)(enum loglevel level, const char *format, ...)
506513
ATTRIBUTE_FORMAT_FPTR(printf, 2, 3);
507514

@@ -743,6 +750,8 @@ extern int my_error_register(const char** (*get_errmsgs) (int nr),
743750
extern my_bool my_error_unregister(uint first, uint last);
744751
extern void my_message(uint my_err, const char *str,myf MyFlags);
745752
extern void my_message_stderr(uint my_err, const char *str, myf MyFlags);
753+
extern struct Prepared_error my_error_prepare(uint nr, myf MyFlags, ...);
754+
extern void my_error_issue(struct Prepared_error* error);
746755
extern my_bool my_init(void);
747756
extern void my_end(int infoflag);
748757
extern int my_redel(const char *from, const char *to, time_t backup_time_stamp,

mysql-test/main/rename.result

+16
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,19 @@ drop table t2;
175175
rename table if exists t1 to t2;
176176
alter table if exists t2 rename to t1;
177177
drop table t1;
178+
#
179+
# MDEV-36484 Atomic DDL: Assertion `!param->got_error' failed upon
180+
# unsuccessful multi-RENAME TABLE
181+
#
182+
CREATE TABLE t1 (a INT);
183+
CREATE TRIGGER t1 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t1 VALUES (0);
184+
RENAME TABLE t1 TO t2, t3 TO t4;
185+
ERROR 42S02: Table 'test.t3' doesn't exist
186+
SHOW CREATE TRIGGER t1;
187+
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
188+
t1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1 AFTER INSERT ON `t1` FOR EACH ROW INSERT INTO t1 VALUES (0) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
189+
RENAME TABLE t1 TO t2;
190+
SHOW CREATE TRIGGER t1;
191+
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
192+
t1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1 AFTER INSERT ON `t2` FOR EACH ROW INSERT INTO t1 VALUES (0) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci #
193+
DROP TABLE t2;

mysql-test/main/rename.test

+18
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,21 @@ drop table t2;
166166
rename table if exists t1 to t2;
167167
alter table if exists t2 rename to t1;
168168
drop table t1;
169+
170+
171+
--echo #
172+
--echo # MDEV-36484 Atomic DDL: Assertion `!param->got_error' failed upon
173+
--echo # unsuccessful multi-RENAME TABLE
174+
--echo #
175+
CREATE TABLE t1 (a INT);
176+
CREATE TRIGGER t1 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t1 VALUES (0);
177+
--error ER_NO_SUCH_TABLE
178+
RENAME TABLE t1 TO t2, t3 TO t4;
179+
180+
--replace_column 7 #
181+
SHOW CREATE TRIGGER t1;
182+
RENAME TABLE t1 TO t2;
183+
184+
--replace_column 7 #
185+
SHOW CREATE TRIGGER t1;
186+
DROP TABLE t2;

mysys/my_error.c

+62-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <m_string.h>
1919
#include <stdarg.h>
2020
#include <m_ctype.h>
21+
#include <my_sys.h>
2122

2223
/* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */
2324
#define ERRMSGSIZE (512)
@@ -93,6 +94,62 @@ const char *my_get_err_msg(uint nr)
9394
return format;
9495
}
9596

97+
/**
98+
Internal method used to format and prepare the error structure.
99+
Called by my_error_prepare() and my_error().
100+
*/
101+
static struct Prepared_error my_error_format(uint nr, myf MyFlags, va_list args)
102+
{
103+
const char *format;
104+
struct Prepared_error error;
105+
error.code= nr;
106+
error.flags= MyFlags;
107+
108+
if (!(format = my_get_err_msg(nr)))
109+
(void) my_snprintf(error.message, sizeof(error.message),
110+
"Unknown error %d", nr);
111+
else
112+
{
113+
(void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci,
114+
error.message, sizeof(error.message), format, args);
115+
}
116+
return error;
117+
}
118+
119+
/**
120+
Prepare the error message for deferred printing with my_error_issue()
121+
122+
@param nr error number
123+
@param MyFlags Flags
124+
@param ... variable list matching that error format string
125+
126+
@retval error prepared structure containg error code, formatted message
127+
and flags
128+
*/
129+
130+
struct Prepared_error my_error_prepare(uint nr, myf MyFlags, ...)
131+
{
132+
struct Prepared_error error;
133+
va_list args;
134+
135+
va_start(args,MyFlags);
136+
error= my_error_format(nr, MyFlags, args);
137+
va_end(args);
138+
139+
return error;
140+
}
141+
142+
143+
/**
144+
Print a previously prepared error (see my_error_prepare())
145+
146+
* @param error error structure prepared for deferred printing@retval
147+
*/
148+
void my_error_issue(struct Prepared_error* error)
149+
{
150+
(*error_handler_hook)(error->code, error->message, error->flags);
151+
}
152+
96153

97154
/**
98155
Fill in and print a previously registered error message.
@@ -107,21 +164,14 @@ const char *my_get_err_msg(uint nr)
107164

108165
void my_error(uint nr, myf MyFlags, ...)
109166
{
110-
const char *format;
167+
struct Prepared_error error;
111168
va_list args;
112-
char ebuff[ERRMSGSIZE];
113169
DBUG_ENTER("my_error");
114170
DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d", nr, MyFlags, errno));
115-
if (!(format = my_get_err_msg(nr)))
116-
(void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr);
117-
else
118-
{
119-
va_start(args,MyFlags);
120-
(void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff,
121-
sizeof(ebuff), format, args);
122-
va_end(args);
123-
}
124-
(*error_handler_hook)(nr, ebuff, MyFlags);
171+
va_start(args,MyFlags);
172+
error= my_error_format(nr, MyFlags, args);
173+
my_error_issue(&error);
174+
va_end(args);
125175
DBUG_VOID_RETURN;
126176
}
127177

0 commit comments

Comments
 (0)