summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/extra/rpl_tests/rpl_ndb_2multi_basic.test119
-rw-r--r--mysql-test/include/rpl_multi_engine2.inc91
-rw-r--r--mysql-test/suite/rpl_ndb/r/rpl_ndb_2other.result1189
-rw-r--r--mysql-test/suite/rpl_ndb/t/disabled.def4
-rw-r--r--mysql-test/suite/rpl_ndb/t/rpl_ndb_2other-slave.opt1
-rw-r--r--mysql-test/suite/rpl_ndb/t/rpl_ndb_2other.test35
-rw-r--r--sql/field.cc22
-rw-r--r--sql/log_event.cc861
-rw-r--r--sql/log_event.h109
-rw-r--r--sql/log_event_old.cc1115
-rw-r--r--sql/log_event_old.h130
-rw-r--r--sql/rpl_record.cc79
-rw-r--r--sql/rpl_record.h10
-rw-r--r--sql/rpl_utility.cc2
-rw-r--r--sql/rpl_utility.h42
-rw-r--r--sql/slave.cc91
-rw-r--r--sql/slave.h4
-rw-r--r--storage/innobase/dict/dict0mem.c2
-rw-r--r--storage/innobase/handler/ha_innodb.cc4
-rw-r--r--storage/innobase/row/row0sel.c10
20 files changed, 3289 insertions, 631 deletions
diff --git a/mysql-test/extra/rpl_tests/rpl_ndb_2multi_basic.test b/mysql-test/extra/rpl_tests/rpl_ndb_2multi_basic.test
new file mode 100644
index 00000000000..16f8116d92e
--- /dev/null
+++ b/mysql-test/extra/rpl_tests/rpl_ndb_2multi_basic.test
@@ -0,0 +1,119 @@
+#######################################
+# Author: Rafal Somla #
+# Date: 2006-08-20 #
+# Purpose: Test replication of basic #
+# table operations in various setups #
+# #
+# Based on rpl_ndb_2multi_eng.test by #
+# JBM #
+#######################################
+
+--echo --- Doing pre test cleanup ---
+
+connection master;
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_query_log
+
+#################################################
+--echo --- Create Table Section ---
+
+CREATE TABLE t1 (id MEDIUMINT NOT NULL,
+ b1 INT,
+ vc VARCHAR(255),
+ bc CHAR(255),
+ d DECIMAL(10,4) DEFAULT 0,
+ f FLOAT DEFAULT 0,
+ total BIGINT UNSIGNED,
+ y YEAR,
+ t DATE,
+ PRIMARY KEY(id));
+
+--echo --- Show table on master ---
+
+SHOW CREATE TABLE t1;
+
+--echo --- Show table on slave ---
+
+sync_slave_with_master;
+SHOW CREATE TABLE t1;
+
+--source include/rpl_multi_engine2.inc
+
+#################################################
+# Okay lets see how it holds up to table changes
+--echo --- Check that simple Alter statements are replicated correctly --
+
+ALTER TABLE t1 DROP PRIMARY KEY;
+# note: table with no PK can't contain blobs if it is to be replicated.
+ALTER TABLE t1 MODIFY vc char(32);
+
+--echo --- Show the new improved table on the master ---
+
+SHOW CREATE TABLE t1;
+
+--echo --- Make sure that our tables on slave are still same engine ---
+--echo --- and that the alter statements replicated correctly ---
+
+sync_slave_with_master;
+SHOW CREATE TABLE t1;
+
+--source include/rpl_multi_engine2.inc
+
+#################################################
+--echo --- Check that replication works when slave has more columns than master
+connection master;
+ALTER TABLE t1 ADD PRIMARY KEY(id,total);
+ALTER TABLE t1 MODIFY vc TEXT;
+
+INSERT INTO t1 VALUES(3,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1905-11-14");
+INSERT INTO t1 VALUES(20,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1965-11-14");
+INSERT INTO t1 VALUES(50,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1985-11-14");
+
+--echo --- Add columns on slave ---
+--sync_slave_with_master
+ALTER TABLE t1 ADD (u int, v char(16) default 'default');
+UPDATE t1 SET u=7 WHERE id < 50;
+UPDATE t1 SET v='explicit' WHERE id >10;
+
+--echo --- Show changed table on slave ---
+
+SHOW CREATE TABLE t1;
+SELECT *
+FROM t1
+ORDER BY id;
+
+--source include/rpl_multi_engine2.inc
+TRUNCATE TABLE t1;
+
+#################################################
+--echo --- Check that replication works when master has more columns than slave
+connection master;
+
+--echo --- Remove columns on slave ---
+--sync_slave_with_master
+ALTER TABLE t1 DROP COLUMN v;
+ALTER TABLE t1 DROP COLUMN u;
+ALTER TABLE t1 DROP COLUMN t;
+ALTER TABLE t1 DROP COLUMN y;
+
+--echo --- Show changed table on slave ---
+
+SHOW CREATE TABLE t1;
+
+--source include/rpl_multi_engine2.inc
+TRUNCATE TABLE t1;
+
+#################################################
+--echo --- Do Cleanup --
+connection master;
+DROP TABLE IF EXISTS t1;
+
+sync_slave_with_master;
+connection master;
diff --git a/mysql-test/include/rpl_multi_engine2.inc b/mysql-test/include/rpl_multi_engine2.inc
new file mode 100644
index 00000000000..e683a1d5838
--- /dev/null
+++ b/mysql-test/include/rpl_multi_engine2.inc
@@ -0,0 +1,91 @@
+#############################################################
+# Author: Rafal
+# Date: 2007-08-20
+# based on rpl_multi_engine3.inc
+#############################################################
+
+connection slave;
+STOP SLAVE;
+RESET SLAVE;
+
+connection master;
+RESET MASTER;
+
+connection slave;
+START SLAVE;
+
+--echo --- Populate t1 with data ---
+connection master;
+--disable_query_log
+INSERT INTO t1 VALUES(42,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1905-11-14");
+INSERT INTO t1 VALUES(2,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1965-11-14");
+INSERT INTO t1 VALUES(4,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1985-11-14");
+INSERT INTO t1 VALUES(142,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"1995-11-14");
+INSERT INTO t1 VALUES(412,1,'Testing MySQL databases is a cool ',
+ 'Must make it bug free for the customer',
+ 654321.4321,15.21,0,1965,"2005-11-14");
+--enable_query_log
+
+--echo --- Select from t1 on master ---
+select *
+from t1
+order by id;
+
+sync_slave_with_master;
+--echo --- Select from t1 on slave ---
+select *
+from t1
+order by id;
+
+--echo --- Perform basic operation on master ---
+--echo --- and ensure replicated correctly ---
+connection master;
+
+--echo --- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+
+--echo --- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+
+# Must give injector thread a little time to get update
+# into the binlog other wise we will miss the update.
+
+sync_slave_with_master;
+--echo --- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+
+connection master;
+--echo --- Remove a record from t1 on master ---
+# Note: there is an error in replication of Delete_row
+# from NDB to MyISAM (BUG#28538). However, if there is
+# only one row in Delete_row event then it works fine,
+# as this test demonstrates.
+DELETE FROM t1 WHERE id = 412;
+
+--echo --- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+
+sync_slave_with_master;
+--echo --- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+
+connection master;
+TRUNCATE TABLE t1;
+sync_slave_with_master;
+connection master;
diff --git a/mysql-test/suite/rpl_ndb/r/rpl_ndb_2other.result b/mysql-test/suite/rpl_ndb/r/rpl_ndb_2other.result
new file mode 100644
index 00000000000..56b997028e9
--- /dev/null
+++ b/mysql-test/suite/rpl_ndb/r/rpl_ndb_2other.result
@@ -0,0 +1,1189 @@
+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;
+SET storage_engine=ndb;
+
+=== NDB -> MYISAM ===
+
+SET storage_engine=myisam;
+--- Doing pre test cleanup ---
+DROP TABLE IF EXISTS t1;
+--- Create Table Section ---
+CREATE TABLE t1 (id MEDIUMINT NOT NULL,
+b1 INT,
+vc VARCHAR(255),
+bc CHAR(255),
+d DECIMAL(10,4) DEFAULT 0,
+f FLOAT DEFAULT 0,
+total BIGINT UNSIGNED,
+y YEAR,
+t DATE,
+PRIMARY KEY(id));
+--- Show table on master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Show table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that simple Alter statements are replicated correctly --
+ALTER TABLE t1 DROP PRIMARY KEY;
+ALTER TABLE t1 MODIFY vc char(32);
+--- Show the new improved table on the master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Make sure that our tables on slave are still same engine ---
+--- and that the alter statements replicated correctly ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that replication works when slave has more columns than master
+ALTER TABLE t1 ADD PRIMARY KEY(id,total);
+ALTER TABLE t1 MODIFY vc TEXT;
+INSERT INTO t1 VALUES(3,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1905-11-14");
+INSERT INTO t1 VALUES(20,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1965-11-14");
+INSERT INTO t1 VALUES(50,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1985-11-14");
+--- Add columns on slave ---
+ALTER TABLE t1 ADD (u int, v char(16) default 'default');
+UPDATE t1 SET u=7 WHERE id < 50;
+UPDATE t1 SET v='explicit' WHERE id >10;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ `u` int(11) DEFAULT NULL,
+ `v` char(16) DEFAULT 'default',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT *
+FROM t1
+ORDER BY id;
+id b1 vc bc d f total y t u v
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t u v
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 NULL default
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 NULL default
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 NULL default
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 NULL default
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t u v
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 default
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 explicit
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL explicit
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Check that replication works when master has more columns than slave
+--- Remove columns on slave ---
+ALTER TABLE t1 DROP COLUMN v;
+ALTER TABLE t1 DROP COLUMN u;
+ALTER TABLE t1 DROP COLUMN t;
+ALTER TABLE t1 DROP COLUMN y;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Do Cleanup --
+DROP TABLE IF EXISTS t1;
+
+=== NDB -> INNODB ===
+
+SET storage_engine=innodb;
+--- Doing pre test cleanup ---
+DROP TABLE IF EXISTS t1;
+--- Create Table Section ---
+CREATE TABLE t1 (id MEDIUMINT NOT NULL,
+b1 INT,
+vc VARCHAR(255),
+bc CHAR(255),
+d DECIMAL(10,4) DEFAULT 0,
+f FLOAT DEFAULT 0,
+total BIGINT UNSIGNED,
+y YEAR,
+t DATE,
+PRIMARY KEY(id));
+--- Show table on master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Show table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that simple Alter statements are replicated correctly --
+ALTER TABLE t1 DROP PRIMARY KEY;
+ALTER TABLE t1 MODIFY vc char(32);
+--- Show the new improved table on the master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Make sure that our tables on slave are still same engine ---
+--- and that the alter statements replicated correctly ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that replication works when slave has more columns than master
+ALTER TABLE t1 ADD PRIMARY KEY(id,total);
+ALTER TABLE t1 MODIFY vc TEXT;
+INSERT INTO t1 VALUES(3,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1905-11-14");
+INSERT INTO t1 VALUES(20,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1965-11-14");
+INSERT INTO t1 VALUES(50,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1985-11-14");
+--- Add columns on slave ---
+ALTER TABLE t1 ADD (u int, v char(16) default 'default');
+UPDATE t1 SET u=7 WHERE id < 50;
+UPDATE t1 SET v='explicit' WHERE id >10;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ `u` int(11) DEFAULT NULL,
+ `v` char(16) DEFAULT 'default',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT *
+FROM t1
+ORDER BY id;
+id b1 vc bc d f total y t u v
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t u v
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 NULL default
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 NULL default
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 NULL default
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 NULL default
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t u v
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 default
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 explicit
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL explicit
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Check that replication works when master has more columns than slave
+--- Remove columns on slave ---
+ALTER TABLE t1 DROP COLUMN v;
+ALTER TABLE t1 DROP COLUMN u;
+ALTER TABLE t1 DROP COLUMN t;
+ALTER TABLE t1 DROP COLUMN y;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Do Cleanup --
+DROP TABLE IF EXISTS t1;
+
+=== NDB -> NDB ===
+
+SET storage_engine=ndb;
+--- Doing pre test cleanup ---
+DROP TABLE IF EXISTS t1;
+--- Create Table Section ---
+CREATE TABLE t1 (id MEDIUMINT NOT NULL,
+b1 INT,
+vc VARCHAR(255),
+bc CHAR(255),
+d DECIMAL(10,4) DEFAULT 0,
+f FLOAT DEFAULT 0,
+total BIGINT UNSIGNED,
+y YEAR,
+t DATE,
+PRIMARY KEY(id));
+--- Show table on master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Show table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` varchar(255) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that simple Alter statements are replicated correctly --
+ALTER TABLE t1 DROP PRIMARY KEY;
+ALTER TABLE t1 MODIFY vc char(32);
+--- Show the new improved table on the master ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=ndbcluster DEFAULT CHARSET=latin1
+--- Make sure that our tables on slave are still same engine ---
+--- and that the alter statements replicated correctly ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` char(32) DEFAULT NULL,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned DEFAULT NULL,
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a coo Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a coo updated 654321.4321 15.21 0 1965 2006-02-22
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+--- Check that replication works when slave has more columns than master
+ALTER TABLE t1 ADD PRIMARY KEY(id,total);
+ALTER TABLE t1 MODIFY vc TEXT;
+INSERT INTO t1 VALUES(3,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1905-11-14");
+INSERT INTO t1 VALUES(20,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1965-11-14");
+INSERT INTO t1 VALUES(50,1,'Testing MySQL databases is a cool ',
+'Must make it bug free for the customer',
+654321.4321,15.21,0,1965,"1985-11-14");
+--- Add columns on slave ---
+ALTER TABLE t1 ADD (u int, v char(16) default 'default');
+UPDATE t1 SET u=7 WHERE id < 50;
+UPDATE t1 SET v='explicit' WHERE id >10;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `y` year(4) DEFAULT NULL,
+ `t` date DEFAULT NULL,
+ `u` int(11) DEFAULT NULL,
+ `v` char(16) DEFAULT 'default',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT *
+FROM t1
+ORDER BY id;
+id b1 vc bc d f total y t u v
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t u v
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 NULL default
+3 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 7 default
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL default
+20 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14 7 explicit
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14 NULL default
+50 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14 NULL explicit
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14 NULL default
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14 NULL default
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t u v
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+3 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 default
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+20 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 7 explicit
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL default
+50 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22 NULL explicit
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+7
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Check that replication works when master has more columns than slave
+--- Remove columns on slave ---
+ALTER TABLE t1 DROP COLUMN v;
+ALTER TABLE t1 DROP COLUMN u;
+ALTER TABLE t1 DROP COLUMN t;
+ALTER TABLE t1 DROP COLUMN y;
+--- Show changed table on slave ---
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` mediumint(9) NOT NULL,
+ `b1` int(11) DEFAULT NULL,
+ `vc` text,
+ `bc` char(255) DEFAULT NULL,
+ `d` decimal(10,4) DEFAULT '0.0000',
+ `f` float DEFAULT '0',
+ `total` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`,`total`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+STOP SLAVE;
+RESET SLAVE;
+RESET MASTER;
+START SLAVE;
+--- Populate t1 with data ---
+--- Select from t1 on master ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total y t
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1965-11-14
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1985-11-14
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1905-11-14
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 1995-11-14
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0 1965 2005-11-14
+--- Select from t1 on slave ---
+select *
+from t1
+order by id;
+id b1 vc bc d f total
+2 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+4 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+42 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+142 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+412 1 Testing MySQL databases is a cool Must make it bug free for the customer 654321.4321 15.21 0
+--- Perform basic operation on master ---
+--- and ensure replicated correctly ---
+--- Update t1 on master --
+UPDATE t1 SET b1 = 0, bc='updated', t="2006-02-22"
+WHERE id < 100
+ORDER BY id;
+--- Check the update on master ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total y t
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0 1965 2006-02-22
+--- Check Update on slave ---
+SELECT *
+FROM t1
+WHERE id < 100
+ORDER BY id;
+id b1 vc bc d f total
+2 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+4 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+42 0 Testing MySQL databases is a cool updated 654321.4321 15.21 0
+--- Remove a record from t1 on master ---
+DELETE FROM t1 WHERE id = 412;
+--- Show current count on master for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+--- Show current count on slave for t1 ---
+SELECT COUNT(*) FROM t1;
+COUNT(*)
+4
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t1;
+--- Do Cleanup --
+DROP TABLE IF EXISTS t1;
diff --git a/mysql-test/suite/rpl_ndb/t/disabled.def b/mysql-test/suite/rpl_ndb/t/disabled.def
index f4cbd3e9d0c..90286ecc421 100644
--- a/mysql-test/suite/rpl_ndb/t/disabled.def
+++ b/mysql-test/suite/rpl_ndb/t/disabled.def
@@ -14,8 +14,8 @@
rpl_ndb_2innodb : BUG#19227 2006-04-20 pekka pk delete apparently not replicated
rpl_ndb_2myisam : BUG#19227 Seems to pass currently
rpl_ndb_dd_partitions : BUG#19259 2006-04-21 rpl_ndb_dd_partitions fails on s/AMD
-#rpl_ndb_innodb2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue
-#rpl_ndb_myisam2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue
+rpl_ndb_innodb2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue
+rpl_ndb_myisam2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue
rpl_ndb_ddl : BUG#28798 2007-05-31 lars Valgrind failure in NDB
rpl_ndb_mix_innodb : BUG#28123 rpl_ndb_mix_innodb.test casue slave to core on sol10-sparc-a
rpl_ndb_ctype_ucs2_def : BUG#27404 util thd mysql_parse sig11 when mysqld default multibyte charset
diff --git a/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other-slave.opt b/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other-slave.opt
new file mode 100644
index 00000000000..a6c65034e68
--- /dev/null
+++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other-slave.opt
@@ -0,0 +1 @@
+--innodb --log-slave-updates=0
diff --git a/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other.test b/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other.test
new file mode 100644
index 00000000000..736e2cadf78
--- /dev/null
+++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_2other.test
@@ -0,0 +1,35 @@
+#############################################################
+# Author: Rafal Somla
+# Date: 2006-08-20
+# Purpose: Trying to test ability to replicate from cluster
+# to other engines (innodb, myisam).
+##############################################################
+--source include/have_ndb.inc
+--source include/have_innodb.inc
+--source include/have_binlog_format_mixed_or_row.inc
+--source include/master-slave.inc
+
+# On master use NDB as storage engine.
+connection master;
+SET storage_engine=ndb;
+
+--echo
+--echo === NDB -> MYISAM ===
+--echo
+connection slave;
+SET storage_engine=myisam;
+--source extra/rpl_tests/rpl_ndb_2multi_basic.test
+
+--echo
+--echo === NDB -> INNODB ===
+--echo
+connection slave;
+SET storage_engine=innodb;
+--source extra/rpl_tests/rpl_ndb_2multi_basic.test
+
+--echo
+--echo === NDB -> NDB ===
+--echo
+connection slave;
+SET storage_engine=ndb;
+--source extra/rpl_tests/rpl_ndb_2multi_basic.test
diff --git a/sql/field.cc b/sql/field.cc
index 2c61f26ad6a..fa93454c757 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -7796,19 +7796,10 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
uint32 length=get_length(); // Length of from string
if (length > max_length)
{
- ptr=to;
length=max_length;
- store_length(length); // Store max length
- ptr= (uchar*) from;
+ store_length(to,packlength,length,TRUE);
}
else
-#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- {
- store_length(to,packlength,length,0);
- }
- else
-#endif
memcpy(to,from,packlength); // Copy length
if (length)
{
@@ -7846,16 +7837,7 @@ const uchar *Field_blob::unpack(uchar *to,
const uchar *Field_blob::unpack(uchar *to, const uchar *from)
{
uint32 length=get_length(from);
-#ifdef WORDS_BIGENDIAN
- if (table->s->db_low_byte_first)
- {
- store_length(to,packlength,length,1);
- }
- else
-#endif
- {
- memcpy(to,from,packlength);
- }
+ memcpy(to,from,packlength);
from+=packlength;
if (length)
memcpy_fixed(to+packlength, &from, sizeof(from));
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7d9c7c69574..44898ca8639 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -5653,14 +5653,16 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
m_table(tbl_arg),
m_table_id(tid),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
- m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
- m_flags(0)
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
+#ifdef HAVE_REPLICATION
+ ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL)
+#endif
{
/*
We allow a special form of dummy event when the table, and cols
are null and the table id is ~0UL. This is a temporary
solution, to be able to terminate a started statement in the
- binary log: the extreneous events will be removed in the future.
+ binary log: the extraneous events will be removed in the future.
*/
DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
!tbl_arg && !cols && tid == ~0UL);
@@ -5669,7 +5671,7 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
set_flags(NO_FOREIGN_KEY_CHECKS_F);
if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
set_flags(RELAXED_UNIQUE_CHECKS_F);
- /* if bitmap_init fails, catched in is_valid() */
+ /* if bitmap_init fails, caught in is_valid() */
if (likely(!bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
m_width,
@@ -5696,7 +5698,13 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
*description_event)
: Log_event(buf, description_event),
m_row_count(0),
- m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+#ifndef MYSQL_CLIENT
+ m_table(NULL),
+#endif
+ m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ ,m_key(NULL), m_curr_row(NULL), m_curr_row_end(NULL)
+#endif
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
uint8 const common_header_len= description_event->common_header_len;
@@ -5755,7 +5763,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
{
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
- /* if bitmap_init fails, catched in is_valid() */
+ /* if bitmap_init fails, caught in is_valid() */
if (likely(!bitmap_init(&m_cols_ai,
m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
m_width,
@@ -5785,6 +5793,9 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
if (likely((bool)m_rows_buf))
{
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ m_curr_row= m_rows_buf;
+#endif
m_rows_end= m_rows_buf + data_size;
m_rows_cur= m_rows_end;
memcpy(m_rows_buf, ptr_rows_data, data_size);
@@ -5889,7 +5900,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
{
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
int error= 0;
- uchar const *row_start= m_rows_buf;
/*
If m_table_id == ~0UL, then we have a dummy event that does not
@@ -6049,7 +6059,9 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
#endif
}
- TABLE* table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
+ TABLE*
+ table=
+ m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
if (table)
{
@@ -6096,22 +6108,41 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
- error= do_before_row_operations(table);
- while (error == 0 && row_start < m_rows_end)
- {
- uchar const *row_end= NULL;
- if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
- break; // We should perform the after-row operation even in
- // the case of error
+ if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ set_flags(COMPLETE_ROWS_F);
+
+ /*
+ Set tables write and read sets.
+
+ Read_set contains all slave columns (in case we are going to fetch
+ a complete record from slave)
+
+ Write_set equals the m_cols bitmap sent from master but it can be
+ longer if slave has extra columns.
+ */
+
+ DBUG_PRINT_BITSET("debug", "Setting table's write_set from: %s", &m_cols);
+
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ if (!get_flags(COMPLETE_ROWS_F))
+ bitmap_intersect(table->write_set,&m_cols);
+
+ // Do event specific preparations
+
+ error= do_before_row_operations(rli);
- DBUG_ASSERT(row_end != NULL); // cannot happen
- DBUG_ASSERT(row_end <= m_rows_end);
+ // row processing loop
+ while (error == 0 && m_curr_row < m_rows_end)
+ {
/* in_use can have been set to NULL in close_tables_for_reopen */
THD* old_thd= table->in_use;
if (!table->in_use)
table->in_use= thd;
- error= do_exec_row(table);
+
+ error= do_exec_row(rli);
+
table->in_use = old_thd;
switch (error)
{
@@ -6132,21 +6163,38 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
break;
}
- row_start= row_end;
- }
+ /*
+ If m_curr_row_end was not set during event execution (e.g., because
+ of errors) we can't proceed to the next row. If the error is transient
+ (i.e., error==0 at this point) we must call unpack_current_row() to set
+ m_curr_row_end.
+ */
+
+ if (!m_curr_row_end && !error)
+ unpack_current_row(rli);
+
+ // at this moment m_curr_row_end should be set
+ DBUG_ASSERT(error || m_curr_row_end != NULL);
+ DBUG_ASSERT(error || m_curr_row < m_curr_row_end);
+ DBUG_ASSERT(error || m_curr_row_end <= m_rows_end);
+
+ m_curr_row= m_curr_row_end;
+
+ } // row processing loop
+
DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
- error= do_after_row_operations(table, error);
+ error= do_after_row_operations(rli, error);
if (!cache_stmt)
{
DBUG_PRINT("info", ("Marked that we need to keep log"));
thd->options|= OPTION_KEEP_LOG;
}
- }
+ } // if (table)
/*
- We need to delay this clear until the table def is no longer needed.
- The table def is needed in unpack_row().
+ We need to delay this clear until here bacause unpack_current_row() uses
+ master-side table definitions stored in rli.
*/
if (rli->tables_to_lock && get_flags(STMT_END_F))
const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
@@ -6199,7 +6247,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
wait (reached end of last relay log and nothing gets appended
there), we timeout after one minute, and notify DBA about the
problem. When WL#2975 is implemented, just remove the member
- Relay_log_info::last_event_start_time and all its occurences.
+ Relay_log_info::last_event_start_time and all its occurrences.
*/
const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
}
@@ -6492,7 +6540,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
m_data_size+= m_tbllen + 2; // Include length and terminating \0
m_data_size+= 1 + m_colcnt; // COLCNT and column types
- /* If malloc fails, catched in is_valid() */
+ /* If malloc fails, caught in is_valid() */
if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
{
m_coltype= reinterpret_cast<uchar*>(m_memory);
@@ -6611,7 +6659,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
(ulong) m_tbllen, (long) (ptr_tbllen-(const uchar*)vpart),
m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart)));
- /* Allocate mem for all fields in one go. If fails, catched in is_valid() */
+ /* Allocate mem for all fields in one go. If fails, caught in is_valid() */
m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
&m_dbnam, (uint) m_dblen + 1,
&m_tblnam, (uint) m_tbllen + 1,
@@ -6937,7 +6985,8 @@ Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Write_rows_log_event::do_before_row_operations(TABLE *table)
+int
+Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
int error= 0;
@@ -6959,26 +7008,26 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table)
/*
Do not raise the error flag in case of hitting to an unique attribute
*/
- table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
/*
NDB specific: update from ndb master wrapped as Write_rows
*/
/*
so that the event should be applied to replace slave's row
*/
- table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
/*
NDB specific: if update from ndb master wrapped as Write_rows
does not find the row it's assumed idempotent binlog applying
is taking place; don't raise the error.
*/
- table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
/*
TODO: the cluster team (Tomas?) says that it's better if the engine knows
how many rows are going to be inserted, then it can allocate needed memory
from the start.
*/
- table->file->ha_start_bulk_insert(0);
+ m_table->file->ha_start_bulk_insert(0);
/*
We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
any TIMESTAMP column with data from the row but instead will use
@@ -6994,47 +7043,31 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table)
some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
analyze if explicit data is provided for slave's TIMESTAMP columns).
*/
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
return error;
}
-int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int
+Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
{
int local_error= 0;
- table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
/*
reseting the extra with
table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
fires bug#27077
todo: explain or fix
*/
- if ((local_error= table->file->ha_end_bulk_insert()))
+ if ((local_error= m_table->file->ha_end_bulk_insert()))
{
- table->file->print_error(local_error, MYF(0));
+ m_table->file->print_error(local_error, MYF(0));
}
return error? error : local_error;
}
-
-int Write_rows_log_event::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
- TABLE *table,
- uchar const *const row_start,
- uchar const **const row_end)
-{
- DBUG_ASSERT(table != NULL);
- DBUG_ASSERT(row_start && row_end);
-
- if (int error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->write_set, WRITE_ROWS_EVENT))
- {
- thd_arg->net.last_errno= error;
- return error;
- }
- bitmap_copy(table->read_set, table->write_set);
- return 0;
-}
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
/*
Check if there are more UNIQUE keys after the given key.
@@ -7048,135 +7081,6 @@ last_uniq_key(TABLE *table, uint keyno)
return 1;
}
-/* Anonymous namespace for template functions/classes */
-namespace {
-
- /*
- Smart pointer that will automatically call my_afree (a macro) when
- the pointer goes out of scope. This is used so that I do not have
- to remember to call my_afree() before each return. There is no
- overhead associated with this, since all functions are inline.
-
- I (Matz) would prefer to use the free function as a template
- parameter, but that is not possible when the "function" is a
- macro.
- */
- template <class Obj>
- class auto_afree_ptr
- {
- Obj* m_ptr;
- public:
- auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
- ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
- void assign(Obj* ptr) {
- /* Only to be called if it hasn't been given a value before. */
- DBUG_ASSERT(m_ptr == NULL);
- m_ptr= ptr;
- }
- Obj* get() { return m_ptr; }
- };
-
-}
-
-
-/*
- Copy "extra" columns from record[1] to record[0].
-
- Copy the extra fields that are not present on the master but are
- present on the slave from record[1] to record[0]. This is used
- after fetching a record that are to be updated, either inside
- replace_record() or as part of executing an update_row().
- */
-static int
-copy_extra_record_fields(TABLE *table,
- size_t master_reclength,
- my_ptrdiff_t master_fields)
-{
- DBUG_ENTER("copy_extra_record_fields(table, master_reclen, master_fields)");
- DBUG_PRINT("info", ("Copying to 0x%lx "
- "from field %lu at offset %lu "
- "to field %d at offset %lu",
- (long) table->record[0],
- (ulong) master_fields, (ulong) master_reclength,
- table->s->fields, table->s->reclength));
- /*
- Copying the extra fields of the slave that does not exist on
- master into record[0] (which are basically the default values).
- */
-
- if (table->s->fields < (uint) master_fields)
- DBUG_RETURN(0);
-
- DBUG_ASSERT(master_reclength <= table->s->reclength);
- if (master_reclength < table->s->reclength)
- bmove_align(table->record[0] + master_reclength,
- table->record[1] + master_reclength,
- table->s->reclength - master_reclength);
-
- /*
- Bit columns are special. We iterate over all the remaining
- columns and copy the "extra" bits to the new record. This is
- not a very good solution: it should be refactored on
- opportunity.
-
- REFACTORING SUGGESTION (Matz). Introduce a member function
- similar to move_field_offset() called copy_field_offset() to
- copy field values and implement it for all Field subclasses. Use
- this function to copy data from the found record to the record
- that are going to be inserted.
-
- The copy_field_offset() function need to be a virtual function,
- which in this case will prevent copying an entire range of
- fields efficiently.
- */
- {
- Field **field_ptr= table->field + master_fields;
- for ( ; *field_ptr ; ++field_ptr)
- {
- /*
- Set the null bit according to the values in record[1]
- */
- if ((*field_ptr)->maybe_null() &&
- (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1])))
- (*field_ptr)->set_null();
- else
- (*field_ptr)->set_notnull();
-
- /*
- Do the extra work for special columns.
- */
- switch ((*field_ptr)->real_type())
- {
- default:
- /* Nothing to do */
- break;
-
- case MYSQL_TYPE_BIT:
- Field_bit *f= static_cast<Field_bit*>(*field_ptr);
- if (f->bit_len > 0)
- {
- my_ptrdiff_t const offset= table->record[1] - table->record[0];
- uchar const bits=
- get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len);
- set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len);
- }
- break;
- }
- }
- }
- DBUG_RETURN(0); // All OK
-}
-
-#define DBUG_PRINT_BITSET(N,FRM,BS) \
- do { \
- char buf[256]; \
- for (uint i = 0 ; i < (BS)->n_bits ; ++i) \
- buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
- buf[(BS)->n_bits] = '\0'; \
- DBUG_PRINT((N), ((FRM), buf)); \
- } while (0)
-
-
/**
Check if an error is a duplicate key error.
@@ -7202,45 +7106,76 @@ is_duplicate_key_error(int errcode)
return false;
}
+/**
+ Write the current row into event's table.
-/*
- Replace the provided record in the database.
+ The row is located in the row buffer, pointed by @c m_curr_row member.
+ Number of columns of the row is stored in @c m_width member (it can be
+ different from the number of columns in the table to which we insert).
+ Bitmap @c m_cols indicates which columns are present in the row. It is assumed
+ that event's table is already open and pointed by @c m_table.
- SYNOPSIS
- replace_record()
- thd Thread context for writing the record.
- table Table to which record should be written.
- master_reclength
- Offset to first column that is not present on the master,
- alternatively the length of the record on the master
- side.
+ If the same record already exists in the table it can be either overwritten
+ or an error is reported depending on the value of @c overwrite flag
+ (error reporting not yet implemented). Note that the matching record can be
+ different from the row we insert if we use primary keys to identify records in
+ the table.
- RETURN VALUE
- Error code on failure, 0 on success.
+ The row to be inserted can contain values only for selected columns. The
+ missing columns are filled with default values using @c prepare_record()
+ function. If a matching record is found in the table and @c overwritte is
+ true, the missing columns are taken from it.
- DESCRIPTION
- Similar to how it is done in mysql_insert(), we first try to do
- a ha_write_row() and of that fails due to duplicated keys (or
- indices), we do an ha_update_row() or a ha_delete_row() instead.
- */
-static int
-replace_record(THD *thd, TABLE *table,
- ulong const master_reclength,
- uint const master_fields)
+ @param rli Relay log info (needed for row unpacking).
+ @param overwrite
+ Shall we overwrite if the row already exists or signal
+ error (currently ignored).
+
+ @returns Error code on failure, 0 on success.
+
+ This method, if successful, sets @c m_curr_row_end pointer to point at the
+ next row in the rows buffer. This is done when unpacking the row to be
+ inserted.
+
+ @note If a matching record is found, it is either updated using
+ @c ha_update_row() or first deleted and then new record written.
+*/
+
+int
+Rows_log_event::write_row(const Relay_log_info *const rli,
+ const bool overwrite)
{
- DBUG_ENTER("replace_record");
- DBUG_ASSERT(table != NULL && thd != NULL);
+ DBUG_ENTER("write_row");
+ DBUG_ASSERT(m_table != NULL && thd != NULL);
+ TABLE *table= m_table; // pointer to event's table
int error;
int keynum;
auto_afree_ptr<char> key(NULL);
+ /* fill table->record[0] with default values */
+
+ if ((error= prepare_record(rli, table, m_width,
+ TRUE /* check if columns have def. values */)))
+ DBUG_RETURN(error);
+
+ /* unpack row into table->record[0] */
+ error= unpack_current_row(rli); // TODO: how to handle errors?
+
#ifndef DBUG_OFF
DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif
+ /*
+ Try to write record. If a corresponding record already exists in the table,
+ we try to change it using ha_update_row() if possible. Otherwise we delete
+ it and repeat the whole process again.
+
+ TODO: Add safety measures against infinite looping.
+ */
+
while ((error= table->file->ha_write_row(table->record[0])))
{
if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
@@ -7250,6 +7185,7 @@ replace_record(THD *thd, TABLE *table,
}
if ((keynum= table->file->get_dup_key(error)) < 0)
{
+ DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum));
table->file->print_error(error, MYF(0));
/*
We failed to retrieve the duplicate key
@@ -7271,17 +7207,22 @@ replace_record(THD *thd, TABLE *table,
*/
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
{
+ DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
if (error)
{
+ DBUG_PRINT("info",("rnd_pos() returns error %d",error));
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
}
else
{
+ DBUG_PRINT("info",("Locating offending record using index_read_idx()"));
+
if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
{
+ DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
DBUG_RETURN(my_errno);
}
@@ -7289,7 +7230,10 @@ replace_record(THD *thd, TABLE *table,
{
key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
if (key.get() == NULL)
+ {
+ DBUG_PRINT("info",("Can't allocate key buffer"));
DBUG_RETURN(ENOMEM);
+ }
}
key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
@@ -7300,20 +7244,33 @@ replace_record(THD *thd, TABLE *table,
HA_READ_KEY_EXACT);
if (error)
{
+ DBUG_PRINT("info",("index_read_idx() returns error %d",error));
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
}
/*
- Now, table->record[1] should contain the offending row. That
+ Now, record[1] should contain the offending row. That
will enable us to update it or, alternatively, delete it (so
that we can insert the new row afterwards).
+ */
- First we copy the columns into table->record[0] that are not
- present on the master from table->record[1], if there are any.
+ /*
+ If row is incomplete we will use the record found to fill
+ missing columns.
*/
- copy_extra_record_fields(table, master_reclength, master_fields);
+ if (!get_flags(COMPLETE_ROWS_F))
+ {
+ restore_record(table,record[1]);
+ error= unpack_current_row(rli);
+ }
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("debug",("preparing for update: before and after image"));
+ DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
+ DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
+#endif
/*
REPLACE is defined as either INSERT or DELETE + INSERT. If
@@ -7333,18 +7290,32 @@ replace_record(THD *thd, TABLE *table,
if (last_uniq_key(table, keynum) &&
!table->file->referenced_by_foreign_key())
{
+ DBUG_PRINT("info",("Updating row using ha_update_row()"));
error=table->file->ha_update_row(table->record[1],
table->record[0]);
- if (error && error != HA_ERR_RECORD_IS_THE_SAME)
- table->file->print_error(error, MYF(0));
- else
+ switch (error) {
+
+ case HA_ERR_RECORD_IS_THE_SAME:
+ DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+ " ha_update_row()"));
error= 0;
+
+ case 0:
+ break;
+
+ default:
+ DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
+
DBUG_RETURN(error);
}
else
{
+ DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
if ((error= table->file->ha_delete_row(table->record[1])))
{
+ DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
}
@@ -7355,12 +7326,20 @@ replace_record(THD *thd, TABLE *table,
DBUG_RETURN(error);
}
-int Write_rows_log_event::do_exec_row(TABLE *table)
+#endif
+
+int
+Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
- DBUG_ASSERT(table != NULL);
- int error= replace_record(thd, table, m_master_reclength, m_width);
- return error;
+ DBUG_ASSERT(m_table != NULL);
+ int error= write_row(rli, TRUE /* overwrite */);
+
+ if (error && !thd->net.last_errno)
+ thd->net.last_errno= error;
+
+ return error;
}
+
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
#ifdef MYSQL_CLIENT
@@ -7453,40 +7432,52 @@ record_compare_exit:
return result;
}
+/**
+ Locate the current row in event's table.
-/*
- Find the row given by 'key', if the table has keys, or else use a table scan
- to find (and fetch) the row.
-
- If the engine allows random access of the records, a combination of
- position() and rnd_pos() will be used.
+ The current row is pointed by @c m_curr_row. Member @c m_width tells how many
+ columns are there in the row (this can be differnet from the number of columns
+ in the table). It is assumed that event's table is already open and pointed
+ by @c m_table.
- @param table Pointer to table to search
- @param key Pointer to key to use for search, if table has key
+ If a corresponding record is found in the table it is stored in
+ @c m_table->record[0]. Note that when record is located based on a primary
+ key, it is possible that the record found differs from the row being located.
- @pre <code>table->record[0]</code> shall contain the row to locate
- and <code>key</code> shall contain a key to use for searching, if
- the engine has a key.
+ If no key is specified or table does not have keys, a table scan is used to
+ find the row. In that case the row should be complete and contain values for
+ all columns. However, it can still be shorter than the table, i.e. the table
+ can contain extra columns not present in the row. It is also possible that
+ the table has fewer columns than the row being located.
- @post If the return value is zero, <code>table->record[1]</code>
- will contain the fetched row and the internal "cursor" will refer to
- the row. If the return value is non-zero,
- <code>table->record[1]</code> is undefined. In either case,
- <code>table->record[0]</code> is undefined.
+ @returns Error code on failure, 0 on success.
+
+ @post In case of success @c m_table->record[0] contains the record found.
+ Also, the internal "cursor" of the table is positioned at the record found.
- @return Zero if the row was successfully fetched into
- <code>table->record[1]</code>, error code otherwise.
+ @note If the engine allows random access of the records, a combination of
+ @c position() and @c rnd_pos() will be used.
*/
-static int find_and_fetch_row(TABLE *table, uchar *key)
+int Rows_log_event::find_row(const Relay_log_info *rli)
{
- DBUG_ENTER("find_and_fetch_row(TABLE *table, uchar *key, uchar *record)");
- DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx",
- (long) table, (long) key, (long) table->record[1]));
+ DBUG_ENTER("find_row");
+
+ DBUG_ASSERT(m_table && m_table->in_use != NULL);
+
+ TABLE *table= m_table;
+ int error;
- DBUG_ASSERT(table->in_use != NULL);
+ /* unpack row - missing fields get default values */
+ // TODO: shall we check and report errors here?
+ prepare_record(NULL,table,m_width,FALSE /* don't check errors */);
+ error= unpack_current_row(rli);
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("info",("looking for the following record"));
DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
table->s->primary_key < MAX_KEY)
@@ -7509,34 +7500,54 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
table->s->reclength) == 0);
*/
+ DBUG_PRINT("info",("locating record using primary key (position)"));
table->file->position(table->record[0]);
- int error= table->file->rnd_pos(table->record[0], table->file->ref);
- /*
- rnd_pos() returns the record in table->record[0], so we have to
- move it to table->record[1].
- */
- bmove_align(table->record[1], table->record[0], table->s->reclength);
+ error= table->file->rnd_pos(table->record[0], table->file->ref);
+ if (error)
+ {
+ DBUG_PRINT("info",("rnd_pos returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
DBUG_RETURN(error);
}
- /* We need to retrieve all fields */
- /* TODO: Move this out from this function to main loop */
+ // We can't use position() - try other methods.
+
+ /*
+ We need to retrieve all fields
+ TODO: Move this out from this function to main loop
+ */
table->use_all_columns();
+ /*
+ Save copy of the record in table->record[1]. It might be needed
+ later if linear search is used to find exact match.
+ */
+ store_record(table,record[1]);
+
if (table->s->keys > 0)
{
- int error;
+ DBUG_PRINT("info",("locating record using primary key (index_read)"));
+
/* We have a key: search the table using the index */
if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ {
+ DBUG_PRINT("info",("ha_index_init returns error %d",error));
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
- /*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
+ /* Fill key data for the row */
+
+ DBUG_ASSERT(m_key);
+ key_copy(m_key, table->record[0], table->key_info, 0);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
#ifndef HAVE_purify
- DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
- DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+ DBUG_DUMP("key data", m_key, table->key_info->key_length);
#endif
/*
@@ -7547,10 +7558,13 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
*/
my_ptrdiff_t const pos=
table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
- table->record[1][pos]= 0xFF;
- if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY,
+ table->record[0][pos]= 0xFF;
+
+ if ((error= table->file->index_read_map(table->record[0], m_key,
+ HA_WHOLE_KEY,
HA_READ_KEY_EXACT)))
{
+ DBUG_PRINT("info",("no record matching the key found in the table"));
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
DBUG_RETURN(error);
@@ -7561,8 +7575,8 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
trigger false warnings.
*/
#ifndef HAVE_purify
- DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
- DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+ DBUG_PRINT("info",("found first matching record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
#endif
/*
Below is a minor "optimization". If the key (i.e., key number
@@ -7584,10 +7598,15 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
DBUG_RETURN(0);
}
+ /*
+ In case key is not unique, we still have to iterate over records found
+ and find the one which is identical to the row given. A copy of the
+ record we are looking for is stored in record[1].
+ */
+ DBUG_PRINT("info",("non-unique index, scanning it to find matching record"));
+
while (record_compare(table))
{
- int error;
-
/*
We need to set the null bytes to ensure that the filler bit
are all set when returning. There are storage engines that
@@ -7599,15 +7618,16 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
*/
if (table->s->null_bytes > 0)
{
- table->record[1][table->s->null_bytes - 1]|=
+ table->record[0][table->s->null_bytes - 1]|=
256U - (1U << table->s->last_null_bit_pos);
}
- if ((error= table->file->index_next(table->record[1])))
+ if ((error= table->file->index_next(table->record[0])))
{
- table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info",("no record matching the given row found"));
+ table->file->print_error(error, MYF(0));
table->file->ha_index_end();
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
}
@@ -7618,44 +7638,57 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
}
else
{
+ DBUG_PRINT("info",("locating record using table scan (rnd_next)"));
+
int restart_count= 0; // Number of times scanning has restarted from top
- int error;
/* We don't have a key: search the table using rnd_next() */
if ((error= table->file->ha_rnd_init(1)))
- return error;
+ {
+ DBUG_PRINT("info",("error initializing table scan"
+ " (ha_rnd_init returns %d)",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
/* Continue until we find the right record or have made a full loop */
do
{
- error= table->file->rnd_next(table->record[1]);
-
- DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
- DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
+ error= table->file->rnd_next(table->record[0]);
switch (error) {
+
case 0:
case HA_ERR_RECORD_DELETED:
- break;
+ break;
case HA_ERR_END_OF_FILE:
- if (++restart_count < 2)
- table->file->ha_rnd_init(1);
- break;
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ break;
default:
- table->file->print_error(error, MYF(0));
- DBUG_PRINT("info", ("Record not found"));
+ DBUG_PRINT("info", ("Failed to get next record"
+ " (rnd_next returns %d)",error));
+ table->file->print_error(error, MYF(0));
table->file->ha_rnd_end();
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
}
while (restart_count < 2 && record_compare(table));
+
+ /*
+ Note: above record_compare will take into accout all record fields
+ which might be incorrect in case a partial row was given in the event
+ */
/*
Have to restart the scan to be able to fetch the next row.
*/
- DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : ""));
+ if (restart_count == 2)
+ DBUG_PRINT("info", ("Record not found"));
+ else
+ 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);
@@ -7664,6 +7697,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
DBUG_RETURN(0);
}
+
#endif
/*
@@ -7675,9 +7709,6 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid, MY_BITMAP const *cols,
bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
-#ifdef HAVE_REPLICATION
- ,m_memory(NULL), m_key(NULL), m_after_image(NULL)
-#endif
{
}
#endif /* #if !defined(MYSQL_CLIENT) */
@@ -7689,107 +7720,58 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
-#if defined(MYSQL_CLIENT)
: Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
-#else
- : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event),
- m_memory(NULL), m_key(NULL), m_after_image(NULL)
-#endif
{
}
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Delete_rows_log_event::do_before_row_operations(TABLE *table)
-{
- DBUG_ASSERT(m_memory == NULL);
- if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
- table->s->primary_key < MAX_KEY)
+int
+Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ m_table->s->primary_key < MAX_KEY)
{
/*
- We don't need to allocate any memory for m_after_image and
- m_key since they are not used.
+ We don't need to allocate any memory for m_key since it is not used.
*/
return 0;
}
- int error= 0;
-
- if (table->s->keys > 0)
+ if (m_table->s->keys > 0)
{
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
- &m_after_image,
- (uint) table->s->reclength,
- &m_key,
- (uint) table->key_info->key_length,
- NullS);
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
}
- else
- {
- m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
- m_memory= (uchar*)m_after_image;
- m_key= NULL;
- }
- if (!m_memory)
- return HA_ERR_OUT_OF_MEM;
-
- return error;
+ return 0;
}
-int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int
+Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
- table->file->ha_index_or_rnd_end();
- my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
- m_memory= NULL;
- m_after_image= NULL;
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR));
m_key= NULL;
return error;
}
-int Delete_rows_log_event::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
- TABLE *table,
- uchar const *const row_start,
- uchar const **const row_end)
-{
- DBUG_ASSERT(row_start && row_end);
- if (int error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->read_set, DELETE_ROWS_EVENT))
- {
- thd_arg->net.last_errno= error;
- return error;
- }
-
- /*
- If we will access rows using the random access method, m_key will
- be set to NULL, so we do not need to make a key copy in that case.
- */
- if (m_key)
- {
- KEY *const key_info= table->key_info;
-
- key_copy(m_key, table->record[0], key_info, 0);
- }
-
- return 0;
-}
-
-int Delete_rows_log_event::do_exec_row(TABLE *table)
+int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
int error;
- DBUG_ASSERT(table != NULL);
+ DBUG_ASSERT(m_table != NULL);
- if (!(error= find_and_fetch_row(table, m_key)))
+ if (!(error= find_row(rli)))
{
/*
- Now we should have the right row to delete. We are using
- record[0] since it is guaranteed to point to a record with the
- correct value.
+ Delete the record found, located in record[0]
*/
- error= table->file->ha_delete_row(table->record[0]);
+ error= m_table->file->ha_delete_row(m_table->record[0]);
}
return error;
}
@@ -7819,10 +7801,6 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
MY_BITMAP const *cols_ai,
bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
-#ifdef HAVE_REPLICATION
- , m_memory(NULL), m_key(NULL)
-
-#endif
{
init(cols_ai);
}
@@ -7832,16 +7810,13 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
MY_BITMAP const *cols,
bool is_transactional)
: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
-#ifdef HAVE_REPLICATION
- , m_memory(NULL), m_key(NULL)
-#endif
{
init(cols);
}
void Update_rows_log_event::init(MY_BITMAP const *cols)
{
- /* if bitmap_init fails, catched in is_valid() */
+ /* if bitmap_init fails, caught in is_valid() */
if (likely(!bitmap_init(&m_cols_ai,
m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
m_width,
@@ -7874,157 +7849,87 @@ Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
const
Format_description_log_event
*description_event)
-#if defined(MYSQL_CLIENT)
: Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
-#else
- : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event),
- m_memory(NULL), m_key(NULL)
-#endif
{
}
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Update_rows_log_event::do_before_row_operations(TABLE *table)
-{
- DBUG_ASSERT(m_memory == NULL);
- int error= 0;
-
- if (table->s->keys > 0)
- {
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
- &m_after_image,
- (uint) table->s->reclength,
- &m_key,
- (uint) table->key_info->key_length,
- NullS);
- }
- else
+int
+Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ if (m_table->s->keys > 0)
{
- m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
- m_memory= m_after_image;
- m_key= NULL;
+ // Allocate buffer for key searches
+ m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ if (!m_key)
+ return HA_ERR_OUT_OF_MEM;
}
- if (!m_memory)
- return HA_ERR_OUT_OF_MEM;
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- return error;
+ return 0;
}
-int Update_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int
+Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
{
- /*
- error= ToDo:find out what this should really be, this triggers
- close_scan in nbd, returning error?
- */
- table->file->ha_index_or_rnd_end();
- my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
- m_memory= NULL;
- m_after_image= NULL;
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
m_key= NULL;
return error;
}
-int Update_rows_log_event::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
- TABLE *table,
- uchar const *const row_start,
- uchar const **const row_end)
+int
+Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
- int error;
- DBUG_ASSERT(row_start && row_end);
- /*
- We need to perform some juggling below since unpack_row() always
- unpacks into table->record[0]. For more information, see the
- comments for unpack_row().
- */
-
- /* record[0] is the before image for the update */
- if ((error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->read_set,
- UPDATE_ROWS_EVENT)))
- {
- thd_arg->net.last_errno= error;
- return error;
- }
+ DBUG_ASSERT(m_table != NULL);
- store_record(table, record[1]);
- uchar const *next_start = *row_end;
- /* m_after_image is the after image for the update */
- if ((error= unpack_row(rli, table, m_width, next_start, &m_cols_ai, row_end,
- &m_master_reclength, table->write_set,
- UPDATE_ROWS_EVENT)))
- {
- thd_arg->net.last_errno= error;
+ int error= find_row(rli);
+ if (error)
return error;
- }
-
- bmove_align(m_after_image, table->record[0], table->s->reclength);
- restore_record(table, record[1]);
/*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
-#ifndef HAVE_purify
- DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
- DBUG_DUMP("m_after_image", m_after_image, table->s->reclength);
-#endif
-
- /*
- If we will access rows using the random access method, m_key will
- be set to NULL, so we do not need to make a key copy in that case.
- */
- if (m_key)
- {
- KEY *const key_info= table->key_info;
-
- key_copy(m_key, table->record[0], key_info, 0);
- }
+ This is the situation after locating BI:
- return error;
-}
+ ===|=== before image ====|=== after image ===|===
+ ^ ^
+ m_curr_row m_curr_row_end
-int Update_rows_log_event::do_exec_row(TABLE *table)
-{
- DBUG_ASSERT(table != NULL);
+ BI found in the table is stored in record[0]. We copy it to record[1]
+ and unpack AI to record[0].
+ */
- int error= find_and_fetch_row(table, m_key);
- if (error)
- return error;
+ store_record(m_table,record[1]);
- /*
- We have to ensure that the new record (i.e., the after image) is
- in record[0] and the old record (i.e., the before image) is in
- record[1]. This since some storage engines require this (for
- example, the partition engine).
-
- Since find_and_fetch_row() puts the fetched record (i.e., the old
- record) in record[1], we can keep it there. We put the new record
- (i.e., the after image) into record[0], and copy the fields that
- are on the slave (i.e., in record[1]) into record[0], effectively
- overwriting the default values that where put there by the
- unpack_row() function.
- */
- bmove_align(table->record[0], m_after_image, table->s->reclength);
- copy_extra_record_fields(table, m_master_reclength, m_width);
+ m_curr_row= m_curr_row_end;
+ error= unpack_current_row(rli); // this also updates m_curr_row_end
/*
Now we have the right row to update. The old row (the one we're
- looking for) is in record[1] and the new row has is in record[0].
- We also have copied the original values already in the slave's
- database into the after image delivered from the master.
+ looking for) is in record[1] and the new row is in record[0].
*/
- error= table->file->ha_update_row(table->record[1], table->record[0]);
+#ifndef HAVE_purify
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+ DBUG_PRINT("info",("Updating row in table"));
+ DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
+ DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
+#endif
+
+ error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
if (error == HA_ERR_RECORD_IS_THE_SAME)
error= 0;
return error;
}
+
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
#ifdef MYSQL_CLIENT
diff --git a/sql/log_event.h b/sql/log_event.h
index 1f45e384e30..0c66d1b190f 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -32,6 +32,10 @@
#include <my_bitmap.h>
#include "rpl_constants.h"
+#ifndef MYSQL_CLIENT
+#include "rpl_record.h"
+#include "rpl_reporting.h"
+#endif
#define LOG_READ_EOF -1
#define LOG_READ_BOGUS -2
@@ -2186,7 +2190,13 @@ public:
NO_FOREIGN_KEY_CHECKS_F = (1U << 1),
/* Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->options */
- RELAXED_UNIQUE_CHECKS_F = (1U << 2)
+ RELAXED_UNIQUE_CHECKS_F = (1U << 2),
+
+ /**
+ Indicates that rows in this event are complete, that is contain
+ values for all columns of the table.
+ */
+ COMPLETE_ROWS_F = (1U << 3)
};
typedef uint16 flag_set;
@@ -2290,7 +2300,26 @@ protected:
uchar *m_rows_cur; /* One-after the end of the data */
uchar *m_rows_end; /* One-after the end of the allocated space */
- flag_set m_flags; /* Flags for row-level events */
+ flag_set m_flags; /* Flags for row-level events */
+
+ /* helper functions */
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ const uchar *m_curr_row; /* Start of the row being processed */
+ const uchar *m_curr_row_end; /* One-after the end of the current row */
+ uchar *m_key; /* Buffer to keep key value during searches */
+
+ int find_row(const Relay_log_info *const);
+ 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)
+ {
+ DBUG_ASSERT(m_table);
+ return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
+ &m_curr_row_end, &m_master_reclength);
+ }
+#endif
private:
@@ -2315,7 +2344,8 @@ private:
The member function will return 0 if all went OK, or a non-zero
error code otherwise.
*/
- virtual int do_before_row_operations(TABLE *table) = 0;
+ virtual
+ int do_before_row_operations(const Slave_reporting_capability *const log) = 0;
/*
Primitive to clean up after a sequence of row executions.
@@ -2325,45 +2355,33 @@ private:
After doing a sequence of do_prepare_row() and do_exec_row(),
this member function should be called to clean up and release
any allocated buffers.
+
+ The error argument, if non-zero, indicates an error which happened during
+ row processing before this function was called. In this case, even if
+ function is successful, it should return the error code given in the argument.
*/
- virtual int do_after_row_operations(TABLE *table, int error) = 0;
-
- /*
- Primitive to prepare for handling one row in a row-level event.
-
- DESCRIPTION
-
- The member function prepares for execution of operations needed for one
- row in a row-level event by reading up data from the buffer containing
- the row. No specific interpretation of the data is normally done here,
- since SQL thread specific data is not available: that data is made
- available for the do_exec function.
-
- A pointer to the start of the next row, or NULL if the preparation
- failed. Currently, preparation cannot fail, but don't rely on this
- behavior.
-
- RETURN VALUE
- Error code, if something went wrong, 0 otherwise.
- */
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
- uchar const *row_start,
- uchar const **row_end) = 0;
+ virtual
+ int do_after_row_operations(const Slave_reporting_capability *const log,
+ int error) = 0;
/*
Primitive to do the actual execution necessary for a row.
DESCRIPTION
The member function will do the actual execution needed to handle a row.
+ The row is located at m_curr_row. When the function returns,
+ m_curr_row_end should point at the next row (one byte after the end
+ of the current row).
RETURN VALUE
0 if execution succeeded, 1 if execution failed.
*/
- virtual int do_exec_row(TABLE *table) = 0;
+ virtual int do_exec_row(const Relay_log_info *const rli) = 0;
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-};
+ friend class Old_rows_log_event;
+};
/*****************************************************************************
@@ -2413,14 +2431,9 @@ private:
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- uchar *m_memory;
- uchar *m_after_image;
-
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
- uchar const *row_start, uchar const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
#endif
};
@@ -2492,15 +2505,9 @@ protected:
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- uchar *m_memory;
- uchar *m_key;
- uchar *m_after_image;
-
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
- uchar const *row_start, uchar const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
};
@@ -2563,15 +2570,9 @@ protected:
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- uchar *m_memory;
- uchar *m_key;
- uchar *m_after_image;
-
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
- uchar const *row_start, uchar const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(const Slave_reporting_capability *const);
+ virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
+ virtual int do_exec_row(const Relay_log_info *const);
#endif
};
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 35e358001fd..420df67dc54 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1,9 +1,967 @@
#include "mysql_priv.h"
+#ifndef MYSQL_CLIENT
+#include "rpl_rli.h"
+#include "rpl_utility.h"
+#endif
#include "log_event_old.h"
#include "rpl_record_old.h"
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+// Old implementation of do_apply_event()
+int
+Old_rows_log_event::do_apply_event(Rows_log_event *ev, const RELAY_LOG_INFO *rli)
+{
+ DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)");
+ int error= 0;
+ THD *thd= ev->thd;
+ uchar const *row_start= ev->m_rows_buf;
+
+ /*
+ If m_table_id == ~0UL, then we have a dummy event that does not
+ contain any data. In that case, we just remove all tables in the
+ tables_to_lock list, close the thread tables, and return with
+ success.
+ */
+ if (ev->m_table_id == ~0UL)
+ {
+ /*
+ This one is supposed to be set: just an extra check so that
+ nothing strange has happened.
+ */
+ DBUG_ASSERT(ev->get_flags(Rows_log_event::STMT_END_F));
+
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ close_thread_tables(thd);
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ do_apply_event(). We still check here to prevent future coding
+ errors.
+ */
+ DBUG_ASSERT(rli->sql_thd == thd);
+
+ /*
+ If there is no locks taken, this is the first binrow event seen
+ after the table map events. We should then lock all the tables
+ used in the transaction and proceed with execution of the actual
+ event.
+ */
+ if (!thd->lock)
+ {
+ bool need_reopen= 1; /* To execute the first lap of the loop below */
+
+ /*
+ lock_tables() reads the contents of thd->lex, so they must be
+ initialized. Contrary to in
+ Table_map_log_event::do_apply_event() we don't call
+ mysql_init_query() as that may reset the binlog format.
+ */
+ lex_start(thd);
+
+ while ((error= lock_tables(thd, rli->tables_to_lock,
+ rli->tables_to_lock_count, &need_reopen)))
+ {
+ if (!need_reopen)
+ {
+ if (thd->query_error || thd->is_fatal_error)
+ {
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ simplifications (we don't honour --slave-skip-errors)
+ */
+ uint actual_error= thd->net.last_errno;
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' in %s event: when locking tables",
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"),
+ ev->get_type_str());
+ thd->is_fatal_error= 1;
+ }
+ else
+ {
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: when locking tables",
+ ev->get_type_str());
+ }
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(error);
+ }
+
+ /*
+ So we need to reopen the tables.
+
+ We need to flush the pending RBR event, since it keeps a
+ pointer to an open table.
+
+ ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
+ the pending RBR event and reset the table pointer after the
+ tables has been reopened.
+
+ NOTE: For this new scheme there should be no pending event:
+ need to add code to assert that is the case.
+ */
+ thd->binlog_flush_pending_rows_event(false);
+ TABLE_LIST *tables= rli->tables_to_lock;
+ close_tables_for_reopen(thd, &tables);
+
+ uint tables_count= rli->tables_to_lock_count;
+ if ((error= open_tables(thd, &tables, &tables_count, 0)))
+ {
+ if (thd->query_error || thd->is_fatal_error)
+ {
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ simplifications (we don't honour --slave-skip-errors)
+ */
+ uint actual_error= thd->net.last_errno;
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' on reopening tables",
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"));
+ thd->query_error= 1;
+ }
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST.
+ */
+
+ {
+ RPL_TABLE_LIST *ptr= rli->tables_to_lock;
+ for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ {
+ if (ptr->m_tabledef.compatible_with(rli, ptr->table))
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ thd->query_error= 1;
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(Rows_log_event::ERR_BAD_TABLE_DEF);
+ }
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and remove
+ them from tables to lock.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Rows_log_event contain at least one row, so after processing one
+ Rows_log_event, we can invalidate the query cache for the
+ associated table.
+ */
+ for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
+ {
+ const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
+ }
+#ifdef HAVE_QUERY_CACHE
+ query_cache.invalidate_locked_for_write(rli->tables_to_lock);
+#endif
+ }
+
+ TABLE* table= const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(ev->m_table_id);
+
+ if (table)
+ {
+ /*
+ table == NULL means that this table should not be replicated
+ (this was set up by Table_map_log_event::do_apply_event()
+ which tested replicate-* rules).
+ */
+
+ /*
+ It's not needed to set_time() but
+ 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+ much slave is behind
+ 2) it will be needed when we allow replication from a table with no
+ TIMESTAMP column to a table with one.
+ So we call set_time(), like in SBR. Presently it changes nothing.
+ */
+ thd->set_time((time_t)ev->when);
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (ev->get_flags(Rows_log_event::NO_FOREIGN_KEY_CHECKS_F))
+ thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (ev->get_flags(Rows_log_event::RELAXED_UNIQUE_CHECKS_F))
+ thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ /*
+ Now we are in a statement and will stay in a statement until we
+ see a STMT_END_F.
+
+ We set this flag here, before actually applying any rows, in
+ case the SQL thread is stopped and we need to detect that we're
+ inside a statement and halting abruptly might cause problems
+ when restarting.
+ */
+ const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);
+
+ error= do_before_row_operations(table);
+ while (error == 0 && row_start < ev->m_rows_end)
+ {
+ uchar const *row_end= NULL;
+ if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
+ break; // We should perform the after-row operation even in
+ // the case of error
+
+ DBUG_ASSERT(row_end != NULL); // cannot happen
+ DBUG_ASSERT(row_end <= ev->m_rows_end);
+
+ /* in_use can have been set to NULL in close_tables_for_reopen */
+ THD* old_thd= table->in_use;
+ if (!table->in_use)
+ table->in_use= thd;
+ error= do_exec_row(table);
+ table->in_use = old_thd;
+ switch (error)
+ {
+ /* Some recoverable errors */
+ case HA_ERR_RECORD_CHANGED:
+ case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if
+ tuple does not exist */
+ error= 0;
+ case 0:
+ break;
+
+ default:
+ rli->report(ERROR_LEVEL, thd->net.last_errno,
+ "Error in %s event: row application failed. %s",
+ ev->get_type_str(),
+ thd->net.last_error ? thd->net.last_error : "");
+ thd->query_error= 1;
+ break;
+ }
+
+ row_start= row_end;
+ }
+ DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ const_cast<RELAY_LOG_INFO*>(rli)->abort_slave= 1;);
+ error= do_after_row_operations(table, error);
+ if (!ev->cache_stmt)
+ {
+ DBUG_PRINT("info", ("Marked that we need to keep log"));
+ thd->options|= OPTION_KEEP_LOG;
+ }
+ }
+
+ /*
+ We need to delay this clear until the table def is no longer needed.
+ The table def is needed in unpack_row().
+ */
+ if (rli->tables_to_lock && ev->get_flags(Rows_log_event::STMT_END_F))
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+
+ if (error)
+ { /* error has occured during the transaction */
+ rli->report(ERROR_LEVEL, thd->net.last_errno,
+ "Error in %s event: error during transaction execution "
+ "on table %s.%s. %s",
+ ev->get_type_str(), table->s->db.str,
+ table->s->table_name.str,
+ thd->net.last_error ? thd->net.last_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->query_error= 1;
+ DBUG_RETURN(error);
+ }
+
+ /*
+ This code would ideally be placed in do_update_pos() instead, but
+ 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) &&
+ !ev->cache_stmt &&
+ ev->get_flags(Rows_log_event::STMT_END_F) == Rows_log_event::RLE_NO_FLAGS)
+ {
+ /*
+ ------------ Temporary fix until WL#2975 is implemented ---------
+
+ This event is not the last one (no STMT_END_F). If we stop now
+ (in case of terminate_slave_thread()), how will we restart? We
+ have to restart from Table_map_log_event, but as this table is
+ not transactional, the rows already inserted will still be
+ present, and idempotency is not guaranteed (no PK) so we risk
+ that repeating leads to double insert. So we desperately try to
+ continue, hope we'll eventually leave this buggy situation (by
+ executing the final Rows_log_event). If we are in a hopeless
+ wait (reached end of last relay log and nothing gets appended
+ there), we timeout after one minute, and notify DBA about the
+ problem. When WL#2975 is implemented, just remove the member
+ st_relay_log_info::last_event_start_time and all its occurences.
+ */
+ const_cast<RELAY_LOG_INFO*>(rli)->last_event_start_time= my_time(0);
+ }
+
+ DBUG_RETURN(0);
+}
+#endif
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+/*
+ Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+ while (++keyno < table->s->keys)
+ if (table->key_info[keyno].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+/*
+ Compares table->record[0] and table->record[1]
+
+ Returns TRUE if different.
+*/
+static bool record_compare(TABLE *table)
+{
+ /*
+ Need to set the X bit and the filler bits in both records since
+ there are engines that do not set it correctly.
+
+ In addition, since MyISAM checks that one hasn't tampered with the
+ record, it is necessary to restore the old bytes into the record
+ after doing the comparison.
+
+ TODO[record format ndb]: Remove it once NDB returns correct
+ records. Check that the other engines also return correct records.
+ */
+
+ bool result= FALSE;
+ uchar saved_x[2], saved_filler[2];
+
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ saved_x[i]= table->record[i][0];
+ saved_filler[i]= table->record[i][table->s->null_bytes - 1];
+ table->record[i][0]|= 1U;
+ table->record[i][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+ }
+
+ if (table->s->blob_fields + table->s->varchar_fields == 0)
+ {
+ result= cmp_record(table,record[1]);
+ goto record_compare_exit;
+ }
+
+ /* Compare null bits */
+ if (memcmp(table->null_flags,
+ table->null_flags+table->s->rec_buff_length,
+ table->s->null_bytes))
+ {
+ result= TRUE; // Diff in NULL value
+ goto record_compare_exit;
+ }
+
+ /* Compare updated fields */
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
+ {
+ result= TRUE;
+ goto record_compare_exit;
+ }
+ }
+
+record_compare_exit:
+ /*
+ Restore the saved bytes.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ for (int i = 0 ; i < 2 ; ++i)
+ {
+ table->record[i][0]= saved_x[i];
+ table->record[i][table->s->null_bytes - 1]= saved_filler[i];
+ }
+ }
+
+ return result;
+}
+
+/*
+ Copy "extra" columns from record[1] to record[0].
+
+ Copy the extra fields that are not present on the master but are
+ present on the slave from record[1] to record[0]. This is used
+ after fetching a record that are to be updated, either inside
+ replace_record() or as part of executing an update_row().
+ */
+static int
+copy_extra_record_fields(TABLE *table,
+ size_t master_reclength,
+ my_ptrdiff_t master_fields)
+{
+ DBUG_ENTER("copy_extra_record_fields(table, master_reclen, master_fields)");
+ DBUG_PRINT("info", ("Copying to 0x%lx "
+ "from field %lu at offset %lu "
+ "to field %d at offset %lu",
+ (long) table->record[0],
+ (ulong) master_fields, (ulong) master_reclength,
+ table->s->fields, table->s->reclength));
+ /*
+ Copying the extra fields of the slave that does not exist on
+ master into record[0] (which are basically the default values).
+ */
+
+ if (table->s->fields < (uint) master_fields)
+ DBUG_RETURN(0);
+
+ DBUG_ASSERT(master_reclength <= table->s->reclength);
+ if (master_reclength < table->s->reclength)
+ bmove_align(table->record[0] + master_reclength,
+ table->record[1] + master_reclength,
+ table->s->reclength - master_reclength);
+
+ /*
+ Bit columns are special. We iterate over all the remaining
+ columns and copy the "extra" bits to the new record. This is
+ not a very good solution: it should be refactored on
+ opportunity.
+
+ REFACTORING SUGGESTION (Matz). Introduce a member function
+ similar to move_field_offset() called copy_field_offset() to
+ copy field values and implement it for all Field subclasses. Use
+ this function to copy data from the found record to the record
+ that are going to be inserted.
+
+ The copy_field_offset() function need to be a virtual function,
+ which in this case will prevent copying an entire range of
+ fields efficiently.
+ */
+ {
+ Field **field_ptr= table->field + master_fields;
+ for ( ; *field_ptr ; ++field_ptr)
+ {
+ /*
+ Set the null bit according to the values in record[1]
+ */
+ if ((*field_ptr)->maybe_null() &&
+ (*field_ptr)->is_null_in_record(reinterpret_cast<uchar*>(table->record[1])))
+ (*field_ptr)->set_null();
+ else
+ (*field_ptr)->set_notnull();
+
+ /*
+ Do the extra work for special columns.
+ */
+ switch ((*field_ptr)->real_type())
+ {
+ default:
+ /* Nothing to do */
+ break;
+
+ case MYSQL_TYPE_BIT:
+ Field_bit *f= static_cast<Field_bit*>(*field_ptr);
+ if (f->bit_len > 0)
+ {
+ my_ptrdiff_t const offset= table->record[1] - table->record[0];
+ uchar const bits=
+ get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len);
+ set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len);
+ }
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(0); // All OK
+}
+
+/*
+ Replace the provided record in the database.
+
+ SYNOPSIS
+ replace_record()
+ thd Thread context for writing the record.
+ table Table to which record should be written.
+ master_reclength
+ Offset to first column that is not present on the master,
+ alternatively the length of the record on the master
+ side.
+
+ RETURN VALUE
+ Error code on failure, 0 on success.
+
+ DESCRIPTION
+ Similar to how it is done in mysql_insert(), we first try to do
+ a ha_write_row() and of that fails due to duplicated keys (or
+ indices), we do an ha_update_row() or a ha_delete_row() instead.
+ */
+static int
+replace_record(THD *thd, TABLE *table,
+ ulong const master_reclength,
+ uint const master_fields)
+{
+ DBUG_ENTER("replace_record");
+ DBUG_ASSERT(table != NULL && thd != NULL);
+
+ int error;
+ int keynum;
+ auto_afree_ptr<char> key(NULL);
+
+#ifndef DBUG_OFF
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_PRINT_BITSET("debug", "write_set = %s", table->write_set);
+ DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
+#endif
+
+ while ((error= table->file->ha_write_row(table->record[0])))
+ {
+ if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT)
+ {
+ table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */
+ DBUG_RETURN(error);
+ }
+ if ((keynum= table->file->get_dup_key(error)) < 0)
+ {
+ table->file->print_error(error, MYF(0));
+ /*
+ We failed to retrieve the duplicate key
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
+ DBUG_RETURN(error);
+ }
+
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ error= table->file->rnd_pos(table->record[1], table->file->dup_ref);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ DBUG_RETURN(my_errno);
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ DBUG_RETURN(ENOMEM);
+ }
+
+ key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
+ 0);
+ error= table->file->index_read_idx_map(table->record[1], keynum,
+ (const uchar*)key.get(),
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Now, table->record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+
+ First we copy the columns into table->record[0] that are not
+ present on the master from table->record[1], if there are any.
+ */
+ copy_extra_record_fields(table, master_reclength, master_fields);
+
+ /*
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+ */
+ if (last_uniq_key(table, keynum) &&
+ !table->file->referenced_by_foreign_key())
+ {
+ error=table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ if (error && error != HA_ERR_RECORD_IS_THE_SAME)
+ table->file->print_error(error, MYF(0));
+ else
+ error= 0;
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /* Will retry ha_write_row() with the offending row removed. */
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+/**
+ Find the row given by 'key', if the table has keys, or else use a table scan
+ to find (and fetch) the row.
+
+ If the engine allows random access of the records, a combination of
+ position() and rnd_pos() will be used.
+
+ @param table Pointer to table to search
+ @param key Pointer to key to use for search, if table has key
+
+ @pre <code>table->record[0]</code> shall contain the row to locate
+ and <code>key</code> shall contain a key to use for searching, if
+ the engine has a key.
+
+ @post If the return value is zero, <code>table->record[1]</code>
+ will contain the fetched row and the internal "cursor" will refer to
+ the row. If the return value is non-zero,
+ <code>table->record[1]</code> is undefined. In either case,
+ <code>table->record[0]</code> is undefined.
+
+ @return Zero if the row was successfully fetched into
+ <code>table->record[1]</code>, error code otherwise.
+ */
+
+static int find_and_fetch_row(TABLE *table, uchar *key)
+{
+ DBUG_ENTER("find_and_fetch_row(TABLE *table, uchar *key, uchar *record)");
+ DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx",
+ (long) table, (long) key, (long) table->record[1]));
+
+ DBUG_ASSERT(table->in_use != NULL);
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" (i.e., record[0] in this case) at the correct row.
+
+ TODO: Add a check that the correct record has been fetched by
+ comparing with the original record. Take into account that the
+ record on the master and slave can be of different
+ length. Something along these lines should work:
+
+ ADD>>> store_record(table,record[1]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
+ table->s->reclength) == 0);
+
+ */
+ table->file->position(table->record[0]);
+ int error= table->file->rnd_pos(table->record[0], table->file->ref);
+ /*
+ rnd_pos() returns the record in table->record[0], so we have to
+ move it to table->record[1].
+ */
+ bmove_align(table->record[1], table->record[0], table->s->reclength);
+ DBUG_RETURN(error);
+ }
+
+ /* We need to retrieve all fields */
+ /* TODO: Move this out from this function to main loop */
+ table->use_all_columns();
+
+ if (table->s->keys > 0)
+ {
+ int error;
+ /* We have a key: search the table using the index */
+ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ DBUG_RETURN(error);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+#endif
+
+ /*
+ We need to set the null bytes to ensure that the filler bit are
+ all set when returning. There are storage engines that just set
+ the necessary bits on the bytes and don't set the filler bits
+ correctly.
+ */
+ my_ptrdiff_t const pos=
+ table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0;
+ table->record[1][pos]= 0xFF;
+ if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT)))
+ {
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_purify
+ DBUG_DUMP("table->record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("table->record[1]", table->record[1], table->s->reclength);
+#endif
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ {
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+ }
+
+ while (record_compare(table))
+ {
+ int error;
+
+ /*
+ We need to set the null bytes to ensure that the filler bit
+ are all set when returning. There are storage engines that
+ just set the necessary bits on the bytes and don't set the
+ filler bits correctly.
+
+ TODO[record format ndb]: Remove this code once NDB returns the
+ correct record format.
+ */
+ if (table->s->null_bytes > 0)
+ {
+ table->record[1][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+ }
+
+ if ((error= table->file->index_next(table->record[1])))
+ {
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ table->file->ha_index_end();
+ }
+ else
+ {
+ int restart_count= 0; // Number of times scanning has restarted from top
+ int error;
+
+ /* We don't have a key: search the table using rnd_next() */
+ if ((error= table->file->ha_rnd_init(1)))
+ return error;
+
+ /* Continue until we find the right record or have made a full loop */
+ do
+ {
+ error= table->file->rnd_next(table->record[1]);
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_DUMP("record[1]", table->record[1], table->s->reclength);
+
+ switch (error) {
+ case 0:
+ case HA_ERR_RECORD_DELETED:
+ break;
+
+ case HA_ERR_END_OF_FILE:
+ if (++restart_count < 2)
+ table->file->ha_rnd_init(1);
+ break;
+
+ default:
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info", ("Record not found"));
+ table->file->ha_rnd_end();
+ DBUG_RETURN(error);
+ }
+ }
+ while (restart_count < 2 && record_compare(table));
+
+ /*
+ Have to restart the scan to be able to fetch the next row.
+ */
+ DBUG_PRINT("info", ("Record %sfound", restart_count == 2 ? "not " : ""));
+ table->file->ha_rnd_end();
+
+ DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+ DBUG_RETURN(error);
+ }
+
+ DBUG_RETURN(0);
+}
+
+/**********************************************************
+ Row handling primitives for Write_rows_log_event_old
+ **********************************************************/
+
+int Write_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ int error= 0;
+
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB and NDB Cluster since they are not (properly) checking the
+ lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ NDB specific: update from ndb master wrapped as Write_rows
+ */
+ /*
+ so that the event should be applied to replace slave's row
+ */
+ table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ /*
+ NDB specific: if update from ndb master wrapped as Write_rows
+ does not find the row it's assumed idempotent binlog applying
+ is taking place; don't raise the error.
+ */
+ table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ /*
+ TODO: the cluster team (Tomas?) says that it's better if the engine knows
+ how many rows are going to be inserted, then it can allocate needed memory
+ from the start.
+ */
+ table->file->ha_start_bulk_insert(0);
+ /*
+ We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
+ any TIMESTAMP column with data from the row but instead will use
+ the event's current time.
+ As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
+ columns, we know that all TIMESTAMP columns on slave will receive explicit
+ data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
+ When we allow a table without TIMESTAMP to be replicated to a table having
+ more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
+ column to be replicated into a BIGINT column and the slave's table has a
+ TIMESTAMP column, then the slave's TIMESTAMP column will take its value
+ from set_time() which we called earlier (consistent with SBR). And then in
+ some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
+ analyze if explicit data is provided for slave's TIMESTAMP columns).
+ */
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ return error;
+}
+
+int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
+{
+ int local_error= 0;
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ reseting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ todo: explain or fix
+ */
+ if ((local_error= table->file->ha_end_bulk_insert()))
+ {
+ table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
int
Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
Relay_log_info const *rli,
@@ -23,6 +981,65 @@ Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
return error;
}
+int Write_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ DBUG_ASSERT(table != NULL);
+ int error= replace_record(thd, table, m_master_reclength, m_width);
+ return error;
+}
+
+/**********************************************************
+ Row handling primitives for Delete_rows_log_event_old
+ **********************************************************/
+
+int Delete_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_after_image and
+ m_key since they are not used.
+ */
+ return 0;
+ }
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_after_image,
+ (uint) table->s->reclength,
+ &m_key,
+ (uint) table->key_info->key_length,
+ NullS);
+ }
+ else
+ {
+ m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_memory= (uchar*)m_after_image;
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ return error;
+}
+
+int Delete_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ table->file->ha_index_or_rnd_end();
+ my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ m_memory= NULL;
+ m_after_image= NULL;
+ m_key= NULL;
+
+ return error;
+}
int
Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
@@ -57,6 +1074,67 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
return error;
}
+int Delete_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ int error;
+ DBUG_ASSERT(table != NULL);
+
+ if (!(error= ::find_and_fetch_row(table, m_key)))
+ {
+ /*
+ Now we should have the right row to delete. We are using
+ record[0] since it is guaranteed to point to a record with the
+ correct value.
+ */
+ error= table->file->ha_delete_row(table->record[0]);
+ }
+ return error;
+}
+
+/**********************************************************
+ Row handling primitives for Update_rows_log_event_old
+ **********************************************************/
+
+int Update_rows_log_event_old::do_before_row_operations(TABLE *table)
+{
+ DBUG_ASSERT(m_memory == NULL);
+
+ int error= 0;
+
+ if (table->s->keys > 0)
+ {
+ m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ &m_after_image,
+ (uint) table->s->reclength,
+ &m_key,
+ (uint) table->key_info->key_length,
+ NullS);
+ }
+ else
+ {
+ m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_memory= m_after_image;
+ m_key= NULL;
+ }
+ if (!m_memory)
+ return HA_ERR_OUT_OF_MEM;
+
+ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ return error;
+}
+
+int Update_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ table->file->ha_index_or_rnd_end();
+ my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+ m_memory= NULL;
+ m_after_image= NULL;
+ m_key= NULL;
+
+ return error;
+}
int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
Relay_log_info const *rli,
@@ -101,4 +1179,41 @@ int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
return error;
}
+int Update_rows_log_event_old::do_exec_row(TABLE *table)
+{
+ DBUG_ASSERT(table != NULL);
+
+ int error= ::find_and_fetch_row(table, m_key);
+ if (error)
+ return error;
+
+ /*
+ We have to ensure that the new record (i.e., the after image) is
+ in record[0] and the old record (i.e., the before image) is in
+ record[1]. This since some storage engines require this (for
+ example, the partition engine).
+
+ Since find_and_fetch_row() puts the fetched record (i.e., the old
+ record) in record[1], we can keep it there. We put the new record
+ (i.e., the after image) into record[0], and copy the fields that
+ are on the slave (i.e., in record[1]) into record[0], effectively
+ overwriting the default values that where put there by the
+ unpack_row() function.
+ */
+ bmove_align(table->record[0], m_after_image, table->s->reclength);
+ copy_extra_record_fields(table, m_master_reclength, m_width);
+
+ /*
+ Now we have the right row to update. The old row (the one we're
+ looking for) is in record[1] and the new row has is in record[0].
+ We also have copied the original values already in the slave's
+ database into the after image delivered from the master.
+ */
+ error= table->file->ha_update_row(table->record[1], table->record[0]);
+ if (error == HA_ERR_RECORD_IS_THE_SAME)
+ error= 0;
+
+ return error;
+}
+
#endif
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index b6e25b6bc73..81e55097905 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -20,9 +20,90 @@
Need to include this file at the proper position of log_event.h
*/
+
+class Old_rows_log_event
+{
+ public:
+
+ virtual ~Old_rows_log_event() {}
+
+ protected:
+
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+ int do_apply_event(Rows_log_event*,const Relay_log_info*);
+
+ /*
+ Primitive to prepare for a sequence of row executions.
+
+ DESCRIPTION
+
+ Before doing a sequence of do_prepare_row() and do_exec_row()
+ calls, this member function should be called to prepare for the
+ entire sequence. Typically, this member function will allocate
+ space for any buffers that are needed for the two member
+ functions mentioned above.
+
+ RETURN VALUE
+
+ The member function will return 0 if all went OK, or a non-zero
+ error code otherwise.
+ */
+ virtual int do_before_row_operations(TABLE *table) = 0;
+
+ /*
+ Primitive to clean up after a sequence of row executions.
+
+ DESCRIPTION
+
+ After doing a sequence of do_prepare_row() and do_exec_row(),
+ this member function should be called to clean up and release
+ any allocated buffers.
+ */
+ virtual int do_after_row_operations(TABLE *table, int error) = 0;
+
+ /*
+ Primitive to prepare for handling one row in a row-level event.
+
+ DESCRIPTION
+
+ The member function prepares for execution of operations needed for one
+ row in a row-level event by reading up data from the buffer containing
+ the row. No specific interpretation of the data is normally done here,
+ since SQL thread specific data is not available: that data is made
+ available for the do_exec function.
+
+ A pointer to the start of the next row, or NULL if the preparation
+ failed. Currently, preparation cannot fail, but don't rely on this
+ behavior.
-class Write_rows_log_event_old : public Write_rows_log_event
+ RETURN VALUE
+ Error code, if something went wrong, 0 otherwise.
+ */
+ virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ uchar const *row_start,
+ uchar const **row_end) = 0;
+
+ /*
+ Primitive to do the actual execution necessary for a row.
+
+ DESCRIPTION
+ The member function will do the actual execution needed to handle a row.
+
+ RETURN VALUE
+ 0 if execution succeeded, 1 if execution failed.
+
+ */
+ virtual int do_exec_row(TABLE *table) = 0;
+
+#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+};
+
+
+class Write_rows_log_event_old
+ : public Write_rows_log_event, public Old_rows_log_event
{
+
public:
enum
{
@@ -49,14 +130,26 @@ private:
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
+
#endif
};
-class Update_rows_log_event_old : public Update_rows_log_event
+class Update_rows_log_event_old
+ : public Update_rows_log_event, public Old_rows_log_event
{
+ uchar *m_after_image, *m_memory;
+
public:
enum
{
@@ -67,14 +160,16 @@ public:
#if !defined(MYSQL_CLIENT)
Update_rows_log_event_old(THD *thd, TABLE *table, ulong table_id,
MY_BITMAP const *cols, bool is_transactional)
- : Update_rows_log_event(thd, table, table_id, cols, is_transactional)
+ : Update_rows_log_event(thd, table, table_id, cols, is_transactional),
+ m_after_image(NULL), m_memory(NULL)
{
}
#endif
#if defined(HAVE_REPLICATION)
Update_rows_log_event_old(const char *buf, uint event_len,
const Format_description_log_event *descr)
- : Update_rows_log_event(buf, event_len, descr)
+ : Update_rows_log_event(buf, event_len, descr),
+ m_after_image(NULL), m_memory(NULL)
{
}
#endif
@@ -83,14 +178,25 @@ private:
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
};
-class Delete_rows_log_event_old : public Delete_rows_log_event
+class Delete_rows_log_event_old
+ : public Delete_rows_log_event, public Old_rows_log_event
{
+ uchar *m_after_image, *m_memory;
+
public:
enum
{
@@ -101,14 +207,16 @@ public:
#if !defined(MYSQL_CLIENT)
Delete_rows_log_event_old(THD *thd, TABLE *table, ulong table_id,
MY_BITMAP const *cols, bool is_transactional)
- : Delete_rows_log_event(thd, table, table_id, cols, is_transactional)
+ : Delete_rows_log_event(thd, table, table_id, cols, is_transactional),
+ m_after_image(NULL), m_memory(NULL)
{
}
#endif
#if defined(HAVE_REPLICATION)
Delete_rows_log_event_old(const char *buf, uint event_len,
const Format_description_log_event *descr)
- : Delete_rows_log_event(buf, event_len, descr)
+ : Delete_rows_log_event(buf, event_len, descr),
+ m_after_image(NULL), m_memory(NULL)
{
}
#endif
@@ -117,8 +225,16 @@ private:
virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ // use old definition of do_apply_event()
+ virtual int do_apply_event(const Relay_log_info *rli)
+ { return Old_rows_log_event::do_apply_event(this,rli); }
+
+ // primitives for old version of do_apply_event()
+ virtual int do_before_row_operations(TABLE *table);
+ virtual int do_after_row_operations(TABLE *table, int error);
virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
uchar const *row_start, uchar const **row_end);
+ virtual int do_exec_row(TABLE *table);
#endif
};
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index 17b759f8426..65c8e106112 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -157,9 +157,8 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
the various member functions of Field and subclasses expect to
write.
- The row is assumed to only consist of the fields for which the
- bitset represented by @c arr and @c bits; the other parts of the
- record are left alone.
+ The row is assumed to only consist of the fields for which the corresponding
+ bit in bitset @c cols is set; the other parts of the record are left alone.
At most @c colcnt columns are read: if the table is larger than
that, the remaining fields are not filled in.
@@ -169,16 +168,12 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
@param colcnt Number of columns to read from record
@param row_data
Packed row data
- @param cols Pointer to columns data to fill in
+ @param cols Pointer to bitset describing columns to fill in
@param row_end Pointer to variable that will hold the value of the
one-after-end position for the row
@param master_reclength
Pointer to variable that will be set to the length of the
record on the master side
- @param rw_set Pointer to bitmap that holds either the read_set or the
- write_set of the table
- @param event_type
-
@retval 0 No error
@@ -192,8 +187,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,
- MY_BITMAP* const rw_set, Log_event_type const event_type)
+ uchar const **const row_end, ulong *const master_reclength)
{
DBUG_ENTER("unpack_row");
DBUG_ASSERT(row_data);
@@ -203,10 +197,6 @@ unpack_row(Relay_log_info const *rli,
uchar const *null_ptr= row_data;
uchar const *pack_ptr= row_data + master_null_byte_count;
- bitmap_clear_all(rw_set);
-
- empty_record(table);
-
Field **const begin_ptr = table->field;
Field **field_ptr;
Field **const end_ptr= begin_ptr + colcnt;
@@ -266,7 +256,6 @@ unpack_row(Relay_log_info const *rli,
#endif
}
- bitmap_set_bit(rw_set, f->field_index);
null_mask <<= 1;
}
i++;
@@ -308,30 +297,58 @@ unpack_row(Relay_log_info const *rli,
else
*master_reclength = table->s->reclength;
}
+
+ DBUG_RETURN(error);
+}
- /*
- Set properties for remaining columns, if there are any. We let the
- corresponding bit in the write_set be set, to write the value if
- it was not there already. We iterate over all remaining columns,
- even if there were an error, to get as many error messages as
- possible. We are still able to return a pointer to the next row,
- so redo that.
-
- This generation of error messages is only relevant when inserting
- new rows.
- */
- for ( ; *field_ptr ; ++field_ptr)
+/**
+ Fills @c table->record[0] with default values.
+
+ First @c empty_record() is called and then, additionally, fields are
+ initialized explicitly with a call to @c set_default().
+
+ For optimization reasons, the explicit initialization can be skipped for
+ first @c skip fields. This is useful if later we are going to fill these
+ fields from other source (e.g. from a Rows replication event).
+
+ If @c check is true, fields are explicitly initialized only if they have
+ default value or can be NULL. Otherwise error is reported.
+
+ @param log Used to report errors.
+ @param table Table whose record[0] buffer is prepared.
+ @param skip Number of columns for which default value initialization
+ should be skipped.
+ @param check Indicates if errors should be checked when setting default
+ values.
+
+ @returns 0 on success.
+ */
+int prepare_record(const Slave_reporting_capability *const log,
+ TABLE *const table,
+ const uint skip, const bool check)
+{
+ DBUG_ENTER("prepare_record");
+
+ int error= 0;
+ empty_record(table);
+
+ if (skip >= table->s->fields) // nothing to do
+ DBUG_RETURN(0);
+
+ /* Explicit initialization of fields */
+
+ for (Field **field_ptr= table->field+skip ; *field_ptr ; ++field_ptr)
{
uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG;
Field *const f= *field_ptr;
- if (event_type == WRITE_ROWS_EVENT &&
- ((*field_ptr)->flags & mask) == mask)
+ if (check && ((f->flags & mask) == mask))
{
- rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD,
+ DBUG_ASSERT(log);
+ log->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD,
"Field `%s` of table `%s`.`%s` "
"has no default value and cannot be NULL",
- (*field_ptr)->field_name, table->s->db.str,
+ f->field_name, table->s->db.str,
table->s->table_name.str);
error = ER_NO_DEFAULT_FOR_FIELD;
}
diff --git a/sql/rpl_record.h b/sql/rpl_record.h
index b817dba0292..0d6ceda7433 100644
--- a/sql/rpl_record.h
+++ b/sql/rpl_record.h
@@ -16,6 +16,8 @@
#ifndef RPL_RECORD_H
#define RPL_RECORD_H
+#include <rpl_reporting.h>
+
#if !defined(MYSQL_CLIENT)
size_t pack_row(TABLE* table, MY_BITMAP const* cols,
uchar *row_data, const uchar *data);
@@ -25,9 +27,11 @@ 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,
- MY_BITMAP* const rw_set,
- Log_event_type const event_type);
+ uchar const **const row_end, ulong *const master_reclength);
+
+// Fill table's record[0] with default values.
+int prepare_record(const Slave_reporting_capability *const, TABLE *const,
+ const uint =0, const bool =FALSE);
#endif
#endif
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index bc552650ac5..d1ce5bf3b7b 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -24,7 +24,7 @@
This function returns the field size in raw bytes based on the type
and the encoded field data from the master's raw data.
*/
-uint32 table_def::calc_field_size(uint col, uchar *master_data)
+uint32 table_def::calc_field_size(uint col, uchar *master_data) const
{
uint32 length;
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index eac3d14dfc6..4fd38022da0 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -217,7 +217,7 @@ public:
WL#3915) or needs to advance the pointer for the fields in the raw
data from the master to a specific column.
*/
- uint32 calc_field_size(uint col, uchar *master_data);
+ uint32 calc_field_size(uint col, uchar *master_data) const;
/**
Decide if the table definition is compatible with a table.
@@ -258,4 +258,44 @@ struct RPL_TABLE_LIST
table_def m_tabledef;
};
+
+/* Anonymous namespace for template functions/classes */
+namespace {
+
+ /*
+ Smart pointer that will automatically call my_afree (a macro) when
+ the pointer goes out of scope. This is used so that I do not have
+ to remember to call my_afree() before each return. There is no
+ overhead associated with this, since all functions are inline.
+
+ I (Matz) would prefer to use the free function as a template
+ parameter, but that is not possible when the "function" is a
+ macro.
+ */
+ template <class Obj>
+ class auto_afree_ptr
+ {
+ Obj* m_ptr;
+ public:
+ auto_afree_ptr(Obj* ptr) : m_ptr(ptr) { }
+ ~auto_afree_ptr() { if (m_ptr) my_afree(m_ptr); }
+ void assign(Obj* ptr) {
+ /* Only to be called if it hasn't been given a value before. */
+ DBUG_ASSERT(m_ptr == NULL);
+ m_ptr= ptr;
+ }
+ Obj* get() { return m_ptr; }
+ };
+
+}
+
+#define DBUG_PRINT_BITSET(N,FRM,BS) \
+ do { \
+ char buf[256]; \
+ for (uint i = 0 ; i < (BS)->n_bits ; ++i) \
+ buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
+ buf[(BS)->n_bits] = '\0'; \
+ DBUG_PRINT((N), ((FRM), buf)); \
+ } while (0)
+
#endif /* RPL_UTILITY_H */
diff --git a/sql/slave.cc b/sql/slave.cc
index cb957b7b3b5..fcbd4eb841b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -132,6 +132,11 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
const char* table_name, bool overwrite);
static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi);
static Log_event* next_event(Relay_log_info* rli);
+static int terminate_slave_thread(THD *thd,
+ pthread_mutex_t* term_lock,
+ pthread_cond_t* term_cond,
+ volatile uint *slave_running,
+ bool skip_lock);
/*
Find out which replications threads are running
@@ -312,35 +317,26 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
DBUG_RETURN(0); /* successfully do nothing */
int error,force_all = (thread_mask & SLAVE_FORCE_ALL);
pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock;
- pthread_mutex_t *sql_cond_lock,*io_cond_lock;
- sql_cond_lock=sql_lock;
- io_cond_lock=io_lock;
-
- if (skip_lock)
- {
- sql_lock = io_lock = 0;
- }
- if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) && mi->slave_running)
+ if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)))
{
DBUG_PRINT("info",("Terminating IO thread"));
mi->abort_slave=1;
if ((error=terminate_slave_thread(mi->io_thd,io_lock,
- io_cond_lock,
&mi->stop_cond,
- &mi->slave_running)) &&
+ &mi->slave_running,
+ skip_lock)) &&
!force_all)
DBUG_RETURN(error);
}
- if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running)
+ if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)))
{
DBUG_PRINT("info",("Terminating SQL thread"));
- DBUG_ASSERT(mi->rli.sql_thd != 0) ;
mi->rli.abort_slave=1;
if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock,
- sql_cond_lock,
&mi->rli.stop_cond,
- &mi->rli.slave_running)) &&
+ &mi->rli.slave_running,
+ skip_lock)) &&
!force_all)
DBUG_RETURN(error);
}
@@ -348,23 +344,60 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
}
-int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
- pthread_mutex_t *cond_lock,
- pthread_cond_t* term_cond,
- volatile uint *slave_running)
+/**
+ Wait for a slave thread to terminate.
+
+ This function is called after requesting the thread to terminate
+ (by setting @c abort_slave member of @c Relay_log_info or @c
+ Master_info structure to 1). Termination of the thread is
+ controlled with the the predicate <code>*slave_running</code>.
+
+ Function will acquire @c term_lock before waiting on the condition
+ unless @c skip_lock is true in which case the mutex should be owned
+ by the caller of this function and will remain acquired after
+ return from the function.
+
+ @param term_lock
+ Associated lock to use when waiting for @c term_cond
+
+ @param term_cond
+ Condition that is signalled when the thread has terminated
+
+ @param slave_running
+ Pointer to predicate to check for slave thread termination
+
+ @param skip_lock
+ If @c true the lock will not be acquired before waiting on
+ the condition. In this case, it is assumed that the calling
+ function acquires the lock before calling this function.
+
+ @retval 0 All OK
+ */
+static int
+terminate_slave_thread(THD *thd,
+ pthread_mutex_t* term_lock,
+ pthread_cond_t* term_cond,
+ volatile uint *slave_running,
+ bool skip_lock)
{
+ int error;
+
DBUG_ENTER("terminate_slave_thread");
- if (term_lock)
- {
+
+ if (!skip_lock)
pthread_mutex_lock(term_lock);
- if (!*slave_running)
- {
+
+ safe_mutex_assert_owner(term_lock);
+
+ if (!*slave_running)
+ {
+ if (!skip_lock)
pthread_mutex_unlock(term_lock);
- DBUG_RETURN(ER_SLAVE_NOT_RUNNING);
- }
+ DBUG_RETURN(ER_SLAVE_NOT_RUNNING);
}
DBUG_ASSERT(thd != 0);
THD_CHECK_SENTRY(thd);
+
/*
Is is critical to test if the slave is running. Otherwise, we might
be referening freed memory trying to kick it
@@ -380,9 +413,13 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock,
*/
struct timespec abstime;
set_timespec(abstime,2);
- pthread_cond_timedwait(term_cond, cond_lock, &abstime);
+ error= pthread_cond_timedwait(term_cond, term_lock, &abstime);
+ DBUG_ASSERT(error == ETIMEDOUT || error == 0);
}
- if (term_lock)
+
+ DBUG_ASSERT(*slave_running == 0);
+
+ if (!skip_lock)
pthread_mutex_unlock(term_lock);
DBUG_RETURN(0);
}
diff --git a/sql/slave.h b/sql/slave.h
index 68b00f65ded..2cd9ea352ba 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -138,10 +138,6 @@ bool flush_relay_log_info(Relay_log_info* rli);
int register_slave_on_master(MYSQL* mysql);
int terminate_slave_threads(Master_info* mi, int thread_mask,
bool skip_lock = 0);
-int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex,
- pthread_mutex_t* cond_lock,
- pthread_cond_t* term_cond,
- volatile uint* slave_running);
int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
Master_info* mi, const char* master_info_fname,
const char* slave_info_fname, int thread_mask);
diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c
index f4012c11973..47cf7a0bc9c 100644
--- a/storage/innobase/dict/dict0mem.c
+++ b/storage/innobase/dict/dict0mem.c
@@ -209,7 +209,7 @@ dict_mem_table_add_col(
col = (dict_col_t*) dict_table_get_nth_col(table, i);
- col->ind = i;
+ col->ind = (unsigned int) i;
col->ord_part = 0;
col->mtype = (unsigned int) mtype;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 355b924ce4b..267ebc5bb1e 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -7220,7 +7220,7 @@ ulong
ha_innobase::innobase_get_auto_increment(
ulonglong* value) /* out: autoinc value */
{
- ulint error;
+ ulong error;
do {
error = innobase_autoinc_lock();
@@ -7323,7 +7323,7 @@ ha_innobase::get_auto_increment(
/* Called for the first time ? */
if (prebuilt->trx->n_autoinc_rows == 0) {
- prebuilt->trx->n_autoinc_rows = nb_desired_values;
+ prebuilt->trx->n_autoinc_rows = (ulint) nb_desired_values;
/* It's possible for nb_desired_values to be 0:
e.g., INSERT INTO T1(C) SELECT C FROM T2; */
diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c
index ee79bc94906..9cb4e0f6f20 100644
--- a/storage/innobase/row/row0sel.c
+++ b/storage/innobase/row/row0sel.c
@@ -4555,6 +4555,15 @@ row_search_autoinc_read_column(
ut_a(len != UNIV_SQL_NULL);
ut_a(len <= sizeof value);
+#ifdef WORDS_BIGENDIAN
+ /* Copy integer data and restore sign bit */
+
+ memcpy((ptr = dest), data, len);
+
+ if (!unsigned_type) {
+ dest[0] ^= 128;
+ }
+#else
/* Convert integer data from Innobase to a little-endian format,
sign bit restored to normal */
@@ -4566,6 +4575,7 @@ row_search_autoinc_read_column(
if (!unsigned_type) {
dest[len - 1] ^= 128;
}
+#endif
/* The assumption here is that the AUTOINC value can't be negative.*/
switch (len) {