Below is the list of changes that have just been committed into a local
5.1 repository of mats. When mats does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-10-30 21:17:19+01:00, mats@stripped +3 -0
BUG#19958 (RBR idempotency issue for UPDATE and DELETE):
The rpl_trigger test case indicated a problem with idempotency support when run
under row-based replication, which this patch fixes.
However, despite this, the test is not designed for execution under row-based
replication and hence rpl_trigger.test is not executed under row-based
replication.
The problem is that the test expects triggers to be executed when the slave
updates rows on the slave, and this is (deliberately) not done with row-based
replication.
mysql-test/suite/rpl/r/rpl_idempotency.result@stripped, 2007-10-30 21:17:14+01:00, mats@stripped +71 -0
New BitKeeper file ``mysql-test/suite/rpl/r/rpl_idempotency.result''
mysql-test/suite/rpl/r/rpl_idempotency.result@stripped, 2007-10-30 21:17:14+01:00, mats@stripped +0 -0
mysql-test/suite/rpl/t/rpl_idempotency.test@stripped, 2007-10-30 21:17:14+01:00, mats@stripped +79 -0
New BitKeeper file ``mysql-test/suite/rpl/t/rpl_idempotency.test''
mysql-test/suite/rpl/t/rpl_idempotency.test@stripped, 2007-10-30 21:17:14+01:00, mats@stripped +0 -0
sql/log_event.cc@stripped, 2007-10-30 21:17:13+01:00, mats@stripped +67 -4
Adding function to print symbolic name of handler errors for debug purposes.
Ignoring some more error messages to provide full idempotency support for
update and delete operations.
diff -Nrup a/mysql-test/suite/rpl/r/rpl_idempotency.result b/mysql-test/suite/rpl/r/rpl_idempotency.result
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/suite/rpl/r/rpl_idempotency.result 2007-10-30 21:17:14 +01:00
@@ -0,0 +1,71 @@
+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 (a INT PRIMARY KEY);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (-1),(-2),(-3);
+INSERT INTO t2 VALUES (-1),(-2),(-3);
+DELETE FROM t1 WHERE a = -2;
+DELETE FROM t2 WHERE a = -2;
+DELETE FROM t1 WHERE a = -2;
+DELETE FROM t2 WHERE a = -2;
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-1
+SELECT * FROM t2 ORDER BY a;
+a
+-3
+-1
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-1
+SELECT * FROM t2 ORDER BY a;
+a
+-3
+-1
+Last_SQL_Error
+0
+INSERT IGNORE INTO t1 VALUES (-2);
+INSERT IGNORE INTO t1 VALUES (-2);
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-2
+-1
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-2
+-1
+Last_SQL_Error
+0
+UPDATE t1 SET a = 1 WHERE a = -1;
+UPDATE t2 SET a = 1 WHERE a = -1;
+UPDATE t1 SET a = 1 WHERE a = -1;
+UPDATE t2 SET a = 1 WHERE a = -1;
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-2
+1
+SELECT * FROM t2 ORDER BY a;
+a
+-3
+1
+SELECT * FROM t1 ORDER BY a;
+a
+-3
+-2
+1
+SELECT * FROM t2 ORDER BY a;
+a
+-3
+1
+Last_SQL_Error
+0
+DROP TABLE t1, t2;
diff -Nrup a/mysql-test/suite/rpl/t/rpl_idempotency.test b/mysql-test/suite/rpl/t/rpl_idempotency.test
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/suite/rpl/t/rpl_idempotency.test 2007-10-30 21:17:14 +01:00
@@ -0,0 +1,79 @@
+# Testing various forms of idempotency for replication that should
+# work the same way under statement based as under row based.
+
+source include/master-slave.inc;
+
+connection master;
+CREATE TABLE t1 (a INT PRIMARY KEY);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (-1),(-2),(-3);
+INSERT INTO t2 VALUES (-1),(-2),(-3);
+sync_slave_with_master;
+
+# A delete for a row that does not exist, the statement is
+# deliberately written to be idempotent for statement-based
+# replication as well. We test this towards both a table with a
+# primary key and without a primary key.
+
+connection slave;
+DELETE FROM t1 WHERE a = -2;
+DELETE FROM t2 WHERE a = -2;
+connection master;
+DELETE FROM t1 WHERE a = -2;
+DELETE FROM t2 WHERE a = -2;
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+sync_slave_with_master;
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1);
+disable_query_log;
+eval SELECT "$last_error" AS Last_SQL_Error;
+enable_query_log;
+
+# An insert of a row that already exists. Since we are replacing the
+# row if it already exists, the most apropriate representation is
+# INSERT IGNORE. We only test this towards a table with a primary key,
+# since the other case does not make sense.
+
+INSERT IGNORE INTO t1 VALUES (-2);
+connection master;
+INSERT IGNORE INTO t1 VALUES (-2);
+SELECT * FROM t1 ORDER BY a;
+sync_slave_with_master;
+SELECT * FROM t1 ORDER BY a;
+let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1);
+disable_query_log;
+eval SELECT "$last_error" AS Last_SQL_Error;
+enable_query_log;
+
+# BUG#19958: RBR idempotency issue for UPDATE and DELETE
+
+# Statement-based and row-based replication have different behaviour
+# when updating a row with an explicit WHERE-clause that matches
+# exactly one row (or no row at all). For statement-based replication,
+# the statement is idempotent since the first time it is executed, it
+# will update exactly one row, and the second time it will not update
+# any row at all. This was not the case for row-based replication, so
+# we test under both row-based and statement-based replication both
+# for tables with and without primary keys.
+
+connection slave;
+UPDATE t1 SET a = 1 WHERE a = -1;
+UPDATE t2 SET a = 1 WHERE a = -1;
+connection master;
+UPDATE t1 SET a = 1 WHERE a = -1;
+UPDATE t2 SET a = 1 WHERE a = -1;
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+sync_slave_with_master;
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1);
+disable_query_log;
+eval SELECT "$last_error" AS Last_SQL_Error;
+enable_query_log;
+
+connection master;
+DROP TABLE t1, t2;
+sync_slave_with_master;
diff -Nrup a/sql/log_event.cc b/sql/log_event.cc
--- a/sql/log_event.cc 2007-10-24 16:54:11 +02:00
+++ b/sql/log_event.cc 2007-10-30 21:17:13 +01:00
@@ -36,6 +36,63 @@
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
+#ifndef MYSQL_CLIENT
+static const char *HA_ERR(int i)
+{
+ switch (i) {
+ case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND";
+ case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY";
+ case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED";
+ case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX";
+ case HA_ERR_CRASHED: return "HA_ERR_CRASHED";
+ case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD";
+ case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM";
+ case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE";
+ case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND";
+ case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE";
+ case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD";
+ case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED";
+ case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL";
+ case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL";
+ case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE";
+ case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED";
+ case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW";
+ case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION";
+ case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE";
+ case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET";
+ case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF";
+ case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR";
+ case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE";
+ case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT";
+ case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL";
+ case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION";
+ case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK";
+ case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN";
+ case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW";
+ case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED";
+ case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT";
+ case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE";
+ case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE";
+ case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST";
+ case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION";
+ case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL";
+ case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED";
+ case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND";
+ case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED";
+ case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK";
+ case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY";
+ case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE";
+ case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY";
+ case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED";
+ case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE";
+ case HA_ERR_GENERIC: return "HA_ERR_GENERIC";
+ case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME";
+ case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE";
+ case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT";
+ }
+}
+#endif
+
/*
Cache that will automatically be written to a dedicated file on
destruction.
@@ -6228,8 +6285,11 @@ int Rows_log_event::do_apply_event(Relay
/* Some recoverable errors */
case HA_ERR_RECORD_CHANGED:
- case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if
- tuple does not exist */
+ case HA_ERR_RECORD_DELETED:
+ case HA_ERR_KEY_NOT_FOUND:
+ case HA_ERR_END_OF_FILE:
+ /* Idempotency support: OK if tuple does not exist */
+ DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
error= 0;
break;
@@ -7467,6 +7527,9 @@ static bool record_compare(TABLE *table)
records. Check that the other engines also return correct records.
*/
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
+
bool result= FALSE;
uchar saved_x[2], saved_filler[2];
@@ -7555,7 +7618,7 @@ record_compare_exit:
int Rows_log_event::find_row(const Relay_log_info *rli)
{
- DBUG_ENTER("find_row");
+ DBUG_ENTER("Rows_log_event::find_row");
DBUG_ASSERT(m_table && m_table->in_use != NULL);
@@ -7784,7 +7847,7 @@ int Rows_log_event::find_row(const Relay
DBUG_DUMP("record found", table->record[0], table->s->reclength);
table->file->ha_rnd_end();
- DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+ DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == HA_ERR_RECORD_DELETED || error == 0);
DBUG_RETURN(error);
}
| Thread |
|---|
| • bk commit into 5.1 tree (mats:1.2582) BUG#19958 | Mats Kindahl | 30 Oct |