diff options
-rw-r--r-- | mysql-test/extra/rpl_tests/rpl_ndb_2multi_basic.test | 119 | ||||
-rw-r--r-- | mysql-test/include/rpl_multi_engine2.inc | 91 | ||||
-rw-r--r-- | mysql-test/suite/rpl_ndb/r/rpl_ndb_2other.result | 1189 | ||||
-rw-r--r-- | mysql-test/suite/rpl_ndb/t/disabled.def | 4 | ||||
-rw-r--r-- | mysql-test/suite/rpl_ndb/t/rpl_ndb_2other-slave.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/rpl_ndb/t/rpl_ndb_2other.test | 35 | ||||
-rw-r--r-- | sql/field.cc | 22 | ||||
-rw-r--r-- | sql/log_event.cc | 861 | ||||
-rw-r--r-- | sql/log_event.h | 109 | ||||
-rw-r--r-- | sql/log_event_old.cc | 1115 | ||||
-rw-r--r-- | sql/log_event_old.h | 130 | ||||
-rw-r--r-- | sql/rpl_record.cc | 79 | ||||
-rw-r--r-- | sql/rpl_record.h | 10 | ||||
-rw-r--r-- | sql/rpl_utility.cc | 2 | ||||
-rw-r--r-- | sql/rpl_utility.h | 42 | ||||
-rw-r--r-- | sql/slave.cc | 91 | ||||
-rw-r--r-- | sql/slave.h | 4 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.c | 2 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 4 | ||||
-rw-r--r-- | storage/innobase/row/row0sel.c | 10 |
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) { |