diff --git a/mysql-test/extra/rpl_tests/rpl_auto_increment.test b/mysql-test/extra/rpl_tests/rpl_auto_increment.test index d81ab15a945..9bfda48df0b 100644 --- a/mysql-test/extra/rpl_tests/rpl_auto_increment.test +++ b/mysql-test/extra/rpl_tests/rpl_auto_increment.test @@ -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 diff --git a/mysql-test/suite/rpl/r/rpl_auto_increment.result b/mysql-test/suite/rpl/r/rpl_auto_increment.result index 2ab515c8d93..03b413e1b12 100644 --- a/mysql-test/suite/rpl/r/rpl_auto_increment.result +++ b/mysql-test/suite/rpl/r/rpl_auto_increment.result @@ -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 diff --git a/sql/log_event.cc b/sql/log_event.cc index b01b1766a9a..916a95e170f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -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 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; /* diff --git a/sql/log_event.h b/sql/log_event.h index a4d76e692a8..1aa4af6c751 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3667,16 +3667,13 @@ protected: 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); diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index dd16318303d..5a7984430ea 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -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 diff --git a/sql/rpl_record.h b/sql/rpl_record.h index 9b3829a435e..104d77738f1 100644 --- a/sql/rpl_record.h +++ b/sql/rpl_record.h @@ -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