Commit bcd47f41 authored by 's avatar

Bug #56662 Assertion failed: next_insert_id == 0, file .\handler.cc

      
Normally, auto_increment value is generated for the column by
inserting either NULL or 0 into it. NO_AUTO_VALUE_ON_ZERO
suppresses this behavior for 0 so that only NULL generates
the auto_increment value. This behavior is also followed by
a slave, specifically by the SQL Thread, when applying events
in the statement format from a master. However, when applying
events in the row format, the flag was ignored thus causing
an assertion failure:
"Assertion failed: next_insert_id == 0, file .\handler.cc"
      
In fact, we never need to generate a auto_increment value for
the column when applying events in row format on slave. So we
don't allow it to happen by using 'MODE_NO_AUTO_VALUE_ON_ZERO'.
      
Refactoring: Get rid of all the sql_mode checks to rows_log_event
when applying it for avoiding problems caused by the inconsistency
of the sql_mode on slave and master as the sql_mode is not set for
Rows_log_event.
parents 9d3b24db 16ca2deb
......@@ -229,5 +229,31 @@ source include/diff_tables.inc;
DROP TABLE t1;
DROP TABLE t2;
SET SQL_MODE='';
sync_slave_with_master;
#
# BUG#56662
# The test verifies if the assertion of "next_insert_id == 0"
# will fail in ha_external_lock() function.
#
connection master;
CREATE TABLE t1 (id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, data INT) ENGINE=innodb;
BEGIN;
--echo # Set sql_mode with NO_AUTO_VALUE_ON_ZERO for allowing
--echo # zero to fill the auto_increment field.
SET SQL_MODE=NO_AUTO_VALUE_ON_ZERO;
INSERT INTO t1(id,data) VALUES(0,2);
--echo # Resetting sql_mode without NO_AUTO_VALUE_ON_ZERO to
--echo # affect the execution of the transaction on slave.
SET SQL_MODE=0;
COMMIT;
SELECT * FROM t1;
sync_slave_with_master;
SELECT * FROM t1;
connection master;
DROP TABLE t1;
sync_slave_with_master;
--source include/rpl_end.inc
......@@ -303,4 +303,21 @@ include/diff_tables.inc [master:t2, slave:t2]
DROP TABLE t1;
DROP TABLE t2;
SET SQL_MODE='';
CREATE TABLE t1 (id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, data INT) ENGINE=innodb;
BEGIN;
# Set sql_mode with NO_AUTO_VALUE_ON_ZERO for allowing
# zero to fill the auto_increment field.
SET SQL_MODE=NO_AUTO_VALUE_ON_ZERO;
INSERT INTO t1(id,data) VALUES(0,2);
# Resetting sql_mode without NO_AUTO_VALUE_ON_ZERO to
# affect the execution of the transaction on slave.
SET SQL_MODE=0;
COMMIT;
SELECT * FROM t1;
id data
0 2
SELECT * FROM t1;
id data
0 2
DROP TABLE t1;
include/rpl_end.inc
......@@ -7740,6 +7740,14 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
// Do event specific preparations
error= do_before_row_operations(rli);
/*
Bug#56662 Assertion failed: next_insert_id == 0, file handler.cc
Don't allow generation of auto_increment value when processing
rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'.
*/
ulong saved_sql_mode= thd->variables.sql_mode;
thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO;
// row processing loop
while (error == 0 && m_curr_row < m_rows_end)
......@@ -7802,6 +7810,11 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
thd->transaction.stmt.modified_non_trans_table= TRUE;
} // row processing loop
/*
Restore the sql_mode after the rows event is processed.
*/
thd->variables.sql_mode= saved_sql_mode;
{/**
The following failure injecion works in cooperation with tests
setting @@global.debug= 'd,stop_slave_middle_group'.
......@@ -8765,16 +8778,11 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
int UNINIT_VAR(keynum);
auto_afree_ptr<char> key(NULL);
/* fill table->record[0] with default values */
bool abort_on_warnings= (rli->sql_thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES));
if ((error= prepare_record(table, m_width,
table->file->ht->db_type != DB_TYPE_NDBCLUSTER,
abort_on_warnings, m_curr_row == m_rows_buf)))
DBUG_RETURN(error);
prepare_record(table, m_width,
table->file->ht->db_type != DB_TYPE_NDBCLUSTER);
/* unpack row into table->record[0] */
if ((error= unpack_current_row(rli, abort_on_warnings)))
if ((error= unpack_current_row(rli)))
DBUG_RETURN(error);
if (m_curr_row == m_rows_buf)
......@@ -9618,11 +9626,9 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
store_record(m_table,record[1]);
bool abort_on_warnings= (rli->sql_thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES));
m_curr_row= m_curr_row_end;
/* this also updates m_curr_row_end */
if ((error= unpack_current_row(rli, abort_on_warnings)))
if ((error= unpack_current_row(rli)))
return error;
/*
......
......@@ -3667,16 +3667,13 @@ class Rows_log_event : public Log_event
int write_row(const Relay_log_info *const, const bool);
// Unpack the current row into m_table->record[0]
int unpack_current_row(const Relay_log_info *const rli,
const bool abort_on_warning= TRUE)
{
int unpack_current_row(const Relay_log_info *const rli)
{
DBUG_ASSERT(m_table);
bool first_row= (m_curr_row == m_rows_buf);
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
&m_curr_row_end, &m_master_reclength,
abort_on_warning, first_row);
&m_curr_row_end, &m_master_reclength);
if (m_curr_row_end > m_rows_end)
my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
......
......@@ -185,8 +185,7 @@ int
unpack_row(Relay_log_info const *rli,
TABLE *table, uint const colcnt,
uchar const *const row_data, MY_BITMAP const *cols,
uchar const **const row_end, ulong *const master_reclength,
const bool abort_on_warning, const bool first_row)
uchar const **const row_end, ulong *const master_reclength)
{
DBUG_ENTER("unpack_row");
DBUG_ASSERT(row_data);
......@@ -285,22 +284,9 @@ unpack_row(Relay_log_info const *rli,
}
else
{
MYSQL_ERROR::enum_warning_level error_type=
MYSQL_ERROR::WARN_LEVEL_NOTE;
if (abort_on_warning && (table->file->has_transactions() ||
first_row))
{
error = HA_ERR_ROWS_EVENT_APPLY;
error_type= MYSQL_ERROR::WARN_LEVEL_ERROR;
}
else
{
f->set_default();
error_type= MYSQL_ERROR::WARN_LEVEL_WARN;
}
push_warning_printf(current_thd, error_type,
ER_BAD_NULL_ERROR,
ER(ER_BAD_NULL_ERROR),
f->set_default();
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_BAD_NULL_ERROR, ER(ER_BAD_NULL_ERROR),
f->field_name);
}
}
......@@ -420,20 +406,13 @@ unpack_row(Relay_log_info const *rli,
@param skip Number of columns for which default/nullable check
should be skipped.
@param check Specifies if lack of default error needs checking.
@param abort_on_warning
Controls how to react on lack of a field's default.
The parameter mimics the master side one for
@c check_that_all_fields_are_given_values.
@returns 0 on success or a handler level error code
*/
int prepare_record(TABLE *const table,
const uint skip, const bool check,
const bool abort_on_warning, const bool first_row)
int prepare_record(TABLE *const table, const uint skip, const bool check)
{
DBUG_ENTER("prepare_record");
int error= 0;
restore_record(table, s->default_values);
/*
......@@ -456,28 +435,16 @@ int prepare_record(TABLE *const table,
if ((f->flags & NO_DEFAULT_VALUE_FLAG) &&
(f->real_type() != MYSQL_TYPE_ENUM))
{
MYSQL_ERROR::enum_warning_level error_type=
MYSQL_ERROR::WARN_LEVEL_NOTE;
if (abort_on_warning && (table->file->has_transactions() ||
first_row))
{
error= HA_ERR_ROWS_EVENT_APPLY;
error_type= MYSQL_ERROR::WARN_LEVEL_ERROR;
}
else
{
f->set_default();
error_type= MYSQL_ERROR::WARN_LEVEL_WARN;
}
push_warning_printf(current_thd, error_type,
f->set_default();
push_warning_printf(current_thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER(ER_NO_DEFAULT_FOR_FIELD),
f->field_name);
}
}
DBUG_RETURN(error);
DBUG_RETURN(0);
}
#endif // HAVE_REPLICATION
......@@ -32,13 +32,10 @@ size_t pack_row(TABLE* table, MY_BITMAP const* cols,
int unpack_row(Relay_log_info const *rli,
TABLE *table, uint const colcnt,
uchar const *const row_data, MY_BITMAP const *cols,
uchar const **const row_end, ulong *const master_reclength,
const bool abort_on_warning= TRUE, const bool first_row= TRUE);
uchar const **const row_end, ulong *const master_reclength);
// Fill table's record[0] with default values.
int prepare_record(TABLE *const table, const uint skip, const bool check,
const bool abort_on_warning= TRUE,
const bool first_row= TRUE);
int prepare_record(TABLE *const table, const uint skip, const bool check);
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment