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 <knielsen@knielsen-hq.org>
This commit is contained in:
Kristian Nielsen 2024-12-03 15:02:06 +01:00
parent b4fde50b1f
commit 2ab10fbec2
4 changed files with 82 additions and 4 deletions

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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;
}