Hi Alfranio,
Thank you for the work!
I think it is not necessary to change the handler structure, I think you
do not need to construct the error message from storage engines and
report the error as a warning, because we do not do that for SBR
eighter.
Alfranio Correia wrote:
> #At
> file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-39393/mysql-5.1-bugteam/
> based on revid:sergey.glukhov@stripped
>
> 2802 Alfranio Correia 2009-03-20
> BUG#39393 slave-skip-errors does not work when using ROW based replication
>
> RBR was not considering the option --slave-skip-errors.
>
> To fix the problem, we are reporting the ignored ERROR(s) as warnings thus
> avoiding
> stopping the SQL Thread. Besides, it fixes the output of "SHOW VARIABLES LIKE
> 'slave_skip_errors'" which was showing nothing when the value "all" was
> assigned
> to --slave-skip-errors.
>
> @include/my_sys.h
> @mysys/my_error.c
> added functions to create messages in a buffer without printing them out.
> @sql/ha_ndbcluster.h
> @sql/ha_ndbcluster.cc
> refactored its interface so specific errors in the cluster are easily
> reported through the handler.
> @sql/ha_partition.cc
> @sql/ha_partition.h
> @sql/partition_info.cc
> @sql/partition_info.h
> refactored its interface so specific errors in the cluster engine
> are easily reported through the handler.
> @sql/handler.h
> @sql/handler.cc
> refactored its interface so errors can be easily checked and reported
> through its interfaces.
> @sql/log_event.cc
> skipped rbr errors when the option skip-slave-errors is set.
> @sql/slave.cc
> fixed the output of for SHOW VARIABLES LIKE 'slave_skip_errors'".
> added:
> mysql-test/suite/rpl/r/rpl_skiperrors_rbr.result
> mysql-test/suite/rpl/t/rpl_skiperrors_rbr-slave.opt
> mysql-test/suite/rpl/t/rpl_skiperrors_rbr.test
> modified:
> include/my_sys.h
> mysys/my_error.c
> sql/ha_ndbcluster.cc
> sql/ha_ndbcluster.h
> sql/ha_partition.cc
> sql/ha_partition.h
> sql/handler.cc
> sql/handler.h
> sql/log_event.cc
> sql/partition_info.cc
> sql/partition_info.h
> sql/slave.cc
>
> === modified file 'include/my_sys.h'
> --- a/include/my_sys.h 2009-02-05 06:16:00 +0000
> +++ b/include/my_sys.h 2009-03-20 12:40:23 +0000
> @@ -645,6 +645,9 @@ extern int my_sync(File fd, myf my_flags
> extern int my_sync_dir(const char *dir_name, myf my_flags);
> extern int my_sync_dir_by_file(const char *file_name, myf my_flags);
> extern int my_error _VARARGS((int nr,myf MyFlags, ...));
> +extern void my_buffer_error _VARARGS((int nr, char* ebuff, size_t size, ...));
> +extern void my_buffer_format _VARARGS((char* ebuff, size_t size,
> + const char* format, ...));
> extern int my_printf_error _VARARGS((uint my_err, const char *format,
> myf MyFlags, ...))
> ATTRIBUTE_FORMAT(printf, 2, 4);
>
> === added file 'mysql-test/suite/rpl/r/rpl_skiperrors_rbr.result'
> --- a/mysql-test/suite/rpl/r/rpl_skiperrors_rbr.result 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/rpl/r/rpl_skiperrors_rbr.result 2009-03-20 12:40:23 +0000
> @@ -0,0 +1,94 @@
> +stop slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +reset master;
> +reset slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +start slave;
> +CREATE TABLE t1(id INT NOT NULL PRIMARY KEY, data INT) Engine=InnoDB;
> +INSERT INTO t1 VALUES(1, 1);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 1;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(1, 2);
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +id data SLAVE DATA
> +1 1 SLAVE DATA
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +id data MASTER DATA
> +1 2 MASTER DATA
> +DELETE FROM t1;
> +start transaction;
> +INSERT INTO t1 VALUES(1, 1);
> +INSERT INTO t1 VALUES(10, 1);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 10;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(10, 2);
> +INSERT INTO t1 VALUES(3, 2);
> +commit;
> +
> +start transaction;
> +INSERT INTO t1 VALUES(4, 3);
> +INSERT INTO t1 VALUES(5, 3);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 5;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(5, 4);
> +INSERT INTO t1 VALUES(6, 4);
> +rollback;
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +id data SLAVE DATA
> +1 1 SLAVE DATA
> +3 2 SLAVE DATA
> +10 1 SLAVE DATA
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +id data MASTER DATA
> +1 1 MASTER DATA
> +3 2 MASTER DATA
> +10 2 MASTER DATA
> +DELETE FROM t1;
> +INSERT INTO t1 VALUES(1, 1);
> +INSERT INTO t1 VALUES(2, 1);
> +INSERT INTO t1 VALUES(3, 1);
> +INSERT INTO t1 VALUES(4, 1);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 4;
> +SET SQL_LOG_BIN=1;
> +UPDATE t1 SET id= id + 3, data = 2;
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +id data SLAVE DATA
> +1 1 SLAVE DATA
> +4 1 SLAVE DATA
> +5 2 SLAVE DATA
> +6 2 SLAVE DATA
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +id data MASTER DATA
> +4 2 MASTER DATA
> +5 2 MASTER DATA
> +6 2 MASTER DATA
> +CREATE TABLE t2(id INT NOT NULL PRIMARY KEY, data INT) Engine=MyIsam;
> +INSERT INTO t2 VALUES(1, 1);
> +INSERT INTO t2 VALUES(2, 1);
> +INSERT INTO t2 VALUES(3, 1);
> +INSERT INTO t2 VALUES(4, 1);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t2 WHERE id = 4;
> +SET SQL_LOG_BIN=1;
> +UPDATE t2 SET id= id + 3, data = 2;
> +
> +SELECT *, "SLAVE DATA" FROM t2 ORDER BY id;
> +id data SLAVE DATA
> +1 1 SLAVE DATA
> +4 1 SLAVE DATA
> +5 2 SLAVE DATA
> +6 2 SLAVE DATA
> +SELECT *, "MASTER DATA" FROM t2 ORDER BY id;
> +id data MASTER DATA
> +4 2 MASTER DATA
> +5 2 MASTER DATA
> +6 2 MASTER DATA
> +DROP TABLE t1;
> +DROP TABLE t2;
>
> === added file 'mysql-test/suite/rpl/t/rpl_skiperrors_rbr-slave.opt'
> --- a/mysql-test/suite/rpl/t/rpl_skiperrors_rbr-slave.opt 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_skiperrors_rbr-slave.opt 2009-03-20 12:40:23 +0000
> @@ -0,0 +1 @@
> +--slave_skip_errors=all
>
> === added file 'mysql-test/suite/rpl/t/rpl_skiperrors_rbr.test'
> --- a/mysql-test/suite/rpl/t/rpl_skiperrors_rbr.test 1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_skiperrors_rbr.test 2009-03-20 12:40:23 +0000
> @@ -0,0 +1,163 @@
> +#################################################################
> +# This test cases checks if slave-skip-errors works when
> +# using ROW based by generating forcing duplicate keys
> +# in the SLAVE.
> +#
> +# The following scenarios are checked:
> +#
> +# 1 - InnoDB without transactions
> +# 2 - InnoDB with transactions, both commit and rollback.
> +# 3 - InnoDB with an UPDATE on a SET of rows.
> +# 4 - MyIsam with an UPDATE on a SET of rows.
> +#
> +#################################################################
> +
> +--source include/have_binlog_format_row.inc
> +--source include/have_innodb.inc
> +--source include/master-slave.inc
> +
> +#################################################################
> +# 1 - (InnoDB) Duplicate key
> +#################################################################
> +connection master;
> +
> +CREATE TABLE t1(id INT NOT NULL PRIMARY KEY, data INT) Engine=InnoDB;
> +
> +INSERT INTO t1 VALUES(1, 1);
> +
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 1;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(1, 2);
> +
> +sync_slave_with_master;
> +
> +let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +echo $error;
> +
> +connection slave;
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +
> +connection master;
> +
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +
> +#################################################################
> +# 2 - (InnoDB with Transaction) Duplicate key
> +#################################################################
> +connection master;
> +
> +DELETE FROM t1;
> +
> +start transaction;
> +INSERT INTO t1 VALUES(1, 1);
> +INSERT INTO t1 VALUES(10, 1);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 10;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(10, 2);
> +INSERT INTO t1 VALUES(3, 2);
> +commit;
> +
> +sync_slave_with_master;
> +
> +let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +echo $error;
> +
> +connection master;
> +
> +start transaction;
> +INSERT INTO t1 VALUES(4, 3);
> +INSERT INTO t1 VALUES(5, 3);
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 5;
> +SET SQL_LOG_BIN=1;
> +INSERT INTO t1 VALUES(5, 4);
> +INSERT INTO t1 VALUES(6, 4);
> +rollback;
> +
> +sync_slave_with_master;
> +
> +let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +echo $error;
> +
> +connection master;
> +
> +connection slave;
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +
> +connection master;
> +
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +
> +#################################################################
> +# 3 - (InnoDB with sets) Duplicate key
> +#################################################################
> +connection master;
> +
> +DELETE FROM t1;
> +
> +INSERT INTO t1 VALUES(1, 1);
> +INSERT INTO t1 VALUES(2, 1);
> +INSERT INTO t1 VALUES(3, 1);
> +INSERT INTO t1 VALUES(4, 1);
> +
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t1 WHERE id = 4;
> +SET SQL_LOG_BIN=1;
> +UPDATE t1 SET id= id + 3, data = 2;
> +
> +sync_slave_with_master;
> +
> +let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +echo $error;
> +
> +connection slave;
> +
> +SELECT *, "SLAVE DATA" FROM t1 ORDER BY id;
> +
> +connection master;
> +
> +SELECT *, "MASTER DATA" FROM t1 ORDER BY id;
> +
> +#################################################################
> +# 4 - (MyIsam with sets) Duplicate key
> +#################################################################
> +connection master;
> +
> +CREATE TABLE t2(id INT NOT NULL PRIMARY KEY, data INT) Engine=MyIsam;
> +
> +INSERT INTO t2 VALUES(1, 1);
> +INSERT INTO t2 VALUES(2, 1);
> +INSERT INTO t2 VALUES(3, 1);
> +INSERT INTO t2 VALUES(4, 1);
> +
> +SET SQL_LOG_BIN=0;
> +DELETE FROM t2 WHERE id = 4;
> +SET SQL_LOG_BIN=1;
> +UPDATE t2 SET id= id + 3, data = 2;
> +
> +sync_slave_with_master;
> +
> +let $error= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
> +echo $error;
> +
> +connection slave;
> +
> +SELECT *, "SLAVE DATA" FROM t2 ORDER BY id;
> +
> +connection master;
> +
> +SELECT *, "MASTER DATA" FROM t2 ORDER BY id;
> +
> +#################################################################
> +# Clean up
> +#################################################################
> +connection master;
> +
> +DROP TABLE t1;
> +DROP TABLE t2;
> +
> +sync_slave_with_master;
>
> === modified file 'mysys/my_error.c'
> --- a/mysys/my_error.c 2009-02-05 06:16:00 +0000
> +++ b/mysys/my_error.c 2009-03-20 12:40:23 +0000
> @@ -101,6 +101,49 @@ int my_error(int nr, myf MyFlags, ...)
>
>
> /*
> + Populates a buffer with the error message associated with the error (nr)
> + number specified as parameter.
> + */
> +void my_buffer_error(int nr, char* ebuff, size_t size, ...)
> +{
> + const char *format;
> + struct my_err_head *meh_p;
> + va_list args;
> + DBUG_ENTER("my_bufer_error");
> +
> + /* Search for the error messages array, which could contain the message. */
> + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next)
> + if (nr <= meh_p->meh_last)
> + break;
> +
> + /* get the error message string. Default, if NULL or empty string (""). */
> + if (! (format= (meh_p && (nr >= meh_p->meh_first)) ?
> + meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format)
> + (void) my_snprintf (ebuff, size, "Unknown error %d", nr);
> + else
> + {
> + va_start(args, size);
> + (void) my_vsnprintf (ebuff, size, format, args);
> + va_end(args);
> + }
> + DBUG_VOID_RETURN;
> +}
> +
> +/*
> + Populates a buffer with an error message specified as parameter (format).
> + */
> +void my_buffer_format(char* ebuff, size_t size, const char* format, ...)
> +{
> + va_list args;
> + DBUG_ENTER("my_buffer_format");
> +
> + va_start(args,format);
> + (void) my_vsnprintf (ebuff, size, format, args);
> + va_end(args);
> + DBUG_VOID_RETURN;
> +}
> +
> +/*
> Error as printf
>
> SYNOPSIS
>
> === modified file 'sql/ha_ndbcluster.cc'
> --- a/sql/ha_ndbcluster.cc 2008-10-23 19:27:09 +0000
> +++ b/sql/ha_ndbcluster.cc 2009-03-20 12:40:23 +0000
> @@ -7545,19 +7545,17 @@ static int ndbcluster_end(handlerton *ht
> DBUG_RETURN(0);
> }
>
> -void ha_ndbcluster::print_error(int error, myf errflag)
> +int ha_ndbcluster::process_error(int error, char* ebuff, int size, myf* srvflag)
> {
> - DBUG_ENTER("ha_ndbcluster::print_error");
> - DBUG_PRINT("enter", ("error: %d", error));
> -
> + DBUG_ENTER("ha_ndbcluster::process_error");
> + int ret= error;
> if (error == HA_ERR_NO_PARTITION_FOUND)
> - m_part_info->print_no_partition_found(table);
> + ret= m_part_info->process_no_partition_found(table, ebuff, size, srvflag);
> else
> - handler::print_error(error, errflag);
> - DBUG_VOID_RETURN;
> + ret= handler::process_error(error, ebuff, size, srvflag);
> + DBUG_RETURN(ret);
> }
>
> -
> /**
> Static error print function called from static handler method
> ndbcluster_commit and ndbcluster_rollback.
>
> === modified file 'sql/ha_ndbcluster.h'
> --- a/sql/ha_ndbcluster.h 2008-02-04 14:40:04 +0000
> +++ b/sql/ha_ndbcluster.h 2009-03-20 12:40:23 +0000
> @@ -279,7 +279,7 @@ class ha_ndbcluster: public handler
> int external_lock(THD *thd, int lock_type);
> void unlock_row();
> int start_stmt(THD *thd, thr_lock_type lock_type);
> - void print_error(int error, myf errflag);
> + int process_error(int error, char* ebuff, int size, myf* errflag);
> const char * table_type() const;
> const char ** bas_ext() const;
> ulonglong table_flags(void) const;
>
> === modified file 'sql/ha_partition.cc'
> --- a/sql/ha_partition.cc 2009-01-07 22:30:10 +0000
> +++ b/sql/ha_partition.cc 2009-03-20 12:40:23 +0000
> @@ -5831,22 +5831,21 @@ enum row_type ha_partition::get_row_type
> return type;
> }
>
> -
> -void ha_partition::print_error(int error, myf errflag)
> +int ha_partition::process_error(int error, char* ebuff, int size, myf* srvflag)
> {
> - DBUG_ENTER("ha_partition::print_error");
> + DBUG_ENTER("ha_partition::process_error");
> + int ret= error;
>
> /* Should probably look for my own errors first */
> DBUG_PRINT("enter", ("error: %d", error));
>
> - if (error == HA_ERR_NO_PARTITION_FOUND)
> - m_part_info->print_no_partition_found(table);
> + if (error == HA_ERR_NO_PARTITION_FOUND)
> + ret= m_part_info->process_no_partition_found(table, ebuff, size, srvflag);
> else
> - m_file[m_last_part]->print_error(error, errflag);
> - DBUG_VOID_RETURN;
> + ret= m_file[m_last_part]->process_error(error, ebuff, size, srvflag);
> + DBUG_RETURN(ret);
> }
>
> -
> bool ha_partition::get_error_message(int error, String *buf)
> {
> DBUG_ENTER("ha_partition::get_error_message");
>
> === modified file 'sql/ha_partition.h'
> --- a/sql/ha_partition.h 2009-01-05 16:10:20 +0000
> +++ b/sql/ha_partition.h 2009-03-20 12:40:23 +0000
> @@ -600,7 +600,8 @@ public:
> /*
> Handler specific error messages
> */
> - virtual void print_error(int error, myf errflag);
> + int process_error(int error, char* ebuff, int size, myf* srvflag);
> +
> virtual bool get_error_message(int error, String * buf);
> /*
> -------------------------------------------------------------------------
>
> === modified file 'sql/handler.cc'
> --- a/sql/handler.cc 2009-02-10 08:37:27 +0000
> +++ b/sql/handler.cc 2009-03-20 12:40:23 +0000
> @@ -2526,9 +2526,22 @@ void handler::ha_release_auto_increment(
> }
> }
>
> -
> void handler::print_keydup_error(uint key_nr, const char *msg)
> {
> + char ebuff[MYSQL_ERRMSG_SIZE];
> + DBUG_ENTER("handler::print_keydup_error");
> +
> + process_keydup_error(key_nr, msg, ebuff, MYSQL_ERRMSG_SIZE);
> + my_message(ER_DUP_ENTRY, ebuff, MYF(0));
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +void handler::process_keydup_error(uint key_nr, const char *msg,
> + char* ebuff, int size)
> +{
> + DBUG_ENTER("handler::process_keydup_error");
> +
> /* Write the duplicated key in the error message */
> char key[MAX_KEY_LENGTH];
> String str(key,sizeof(key),system_charset_info);
> @@ -2537,7 +2550,7 @@ void handler::print_keydup_error(uint ke
> {
> /* Key is unknown */
> str.copy("", 0, system_charset_info);
> - my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
> + my_buffer_format(ebuff, size, msg, str.c_ptr(), "*UNKNOWN*");
> }
> else
> {
> @@ -2549,14 +2562,26 @@ void handler::print_keydup_error(uint ke
> str.length(max_length-4);
> str.append(STRING_WITH_LEN("..."));
> }
> - my_printf_error(ER_DUP_ENTRY, msg,
> - MYF(0), str.c_ptr_safe(), table->key_info[key_nr].name);
> + my_buffer_format(ebuff, size, msg, str.c_ptr_safe(),
> + table->key_info[key_nr].name);
> }
> + DBUG_VOID_RETURN;
> }
>
> +void handler::print_error(int error, myf errflag)
> +{
> + DBUG_ENTER("handler::print_error");
> +
> + char ebuff[MYSQL_ERRMSG_SIZE];
> + myf srvflag= errflag;
> + int srverror= process_error(error, ebuff, MYSQL_ERRMSG_SIZE, &srvflag);
> + my_message(srverror, ebuff, srvflag);
> +
> + DBUG_VOID_RETURN;
> +}
>
> /**
> - Print error that we got from handler function.
> + Process error that we got from handler function.
>
> @note
> In case of delete table it's only safe to use the following parts of
> @@ -2564,39 +2589,42 @@ void handler::print_keydup_error(uint ke
> - table->s->path
> - table->alias
> */
> -void handler::print_error(int error, myf errflag)
> +int handler::process_error(int error, char* ebuff, int size, myf* srvflag)
> {
> - DBUG_ENTER("handler::print_error");
> + DBUG_ENTER("handler::process_error");
> DBUG_PRINT("enter",("error: %d",error));
> + DBUG_ASSERT(ebuff != 0 && size >= 0 && srvflag != 0);
>
> - int textno=ER_GET_ERRNO;
> + int textno= ER_GET_ERRNO;
> switch (error) {
> case EACCES:
> - textno=ER_OPEN_AS_READONLY;
> + textno= ER_OPEN_AS_READONLY;
> break;
> case EAGAIN:
> - textno=ER_FILE_USED;
> + textno= ER_FILE_USED;
> break;
> case ENOENT:
> - textno=ER_FILE_NOT_FOUND;
> + textno= ER_FILE_NOT_FOUND;
> break;
> case HA_ERR_KEY_NOT_FOUND:
> case HA_ERR_NO_ACTIVE_RECORD:
> case HA_ERR_END_OF_FILE:
> - textno=ER_KEY_NOT_FOUND;
> + textno= ER_KEY_NOT_FOUND;
> break;
> case HA_ERR_WRONG_MRG_TABLE_DEF:
> - textno=ER_WRONG_MRG_TABLE;
> + textno= ER_WRONG_MRG_TABLE;
> break;
> case HA_ERR_FOUND_DUPP_KEY:
> {
> uint key_nr=get_dup_key(error);
> if ((int) key_nr >= 0)
> {
> - print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME));
> - DBUG_VOID_RETURN;
> + textno= ER_DUP_ENTRY;
> + process_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME), ebuff, size);
> + (*srvflag)= MYF(0);
> + DBUG_RETURN(textno);
> }
> - textno=ER_DUP_KEY;
> + textno= ER_DUP_KEY;
> break;
> }
> case HA_ERR_FOREIGN_DUPLICATE_KEY:
> @@ -2604,6 +2632,7 @@ void handler::print_error(int error, myf
> uint key_nr= get_dup_key(error);
> if ((int) key_nr >= 0)
> {
> + textno= ER_FOREIGN_DUPLICATE_KEY;
> uint max_length;
> /* Write the key in the error message */
> char key[MAX_KEY_LENGTH];
> @@ -2611,110 +2640,127 @@ void handler::print_error(int error, myf
> /* Table is opened and defined at this point */
> key_unpack(&str,table,(uint) key_nr);
> max_length= (MYSQL_ERRMSG_SIZE-
> - (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
> + (uint) strlen(ER(textno)));
> if (str.length() >= max_length)
> {
> str.length(max_length-4);
> str.append(STRING_WITH_LEN("..."));
> }
> - my_error(ER_FOREIGN_DUPLICATE_KEY, MYF(0), table_share->table_name.str,
> - str.c_ptr_safe(), key_nr+1);
> - DBUG_VOID_RETURN;
> - }
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, table_share->table_name.str,
> + str.c_ptr_safe(), key_nr+1);
> + DBUG_RETURN(textno);
> + }
> textno= ER_DUP_KEY;
> break;
> }
> case HA_ERR_NULL_IN_SPATIAL:
> - my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
> - DBUG_VOID_RETURN;
> + textno= ER_CANT_CREATE_GEOMETRY_OBJECT;
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size);
> + DBUG_RETURN(textno);
> + break;
> case HA_ERR_FOUND_DUPP_UNIQUE:
> - textno=ER_DUP_UNIQUE;
> + textno= ER_DUP_UNIQUE;
> break;
> case HA_ERR_RECORD_CHANGED:
> - textno=ER_CHECKREAD;
> + textno= ER_CHECKREAD;
> break;
> case HA_ERR_CRASHED:
> - textno=ER_NOT_KEYFILE;
> + textno= ER_NOT_KEYFILE;
> break;
> case HA_ERR_WRONG_IN_RECORD:
> textno= ER_CRASHED_ON_USAGE;
> break;
> case HA_ERR_CRASHED_ON_USAGE:
> - textno=ER_CRASHED_ON_USAGE;
> + textno= ER_CRASHED_ON_USAGE;
> break;
> case HA_ERR_NOT_A_TABLE:
> textno= error;
> break;
> case HA_ERR_CRASHED_ON_REPAIR:
> - textno=ER_CRASHED_ON_REPAIR;
> + textno= ER_CRASHED_ON_REPAIR;
> break;
> case HA_ERR_OUT_OF_MEM:
> - textno=ER_OUT_OF_RESOURCES;
> + textno= ER_OUT_OF_RESOURCES;
> break;
> case HA_ERR_WRONG_COMMAND:
> - textno=ER_ILLEGAL_HA;
> + textno= ER_ILLEGAL_HA;
> break;
> case HA_ERR_OLD_FILE:
> - textno=ER_OLD_KEYFILE;
> + textno= ER_OLD_KEYFILE;
> break;
> case HA_ERR_UNSUPPORTED:
> - textno=ER_UNSUPPORTED_EXTENSION;
> + textno= ER_UNSUPPORTED_EXTENSION;
> break;
> case HA_ERR_RECORD_FILE_FULL:
> case HA_ERR_INDEX_FILE_FULL:
> {
> - textno=ER_RECORD_FILE_FULL;
> + textno= ER_RECORD_FILE_FULL;
> /* Write the error message to error log */
> - errflag|= ME_NOREFRESH;
> + if (srvflag)
> + (*srvflag)|= ME_NOREFRESH;
> break;
> }
> case HA_ERR_LOCK_WAIT_TIMEOUT:
> - textno=ER_LOCK_WAIT_TIMEOUT;
> + textno= ER_LOCK_WAIT_TIMEOUT;
> break;
> case HA_ERR_LOCK_TABLE_FULL:
> - textno=ER_LOCK_TABLE_FULL;
> + textno= ER_LOCK_TABLE_FULL;
> break;
> case HA_ERR_LOCK_DEADLOCK:
> - textno=ER_LOCK_DEADLOCK;
> + textno= ER_LOCK_DEADLOCK;
> break;
> case HA_ERR_READ_ONLY_TRANSACTION:
> - textno=ER_READ_ONLY_TRANSACTION;
> + textno= ER_READ_ONLY_TRANSACTION;
> break;
> case HA_ERR_CANNOT_ADD_FOREIGN:
> - textno=ER_CANNOT_ADD_FOREIGN;
> + textno= ER_CANNOT_ADD_FOREIGN;
> break;
> case HA_ERR_ROW_IS_REFERENCED:
> {
> String str;
> + textno= ER_ROW_IS_REFERENCED_2;
> get_error_message(error, &str);
> - my_error(ER_ROW_IS_REFERENCED_2, MYF(0), str.c_ptr_safe());
> - DBUG_VOID_RETURN;
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, str.c_ptr_safe());
> + DBUG_RETURN(textno);
> + break;
> }
> case HA_ERR_NO_REFERENCED_ROW:
> {
> String str;
> + textno= ER_NO_REFERENCED_ROW_2;
> get_error_message(error, &str);
> - my_error(ER_NO_REFERENCED_ROW_2, MYF(0), str.c_ptr_safe());
> - DBUG_VOID_RETURN;
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, str.c_ptr_safe());
> + DBUG_RETURN(textno);
> + break;
> }
> case HA_ERR_TABLE_DEF_CHANGED:
> - textno=ER_TABLE_DEF_CHANGED;
> + textno= ER_TABLE_DEF_CHANGED;
> break;
> case HA_ERR_NO_SUCH_TABLE:
> - my_error(ER_NO_SUCH_TABLE, MYF(0), table_share->db.str,
> - table_share->table_name.str);
> - DBUG_VOID_RETURN;
> + textno= ER_NO_SUCH_TABLE;
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, table_share->db.str,
> + table_share->table_name.str);
> + DBUG_RETURN(textno);
> + break;
> case HA_ERR_RBR_LOGGING_FAILED:
> textno= ER_BINLOG_ROW_LOGGING_FAILED;
> break;
> case HA_ERR_DROP_INDEX_FK:
> {
> + textno= ER_DROP_INDEX_FK;
> const char *ptr= "???";
> uint key_nr= get_dup_key(error);
> if ((int) key_nr >= 0)
> ptr= table->key_info[key_nr].name;
> - my_error(ER_DROP_INDEX_FK, MYF(0), ptr);
> - DBUG_VOID_RETURN;
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, ptr);
> + DBUG_RETURN(textno);
> + break;
> }
> case HA_ERR_TABLE_NEEDS_UPGRADE:
> textno=ER_TABLE_NEEDS_UPGRADE;
> @@ -2732,24 +2778,26 @@ void handler::print_error(int error, myf
> {
> /* The error was "unknown" to this function.
> Ask handler if it has got a message for this error */
> - bool temporary= FALSE;
> String str;
> - temporary= get_error_message(error, &str);
> - if (!str.is_empty())
> + bool temporary= get_error_message(error, &str);
> + textno= (str.is_empty() ? ER_GET_ERRNO :
> + (temporary ? ER_GET_TEMPORARY_ERRMSG : ER_GET_ERRMSG));
> + if (textno != ER_GET_ERRNO)
> {
> - const char* engine= table_type();
> - if (temporary)
> - my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine);
> - else
> - my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine);
> + const char* engine= table_type();
> + (*srvflag)= MYF(0);
> + my_buffer_error(textno, ebuff, size, error, str.ptr(), engine);
> }
> else
> - my_error(ER_GET_ERRNO,errflag,error);
> - DBUG_VOID_RETURN;
> + {
> + my_buffer_error(textno, ebuff, size, error);
> + }
> + DBUG_RETURN(textno);
> + break;
> }
> }
> - my_error(textno, errflag, table_share->table_name.str, error);
> - DBUG_VOID_RETURN;
> + my_buffer_error(textno, ebuff, size, table_share->table_name.str, error);
> + DBUG_RETURN(textno);
> }
>
>
>
> === modified file 'sql/handler.h'
> --- a/sql/handler.h 2008-12-10 20:14:50 +0000
> +++ b/sql/handler.h 2009-03-20 12:40:23 +0000
> @@ -1262,8 +1262,11 @@ public:
>
> void adjust_next_insert_id_after_explicit_value(ulonglong nr);
> int update_auto_increment();
> + void process_keydup_error(uint key_nr, const char *msg,
> + char* ebuff, int size);
> void print_keydup_error(uint key_nr, const char *msg);
> - virtual void print_error(int error, myf errflag);
> + virtual int process_error(int error, char* ebuff, int size, myf* srvflag);
> + void print_error(int error, myf errflag);
> virtual bool get_error_message(int error, String *buf);
> uint get_dup_key(int error);
> virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
>
> === modified file 'sql/log_event.cc'
> --- a/sql/log_event.cc 2009-02-04 11:08:27 +0000
> +++ b/sql/log_event.cc 2009-03-20 12:40:23 +0000
> @@ -277,6 +277,47 @@ static void clear_all_errors(THD *thd, R
> rli->clear_error();
> }
>
> +inline int idempotent_error_code(int err_code)
> +{
> + int ret= 0;
> +
> + switch (err_code)
> + {
> + case 0:
> + ret= 1;
> + break;
> + /*
> + The following list of "idempotent" errors
> + means that an error from the list might happen
> + because of idempotent (more than once)
> + applying of a binlog file.
> + Notice, that binlog has a ddl operation its
> + second applying may cause
> +
> + case HA_ERR_TABLE_DEF_CHANGED:
> + case HA_ERR_CANNOT_ADD_FOREIGN:
> +
> + which are not included into to the list.
> +
> + Note that HA_ERR_RECORD_DELETED is not in the list since
> + do_exec_row() should not return that error code.
> + */
> + case HA_ERR_RECORD_CHANGED:
> + case HA_ERR_KEY_NOT_FOUND:
> + case HA_ERR_END_OF_FILE:
> + case HA_ERR_FOUND_DUPP_KEY:
> + case HA_ERR_FOUND_DUPP_UNIQUE:
> + case HA_ERR_FOREIGN_DUPLICATE_KEY:
> + case HA_ERR_NO_REFERENCED_ROW:
> + case HA_ERR_ROW_IS_REFERENCED:
> + ret= 1;
> + break;
> + default:
> + ret= 0;
> + break;
> + }
> + return (ret);
> +}
>
> /**
> Ignore error code specified on command line.
> @@ -7151,7 +7192,9 @@ int Rows_log_event::do_apply_event(Relay
> {
> /*
> Error reporting borrowed from Query_log_event with many excessive
> - simplifications (we don't honour --slave-skip-errors)
> + simplifications.
> + We should not honour --slave-skip-errors at this point as we are
> + having severe errors which should not be skiped.
> */
> rli->report(ERROR_LEVEL, actual_error,
> "Error '%s' on opening tables",
> @@ -7177,6 +7220,10 @@ int Rows_log_event::do_apply_event(Relay
> {
> if (ptr->m_tabledef.compatible_with(rli, ptr->table))
> {
> + /*
> + We should not honour --slave-skip-errors at this point as we are
> + having severe errors which should not be skiped.
> + */
> mysql_unlock_tables(thd, thd->lock);
> thd->lock= 0;
> thd->is_slave_error= 1;
> @@ -7214,7 +7261,7 @@ int Rows_log_event::do_apply_event(Relay
> m_table=
> const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
>
> DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu", (ulong) m_table,
> m_table_id));
> -
> +
> if (table)
> {
> /*
> @@ -7284,48 +7331,32 @@ int Rows_log_event::do_apply_event(Relay
> DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
>
> table->in_use = old_thd;
> - switch (error)
> - {
> - case 0:
> - break;
> - /*
> - The following list of "idempotent" errors
> - means that an error from the list might happen
> - because of idempotent (more than once)
> - applying of a binlog file.
> - Notice, that binlog has a ddl operation its
> - second applying may cause
> -
> - case HA_ERR_TABLE_DEF_CHANGED:
> - case HA_ERR_CANNOT_ADD_FOREIGN:
>
> - which are not included into to the list.
> -
> - Note that HA_ERR_RECORD_DELETED is not in the list since
> - do_exec_row() should not return that error code.
> - */
> - case HA_ERR_RECORD_CHANGED:
> - case HA_ERR_KEY_NOT_FOUND:
> - case HA_ERR_END_OF_FILE:
> - case HA_ERR_FOUND_DUPP_KEY:
> - case HA_ERR_FOUND_DUPP_UNIQUE:
> - case HA_ERR_FOREIGN_DUPLICATE_KEY:
> - case HA_ERR_NO_REFERENCED_ROW:
> - case HA_ERR_ROW_IS_REFERENCED:
> -
> - if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1)
> - {
> - if (global_system_variables.log_warnings)
> - slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
> - get_type_str(),
> - RPL_LOG_NAME, (ulong) log_pos);
> - error= 0;
> - }
> - break;
> -
> - default:
> - thd->is_slave_error= 1;
> - break;
> + DBUG_PRINT("info",("error (0) %d %d \n", error, thd->is_error() ?
> thd->main_da.sql_errno() : 0));
> + if (error)
> + {
> + int srvflag;
> + char ebuff[MYSQL_ERRMSG_SIZE];
> + uint actual_error_code = (thd->is_error() ? thd->main_da.sql_errno()
> :
> + table->file->process_error(error, ebuff,
> + MYSQL_ERRMSG_SIZE,
> + &srvflag));
> + if (ignored_error_code(actual_error_code) ||
> + (idempotent_error_code(error) &&
> + bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT)))
> + {
> + if (global_system_variables.log_warnings)
> + {
> + sql_print_warning(ebuff);
> + slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
> + get_type_str(),
> + RPL_LOG_NAME, (ulong) log_pos);
> + }
> + clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
> + thd->killed= THD::NOT_KILLED;
> + thd->is_slave_error= 0;
> + error= 0;
> + }
> }
>
> /*
> @@ -7353,6 +7384,9 @@ int Rows_log_event::do_apply_event(Relay
> DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
> const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
> error= do_after_row_operations(rli, error);
> +
> + DBUG_PRINT("info",("error (1) %d %d \n", error, thd->is_error() ?
> thd->main_da.sql_errno() : 0));
> +
> if (!cache_stmt)
> {
> DBUG_PRINT("info", ("Marked that we need to keep log"));
> @@ -7366,29 +7400,39 @@ int Rows_log_event::do_apply_event(Relay
> */
> if (rli->tables_to_lock && get_flags(STMT_END_F))
> const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
> -
> - if (error)
> - { /* error has occured during the transaction */
> - slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
> - get_type_str(), RPL_LOG_NAME, (ulong) log_pos);
> - }
> +
> if (error)
> {
> - /*
> - If one day we honour --skip-slave-errors in row-based replication, and
> - the error should be skipped, then we would clear mappings, rollback,
> - close tables, but the slave SQL thread would not stop and then may
> - assume the mapping is still available, the tables are still open...
> - So then we should clear mappings/rollback/close here only if this is a
> - STMT_END_F.
> - For now we code, knowing that error is not skippable and so slave SQL
> - thread is certainly going to stop.
> - rollback at the caller along with sbr.
> - */
> - thd->reset_current_stmt_binlog_row_based();
> - const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
> - thd->is_slave_error= 1;
> - DBUG_RETURN(error);
> + DBUG_PRINT("info",("error (2) %d %d \n", error, thd->is_error() ?
> thd->main_da.sql_errno() : 0));
> + int srvflag;
> + char ebuff[MYSQL_ERRMSG_SIZE];
> + uint actual_error_code = (thd->is_error() ? thd->main_da.sql_errno() :
> + table->file->process_error(error, ebuff,
> + MYSQL_ERRMSG_SIZE,
> + &srvflag));
> + if (ignored_error_code(actual_error_code))
> + {
> + if (global_system_variables.log_warnings)
> + {
> + sql_print_warning(ebuff);
> + slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
> + get_type_str(),
> + RPL_LOG_NAME, (ulong) log_pos);
> + }
> + clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
> + thd->killed= THD::NOT_KILLED;
> + error= 0;
> + thd->is_slave_error= 0;
> + }
> + else
> + {
> + slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
> + get_type_str(),
> + RPL_LOG_NAME, (ulong) log_pos);
> + thd->reset_current_stmt_binlog_row_based();
> + const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
> + thd->is_slave_error= 1;
> + }
> }
>
> /*
> @@ -7396,7 +7440,8 @@ int Rows_log_event::do_apply_event(Relay
> since we have no access to table there, we do the setting of
> last_event_start_time here instead.
> */
> - if (table && (table->s->primary_key == MAX_KEY) &&
> + if (!error &&
> + table && (table->s->primary_key == MAX_KEY) &&
> !cache_stmt && get_flags(STMT_END_F) == RLE_NO_FLAGS)
> {
> /*
> @@ -7418,7 +7463,7 @@ int Rows_log_event::do_apply_event(Relay
> const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
> }
>
> - DBUG_RETURN(0);
> + DBUG_RETURN(error);
> }
>
> Log_event::enum_skip_reason
>
> === modified file 'sql/partition_info.cc'
> --- a/sql/partition_info.cc 2008-12-02 10:18:01 +0000
> +++ b/sql/partition_info.cc 2009-03-20 12:40:23 +0000
> @@ -1075,22 +1075,41 @@ end:
> DBUG_RETURN(result);
> }
>
> +void partition_info::print_no_partition_found(TABLE *table)
> +{
> + DBUG_ENTER("partition_info::print_no_partition_found");
> +
> + char ebuff[MYSQL_ERRMSG_SIZE];
> + myf srvflag= MYF(0);
> + int srverror= process_no_partition_found(table, ebuff, MYSQL_ERRMSG_SIZE,
> + &srvflag);
> + my_message(srverror, ebuff, srvflag);
> +
> + DBUG_VOID_RETURN;
> +}
>
> /*
> - Print error for no partition found
> + Process error for no partition found
>
> SYNOPSIS
> - print_no_partition_found()
> + process_no_partition_found()
> table Table object
> + ebuff char* buffer
> + srvflag MYF flags
>
> RETURN VALUES
> */
> -
> -void partition_info::print_no_partition_found(TABLE *table)
> +int partition_info::process_no_partition_found(TABLE *table, char* ebuff,
> + int size, myf* srvflag)
> {
> char buf[100];
> char *buf_ptr= (char*)&buf;
> TABLE_LIST table_list;
> + int textno= ER_NO_PARTITION_FOR_GIVEN_VALUE;
> +
> + DBUG_ENTER("partition_info::process_no_partition_found");
> +
> + DBUG_ASSERT(ebuff != 0 && size >= 0 && srvflag != 0);
>
> bzero(&table_list, sizeof(table_list));
> table_list.db= table->s->db.str;
> @@ -1098,8 +1117,7 @@ void partition_info::print_no_partition_
>
> if (check_single_table_access(current_thd,
> SELECT_ACL, &table_list, TRUE))
> - my_message(ER_NO_PARTITION_FOR_GIVEN_VALUE,
> - ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), MYF(0));
> + my_buffer_format(ebuff, size, ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT));
> else
> {
> my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
> @@ -1108,9 +1126,13 @@ void partition_info::print_no_partition_
> else
> longlong2str(err_value, buf,
> part_expr->unsigned_flag ? 10 : -10);
> - my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), buf_ptr);
> +
> + my_buffer_error(textno, ebuff, size, buf_ptr);
> dbug_tmp_restore_column_map(table->read_set, old_map);
> }
> + (*srvflag)= MYF(0);
> +
> + DBUG_RETURN(textno);
> }
> /*
> Set up buffers and arrays for fields requiring preparation
>
> === modified file 'sql/partition_info.h'
> --- a/sql/partition_info.h 2008-11-10 20:21:49 +0000
> +++ b/sql/partition_info.h 2009-03-20 12:40:23 +0000
> @@ -276,6 +276,8 @@ public:
> bool check_partition_info(THD *thd, handlerton **eng_type,
> handler *file, HA_CREATE_INFO *info,
> bool check_partition_function);
> + int process_no_partition_found(TABLE *table, char* ebuff, int size,
> + myf* srvflag);
> void print_no_partition_found(TABLE *table);
> bool set_up_charset_field_preps();
> private:
>
> === modified file 'sql/slave.cc'
> --- a/sql/slave.cc 2009-01-09 12:49:24 +0000
> +++ b/sql/slave.cc 2009-03-20 12:40:23 +0000
> @@ -361,6 +361,7 @@ void init_slave_skip_errors(const char*
> if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4))
> {
> bitmap_set_all(&slave_error_mask);
> + print_slave_skip_errors();
> DBUG_VOID_RETURN;
> }
> for (p= arg ; *p; )
>
>