diff options
109 files changed, 7449 insertions, 2282 deletions
diff --git a/BitKeeper/etc/collapsed b/BitKeeper/etc/collapsed index 2509fa50a7b..5ff033b2163 100644 --- a/BitKeeper/etc/collapsed +++ b/BitKeeper/etc/collapsed @@ -15,6 +15,8 @@ 45214442pBGT9KuZEGixBH71jTzbOA 45214a07hVsIGwvwa-WrO-jpeaSwVw 452a92d0-31-8wSzSfZi165fcGcXPA +452c6c6dAjuNghfc1ObZ_UQ5SCl85g +4538a7b0EbDHHkWPbIwxO6ZIDdg6Dg 454a7ef8gdvE_ddMlJyghvOAkKPNOQ 454bb488ijVLOUK_GFjcoISE0GxPUA 454bb9a8AwlGRC_wWLS2sNMoRBMRGw diff --git a/BitKeeper/triggers/post-commit b/BitKeeper/triggers/post-commit index f7b00625264..86f3db012ee 100755 --- a/BitKeeper/triggers/post-commit +++ b/BitKeeper/triggers/post-commit @@ -35,9 +35,17 @@ fi CHANGESET=`bk -R prs -r+ -h -d':P:::I:' ChangeSet` CSETKEY=`bk -R prs -r+ -h -d':KEY:' ChangeSet` -BUG=`bk -R prs -r+ -h -d':C:' ChangeSet | sed -ne 's/^.*[Bb][Uu][Gg] *# *\([0-9][0-9]*\).*$/\1/p'` -WL=`bk -R prs -r+ -h -d':C:' ChangeSet | sed -ne 's/^.*[Ww][Ll] *# *\([0-9][0-9]*\).*$/ WL#\1/p'` - +# +# composing subject lines of commit mails. +# if a fix targets to a WL and there is a bug referred +# then X-Bug mail header will contain the first found bug's number +# +BUG=`bk -R prs -r+ -h -d':C:' ChangeSet | \ + sed -ne 's/[Bb][Uu][Gg] *# *\([0-9][0-9]*\).*$/BUG#\1/ + s/.*BUG#\([0-9][0-9]*\)/\1/p'` +WL=`bk -R prs -r+ -h -d':C:' ChangeSet | \ + sed -ne 's/[Ww][Ll] *# *\([0-9][0-9]*\).*$/WL#\1/ + s/.*\(WL#[0-9][0-9]*\)/ \1/p'` if [ "$BUG" = "" ] then TO=dev-public@mysql.com diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index ab94c415db7..660be6bce50 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1360,6 +1360,21 @@ static int dump_local_log_entries(const char* logname) } else // reading from stdin; { + /* + Windows opens stdin in text mode by default. Certain characters + such as CTRL-Z are interpeted as events and the read() method + will stop. CTRL-Z is the EOF marker in Windows. to get past this + you have to open stdin in binary mode. Setmode() is used to set + stdin in binary mode. Errors on setting this mode result in + halting the function and printing an error message to stderr. + */ +#if defined (__WIN__) || (_WIN64) + if (_setmode(fileno(stdin), O_BINARY) == -1) + { + fprintf(stderr, "Could not set binary mode on stdin.\n"); + return 1; + } +#endif if (init_io_cache(file, fileno(stdin), 0, READ_CACHE, (my_off_t) 0, 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) return 1; diff --git a/configure.in b/configure.in index 18fe2a6ce87..6388e5836d6 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.1.13-beta) +AM_INIT_AUTOMAKE(mysql, 5.1.14-beta) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -15,7 +15,6 @@ DOT_FRM_VERSION=6 # See the libtool docs for information on how to do shared lib versions. SHARED_LIB_MAJOR_VERSION=15 SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 - # Set all version vars based on $VERSION. How do we do this more elegant ? # Remember that regexps needs to quote [ and ] since this is run through m4 MYSQL_NO_DASH_VERSION=`echo $VERSION | sed -e "s|[[a-z]]*-.*$||"` diff --git a/include/m_ctype.h b/include/m_ctype.h index 5f946a3b443..0d586030735 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -190,8 +190,8 @@ typedef struct my_charset_handler_st const unsigned char *s, const unsigned char *e); /* Functions for case and sort conversion */ - void (*caseup_str)(struct charset_info_st *, char *); - void (*casedn_str)(struct charset_info_st *, char *); + uint (*caseup_str)(struct charset_info_st *, char *); + uint (*casedn_str)(struct charset_info_st *, char *); uint (*caseup)(struct charset_info_st *, char *src, uint srclen, char *dst, uint dstlen); uint (*casedn)(struct charset_info_st *, char *src, uint srclen, @@ -324,8 +324,8 @@ extern uint my_instr_simple(struct charset_info_st *, /* Functions for 8bit */ -extern void my_caseup_str_8bit(CHARSET_INFO *, char *); -extern void my_casedn_str_8bit(CHARSET_INFO *, char *); +extern uint my_caseup_str_8bit(CHARSET_INFO *, char *); +extern uint my_casedn_str_8bit(CHARSET_INFO *, char *); extern uint my_caseup_8bit(CHARSET_INFO *, char *src, uint srclen, char *dst, uint dstlen); extern uint my_casedn_8bit(CHARSET_INFO *, char *src, uint srclen, @@ -415,8 +415,8 @@ int my_mbcharlen_8bit(CHARSET_INFO *, uint c); /* Functions for multibyte charsets */ -extern void my_caseup_str_mb(CHARSET_INFO *, char *); -extern void my_casedn_str_mb(CHARSET_INFO *, char *); +extern uint my_caseup_str_mb(CHARSET_INFO *, char *); +extern uint my_casedn_str_mb(CHARSET_INFO *, char *); extern uint my_caseup_mb(CHARSET_INFO *, char *src, uint srclen, char *dst, uint dstlen); extern uint my_casedn_mb(CHARSET_INFO *, char *src, uint srclen, diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index f0bc1e9b20a..e8f00902a33 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -59,6 +59,7 @@ dist-hook: -$(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.opt $(distdir)/extra/binlog_tests -$(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.opt $(distdir)/extra/rpl_tests $(INSTALL_DATA) $(srcdir)/include/*.inc $(distdir)/include + $(INSTALL_DATA) $(srcdir)/include/*.test $(distdir)/include $(INSTALL_DATA) $(srcdir)/r/*.result $(srcdir)/r/*.require $(distdir)/r $(INSTALL_DATA) $(srcdir)/std_data/Moscow_leap $(distdir)/std_data $(INSTALL_DATA) $(srcdir)/std_data/*.dat $(srcdir)/std_data/*.000001 $(distdir)/std_data @@ -98,6 +99,7 @@ install-data-local: -$(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.opt $(DESTDIR)$(testdir)/extra/binlog_tests -$(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.opt $(DESTDIR)$(testdir)/extra/rpl_tests $(INSTALL_DATA) $(srcdir)/include/*.inc $(DESTDIR)$(testdir)/include + $(INSTALL_DATA) $(srcdir)/include/*.test $(DESTDIR)$(testdir)/include $(INSTALL_DATA) $(srcdir)/std_data/*.dat $(DESTDIR)$(testdir)/std_data $(INSTALL_DATA) $(srcdir)/std_data/*.*001 $(DESTDIR)$(testdir)/std_data $(INSTALL_DATA) $(srcdir)/std_data/*.cnf $(DESTDIR)$(testdir)/std_data diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index 81c813e78d0..48d9b859c26 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -59,4 +59,17 @@ insert into t1 values(null); select * from t1; drop table t1; +# bug#22027 +create table t1 (a int); +create table if not exists t2 select * from t1; + +# bug#22762 +create temporary table tt1 (a int); +create table if not exists t3 like tt1; + +--replace_column 2 # 5 # +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// +show binlog events from 102; +drop table t1,t2,t3,tt1; + -- source extra/binlog_tests/binlog_insert_delayed.test diff --git a/mysql-test/extra/rpl_tests/rpl_deadlock.test b/mysql-test/extra/rpl_tests/rpl_deadlock.test index adf35a92dc6..236a5f801b0 100644 --- a/mysql-test/extra/rpl_tests/rpl_deadlock.test +++ b/mysql-test/extra/rpl_tests/rpl_deadlock.test @@ -15,7 +15,8 @@ connection master; eval CREATE TABLE t1 (a INT NOT NULL, KEY(a)) ENGINE=$engine_type; eval CREATE TABLE t2 (a INT NOT NULL, KEY(a)) ENGINE=$engine_type; -eval CREATE TABLE t3 (a INT) ENGINE=$engine_type; +# requiring 'unique' for the timeout part of the test +eval CREATE TABLE t3 (a INT UNIQUE) ENGINE=$engine_type; eval CREATE TABLE t4 (a INT) ENGINE=$engine_type; show variables like 'slave_transaction_retries'; sync_slave_with_master; @@ -30,7 +31,9 @@ stop slave; connection master; begin; # Let's keep BEGIN and the locked statement in two different relay logs. -let $1=200; +insert into t2 values (0); # t2,t1 actors of deadlock in repl-ed ta +#insert into t3 select * from t2 for update; +let $1=10; disable_query_log; while ($1) { @@ -38,16 +41,14 @@ while ($1) dec $1; } enable_query_log; -insert into t3 select * from t2 for update; insert into t1 values(1); commit; save_master_pos; connection slave; begin; -# Let's make our transaction large so that it's slave who is chosen as -# victim -let $1=1000; +# Let's make our transaction large so that it's repl-ed msta that's victim +let $1=100; disable_query_log; while ($1) { @@ -55,14 +56,21 @@ while ($1) dec $1; } enable_query_log; -select * from t1 for update; +select * from t1 for update; # t1,t2 on local slave's start slave; + +# bad option, todo: replicate a non-transactional t_sync with the transaction +# and use wait_until_rows_count macro below --real_sleep 3 # hope that slave is blocked now -insert into t2 values(22); # provoke deadlock, slave should be victim +#let $count=11; +#let $table=t_sync; +#--include wait_until_rows_count.inc + +select * from t2 for update /* dl */; # provoke deadlock, repl-ed should be victim commit; sync_with_master; -select * from t1; # check that slave succeeded finally -select * from t2; +select * from t1; # check that repl-ed succeeded finally +select * from t2 /* must be 1 */; # check that no error is reported --replace_column 1 # 7 # 8 # 9 # 16 # 22 # 23 # 33 # --replace_result $MASTER_MYPORT MASTER_MYPORT @@ -73,14 +81,16 @@ show slave status; # 2) Test lock wait timeout stop slave; -change master to master_log_pos=536; # the BEGIN log event +delete from t3; +change master to master_log_pos=544; # the BEGIN log event begin; select * from t2 for update; # hold lock start slave; ---real_sleep 10 # slave should have blocked, and be retrying +--real_sleep 10 # repl-ed should have blocked, and be retrying +select count(*) from t3 /* must be zero */; # replaying begins after rollback commit; sync_with_master; -select * from t1; # check that slave succeeded finally +select * from t1; # check that repl-ed succeeded finally select * from t2; # check that no error is reported --replace_column 1 # 7 # 8 # 9 # 11 # 16 # 22 # 23 # 33 # @@ -96,11 +106,13 @@ set global max_relay_log_size=0; # This is really copy-paste of 2) of above stop slave; -change master to master_log_pos=536; +delete from t3; +change master to master_log_pos=544; begin; select * from t2 for update; start slave; --real_sleep 10 +select count(*) from t3 /* must be zero */; # replaying begins after rollback commit; sync_with_master; select * from t1; @@ -116,4 +128,4 @@ drop table t1,t2,t3,t4; sync_slave_with_master; set global max_relay_log_size= @my_max_relay_log_size; -# End of 4.1 tests +--echo End of 5.1 tests diff --git a/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test new file mode 100644 index 00000000000..34e01b7a675 --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test @@ -0,0 +1,853 @@ +################################################# +# Author: Jeb +# Date: 2006-09-07 +# Purpose: To test having extra columns on the slave. +################################################## + +########### Clean up ################ +--disable_warnings +--disable_query_log +DROP TABLE IF EXISTS t1, t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17; +--enable_query_log +--enable_warnings + +################################################# +############ Different Table Def Test ########### +################################################# +# Purpose: To have different table def on the # +# master and slave. Most of these tests# +# should stop the slave. # +################################################# + +--echo **** Diff Table Def Start **** + +############################################## +### Try to replicate w/ PK on diff columns ### +### Should Stop Slave ### +############################################## + +--echo *** On Slave *** +sync_slave_with_master; +STOP SLAVE; +RESET SLAVE; + +eval CREATE TABLE t1 (a INT, b INT PRIMARY KEY, c CHAR(20), + d FLOAT DEFAULT '2.00', + e CHAR(4) DEFAULT 'TEST') + ENGINE=$engine_type; + +--echo *** Create t1 on Master *** +connection master; +eval CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(10) + ) ENGINE=$engine_type; + +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +INSERT INTO t1 () VALUES(1,2,'TEXAS'),(2,1,'AUSTIN'),(3,4,'QA'); +SELECT * FROM t1 ORDER BY a; + +--echo *** Select from slave *** +sync_slave_with_master; +SELECT * FROM t1 ORDER BY a; + +--echo *** Drop t1 *** +connection master; +DROP TABLE t1; +sync_slave_with_master; + +############################################ +### Try to replicate CHAR(10) to CHAR(5) ### +### Should Stop Slave or truncate value ### +############################################ + +## BUG22086 +#--echo *** Create t2 on slave *** +#STOP SLAVE; +#RESET SLAVE; +#eval CREATE TABLE t2 (a INT, b INT PRIMARY KEY, c CHAR(5), +# d FLOAT DEFAULT '2.00', +# e CHAR(5) DEFAULT 'TEST2') +# ENGINE=$engine_type; +# +#--echo *** Create t2 on Master *** +#connection master; +#eval CREATE TABLE t2 (a INT PRIMARY KEY, b INT, c CHAR(10) +# ) ENGINE=$engine_type; +#RESET MASTER; +# +#--echo *** Start Slave *** +#connection slave; +#START SLAVE; +# +#--echo *** Master Data Insert *** +#connection master; +# +#INSERT INTO t2 () VALUES(1,2,'Kyle, TEX'),(2,1,'JOE AUSTIN'),(3,4,'QA TESTING'); +#SELECT * FROM t2 ORDER BY a; + +#--echo *** Select from slave *** +#sync_slave_with_master; +#SELECT * FROM t2 ORDER BY a; + +#--echo *** Drop t2 *** +#connection master; +#DROP TABLE t2; +#sync_slave_with_master; + +#################################### +### Try to replicate BLOB to INT ### +### Should Stop Slave ### +#################################### +--echo *** Create t3 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t3 (a INT, b INT PRIMARY KEY, c CHAR(20), + d FLOAT DEFAULT '2.00', + e CHAR(5) DEFAULT 'TEST2') + ENGINE=$engine_type; + +--echo *** Create t3 on Master *** +connection master; +eval CREATE TABLE t3 (a BLOB, b INT PRIMARY KEY, c CHAR(20) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); + +INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TESTING'); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Drop t3 *** +connection master; +DROP TABLE t3; +sync_slave_with_master; + +##################################################### +# Columns with different types, more columns at end # +# Expect: proper error message (wrong types) # +##################################################### + +--echo *** Create t4 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t4 (a INT, b INT PRIMARY KEY, c CHAR(20), + d FLOAT DEFAULT '2.00', + e CHAR(5) DEFAULT 'TEST2') + ENGINE=$engine_type; + +--echo *** Create t4 on Master *** +connection master; +eval CREATE TABLE t4 (a DECIMAL(8,2), b INT PRIMARY KEY, c CHAR(20) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +INSERT INTO t4 () VALUES(100.22,2,'Kyle, TEX'),(200.26,1,'JOE AUSTIN'), + (30000.22,4,'QA TESTING'); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Drop t4 *** +connection master; +DROP TABLE t4; +sync_slave_with_master; + +####################################################### +# Columns with different types, same number of colums # +# Expect: Proper error message # +####################################################### + +--echo *** Create t5 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t5 (a INT PRIMARY KEY, b CHAR(5), + c FLOAT, d INT, e DOUBLE, + f DECIMAL(8,2))ENGINE=$engine_type; + +--echo *** Create t5 on Master *** +connection master; +eval CREATE TABLE t5 (a INT PRIMARY KEY, b VARCHAR(6), + c DECIMAL(8,2), d BIT, e BLOB, + f FLOAT) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +INSERT INTO t5 () VALUES(1,'Kyle',200.23,1,'b1b1',23.00098), + (2,'JOE',300.01,0,'b2b2',1.0000009); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Drop t5 *** +connection master; +DROP TABLE t5; +sync_slave_with_master; + +####################################################### +################## Continued ########################## +####################################################### +# Columns with different types, same number of colums # +# Expect: Proper error message # +####################################################### + +--echo *** Create t6 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t6 (a INT PRIMARY KEY, b CHAR(5), + c FLOAT, d INT)ENGINE=$engine_type; + +--echo *** Create t6 on Master *** +connection master; +eval CREATE TABLE t6 (a INT PRIMARY KEY, b VARCHAR(6), + c DECIMAL(8,2), d BIT + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +INSERT INTO t6 () VALUES(1,'Kyle',200.23,1), + (2,'JOE',300.01,0); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=3; +#START SLAVE; + +--echo *** Drop t6 *** +connection master; +DROP TABLE t6; +connection slave; +DROP TABLE t6; +START SLAVE; +#sync_slave_with_master; + + +--echo **** Diff Table Def End **** + +####################################### +#### Extra Column on Slave Testing #### +####################################### +# Purpose: To test extra colums on the# +# Slave # +####################################### + +--echo **** Extra Colums Start **** + +########################################## +# More columns in slave at end of table, # +# added columns have default values # +# Expect: it should work, default values # +# should be used # +########################################## + +--echo *** Create t7 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t7 (a INT KEY, b BLOB, c CHAR(5), + d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', + e CHAR(20) DEFAULT 'Extra Column Testing') + ENGINE=$engine_type; + +--echo *** Create t7 on Master *** +connection master; +eval CREATE TABLE t7 (a INT PRIMARY KEY, b BLOB, c CHAR(5) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t7 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t7 ORDER BY a; + +--echo *** Select from slave *** +sync_slave_with_master; +SELECT * FROM t7 ORDER BY a; + +--echo *** Drop t7 *** +connection master; +DROP TABLE t7; +sync_slave_with_master; + +########################################### +# More columns in slave at end of table, # +# added columns do not have default values# +# Expect: Proper error message # +########################################### +# NOTE: This should fail but currently # +# works. BUG#22101 # +########################################### +--echo *** Create t8 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t8 (a INT KEY, b BLOB, c CHAR(5), + d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', + e INT)ENGINE=$engine_type; + +--echo *** Create t8 on Master *** +connection master; +eval CREATE TABLE t8 (a INT PRIMARY KEY, b BLOB, c CHAR(5) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t8 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); + +### Uncomment once bug is fixed + +#connection slave; +#wait_for_slave_to_stop; +#--replace_result $MASTER_MYPORT MASTER_PORT +#--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +#--query_vertical SHOW SLAVE STATUS +#SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +#START SLAVE; + +--echo *** Drop t8 *** +connection master; +DROP TABLE t8; +sync_slave_with_master; + +########################################### +############# Continued ################### +# More columns in slave at end of table, # +# added columns do not have default values# +# Expect: Proper error message # +########################################### +# Commented out due to Bug #23907 Extra Slave Col is not +# erroring on extra col with no default values. +######################################################## +#--echo *** Create t9 on slave *** +#STOP SLAVE; +#RESET SLAVE; +#eval CREATE TABLE t9 (a INT KEY, b BLOB, c CHAR(5), +# d TIMESTAMP, +# e INT DEFAULT '1')ENGINE=$engine_type; + +#--echo *** Create t9 on Master *** +#connection master; +#eval CREATE TABLE t9 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +# ) ENGINE=$engine_type; +#RESET MASTER; + +#--echo *** Start Slave *** +#connection slave; +#START SLAVE; + +#--echo *** Master Data Insert *** +#connection master; +#set @b1 = 'b1b1b1b1'; +#set @b1 = concat(@b1,@b1); +#INSERT INTO t9 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); + +#--echo ************************************************* +#--echo ** Currently giving wrong error see bug#22234 *** +#--echo ************************************************* +#sync_slave_with_master; +#connection slave; + +#--echo *** Select from T9 *** +#wait_for_slave_to_stop; +#--replace_result $MASTER_MYPORT MASTER_PORT +#--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +#--query_vertical SHOW SLAVE STATUS +#SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +#START SLAVE; + +#--echo *** Drop t9 *** +#connection master; +#DROP TABLE t9; +#sync_slave_with_master; + +############################################ +# More columns in slave at middle of table # +# Expect: Proper error message # +############################################ +--echo *** Create t10 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t10 (a INT KEY, b BLOB, f DOUBLE DEFAULT '233', + c CHAR(5), e INT DEFAULT '1')ENGINE=$engine_type; + +--echo *** Create t10 on Master *** +connection master; +eval CREATE TABLE t10 (a INT PRIMARY KEY, b BLOB, c CHAR(5) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t10 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Drop t10 *** +connection master; +DROP TABLE t10; +sync_slave_with_master; + +############################################ +############## Continued ################### +############################################ +# More columns in slave at middle of table # +# Expect: Proper error message # +############################################ +--echo *** Create t11 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t11 (a INT KEY, b BLOB, f TEXT, + c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE=$engine_type; + +--echo *** Create t11 on Master *** +connection master; +eval CREATE TABLE t11 (a INT PRIMARY KEY, b BLOB, c VARCHAR(254) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t11 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Drop t11 *** +connection master; +DROP TABLE t11; +sync_slave_with_master; + +############################################ +############## Continued ################### +############################################ +# More columns in slave at middle of table # +# Expect: This one should pass blob-text # +############################################ +--echo *** Create t12 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t12 (a INT KEY, b BLOB, f TEXT, + c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE=$engine_type; + +--echo *** Create t12 on Master *** +connection master; +eval CREATE TABLE t12 (a INT PRIMARY KEY, b BLOB, c BLOB + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t12 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t12 ORDER BY a; + +--echo *** Select on Slave *** +sync_slave_with_master; +SELECT * FROM t12 ORDER BY a; + +--echo *** Drop t12 *** +connection master; +DROP TABLE t12; +sync_slave_with_master; + +--echo **** Extra Colums End **** + +############################### +# BUG#22177 CURRENT_TIMESTAMP # +# Sould work with ^ # +############################### +--echo *** BUG 22177 Start *** +--echo *** Create t13 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t13 (a INT KEY, b BLOB, c CHAR(5), + d INT DEFAULT '1', + e TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP + )ENGINE=$engine_type; + +--echo *** Create t13 on Master *** +connection master; +eval CREATE TABLE t13 (a INT PRIMARY KEY, b BLOB, c CHAR(5) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t13 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t13 ORDER BY a; + +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 5 CURRENT_TIMESTAMP +SELECT * FROM t13 ORDER BY a; + +--echo *** Drop t13 *** +connection master; +DROP TABLE t13; +sync_slave_with_master; + +--echo *** 22117 END *** + +############################## +# ALTER MASTER TABLE TESTING # +############################## + +--echo *** Alter Master Table Testing Start *** + +#################################################### +# - Alter Master adding columns at middle of table # +# Expect: columns added # +#################################################### + +--echo *** Create t14 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t14 (c1 INT KEY, c4 BLOB, c5 CHAR(5), + c6 INT DEFAULT '1', + c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP + )ENGINE=$engine_type; + +--echo *** Create t14 on Master *** +connection master; +eval CREATE TABLE t14 (c1 INT PRIMARY KEY, c4 BLOB, c5 CHAR(5) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +ALTER TABLE t14 ADD COLUMN c2 DECIMAL(8,2) AFTER c1; +ALTER TABLE t14 ADD COLUMN c3 TEXT AFTER c2; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), + (2,2.00,'This Test Should work',@b1,'JOE'), + (3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t14 ORDER BY c1; + + +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 7 CURRENT_TIMESTAMP +SELECT * FROM t14 ORDER BY c1; + + +#################################################### +# - Alter Master Dropping columns from the middle. # +# Expect: columns dropped # +#################################################### + +--echo *** connect to master and drop columns *** +connection master; +ALTER TABLE t14 DROP COLUMN c2; +ALTER TABLE t14 DROP COLUMN c4; +--echo *** Select from Master *** +SELECT * FROM t14 ORDER BY c1; + +--echo *** Select from Slave *** +sync_slave_with_master; +--replace_column 5 CURRENT_TIMESTAMP +SELECT * FROM t14 ORDER BY c1; + +--echo *** Drop t14 *** +connection master; +DROP TABLE t14; +sync_slave_with_master; + +############################################################## +# - Alter Master adding columns that already exist on slave. # +# Expect: proper error message # +############################################################## + +--echo *** Create t15 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t15 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, + c4 BLOB, c5 CHAR(5), + c6 INT DEFAULT '1', + c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP + )ENGINE=$engine_type; + +--echo *** Create t15 on Master *** +connection master; +eval CREATE TABLE t15 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, + c4 BLOB, c5 CHAR(5)) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t15 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), + (2,2.00,'This Test Should work',@b1,'JOE'), + (3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t15 ORDER BY c1; + + +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 7 CURRENT_TIMESTAMP +SELECT * FROM t15 ORDER BY c1; + +--echo *** Add column on master that is a Extra on Slave *** +connection master; +ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5; + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1060 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo *** Try to insert in master **** +connection master; +INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); +SELECT * FROM t15 ORDER BY c1; + +--echo *** Try to select from slave **** +sync_slave_with_master; +--replace_column 7 CURRENT_TIMESTAMP +SELECT * FROM t15 ORDER BY c1; + +--echo *** DROP TABLE t15 *** +connection master; +DROP TABLE t15; +sync_slave_with_master; + +#################################### +# - Alter Master and ADD PARTITION # +# Expect:? # +#################################### + +--echo *** Create t16 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t16 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, + c4 BLOB, c5 CHAR(5), + c6 INT DEFAULT '1', + c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP + )ENGINE=$engine_type; + +--echo *** Create t16 on Master *** +connection master; +eval CREATE TABLE t16 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, + c4 BLOB, c5 CHAR(5))ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t16 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), + (2,2.00,'This Test Should work',@b1,'JOE'), + (3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t16 ORDER BY c1; + +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 7 CURRENT_TIMESTAMP +SELECT * FROM t16 ORDER BY c1; + +--echo *** Add Partition on master *** +connection master; +ALTER TABLE t16 PARTITION BY KEY(c1) PARTITIONS 4; +INSERT INTO t16 () VALUES(4,1.00,'Replication Rocks',@b1,'Omer'); +SHOW CREATE TABLE t16; + +--echo *** Show table on Slave **** +sync_slave_with_master; +SHOW CREATE TABLE t16; + +--echo *** DROP TABLE t16 *** +connection master; +DROP TABLE t16; +sync_slave_with_master; + +--echo *** Alter Master End *** + +############################################ +### Try to replicate BIGINT to SMALLINT ### +### Should Stop Slave ### +############################################ + +--echo *** Create t17 on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t17 (a SMALLINT, b INT PRIMARY KEY, c CHAR(5), + d FLOAT DEFAULT '2.00', + e CHAR(5) DEFAULT 'TEST2') + ENGINE=$engine_type; + +--echo *** Create t17 on Master *** +connection master; +eval CREATE TABLE t17 (a BIGINT PRIMARY KEY, b INT, c CHAR(10) + ) ENGINE=$engine_type; +RESET MASTER; + +--echo *** Start Slave *** +connection slave; +START SLAVE; + +--echo *** Master Data Insert *** +connection master; + +INSERT INTO t17 () VALUES(9223372036854775807,2,'Kyle, TEX'); + +--echo ******************************************** +--echo *** Expect slave to fail with Error 1522 *** +--echo ******************************************** +connection slave; +wait_for_slave_to_stop; +--replace_result $MASTER_MYPORT MASTER_PORT +--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # +--query_vertical SHOW SLAVE STATUS +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; + +--echo ** DROP table t17 *** +connection master; +DROP TABLE t17; +sync_slave_with_master; + +#### Clean Up #### +--disable_warnings +--disable_query_log +DROP TABLE IF EXISTS t1, t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17; +--enable_query_log +--enable_warnings + +# END 5.1 Test Case + + diff --git a/mysql-test/extra/rpl_tests/rpl_ndb_ddl.test b/mysql-test/extra/rpl_tests/rpl_ndb_ddl.test new file mode 100644 index 00000000000..26c368589ba --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_ndb_ddl.test @@ -0,0 +1,507 @@ +######################## rpl_ddl.test ######################## +# # +# DDL statements (sometimes with implicit COMMIT) executed # +# by the master and it's propagation into the slave # +# # +############################################################## + +# +# NOTE, PLEASE BE CAREFUL, WHEN MODIFYING THE TESTS !! +# +# 1. !All! objects to be dropped, renamed, altered ... must be created +# in AUTOCOMMIT= 1 mode before AUTOCOMMIT is set to 0 and the test +# sequences start. +# +# 2. Never use a test object, which was direct or indirect affected by a +# preceeding test sequence again. +# Except table d1.t1 where ONLY DML is allowed. +# +# If one preceeding test sequence hits a (sometimes not good visible, +# because the sql error code of the statement might be 0) bug +# and these rules are ignored, a following test sequence might earn ugly +# effects like failing 'sync_slave_with_master', crashes of the slave or +# abort of the test case etc.. +# +# 3. The assignment of the DDL command to be tested to $my_stmt can +# be a bit difficult. "'" must be avoided, because the test +# routine "include/rpl_stmt_seq.inc" performs a +# eval SELECT CONCAT('######## ','$my_stmt',' ########') as ""; +# + +############################################################### +# Some preparations +############################################################### +# The sync_slave_with_master is needed to make the xids deterministic. +sync_slave_with_master; +connection master; + +SET AUTOCOMMIT = 1; +# +# 1. DROP all objects, which probably already exist, but must be created here +# +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +DROP DATABASE IF EXISTS mysqltest3; +--enable_warnings +# +# 2. CREATE all objects needed +# working database is mysqltest1 +# working (transactional!) is mysqltest1.t1 +# +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +eval CREATE TABLE mysqltest1.t1 (f1 BIGINT) ENGINE=$engine_type; +INSERT INTO mysqltest1.t1 SET f1= 0; +eval CREATE TABLE mysqltest1.t2 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t3 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t4 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t5 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t6 (f1 BIGINT) ENGINE=$engine_type; +CREATE INDEX my_idx6 ON mysqltest1.t6(f1); +eval CREATE TABLE mysqltest1.t7 (f1 BIGINT) ENGINE=$engine_type; +INSERT INTO mysqltest1.t7 SET f1= 0; +eval CREATE TABLE mysqltest1.t8 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t9 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t10 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t11 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t12 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t13 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t14 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t15 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t16 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t17 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t18 (f1 BIGINT) ENGINE=$engine_type; +eval CREATE TABLE mysqltest1.t19 (f1 BIGINT) ENGINE=$engine_type; +CREATE TEMPORARY TABLE mysqltest1.t23 (f1 BIGINT); + +# +# 3. master sessions: never do AUTOCOMMIT +# slave sessions: never do AUTOCOMMIT +# +SET AUTOCOMMIT = 0; +use mysqltest1; +sync_slave_with_master; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SET AUTOCOMMIT = 0; +use mysqltest1; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + + +# We don't want to abort the whole test if one statement sent +# to the server gets an error, because the following test +# sequences are nearly independend of the previous statements. +--disable_abort_on_error + +############################################################### +# Banal case: (explicit) COMMIT and ROLLBACK +# Just for checking if the test sequence is usable +############################################################### + +let $my_stmt= COMMIT; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc + +let $my_stmt= ROLLBACK; +let $my_master_commit= false; +let $my_slave_commit= false; +--source include/rpl_stmt_seq.inc + +############################################################### +# Cases with commands very similar to COMMIT +############################################################### + +let $my_stmt= SET AUTOCOMMIT=1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SET AUTOCOMMIT=0; + +let $my_stmt= START TRANSACTION; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc + +let $my_stmt= BEGIN; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc + +############################################################### +# Cases with (BASE) TABLES and (UPDATABLE) VIEWs +############################################################### + +let $my_stmt= DROP TABLE mysqltest1.t2; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW TABLES LIKE 't2'; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW TABLES LIKE 't2'; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +# Note: Since this test is executed with a skip-innodb slave, the +# slave incorrectly commits the insert. One can *not* have InnoDB on +# master and MyISAM on slave and expect that a transactional rollback +# after a CREATE TEMPORARY TABLE should work correctly on the slave. +# For this to work properly the handler on the slave must be able to +# handle transactions (e.g. InnoDB or NDB). +let $my_stmt= DROP TEMPORARY TABLE mysqltest1.t23; +let $my_master_commit= false; +let $my_slave_commit= false; +--source include/rpl_stmt_seq.inc +SHOW TABLES LIKE 't23'; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW TABLES LIKE 't23'; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +let $my_stmt= RENAME TABLE mysqltest1.t3 to mysqltest1.t20; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW TABLES LIKE 't20'; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW TABLES LIKE 't20'; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +let $my_stmt= ALTER TABLE mysqltest1.t4 ADD column f2 BIGINT; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +describe mysqltest1.t4; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +describe mysqltest1.t4; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +let $my_stmt= CREATE TABLE mysqltest1.t21 (f1 BIGINT) ENGINE=; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq2.inc + +# Note: Since this test is executed with a skip-innodb slave, the +# slave incorrectly commits the insert. One can *not* have InnoDB on +# master and MyISAM on slave and expect that a transactional rollback +# after a CREATE TEMPORARY TABLE should work correctly on the slave. +# For this to work properly the handler on the slave must be able to +# handle transactions (e.g. InnoDB or NDB). +let $engine=''; +let $eng_type=''; + +let $my_stmt= CREATE TEMPORARY TABLE mysqltest1.t22 (f1 BIGINT); +let $my_master_commit= false; +let $my_slave_commit= false; +--source include/rpl_stmt_seq.inc + +let $my_stmt= TRUNCATE TABLE mysqltest1.t7; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SELECT * FROM mysqltest1.t7; +--echo -------- switch to slave -------- +sync_slave_with_master; +SELECT * FROM mysqltest1.t7; +--echo -------- switch to master ------- +connection master; + +############################################################### +# Cases with LOCK/UNLOCK +############################################################### + +# MySQL insists in locking mysqltest1.t1, because rpl_stmt_seq performs an +# INSERT into this table. +let $my_stmt= LOCK TABLES mysqltest1.t1 WRITE, mysqltest1.t8 READ; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +UNLOCK TABLES; + +# No prior locking +let $my_stmt= UNLOCK TABLES; +let $my_master_commit= false; +let $my_slave_commit= false; +--source include/rpl_stmt_seq.inc + +# With prior read locking +# Note that this test generate an error since the rpl_stmt_seq.inc +# tries to insert into t1. +LOCK TABLES mysqltest1.t1 READ; +let $my_stmt= UNLOCK TABLES; +let $my_master_commit= false; +let $my_slave_commit= false; +--source include/rpl_stmt_seq.inc + +# With prior write locking +LOCK TABLES mysqltest1.t1 WRITE, mysqltest1.t8 READ; +let $my_stmt= UNLOCK TABLES; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc + +############################################################### +# Cases with INDEXES +############################################################### + +let $my_stmt= DROP INDEX my_idx6 ON mysqltest1.t6; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW INDEX FROM mysqltest1.t6; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW INDEX FROM mysqltest1.t6; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +let $my_stmt= CREATE INDEX my_idx5 ON mysqltest1.t5(f1); +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW INDEX FROM mysqltest1.t5; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW INDEX FROM mysqltest1.t5; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +############################################################### +# Cases with DATABASE +############################################################### + +let $my_stmt= DROP DATABASE mysqltest2; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW DATABASES LIKE "mysqltest2"; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW DATABASES LIKE "mysqltest2"; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +let $my_stmt= CREATE DATABASE mysqltest3; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW DATABASES LIKE "mysqltest3"; +connection slave; +--disable_query_log +SELECT '-------- switch to slave --------' as ""; +--enable_query_log +SHOW DATABASES LIKE "mysqltest3"; +connection master; +--disable_query_log +SELECT '-------- switch to master -------' as ""; +--enable_query_log + +# End of 4.1 tests + +############################################################### +# Cases with stored procedures +############################################################### +let $my_stmt= CREATE PROCEDURE p1() READS SQL DATA SELECT "this is p1"; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +--vertical_results +--replace_column 5 # 6 # +SHOW PROCEDURE STATUS LIKE 'p1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +--replace_column 5 # 6 # +SHOW PROCEDURE STATUS LIKE 'p1'; +connection master; +--horizontal_results + +let $my_stmt= ALTER PROCEDURE p1 COMMENT "I have been altered"; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +--vertical_results +--replace_column 5 # 6 # +SHOW PROCEDURE STATUS LIKE 'p1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +--replace_column 5 # 6 # +SHOW PROCEDURE STATUS LIKE 'p1'; +connection master; +--horizontal_results + +let $my_stmt= DROP PROCEDURE p1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +--vertical_results +SHOW PROCEDURE STATUS LIKE 'p1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SHOW PROCEDURE STATUS LIKE 'p1'; +connection master; +--horizontal_results + +############################################################### +# Cases with VIEWs +############################################################### +let $my_stmt= CREATE OR REPLACE VIEW v1 as select * from t1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW CREATE VIEW v1; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SHOW CREATE VIEW v1; +connection master; + +let $my_stmt= ALTER VIEW v1 AS select f1 from t1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW CREATE VIEW v1; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SHOW CREATE VIEW v1; +connection master; + +let $my_stmt= DROP VIEW IF EXISTS v1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +--error 1146 +SHOW CREATE VIEW v1; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +--error 1146 +SHOW CREATE VIEW v1; +connection master; + +############################################################### +# Cases with TRIGGERs +############################################################### +let $my_stmt= CREATE TRIGGER trg1 BEFORE INSERT ON t1 FOR EACH ROW SET @a:=1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW TRIGGERS; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SHOW TRIGGERS; +connection master; + +let $my_stmt= DROP TRIGGER trg1; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SHOW TRIGGERS; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SHOW TRIGGERS; +connection master; + +############################################################### +# Cases with USERs +############################################################### +let $my_stmt= CREATE USER user1@localhost; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SELECT user FROM mysql.user WHERE user = 'user1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SELECT user FROM mysql.user WHERE user = 'user1'; +connection master; + +let $my_stmt= RENAME USER user1@localhost TO rename1@localhost; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SELECT user FROM mysql.user WHERE user = 'rename1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SELECT user FROM mysql.user WHERE user = 'rename1'; +connection master; + +let $my_stmt= DROP USER rename1@localhost; +let $my_master_commit= true; +let $my_slave_commit= true; +--source include/rpl_stmt_seq.inc +SELECT user FROM mysql.user WHERE user = 'rename1'; +--disable_query_log +SELECT '-------- switch to slave -------' as ""; +--enable_query_log +connection slave; +SELECT user FROM mysql.user WHERE user = 'rename1'; +connection master; + +############################################################### +# Cleanup +############################################################### +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +DROP DATABASE IF EXISTS mysqltest3; +--enable_warnings + +-- source include/master-slave-end.inc + diff --git a/mysql-test/include/strict_autoinc.inc b/mysql-test/include/strict_autoinc.inc index 6960440f3a7..823efcc2040 100644 --- a/mysql-test/include/strict_autoinc.inc +++ b/mysql-test/include/strict_autoinc.inc @@ -2,6 +2,10 @@ # Test for strict-mode autoincrement # +--disable_warnings +drop table if exists t1; +--enable_warnings + set @org_mode=@@sql_mode; eval create table t1 ( diff --git a/mysql-test/install_test_db.sh b/mysql-test/install_test_db.sh index d7c19438836..87ca7e5a530 100644 --- a/mysql-test/install_test_db.sh +++ b/mysql-test/install_test_db.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (C) 1997-2002 MySQL AB +# Copyright (C) 1997-2006 MySQL AB # For a more info consult the file COPYRIGHT distributed with this file # This scripts creates the privilege tables db, host, user, tables_priv, @@ -7,19 +7,26 @@ if [ x$1 = x"--bin" ]; then shift 1 + BINARY_DIST=1 + + bindir=../bin + scriptdir=../bin + libexecdir=../libexec # Check if it's a binary distribution or a 'make install' if test -x ../libexec/mysqld then execdir=../libexec + elif test -x ../../sbin/mysqld # RPM installation + then + execdir=../../sbin + bindir=../../bin + scriptdir=../../bin + libexecdir=../../libexec else execdir=../bin fi - bindir=../bin - BINARY_DIST=1 fix_bin=mysql-test - scriptdir=../bin - libexecdir=../libexec else execdir=../sql bindir=../client diff --git a/mysql-test/mysql-test-run-shell.sh b/mysql-test/mysql-test-run-shell.sh index 99c844b2f78..9c55d879db4 100644 --- a/mysql-test/mysql-test-run-shell.sh +++ b/mysql-test/mysql-test-run-shell.sh @@ -180,18 +180,29 @@ fi # Misc. Definitions #-- -if [ -d ../sql ] ; then +# BASEDIR is always above mysql-test directory ... +MYSQL_TEST_DIR=`pwd` +cd .. + +if [ -d ./sql ] ; then SOURCE_DIST=1 else BINARY_DIST=1 fi -#BASEDIR is always one above mysql-test directory -CWD=`pwd` -cd .. -BASEDIR=`pwd` -cd $CWD -MYSQL_TEST_DIR=$BASEDIR/mysql-test +# ... one level for tar.gz, two levels for a RPM installation +if [ -d ./bin ] ; then + # this is not perfect: we have + # /usr/share/mysql/ # mysql-test-run is here, so this is "$MYSQL_TEST_DIR" + # /usr/bin/ # with MySQL client programs + # so the existence of "/usr/share/bin/" would make this test fail. + BASEDIR=`pwd` +else + cd .. + BASEDIR=`pwd` +fi + +cd $MYSQL_TEST_DIR MYSQL_TEST_WINDIR=$MYSQL_TEST_DIR MYSQLTEST_VARDIR=$MYSQL_TEST_DIR/var export MYSQL_TEST_DIR MYSQL_TEST_WINDIR MYSQLTEST_VARDIR @@ -784,8 +795,15 @@ else if test -x "$BASEDIR/libexec/mysqld" then MYSQLD="$VALGRIND $BASEDIR/libexec/mysqld" - else + elif test -x "$BASEDIR/bin/mysqld" + then MYSQLD="$VALGRIND $BASEDIR/bin/mysqld" + elif test -x "$BASEDIR/sbin/mysqld" + then + MYSQLD="$VALGRIND $BASEDIR/sbin/mysqld" + else + $ECHO "Fatal error: Cannot find program mysqld in $BASEDIR/{libexec,bin,sbin}" 1>&2 + exit 1 fi CLIENT_BINDIR="$BASEDIR/bin" if test -d "$BASEDIR/tests" @@ -1406,7 +1424,7 @@ start_master() then $ECHO "set args $master_args" > $GDB_MASTER_INIT$1 $ECHO "To start gdb for the master , type in another window:" - $ECHO "cd $CWD ; gdb -x $GDB_MASTER_INIT$1 $MASTER_MYSQLD" + $ECHO "cd $MYSQL_TEST_DIR ; gdb -x $GDB_MASTER_INIT$1 $MASTER_MYSQLD" wait_for_master=1500 else ( $ECHO set args $master_args; @@ -1563,7 +1581,7 @@ start_slave() then $ECHO "set args $slave_args" > $GDB_SLAVE_INIT echo "To start gdb for the slave, type in another window:" - echo "cd $CWD ; gdb -x $GDB_SLAVE_INIT $SLAVE_MYSQLD" + echo "cd $MYSQL_TEST_DIR ; gdb -x $GDB_SLAVE_INIT $SLAVE_MYSQLD" wait_for_slave=1500 else ( $ECHO set args $slave_args; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 73bde3b3e57..3c855fb0499 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -225,6 +225,7 @@ our $opt_ndbconnectstring_slave; our $opt_record; our $opt_report_features; our $opt_check_testcases; +my $opt_report_features; our $opt_skip; our $opt_skip_rpl; @@ -639,7 +640,7 @@ sub command_line_setup () { $glob_hostname= mtr_short_hostname(); - # 'basedir' is always parent of "mysql-test" directory + # Find the absolute path to the test directory $glob_mysql_test_dir= cwd(); if ( $glob_cygwin_perl ) { @@ -647,11 +648,27 @@ sub command_line_setup () { $glob_mysql_test_dir= `cygpath -m "$glob_mysql_test_dir"`; chomp($glob_mysql_test_dir); } - $glob_basedir= dirname($glob_mysql_test_dir); + + # In most cases, the base directory we find everything relative to, + # is the parent directory of the "mysql-test" directory. For source + # distributions, TAR binary distributions and some other packages. + $glob_basedir= dirname($glob_mysql_test_dir); + + # In the RPM case, binaries and libraries are installed in the + # default system locations, instead of having our own private base + # directory. And we install "/usr/share/mysql-test". Moving up one + # more directory relative to "mysql-test" gives us a usable base + # directory for RPM installs. + if ( ! $opt_source_dist and ! -d "$glob_basedir/bin" ) + { + $glob_basedir= dirname($glob_basedir); + } # Expect mysql-bench to be located adjacent to the source tree, by default $glob_mysql_bench_dir= "$glob_basedir/../mysql-bench" unless defined $glob_mysql_bench_dir; + $glob_mysql_bench_dir= undef + unless -d $glob_mysql_bench_dir; $path_my_basedir= $opt_source_dist ? $glob_mysql_test_dir : $glob_basedir; @@ -679,7 +696,8 @@ sub command_line_setup () { "$path_client_bindir/mysqld-debug", "$path_client_bindir/mysqld-max", "$glob_basedir/libexec/mysqld", - "$glob_basedir/bin/mysqld"); + "$glob_basedir/bin/mysqld", + "$glob_basedir/sbin/mysqld"); # Use the mysqld found above to find out what features are available collect_mysqld_features(); @@ -796,6 +814,13 @@ sub command_line_setup () { $opt_vardir= "$glob_mysql_test_dir/$opt_vardir"; } + # Ensure a proper error message + mkpath("$opt_vardir"); + unless ( -d $opt_vardir and -w $opt_vardir ) + { + mtr_error("Writable 'var' directory is needed, use the '--vardir' option"); + } + # -------------------------------------------------------------------------- # Set tmpdir # -------------------------------------------------------------------------- @@ -1308,7 +1333,8 @@ sub executable_setup_im () { mtr_exe_maybe_exists( "$glob_basedir/server-tools/instance-manager/mysqlmanager", "$glob_basedir/libexec/mysqlmanager", - "$glob_basedir/bin/mysqlmanager"); + "$glob_basedir/bin/mysqlmanager", + "$glob_basedir/sbin/mysqlmanager"); return ($exe_im eq ""); } diff --git a/mysql-test/r/binlog_row_binlog.result b/mysql-test/r/binlog_row_binlog.result index 7cbfa525798..aee270bd7f6 100644 --- a/mysql-test/r/binlog_row_binlog.result +++ b/mysql-test/r/binlog_row_binlog.result @@ -245,6 +245,24 @@ select * from t1; id 127 drop table t1; +create table t1 (a int); +create table if not exists t2 select * from t1; +create temporary table tt1 (a int); +create table if not exists t3 like tt1; +show binlog events from 102; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) +master-bin.000001 # Table_map 1 # table_id: # (test.t1) +master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; create table t1 (a int) +master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( + `a` int(11) DEFAULT NULL +) +master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( + `a` int(11) DEFAULT NULL +) +drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; insert delayed into t1 values (207); @@ -256,6 +274,14 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_incre master-bin.000001 # Table_map 1 # table_id: # (test.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; create table t1 (a int) +master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t2` ( + `a` int(11) DEFAULT NULL +) +master-bin.000001 # Query 1 # use `test`; CREATE TABLE IF NOT EXISTS `t3` ( + `a` int(11) DEFAULT NULL +) +master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1`,`t2`,`t3` /* generated by server */ master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F diff --git a/mysql-test/r/binlog_stm_binlog.result b/mysql-test/r/binlog_stm_binlog.result index eed2df28d1f..a9b3f69ecee 100644 --- a/mysql-test/r/binlog_stm_binlog.result +++ b/mysql-test/r/binlog_stm_binlog.result @@ -155,6 +155,21 @@ select * from t1; id 127 drop table t1; +create table t1 (a int); +create table if not exists t2 select * from t1; +create temporary table tt1 (a int); +create table if not exists t3 like tt1; +show binlog events from 102; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_increment primary key) +master-bin.000001 # Intvar 1 # INSERT_ID=127 +master-bin.000001 # Query 1 # use `test`; insert into t1 values(null) +master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; create table t1 (a int) +master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) +master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +drop table t1,t2,t3,tt1; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam; set @@session.auto_increment_increment=1, @@session.auto_increment_offset=1; insert delayed into t1 values (207); @@ -166,6 +181,11 @@ master-bin.000001 # Query 1 # use `test`; create table t1 (id tinyint auto_incre master-bin.000001 # Intvar 1 # INSERT_ID=127 master-bin.000001 # Query 1 # use `test`; insert into t1 values(null) master-bin.000001 # Query 1 # use `test`; drop table t1 +master-bin.000001 # Query 1 # use `test`; create table t1 (a int) +master-bin.000001 # Query 1 # use `test`; create table if not exists t2 select * from t1 +master-bin.000001 # Query 1 # use `test`; create temporary table tt1 (a int) +master-bin.000001 # Query 1 # use `test`; create table if not exists t3 like tt1 +master-bin.000001 # Query 1 # use `test`; drop table t1,t2,t3,tt1 master-bin.000001 # Query 1 # use `test`; create table t1 (a int not null auto_increment, primary key (a)) engine=myisam master-bin.000001 # Table_map 1 # table_id: # (test.t1) master-bin.000001 # Write_rows 1 # table_id: # flags: STMT_END_F diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result index d0087b03c17..e85d379c932 100644 --- a/mysql-test/r/ctype_recoding.result +++ b/mysql-test/r/ctype_recoding.result @@ -171,8 +171,8 @@ create table t1 (a char(10) character set koi8r, b text character set koi8r); insert into t1 values ('test','test'); insert into t1 values ('ÊÃÕË','ÊÃÕË'); Warnings: -Warning 1265 Data truncated for column 'a' at row 1 -Warning 1265 Data truncated for column 'b' at row 1 +Warning 1366 Incorrect string value: '\xCA\xC3\xD5\xCB' for column 'a' at row 1 +Warning 1366 Incorrect string value: '\xCA\xC3\xD5\xCB' for column 'b' at row 1 drop table t1; set names koi8r; create table t1 (a char(10) character set cp1251); diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 4f08b97492f..a65b19695ec 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -723,6 +723,28 @@ lily river drop table t1; deallocate prepare stmt; +create table t1 ( +a char(10) unicode not null, +index a (a) +) engine=myisam; +insert into t1 values (repeat(0x201f, 10)); +insert into t1 values (repeat(0x2020, 10)); +insert into t1 values (repeat(0x2021, 10)); +explain select hex(a) from t1 order by a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 20 NULL 3 Using index +select hex(a) from t1 order by a; +hex(a) +201F201F201F201F201F201F201F201F201F201F +2020202020202020202020202020202020202020 +2021202120212021202120212021202120212021 +alter table t1 drop index a; +select hex(a) from t1 order by a; +hex(a) +201F201F201F201F201F201F201F201F201F201F +2020202020202020202020202020202020202020 +2021202120212021202120212021202120212021 +drop table t1; CREATE TABLE t1 (id int, s char(5) CHARACTER SET ucs2 COLLATE ucs2_unicode_ci); INSERT INTO t1 VALUES (1, 'ZZZZZ'), (1, 'ZZZ'), (2, 'ZZZ'), (2, 'ZZZZZ'); SELECT id, MIN(s) FROM t1 GROUP BY id; diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 4ae19fc8f7c..4aa22944b9b 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -197,7 +197,7 @@ drop table t1; create table t1 (s1 char(10) character set utf8); insert into t1 values (0x41FF); Warnings: -Warning 1265 Data truncated for column 's1' at row 1 +Warning 1366 Incorrect string value: '\xFF' for column 's1' at row 1 select hex(s1) from t1; hex(s1) 41 @@ -205,7 +205,7 @@ drop table t1; create table t1 (s1 varchar(10) character set utf8); insert into t1 values (0x41FF); Warnings: -Warning 1265 Data truncated for column 's1' at row 1 +Warning 1366 Incorrect string value: '\xFF' for column 's1' at row 1 select hex(s1) from t1; hex(s1) 41 @@ -213,7 +213,7 @@ drop table t1; create table t1 (s1 text character set utf8); insert into t1 values (0x41FF); Warnings: -Warning 1265 Data truncated for column 's1' at row 1 +Warning 1366 Incorrect string value: '\xFF' for column 's1' at row 1 select hex(s1) from t1; hex(s1) 41 @@ -1536,6 +1536,32 @@ set @a:=null; execute my_stmt using @a; a b drop table if exists t1; +drop table if exists t1; +drop view if exists v1, v2; +set names utf8; +create table t1(col1 varchar(12) character set utf8 collate utf8_unicode_ci); +insert into t1 values('t1_val'); +create view v1 as select 'v1_val' as col1; +select coercibility(col1), collation(col1) from v1; +coercibility(col1) collation(col1) +4 utf8_general_ci +create view v2 as select col1 from v1 union select col1 from t1; +select coercibility(col1), collation(col1)from v2; +coercibility(col1) collation(col1) +2 utf8_unicode_ci +2 utf8_unicode_ci +drop view v1, v2; +create view v1 as select 'v1_val' collate utf8_swedish_ci as col1; +select coercibility(col1), collation(col1) from v1; +coercibility(col1) collation(col1) +0 utf8_swedish_ci +create view v2 as select col1 from v1 union select col1 from t1; +select coercibility(col1), collation(col1) from v2; +coercibility(col1) collation(col1) +0 utf8_swedish_ci +0 utf8_swedish_ci +drop view v1, v2; +drop table t1; CREATE TABLE t1 ( colA int(11) NOT NULL, colB varchar(255) character set utf8 NOT NULL, diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index 457155b4299..829cf25a896 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -381,10 +381,10 @@ t collation(t) FORMAT(MATCH t AGAINST ('Osnabruck'),6) aus Osnabrück utf8_general_ci 1.591140 alter table t1 modify t varchar(200) collate latin1_german2_ci not null; Warnings: -Warning 1265 Data truncated for column 't' at row 3 -Warning 1265 Data truncated for column 't' at row 4 -Warning 1265 Data truncated for column 't' at row 5 -Warning 1265 Data truncated for column 't' at row 6 +Warning 1366 Incorrect string value: '\xD0\xAD\xD1\x82\xD0\xBE...' for column 't' at row 3 +Warning 1366 Incorrect string value: '\xD0\x9E\xD1\x82\xD0\xBB...' for column 't' at row 4 +Warning 1366 Incorrect string value: '\xD0\x9D\xD0\xB5 \xD0...' for column 't' at row 5 +Warning 1366 Incorrect string value: '\xD0\xB8 \xD0\xB1\xD1...' for column 't' at row 6 SELECT t, collation(t) FROM t1 WHERE MATCH t AGAINST ('Osnabrück'); t collation(t) aus Osnabrück latin1_german2_ci diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 619c8dafeae..57b0a2aec25 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -664,3 +664,73 @@ GROUP_CONCAT(a) x 2 1,2 1 2,3 DROP TABLE t1; +set names utf8; +create table t1 +( +x text character set utf8 not null, +y integer not null +); +insert into t1 values (repeat('a', 1022), 0), (repeat(_utf8 0xc3b7, 4), 0); +set group_concat_max_len= 1022 + 10; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1032 1031 1027 aaaaaaa,÷÷÷÷ C3B7C3B7C3B7 +set group_concat_max_len= 1022 + 9; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1031 1031 1027 aaaaaaa,÷÷÷÷ C3B7C3B7C3B7 +set group_concat_max_len= 1022 + 8; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1030 1029 1026 aaaaaaaa,÷÷÷ C3B7C3B7C3B7 +set group_concat_max_len= 1022 + 7; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1029 1029 1026 aaaaaaaa,÷÷÷ C3B7C3B7C3B7 +set group_concat_max_len= 1022 + 6; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1028 1027 1025 aaaaaaaaa,÷÷ 612CC3B7C3B7 +set group_concat_max_len= 1022 + 5; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1027 1027 1025 aaaaaaaaa,÷÷ 612CC3B7C3B7 +set group_concat_max_len= 1022 + 4; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1026 1025 1024 aaaaaaaaaa,÷ 6161612CC3B7 +set group_concat_max_len= 1022 + 3; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1025 1025 1024 aaaaaaaaaa,÷ 6161612CC3B7 +set group_concat_max_len= 1022 + 2; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1024 1023 1023 aaaaaaaaaaa, 61616161612C +set group_concat_max_len= 1022 + 1; +select @x:=group_concat(x) from t1 group by y; +select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); +@@group_concat_max_len length(@x) char_length(@x) right(@x,12) right(HEX(@x),12) +1023 1023 1023 aaaaaaaaaaa, 61616161612C +drop table t1; +set group_concat_max_len=1024; +set names latin1; +create table t1 (f1 int unsigned, f2 varchar(255)); +insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); +select f2,group_concat(f1) from t1 group by f2; +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def test t1 t1 f2 f2 253 255 255 Y 0 0 8 +def group_concat(f1) 252 1024 1 Y 128 0 63 +f2 group_concat(f1) +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1 +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2 +drop table t1; diff --git a/mysql-test/r/lowercase_table.result b/mysql-test/r/lowercase_table.result index 7705961d08d..464f423fa92 100644 --- a/mysql-test/r/lowercase_table.result +++ b/mysql-test/r/lowercase_table.result @@ -84,3 +84,27 @@ create table t2 like T1; drop table t1, t2; show tables; Tables_in_test +set names utf8; +drop table if exists Ä°,Ä°Ä°; +create table Ä° (s1 int); +show create table Ä°; +Table Create Table +Ä° CREATE TABLE `i` ( + `s1` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +show tables; +Tables_in_test +i +drop table Ä°; +create table Ä°Ä° (s1 int); +show create table Ä°Ä°; +Table Create Table +Ä°Ä° CREATE TABLE `ii` ( + `s1` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +show tables; +Tables_in_test +ii +drop table Ä°Ä°; +set names latin1; +End of 5.0 tests diff --git a/mysql-test/r/rpl_deadlock_innodb.result b/mysql-test/r/rpl_deadlock_innodb.result index a384be6eb37..caf040c0997 100644 --- a/mysql-test/r/rpl_deadlock_innodb.result +++ b/mysql-test/r/rpl_deadlock_innodb.result @@ -6,7 +6,7 @@ drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; CREATE TABLE t1 (a INT NOT NULL, KEY(a)) ENGINE=innodb; CREATE TABLE t2 (a INT NOT NULL, KEY(a)) ENGINE=innodb; -CREATE TABLE t3 (a INT) ENGINE=innodb; +CREATE TABLE t3 (a INT UNIQUE) ENGINE=innodb; CREATE TABLE t4 (a INT) ENGINE=innodb; show variables like 'slave_transaction_retries'; Variable_name Value @@ -28,21 +28,22 @@ Variable_name Value slave_transaction_retries 2 stop slave; begin; -insert into t3 select * from t2 for update; +insert into t2 values (0); insert into t1 values(1); commit; begin; select * from t1 for update; a start slave; -insert into t2 values(22); +select * from t2 for update /* dl */; +a commit; select * from t1; a 1 -select * from t2; +select * from t2 /* must be 1 */; a -22 +0 show slave status; Slave_IO_State # Master_Host 127.0.0.1 @@ -78,12 +79,16 @@ Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master # stop slave; -change master to master_log_pos=536; +delete from t3; +change master to master_log_pos=544; begin; select * from t2 for update; a -22 +0 start slave; +select count(*) from t3 /* must be zero */; +count(*) +0 commit; select * from t1; a @@ -91,7 +96,8 @@ a 1 select * from t2; a -22 +0 +0 show slave status; Slave_IO_State # Master_Host 127.0.0.1 @@ -129,12 +135,17 @@ Seconds_Behind_Master # set @my_max_relay_log_size= @@global.max_relay_log_size; set global max_relay_log_size=0; stop slave; -change master to master_log_pos=536; +delete from t3; +change master to master_log_pos=544; begin; select * from t2 for update; a -22 +0 +0 start slave; +select count(*) from t3 /* must be zero */; +count(*) +0 commit; select * from t1; a @@ -143,7 +154,9 @@ a 1 select * from t2; a -22 +0 +0 +0 show slave status; Slave_IO_State # Master_Host 127.0.0.1 @@ -180,3 +193,4 @@ Master_SSL_Key Seconds_Behind_Master # drop table t1,t2,t3,t4; set global max_relay_log_size= @my_max_relay_log_size; +End of 5.1 tests diff --git a/mysql-test/r/rpl_do_grant.result b/mysql-test/r/rpl_do_grant.result index 8bf8e615448..50d181be0ca 100644 --- a/mysql-test/r/rpl_do_grant.result +++ b/mysql-test/r/rpl_do_grant.result @@ -23,6 +23,8 @@ password<>_binary'' delete from mysql.user where user=_binary'rpl_do_grant'; delete from mysql.db where user=_binary'rpl_do_grant'; flush privileges; +delete from mysql.user where user=_binary'rpl_do_grant'; +delete from mysql.db where user=_binary'rpl_do_grant'; flush privileges; show grants for rpl_do_grant@localhost; ERROR 42000: There is no such grant defined for user 'rpl_do_grant' on host 'localhost' diff --git a/mysql-test/r/rpl_extraCol_innodb.result b/mysql-test/r/rpl_extraCol_innodb.result new file mode 100644 index 00000000000..7f681aef4be --- /dev/null +++ b/mysql-test/r/rpl_extraCol_innodb.result @@ -0,0 +1,741 @@ +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; +**** Diff Table Def Start **** +*** On Slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t1 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(4) DEFAULT 'TEST') +ENGINE='InnoDB'; +*** Create t1 on Master *** +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t1 () VALUES(1,2,'TEXAS'),(2,1,'AUSTIN'),(3,4,'QA'); +SELECT * FROM t1 ORDER BY a; +a b c +1 2 TEXAS +2 1 AUSTIN +3 4 QA +*** Select from slave *** +SELECT * FROM t1 ORDER BY a; +a b c d e +1 2 TEXAS 2 TEST +2 1 AUSTIN 2 TEST +3 4 QA 2 TEST +*** Drop t1 *** +DROP TABLE t1; +*** Create t3 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t3 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='InnoDB'; +*** Create t3 on Master *** +CREATE TABLE t3 (a BLOB, b INT PRIMARY KEY, c CHAR(20) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 252, test.t3 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t3 *** +DROP TABLE t3; +*** Create t4 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t4 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='InnoDB'; +*** Create t4 on Master *** +CREATE TABLE t4 (a DECIMAL(8,2), b INT PRIMARY KEY, c CHAR(20) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t4 () VALUES(100.22,2,'Kyle, TEX'),(200.26,1,'JOE AUSTIN'), +(30000.22,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 246, test.t4 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t4 *** +DROP TABLE t4; +*** Create t5 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t5 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT, e DOUBLE, +f DECIMAL(8,2))ENGINE='InnoDB'; +*** Create t5 on Master *** +CREATE TABLE t5 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT, e BLOB, +f FLOAT) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t5 () VALUES(1,'Kyle',200.23,1,'b1b1',23.00098), +(2,'JOE',300.01,0,'b2b2',1.0000009); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 5 type mismatch - received type 4, test.t5 has type 246 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t5 *** +DROP TABLE t5; +*** Create t6 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t6 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT)ENGINE='InnoDB'; +*** Create t6 on Master *** +CREATE TABLE t6 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t6 () VALUES(1,'Kyle',200.23,1), +(2,'JOE',300.01,0); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 3 type mismatch - received type 16, test.t6 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=3; +*** Drop t6 *** +DROP TABLE t6; +DROP TABLE t6; +START SLAVE; +**** Diff Table Def End **** +**** Extra Colums Start **** +*** Create t7 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t7 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e CHAR(20) DEFAULT 'Extra Column Testing') +ENGINE='InnoDB'; +*** Create t7 on Master *** +CREATE TABLE t7 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t7 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t7 ORDER BY a; +a b c +1 b1b1 Kyle +2 b1b1 JOE +3 b1b1 QA +*** Select from slave *** +SELECT * FROM t7 ORDER BY a; +a b c d e +1 b1b1 Kyle 0000-00-00 00:00:00 Extra Column Testing +2 b1b1 JOE 0000-00-00 00:00:00 Extra Column Testing +3 b1b1 QA 0000-00-00 00:00:00 Extra Column Testing +*** Drop t7 *** +DROP TABLE t7; +*** Create t8 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t8 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e INT)ENGINE='InnoDB'; +*** Create t8 on Master *** +CREATE TABLE t8 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t8 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +*** Drop t8 *** +DROP TABLE t8; +*** Create t10 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t10 (a INT KEY, b BLOB, f DOUBLE DEFAULT '233', +c CHAR(5), e INT DEFAULT '1')ENGINE='InnoDB'; +*** Create t10 on Master *** +CREATE TABLE t10 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t10 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 254, test.t10 has type 5 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t10 *** +DROP TABLE t10; +*** Create t11 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t11 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='InnoDB'; +*** Create t11 on Master *** +CREATE TABLE t11 (a INT PRIMARY KEY, b BLOB, c VARCHAR(254) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t11 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 15, test.t11 has type 252 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t11 *** +DROP TABLE t11; +*** Create t12 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t12 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='InnoDB'; +*** Create t12 on Master *** +CREATE TABLE t12 (a INT PRIMARY KEY, b BLOB, c BLOB +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t12 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t12 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave *** +SELECT * FROM t12 ORDER BY a; +a b f c e +1 b1b1b1b1b1b1b1b1 Kyle test 1 +2 b1b1b1b1b1b1b1b1 JOE test 1 +3 b1b1b1b1b1b1b1b1 QA test 1 +*** Drop t12 *** +DROP TABLE t12; +**** Extra Colums End **** +*** BUG 22177 Start *** +*** Create t13 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t13 (a INT KEY, b BLOB, c CHAR(5), +d INT DEFAULT '1', +e TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='InnoDB'; +*** Create t13 on Master *** +CREATE TABLE t13 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t13 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t13 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t13 ORDER BY a; +a b c d e +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Drop t13 *** +DROP TABLE t13; +*** 22117 END *** +*** Alter Master Table Testing Start *** +*** Create t14 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14 (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='InnoDB'; +*** Create t14 on Master *** +CREATE TABLE t14 (c1 INT PRIMARY KEY, c4 BLOB, c5 CHAR(5) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +ALTER TABLE t14 ADD COLUMN c2 DECIMAL(8,2) AFTER c1; +ALTER TABLE t14 ADD COLUMN c3 TEXT AFTER c2; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** connect to master and drop columns *** +ALTER TABLE t14 DROP COLUMN c2; +ALTER TABLE t14 DROP COLUMN c4; +*** Select from Master *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 +1 Replication Testing Extra Col Kyle +2 This Test Should work JOE +3 If is does not, I will open a bug QA +*** Select from Slave *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 c6 c7 +1 Replication Testing Extra Col Kyle 1 CURRENT_TIMESTAMP +2 This Test Should work JOE 1 CURRENT_TIMESTAMP +3 If is does not, I will open a bug QA 1 CURRENT_TIMESTAMP +*** Drop t14 *** +DROP TABLE t14; +*** Create t15 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t15 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='InnoDB'; +*** Create t15 on Master *** +CREATE TABLE t15 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5)) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t15 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add column on master that is a Extra on Slave *** +ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5; +******************************************** +*** Expect slave to fail with Error 1060 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1060 +Last_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Try to insert in master **** +INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL +5 2.00 Replication Testing b1b1b1b1b1b1b1b1 Buda 2 +*** Try to select from slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** DROP TABLE t15 *** +DROP TABLE t15; +*** Create t16 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t16 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='InnoDB'; +*** Create t16 on Master *** +CREATE TABLE t16 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5))ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t16 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add Partition on master *** +ALTER TABLE t16 PARTITION BY KEY(c1) PARTITIONS 4; +INSERT INTO t16 () VALUES(4,1.00,'Replication Rocks',@b1,'Omer'); +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** Show table on Slave **** +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + `c6` int(11) DEFAULT '1', + `c7` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** DROP TABLE t16 *** +DROP TABLE t16; +*** Alter Master End *** +*** Create t17 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t17 (a SMALLINT, b INT PRIMARY KEY, c CHAR(5), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='InnoDB'; +*** Create t17 on Master *** +CREATE TABLE t17 (a BIGINT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='InnoDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t17 () VALUES(9223372036854775807,2,'Kyle, TEX'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 8, test.t17 has type 2 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +** DROP table t17 *** +DROP TABLE t17; diff --git a/mysql-test/r/rpl_extraCol_myisam.result b/mysql-test/r/rpl_extraCol_myisam.result new file mode 100644 index 00000000000..3c0d1fae4a8 --- /dev/null +++ b/mysql-test/r/rpl_extraCol_myisam.result @@ -0,0 +1,741 @@ +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; +**** Diff Table Def Start **** +*** On Slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t1 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(4) DEFAULT 'TEST') +ENGINE='MyISAM'; +*** Create t1 on Master *** +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t1 () VALUES(1,2,'TEXAS'),(2,1,'AUSTIN'),(3,4,'QA'); +SELECT * FROM t1 ORDER BY a; +a b c +1 2 TEXAS +2 1 AUSTIN +3 4 QA +*** Select from slave *** +SELECT * FROM t1 ORDER BY a; +a b c d e +1 2 TEXAS 2 TEST +2 1 AUSTIN 2 TEST +3 4 QA 2 TEST +*** Drop t1 *** +DROP TABLE t1; +*** Create t3 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t3 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='MyISAM'; +*** Create t3 on Master *** +CREATE TABLE t3 (a BLOB, b INT PRIMARY KEY, c CHAR(20) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 252, test.t3 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t3 *** +DROP TABLE t3; +*** Create t4 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t4 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='MyISAM'; +*** Create t4 on Master *** +CREATE TABLE t4 (a DECIMAL(8,2), b INT PRIMARY KEY, c CHAR(20) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t4 () VALUES(100.22,2,'Kyle, TEX'),(200.26,1,'JOE AUSTIN'), +(30000.22,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 246, test.t4 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t4 *** +DROP TABLE t4; +*** Create t5 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t5 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT, e DOUBLE, +f DECIMAL(8,2))ENGINE='MyISAM'; +*** Create t5 on Master *** +CREATE TABLE t5 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT, e BLOB, +f FLOAT) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t5 () VALUES(1,'Kyle',200.23,1,'b1b1',23.00098), +(2,'JOE',300.01,0,'b2b2',1.0000009); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 5 type mismatch - received type 4, test.t5 has type 246 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t5 *** +DROP TABLE t5; +*** Create t6 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t6 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT)ENGINE='MyISAM'; +*** Create t6 on Master *** +CREATE TABLE t6 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t6 () VALUES(1,'Kyle',200.23,1), +(2,'JOE',300.01,0); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 3 type mismatch - received type 16, test.t6 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=3; +*** Drop t6 *** +DROP TABLE t6; +DROP TABLE t6; +START SLAVE; +**** Diff Table Def End **** +**** Extra Colums Start **** +*** Create t7 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t7 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e CHAR(20) DEFAULT 'Extra Column Testing') +ENGINE='MyISAM'; +*** Create t7 on Master *** +CREATE TABLE t7 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t7 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t7 ORDER BY a; +a b c +1 b1b1 Kyle +2 b1b1 JOE +3 b1b1 QA +*** Select from slave *** +SELECT * FROM t7 ORDER BY a; +a b c d e +1 b1b1 Kyle 0000-00-00 00:00:00 Extra Column Testing +2 b1b1 JOE 0000-00-00 00:00:00 Extra Column Testing +3 b1b1 QA 0000-00-00 00:00:00 Extra Column Testing +*** Drop t7 *** +DROP TABLE t7; +*** Create t8 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t8 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e INT)ENGINE='MyISAM'; +*** Create t8 on Master *** +CREATE TABLE t8 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t8 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +*** Drop t8 *** +DROP TABLE t8; +*** Create t10 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t10 (a INT KEY, b BLOB, f DOUBLE DEFAULT '233', +c CHAR(5), e INT DEFAULT '1')ENGINE='MyISAM'; +*** Create t10 on Master *** +CREATE TABLE t10 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t10 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 254, test.t10 has type 5 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t10 *** +DROP TABLE t10; +*** Create t11 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t11 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='MyISAM'; +*** Create t11 on Master *** +CREATE TABLE t11 (a INT PRIMARY KEY, b BLOB, c VARCHAR(254) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t11 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 15, test.t11 has type 252 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t11 *** +DROP TABLE t11; +*** Create t12 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t12 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='MyISAM'; +*** Create t12 on Master *** +CREATE TABLE t12 (a INT PRIMARY KEY, b BLOB, c BLOB +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t12 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t12 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave *** +SELECT * FROM t12 ORDER BY a; +a b f c e +1 b1b1b1b1b1b1b1b1 Kyle test 1 +2 b1b1b1b1b1b1b1b1 JOE test 1 +3 b1b1b1b1b1b1b1b1 QA test 1 +*** Drop t12 *** +DROP TABLE t12; +**** Extra Colums End **** +*** BUG 22177 Start *** +*** Create t13 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t13 (a INT KEY, b BLOB, c CHAR(5), +d INT DEFAULT '1', +e TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='MyISAM'; +*** Create t13 on Master *** +CREATE TABLE t13 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t13 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t13 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t13 ORDER BY a; +a b c d e +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Drop t13 *** +DROP TABLE t13; +*** 22117 END *** +*** Alter Master Table Testing Start *** +*** Create t14 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14 (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='MyISAM'; +*** Create t14 on Master *** +CREATE TABLE t14 (c1 INT PRIMARY KEY, c4 BLOB, c5 CHAR(5) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +ALTER TABLE t14 ADD COLUMN c2 DECIMAL(8,2) AFTER c1; +ALTER TABLE t14 ADD COLUMN c3 TEXT AFTER c2; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** connect to master and drop columns *** +ALTER TABLE t14 DROP COLUMN c2; +ALTER TABLE t14 DROP COLUMN c4; +*** Select from Master *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 +1 Replication Testing Extra Col Kyle +2 This Test Should work JOE +3 If is does not, I will open a bug QA +*** Select from Slave *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 c6 c7 +1 Replication Testing Extra Col Kyle 1 CURRENT_TIMESTAMP +2 This Test Should work JOE 1 CURRENT_TIMESTAMP +3 If is does not, I will open a bug QA 1 CURRENT_TIMESTAMP +*** Drop t14 *** +DROP TABLE t14; +*** Create t15 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t15 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='MyISAM'; +*** Create t15 on Master *** +CREATE TABLE t15 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5)) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t15 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add column on master that is a Extra on Slave *** +ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5; +******************************************** +*** Expect slave to fail with Error 1060 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1060 +Last_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Try to insert in master **** +INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL +5 2.00 Replication Testing b1b1b1b1b1b1b1b1 Buda 2 +*** Try to select from slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** DROP TABLE t15 *** +DROP TABLE t15; +*** Create t16 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t16 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='MyISAM'; +*** Create t16 on Master *** +CREATE TABLE t16 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5))ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t16 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add Partition on master *** +ALTER TABLE t16 PARTITION BY KEY(c1) PARTITIONS 4; +INSERT INTO t16 () VALUES(4,1.00,'Replication Rocks',@b1,'Omer'); +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + PRIMARY KEY (`c1`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** Show table on Slave **** +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + `c6` int(11) DEFAULT '1', + `c7` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`c1`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** DROP TABLE t16 *** +DROP TABLE t16; +*** Alter Master End *** +*** Create t17 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t17 (a SMALLINT, b INT PRIMARY KEY, c CHAR(5), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='MyISAM'; +*** Create t17 on Master *** +CREATE TABLE t17 (a BIGINT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='MyISAM'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t17 () VALUES(9223372036854775807,2,'Kyle, TEX'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 8, test.t17 has type 2 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +** DROP table t17 *** +DROP TABLE t17; diff --git a/mysql-test/r/rpl_ignore_table.result b/mysql-test/r/rpl_ignore_table.result index 356a9dcb2f8..136cf5cc5eb 100644 --- a/mysql-test/r/rpl_ignore_table.result +++ b/mysql-test/r/rpl_ignore_table.result @@ -14,3 +14,19 @@ SELECT * FROM t4; a DROP TABLE t1; DROP TABLE t4; +DROP TABLE IF EXISTS t5; +CREATE TABLE t5 ( +word varchar(50) collate utf8_unicode_ci NOT NULL default '' +) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +SET @@session.character_set_client=33,@@session.collation_connection=192; +CREATE TEMPORARY TABLE tmptbl504451f4258$1 (id INT NOT NULL) ENGINE=MEMORY; +INSERT INTO t5 (word) VALUES ('TEST’'); +SELECT HEX(word) FROM t5; +HEX(word) +54455354E28099 +SELECT HEX(word) FROM t5; +HEX(word) +54455354E28099 +SELECT * FROM tmptbl504451f4258$1; +ERROR 42S02: Table 'test.tmptbl504451f4258$1' doesn't exist +DROP TABLE t5; diff --git a/mysql-test/r/rpl_ndb_dd_advance.result b/mysql-test/r/rpl_ndb_dd_advance.result index bbc67a04027..b29d1201a85 100644 --- a/mysql-test/r/rpl_ndb_dd_advance.result +++ b/mysql-test/r/rpl_ndb_dd_advance.result @@ -326,12 +326,12 @@ COUNT(*) **** Must make sure slave is clean ***** STOP SLAVE; RESET SLAVE; -DROP PROCEDURE tpcb.load; -DROP PROCEDURE tpcb.trans; -DROP TABLE tpcb.account; -DROP TABLE tpcb.teller; -DROP TABLE tpcb.branch; -DROP TABLE tpcb.history; +DROP PROCEDURE IF EXISTS tpcb.load; +DROP PROCEDURE IF EXISTS tpcb.trans; +DROP TABLE IF EXISTS tpcb.account; +DROP TABLE IF EXISTS tpcb.teller; +DROP TABLE IF EXISTS tpcb.branch; +DROP TABLE IF EXISTS tpcb.history; DROP DATABASE tpcb; ALTER TABLESPACE ts1 DROP DATAFILE 'datafile.dat' @@ -392,8 +392,8 @@ COUNT(*) 4050 *** DUMP MASTER & SLAVE FOR COMPARE ******** *************** TEST 2 CLEANUP SECTION ******************** -DROP PROCEDURE tpcb.load; -DROP PROCEDURE tpcb.trans; +DROP PROCEDURE IF EXISTS tpcb.load; +DROP PROCEDURE IF EXISTS tpcb.trans; DROP TABLE tpcb.account; DROP TABLE tpcb.teller; DROP TABLE tpcb.branch; diff --git a/mysql-test/r/rpl_ndb_ddl.result b/mysql-test/r/rpl_ndb_ddl.result index de35175bf18..f5d8073be94 100644 --- a/mysql-test/r/rpl_ndb_ddl.result +++ b/mysql-test/r/rpl_ndb_ddl.result @@ -359,8 +359,6 @@ MAX(f1) -------- switch to master ------- ROLLBACK; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back SELECT MAX(f1) FROM t1; MAX(f1) 5 @@ -370,9 +368,9 @@ TEST-INFO: MASTER: The INSERT is not committed (Succeeded) -------- switch to slave -------- SELECT MAX(f1) FROM t1; MAX(f1) -6 +5 -TEST-INFO: SLAVE: The INSERT is committed (Succeeded) +TEST-INFO: SLAVE: The INSERT is not committed (Succeeded) -------- switch to master ------- flush logs; @@ -401,7 +399,7 @@ MAX(f1) -------- switch to slave -------- SELECT MAX(f1) FROM t1; MAX(f1) -6 +5 -------- switch to master ------- RENAME TABLE mysqltest1.t3 to mysqltest1.t20; @@ -506,7 +504,7 @@ f2 bigint(20) YES NULL -------- switch to master ------- -######## CREATE TABLE mysqltest1.t21 (f1 BIGINT) ENGINE= "InnoDB" ######## +######## CREATE TABLE mysqltest1.t21 (f1 BIGINT) ENGINE= "NDB" ######## -------- switch to master ------- INSERT INTO t1 SET f1= 7 + 1; @@ -520,7 +518,7 @@ MAX(f1) 7 -------- switch to master ------- -CREATE TABLE mysqltest1.t21 (f1 BIGINT) ENGINE= "InnoDB"; +CREATE TABLE mysqltest1.t21 (f1 BIGINT) ENGINE= "NDB"; SELECT MAX(f1) FROM t1; MAX(f1) 8 @@ -579,8 +577,6 @@ MAX(f1) -------- switch to master ------- ROLLBACK; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back SELECT MAX(f1) FROM t1; MAX(f1) 8 @@ -590,9 +586,9 @@ TEST-INFO: MASTER: The INSERT is not committed (Succeeded) -------- switch to slave -------- SELECT MAX(f1) FROM t1; MAX(f1) -9 +8 -TEST-INFO: SLAVE: The INSERT is committed (Succeeded) +TEST-INFO: SLAVE: The INSERT is not committed (Succeeded) -------- switch to master ------- flush logs; @@ -613,7 +609,7 @@ MAX(f1) -------- switch to slave -------- SELECT MAX(f1) FROM t1; MAX(f1) -9 +8 -------- switch to master ------- TRUNCATE TABLE mysqltest1.t7; @@ -650,11 +646,9 @@ flush logs; -------- switch to master ------- SELECT * FROM mysqltest1.t7; f1 - -------- switch to slave -------- SELECT * FROM mysqltest1.t7; f1 - -------- switch to master ------- ######## LOCK TABLES mysqltest1.t1 WRITE, mysqltest1.t8 READ ######## @@ -957,7 +951,7 @@ t5 1 my_idx5 1 f1 A 0 NULL NULL YES BTREE -------- switch to slave -------- SHOW INDEX FROM mysqltest1.t5; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment -t5 1 my_idx5 1 f1 A NULL NULL NULL YES BTREE +t5 1 my_idx5 1 f1 A 0 NULL NULL YES BTREE -------- switch to master ------- @@ -1691,3 +1685,4 @@ user DROP DATABASE IF EXISTS mysqltest1; DROP DATABASE IF EXISTS mysqltest2; DROP DATABASE IF EXISTS mysqltest3; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction diff --git a/mysql-test/r/rpl_ndb_extraCol.result b/mysql-test/r/rpl_ndb_extraCol.result new file mode 100644 index 00000000000..e51fae29c54 --- /dev/null +++ b/mysql-test/r/rpl_ndb_extraCol.result @@ -0,0 +1,742 @@ +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; +**** Diff Table Def Start **** +*** On Slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t1 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(4) DEFAULT 'TEST') +ENGINE='NDB'; +*** Create t1 on Master *** +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t1 () VALUES(1,2,'TEXAS'),(2,1,'AUSTIN'),(3,4,'QA'); +SELECT * FROM t1 ORDER BY a; +a b c +1 2 TEXAS +2 1 AUSTIN +3 4 QA +*** Select from slave *** +SELECT * FROM t1 ORDER BY a; +a b c d e +1 2 TEXAS 2 TEST +2 1 AUSTIN 2 TEST +3 4 QA 2 TEST +*** Drop t1 *** +DROP TABLE t1; +*** Create t3 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t3 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='NDB'; +*** Create t3 on Master *** +CREATE TABLE t3 (a BLOB, b INT PRIMARY KEY, c CHAR(20) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t3 () VALUES(@b1,2,'Kyle, TEX'),(@b1,1,'JOE AUSTIN'),(@b1,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 252, test.t3 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t3 *** +DROP TABLE t3; +*** Create t4 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t4 (a INT, b INT PRIMARY KEY, c CHAR(20), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='NDB'; +*** Create t4 on Master *** +CREATE TABLE t4 (a DECIMAL(8,2), b INT PRIMARY KEY, c CHAR(20) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t4 () VALUES(100.22,2,'Kyle, TEX'),(200.26,1,'JOE AUSTIN'), +(30000.22,4,'QA TESTING'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 246, test.t4 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t4 *** +DROP TABLE t4; +*** Create t5 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t5 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT, e DOUBLE, +f DECIMAL(8,2))ENGINE='NDB'; +*** Create t5 on Master *** +CREATE TABLE t5 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT, e BLOB, +f FLOAT) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t5 () VALUES(1,'Kyle',200.23,1,'b1b1',23.00098), +(2,'JOE',300.01,0,'b2b2',1.0000009); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 5 type mismatch - received type 4, test.t5 has type 246 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t5 *** +DROP TABLE t5; +*** Create t6 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t6 (a INT PRIMARY KEY, b CHAR(5), +c FLOAT, d INT)ENGINE='NDB'; +*** Create t6 on Master *** +CREATE TABLE t6 (a INT PRIMARY KEY, b VARCHAR(6), +c DECIMAL(8,2), d BIT +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t6 () VALUES(1,'Kyle',200.23,1), +(2,'JOE',300.01,0); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 3 type mismatch - received type 16, test.t6 has type 3 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=3; +*** Drop t6 *** +DROP TABLE t6; +DROP TABLE t6; +START SLAVE; +**** Diff Table Def End **** +**** Extra Colums Start **** +*** Create t7 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t7 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e CHAR(20) DEFAULT 'Extra Column Testing') +ENGINE='NDB'; +*** Create t7 on Master *** +CREATE TABLE t7 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t7 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t7 ORDER BY a; +a b c +1 b1b1 Kyle +2 b1b1 JOE +3 b1b1 QA +*** Select from slave *** +SELECT * FROM t7 ORDER BY a; +a b c d e +1 b1b1 Kyle 0000-00-00 00:00:00 Extra Column Testing +2 b1b1 JOE 0000-00-00 00:00:00 Extra Column Testing +3 b1b1 QA 0000-00-00 00:00:00 Extra Column Testing +*** Drop t7 *** +DROP TABLE t7; +*** Create t8 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t8 (a INT KEY, b BLOB, c CHAR(5), +d TIMESTAMP NULL DEFAULT '0000-00-00 00:00:00', +e INT)ENGINE='NDB'; +*** Create t8 on Master *** +CREATE TABLE t8 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t8 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +*** Drop t8 *** +DROP TABLE t8; +*** Create t10 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t10 (a INT KEY, b BLOB, f DOUBLE DEFAULT '233', +c CHAR(5), e INT DEFAULT '1')ENGINE='NDB'; +*** Create t10 on Master *** +CREATE TABLE t10 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t10 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 254, test.t10 has type 5 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t10 *** +DROP TABLE t10; +*** Create t11 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t11 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='NDB'; +*** Create t11 on Master *** +CREATE TABLE t11 (a INT PRIMARY KEY, b BLOB, c VARCHAR(254) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t11 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 2 type mismatch - received type 15, test.t11 has type 252 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Drop t11 *** +DROP TABLE t11; +*** Create t12 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t12 (a INT KEY, b BLOB, f TEXT, +c CHAR(5) DEFAULT 'test', e INT DEFAULT '1')ENGINE='NDB'; +*** Create t12 on Master *** +CREATE TABLE t12 (a INT PRIMARY KEY, b BLOB, c BLOB +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t12 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t12 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave *** +SELECT * FROM t12 ORDER BY a; +a b f c e +1 b1b1b1b1b1b1b1b1 Kyle test 1 +2 b1b1b1b1b1b1b1b1 JOE test 1 +3 b1b1b1b1b1b1b1b1 QA test 1 +*** Drop t12 *** +DROP TABLE t12; +**** Extra Colums End **** +*** BUG 22177 Start *** +*** Create t13 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t13 (a INT KEY, b BLOB, c CHAR(5), +d INT DEFAULT '1', +e TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='NDB'; +*** Create t13 on Master *** +CREATE TABLE t13 (a INT PRIMARY KEY, b BLOB, c CHAR(5) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t13 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +SELECT * FROM t13 ORDER BY a; +a b c +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t13 ORDER BY a; +a b c d e +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Drop t13 *** +DROP TABLE t13; +*** 22117 END *** +*** Alter Master Table Testing Start *** +*** Create t14 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14 (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='NDB'; +*** Create t14 on Master *** +CREATE TABLE t14 (c1 INT PRIMARY KEY, c4 BLOB, c5 CHAR(5) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +ALTER TABLE t14 ADD COLUMN c2 DECIMAL(8,2) AFTER c1; +ALTER TABLE t14 ADD COLUMN c3 TEXT AFTER c2; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** connect to master and drop columns *** +ALTER TABLE t14 DROP COLUMN c2; +ALTER TABLE t14 DROP COLUMN c4; +*** Select from Master *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 +1 Replication Testing Extra Col Kyle +2 This Test Should work JOE +3 If is does not, I will open a bug QA +*** Select from Slave *** +SELECT * FROM t14 ORDER BY c1; +c1 c3 c5 c6 c7 +1 Replication Testing Extra Col Kyle 1 CURRENT_TIMESTAMP +2 This Test Should work JOE 1 CURRENT_TIMESTAMP +3 If is does not, I will open a bug QA 1 CURRENT_TIMESTAMP +*** Drop t14 *** +DROP TABLE t14; +*** Create t15 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t15 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='NDB'; +*** Create t15 on Master *** +CREATE TABLE t15 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5)) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t15 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add column on master that is a Extra on Slave *** +ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5; +******************************************** +*** Expect slave to fail with Error 1060 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1060 +Last_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +*** Try to insert in master **** +INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL +5 2.00 Replication Testing b1b1b1b1b1b1b1b1 Buda 2 +*** Try to select from slave **** +SELECT * FROM t15 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +5 2.00 Replication Testing b1b1b1b1b1b1b1b1 Buda 2 CURRENT_TIMESTAMP +*** DROP TABLE t15 *** +DROP TABLE t15; +*** Create t16 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t16 (c1 INT KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='NDB'; +*** Create t16 on Master *** +CREATE TABLE t16 (c1 INT PRIMARY KEY, c2 DECIMAL(8,2), c3 TEXT, +c4 BLOB, c5 CHAR(5))ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t16 () VALUES(1,1.00,'Replication Testing Extra Col',@b1,'Kyle'), +(2,2.00,'This Test Should work',@b1,'JOE'), +(3,3.00,'If is does not, I will open a bug',@b1,'QA'); +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t16 ORDER BY c1; +c1 c2 c3 c4 c5 c6 c7 +1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +*** Add Partition on master *** +ALTER TABLE t16 PARTITION BY KEY(c1) PARTITIONS 4; +INSERT INTO t16 () VALUES(4,1.00,'Replication Rocks',@b1,'Omer'); +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + PRIMARY KEY (`c1`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** Show table on Slave **** +SHOW CREATE TABLE t16; +Table Create Table +t16 CREATE TABLE `t16` ( + `c1` int(11) NOT NULL, + `c2` decimal(8,2) DEFAULT NULL, + `c3` text, + `c4` blob, + `c5` char(5) DEFAULT NULL, + `c6` int(11) DEFAULT '1', + `c7` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`c1`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 /*!50100 PARTITION BY KEY (c1) PARTITIONS 4 */ +*** DROP TABLE t16 *** +DROP TABLE t16; +*** Alter Master End *** +*** Create t17 on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t17 (a SMALLINT, b INT PRIMARY KEY, c CHAR(5), +d FLOAT DEFAULT '2.00', +e CHAR(5) DEFAULT 'TEST2') +ENGINE='NDB'; +*** Create t17 on Master *** +CREATE TABLE t17 (a BIGINT PRIMARY KEY, b INT, c CHAR(10) +) ENGINE='NDB'; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +INSERT INTO t17 () VALUES(9223372036854775807,2,'Kyle, TEX'); +******************************************** +*** Expect slave to fail with Error 1522 *** +******************************************** +SHOW SLAVE STATUS; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_PORT +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos # +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running No +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 1522 +Last_Error Column 0 type mismatch - received type 8, test.t17 has type 2 +Skip_Counter 0 +Exec_Master_Log_Pos # +Relay_Log_Space # +Until_Condition None +Until_Log_File +Until_Log_Pos 0 +Master_SSL_Allowed No +Master_SSL_CA_File +Master_SSL_CA_Path +Master_SSL_Cert +Master_SSL_Cipher +Master_SSL_Key +Seconds_Behind_Master # +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +START SLAVE; +** DROP table t17 *** +DROP TABLE t17; diff --git a/mysql-test/r/rpl_packet.result b/mysql-test/r/rpl_packet.result new file mode 100644 index 00000000000..a5c9b43cabb --- /dev/null +++ b/mysql-test/r/rpl_packet.result @@ -0,0 +1,17 @@ +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; +drop database if exists DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________; +create database DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________; +select @@net_buffer_length, @@max_allowed_packet; +@@net_buffer_length @@max_allowed_packet +1024 1024 +create table `t1` (`f1` LONGTEXT) ENGINE=MyISAM; +INSERT INTO `t1`(`f1`) VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1023'); +select count(*) from `DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________`.`t1` /* must be 1 */; +count(*) +1 +drop database DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________; diff --git a/mysql-test/r/strict_autoinc_1myisam.result b/mysql-test/r/strict_autoinc_1myisam.result index dcda74a19cd..afcccb1c40f 100644 --- a/mysql-test/r/strict_autoinc_1myisam.result +++ b/mysql-test/r/strict_autoinc_1myisam.result @@ -1,3 +1,4 @@ +drop table if exists t1; set @org_mode=@@sql_mode; create table t1 ( diff --git a/mysql-test/r/strict_autoinc_2innodb.result b/mysql-test/r/strict_autoinc_2innodb.result index 9e90ab886f3..e534286e2a2 100644 --- a/mysql-test/r/strict_autoinc_2innodb.result +++ b/mysql-test/r/strict_autoinc_2innodb.result @@ -1,3 +1,4 @@ +drop table if exists t1; set @org_mode=@@sql_mode; create table t1 ( diff --git a/mysql-test/r/strict_autoinc_3heap.result b/mysql-test/r/strict_autoinc_3heap.result index d22054eb59d..0a31da04460 100644 --- a/mysql-test/r/strict_autoinc_3heap.result +++ b/mysql-test/r/strict_autoinc_3heap.result @@ -1,3 +1,4 @@ +drop table if exists t1; set @org_mode=@@sql_mode; create table t1 ( diff --git a/mysql-test/r/strict_autoinc_4bdb.result b/mysql-test/r/strict_autoinc_4bdb.result index 48ebe196baf..2e8980e435b 100644 --- a/mysql-test/r/strict_autoinc_4bdb.result +++ b/mysql-test/r/strict_autoinc_4bdb.result @@ -1,3 +1,4 @@ +drop table if exists t1; set @org_mode=@@sql_mode; create table t1 ( diff --git a/mysql-test/r/strict_autoinc_5ndb.result b/mysql-test/r/strict_autoinc_5ndb.result index debc7242e15..ea6e5ffc741 100644 --- a/mysql-test/r/strict_autoinc_5ndb.result +++ b/mysql-test/r/strict_autoinc_5ndb.result @@ -1,3 +1,4 @@ +drop table if exists t1; set @org_mode=@@sql_mode; create table t1 ( diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 501d15efa82..4bc122cc97b 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3015,3 +3015,22 @@ i j DROP VIEW v1, v2; DROP TABLE t1; End of 5.0 tests. +DROP DATABASE IF EXISTS `d-1`; +CREATE DATABASE `d-1`; +USE `d-1`; +CREATE TABLE `t-1` (c1 INT); +CREATE VIEW `v-1` AS SELECT c1 FROM `t-1`; +SHOW TABLES; +Tables_in_d-1 +t-1 +v-1 +RENAME TABLE `t-1` TO `t-2`; +RENAME TABLE `v-1` TO `v-2`; +SHOW TABLES; +Tables_in_d-1 +t-2 +v-2 +DROP TABLE `t-2`; +DROP VIEW `v-2`; +DROP DATABASE `d-1`; +USE test; diff --git a/mysql-test/r/xml.result b/mysql-test/r/xml.result index efe7d14095d..bb7f84d0287 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -736,3 +736,76 @@ test select extractValue('<x.-_:>test</x.-_:>','//*'); extractValue('<x.-_:>test</x.-_:>','//*') test +set @xml= "<entry><id>pt10</id><pt>10</pt></entry><entry><id>pt50</id><pt>50</pt></entry>"; +select ExtractValue(@xml, "/entry[(pt=10)]/id"); +ExtractValue(@xml, "/entry[(pt=10)]/id") +pt10 +select ExtractValue(@xml, "/entry[(pt!=10)]/id"); +ExtractValue(@xml, "/entry[(pt!=10)]/id") +pt50 +select ExtractValue(@xml, "/entry[(pt<10)]/id"); +ExtractValue(@xml, "/entry[(pt<10)]/id") + +select ExtractValue(@xml, "/entry[(pt<=10)]/id"); +ExtractValue(@xml, "/entry[(pt<=10)]/id") +pt10 +select ExtractValue(@xml, "/entry[(pt>10)]/id"); +ExtractValue(@xml, "/entry[(pt>10)]/id") +pt50 +select ExtractValue(@xml, "/entry[(pt>=10)]/id"); +ExtractValue(@xml, "/entry[(pt>=10)]/id") +pt10 pt50 +select ExtractValue(@xml, "/entry[(pt=50)]/id"); +ExtractValue(@xml, "/entry[(pt=50)]/id") +pt50 +select ExtractValue(@xml, "/entry[(pt!=50)]/id"); +ExtractValue(@xml, "/entry[(pt!=50)]/id") +pt10 +select ExtractValue(@xml, "/entry[(pt<50)]/id"); +ExtractValue(@xml, "/entry[(pt<50)]/id") +pt10 +select ExtractValue(@xml, "/entry[(pt<=50)]/id"); +ExtractValue(@xml, "/entry[(pt<=50)]/id") +pt10 pt50 +select ExtractValue(@xml, "/entry[(pt>50)]/id"); +ExtractValue(@xml, "/entry[(pt>50)]/id") + +select ExtractValue(@xml, "/entry[(pt>=50)]/id"); +ExtractValue(@xml, "/entry[(pt>=50)]/id") +pt50 +select ExtractValue(@xml, "/entry[(10=pt)]/id"); +ExtractValue(@xml, "/entry[(10=pt)]/id") +pt10 +select ExtractValue(@xml, "/entry[(10!=pt)]/id"); +ExtractValue(@xml, "/entry[(10!=pt)]/id") +pt50 +select ExtractValue(@xml, "/entry[(10>pt)]/id"); +ExtractValue(@xml, "/entry[(10>pt)]/id") + +select ExtractValue(@xml, "/entry[(10>=pt)]/id"); +ExtractValue(@xml, "/entry[(10>=pt)]/id") +pt10 +select ExtractValue(@xml, "/entry[(10<pt)]/id"); +ExtractValue(@xml, "/entry[(10<pt)]/id") +pt50 +select ExtractValue(@xml, "/entry[(10<=pt)]/id"); +ExtractValue(@xml, "/entry[(10<=pt)]/id") +pt10 pt50 +select ExtractValue(@xml, "/entry[(50=pt)]/id"); +ExtractValue(@xml, "/entry[(50=pt)]/id") +pt50 +select ExtractValue(@xml, "/entry[(50!=pt)]/id"); +ExtractValue(@xml, "/entry[(50!=pt)]/id") +pt10 +select ExtractValue(@xml, "/entry[(50>pt)]/id"); +ExtractValue(@xml, "/entry[(50>pt)]/id") +pt10 +select ExtractValue(@xml, "/entry[(50>=pt)]/id"); +ExtractValue(@xml, "/entry[(50>=pt)]/id") +pt10 pt50 +select ExtractValue(@xml, "/entry[(50<pt)]/id"); +ExtractValue(@xml, "/entry[(50<pt)]/id") + +select ExtractValue(@xml, "/entry[(50<=pt)]/id"); +ExtractValue(@xml, "/entry[(50<=pt)]/id") +pt50 diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index 6c814368c88..dedba4caf0f 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -455,6 +455,23 @@ drop table t1; deallocate prepare stmt; # +# Bug#22052 Trailing spaces are not removed from UNICODE fields in an index +# +create table t1 ( + a char(10) unicode not null, + index a (a) +) engine=myisam; +insert into t1 values (repeat(0x201f, 10)); +insert into t1 values (repeat(0x2020, 10)); +insert into t1 values (repeat(0x2021, 10)); +# make sure "index read" is used +explain select hex(a) from t1 order by a; +select hex(a) from t1 order by a; +alter table t1 drop index a; +select hex(a) from t1 order by a; +drop table t1; + +# # Bug #20076: server crashes for a query with GROUP BY if MIN/MAX aggregation # over a 'ucs2' field uses a temporary table # diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 65ee82fff23..804b17ab6bb 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -1229,6 +1229,30 @@ set @a:=null; execute my_stmt using @a; drop table if exists t1; + +# +# Bug#21505 Create view - illegal mix of collation for operation 'UNION' +# +--disable_warnings +drop table if exists t1; +drop view if exists v1, v2; +--enable_warnings +set names utf8; +create table t1(col1 varchar(12) character set utf8 collate utf8_unicode_ci); +insert into t1 values('t1_val'); +create view v1 as select 'v1_val' as col1; +select coercibility(col1), collation(col1) from v1; +create view v2 as select col1 from v1 union select col1 from t1; +select coercibility(col1), collation(col1)from v2; +drop view v1, v2; +create view v1 as select 'v1_val' collate utf8_swedish_ci as col1; +select coercibility(col1), collation(col1) from v1; +create view v2 as select col1 from v1 union select col1 from t1; +select coercibility(col1), collation(col1) from v2; +drop view v1, v2; +drop table t1; + + # # Bug#19960: Inconsistent results when joining # InnoDB tables using partial UTF8 indexes diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index db3536c6d36..cde9c1fcea3 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -461,3 +461,38 @@ SELECT GROUP_CONCAT(a), x GROUP BY x; DROP TABLE t1; +# +# Bug#23451 GROUP_CONCAT truncates a multibyte utf8 character +# +set names utf8; +create table t1 +( + x text character set utf8 not null, + y integer not null +); +insert into t1 values (repeat('a', 1022), 0), (repeat(_utf8 0xc3b7, 4), 0); +let $1= 10; +while ($1) +{ + eval set group_concat_max_len= 1022 + $1; + --disable_result_log + select @x:=group_concat(x) from t1 group by y; + --enable_result_log + select @@group_concat_max_len, length(@x), char_length(@x), right(@x,12), right(HEX(@x),12); + dec $1; +} +drop table t1; +set group_concat_max_len=1024; +set names latin1; + +# +# Bug#14169 type of group_concat() result changed to blob if tmp_table was used +# +create table t1 (f1 int unsigned, f2 varchar(255)); +insert into t1 values (1,repeat('a',255)),(2,repeat('b',255)); +--enable_metadata +select f2,group_concat(f1) from t1 group by f2; +--disable_metadata +drop table t1; + +# End of 4.1 tests diff --git a/mysql-test/t/lowercase_table.test b/mysql-test/t/lowercase_table.test index 96437bc7636..31513f1bd06 100644 --- a/mysql-test/t/lowercase_table.test +++ b/mysql-test/t/lowercase_table.test @@ -85,3 +85,23 @@ drop table t1, t2; show tables; # End of 4.1 tests + + +# +# Bug#20404: SHOW CREATE TABLE fails with Turkish I +# +set names utf8; +--disable_warnings +drop table if exists Ä°,Ä°Ä°; +--enable_warnings +create table Ä° (s1 int); +show create table Ä°; +show tables; +drop table Ä°; +create table Ä°Ä° (s1 int); +show create table Ä°Ä°; +show tables; +drop table Ä°Ä°; +set names latin1; + +--echo End of 5.0 tests diff --git a/mysql-test/t/rpl_do_grant.test b/mysql-test/t/rpl_do_grant.test index 8d6f99e4bf1..4e398114269 100644 --- a/mysql-test/t/rpl_do_grant.test +++ b/mysql-test/t/rpl_do_grant.test @@ -39,11 +39,11 @@ connection master; delete from mysql.user where user=_binary'rpl_do_grant'; delete from mysql.db where user=_binary'rpl_do_grant'; flush privileges; -save_master_pos; -connection slave; -sync_with_master; -# no need to delete manually, as the DELETEs must have done some real job on -# master (updated binlog) +sync_slave_with_master; +# The mysql database is not replicated, so we have to do the deletes +# manually on the slave as well. +delete from mysql.user where user=_binary'rpl_do_grant'; +delete from mysql.db where user=_binary'rpl_do_grant'; flush privileges; # End of 4.1 tests diff --git a/mysql-test/t/rpl_extraCol_innodb-master.opt b/mysql-test/t/rpl_extraCol_innodb-master.opt new file mode 100644 index 00000000000..627becdbfb5 --- /dev/null +++ b/mysql-test/t/rpl_extraCol_innodb-master.opt @@ -0,0 +1 @@ +--innodb diff --git a/mysql-test/t/rpl_extraCol_innodb-slave.opt b/mysql-test/t/rpl_extraCol_innodb-slave.opt new file mode 100644 index 00000000000..627becdbfb5 --- /dev/null +++ b/mysql-test/t/rpl_extraCol_innodb-slave.opt @@ -0,0 +1 @@ +--innodb diff --git a/mysql-test/t/rpl_extraCol_innodb.test b/mysql-test/t/rpl_extraCol_innodb.test new file mode 100644 index 00000000000..e9685baf01b --- /dev/null +++ b/mysql-test/t/rpl_extraCol_innodb.test @@ -0,0 +1,13 @@ +########################################### +# Author: Jeb +# Date: 2006-09-08 +# Purpose: Wapper for rpl_extraSlave_Col.test +# Using innodb +########################################### +-- source include/have_binlog_format_row.inc +-- source include/have_innodb.inc +-- source include/master-slave.inc +let $engine_type = 'InnoDB'; +-- source extra/rpl_tests/rpl_extraSlave_Col.test + + diff --git a/mysql-test/t/rpl_extraCol_myisam.test b/mysql-test/t/rpl_extraCol_myisam.test new file mode 100644 index 00000000000..d56df394ccf --- /dev/null +++ b/mysql-test/t/rpl_extraCol_myisam.test @@ -0,0 +1,12 @@ +########################################### +# Author: Jeb +# Date: 2006-09-07 +# Purpose: Wapper for rpl_extraSlave_Col.test +# Using MyISAM +########################################### +-- source include/have_binlog_format_row.inc +-- source include/master-slave.inc +let $engine_type = 'MyISAM'; +-- source extra/rpl_tests/rpl_extraSlave_Col.test + + diff --git a/mysql-test/t/rpl_ignore_table-slave.opt b/mysql-test/t/rpl_ignore_table-slave.opt index cb49119bfcb..3aabbb2e0f5 100644 --- a/mysql-test/t/rpl_ignore_table-slave.opt +++ b/mysql-test/t/rpl_ignore_table-slave.opt @@ -1 +1 @@ ---replicate-ignore-table=test.t1 --replicate-ignore-table=test.t2 --replicate-ignore-table=test.t3 +--replicate-ignore-table=test.t1 --replicate-ignore-table=test.t2 --replicate-ignore-table=test.t3 --replicate-wild-ignore-table=%.tmptbl% diff --git a/mysql-test/t/rpl_ignore_table.test b/mysql-test/t/rpl_ignore_table.test index 84b0a4cde38..10c75db0b70 100644 --- a/mysql-test/t/rpl_ignore_table.test +++ b/mysql-test/t/rpl_ignore_table.test @@ -28,3 +28,27 @@ connection master; DROP TABLE t1; DROP TABLE t4; sync_slave_with_master; + +# +# bug#22877 replication character sets get out of sync +# using replicate-wild-ignore-table +# +connection master; +--disable_warnings +DROP TABLE IF EXISTS t5; +--enable_warnings +CREATE TABLE t5 ( + word varchar(50) collate utf8_unicode_ci NOT NULL default '' +) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +SET @@session.character_set_client=33,@@session.collation_connection=192; +CREATE TEMPORARY TABLE tmptbl504451f4258$1 (id INT NOT NULL) ENGINE=MEMORY; +INSERT INTO t5 (word) VALUES ('TEST’'); +SELECT HEX(word) FROM t5; +sync_slave_with_master; +connection slave; +SELECT HEX(word) FROM t5; +--error 1146 +SELECT * FROM tmptbl504451f4258$1; +connection master; +DROP TABLE t5; +sync_slave_with_master; diff --git a/mysql-test/t/rpl_ndb_dd_advance.test b/mysql-test/t/rpl_ndb_dd_advance.test index 1fe36ecd8a1..a77521f3a82 100644 --- a/mysql-test/t/rpl_ndb_dd_advance.test +++ b/mysql-test/t/rpl_ndb_dd_advance.test @@ -385,12 +385,12 @@ while ($j) --connection slave STOP SLAVE; RESET SLAVE; -DROP PROCEDURE tpcb.load; -DROP PROCEDURE tpcb.trans; -DROP TABLE tpcb.account; -DROP TABLE tpcb.teller; -DROP TABLE tpcb.branch; -DROP TABLE tpcb.history; +DROP PROCEDURE IF EXISTS tpcb.load; +DROP PROCEDURE IF EXISTS tpcb.trans; +DROP TABLE IF EXISTS tpcb.account; +DROP TABLE IF EXISTS tpcb.teller; +DROP TABLE IF EXISTS tpcb.branch; +DROP TABLE IF EXISTS tpcb.history; DROP DATABASE tpcb; ALTER TABLESPACE ts1 @@ -534,8 +534,8 @@ SELECT COUNT(*) FROM history; --echo *************** TEST 2 CLEANUP SECTION ******************** connection master; -DROP PROCEDURE tpcb.load; -DROP PROCEDURE tpcb.trans; +DROP PROCEDURE IF EXISTS tpcb.load; +DROP PROCEDURE IF EXISTS tpcb.trans; DROP TABLE tpcb.account; DROP TABLE tpcb.teller; DROP TABLE tpcb.branch; diff --git a/mysql-test/t/rpl_ndb_ddl.test b/mysql-test/t/rpl_ndb_ddl.test index b3d32232518..0c503e56c9c 100644 --- a/mysql-test/t/rpl_ndb_ddl.test +++ b/mysql-test/t/rpl_ndb_ddl.test @@ -31,4 +31,5 @@ --source include/have_ndb.inc --source include/master-slave.inc let $engine_type= "NDB"; --- source extra/rpl_tests/rpl_ddl.test +-- source extra/rpl_tests/rpl_ndb_ddl.test + diff --git a/mysql-test/t/rpl_ndb_extraCol.test b/mysql-test/t/rpl_ndb_extraCol.test new file mode 100644 index 00000000000..cf0501c490a --- /dev/null +++ b/mysql-test/t/rpl_ndb_extraCol.test @@ -0,0 +1,13 @@ +########################################### +# Author: Jeb +# Date: 2006-09-08 +# Purpose: Wapper for rpl_extraSlave_Col.test +# Using NDB +########################################### +-- source include/have_binlog_format_row.inc +--source include/have_ndb.inc +-- source include/master-slave.inc +let $engine_type = 'NDB'; +-- source extra/rpl_tests/rpl_extraSlave_Col.test + + diff --git a/mysql-test/t/rpl_packet-master.opt b/mysql-test/t/rpl_packet-master.opt new file mode 100644 index 00000000000..42d4f94c999 --- /dev/null +++ b/mysql-test/t/rpl_packet-master.opt @@ -0,0 +1 @@ +-O max_allowed_packet=1024 -O net_buffer_length=1024 diff --git a/mysql-test/t/rpl_packet-slave.opt b/mysql-test/t/rpl_packet-slave.opt new file mode 100644 index 00000000000..42d4f94c999 --- /dev/null +++ b/mysql-test/t/rpl_packet-slave.opt @@ -0,0 +1 @@ +-O max_allowed_packet=1024 -O net_buffer_length=1024 diff --git a/mysql-test/t/rpl_packet.test b/mysql-test/t/rpl_packet.test new file mode 100644 index 00000000000..d01979a4731 --- /dev/null +++ b/mysql-test/t/rpl_packet.test @@ -0,0 +1,39 @@ +# +# Check replication protocol packet size handling +# Bug#19402 SQL close to the size of the max_allowed_packet fails on slave +# + +# max-out size db name +source include/master-slave.inc; + +let $db= DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________; +disable_warnings; +eval drop database if exists $db; +enable_warnings; +eval create database $db; + +connection master; +select @@net_buffer_length, @@max_allowed_packet; +disconnect master; + +# alas, can't use eval here; if db name changed apply the change here +connect (master,localhost,root,,DB_NAME_OF_MAX_LENGTH_AKA_NAME_LEN_64_BYTES_____________________); + +connection master; +create table `t1` (`f1` LONGTEXT) ENGINE=MyISAM; + +INSERT INTO `t1`(`f1`) VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1023'); +save_master_pos; + +connection slave; +sync_with_master; +eval select count(*) from `$db`.`t1` /* must be 1 */; + +connection master; +eval drop database $db; +save_master_pos; + +connection slave; +sync_with_master; + +# End of tests diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index fa3ddd28489..7175db1db4e 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2959,3 +2959,22 @@ DROP TABLE t1; --echo End of 5.0 tests. + +# +# Bug#21370 View renaming lacks tablename_to_filename encoding +# +--disable_warnings +DROP DATABASE IF EXISTS `d-1`; +--enable_warnings +CREATE DATABASE `d-1`; +USE `d-1`; +CREATE TABLE `t-1` (c1 INT); +CREATE VIEW `v-1` AS SELECT c1 FROM `t-1`; +SHOW TABLES; +RENAME TABLE `t-1` TO `t-2`; +RENAME TABLE `v-1` TO `v-2`; +SHOW TABLES; +DROP TABLE `t-2`; +DROP VIEW `v-2`; +DROP DATABASE `d-1`; +USE test; diff --git a/mysql-test/t/windows.test b/mysql-test/t/windows.test index d979c001454..4dab646df2c 100644 --- a/mysql-test/t/windows.test +++ b/mysql-test/t/windows.test @@ -39,3 +39,4 @@ execute abc; --error ER_UDF_NO_PATHS execute abc; deallocate prepare abc; + diff --git a/mysql-test/t/xml.test b/mysql-test/t/xml.test index 3347573b4b7..ef94c7508c4 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -376,3 +376,33 @@ select extractValue('<:>test</:>','//*'); select extractValue('<_>test</_>','//*'); # dot, dash, underscore and semicolon are good identifier middle characters select extractValue('<x.-_:>test</x.-_:>','//*'); + +# +# Bug#22823 gt and lt operators appear to be +# reversed in ExtractValue() command +# +set @xml= "<entry><id>pt10</id><pt>10</pt></entry><entry><id>pt50</id><pt>50</pt></entry>"; +select ExtractValue(@xml, "/entry[(pt=10)]/id"); +select ExtractValue(@xml, "/entry[(pt!=10)]/id"); +select ExtractValue(@xml, "/entry[(pt<10)]/id"); +select ExtractValue(@xml, "/entry[(pt<=10)]/id"); +select ExtractValue(@xml, "/entry[(pt>10)]/id"); +select ExtractValue(@xml, "/entry[(pt>=10)]/id"); +select ExtractValue(@xml, "/entry[(pt=50)]/id"); +select ExtractValue(@xml, "/entry[(pt!=50)]/id"); +select ExtractValue(@xml, "/entry[(pt<50)]/id"); +select ExtractValue(@xml, "/entry[(pt<=50)]/id"); +select ExtractValue(@xml, "/entry[(pt>50)]/id"); +select ExtractValue(@xml, "/entry[(pt>=50)]/id"); +select ExtractValue(@xml, "/entry[(10=pt)]/id"); +select ExtractValue(@xml, "/entry[(10!=pt)]/id"); +select ExtractValue(@xml, "/entry[(10>pt)]/id"); +select ExtractValue(@xml, "/entry[(10>=pt)]/id"); +select ExtractValue(@xml, "/entry[(10<pt)]/id"); +select ExtractValue(@xml, "/entry[(10<=pt)]/id"); +select ExtractValue(@xml, "/entry[(50=pt)]/id"); +select ExtractValue(@xml, "/entry[(50!=pt)]/id"); +select ExtractValue(@xml, "/entry[(50>pt)]/id"); +select ExtractValue(@xml, "/entry[(50>=pt)]/id"); +select ExtractValue(@xml, "/entry[(50<pt)]/id"); +select ExtractValue(@xml, "/entry[(50<=pt)]/id"); diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh index 68798e92725..ddcea343609 100644 --- a/scripts/make_binary_distribution.sh +++ b/scripts/make_binary_distribution.sh @@ -243,6 +243,7 @@ $CP mysql-test/lib/*.pl $BASE/mysql-test/lib $CP mysql-test/lib/*.sql $BASE/mysql-test/lib $CP mysql-test/t/*.def $BASE/mysql-test/t $CP mysql-test/include/*.inc $BASE/mysql-test/include +$CP mysql-test/include/*.test $BASE/mysql-test/include $CP mysql-test/t/*.def $BASE/mysql-test/t $CP mysql-test/std_data/*.dat mysql-test/std_data/*.frm \ mysql-test/std_data/*.pem mysql-test/std_data/Moscow_leap \ diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 1c2944de530..4cdc4c01c4e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -54,6 +54,7 @@ ADD_EXECUTABLE(mysqld ../sql-common/client.c derror.cc des_key_file.cc event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc + rpl_rli.cc rpl_mi.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h diff --git a/sql/Makefile.am b/sql/Makefile.am index 9a0303a433f..ac2f4836c15 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -54,7 +54,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ ha_ndbcluster.h ha_ndbcluster_binlog.h \ ha_ndbcluster_tables.h \ opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \ - log.h sql_show.h rpl_rli.h \ + log.h sql_show.h rpl_rli.h rpl_mi.h \ sql_select.h structs.h table.h sql_udf.h hash_filo.h \ lex.h lex_symbol.h sql_acl.h sql_crypt.h \ log_event.h sql_repl.h slave.h rpl_filter.h \ @@ -94,7 +94,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ slave.cc sql_repl.cc rpl_filter.cc rpl_tblmap.cc \ - rpl_utility.cc rpl_injector.cc \ + rpl_utility.cc rpl_injector.cc rpl_rli.cc rpl_mi.cc \ sql_union.cc sql_derived.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc \ diff --git a/sql/field.cc b/sql/field.cc index 122a44305f2..1e42a53e45a 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1427,6 +1427,7 @@ Field_str::Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, field_charset=charset; if (charset->state & MY_CS_BINSORT) flags|=BINARY_FLAG; + field_derivation= DERIVATION_IMPLICIT; } @@ -5921,38 +5922,149 @@ void Field_datetime::sql_type(String &res) const ** A string may be varchar or binary ****************************************************************************/ +/* + Report "not well formed" or "cannot convert" error + after storing a character string info a field. + + SYNOPSIS + check_string_copy_error() + field - Field + well_formed_error_pos - where not well formed data was first met + cannot_convert_error_pos - where a not-convertable character was first met + end - end of the string + + NOTES + As of version 5.0 both cases return the same error: + + "Invalid string value: 'xxx' for column 't' at row 1" + + Future versions will possibly introduce a new error message: + + "Cannot convert character string: 'xxx' for column 't' at row 1" + + RETURN + FALSE - If errors didn't happen + TRUE - If an error happened +*/ + +static bool +check_string_copy_error(Field_str *field, + const char *well_formed_error_pos, + const char *cannot_convert_error_pos, + const char *end) +{ + const char *pos, *end_orig; + char tmp[64], *t; + + if (!(pos= well_formed_error_pos) && + !(pos= cannot_convert_error_pos)) + return FALSE; + + end_orig= end; + set_if_smaller(end, pos + 6); + + for (t= tmp; pos < end; pos++) + { + if (((unsigned char) *pos) >= 0x20 && + ((unsigned char) *pos) <= 0x7F) + { + *t++= *pos; + } + else + { + *t++= '\\'; + *t++= 'x'; + *t++= _dig_vec_upper[((unsigned char) *pos) >> 4]; + *t++= _dig_vec_upper[((unsigned char) *pos) & 15]; + } + } + if (end_orig > end) + { + *t++= '.'; + *t++= '.'; + *t++= '.'; + } + *t= '\0'; + push_warning_printf(field->table->in_use, + field->table->in_use->abort_on_warning ? + MYSQL_ERROR::WARN_LEVEL_ERROR : + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, + ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), + "string", tmp, field->field_name, + (ulong) field->table->in_use->row_count); + return TRUE; +} + + + +/* + Send a truncation warning or a truncation error + after storing a too long character string info a field. + + SYNOPSIS + report_data_too_long() + field - Field + + RETURN + N/A +*/ + +inline void +report_data_too_long(Field_str *field) +{ + if (field->table->in_use->abort_on_warning) + field->set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); + else + field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); +} + + +/* + Test if the given string contains important data: + not spaces for character string, + or any data for binary string. + + SYNOPSIS + test_if_important_data() + cs Character set + str String to test + strend String end + + RETURN + FALSE - If string does not have important data + TRUE - If string has some important data +*/ + +static bool +test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend) +{ + if (cs != &my_charset_bin) + str+= cs->cset->scan(cs, str, strend, MY_SEQ_SPACES); + return (str < strend); +} + + /* Copy a string and fill with space */ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - int error= 0, well_formed_error; - uint32 not_used; - char buff[STRING_BUFFER_USUAL_SIZE]; - String tmpstr(buff,sizeof(buff), &my_charset_bin); uint copy_length; + const char *well_formed_error_pos; + const char *cannot_convert_error_pos; + const char *from_end_pos; /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); - /* Convert character set if necessary */ - if (String::needs_conversion(length, cs, field_charset, ¬_used)) - { - uint conv_errors; - tmpstr.copy(from, length, cs, field_charset, &conv_errors); - from= tmpstr.ptr(); - length= tmpstr.length(); - if (conv_errors) - error= 2; - } - - /* Make sure we don't break a multibyte sequence or copy malformed data. */ - copy_length= field_charset->cset->well_formed_len(field_charset, - from,from+length, - field_length/ - field_charset->mbmaxlen, - &well_formed_error); - memmove(ptr, from, copy_length); + copy_length= well_formed_copy_nchars(field_charset, + ptr, field_length, + cs, from, length, + field_length / field_charset->mbmaxlen, + &well_formed_error_pos, + &cannot_convert_error_pos, + &from_end_pos); /* Append spaces if the string was shorter than the field. */ if (copy_length < field_length) @@ -5960,32 +6072,23 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) field_length-copy_length, field_charset->pad_char); + if (check_string_copy_error(this, well_formed_error_pos, + cannot_convert_error_pos, from + length)) + return 2; + /* Check if we lost any important data (anything in a binary string, or any non-space in others). */ - if ((copy_length < length) && table->in_use->count_cuted_fields) + if ((from_end_pos < from + length) && table->in_use->count_cuted_fields) { - if (binary()) - error= 2; - else + if (test_if_important_data(field_charset, from_end_pos, from + length)) { - const char *end=from+length; - from+= copy_length; - from+= field_charset->cset->scan(field_charset, from, end, - MY_SEQ_SPACES); - if (from != end) - error= 2; + report_data_too_long(this); + return 2; } } - if (error) - { - if (table->in_use->abort_on_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); - else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); - } - return error; + return 0; } @@ -6343,58 +6446,35 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table, int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - uint32 not_used, copy_length; - char buff[STRING_BUFFER_USUAL_SIZE]; - String tmpstr(buff,sizeof(buff), &my_charset_bin); - int error_code= 0, well_formed_error; - enum MYSQL_ERROR::enum_warning_level level= MYSQL_ERROR::WARN_LEVEL_WARN; + uint copy_length; + const char *well_formed_error_pos; + const char *cannot_convert_error_pos; + const char *from_end_pos; + + copy_length= well_formed_copy_nchars(field_charset, + ptr + length_bytes, field_length, + cs, from, length, + field_length / field_charset->mbmaxlen, + &well_formed_error_pos, + &cannot_convert_error_pos, + &from_end_pos); - /* Convert character set if necessary */ - if (String::needs_conversion(length, cs, field_charset, ¬_used)) - { - uint conv_errors; - tmpstr.copy(from, length, cs, field_charset, &conv_errors); - from= tmpstr.ptr(); - length= tmpstr.length(); - if (conv_errors) - error_code= WARN_DATA_TRUNCATED; - } - /* - Make sure we don't break a multibyte sequence - as well as don't copy a malformed data. - */ - copy_length= field_charset->cset->well_formed_len(field_charset, - from,from+length, - field_length/ - field_charset->mbmaxlen, - &well_formed_error); - memmove(ptr + length_bytes, from, copy_length); if (length_bytes == 1) *ptr= (uchar) copy_length; else int2store(ptr, copy_length); + if (check_string_copy_error(this, well_formed_error_pos, + cannot_convert_error_pos, from + length)) + return 2; + // Check if we lost something other than just trailing spaces - if ((copy_length < length) && table->in_use->count_cuted_fields && - !error_code) - { - if (!binary()) - { - const char *end= from + length; - from+= copy_length; - from+= field_charset->cset->scan(field_charset, from, end, MY_SEQ_SPACES); - /* If we lost only spaces then produce a NOTE, not a WARNING */ - if (from == end) - level= MYSQL_ERROR::WARN_LEVEL_NOTE; - } - error_code= WARN_DATA_TRUNCATED; - } - if (error_code) + if ((from_end_pos < from + length) && table->in_use->count_cuted_fields) { - if (level == MYSQL_ERROR::WARN_LEVEL_WARN && - table->in_use->abort_on_warning) - error_code= ER_DATA_TOO_LONG; - set_warning(level, error_code, 1); + if (test_if_important_data(field_charset, from_end_pos, from + length)) + report_data_too_long(this); + else /* If we lost only spaces then produce a NOTE, not a WARNING */ + set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); return 2; } return 0; @@ -7012,68 +7092,70 @@ void Field_blob::put_length(char *pos, uint32 length) int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { ASSERT_COLUMN_MARKED_FOR_WRITE; - int error= 0, well_formed_error; + uint copy_length, new_length; + const char *well_formed_error_pos; + const char *cannot_convert_error_pos; + const char *from_end_pos, *tmp; + char buff[STRING_BUFFER_USUAL_SIZE]; + String tmpstr(buff,sizeof(buff), &my_charset_bin); + if (!length) { bzero(ptr,Field_blob::pack_length()); + return 0; } - else - { - bool was_conversion; - char buff[STRING_BUFFER_USUAL_SIZE]; - String tmpstr(buff,sizeof(buff), &my_charset_bin); - uint copy_length; - uint32 not_used; - /* Convert character set if necessary */ - if ((was_conversion= String::needs_conversion(length, cs, field_charset, - ¬_used))) - { - uint conv_errors; - if (tmpstr.copy(from, length, cs, field_charset, &conv_errors)) - { - /* Fatal OOM error */ - bzero(ptr,Field_blob::pack_length()); - return -1; - } - from= tmpstr.ptr(); - length= tmpstr.length(); - if (conv_errors) - error= 2; - } - - copy_length= max_data_length(); - /* - copy_length is OK as last argument to well_formed_len as this is never - used to limit the length of the data. The cut of long data is done with - the 'min()' call below. - */ - copy_length= field_charset->cset->well_formed_len(field_charset, - from,from + - min(length, copy_length), - copy_length, - &well_formed_error); - if (copy_length < length) - error= 2; - Field_blob::store_length(copy_length); - if (was_conversion || table->copy_blobs || copy_length <= MAX_FIELD_WIDTH) - { // Must make a copy - if (from != value.ptr()) // For valgrind - { - value.copy(from,copy_length,charset()); - from=value.ptr(); - } + if (from == value.ptr()) + { + uint32 dummy_offset; + if (!String::needs_conversion(length, cs, field_charset, &dummy_offset)) + { + Field_blob::store_length(length); + bmove(ptr+packlength,(char*) &from,sizeof(char*)); + return 0; } - bmove(ptr+packlength,(char*) &from,sizeof(char*)); + if (tmpstr.copy(from, length, cs)) + goto oom_error; + from= tmpstr.ptr(); } - if (error) + + new_length= min(max_data_length(), field_charset->mbmaxlen * length); + if (value.alloc(new_length)) + goto oom_error; + + /* + "length" is OK as "nchars" argument to well_formed_copy_nchars as this + is never used to limit the length of the data. The cut of long data + is done with the new_length value. + */ + copy_length= well_formed_copy_nchars(field_charset, + (char*) value.ptr(), new_length, + cs, from, length, + length, + &well_formed_error_pos, + &cannot_convert_error_pos, + &from_end_pos); + + Field_blob::store_length(copy_length); + tmp= value.ptr(); + bmove(ptr+packlength,(char*) &tmp,sizeof(char*)); + + if (check_string_copy_error(this, well_formed_error_pos, + cannot_convert_error_pos, from + length)) + return 2; + + if (copy_length < length) { - if (table->in_use->abort_on_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1); - else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + report_data_too_long(this); + return 2; } + return 0; + +oom_error: + /* Fatal OOM error */ + bzero(ptr,Field_blob::pack_length()); + return -1; } diff --git a/sql/field.h b/sql/field.h index 9b81931d416..f0cd9cc6f03 100644 --- a/sql/field.h +++ b/sql/field.h @@ -351,6 +351,9 @@ public: virtual CHARSET_INFO *sort_charset(void) const { return charset(); } virtual bool has_charset(void) const { return FALSE; } virtual void set_charset(CHARSET_INFO *charset) { } + virtual enum Derivation derivation(void) const + { return DERIVATION_IMPLICIT; } + virtual void set_derivation(enum Derivation derivation) { } bool set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code, int cuted_increment); bool check_int(const char *str, int length, const char *int_end, @@ -446,6 +449,7 @@ public: class Field_str :public Field { protected: CHARSET_INFO *field_charset; + enum Derivation field_derivation; public: Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, @@ -459,6 +463,9 @@ public: uint size_of() const { return sizeof(*this); } CHARSET_INFO *charset(void) const { return field_charset; } void set_charset(CHARSET_INFO *charset) { field_charset=charset; } + enum Derivation derivation(void) const { return field_derivation; } + virtual void set_derivation(enum Derivation derivation_arg) + { field_derivation= derivation_arg; } bool binary() const { return field_charset == &my_charset_bin; } uint32 max_length() { return field_length; } friend class create_field; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 9df2171d85c..76999896e3c 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -413,7 +413,7 @@ Thd_ndb::get_open_table(THD *thd, const void *key) thd_ndb_share->stat.no_uncommitted_rows_count= 0; thd_ndb_share->stat.records= ~(ha_rows)0; } - DBUG_PRINT("exit", ("thd_ndb_share: 0x%x key: 0x%x", thd_ndb_share, key)); + DBUG_PRINT("exit", ("thd_ndb_share: %p key: %p", thd_ndb_share, key)); DBUG_RETURN(thd_ndb_share); } @@ -1171,7 +1171,7 @@ int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info, index= dict->getIndexGlobal(index_name, *m_table); if (!index) ERR_RETURN(dict->getNdbError()); - DBUG_PRINT("info", ("index: 0x%x id: %d version: %d.%d status: %d", + DBUG_PRINT("info", ("index: %p id: %d version: %d.%d status: %d", index, index->getObjectId(), index->getObjectVersion() & 0xFFFFFF, @@ -1215,7 +1215,7 @@ int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info, index= dict->getIndexGlobal(unique_index_name, *m_table); if (!index) ERR_RETURN(dict->getNdbError()); - DBUG_PRINT("info", ("index: 0x%x id: %d version: %d.%d status: %d", + DBUG_PRINT("info", ("index: %p id: %d version: %d.%d status: %d", index, index->getObjectId(), index->getObjectVersion() & 0xFFFFFF, @@ -2072,7 +2072,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) all pending update or delete operations should be sent to NDB */ - DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending)); + DBUG_PRINT("info", ("ops_pending: %llu", m_ops_pending)); if (m_ops_pending) { if (m_transaction_on) @@ -3480,7 +3480,7 @@ int ha_ndbcluster::close_scan() Take over any pending transactions to the deleteing/updating transaction before closing the scan */ - DBUG_PRINT("info", ("ops_pending: %d", m_ops_pending)); + DBUG_PRINT("info", ("ops_pending: %llu", m_ops_pending)); if (execute_no_commit(this,trans,false) != 0) { no_uncommitted_rows_execute_failure(); DBUG_RETURN(ndb_err(trans)); @@ -4286,7 +4286,7 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all) while ((share= it++)) { pthread_mutex_lock(&share->mutex); - DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %d ", + DBUG_PRINT("info", ("Invalidate commit_count for %s, commit_count: %llu ", share->key, share->commit_count)); share->commit_count= 0; share->commit_count_lock++; @@ -4691,7 +4691,7 @@ int ha_ndbcluster::create(const char *name, DBUG_RETURN(2); } - DBUG_PRINT("info", ("setFrm data=%lx len=%d", pack_data, pack_length)); + DBUG_PRINT("info", ("setFrm data=%p len=%d", pack_data, pack_length)); tab.setFrm(pack_data, pack_length); my_free((char*)data, MYF(0)); my_free((char*)pack_data, MYF(0)); @@ -5103,8 +5103,8 @@ int ha_ndbcluster::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys) { DBUG_ENTER("ha_ndbcluster::add_index"); - DBUG_PRINT("info", ("ha_ndbcluster::add_index to table %s", - table_arg->s->table_name)); + DBUG_PRINT("info", ("ha_ndbcluster::add_index to table %s", + table_arg->s->table_name.str)); int error= 0; uint idx; @@ -6103,9 +6103,7 @@ static void ndbcluster_drop_database(handlerton *hton, char *path) #endif DBUG_VOID_RETURN; } -/* - find all tables in ndb and discover those needed -*/ + int ndb_create_table_from_engine(THD *thd, const char *db, const char *table_name) { @@ -6118,6 +6116,9 @@ int ndb_create_table_from_engine(THD *thd, const char *db, return res; } +/* + find all tables in ndb and discover those needed +*/ int ndbcluster_find_all_files(THD *thd) { DBUG_ENTER("ndbcluster_find_all_files"); @@ -7162,31 +7163,51 @@ static byte *ndbcluster_get_key(NDB_SHARE *share,uint *length, return (byte*) share->key; } + #ifndef DBUG_OFF -static void dbug_print_open_tables() + +static void print_share(const char* where, NDB_SHARE* share) { - DBUG_ENTER("dbug_print_open_tables"); - for (uint i= 0; i < ndbcluster_open_tables.records; i++) - { - NDB_SHARE *share= (NDB_SHARE*) hash_element(&ndbcluster_open_tables, i); - DBUG_PRINT("share", - ("[%d] 0x%lx key: %s key_length: %d", - i, share, share->key, share->key_length)); - DBUG_PRINT("share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + fprintf(DBUG_FILE, + "%s %s.%s: use_count: %u, commit_count: %llu\n", + where, share->db, share->table_name, share->use_count, + share->commit_count); + fprintf(DBUG_FILE, + " - key: %s, key_length: %d\n", + share->key, share->key_length); + #ifdef HAVE_NDB_BINLOG - if (share->table) - DBUG_PRINT("share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); + if (share->table) + fprintf(DBUG_FILE, + " - share->table: %p %s.%s\n", + share->table, share->table->s->db.str, + share->table->s->table_name.str); #endif - } - DBUG_VOID_RETURN; } -#else -#define dbug_print_open_tables() + + +static void print_ndbcluster_open_tables() +{ + DBUG_LOCK_FILE; + fprintf(DBUG_FILE, ">ndbcluster_open_tables\n"); + for (uint i= 0; i < ndbcluster_open_tables.records; i++) + print_share("", + (NDB_SHARE*)hash_element(&ndbcluster_open_tables, i)); + fprintf(DBUG_FILE, "<ndbcluster_open_tables\n"); + DBUG_UNLOCK_FILE; +} + + +#define dbug_print_open_tables() \ + DBUG_EXECUTE("info", \ + print_ndbcluster_open_tables();); + +#define dbug_print_share(t, s) \ + DBUG_LOCK_FILE; \ + DBUG_EXECUTE("info", \ + print_share((t), (s));); \ + DBUG_UNLOCK_FILE; + #endif #ifdef HAVE_NDB_BINLOG @@ -7331,19 +7352,9 @@ static int rename_share(NDB_SHARE *share, const char *new_key) share->table_name= share->db + strlen(share->db) + 1; ha_ndbcluster::set_tabname(new_key, share->table_name); - DBUG_PRINT("rename_share", - ("0x%lx key: %s key_length: %d", - share, share->key, share->key_length)); - DBUG_PRINT("rename_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("rename_share:", share); if (share->table) { - DBUG_PRINT("rename_share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); - if (share->op == 0) { share->table->s->db.str= share->db; @@ -7371,14 +7382,7 @@ NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share) share->use_count++; dbug_print_open_tables(); - - DBUG_PRINT("get_share", - ("0x%lx key: %s key_length: %d", - share, share->key, share->key_length)); - DBUG_PRINT("get_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("ndbcluster_get_share:", share); pthread_mutex_unlock(&ndbcluster_mutex); return share; } @@ -7469,14 +7473,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, share->use_count++; dbug_print_open_tables(); - - DBUG_PRINT("info", - ("0x%lx key: %s key_length: %d key: %s", - share, share->key, share->key_length, key)); - DBUG_PRINT("info", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - share->db, share->table_name, - share->use_count, share->commit_count)); + dbug_print_share("ndbcluster_get_share:", share); if (!have_lock) pthread_mutex_unlock(&ndbcluster_mutex); DBUG_RETURN(share); @@ -7486,13 +7483,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table, void ndbcluster_real_free_share(NDB_SHARE **share) { DBUG_ENTER("ndbcluster_real_free_share"); - DBUG_PRINT("real_free_share", - ("0x%lx key: %s key_length: %d", - (*share), (*share)->key, (*share)->key_length)); - DBUG_PRINT("real_free_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - (*share)->db, (*share)->table_name, - (*share)->use_count, (*share)->commit_count)); + dbug_print_share("ndbcluster_real_free_share:", *share); hash_delete(&ndbcluster_open_tables, (byte*) *share); thr_lock_delete(&(*share)->lock); @@ -7521,12 +7512,7 @@ void ndbcluster_real_free_share(NDB_SHARE **share) DBUG_VOID_RETURN; } -/* - decrease refcount of share - calls real_free_share when refcount reaches 0 - have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken -*/ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock) { if (!have_lock) @@ -7540,13 +7526,7 @@ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock) else { dbug_print_open_tables(); - DBUG_PRINT("free_share", - ("0x%lx key: %s key_length: %d", - *share, (*share)->key, (*share)->key_length)); - DBUG_PRINT("free_share", - ("db.tablename: %s.%s use_count: %d commit_count: %d", - (*share)->db, (*share)->table_name, - (*share)->use_count, (*share)->commit_count)); + dbug_print_share("ndbcluster_free_share:", *share); } if (!have_lock) pthread_mutex_unlock(&ndbcluster_mutex); @@ -8176,27 +8156,25 @@ ha_ndbcluster::update_table_comment( pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) { THD *thd; /* needs to be first for thread_stack */ - Ndb* ndb; struct timespec abstime; List<NDB_SHARE> util_open_tables; + Thd_ndb *thd_ndb; my_thread_init(); DBUG_ENTER("ndb_util_thread"); - DBUG_PRINT("enter", ("ndb_cache_check_time: %d", ndb_cache_check_time)); + DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time)); thd= new THD; /* note that contructor of THD uses DBUG_ */ THD_CHECK_SENTRY(thd); - ndb= new Ndb(g_ndb_cluster_connection, ""); pthread_detach_this_thread(); ndb_util_thread= pthread_self(); thd->thread_stack= (char*)&thd; /* remember where our stack is */ - if (thd->store_globals() || (ndb->init() != 0)) + if (thd->store_globals()) { thd->cleanup(); delete thd; - delete ndb; DBUG_RETURN(NULL); } thd->init_for_queries(); @@ -8238,16 +8216,14 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) } pthread_mutex_unlock(&LOCK_ndb_util_thread); + /* Get thd_ndb for this thread */ + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) { - Thd_ndb *thd_ndb; - if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) - { - sql_print_error("Could not allocate Thd_ndb object"); - goto ndb_util_thread_end; - } - set_thd_ndb(thd, thd_ndb); - thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; + sql_print_error("Could not allocate Thd_ndb object"); + goto ndb_util_thread_end; } + set_thd_ndb(thd, thd_ndb); + thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; #ifdef HAVE_NDB_BINLOG if (ndb_extra_logging && ndb_binlog_running) @@ -8330,26 +8306,26 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) } #endif /* HAVE_NDB_BINLOG */ DBUG_PRINT("ndb_util_thread", - ("Fetching commit count for: %s", - share->key)); + ("Fetching commit count for: %s", share->key)); - /* Contact NDB to get commit count for table */ - ndb->setDatabaseName(share->db); struct Ndb_statistics stat; - uint lock; pthread_mutex_lock(&share->mutex); lock= share->commit_count_lock; pthread_mutex_unlock(&share->mutex); { + /* Contact NDB to get commit count for table */ + Ndb* ndb= thd_ndb->ndb; + ndb->setDatabaseName(share->db); Ndb_table_guard ndbtab_g(ndb->getDictionary(), share->table_name); if (ndbtab_g.get_table() && - ndb_get_table_statistics(NULL, false, ndb, ndbtab_g.get_table(), &stat) == 0) + ndb_get_table_statistics(NULL, false, ndb, + ndbtab_g.get_table(), &stat) == 0) { char buff[22], buff2[22]; DBUG_PRINT("ndb_util_thread", - ("Table: %s, commit_count: %llu, rows: %llu", + ("Table: %s, commit_count: %s, rows: %s", share->key, llstr(stat.commit_count, buff), llstr(stat.row_count, buff2))); @@ -8401,7 +8377,6 @@ ndb_util_thread_end: net_end(&thd->net); thd->cleanup(); delete thd; - delete ndb; DBUG_PRINT("exit", ("ndb_util_thread")); my_thread_end(); pthread_exit(0); @@ -9191,7 +9166,7 @@ void ndb_serialize_cond(const Item *item, void *arg) if (context->expecting(Item::INT_ITEM)) { Item_int *int_item= (Item_int *) item; - DBUG_PRINT("info", ("value %d", int_item->value)); + DBUG_PRINT("info", ("value %llu", int_item->value)); NDB_ITEM_QUALIFICATION q; q.value_type= Item::INT_ITEM; curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item); @@ -9214,7 +9189,7 @@ void ndb_serialize_cond(const Item *item, void *arg) context->supported= FALSE; break; case Item::REAL_ITEM: - DBUG_PRINT("info", ("REAL_ITEM %s")); + DBUG_PRINT("info", ("REAL_ITEM")); if (context->expecting(Item::REAL_ITEM)) { Item_float *float_item= (Item_float *) item; @@ -9262,7 +9237,7 @@ void ndb_serialize_cond(const Item *item, void *arg) context->supported= FALSE; break; case Item::DECIMAL_ITEM: - DBUG_PRINT("info", ("DECIMAL_ITEM %s")); + DBUG_PRINT("info", ("DECIMAL_ITEM")); if (context->expecting(Item::DECIMAL_ITEM)) { Item_decimal *decimal_item= (Item_decimal *) item; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 3dfca5d1bb2..e0b7502a40a 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -3436,7 +3436,6 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) global_system_variables.binlog_format == BINLOG_FORMAT_MIXED) { ndb_binlog_running= TRUE; - thd->current_stmt_binlog_row_based= TRUE; // If in mixed mode } else { diff --git a/sql/handler.cc b/sql/handler.cc index 451f974a066..f16876f2ffd 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3460,38 +3460,15 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) declared static, but it works by putting it into an anonymous namespace. */ namespace { - struct st_table_data { - char const *db; - char const *name; - }; - - static int table_name_compare(void const *a, void const *b) - { - st_table_data const *x = (st_table_data const*) a; - st_table_data const *y = (st_table_data const*) b; - - /* Doing lexical compare in order (db,name) */ - int const res= strcmp(x->db, y->db); - return res != 0 ? res : strcmp(x->name, y->name); - } - bool check_table_binlog_row_based(THD *thd, TABLE *table) { - static st_table_data const ignore[] = { - { "mysql", "event" }, - { "mysql", "general_log" }, - { "mysql", "slow_log" } - }; - - my_size_t const ignore_size = sizeof(ignore)/sizeof(*ignore); - st_table_data const item = { table->s->db.str, table->s->table_name.str }; - if (table->s->cached_row_logging_check == -1) - table->s->cached_row_logging_check= - (table->s->tmp_table == NO_TMP_TABLE) && - binlog_filter->db_ok(table->s->db.str) && - bsearch(&item, ignore, ignore_size, - sizeof(st_table_data), table_name_compare) == NULL; + { + int const check(table->s->tmp_table == NO_TMP_TABLE && + binlog_filter->db_ok(table->s->db.str) && + strcmp("mysql", table->s->db.str) != 0); + table->s->cached_row_logging_check= check; + } DBUG_ASSERT(table->s->cached_row_logging_check == 0 || table->s->cached_row_logging_check == 1); diff --git a/sql/item.cc b/sql/item.cc index 8914593e9f0..0662ddefbb4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1655,7 +1655,7 @@ void Item_field::set_field(Field *field_par) db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); - collation.set(field_par->charset(), DERIVATION_IMPLICIT); + collation.set(field_par->charset(), field_par->derivation()); fixed= 1; } diff --git a/sql/item.h b/sql/item.h index 8799fa07eb7..2c26e1c4a07 100644 --- a/sql/item.h +++ b/sql/item.h @@ -27,19 +27,7 @@ class Item_field; /* "Declared Type Collation" A combination of collation and its derivation. -*/ -enum Derivation -{ - DERIVATION_IGNORABLE= 5, - DERIVATION_COERCIBLE= 4, - DERIVATION_SYSCONST= 3, - DERIVATION_IMPLICIT= 2, - DERIVATION_NONE= 1, - DERIVATION_EXPLICIT= 0 -}; - -/* Flags for collation aggregation modes: MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b2ff62028c1..1cb943e45c2 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3019,6 +3019,7 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), String tmp2; String *result= &item->result; Item **arg= item->args, **arg_end= item->args + item->arg_count_field; + uint old_length= result->length(); if (item->no_appended) item->no_appended= FALSE; @@ -3053,8 +3054,22 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), /* stop if length of result more than max_length */ if (result->length() > item->max_length) { + int well_formed_error; + CHARSET_INFO *cs= item->collation.collation; + const char *ptr= result->ptr(); + uint add_length; + /* + It's ok to use item->result.length() as the fourth argument + as this is never used to limit the length of the data. + Cut is done with the third argument. + */ + add_length= cs->cset->well_formed_len(cs, + ptr + old_length, + ptr + item->max_length, + result->length(), + &well_formed_error); + result->length(old_length + add_length); item->count_cut_values++; - result->length(item->max_length); item->warning_for_row= TRUE; return 1; } @@ -3244,8 +3259,7 @@ bool Item_func_group_concat::add() we can dump the row here in case of GROUP_CONCAT(DISTINCT...) instead of doing tree traverse later. */ - if (result.length() <= max_length && - !warning_for_row && + if (!warning_for_row && (!tree || (el->count == 1 && distinct && !arg_count_order))) dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this); @@ -3314,7 +3328,8 @@ bool Item_func_group_concat::setup(THD *thd) DBUG_RETURN(TRUE); /* We'll convert all blobs to varchar fields in the temporary table */ - tmp_table_param->convert_blob_length= max_length; + tmp_table_param->convert_blob_length= max_length * + collation.collation->mbmaxlen; /* Push all not constant fields to the list and create a temp table */ always_null= 0; for (uint i= 0; i < arg_count_field; i++) diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 44a2b690bac..21239a13735 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -532,7 +532,7 @@ public: longlong val_int() { Item_func *comp= (Item_func*)args[1]; - Item_string *fake= (Item_string*)(comp->arguments()[1]); + Item_string *fake= (Item_string*)(comp->arguments()[0]); String *res= args[0]->val_nodeset(&tmp_nodeset); MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr(); MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length()); @@ -884,7 +884,7 @@ static Item *eq_func(int oper, Item *a, Item *b) Create a comparator function for scalar arguments, for the given arguments and reverse operation, e.g. - A >= B is converted into A < B + A > B is converted into B < A RETURN The newly created item. @@ -895,10 +895,10 @@ static Item *eq_func_reverse(int oper, Item *a, Item *b) { case '=': return new Item_func_eq(a, b); case '!': return new Item_func_ne(a, b); - case MY_XPATH_LEX_GE: return new Item_func_lt(a, b); - case MY_XPATH_LEX_LE: return new Item_func_gt(a, b); - case MY_XPATH_LEX_GREATER: return new Item_func_le(a, b); - case MY_XPATH_LEX_LESS: return new Item_func_ge(a, b); + case MY_XPATH_LEX_GE: return new Item_func_le(a, b); + case MY_XPATH_LEX_LE: return new Item_func_ge(a, b); + case MY_XPATH_LEX_GREATER: return new Item_func_lt(a, b); + case MY_XPATH_LEX_LESS: return new Item_func_gt(a, b); } return 0; } @@ -951,13 +951,13 @@ static Item *create_comparator(MY_XPATH *xpath, { nodeset= (Item_nodeset_func*) a; scalar= b; - comp= eq_func(oper, scalar, fake); + comp= eq_func(oper, fake, scalar); } else { nodeset= (Item_nodeset_func*) b; scalar= a; - comp= eq_func_reverse(oper, scalar, fake); + comp= eq_func_reverse(oper, fake, scalar); } return new Item_nodeset_to_const_comparator(nodeset, comp, xpath->pxml); } diff --git a/sql/log.cc b/sql/log.cc index 83e190a5c01..620445aecfa 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -424,6 +424,18 @@ bool Log_to_csv_event_handler:: { TABLE *table= general_log.table; + /* + "INSERT INTO general_log" can generate warning sometimes. + Let's reset warnings from previous queries, + otherwise warning list can grow too much, + so thd->query gets spoiled as some point in time, + and mysql_parse() receives a broken query. + QQ: this problem needs to be studied in more details. + Probably it's better to suppress warnings in logging INSERTs at all. + Comment this line and run "cast.test" to see what's happening: + */ + mysql_reset_errors(table->in_use, 1); + /* below should never happen */ if (unlikely(!logger.is_log_tables_initialized)) return FALSE; diff --git a/sql/log_event.cc b/sql/log_event.cc index d99fb9da8f8..f11822d8ad0 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -140,6 +140,21 @@ static void pretty_print_str(IO_CACHE* cache, char* str, int len) } #endif /* MYSQL_CLIENT */ +#ifdef HAVE_purify +static void +valgrind_check_mem(void *ptr, size_t len) +{ + static volatile uchar dummy; + for (volatile uchar *p= (uchar*) ptr ; p != (uchar*) ptr + len ; ++p) + { + int const c = *p; + if (c < 128) + dummy= c + 1; + else + dummy = c - 1; + } +} +#endif #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) @@ -1398,6 +1413,7 @@ bool Query_log_event::write(IO_CACHE* file) /* Store length of status variables */ status_vars_len= (uint) (start-start_of_status); + DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS); int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len); /* @@ -5401,7 +5417,13 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DBUG_ENTER("Rows_log_event::do_add_row_data"); DBUG_PRINT("enter", ("row_data: 0x%lx length: %lu", (ulong) row_data, length)); + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify DBUG_DUMP("row_data", (const char*)row_data, min(length, 32)); +#endif DBUG_ASSERT(m_rows_buf <= m_rows_cur); DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end); @@ -5445,14 +5467,13 @@ int Rows_log_event::do_add_row_data(byte *const row_data, #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) /* - Unpack a row into a record. + Unpack a row into table->record[0]. SYNOPSIS unpack_row() rli Relay log info table Table to unpack into colcnt Number of columns to read from record - record Record where the data should be unpacked row Packed row data cols Pointer to columns data to fill in row_end Pointer to variable that will hold the value of the @@ -5465,6 +5486,11 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DESCRIPTION + The function will always unpack into the table->record[0] + record. This is because there are too many dependencies on + where 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 'arr' and 'bits'; the other parts of the record are left alone. @@ -5483,13 +5509,15 @@ int Rows_log_event::do_add_row_data(byte *const row_data, */ static int unpack_row(RELAY_LOG_INFO *rli, - TABLE *table, uint const colcnt, byte *record, + TABLE *table, uint const colcnt, char const *row, MY_BITMAP const *cols, char const **row_end, ulong *master_reclength, MY_BITMAP* const rw_set, Log_event_type const event_type) { + byte *const record= table->record[0]; + DBUG_ENTER("unpack_row"); DBUG_ASSERT(record && row); - my_ptrdiff_t const offset= record - (byte*) table->record[0]; + DBUG_PRINT("enter", ("row=0x%lx; table->record[0]=0x%lx", row, record)); my_size_t master_null_bytes= table->s->null_bytes; if (colcnt != table->s->fields) @@ -5529,9 +5557,11 @@ unpack_row(RELAY_LOG_INFO *rli, if (bitmap_is_set(cols, field_ptr - begin_ptr)) { - f->move_field_offset(offset); + DBUG_ASSERT(table->record[0] <= f->ptr); + DBUG_ASSERT(f->ptr < table->record[0] + table->s->reclength + (f->pack_length_in_rec() == 0)); + + DBUG_PRINT("info", ("unpacking column '%s' to 0x%lx", f->field_name, f->ptr)); ptr= f->unpack(f->ptr, ptr); - f->move_field_offset(-offset); /* Field...::unpack() cannot return 0 */ DBUG_ASSERT(ptr != NULL); } @@ -5562,13 +5592,10 @@ unpack_row(RELAY_LOG_INFO *rli, for ( ; *field_ptr ; ++field_ptr) { uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG; + Field *const f= *field_ptr; - DBUG_PRINT("debug", ("flags = 0x%x, mask = 0x%x, flags & mask = 0x%x", - (*field_ptr)->flags, mask, - (*field_ptr)->flags & mask)); - - if (event_type == WRITE_ROWS_EVENT && - ((*field_ptr)->flags & mask) == mask) + DBUG_PRINT("info", ("processing column '%s' @ 0x%lx", f->field_name, f->ptr)); + if (event_type == WRITE_ROWS_EVENT && (f->flags & mask) == mask) { slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, "Field `%s` of table `%s`.`%s` " @@ -5578,10 +5605,10 @@ unpack_row(RELAY_LOG_INFO *rli, error = ER_NO_DEFAULT_FOR_FIELD; } else - (*field_ptr)->set_default(); + f->set_default(); } - return error; + DBUG_RETURN(error); } int Rows_log_event::exec_event(st_relay_log_info *rli) @@ -5689,12 +5716,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) We also invalidate the query cache for all the tables, since they will now be changed. */ - TABLE_LIST *ptr; for (ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global) { rli->m_table_map.set_table(ptr->table_id, ptr->table); - rli->touching_table(ptr->db, ptr->table_name, ptr->table_id); } #ifdef HAVE_QUERY_CACHE query_cache.invalidate_locked_for_write(rli->tables_to_lock); @@ -5747,7 +5772,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) 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 <= (const char*)m_rows_end); @@ -5803,9 +5828,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) 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(); - rli->cleanup_context(thd, 1); + rli->cleanup_context(thd, 0); /* rollback at caller in step with sbr */ thd->query_error= 1; DBUG_RETURN(error); } @@ -6047,7 +6073,13 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d", event_len, common_header_len, post_header_len)); + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify DBUG_DUMP("event buffer", buf, event_len); +#endif /* Read the post-header */ const char *post_start= buf + common_header_len; @@ -6230,8 +6262,7 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* We record in the slave's information that the table should be - locked by linking the table into the list of tables to lock, and - tell the RLI that we are touching a table. + locked by linking the table into the list of tables to lock. */ table_list->next_global= table_list->next_local= rli->tables_to_lock; rli->tables_to_lock= table_list; @@ -6423,17 +6454,15 @@ int Write_rows_log_event::do_after_row_operations(TABLE *table, int error) int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { DBUG_ASSERT(table != NULL); DBUG_ASSERT(row_start && row_end); int error; - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->write_set, WRITE_ROWS_EVENT); + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->write_set, WRITE_ROWS_EVENT); bitmap_copy(table->read_set, table->write_set); return error; } @@ -6495,7 +6524,7 @@ copy_extra_record_fields(TABLE *table, my_ptrdiff_t master_fields) { DBUG_PRINT("info", ("Copying to %p " - "from field %d at offset %u " + "from field %ld at offset %u " "to field %d at offset %u", table->record[0], master_fields, master_reclength, @@ -6595,6 +6624,11 @@ replace_record(THD *thd, TABLE *table, 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) { /* We failed to retrieve the duplicate key */ @@ -6649,7 +6683,7 @@ replace_record(THD *thd, TABLE *table, 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 @@ -6728,8 +6762,26 @@ static bool record_compare(TABLE *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. + 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, byte *key) @@ -6749,13 +6801,28 @@ static int find_and_fetch_row(TABLE *table, byte *key) 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]); - DBUG_RETURN(table->file->rnd_pos(table->record[0], table->file->ref)); + 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); } - DBUG_ASSERT(table->record[1]); - /* We need to retrieve all fields */ /* TODO: Move this out from this function to main loop */ table->use_all_columns(); @@ -6765,7 +6832,16 @@ static int find_and_fetch_row(TABLE *table, byte *key) int error; /* We have a key: search the table using the index */ if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE))) - return error; + 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 @@ -6785,6 +6861,14 @@ static int find_and_fetch_row(TABLE *table, byte *key) 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 @@ -6969,8 +7053,8 @@ int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error) int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { int error; DBUG_ASSERT(row_start && row_end); @@ -6980,10 +7064,8 @@ int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, */ DBUG_ASSERT(table->s->fields >= m_width); - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->read_set, DELETE_ROWS_EVENT); + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->read_set, DELETE_ROWS_EVENT); /* 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. @@ -7106,8 +7188,8 @@ int Update_rows_log_event::do_after_row_operations(TABLE *table, int error) int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, TABLE *table, - char const *row_start, - char const **row_end) + char const *const row_start, + char const **const row_end) { int error; DBUG_ASSERT(row_start && row_end); @@ -7117,21 +7199,31 @@ int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli, */ DBUG_ASSERT(table->s->fields >= m_width); + /* + 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 */ - error= unpack_row(rli, - table, m_width, table->record[0], - row_start, &m_cols, row_end, &m_master_reclength, - table->read_set, UPDATE_ROWS_EVENT); - row_start = *row_end; + error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end, + &m_master_reclength, table->read_set, UPDATE_ROWS_EVENT); + store_record(table, record[1]); + char const *next_start = *row_end; /* m_after_image is the after image for the update */ - error= unpack_row(rli, - table, m_width, m_after_image, - row_start, &m_cols, row_end, &m_master_reclength, - table->write_set, UPDATE_ROWS_EVENT); + error= unpack_row(rli, table, m_width, next_start, &m_cols, row_end, + &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT); + 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]", (const char *)table->record[0], table->s->reclength); DBUG_DUMP("m_after_image", (const char *)m_after_image, table->s->reclength); - +#endif /* If we will access rows using the random access method, m_key will diff --git a/sql/log_event.h b/sql/log_event.h index 81ce2f18b4d..c3f015e723c 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -200,8 +200,26 @@ struct sql_ex_info #define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1) #define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN) -/* - Event header offsets; +/* + Max number of possible extra bytes in a replication event compared to a + packet (i.e. a query) sent from client to master; + First, an auxiliary log_event status vars estimation: +*/ +#define MAX_SIZE_LOG_EVENT_STATUS (4 /* flags2 */ + \ + 8 /* sql mode */ + \ + 1 + 1 + 255 /* catalog */ + \ + 4 /* autoinc */ + \ + 6 /* charset */ + \ + MAX_TIME_ZONE_NAME_LENGTH) +#define MAX_LOG_EVENT_HEADER ( /* in order of Query_log_event::write */ \ + LOG_EVENT_HEADER_LEN + /* write_header */ \ + QUERY_HEADER_LEN + /* write_data */ \ + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN + /*write_post_header_for_derived */ \ + MAX_SIZE_LOG_EVENT_STATUS + /* status */ \ + NAME_LEN + 1) + +/* + Event header offsets; these point to places inside the fixed header. */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b0947249439..e30da1f2f0b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -105,6 +105,17 @@ extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; +enum Derivation +{ + DERIVATION_IGNORABLE= 5, + DERIVATION_COERCIBLE= 4, + DERIVATION_SYSCONST= 3, + DERIVATION_IMPLICIT= 2, + DERIVATION_NONE= 1, + DERIVATION_EXPLICIT= 0 +}; + + typedef struct my_locale_st { const char *name; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7e57afeafee..16bfbcbc565 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3160,11 +3160,6 @@ with --log-bin instead."); } if (global_system_variables.binlog_format == BINLOG_FORMAT_UNSPEC) { -#if defined(HAVE_NDB_BINLOG) && defined(HAVE_ROW_BASED_REPLICATION) - if (opt_bin_log && have_ndbcluster == SHOW_OPTION_YES) - global_system_variables.binlog_format= BINLOG_FORMAT_ROW; - else -#endif #if defined(HAVE_ROW_BASED_REPLICATION) global_system_variables.binlog_format= BINLOG_FORMAT_MIXED; #else diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 0071d59242e..0a2d4012af4 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -368,32 +368,33 @@ my_bool rename_in_schema_file(const char *schema, const char *old_name, { char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN]; - strxnmov(old_path, FN_REFLEN-1, mysql_data_home, "/", schema, "/", - old_name, reg_ext, NullS); - (void) unpack_filename(old_path, old_path); - - strxnmov(new_path, FN_REFLEN-1, mysql_data_home, "/", schema, "/", - new_name, reg_ext, NullS); - (void) unpack_filename(new_path, new_path); + build_table_filename(old_path, sizeof(old_path) - 1, + schema, old_name, reg_ext, 0); + build_table_filename(new_path, sizeof(new_path) - 1, + schema, new_name, reg_ext, 0); if (my_rename(old_path, new_path, MYF(MY_WME))) return 1; /* check if arc_dir exists */ - strxnmov(arc_path, FN_REFLEN-1, mysql_data_home, "/", schema, "/arc", NullS); - (void) unpack_filename(arc_path, arc_path); + build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0); if (revision > 0 && !access(arc_path, F_OK)) { + char old_name_buf[FN_REFLEN], new_name_buf[FN_REFLEN]; ulonglong limit= ((revision > num_view_backups) ? revision - num_view_backups : 0); + + VOID(tablename_to_filename(old_name, old_name_buf, sizeof(old_name_buf))); + VOID(tablename_to_filename(new_name, new_name_buf, sizeof(new_name_buf))); + for (; revision > limit ; revision--) { my_snprintf(old_path, FN_REFLEN, "%s/%s%s-%04lu", - arc_path, old_name, reg_ext, (ulong)revision); + arc_path, old_name_buf, reg_ext, (ulong) revision); (void) unpack_filename(old_path, old_path); my_snprintf(new_path, FN_REFLEN, "%s/%s%s-%04lu", - arc_path, new_name, reg_ext, (ulong)revision); + arc_path, new_name_buf, reg_ext, (ulong) revision); (void) unpack_filename(new_path, new_path); my_rename(old_path, new_path, MYF(0)); } diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 66e2aa1c31c..2b034d50d6a 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -962,7 +962,7 @@ bool load_master_data(THD* thd) Cancel the previous START SLAVE UNTIL, as the fact to download a new copy logically makes UNTIL irrelevant. */ - clear_until_condition(&active_mi->rli); + active_mi->rli.clear_until_condition(); /* No need to update rli.event* coordinates, they will be when the slave diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 5a74fd58755..3a0fca4dfa5 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -39,6 +39,8 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd) m_start_pos.m_file_pos= log_info.pos; begin_trans(m_thd); + + thd->set_current_stmt_binlog_row_based(); } injector::transaction::~transaction() diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc new file mode 100644 index 00000000000..c89c8aa131e --- /dev/null +++ b/sql/rpl_mi.cc @@ -0,0 +1,386 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> // For HAVE_REPLICATION +#include "mysql_priv.h" +#include <my_dir.h> + +#include "rpl_mi.h" + +#ifdef HAVE_REPLICATION + + +// Defined in slave.cc +int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); +int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, + const char *default_val); + +MASTER_INFO::MASTER_INFO() + :ssl(0), fd(-1), io_thd(0), inited(0), + abort_slave(0),slave_running(0), slave_run_id(0) +{ + host[0] = 0; user[0] = 0; password[0] = 0; + ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; + ssl_cipher[0]= 0; ssl_key[0]= 0; + + bzero((char*) &file, sizeof(file)); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); +} + +MASTER_INFO::~MASTER_INFO() +{ + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); +} + + +void init_master_info_with_options(MASTER_INFO* mi) +{ + DBUG_ENTER("init_master_info_with_options"); + + mi->master_log_name[0] = 0; + mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number + + if (master_host) + strmake(mi->host, master_host, sizeof(mi->host) - 1); + if (master_user) + strmake(mi->user, master_user, sizeof(mi->user) - 1); + if (master_password) + strmake(mi->password, master_password, MAX_PASSWORD_LENGTH); + mi->port = master_port; + mi->connect_retry = master_connect_retry; + + mi->ssl= master_ssl; + if (master_ssl_ca) + strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1); + if (master_ssl_capath) + strmake(mi->ssl_capath, master_ssl_capath, sizeof(mi->ssl_capath)-1); + if (master_ssl_cert) + strmake(mi->ssl_cert, master_ssl_cert, sizeof(mi->ssl_cert)-1); + if (master_ssl_cipher) + strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1); + if (master_ssl_key) + strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); + DBUG_VOID_RETURN; +} + + +#define LINES_IN_MASTER_INFO_WITH_SSL 14 + + +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, + bool abort_if_no_master_info_file, + int thread_mask) +{ + int fd,error; + char fname[FN_REFLEN+128]; + DBUG_ENTER("init_master_info"); + + if (mi->inited) + { + /* + We have to reset read position of relay-log-bin as we may have + already been reading from 'hotlog' when the slave was stopped + last time. If this case pos_in_file would be set and we would + get a crash when trying to read the signature for the binary + relay log. + + We only rewind the read position if we are starting the SQL + thread. The handle_slave_sql thread assumes that the read + position is at the beginning of the file, and will read the + "signature" and then fast-forward to the last position read. + */ + if (thread_mask & SLAVE_SQL) + { + my_b_seek(mi->rli.cur_log, (my_off_t) 0); + } + DBUG_RETURN(0); + } + + mi->mysql=0; + mi->file_id=1; + fn_format(fname, master_info_fname, mysql_data_home, "", 4+32); + + /* + We need a mutex while we are changing master info parameters to + keep other threads from reading bogus info + */ + + pthread_mutex_lock(&mi->data_lock); + fd = mi->fd; + + /* does master.info exist ? */ + + if (access(fname,F_OK)) + { + if (abort_if_no_master_info_file) + { + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(0); + } + /* + if someone removed the file from underneath our feet, just close + the old descriptor and re-create the old file + */ + if (fd >= 0) + my_close(fd, MYF(MY_WME)); + if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + { + sql_print_error("Failed to create a new master info file (\ +file '%s', errno %d)", fname, my_errno); + goto err; + } + if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on master info file (\ +file '%s')", fname); + goto err; + } + + mi->fd = fd; + init_master_info_with_options(mi); + + } + else // file exists + { + if (fd >= 0) + reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0); + else + { + if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) + { + sql_print_error("Failed to open the existing master info file (\ +file '%s', errno %d)", fname, my_errno); + goto err; + } + if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, + 0, MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on master info file (\ +file '%s')", fname); + goto err; + } + } + + mi->fd = fd; + int port, connect_retry, master_log_pos, ssl= 0, lines; + char *first_non_digit; + + /* + Starting from 4.1.x master.info has new format. Now its + first line contains number of lines in file. By reading this + number we will be always distinguish to which version our + master.info corresponds to. We can't simply count lines in + file since versions before 4.1.x could generate files with more + lines than needed. + If first line doesn't contain a number or contain number less than + 14 then such file is treated like file from pre 4.1.1 version. + There is no ambiguity when reading an old master.info, as before + 4.1.1, the first line contained the binlog's name, which is either + empty or has an extension (contains a '.'), so can't be confused + with an integer. + + So we're just reading first line and trying to figure which version + is this. + */ + + /* + The first row is temporarily stored in mi->master_log_name, + if it is line count and not binlog name (new format) it will be + overwritten by the second row later. + */ + if (init_strvar_from_file(mi->master_log_name, + sizeof(mi->master_log_name), &mi->file, + "")) + goto errwithmsg; + + lines= strtoul(mi->master_log_name, &first_non_digit, 10); + + if (mi->master_log_name[0]!='\0' && + *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL) + { // Seems to be new format + if (init_strvar_from_file(mi->master_log_name, + sizeof(mi->master_log_name), &mi->file, "")) + goto errwithmsg; + } + else + lines= 7; + + if (init_intvar_from_file(&master_log_pos, &mi->file, 4) || + init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, + master_host) || + init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, + master_user) || + init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1, + &mi->file, master_password) || + init_intvar_from_file(&port, &mi->file, master_port) || + init_intvar_from_file(&connect_retry, &mi->file, + master_connect_retry)) + goto errwithmsg; + + /* + If file has ssl part use it even if we have server without + SSL support. But these option will be ignored later when + slave will try connect to master, so in this case warning + is printed. + */ + if (lines >= LINES_IN_MASTER_INFO_WITH_SSL && + (init_intvar_from_file(&ssl, &mi->file, master_ssl) || + init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca), + &mi->file, master_ssl_ca) || + init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath), + &mi->file, master_ssl_capath) || + init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert), + &mi->file, master_ssl_cert) || + init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher), + &mi->file, master_ssl_cipher) || + init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key), + &mi->file, master_ssl_key))) + goto errwithmsg; +#ifndef HAVE_OPENSSL + if (ssl) + sql_print_warning("SSL information in the master info file " + "('%s') are ignored because this MySQL slave was compiled " + "without SSL support.", fname); +#endif /* HAVE_OPENSSL */ + + /* + This has to be handled here as init_intvar_from_file can't handle + my_off_t types + */ + mi->master_log_pos= (my_off_t) master_log_pos; + mi->port= (uint) port; + mi->connect_retry= (uint) connect_retry; + mi->ssl= (my_bool) ssl; + } + DBUG_PRINT("master_info",("log_file_name: %s position: %ld", + mi->master_log_name, + (ulong) mi->master_log_pos)); + + mi->rli.mi = mi; + if (init_relay_log_info(&mi->rli, slave_info_fname)) + goto err; + + mi->inited = 1; + // now change cache READ -> WRITE - must do this before flush_master_info + reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1); + if ((error=test(flush_master_info(mi, 1)))) + sql_print_error("Failed to flush master info file"); + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(error); + +errwithmsg: + sql_print_error("Error reading master configuration"); + +err: + if (fd >= 0) + { + my_close(fd, MYF(0)); + end_io_cache(&mi->file); + } + mi->fd= -1; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(1); +} + + +/* + RETURN + 2 - flush relay log failed + 1 - flush master info failed + 0 - all ok +*/ +int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache) +{ + IO_CACHE* file = &mi->file; + char lbuf[22]; + DBUG_ENTER("flush_master_info"); + DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos)); + + /* + Flush the relay log to disk. If we don't do it, then the relay log while + have some part (its last kilobytes) in memory only, so if the slave server + dies now, with, say, from master's position 100 to 150 in memory only (not + on disk), and with position 150 in master.info, then when the slave + restarts, the I/O thread will fetch binlogs from 150, so in the relay log + we will have "[0, 100] U [150, infinity[" and nobody will notice it, so the + SQL thread will jump from 100 to 150, and replication will silently break. + + When we come to this place in code, relay log may or not be initialized; + the caller is responsible for setting 'flush_relay_log_cache' accordingly. + */ + if (flush_relay_log_cache && + flush_io_cache(mi->rli.relay_log.get_log_file())) + DBUG_RETURN(2); + + /* + We flushed the relay log BEFORE the master.info file, because if we crash + now, we will get a duplicate event in the relay log at restart. If we + flushed in the other order, we would get a hole in the relay log. + And duplicate is better than hole (with a duplicate, in later versions we + can add detection and scrap one event; with a hole there's nothing we can + do). + */ + + /* + In certain cases this code may create master.info files that seems + corrupted, because of extra lines filled with garbage in the end + file (this happens if new contents take less space than previous + contents of file). But because of number of lines in the first line + of file we don't care about this garbage. + */ + + my_b_seek(file, 0L); + my_b_printf(file, "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n", + LINES_IN_MASTER_INFO_WITH_SSL, + mi->master_log_name, llstr(mi->master_log_pos, lbuf), + mi->host, mi->user, + mi->password, mi->port, mi->connect_retry, + (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert, + mi->ssl_cipher, mi->ssl_key); + DBUG_RETURN(-flush_io_cache(file)); +} + + +void end_master_info(MASTER_INFO* mi) +{ + DBUG_ENTER("end_master_info"); + + if (!mi->inited) + DBUG_VOID_RETURN; + end_relay_log_info(&mi->rli); + if (mi->fd >= 0) + { + end_io_cache(&mi->file); + (void)my_close(mi->fd, MYF(MY_WME)); + mi->fd = -1; + } + mi->inited = 0; + + DBUG_VOID_RETURN; +} + + +#endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h new file mode 100644 index 00000000000..f0a7d6681fe --- /dev/null +++ b/sql/rpl_mi.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef RPL_MI_H +#define RPL_MI_H + +#ifdef HAVE_REPLICATION + +/***************************************************************************** + + Replication IO Thread + + MASTER_INFO contains: + - information about how to connect to a master + - current master log name + - current master log offset + - misc control variables + + MASTER_INFO is initialized once from the master.info file if such + exists. Otherwise, data members corresponding to master.info fields + are initialized with defaults specified by master-* options. The + initialization is done through init_master_info() call. + + The format of master.info file: + + log_name + log_pos + master_host + master_user + master_pass + master_port + master_connect_retry + + To write out the contents of master.info file to disk ( needed every + time we read and queue data from the master ), a call to + flush_master_info() is required. + + To clean up, call end_master_info() + +*****************************************************************************/ + +class MASTER_INFO +{ + public: + MASTER_INFO(); + ~MASTER_INFO(); + + /* the variables below are needed because we can change masters on the fly */ + char master_log_name[FN_REFLEN]; + char host[HOSTNAME_LENGTH+1]; + char user[USERNAME_LENGTH+1]; + char password[MAX_PASSWORD_LENGTH+1]; + my_bool ssl; // enables use of SSL connection if true + char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN]; + char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN]; + + my_off_t master_log_pos; + File fd; // we keep the file open, so we need to remember the file pointer + IO_CACHE file; + + pthread_mutex_t data_lock,run_lock; + pthread_cond_t data_cond,start_cond,stop_cond; + THD *io_thd; + MYSQL* mysql; + uint32 file_id; /* for 3.23 load data infile */ + RELAY_LOG_INFO rli; + uint port; + uint connect_retry; +#ifndef DBUG_OFF + int events_till_disconnect; +#endif + bool inited; + volatile bool abort_slave; + volatile uint slave_running; + volatile ulong slave_run_id; + /* + The difference in seconds between the clock of the master and the clock of + the slave (second - first). It must be signed as it may be <0 or >0. + clock_diff_with_master is computed when the I/O thread starts; for this the + I/O thread does a SELECT UNIX_TIMESTAMP() on the master. + "how late the slave is compared to the master" is computed like this: + clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master + + */ + long clock_diff_with_master; +}; + +void init_master_info_with_options(MASTER_INFO* mi); +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, + bool abort_if_no_master_info_file, + int thread_mask); +void end_master_info(MASTER_INFO* mi); +int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache); + +#endif /* HAVE_REPLICATION */ +#endif /* RPL_MI_H */ diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc new file mode 100644 index 00000000000..f01fc5d1c9e --- /dev/null +++ b/sql/rpl_rli.cc @@ -0,0 +1,1112 @@ +/* Copyright (C) 2000-2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" + +#include "rpl_rli.h" +#include <my_dir.h> // For MY_STAT +#include "sql_repl.h" // For check_binlog_magic + +static int count_relay_log_space(RELAY_LOG_INFO* rli); + +// Defined in slave.cc +int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); +int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, + const char *default_val); + + +st_relay_log_info::st_relay_log_info() + :no_storage(FALSE), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), + cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), + ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0), + abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0), + inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), + until_log_pos(0), retried_trans(0), + tables_to_lock(0), tables_to_lock_count(0), + unsafe_to_stop_at(0) +{ + DBUG_ENTER("st_relay_log_info::st_relay_log_info"); + + group_relay_log_name[0]= event_relay_log_name[0]= + group_master_log_name[0]= 0; + last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0; + bzero((char*) &info_file, sizeof(info_file)); + bzero((char*) &cache_buf, sizeof(cache_buf)); + cached_charset_invalidate(); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); + pthread_cond_init(&log_space_cond, NULL); + relay_log.init_pthread_objects(); + DBUG_VOID_RETURN; +} + + +st_relay_log_info::~st_relay_log_info() +{ + DBUG_ENTER("st_relay_log_info::~st_relay_log_info"); + + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_mutex_destroy(&log_space_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); + pthread_cond_destroy(&log_space_cond); + relay_log.cleanup(); + DBUG_VOID_RETURN; +} + + +int init_relay_log_info(RELAY_LOG_INFO* rli, + const char* info_fname) +{ + char fname[FN_REFLEN+128]; + int info_fd; + const char* msg = 0; + int error = 0; + DBUG_ENTER("init_relay_log_info"); + DBUG_ASSERT(!rli->no_storage); // Don't init if there is no storage + + if (rli->inited) // Set if this function called + DBUG_RETURN(0); + fn_format(fname, info_fname, mysql_data_home, "", 4+32); + pthread_mutex_lock(&rli->data_lock); + info_fd = rli->info_fd; + rli->cur_log_fd = -1; + rli->slave_skip_counter=0; + rli->abort_pos_wait=0; + rli->log_space_limit= relay_log_space_limit; + rli->log_space_total= 0; + rli->tables_to_lock= 0; + rli->tables_to_lock_count= 0; + + /* + The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE. + Note that the I/O thread flushes it to disk after writing every + event, in flush_master_info(mi, 1). + */ + + /* + For the maximum log size, we choose max_relay_log_size if it is + non-zero, max_binlog_size otherwise. If later the user does SET + GLOBAL on one of these variables, fix_max_binlog_size and + fix_max_relay_log_size will reconsider the choice (for example + if the user changes max_relay_log_size to zero, we have to + switch to using max_binlog_size for the relay log) and update + rli->relay_log.max_size (and mysql_bin_log.max_size). + */ + { + char buf[FN_REFLEN]; + const char *ln; + static bool name_warning_sent= 0; + ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin", + 1, buf); + /* We send the warning only at startup, not after every RESET SLAVE */ + if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent) + { + /* + User didn't give us info to name the relay log index file. + Picking `hostname`-relay-bin.index like we do, causes replication to + fail if this slave's hostname is changed later. So, we would like to + instead require a name. But as we don't want to break many existing + setups, we only give warning, not error. + */ + sql_print_warning("Neither --relay-log nor --relay-log-index were used;" + " so replication " + "may break when this MySQL server acts as a " + "slave and has his hostname changed!! Please " + "use '--relay-log=%s' to avoid this problem.", ln); + name_warning_sent= 1; + } + /* + note, that if open() fails, we'll still have index file open + but a destructor will take care of that + */ + if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln) || + rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0, + (max_relay_log_size ? max_relay_log_size : + max_binlog_size), 1)) + { + pthread_mutex_unlock(&rli->data_lock); + sql_print_error("Failed in open_log() called from init_relay_log_info()"); + DBUG_RETURN(1); + } + } + + /* if file does not exist */ + if (access(fname,F_OK)) + { + /* + If someone removed the file from underneath our feet, just close + the old descriptor and re-create the old file + */ + if (info_fd >= 0) + my_close(info_fd, MYF(MY_WME)); + if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0) + { + sql_print_error("Failed to create a new relay log info file (\ +file '%s', errno %d)", fname, my_errno); + msg= current_thd->net.last_error; + goto err; + } + if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on relay log info file '%s'", + fname); + msg= current_thd->net.last_error; + goto err; + } + + /* Init relay log with first entry in the relay index file */ + if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, + &msg, 0)) + { + sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)"); + goto err; + } + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; + rli->info_fd= info_fd; + } + else // file exists + { + if (info_fd >= 0) + reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); + else + { + int error=0; + if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0) + { + sql_print_error("\ +Failed to open the existing relay log info file '%s' (errno %d)", + fname, my_errno); + error= 1; + } + else if (init_io_cache(&rli->info_file, info_fd, + IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) + { + sql_print_error("Failed to create a cache on relay log info file '%s'", + fname); + error= 1; + } + if (error) + { + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd= -1; + rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); + } + } + + rli->info_fd = info_fd; + int relay_log_pos, master_log_pos; + if (init_strvar_from_file(rli->group_relay_log_name, + sizeof(rli->group_relay_log_name), + &rli->info_file, "") || + init_intvar_from_file(&relay_log_pos, + &rli->info_file, BIN_LOG_HEADER_SIZE) || + init_strvar_from_file(rli->group_master_log_name, + sizeof(rli->group_master_log_name), + &rli->info_file, "") || + init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) + { + msg="Error reading slave log configuration"; + goto err; + } + strmake(rli->event_relay_log_name,rli->group_relay_log_name, + sizeof(rli->event_relay_log_name)-1); + rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos; + rli->group_master_log_pos= master_log_pos; + + if (init_relay_log_pos(rli, + rli->group_relay_log_name, + rli->group_relay_log_pos, + 0 /* no data lock*/, + &msg, 0)) + { + char llbuf[22]; + sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)", + rli->group_relay_log_name, + llstr(rli->group_relay_log_pos, llbuf)); + goto err; + } + } + +#ifndef DBUG_OFF + { + char llbuf1[22], llbuf2[22]; + DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", + llstr(my_b_tell(rli->cur_log),llbuf1), + llstr(rli->event_relay_log_pos,llbuf2))); + DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); + } +#endif + + /* + Now change the cache from READ to WRITE - must do this + before flush_relay_log_info + */ + reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); + if ((error= flush_relay_log_info(rli))) + sql_print_error("Failed to flush relay log info file"); + if (count_relay_log_space(rli)) + { + msg="Error counting relay log space"; + goto err; + } + rli->inited= 1; + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); + +err: + sql_print_error(msg); + end_io_cache(&rli->info_file); + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd= -1; + rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); +} + + +static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) +{ + MY_STAT s; + DBUG_ENTER("add_relay_log"); + if (!my_stat(linfo->log_file_name,&s,MYF(0))) + { + sql_print_error("log %s listed in the index, but failed to stat", + linfo->log_file_name); + DBUG_RETURN(1); + } + rli->log_space_total += s.st_size; +#ifndef DBUG_OFF + char buf[22]; + DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); +#endif + DBUG_RETURN(0); +} + + +static int count_relay_log_space(RELAY_LOG_INFO* rli) +{ + LOG_INFO linfo; + DBUG_ENTER("count_relay_log_space"); + rli->log_space_total= 0; + if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) + { + sql_print_error("Could not find first log while counting relay log space"); + DBUG_RETURN(1); + } + do + { + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + } while (!rli->relay_log.find_next_log(&linfo, 1)); + /* + As we have counted everything, including what may have written in a + preceding write, we must reset bytes_written, or we may count some space + twice. + */ + rli->relay_log.reset_bytes_written(); + DBUG_RETURN(0); +} + + +void st_relay_log_info::clear_slave_error() +{ + DBUG_ENTER("clear_slave_error"); + + /* Clear the errors displayed by SHOW SLAVE STATUS */ + last_slave_error[0]= 0; + last_slave_errno= 0; + DBUG_VOID_RETURN; +} + +/* + Reset UNTIL condition for RELAY_LOG_INFO + + SYNOPSYS + clear_until_condition() + rli - RELAY_LOG_INFO structure where UNTIL condition should be reset + */ + +void st_relay_log_info::clear_until_condition() +{ + DBUG_ENTER("clear_until_condition"); + + until_condition= RELAY_LOG_INFO::UNTIL_NONE; + until_log_name[0]= 0; + until_log_pos= 0; + DBUG_VOID_RETURN; +} + + +/* + Open the given relay log + + SYNOPSIS + init_relay_log_pos() + rli Relay information (will be initialized) + log Name of relay log file to read from. NULL = First log + pos Position in relay log file + need_data_lock Set to 1 if this functions should do mutex locks + errmsg Store pointer to error message here + look_for_description_event + 1 if we should look for such an event. We only need + this when the SQL thread starts and opens an existing + relay log and has to execute it (possibly from an + offset >4); then we need to read the first event of + the relay log to be able to parse the events we have + to execute. + + DESCRIPTION + - Close old open relay log files. + - If we are using the same relay log as the running IO-thread, then set + rli->cur_log to point to the same IO_CACHE entry. + - If not, open the 'log' binary file. + + TODO + - check proper initialization of group_master_log_name/group_master_log_pos + + RETURN VALUES + 0 ok + 1 error. errmsg is set to point to the error message +*/ + +int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, + ulonglong pos, bool need_data_lock, + const char** errmsg, + bool look_for_description_event) +{ + DBUG_ENTER("init_relay_log_pos"); + DBUG_PRINT("info", ("pos=%lu", pos)); + + *errmsg=0; + pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); + + if (need_data_lock) + pthread_mutex_lock(&rli->data_lock); + + /* + Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER + is, too, and init_slave() too; these 2 functions allocate a description + event in init_relay_log_pos, which is not freed by the terminating SQL slave + thread as that thread is not started by these functions. So we have to free + the description_event here, in case, so that there is no memory leak in + running, say, CHANGE MASTER. + */ + delete rli->relay_log.description_event_for_exec; + /* + By default the relay log is in binlog format 3 (4.0). + Even if format is 4, this will work enough to read the first event + (Format_desc) (remember that format 4 is just lenghtened compared to format + 3; format 3 is a prefix of format 4). + */ + rli->relay_log.description_event_for_exec= new + Format_description_log_event(3); + + pthread_mutex_lock(log_lock); + + /* Close log file and free buffers if it's already open */ + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + } + + rli->group_relay_log_pos = rli->event_relay_log_pos = pos; + + /* + Test to see if the previous run was with the skip of purging + If yes, we do not purge when we restart + */ + if (rli->relay_log.find_log_pos(&rli->linfo, NullS, 1)) + { + *errmsg="Could not find first log during relay log initialization"; + goto err; + } + + if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1)) + { + *errmsg="Could not find target log during relay log initialization"; + goto err; + } + strmake(rli->group_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name,rli->linfo.log_file_name, + sizeof(rli->event_relay_log_name)-1); + if (rli->relay_log.is_active(rli->linfo.log_file_name)) + { + /* + The IO thread is using this log file. + In this case, we will use the same IO_CACHE pointer to + read data as the IO thread is using to write data. + */ + my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0); + if (check_binlog_magic(rli->cur_log,errmsg)) + goto err; + rli->cur_log_old_open_count=rli->relay_log.get_open_count(); + } + else + { + /* + Open the relay log and set rli->cur_log to point at this one + */ + if ((rli->cur_log_fd=open_binlog(&rli->cache_buf, + rli->linfo.log_file_name,errmsg)) < 0) + goto err; + rli->cur_log = &rli->cache_buf; + } + /* + In all cases, check_binlog_magic() has been called so we're at offset 4 for + sure. + */ + if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */ + { + Log_event* ev; + while (look_for_description_event) + { + /* + Read the possible Format_description_log_event; if position + was 4, no need, it will be read naturally. + */ + DBUG_PRINT("info",("looking for a Format_description_log_event")); + + if (my_b_tell(rli->cur_log) >= pos) + break; + + /* + Because of we have rli->data_lock and log_lock, we can safely read an + event + */ + if (!(ev=Log_event::read_log_event(rli->cur_log,0, + rli->relay_log.description_event_for_exec))) + { + DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d", + rli->cur_log->error)); + if (rli->cur_log->error) /* not EOF */ + { + *errmsg= "I/O error reading event at position 4"; + goto err; + } + break; + } + else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT) + { + DBUG_PRINT("info",("found Format_description_log_event")); + delete rli->relay_log.description_event_for_exec; + rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev; + /* + As ev was returned by read_log_event, it has passed is_valid(), so + my_malloc() in ctor worked, no need to check again. + */ + /* + Ok, we found a Format_description event. But it is not sure that this + describes the whole relay log; indeed, one can have this sequence + (starting from position 4): + Format_desc (of slave) + Rotate (of master) + Format_desc (of master) + So the Format_desc which really describes the rest of the relay log + is the 3rd event (it can't be further than that, because we rotate + the relay log when we queue a Rotate event from the master). + But what describes the Rotate is the first Format_desc. + So what we do is: + go on searching for Format_description events, until you exceed the + position (argument 'pos') or until you find another event than Rotate + or Format_desc. + */ + } + else + { + DBUG_PRINT("info",("found event of another type=%d", + ev->get_type_code())); + look_for_description_event= (ev->get_type_code() == ROTATE_EVENT); + delete ev; + } + } + my_b_seek(rli->cur_log,(off_t)pos); +#ifndef DBUG_OFF + { + char llbuf1[22], llbuf2[22]; + DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", + llstr(my_b_tell(rli->cur_log),llbuf1), + llstr(rli->event_relay_log_pos,llbuf2))); + } +#endif + + } + +err: + /* + If we don't purge, we can't honour relay_log_space_limit ; + silently discard it + */ + if (!relay_log_purge) + rli->log_space_limit= 0; + pthread_cond_broadcast(&rli->data_cond); + + pthread_mutex_unlock(log_lock); + + if (need_data_lock) + pthread_mutex_unlock(&rli->data_lock); + if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg) + *errmsg= "Invalid Format_description log event; could be out of memory"; + + DBUG_RETURN ((*errmsg) ? 1 : 0); +} + + +/* + Waits until the SQL thread reaches (has executed up to) the + log/position or timed out. + + SYNOPSIS + wait_for_pos() + thd client thread that sent SELECT MASTER_POS_WAIT + log_name log name to wait for + log_pos position to wait for + timeout timeout in seconds before giving up waiting + + NOTES + timeout is longlong whereas it should be ulong ; but this is + to catch if the user submitted a negative timeout. + + RETURN VALUES + -2 improper arguments (log_pos<0) + or slave not running, or master info changed + during the function's execution, + or client thread killed. -2 is translated to NULL by caller + -1 timed out + >=0 number of log events the function had to wait + before reaching the desired log/position + */ + +int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, + longlong log_pos, + longlong timeout) +{ + int event_count = 0; + ulong init_abort_pos_wait; + int error=0; + struct timespec abstime; // for timeout checking + const char *msg; + DBUG_ENTER("st_relay_log_info::wait_for_pos"); + + if (!inited) + DBUG_RETURN(-1); + + DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu", + log_name->c_ptr(), (ulong) log_pos, (ulong) timeout)); + + set_timespec(abstime,timeout); + pthread_mutex_lock(&data_lock); + msg= thd->enter_cond(&data_cond, &data_lock, + "Waiting for the slave SQL thread to " + "advance position"); + /* + This function will abort when it notices that some CHANGE MASTER or + RESET MASTER has changed the master info. + To catch this, these commands modify abort_pos_wait ; We just monitor + abort_pos_wait and see if it has changed. + Why do we have this mechanism instead of simply monitoring slave_running + in the loop (we do this too), as CHANGE MASTER/RESET SLAVE require that + the SQL thread be stopped? + This is becasue if someones does: + STOP SLAVE;CHANGE MASTER/RESET SLAVE; START SLAVE; + the change may happen very quickly and we may not notice that + slave_running briefly switches between 1/0/1. + */ + init_abort_pos_wait= abort_pos_wait; + + /* + We'll need to + handle all possible log names comparisons (e.g. 999 vs 1000). + We use ulong for string->number conversion ; this is no + stronger limitation than in find_uniq_filename in sql/log.cc + */ + ulong log_name_extension; + char log_name_tmp[FN_REFLEN]; //make a char[] from String + + strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1)); + + char *p= fn_ext(log_name_tmp); + char *p_end; + if (!*p || log_pos<0) + { + error= -2; //means improper arguments + goto err; + } + // Convert 0-3 to 4 + log_pos= max(log_pos, BIN_LOG_HEADER_SIZE); + /* p points to '.' */ + log_name_extension= strtoul(++p, &p_end, 10); + /* + p_end points to the first invalid character. + If it equals to p, no digits were found, error. + If it contains '\0' it means conversion went ok. + */ + if (p_end==p || *p_end) + { + error= -2; + goto err; + } + + /* The "compare and wait" main loop */ + while (!thd->killed && + init_abort_pos_wait == abort_pos_wait && + slave_running) + { + bool pos_reached; + int cmp_result= 0; + + DBUG_PRINT("info", + ("init_abort_pos_wait: %ld abort_pos_wait: %ld", + init_abort_pos_wait, abort_pos_wait)); + DBUG_PRINT("info",("group_master_log_name: '%s' pos: %lu", + group_master_log_name, (ulong) group_master_log_pos)); + + /* + group_master_log_name can be "", if we are just after a fresh + replication start or after a CHANGE MASTER TO MASTER_HOST/PORT + (before we have executed one Rotate event from the master) or + (rare) if the user is doing a weird slave setup (see next + paragraph). If group_master_log_name is "", we assume we don't + have enough info to do the comparison yet, so we just wait until + more data. In this case master_log_pos is always 0 except if + somebody (wrongly) sets this slave to be a slave of itself + without using --replicate-same-server-id (an unsupported + configuration which does nothing), then group_master_log_pos + will grow and group_master_log_name will stay "". + */ + if (*group_master_log_name) + { + char *basename= (group_master_log_name + + dirname_length(group_master_log_name)); + /* + First compare the parts before the extension. + Find the dot in the master's log basename, + and protect against user's input error : + if the names do not match up to '.' included, return error + */ + char *q= (char*)(fn_ext(basename)+1); + if (strncmp(basename, log_name_tmp, (int)(q-basename))) + { + error= -2; + break; + } + // Now compare extensions. + char *q_end; + ulong group_master_log_name_extension= strtoul(q, &q_end, 10); + if (group_master_log_name_extension < log_name_extension) + cmp_result= -1 ; + else + cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ; + + pos_reached= ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) || + cmp_result > 0); + if (pos_reached || thd->killed) + break; + } + + //wait for master update, with optional timeout. + + DBUG_PRINT("info",("Waiting for master update")); + /* + We are going to pthread_cond_(timed)wait(); if the SQL thread stops it + will wake us up. + */ + if (timeout > 0) + { + /* + Note that pthread_cond_timedwait checks for the timeout + before for the condition ; i.e. it returns ETIMEDOUT + if the system time equals or exceeds the time specified by abstime + before the condition variable is signaled or broadcast, _or_ if + the absolute time specified by abstime has already passed at the time + of the call. + For that reason, pthread_cond_timedwait will do the "timeoutting" job + even if its condition is always immediately signaled (case of a loaded + master). + */ + error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime); + } + else + pthread_cond_wait(&data_cond, &data_lock); + DBUG_PRINT("info",("Got signal of master update or timed out")); + if (error == ETIMEDOUT || error == ETIME) + { + error= -1; + break; + } + error=0; + event_count++; + DBUG_PRINT("info",("Testing if killed or SQL thread not running")); + } + +err: + thd->exit_cond(msg); + DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \ +improper_arguments: %d timed_out: %d", + thd->killed_errno(), + (int) (init_abort_pos_wait != abort_pos_wait), + (int) slave_running, + (int) (error == -2), + (int) (error == -1))); + if (thd->killed || init_abort_pos_wait != abort_pos_wait || + !slave_running) + { + error= -2; + } + DBUG_RETURN( error ? error : event_count ); +} + + +void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, + bool skip_lock) +{ + DBUG_ENTER("st_relay_log_info::inc_group_relay_log_pos"); + + if (!skip_lock) + pthread_mutex_lock(&data_lock); + inc_event_relay_log_pos(); + group_relay_log_pos= event_relay_log_pos; + strmake(group_relay_log_name,event_relay_log_name, + sizeof(group_relay_log_name)-1); + + notify_group_relay_log_name_update(); + + /* + If the slave does not support transactions and replicates a transaction, + users should not trust group_master_log_pos (which they can display with + SHOW SLAVE STATUS or read from relay-log.info), because to compute + group_master_log_pos the slave relies on log_pos stored in the master's + binlog, but if we are in a master's transaction these positions are always + the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does + not advance as it should on the non-transactional slave (it advances by + big leaps, whereas it should advance by small leaps). + */ + /* + In 4.x we used the event's len to compute the positions here. This is + wrong if the event was 3.23/4.0 and has been converted to 5.0, because + then the event's len is not what is was in the master's binlog, so this + will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0 + replication: Exec_master_log_pos is wrong). Only way to solve this is to + have the original offset of the end of the event the relay log. This is + what we do in 5.0: log_pos has become "end_log_pos" (because the real use + of log_pos in 4.0 was to compute the end_log_pos; so better to store + end_log_pos instead of begin_log_pos. + If we had not done this fix here, the problem would also have appeared + when the slave and master are 5.0 but with different event length (for + example the slave is more recent than the master and features the event + UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in + SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this + value which would lead to badly broken replication. + Even the relay_log_pos will be corrupted in this case, because the len is + the relay log is not "val". + With the end_log_pos solution, we avoid computations involving lengthes. + */ + DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu", + (long) log_pos, (long) group_master_log_pos)); + if (log_pos) // 3.23 binlogs don't have log_posx + { + group_master_log_pos= log_pos; + } + pthread_cond_broadcast(&data_cond); + if (!skip_lock) + pthread_mutex_unlock(&data_lock); + DBUG_VOID_RETURN; +} + + +void st_relay_log_info::close_temporary_tables() +{ + TABLE *table,*next; + DBUG_ENTER("st_relay_log_info::close_temporary_tables"); + + for (table=save_temporary_tables ; table ; table=next) + { + next=table->next; + /* + Don't ask for disk deletion. For now, anyway they will be deleted when + slave restarts, but it is a better intention to not delete them. + */ + DBUG_PRINT("info", ("table: %p", table)); + close_temporary(table, 1, 0); + } + save_temporary_tables= 0; + slave_open_temp_tables= 0; + DBUG_VOID_RETURN; +} + +/* + purge_relay_logs() + + NOTES + Assumes to have a run lock on rli and that no slave thread are running. +*/ + +int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, + const char** errmsg) +{ + int error=0; + DBUG_ENTER("purge_relay_logs"); + + /* + Even if rli->inited==0, we still try to empty rli->master_log_* variables. + Indeed, rli->inited==0 does not imply that they already are empty. + It could be that slave's info initialization partly succeeded : + for example if relay-log.info existed but *relay-bin*.* + have been manually removed, init_relay_log_info reads the old + relay-log.info and fills rli->master_log_*, then init_relay_log_info + checks for the existence of the relay log, this fails and + init_relay_log_info leaves rli->inited to 0. + In that pathological case, rli->master_log_pos* will be properly reinited + at the next START SLAVE (as RESET SLAVE or CHANGE + MASTER, the callers of purge_relay_logs, will delete bogus *.info files + or replace them with correct files), however if the user does SHOW SLAVE + STATUS before START SLAVE, he will see old, confusing rli->master_log_*. + In other words, we reinit rli->master_log_* for SHOW SLAVE STATUS + to display fine in any case. + */ + + rli->group_master_log_name[0]= 0; + rli->group_master_log_pos= 0; + + if (!rli->inited) + { + DBUG_PRINT("info", ("rli->inited == 0")); + DBUG_RETURN(0); + } + + DBUG_ASSERT(rli->slave_running == 0); + DBUG_ASSERT(rli->mi->slave_running == 0); + + rli->slave_skip_counter=0; + pthread_mutex_lock(&rli->data_lock); + + /* + we close the relay log fd possibly left open by the slave SQL thread, + to be able to delete it; the relay log fd possibly left open by the slave + I/O thread will be closed naturally in reset_logs() by the + close(LOG_CLOSE_TO_BE_OPENED) call + */ + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd= -1; + } + + if (rli->relay_log.reset_logs(thd)) + { + *errmsg = "Failed during log reset"; + error=1; + goto err; + } + /* Save name of used relay log file */ + strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->group_relay_log_name)-1); + strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->event_relay_log_name)-1); + rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; + if (count_relay_log_space(rli)) + { + *errmsg= "Error counting relay log space"; + goto err; + } + if (!just_reset) + error= init_relay_log_pos(rli, rli->group_relay_log_name, + rli->group_relay_log_pos, + 0 /* do not need data lock */, errmsg, 0); + +err: +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf))); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); +} + + +/* + Check if condition stated in UNTIL clause of START SLAVE is reached. + SYNOPSYS + st_relay_log_info::is_until_satisfied() + DESCRIPTION + Checks if UNTIL condition is reached. Uses caching result of last + comparison of current log file name and target log file name. So cached + value should be invalidated if current log file name changes + (see st_relay_log_info::notify_... functions). + + This caching is needed to avoid of expensive string comparisons and + strtol() conversions needed for log names comparison. We don't need to + compare them each time this function is called, we only need to do this + when current log name changes. If we have UNTIL_MASTER_POS condition we + need to do this only after Rotate_log_event::exec_event() (which is + rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS + condition then we should invalidate cached comarison value after + inc_group_relay_log_pos() which called for each group of events (so we + have some benefit if we have something like queries that use + autoincrement or if we have transactions). + + Should be called ONLY if until_condition != UNTIL_NONE ! + RETURN VALUE + true - condition met or error happened (condition seems to have + bad log file name) + false - condition not met +*/ + +bool st_relay_log_info::is_until_satisfied() +{ + const char *log_name; + ulonglong log_pos; + DBUG_ENTER("st_relay_log_info::is_until_satisfied"); + + DBUG_ASSERT(until_condition != UNTIL_NONE); + + if (until_condition == UNTIL_MASTER_POS) + { + log_name= group_master_log_name; + log_pos= group_master_log_pos; + } + else + { /* until_condition == UNTIL_RELAY_POS */ + log_name= group_relay_log_name; + log_pos= group_relay_log_pos; + } + + if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN) + { + /* + We have no cached comparison results so we should compare log names + and cache result. + If we are after RESET SLAVE, and the SQL slave thread has not processed + any event yet, it could be that group_master_log_name is "". In that case, + just wait for more events (as there is no sensible comparison to do). + */ + + if (*log_name) + { + const char *basename= log_name + dirname_length(log_name); + + const char *q= (const char*)(fn_ext(basename)+1); + if (strncmp(basename, until_log_name, (int)(q-basename)) == 0) + { + /* Now compare extensions. */ + char *q_end; + ulong log_name_extension= strtoul(q, &q_end, 10); + if (log_name_extension < until_log_name_extension) + until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS; + else + until_log_names_cmp_result= + (log_name_extension > until_log_name_extension) ? + UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ; + } + else + { + /* Probably error so we aborting */ + sql_print_error("Slave SQL thread is stopped because UNTIL " + "condition is bad."); + DBUG_RETURN(TRUE); + } + } + else + DBUG_RETURN(until_log_pos == 0); + } + + DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && + log_pos >= until_log_pos) || + until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); +} + + +void st_relay_log_info::cached_charset_invalidate() +{ + DBUG_ENTER("st_relay_log_info::cached_charset_invalidate"); + + /* Full of zeroes means uninitialized. */ + bzero(cached_charset, sizeof(cached_charset)); + DBUG_VOID_RETURN; +} + + +bool st_relay_log_info::cached_charset_compare(char *charset) +{ + DBUG_ENTER("st_relay_log_info::cached_charset_compare"); + + if (bcmp(cached_charset, charset, sizeof(cached_charset))) + { + memcpy(cached_charset, charset, sizeof(cached_charset)); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +void st_relay_log_info::transaction_end(THD* thd) +{ + DBUG_ENTER("st_relay_log_info::transaction_end"); + + /* + Nothing to do here right now. + */ + + DBUG_VOID_RETURN; +} + +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) +void st_relay_log_info::cleanup_context(THD *thd, bool error) +{ + DBUG_ENTER("st_relay_log_info::cleanup_context"); + + DBUG_ASSERT(sql_thd == thd); + /* + 1) Instances of Table_map_log_event, if ::exec_event() was called on them, + may have opened tables, which we cannot be sure have been closed (because + maybe the Rows_log_event have not been found or will not be, because slave + SQL thread is stopping, or relay log has a missing tail etc). So we close + all thread's tables. And so the table mappings have to be cancelled. + 2) Rows_log_event::exec_event() may even have started statements or + transactions on them, which we need to rollback in case of error. + 3) If finding a Format_description_log_event after a BEGIN, we also need + to rollback before continuing with the next events. + 4) so we need this "context cleanup" function. + */ + if (error) + { + ha_autocommit_or_rollback(thd, 1); // if a "statement transaction" + end_trans(thd, ROLLBACK); // if a "real transaction" + } + m_table_map.clear_tables(); + close_thread_tables(thd); + clear_tables_to_lock(); + unsafe_to_stop_at= 0; + DBUG_VOID_RETURN; +} +#endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 392f12c2a71..d737055baf2 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -21,6 +21,7 @@ #include "rpl_tblmap.h" + /**************************************************************************** Replication SQL Thread @@ -98,8 +99,8 @@ typedef struct st_relay_log_info */ pthread_cond_t start_cond, stop_cond, data_cond; - /* parent master info structure */ - struct st_master_info *mi; + /* parent MASTER_INFO structure */ + class MASTER_INFO *mi; /* Needed to deal properly with cur_log getting closed and re-opened with @@ -164,6 +165,9 @@ typedef struct st_relay_log_info time_t last_master_timestamp; + void clear_slave_error(); + void clear_until_condition(); + /* Needed for problems when slave stops and we want to restart it skipping one or more events in the master log that have caused @@ -289,22 +293,6 @@ typedef struct st_relay_log_info void cached_charset_invalidate(); bool cached_charset_compare(char *charset); - /* - To reload special tables when they are changes, we introduce a set - of functions that will mark whenever special functions need to be - called after modifying tables. Right now, the tables are either - ACL tables or grants tables. - */ - enum enum_reload_flag - { - RELOAD_NONE_F = 0UL, - RELOAD_GRANT_F = (1UL << 0), - RELOAD_ACCESS_F = (1UL << 1) - }; - - ulong m_reload_flags; - - void touching_table(char const* db, char const* table, ulong table_id); void transaction_end(THD*); void cleanup_context(THD *, bool); @@ -322,4 +310,9 @@ typedef struct st_relay_log_info time_t unsafe_to_stop_at; } RELAY_LOG_INFO; + +// Defined in rpl_rli.cc +int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname); + + #endif /* RPL_RLI_H */ diff --git a/sql/slave.cc b/sql/slave.cc index 00d6d168fb8..b497b7c8520 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -31,13 +31,15 @@ #include "rpl_tblmap.h" +int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); + + #define MAX_SLAVE_RETRY_PAUSE 5 bool use_slave_mask = 0; MY_BITMAP slave_error_mask; typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); -volatile bool slave_sql_running = 0, slave_io_running = 0; char* slave_load_tmpdir = 0; MASTER_INFO *active_mi= 0; my_bool replicate_same_server_id; @@ -59,7 +61,6 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev); static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli); static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi); static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli); -static int count_relay_log_space(RELAY_LOG_INFO* rli); static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, @@ -202,223 +203,6 @@ err: /* - Open the given relay log - - SYNOPSIS - init_relay_log_pos() - rli Relay information (will be initialized) - log Name of relay log file to read from. NULL = First log - pos Position in relay log file - need_data_lock Set to 1 if this functions should do mutex locks - errmsg Store pointer to error message here - look_for_description_event - 1 if we should look for such an event. We only need - this when the SQL thread starts and opens an existing - relay log and has to execute it (possibly from an - offset >4); then we need to read the first event of - the relay log to be able to parse the events we have - to execute. - - DESCRIPTION - - Close old open relay log files. - - If we are using the same relay log as the running IO-thread, then set - rli->cur_log to point to the same IO_CACHE entry. - - If not, open the 'log' binary file. - - TODO - - check proper initialization of group_master_log_name/group_master_log_pos - - RETURN VALUES - 0 ok - 1 error. errmsg is set to point to the error message -*/ - -int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, - ulonglong pos, bool need_data_lock, - const char** errmsg, - bool look_for_description_event) -{ - DBUG_ENTER("init_relay_log_pos"); - DBUG_PRINT("info", ("pos=%lu", pos)); - - *errmsg=0; - pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); - - if (need_data_lock) - pthread_mutex_lock(&rli->data_lock); - - /* - Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER - is, too, and init_slave() too; these 2 functions allocate a description - event in init_relay_log_pos, which is not freed by the terminating SQL slave - thread as that thread is not started by these functions. So we have to free - the description_event here, in case, so that there is no memory leak in - running, say, CHANGE MASTER. - */ - delete rli->relay_log.description_event_for_exec; - /* - By default the relay log is in binlog format 3 (4.0). - Even if format is 4, this will work enough to read the first event - (Format_desc) (remember that format 4 is just lenghtened compared to format - 3; format 3 is a prefix of format 4). - */ - rli->relay_log.description_event_for_exec= new - Format_description_log_event(3); - - pthread_mutex_lock(log_lock); - - /* Close log file and free buffers if it's already open */ - if (rli->cur_log_fd >= 0) - { - end_io_cache(&rli->cache_buf); - my_close(rli->cur_log_fd, MYF(MY_WME)); - rli->cur_log_fd = -1; - } - - rli->group_relay_log_pos = rli->event_relay_log_pos = pos; - - /* - Test to see if the previous run was with the skip of purging - If yes, we do not purge when we restart - */ - if (rli->relay_log.find_log_pos(&rli->linfo, NullS, 1)) - { - *errmsg="Could not find first log during relay log initialization"; - goto err; - } - - if (log && rli->relay_log.find_log_pos(&rli->linfo, log, 1)) - { - *errmsg="Could not find target log during relay log initialization"; - goto err; - } - strmake(rli->group_relay_log_name,rli->linfo.log_file_name, - sizeof(rli->group_relay_log_name)-1); - strmake(rli->event_relay_log_name,rli->linfo.log_file_name, - sizeof(rli->event_relay_log_name)-1); - if (rli->relay_log.is_active(rli->linfo.log_file_name)) - { - /* - The IO thread is using this log file. - In this case, we will use the same IO_CACHE pointer to - read data as the IO thread is using to write data. - */ - my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0); - if (check_binlog_magic(rli->cur_log,errmsg)) - goto err; - rli->cur_log_old_open_count=rli->relay_log.get_open_count(); - } - else - { - /* - Open the relay log and set rli->cur_log to point at this one - */ - if ((rli->cur_log_fd=open_binlog(&rli->cache_buf, - rli->linfo.log_file_name,errmsg)) < 0) - goto err; - rli->cur_log = &rli->cache_buf; - } - /* - In all cases, check_binlog_magic() has been called so we're at offset 4 for - sure. - */ - if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */ - { - Log_event* ev; - while (look_for_description_event) - { - /* - Read the possible Format_description_log_event; if position - was 4, no need, it will be read naturally. - */ - DBUG_PRINT("info",("looking for a Format_description_log_event")); - - if (my_b_tell(rli->cur_log) >= pos) - break; - - /* - Because of we have rli->data_lock and log_lock, we can safely read an - event - */ - if (!(ev=Log_event::read_log_event(rli->cur_log,0, - rli->relay_log.description_event_for_exec))) - { - DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d", - rli->cur_log->error)); - if (rli->cur_log->error) /* not EOF */ - { - *errmsg= "I/O error reading event at position 4"; - goto err; - } - break; - } - else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT) - { - DBUG_PRINT("info",("found Format_description_log_event")); - delete rli->relay_log.description_event_for_exec; - rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev; - /* - As ev was returned by read_log_event, it has passed is_valid(), so - my_malloc() in ctor worked, no need to check again. - */ - /* - Ok, we found a Format_description event. But it is not sure that this - describes the whole relay log; indeed, one can have this sequence - (starting from position 4): - Format_desc (of slave) - Rotate (of master) - Format_desc (of master) - So the Format_desc which really describes the rest of the relay log - is the 3rd event (it can't be further than that, because we rotate - the relay log when we queue a Rotate event from the master). - But what describes the Rotate is the first Format_desc. - So what we do is: - go on searching for Format_description events, until you exceed the - position (argument 'pos') or until you find another event than Rotate - or Format_desc. - */ - } - else - { - DBUG_PRINT("info",("found event of another type=%d", - ev->get_type_code())); - look_for_description_event= (ev->get_type_code() == ROTATE_EVENT); - delete ev; - } - } - my_b_seek(rli->cur_log,(off_t)pos); -#ifndef DBUG_OFF - { - char llbuf1[22], llbuf2[22]; - DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", - llstr(my_b_tell(rli->cur_log),llbuf1), - llstr(rli->event_relay_log_pos,llbuf2))); - } -#endif - - } - -err: - /* - If we don't purge, we can't honour relay_log_space_limit ; - silently discard it - */ - if (!relay_log_purge) - rli->log_space_limit= 0; - pthread_cond_broadcast(&rli->data_cond); - - pthread_mutex_unlock(log_lock); - - if (need_data_lock) - pthread_mutex_unlock(&rli->data_lock); - if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg) - *errmsg= "Invalid Format_description log event; could be out of memory"; - - DBUG_RETURN ((*errmsg) ? 1 : 0); -} - - -/* Init function to set up array for errors that should be skipped for slave SYNOPSIS @@ -461,174 +245,6 @@ void init_slave_skip_errors(const char* arg) } -void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, - bool skip_lock) -{ - DBUG_ENTER("st_relay_log_info::inc_group_relay_log_pos"); - - if (!skip_lock) - pthread_mutex_lock(&data_lock); - inc_event_relay_log_pos(); - group_relay_log_pos= event_relay_log_pos; - strmake(group_relay_log_name,event_relay_log_name, - sizeof(group_relay_log_name)-1); - - notify_group_relay_log_name_update(); - - /* - If the slave does not support transactions and replicates a transaction, - users should not trust group_master_log_pos (which they can display with - SHOW SLAVE STATUS or read from relay-log.info), because to compute - group_master_log_pos the slave relies on log_pos stored in the master's - binlog, but if we are in a master's transaction these positions are always - the BEGIN's one (excepted for the COMMIT), so group_master_log_pos does - not advance as it should on the non-transactional slave (it advances by - big leaps, whereas it should advance by small leaps). - */ - /* - In 4.x we used the event's len to compute the positions here. This is - wrong if the event was 3.23/4.0 and has been converted to 5.0, because - then the event's len is not what is was in the master's binlog, so this - will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0 - replication: Exec_master_log_pos is wrong). Only way to solve this is to - have the original offset of the end of the event the relay log. This is - what we do in 5.0: log_pos has become "end_log_pos" (because the real use - of log_pos in 4.0 was to compute the end_log_pos; so better to store - end_log_pos instead of begin_log_pos. - If we had not done this fix here, the problem would also have appeared - when the slave and master are 5.0 but with different event length (for - example the slave is more recent than the master and features the event - UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in - SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this - value which would lead to badly broken replication. - Even the relay_log_pos will be corrupted in this case, because the len is - the relay log is not "val". - With the end_log_pos solution, we avoid computations involving lengthes. - */ - DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu", - (long) log_pos, (long) group_master_log_pos)); - if (log_pos) // 3.23 binlogs don't have log_posx - { - group_master_log_pos= log_pos; - } - pthread_cond_broadcast(&data_cond); - if (!skip_lock) - pthread_mutex_unlock(&data_lock); - DBUG_VOID_RETURN; -} - - -void st_relay_log_info::close_temporary_tables() -{ - TABLE *table,*next; - DBUG_ENTER("st_relay_log_info::close_temporary_tables"); - - for (table=save_temporary_tables ; table ; table=next) - { - next=table->next; - /* - Don't ask for disk deletion. For now, anyway they will be deleted when - slave restarts, but it is a better intention to not delete them. - */ - DBUG_PRINT("info", ("table: %p", table)); - close_temporary(table, 1, 0); - } - save_temporary_tables= 0; - slave_open_temp_tables= 0; - DBUG_VOID_RETURN; -} - -/* - purge_relay_logs() - - NOTES - Assumes to have a run lock on rli and that no slave thread are running. -*/ - -int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, - const char** errmsg) -{ - int error=0; - DBUG_ENTER("purge_relay_logs"); - - /* - Even if rli->inited==0, we still try to empty rli->master_log_* variables. - Indeed, rli->inited==0 does not imply that they already are empty. - It could be that slave's info initialization partly succeeded : - for example if relay-log.info existed but *relay-bin*.* - have been manually removed, init_relay_log_info reads the old - relay-log.info and fills rli->master_log_*, then init_relay_log_info - checks for the existence of the relay log, this fails and - init_relay_log_info leaves rli->inited to 0. - In that pathological case, rli->master_log_pos* will be properly reinited - at the next START SLAVE (as RESET SLAVE or CHANGE - MASTER, the callers of purge_relay_logs, will delete bogus *.info files - or replace them with correct files), however if the user does SHOW SLAVE - STATUS before START SLAVE, he will see old, confusing rli->master_log_*. - In other words, we reinit rli->master_log_* for SHOW SLAVE STATUS - to display fine in any case. - */ - - rli->group_master_log_name[0]= 0; - rli->group_master_log_pos= 0; - - if (!rli->inited) - { - DBUG_PRINT("info", ("rli->inited == 0")); - DBUG_RETURN(0); - } - - DBUG_ASSERT(rli->slave_running == 0); - DBUG_ASSERT(rli->mi->slave_running == 0); - - rli->slave_skip_counter=0; - pthread_mutex_lock(&rli->data_lock); - - /* - we close the relay log fd possibly left open by the slave SQL thread, - to be able to delete it; the relay log fd possibly left open by the slave - I/O thread will be closed naturally in reset_logs() by the - close(LOG_CLOSE_TO_BE_OPENED) call - */ - if (rli->cur_log_fd >= 0) - { - end_io_cache(&rli->cache_buf); - my_close(rli->cur_log_fd, MYF(MY_WME)); - rli->cur_log_fd= -1; - } - - if (rli->relay_log.reset_logs(thd)) - { - *errmsg = "Failed during log reset"; - error=1; - goto err; - } - /* Save name of used relay log file */ - strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->group_relay_log_name)-1); - strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(), - sizeof(rli->event_relay_log_name)-1); - rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE; - if (count_relay_log_space(rli)) - { - *errmsg= "Error counting relay log space"; - goto err; - } - if (!just_reset) - error= init_relay_log_pos(rli, rli->group_relay_log_name, - rli->group_relay_log_pos, - 0 /* do not need data lock */, errmsg, 0); - -err: -#ifndef DBUG_OFF - char buf[22]; -#endif - DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf))); - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(error); -} - - int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) { DBUG_ENTER("terminate_slave_threads"); @@ -1023,7 +639,7 @@ const char *print_slave_db_safe(const char* db) DBUG_RETURN((db ? db : "")); } -static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, +int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val) { uint length; @@ -1054,7 +670,7 @@ static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, } -static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) +int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) { char buf[32]; DBUG_ENTER("init_intvar_from_file"); @@ -1456,261 +1072,6 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, } -void end_master_info(MASTER_INFO* mi) -{ - DBUG_ENTER("end_master_info"); - - if (!mi->inited) - DBUG_VOID_RETURN; - end_relay_log_info(&mi->rli); - if (mi->fd >= 0) - { - end_io_cache(&mi->file); - (void)my_close(mi->fd, MYF(MY_WME)); - mi->fd = -1; - } - mi->inited = 0; - - DBUG_VOID_RETURN; -} - - -static int init_relay_log_info(RELAY_LOG_INFO* rli, - const char* info_fname) -{ - char fname[FN_REFLEN+128]; - int info_fd; - const char* msg = 0; - int error = 0; - DBUG_ENTER("init_relay_log_info"); - DBUG_ASSERT(!rli->no_storage); // Don't init if there is no storage - - if (rli->inited) // Set if this function called - DBUG_RETURN(0); - fn_format(fname, info_fname, mysql_data_home, "", 4+32); - pthread_mutex_lock(&rli->data_lock); - info_fd = rli->info_fd; - rli->cur_log_fd = -1; - rli->slave_skip_counter=0; - rli->abort_pos_wait=0; - rli->log_space_limit= relay_log_space_limit; - rli->log_space_total= 0; - rli->tables_to_lock= 0; - rli->tables_to_lock_count= 0; - - /* - The relay log will now be opened, as a SEQ_READ_APPEND IO_CACHE. - Note that the I/O thread flushes it to disk after writing every - event, in flush_master_info(mi, 1). - */ - - /* - For the maximum log size, we choose max_relay_log_size if it is - non-zero, max_binlog_size otherwise. If later the user does SET - GLOBAL on one of these variables, fix_max_binlog_size and - fix_max_relay_log_size will reconsider the choice (for example - if the user changes max_relay_log_size to zero, we have to - switch to using max_binlog_size for the relay log) and update - rli->relay_log.max_size (and mysql_bin_log.max_size). - */ - { - char buf[FN_REFLEN]; - const char *ln; - static bool name_warning_sent= 0; - ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin", - 1, buf); - /* We send the warning only at startup, not after every RESET SLAVE */ - if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent) - { - /* - User didn't give us info to name the relay log index file. - Picking `hostname`-relay-bin.index like we do, causes replication to - fail if this slave's hostname is changed later. So, we would like to - instead require a name. But as we don't want to break many existing - setups, we only give warning, not error. - */ - sql_print_warning("Neither --relay-log nor --relay-log-index were used;" - " so replication " - "may break when this MySQL server acts as a " - "slave and has his hostname changed!! Please " - "use '--relay-log=%s' to avoid this problem.", ln); - name_warning_sent= 1; - } - /* - note, that if open() fails, we'll still have index file open - but a destructor will take care of that - */ - if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln) || - rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0, - (max_relay_log_size ? max_relay_log_size : - max_binlog_size), 1)) - { - pthread_mutex_unlock(&rli->data_lock); - sql_print_error("Failed in open_log() called from init_relay_log_info()"); - DBUG_RETURN(1); - } - } - - /* if file does not exist */ - if (access(fname,F_OK)) - { - /* - If someone removed the file from underneath our feet, just close - the old descriptor and re-create the old file - */ - if (info_fd >= 0) - my_close(info_fd, MYF(MY_WME)); - if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0) - { - sql_print_error("Failed to create a new relay log info file (\ -file '%s', errno %d)", fname, my_errno); - msg= current_thd->net.last_error; - goto err; - } - if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) - { - sql_print_error("Failed to create a cache on relay log info file '%s'", - fname); - msg= current_thd->net.last_error; - goto err; - } - - /* Init relay log with first entry in the relay index file */ - if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, - &msg, 0)) - { - sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)"); - goto err; - } - rli->group_master_log_name[0]= 0; - rli->group_master_log_pos= 0; - rli->info_fd= info_fd; - } - else // file exists - { - if (info_fd >= 0) - reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); - else - { - int error=0; - if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0) - { - sql_print_error("\ -Failed to open the existing relay log info file '%s' (errno %d)", - fname, my_errno); - error= 1; - } - else if (init_io_cache(&rli->info_file, info_fd, - IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) - { - sql_print_error("Failed to create a cache on relay log info file '%s'", - fname); - error= 1; - } - if (error) - { - if (info_fd >= 0) - my_close(info_fd, MYF(0)); - rli->info_fd= -1; - rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(1); - } - } - - rli->info_fd = info_fd; - int relay_log_pos, master_log_pos; - if (init_strvar_from_file(rli->group_relay_log_name, - sizeof(rli->group_relay_log_name), - &rli->info_file, "") || - init_intvar_from_file(&relay_log_pos, - &rli->info_file, BIN_LOG_HEADER_SIZE) || - init_strvar_from_file(rli->group_master_log_name, - sizeof(rli->group_master_log_name), - &rli->info_file, "") || - init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) - { - msg="Error reading slave log configuration"; - goto err; - } - strmake(rli->event_relay_log_name,rli->group_relay_log_name, - sizeof(rli->event_relay_log_name)-1); - rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos; - rli->group_master_log_pos= master_log_pos; - - if (init_relay_log_pos(rli, - rli->group_relay_log_name, - rli->group_relay_log_pos, - 0 /* no data lock*/, - &msg, 0)) - { - char llbuf[22]; - sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)", - rli->group_relay_log_name, - llstr(rli->group_relay_log_pos, llbuf)); - goto err; - } - } - -#ifndef DBUG_OFF - { - char llbuf1[22], llbuf2[22]; - DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s", - llstr(my_b_tell(rli->cur_log),llbuf1), - llstr(rli->event_relay_log_pos,llbuf2))); - DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE); - DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos); - } -#endif - - /* - Now change the cache from READ to WRITE - must do this - before flush_relay_log_info - */ - reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); - if ((error= flush_relay_log_info(rli))) - sql_print_error("Failed to flush relay log info file"); - if (count_relay_log_space(rli)) - { - msg="Error counting relay log space"; - goto err; - } - rli->inited= 1; - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(error); - -err: - sql_print_error(msg); - end_io_cache(&rli->info_file); - if (info_fd >= 0) - my_close(info_fd, MYF(0)); - rli->info_fd= -1; - rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); - pthread_mutex_unlock(&rli->data_lock); - DBUG_RETURN(1); -} - - -static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) -{ - MY_STAT s; - DBUG_ENTER("add_relay_log"); - if (!my_stat(linfo->log_file_name,&s,MYF(0))) - { - sql_print_error("log %s listed in the index, but failed to stat", - linfo->log_file_name); - DBUG_RETURN(1); - } - rli->log_space_total += s.st_size; -#ifndef DBUG_OFF - char buf[22]; - DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); -#endif - DBUG_RETURN(0); -} - - static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) { bool slave_killed=0; @@ -1733,31 +1094,6 @@ Waiting for the slave SQL thread to free enough relay log space"); } -static int count_relay_log_space(RELAY_LOG_INFO* rli) -{ - LOG_INFO linfo; - DBUG_ENTER("count_relay_log_space"); - rli->log_space_total= 0; - if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) - { - sql_print_error("Could not find first log while counting relay log space"); - DBUG_RETURN(1); - } - do - { - if (add_relay_log(rli,&linfo)) - DBUG_RETURN(1); - } while (!rli->relay_log.find_next_log(&linfo, 1)); - /* - As we have counted everything, including what may have written in a - preceding write, we must reset bytes_written, or we may count some space - twice. - */ - rli->relay_log.reset_bytes_written(); - DBUG_RETURN(0); -} - - /* Builds a Rotate from the ignored events' info and writes it to relay log. @@ -1811,284 +1147,6 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) } -void init_master_info_with_options(MASTER_INFO* mi) -{ - DBUG_ENTER("init_master_info_with_options"); - - mi->master_log_name[0] = 0; - mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number - - if (master_host) - strmake(mi->host, master_host, sizeof(mi->host) - 1); - if (master_user) - strmake(mi->user, master_user, sizeof(mi->user) - 1); - if (master_password) - strmake(mi->password, master_password, MAX_PASSWORD_LENGTH); - mi->port = master_port; - mi->connect_retry = master_connect_retry; - - mi->ssl= master_ssl; - if (master_ssl_ca) - strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1); - if (master_ssl_capath) - strmake(mi->ssl_capath, master_ssl_capath, sizeof(mi->ssl_capath)-1); - if (master_ssl_cert) - strmake(mi->ssl_cert, master_ssl_cert, sizeof(mi->ssl_cert)-1); - if (master_ssl_cipher) - strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1); - if (master_ssl_key) - strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); - DBUG_VOID_RETURN; -} - -void clear_slave_error(RELAY_LOG_INFO* rli) -{ - DBUG_ENTER("clear_slave_error"); - - /* Clear the errors displayed by SHOW SLAVE STATUS */ - rli->last_slave_error[0]= 0; - rli->last_slave_errno= 0; - DBUG_VOID_RETURN; -} - -/* - Reset UNTIL condition for RELAY_LOG_INFO - SYNOPSYS - clear_until_condition() - rli - RELAY_LOG_INFO structure where UNTIL condition should be reset - */ -void clear_until_condition(RELAY_LOG_INFO* rli) -{ - DBUG_ENTER("clear_until_condition"); - - rli->until_condition= RELAY_LOG_INFO::UNTIL_NONE; - rli->until_log_name[0]= 0; - rli->until_log_pos= 0; - DBUG_VOID_RETURN; -} - - -#define LINES_IN_MASTER_INFO_WITH_SSL 14 - - -int init_master_info(MASTER_INFO* mi, const char* master_info_fname, - const char* slave_info_fname, - bool abort_if_no_master_info_file, - int thread_mask) -{ - int fd,error; - char fname[FN_REFLEN+128]; - DBUG_ENTER("init_master_info"); - - if (mi->inited) - { - /* - We have to reset read position of relay-log-bin as we may have - already been reading from 'hotlog' when the slave was stopped - last time. If this case pos_in_file would be set and we would - get a crash when trying to read the signature for the binary - relay log. - - We only rewind the read position if we are starting the SQL - thread. The handle_slave_sql thread assumes that the read - position is at the beginning of the file, and will read the - "signature" and then fast-forward to the last position read. - */ - if (thread_mask & SLAVE_SQL) - { - my_b_seek(mi->rli.cur_log, (my_off_t) 0); - } - DBUG_RETURN(0); - } - - mi->mysql=0; - mi->file_id=1; - fn_format(fname, master_info_fname, mysql_data_home, "", 4+32); - - /* - We need a mutex while we are changing master info parameters to - keep other threads from reading bogus info - */ - - pthread_mutex_lock(&mi->data_lock); - fd = mi->fd; - - /* does master.info exist ? */ - - if (access(fname,F_OK)) - { - if (abort_if_no_master_info_file) - { - pthread_mutex_unlock(&mi->data_lock); - DBUG_RETURN(0); - } - /* - if someone removed the file from underneath our feet, just close - the old descriptor and re-create the old file - */ - if (fd >= 0) - my_close(fd, MYF(MY_WME)); - if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) - { - sql_print_error("Failed to create a new master info file (\ -file '%s', errno %d)", fname, my_errno); - goto err; - } - if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) - { - sql_print_error("Failed to create a cache on master info file (\ -file '%s')", fname); - goto err; - } - - mi->fd = fd; - init_master_info_with_options(mi); - - } - else // file exists - { - if (fd >= 0) - reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0); - else - { - if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 ) - { - sql_print_error("Failed to open the existing master info file (\ -file '%s', errno %d)", fname, my_errno); - goto err; - } - if (init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, - 0, MYF(MY_WME))) - { - sql_print_error("Failed to create a cache on master info file (\ -file '%s')", fname); - goto err; - } - } - - mi->fd = fd; - int port, connect_retry, master_log_pos, ssl= 0, lines; - char *first_non_digit; - - /* - Starting from 4.1.x master.info has new format. Now its - first line contains number of lines in file. By reading this - number we will be always distinguish to which version our - master.info corresponds to. We can't simply count lines in - file since versions before 4.1.x could generate files with more - lines than needed. - If first line doesn't contain a number or contain number less than - 14 then such file is treated like file from pre 4.1.1 version. - There is no ambiguity when reading an old master.info, as before - 4.1.1, the first line contained the binlog's name, which is either - empty or has an extension (contains a '.'), so can't be confused - with an integer. - - So we're just reading first line and trying to figure which version - is this. - */ - - /* - The first row is temporarily stored in mi->master_log_name, - if it is line count and not binlog name (new format) it will be - overwritten by the second row later. - */ - if (init_strvar_from_file(mi->master_log_name, - sizeof(mi->master_log_name), &mi->file, - "")) - goto errwithmsg; - - lines= strtoul(mi->master_log_name, &first_non_digit, 10); - - if (mi->master_log_name[0]!='\0' && - *first_non_digit=='\0' && lines >= LINES_IN_MASTER_INFO_WITH_SSL) - { // Seems to be new format - if (init_strvar_from_file(mi->master_log_name, - sizeof(mi->master_log_name), &mi->file, "")) - goto errwithmsg; - } - else - lines= 7; - - if (init_intvar_from_file(&master_log_pos, &mi->file, 4) || - init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, - master_host) || - init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, - master_user) || - init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1, - &mi->file, master_password) || - init_intvar_from_file(&port, &mi->file, master_port) || - init_intvar_from_file(&connect_retry, &mi->file, - master_connect_retry)) - goto errwithmsg; - - /* - If file has ssl part use it even if we have server without - SSL support. But these option will be ignored later when - slave will try connect to master, so in this case warning - is printed. - */ - if (lines >= LINES_IN_MASTER_INFO_WITH_SSL && - (init_intvar_from_file(&ssl, &mi->file, master_ssl) || - init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca), - &mi->file, master_ssl_ca) || - init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath), - &mi->file, master_ssl_capath) || - init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert), - &mi->file, master_ssl_cert) || - init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher), - &mi->file, master_ssl_cipher) || - init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key), - &mi->file, master_ssl_key))) - goto errwithmsg; -#ifndef HAVE_OPENSSL - if (ssl) - sql_print_warning("SSL information in the master info file " - "('%s') are ignored because this MySQL slave was compiled " - "without SSL support.", fname); -#endif /* HAVE_OPENSSL */ - - /* - This has to be handled here as init_intvar_from_file can't handle - my_off_t types - */ - mi->master_log_pos= (my_off_t) master_log_pos; - mi->port= (uint) port; - mi->connect_retry= (uint) connect_retry; - mi->ssl= (my_bool) ssl; - } - DBUG_PRINT("master_info",("log_file_name: %s position: %ld", - mi->master_log_name, - (ulong) mi->master_log_pos)); - - mi->rli.mi = mi; - if (init_relay_log_info(&mi->rli, slave_info_fname)) - goto err; - - mi->inited = 1; - // now change cache READ -> WRITE - must do this before flush_master_info - reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1); - if ((error=test(flush_master_info(mi, 1)))) - sql_print_error("Failed to flush master info file"); - pthread_mutex_unlock(&mi->data_lock); - DBUG_RETURN(error); - -errwithmsg: - sql_print_error("Error reading master configuration"); - -err: - if (fd >= 0) - { - my_close(fd, MYF(0)); - end_io_cache(&mi->file); - } - mi->fd= -1; - pthread_mutex_unlock(&mi->data_lock); - DBUG_RETURN(1); -} - - int register_slave_on_master(MYSQL* mysql) { char buf[1024], *pos= buf; @@ -2307,313 +1365,6 @@ bool show_master_info(THD* thd, MASTER_INFO* mi) DBUG_RETURN(FALSE); } -/* - RETURN - 2 - flush relay log failed - 1 - flush master info failed - 0 - all ok -*/ -int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache) -{ - IO_CACHE* file = &mi->file; - char lbuf[22]; - DBUG_ENTER("flush_master_info"); - DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos)); - - /* - Flush the relay log to disk. If we don't do it, then the relay log while - have some part (its last kilobytes) in memory only, so if the slave server - dies now, with, say, from master's position 100 to 150 in memory only (not - on disk), and with position 150 in master.info, then when the slave - restarts, the I/O thread will fetch binlogs from 150, so in the relay log - we will have "[0, 100] U [150, infinity[" and nobody will notice it, so the - SQL thread will jump from 100 to 150, and replication will silently break. - - When we come to this place in code, relay log may or not be initialized; - the caller is responsible for setting 'flush_relay_log_cache' accordingly. - */ - if (flush_relay_log_cache && - flush_io_cache(mi->rli.relay_log.get_log_file())) - DBUG_RETURN(2); - - /* - We flushed the relay log BEFORE the master.info file, because if we crash - now, we will get a duplicate event in the relay log at restart. If we - flushed in the other order, we would get a hole in the relay log. - And duplicate is better than hole (with a duplicate, in later versions we - can add detection and scrap one event; with a hole there's nothing we can - do). - */ - - /* - In certain cases this code may create master.info files that seems - corrupted, because of extra lines filled with garbage in the end - file (this happens if new contents take less space than previous - contents of file). But because of number of lines in the first line - of file we don't care about this garbage. - */ - - my_b_seek(file, 0L); - my_b_printf(file, "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n", - LINES_IN_MASTER_INFO_WITH_SSL, - mi->master_log_name, llstr(mi->master_log_pos, lbuf), - mi->host, mi->user, - mi->password, mi->port, mi->connect_retry, - (int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert, - mi->ssl_cipher, mi->ssl_key); - DBUG_RETURN(-flush_io_cache(file)); -} - - -st_relay_log_info::st_relay_log_info() - :no_storage(FALSE), info_fd(-1), cur_log_fd(-1), save_temporary_tables(0), - cur_log_old_open_count(0), group_master_log_pos(0), log_space_total(0), - ignore_log_space_limit(0), last_master_timestamp(0), slave_skip_counter(0), - abort_pos_wait(0), slave_run_id(0), sql_thd(0), last_slave_errno(0), - inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), - until_log_pos(0), retried_trans(0), - tables_to_lock(0), tables_to_lock_count(0), - m_reload_flags(RELOAD_NONE_F), - unsafe_to_stop_at(0) -{ - DBUG_ENTER("st_relay_log_info::st_relay_log_info"); - - group_relay_log_name[0]= event_relay_log_name[0]= - group_master_log_name[0]= 0; - last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0; - bzero((char*) &info_file, sizeof(info_file)); - bzero((char*) &cache_buf, sizeof(cache_buf)); - cached_charset_invalidate(); - pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); - pthread_cond_init(&data_cond, NULL); - pthread_cond_init(&start_cond, NULL); - pthread_cond_init(&stop_cond, NULL); - pthread_cond_init(&log_space_cond, NULL); - relay_log.init_pthread_objects(); - DBUG_VOID_RETURN; -} - - -st_relay_log_info::~st_relay_log_info() -{ - DBUG_ENTER("st_relay_log_info::~st_relay_log_info"); - - pthread_mutex_destroy(&run_lock); - pthread_mutex_destroy(&data_lock); - pthread_mutex_destroy(&log_space_lock); - pthread_cond_destroy(&data_cond); - pthread_cond_destroy(&start_cond); - pthread_cond_destroy(&stop_cond); - pthread_cond_destroy(&log_space_cond); - relay_log.cleanup(); - DBUG_VOID_RETURN; -} - -/* - Waits until the SQL thread reaches (has executed up to) the - log/position or timed out. - - SYNOPSIS - wait_for_pos() - thd client thread that sent SELECT MASTER_POS_WAIT - log_name log name to wait for - log_pos position to wait for - timeout timeout in seconds before giving up waiting - - NOTES - timeout is longlong whereas it should be ulong ; but this is - to catch if the user submitted a negative timeout. - - RETURN VALUES - -2 improper arguments (log_pos<0) - or slave not running, or master info changed - during the function's execution, - or client thread killed. -2 is translated to NULL by caller - -1 timed out - >=0 number of log events the function had to wait - before reaching the desired log/position - */ - -int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, - longlong log_pos, - longlong timeout) -{ - int event_count = 0; - ulong init_abort_pos_wait; - int error=0; - struct timespec abstime; // for timeout checking - const char *msg; - DBUG_ENTER("st_relay_log_info::wait_for_pos"); - - if (!inited) - DBUG_RETURN(-1); - - DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu", - log_name->c_ptr(), (ulong) log_pos, (ulong) timeout)); - - set_timespec(abstime,timeout); - pthread_mutex_lock(&data_lock); - msg= thd->enter_cond(&data_cond, &data_lock, - "Waiting for the slave SQL thread to " - "advance position"); - /* - This function will abort when it notices that some CHANGE MASTER or - RESET MASTER has changed the master info. - To catch this, these commands modify abort_pos_wait ; We just monitor - abort_pos_wait and see if it has changed. - Why do we have this mechanism instead of simply monitoring slave_running - in the loop (we do this too), as CHANGE MASTER/RESET SLAVE require that - the SQL thread be stopped? - This is becasue if someones does: - STOP SLAVE;CHANGE MASTER/RESET SLAVE; START SLAVE; - the change may happen very quickly and we may not notice that - slave_running briefly switches between 1/0/1. - */ - init_abort_pos_wait= abort_pos_wait; - - /* - We'll need to - handle all possible log names comparisons (e.g. 999 vs 1000). - We use ulong for string->number conversion ; this is no - stronger limitation than in find_uniq_filename in sql/log.cc - */ - ulong log_name_extension; - char log_name_tmp[FN_REFLEN]; //make a char[] from String - - strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1)); - - char *p= fn_ext(log_name_tmp); - char *p_end; - if (!*p || log_pos<0) - { - error= -2; //means improper arguments - goto err; - } - // Convert 0-3 to 4 - log_pos= max(log_pos, BIN_LOG_HEADER_SIZE); - /* p points to '.' */ - log_name_extension= strtoul(++p, &p_end, 10); - /* - p_end points to the first invalid character. - If it equals to p, no digits were found, error. - If it contains '\0' it means conversion went ok. - */ - if (p_end==p || *p_end) - { - error= -2; - goto err; - } - - /* The "compare and wait" main loop */ - while (!thd->killed && - init_abort_pos_wait == abort_pos_wait && - slave_running) - { - bool pos_reached; - int cmp_result= 0; - - DBUG_PRINT("info", - ("init_abort_pos_wait: %ld abort_pos_wait: %ld", - init_abort_pos_wait, abort_pos_wait)); - DBUG_PRINT("info",("group_master_log_name: '%s' pos: %lu", - group_master_log_name, (ulong) group_master_log_pos)); - - /* - group_master_log_name can be "", if we are just after a fresh - replication start or after a CHANGE MASTER TO MASTER_HOST/PORT - (before we have executed one Rotate event from the master) or - (rare) if the user is doing a weird slave setup (see next - paragraph). If group_master_log_name is "", we assume we don't - have enough info to do the comparison yet, so we just wait until - more data. In this case master_log_pos is always 0 except if - somebody (wrongly) sets this slave to be a slave of itself - without using --replicate-same-server-id (an unsupported - configuration which does nothing), then group_master_log_pos - will grow and group_master_log_name will stay "". - */ - if (*group_master_log_name) - { - char *basename= (group_master_log_name + - dirname_length(group_master_log_name)); - /* - First compare the parts before the extension. - Find the dot in the master's log basename, - and protect against user's input error : - if the names do not match up to '.' included, return error - */ - char *q= (char*)(fn_ext(basename)+1); - if (strncmp(basename, log_name_tmp, (int)(q-basename))) - { - error= -2; - break; - } - // Now compare extensions. - char *q_end; - ulong group_master_log_name_extension= strtoul(q, &q_end, 10); - if (group_master_log_name_extension < log_name_extension) - cmp_result= -1 ; - else - cmp_result= (group_master_log_name_extension > log_name_extension) ? 1 : 0 ; - - pos_reached= ((!cmp_result && group_master_log_pos >= (ulonglong)log_pos) || - cmp_result > 0); - if (pos_reached || thd->killed) - break; - } - - //wait for master update, with optional timeout. - - DBUG_PRINT("info",("Waiting for master update")); - /* - We are going to pthread_cond_(timed)wait(); if the SQL thread stops it - will wake us up. - */ - if (timeout > 0) - { - /* - Note that pthread_cond_timedwait checks for the timeout - before for the condition ; i.e. it returns ETIMEDOUT - if the system time equals or exceeds the time specified by abstime - before the condition variable is signaled or broadcast, _or_ if - the absolute time specified by abstime has already passed at the time - of the call. - For that reason, pthread_cond_timedwait will do the "timeoutting" job - even if its condition is always immediately signaled (case of a loaded - master). - */ - error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime); - } - else - pthread_cond_wait(&data_cond, &data_lock); - DBUG_PRINT("info",("Got signal of master update or timed out")); - if (error == ETIMEDOUT || error == ETIME) - { - error= -1; - break; - } - error=0; - event_count++; - DBUG_PRINT("info",("Testing if killed or SQL thread not running")); - } - -err: - thd->exit_cond(msg); - DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \ -improper_arguments: %d timed_out: %d", - thd->killed_errno(), - (int) (init_abort_pos_wait != abort_pos_wait), - (int) slave_running, - (int) (error == -2), - (int) (error == -1))); - if (thd->killed || init_abort_pos_wait != abort_pos_wait || - !slave_running) - { - error= -2; - } - DBUG_RETURN( error ? error : event_count ); -} void set_slave_thread_options(THD* thd) { @@ -2663,6 +1414,13 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; thd->security_ctx->skip_grants(); my_net_init(&thd->net, 0); +/* + Adding MAX_LOG_EVENT_HEADER_LEN to the max_allowed_packet on all + slave threads, since a replication event can become this much larger + than the corresponding packet (query) sent from client to master. +*/ + thd->variables.max_allowed_packet= global_system_variables.max_allowed_packet + + MAX_LOG_EVENT_HEADER; /* note, incr over the global not session var */ thd->net.read_timeout = slave_net_timeout; thd->slave_thread = 1; set_slave_thread_options(thd); @@ -2872,119 +1630,6 @@ int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) } } -/* - Check if condition stated in UNTIL clause of START SLAVE is reached. - SYNOPSYS - st_relay_log_info::is_until_satisfied() - DESCRIPTION - Checks if UNTIL condition is reached. Uses caching result of last - comparison of current log file name and target log file name. So cached - value should be invalidated if current log file name changes - (see st_relay_log_info::notify_... functions). - - This caching is needed to avoid of expensive string comparisons and - strtol() conversions needed for log names comparison. We don't need to - compare them each time this function is called, we only need to do this - when current log name changes. If we have UNTIL_MASTER_POS condition we - need to do this only after Rotate_log_event::exec_event() (which is - rare, so caching gives real benifit), and if we have UNTIL_RELAY_POS - condition then we should invalidate cached comarison value after - inc_group_relay_log_pos() which called for each group of events (so we - have some benefit if we have something like queries that use - autoincrement or if we have transactions). - - Should be called ONLY if until_condition != UNTIL_NONE ! - RETURN VALUE - true - condition met or error happened (condition seems to have - bad log file name) - false - condition not met -*/ - -bool st_relay_log_info::is_until_satisfied() -{ - const char *log_name; - ulonglong log_pos; - DBUG_ENTER("st_relay_log_info::is_until_satisfied"); - - DBUG_ASSERT(until_condition != UNTIL_NONE); - - if (until_condition == UNTIL_MASTER_POS) - { - log_name= group_master_log_name; - log_pos= group_master_log_pos; - } - else - { /* until_condition == UNTIL_RELAY_POS */ - log_name= group_relay_log_name; - log_pos= group_relay_log_pos; - } - - if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN) - { - /* - We have no cached comparison results so we should compare log names - and cache result. - If we are after RESET SLAVE, and the SQL slave thread has not processed - any event yet, it could be that group_master_log_name is "". In that case, - just wait for more events (as there is no sensible comparison to do). - */ - - if (*log_name) - { - const char *basename= log_name + dirname_length(log_name); - - const char *q= (const char*)(fn_ext(basename)+1); - if (strncmp(basename, until_log_name, (int)(q-basename)) == 0) - { - /* Now compare extensions. */ - char *q_end; - ulong log_name_extension= strtoul(q, &q_end, 10); - if (log_name_extension < until_log_name_extension) - until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_LESS; - else - until_log_names_cmp_result= - (log_name_extension > until_log_name_extension) ? - UNTIL_LOG_NAMES_CMP_GREATER : UNTIL_LOG_NAMES_CMP_EQUAL ; - } - else - { - /* Probably error so we aborting */ - sql_print_error("Slave SQL thread is stopped because UNTIL " - "condition is bad."); - DBUG_RETURN(TRUE); - } - } - else - DBUG_RETURN(until_log_pos == 0); - } - - DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && - log_pos >= until_log_pos) || - until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); -} - - -void st_relay_log_info::cached_charset_invalidate() -{ - DBUG_ENTER("st_relay_log_info::cached_charset_invalidate"); - - /* Full of zeroes means uninitialized. */ - bzero(cached_charset, sizeof(cached_charset)); - DBUG_VOID_RETURN; -} - - -bool st_relay_log_info::cached_charset_compare(char *charset) -{ - DBUG_ENTER("st_relay_log_info::cached_charset_compare"); - - if (bcmp(cached_charset, charset, sizeof(cached_charset))) - { - memcpy(cached_charset, charset, sizeof(cached_charset)); - DBUG_RETURN(1); - } - DBUG_RETURN(0); -} /* Check if the current error is of temporary nature of not. @@ -3176,6 +1821,8 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) We were in a transaction which has been rolled back because of a temporary error; let's seek back to BEGIN log event and retry it all again. + Note, if lock wait timeout (innodb_lock_wait_timeout exceeded) + there is no rollback since 5.0.13 (ref: manual). We have to not only seek but also a) init_master_info(), to seek back to hot relay log's start for later (for when we will come back to this hot log after re-processing the @@ -3303,11 +1950,19 @@ pthread_handler_t handle_slave_io(void *arg) thd->proc_info = "Connecting to master"; // we can get killed during safe_connect if (!safe_connect(thd, mysql, mi)) + { sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',\ replication started in log '%s' at position %s", mi->user, - mi->host, mi->port, - IO_RPL_LOG_NAME, - llstr(mi->master_log_pos,llbuff)); + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + /* + Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O + thread, since a replication event can become this much larger than + the corresponding packet (query) sent from client to master. + */ + mysql->net.max_packet_size= thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER; + } else { sql_print_information("Slave I/O thread killed while connecting to master"); @@ -3632,7 +2287,7 @@ pthread_handler_t handle_slave_sql(void *arg) now. But the master timestamp is reset by RESET SLAVE & CHANGE MASTER. */ - clear_slave_error(rli); + rli->clear_slave_error(); //tell the I/O thread to take relay_log_space_limit into account from now on pthread_mutex_lock(&rli->log_space_lock); @@ -4455,7 +3110,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, '%s@%s:%d': \ Error: '%s' errno: %d retry-time: %d retries: %lu", (reconnect ? "reconnecting" : "connecting"), - mi->user,mi->host,mi->port, + mi->user, mi->host, mi->port, mysql_error(mysql), last_errno, mi->connect_retry, master_retry_count); @@ -4990,121 +3645,6 @@ end: DBUG_VOID_RETURN; } -/* - Some system tables needed to be re-read by the MySQL server after it has - updated them; in statement-based replication, the GRANT and other commands - are sent verbatim to the slave which then reloads; in row-based replication, - changes to these tables are done through ordinary Rows binlog events, so - master must add some flag for the slave to know it has to reload the tables. -*/ -struct st_reload_entry -{ - char const *table; - st_relay_log_info::enum_reload_flag flag; -}; - -/* - Sorted array of table names, please keep it sorted since we are - using bsearch() on it below. - */ -static st_reload_entry s_mysql_tables[] = -{ - { "columns_priv", st_relay_log_info::RELOAD_GRANT_F }, - { "db", st_relay_log_info::RELOAD_ACCESS_F }, - { "host", st_relay_log_info::RELOAD_ACCESS_F }, - { "procs_priv", st_relay_log_info::RELOAD_GRANT_F }, - { "tables_priv", st_relay_log_info::RELOAD_GRANT_F }, - { "user", st_relay_log_info::RELOAD_ACCESS_F } -}; - -static const my_size_t s_mysql_tables_size = - sizeof(s_mysql_tables)/sizeof(*s_mysql_tables); - -static int reload_entry_compare(const void *lhs, const void *rhs) -{ - const char *lstr = static_cast<const char *>(lhs); - const char *rstr = static_cast<const st_reload_entry*>(rhs)->table; - DBUG_ENTER("reload_entry_compare"); - - DBUG_RETURN(strcmp(lstr, rstr)); -} - -void st_relay_log_info::touching_table(char const* db, char const* table, - ulong table_id) -{ - DBUG_ENTER("st_relay_log_info::touching_table"); - - if (strcmp(db,"mysql") == 0) - { -#if defined(HAVE_BSEARCH) && defined(HAVE_SIZE_T) - void *const ptr= bsearch(table, s_mysql_tables, - s_mysql_tables_size, - sizeof(*s_mysql_tables), reload_entry_compare); - st_reload_entry const *const entry= static_cast<st_reload_entry*>(ptr); -#else - /* - Fall back to full scan, there are few rows anyway and updating the - "mysql" database is rare. - */ - st_reload_entry const *entry= s_mysql_tables; - for ( ; entry < s_mysql_tables + s_mysql_tables_size ; entry++) - if (reload_entry_compare(table, entry) == 0) - break; -#endif - if (entry) - m_reload_flags|= entry->flag; - } - DBUG_VOID_RETURN; -} - -void st_relay_log_info::transaction_end(THD* thd) -{ - DBUG_ENTER("st_relay_log_info::transaction_end"); - - if (m_reload_flags != RELOAD_NONE_F) - { - if (m_reload_flags & RELOAD_ACCESS_F) - acl_reload(thd); - - if (m_reload_flags & RELOAD_GRANT_F) - grant_reload(thd); - - m_reload_flags= RELOAD_NONE_F; - } - DBUG_VOID_RETURN; -} - -#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -void st_relay_log_info::cleanup_context(THD *thd, bool error) -{ - DBUG_ENTER("st_relay_log_info::cleanup_context"); - - DBUG_ASSERT(sql_thd == thd); - /* - 1) Instances of Table_map_log_event, if ::exec_event() was called on them, - may have opened tables, which we cannot be sure have been closed (because - maybe the Rows_log_event have not been found or will not be, because slave - SQL thread is stopping, or relay log has a missing tail etc). So we close - all thread's tables. And so the table mappings have to be cancelled. - 2) Rows_log_event::exec_event() may even have started statements or - transactions on them, which we need to rollback in case of error. - 3) If finding a Format_description_log_event after a BEGIN, we also need - to rollback before continuing with the next events. - 4) so we need this "context cleanup" function. - */ - if (error) - { - ha_autocommit_or_rollback(thd, 1); // if a "statement transaction" - end_trans(thd, ROLLBACK); // if a "real transaction" - } - m_table_map.clear_tables(); - close_thread_tables(thd); - clear_tables_to_lock(); - unsafe_to_stop_at= 0; - DBUG_VOID_RETURN; -} -#endif - #ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION template class I_List_iterator<i_string>; diff --git a/sql/slave.h b/sql/slave.h index e70b2e4b326..24ba09d78d3 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -24,6 +24,7 @@ #include "rpl_filter.h" #include "rpl_tblmap.h" #include "rpl_rli.h" +#include "rpl_mi.h" #define SLAVE_NET_TIMEOUT 3600 @@ -38,11 +39,11 @@ I/O Thread - One of these threads is started for each master server. They maintain a connection to their master server, read log events from the master as they arrive, and queues them into - a single, shared relay log file. A MASTER_INFO struct + a single, shared relay log file. A MASTER_INFO represents each of these threads. SQL Thread - One of these threads is started and reads from the relay log - file, executing each event. A RELAY_LOG_INFO struct + file, executing each event. A RELAY_LOG_INFO represents this thread. Buffering in the relay log file makes it unnecessary to reread events from @@ -95,7 +96,6 @@ extern my_string opt_relay_logname, opt_relaylog_index_name; extern my_bool opt_skip_slave_start, opt_reckless_slave; extern my_bool opt_log_slave_updates; extern ulonglong relay_log_space_limit; -struct st_master_info; /* 3 possible values for MASTER_INFO::slave_running and @@ -114,110 +114,6 @@ struct st_master_info; static Log_event* next_event(RELAY_LOG_INFO* rli); -/***************************************************************************** - - Replication IO Thread - - st_master_info contains: - - information about how to connect to a master - - current master log name - - current master log offset - - misc control variables - - st_master_info is initialized once from the master.info file if such - exists. Otherwise, data members corresponding to master.info fields - are initialized with defaults specified by master-* options. The - initialization is done through init_master_info() call. - - The format of master.info file: - - log_name - log_pos - master_host - master_user - master_pass - master_port - master_connect_retry - - To write out the contents of master.info file to disk ( needed every - time we read and queue data from the master ), a call to - flush_master_info() is required. - - To clean up, call end_master_info() - -*****************************************************************************/ - -typedef struct st_master_info -{ - /* the variables below are needed because we can change masters on the fly */ - char master_log_name[FN_REFLEN]; - char host[HOSTNAME_LENGTH+1]; - char user[USERNAME_LENGTH+1]; - char password[MAX_PASSWORD_LENGTH+1]; - my_bool ssl; // enables use of SSL connection if true - char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN]; - char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN]; - - my_off_t master_log_pos; - File fd; // we keep the file open, so we need to remember the file pointer - IO_CACHE file; - - pthread_mutex_t data_lock,run_lock; - pthread_cond_t data_cond,start_cond,stop_cond; - THD *io_thd; - MYSQL* mysql; - uint32 file_id; /* for 3.23 load data infile */ - RELAY_LOG_INFO rli; - uint port; - uint connect_retry; -#ifndef DBUG_OFF - int events_till_disconnect; -#endif - bool inited; - volatile bool abort_slave; - volatile uint slave_running; - volatile ulong slave_run_id; - /* - The difference in seconds between the clock of the master and the clock of - the slave (second - first). It must be signed as it may be <0 or >0. - clock_diff_with_master is computed when the I/O thread starts; for this the - I/O thread does a SELECT UNIX_TIMESTAMP() on the master. - "how late the slave is compared to the master" is computed like this: - clock_of_slave - last_timestamp_executed_by_SQL_thread - clock_diff_with_master - - */ - long clock_diff_with_master; - - st_master_info() - :ssl(0), fd(-1), io_thd(0), inited(0), - abort_slave(0),slave_running(0), slave_run_id(0) - { - host[0] = 0; user[0] = 0; password[0] = 0; - ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; - ssl_cipher[0]= 0; ssl_key[0]= 0; - - bzero((char*) &file, sizeof(file)); - pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); - pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); - pthread_cond_init(&data_cond, NULL); - pthread_cond_init(&start_cond, NULL); - pthread_cond_init(&stop_cond, NULL); - } - - ~st_master_info() - { - pthread_mutex_destroy(&run_lock); - pthread_mutex_destroy(&data_lock); - pthread_cond_destroy(&data_cond); - pthread_cond_destroy(&start_cond); - pthread_cond_destroy(&stop_cond); - } - -} MASTER_INFO; - - -int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); - #define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") #define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ @@ -231,7 +127,6 @@ int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); int init_slave(); void init_slave_skip_errors(const char* arg); -int flush_master_info(MASTER_INFO* mi, bool flush_relay_log_cache); 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, @@ -276,14 +171,8 @@ void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli, ATTRIBUTE_FORMAT(printf, 4, 5); void end_slave(); /* clean up */ -void init_master_info_with_options(MASTER_INFO* mi); void clear_until_condition(RELAY_LOG_INFO* rli); void clear_slave_error(RELAY_LOG_INFO* rli); -int init_master_info(MASTER_INFO* mi, const char* master_info_fname, - const char* slave_info_fname, - bool abort_if_no_master_info_file, - int thread_mask); -void end_master_info(MASTER_INFO* mi); void end_relay_log_info(RELAY_LOG_INFO* rli); void lock_slave_threads(MASTER_INFO* mi); void unlock_slave_threads(MASTER_INFO* mi); diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index 23ca5330053..732981b58f3 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -79,9 +79,15 @@ void mysql_client_binlog_statement(THD* thd) char const *endptr= 0; int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr); +#ifndef HAVE_purify + /* + This debug printout should not be used for valgrind builds + since it will read from unassigned memory. + */ DBUG_PRINT("info", ("bytes_decoded=%d; strptr=0x%lu; endptr=0x%lu ('%c':%d)", bytes_decoded, strptr, endptr, *endptr, *endptr)); +#endif if (bytes_decoded < 0) { @@ -147,8 +153,14 @@ void mysql_client_binlog_statement(THD* thd) DBUG_PRINT("info",("ev->get_type_code()=%d", ev->get_type_code())); DBUG_PRINT("info",("bufptr+EVENT_TYPE_OFFSET=0x%lx", bufptr+EVENT_TYPE_OFFSET)); +#ifndef HAVE_purify + /* + This debug printout should not be used for valgrind builds + since it will read from unassigned memory. + */ DBUG_PRINT("info", ("bytes_decoded=%d; bufptr=0x%lx; buf[EVENT_LEN_OFFSET]=%u", bytes_decoded, bufptr, uint4korr(bufptr+EVENT_LEN_OFFSET))); +#endif ev->thd= thd; if (int err= ev->exec_event(thd->rli_fake)) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 776ba4dabea..20acb28bc77 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2552,6 +2552,128 @@ my_size_t THD::pack_row(TABLE *table, MY_BITMAP const* cols, byte *row_data, } +namespace { + /** + Class to handle temporary allocation of memory for row data. + + The responsibilities of the class is to provide memory for + packing one or two rows of packed data (depending on what + constructor is called). + + In order to make the allocation more efficient for "simple" rows, + i.e., rows that do not contain any blobs, a pointer to the + allocated memory is of memory is stored in the table structure + for simple rows. If memory for a table containing a blob field + is requested, only memory for that is allocated, and subsequently + released when the object is destroyed. + + */ + class Row_data_memory { + public: + /** + Build an object to keep track of a block-local piece of memory + for storing a row of data. + + @param table + Table where the pre-allocated memory is stored. + + @param length + Length of data that is needed, if the record contain blobs. + */ + Row_data_memory(TABLE *table, my_size_t const len1) + : m_memory(0) + { +#ifndef DBUG_OFF + m_alloc_checked= false; +#endif + allocate_memory(table, len1); + m_ptr[0]= has_memory() ? m_memory : 0; + m_ptr[1]= 0; + } + + Row_data_memory(TABLE *table, my_size_t const len1, my_size_t const len2) + : m_memory(0) + { +#ifndef DBUG_OFF + m_alloc_checked= false; +#endif + allocate_memory(table, len1 + len2); + m_ptr[0]= has_memory() ? m_memory : 0; + m_ptr[1]= has_memory() ? m_memory + len1 : 0; + } + + ~Row_data_memory() + { + if (m_memory != 0 && m_release_memory_on_destruction) + my_free((gptr) m_memory, MYF(MY_WME)); + } + + /** + Is there memory allocated? + + @retval true There is memory allocated + @retval false Memory allocation failed + */ + bool has_memory() const { +#ifndef DBUG_OFF + m_alloc_checked= true; +#endif + return m_memory != 0; + } + + byte *slot(int const s) + { + DBUG_ASSERT(0 <= s && s < sizeof(m_ptr)/sizeof(*m_ptr)); + DBUG_ASSERT(m_ptr[s] != 0); + DBUG_ASSERT(m_alloc_checked == true); + return m_ptr[s]; + } + + private: + void allocate_memory(TABLE *const table, my_size_t const total_length) + { + if (table->s->blob_fields == 0) + { + /* + The maximum length of a packed record is less than this + length. We use this value instead of the supplied length + when allocating memory for records, since we don't know how + the memory will be used in future allocations. + + Since table->s->reclength is for unpacked records, we have + to add two bytes for each field, which can potentially be + added to hold the length of a packed field. + */ + my_size_t const maxlen= table->s->reclength + 2 * table->s->fields; + + /* + Allocate memory for two records if memory hasn't been + allocated. We allocate memory for two records so that it can + be used when processing update rows as well. + */ + if (table->write_row_record == 0) + table->write_row_record= + (byte *) alloc_root(&table->mem_root, 2 * maxlen); + m_memory= table->write_row_record; + m_release_memory_on_destruction= false; + } + else + { + m_memory= (byte *) my_malloc(total_length, MYF(MY_WME)); + m_release_memory_on_destruction= true; + } + } + +#ifndef DBUG_OFF + mutable bool m_alloc_checked; +#endif + bool m_release_memory_on_destruction; + byte *m_memory; + byte *m_ptr[2]; + }; +} + + int THD::binlog_write_row(TABLE* table, bool is_trans, MY_BITMAP const* cols, my_size_t colcnt, byte const *record) @@ -2562,40 +2684,25 @@ int THD::binlog_write_row(TABLE* table, bool is_trans, Pack records into format for transfer. We are allocating more memory than needed, but that doesn't matter. */ - bool error= 0; - byte *row_data= table->write_row_record; - my_size_t const max_len= max_row_length(table, record); - my_size_t len; - Rows_log_event *ev; - - /* Allocate room for a row (if needed) */ - if (!row_data) - { - if (!table->s->blob_fields) - { - /* multiply max_len by 2 so it can be used for update_row as well */ - table->write_row_record= (byte *) alloc_root(&table->mem_root, - 2*max_len); - if (!table->write_row_record) - return HA_ERR_OUT_OF_MEM; - row_data= table->write_row_record; - } - else if (unlikely(!(row_data= (byte *) my_malloc(max_len, MYF(MY_WME))))) - return HA_ERR_OUT_OF_MEM; - } - len= pack_row(table, cols, row_data, record); + int error= 0; - ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, - len, is_trans, - static_cast<Write_rows_log_event*>(0)); + Row_data_memory memory(table, max_row_length(table, record)); + if (!memory.has_memory()) + return HA_ERR_OUT_OF_MEM; - /* add_row_data copies row_data to internal buffer */ - error= likely(ev != 0) ? ev->add_row_data(row_data,len) : HA_ERR_OUT_OF_MEM ; + byte *row_data= memory.slot(0); - if (table->write_row_record == 0) - my_free((gptr) row_data, MYF(MY_WME)); + my_size_t const len= pack_row(table, cols, row_data, record); - return error; + Rows_log_event* const ev= + binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, + len, is_trans, + static_cast<Write_rows_log_event*>(0)); + + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; + + return ev->add_row_data(row_data, len); } int THD::binlog_update_row(TABLE* table, bool is_trans, @@ -2605,53 +2712,44 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, { DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open()); - bool error= 0; + int error= 0; my_size_t const before_maxlen = max_row_length(table, before_record); my_size_t const after_maxlen = max_row_length(table, after_record); - byte *row_data= table->write_row_record; - byte *before_row, *after_row; - if (row_data != 0) - { - before_row= row_data; - after_row= before_row + before_maxlen; - } - else - { - if (unlikely(!(row_data= (byte*)my_multi_malloc(MYF(MY_WME), - &before_row, before_maxlen, - &after_row, after_maxlen, - NULL)))) - return HA_ERR_OUT_OF_MEM; - } + Row_data_memory row_data(table, before_maxlen, after_maxlen); + if (!row_data.has_memory()) + return HA_ERR_OUT_OF_MEM; + + byte *before_row= row_data.slot(0); + byte *after_row= row_data.slot(1); my_size_t const before_size= pack_row(table, cols, before_row, before_record); my_size_t const after_size= pack_row(table, cols, after_row, after_record); - + + /* + Don't print debug messages when running valgrind since they can + trigger false warnings. + */ +#ifndef HAVE_purify DBUG_DUMP("before_record", (const char *)before_record, table->s->reclength); DBUG_DUMP("after_record", (const char *)after_record, table->s->reclength); DBUG_DUMP("before_row", (const char *)before_row, before_size); DBUG_DUMP("after_row", (const char *)after_row, after_size); +#endif Rows_log_event* const ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, before_size + after_size, is_trans, static_cast<Update_rows_log_event*>(0)); - error= - unlikely(!ev) || + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; + + return ev->add_row_data(before_row, before_size) || ev->add_row_data(after_row, after_size); - - if (!table->write_row_record) - { - /* add_row_data copies row_data to internal buffer */ - my_free((gptr)row_data, MYF(MY_WME)); - } - - return error; } int THD::binlog_delete_row(TABLE* table, bool is_trans, @@ -2664,11 +2762,14 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans, Pack records into format for transfer. We are allocating more memory than needed, but that doesn't matter. */ - bool error= 0; - my_size_t const max_len= max_row_length(table, record); - byte *row_data= table->write_row_record; - if (!row_data && unlikely(!(row_data= (byte*)my_malloc(max_len, MYF(MY_WME))))) + int error= 0; + + Row_data_memory memory(table, max_row_length(table, record)); + if (unlikely(!memory.has_memory())) return HA_ERR_OUT_OF_MEM; + + byte *row_data= memory.slot(0); + my_size_t const len= pack_row(table, cols, row_data, record); Rows_log_event* const ev= @@ -2676,13 +2777,10 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans, len, is_trans, static_cast<Delete_rows_log_event*>(0)); - error= (unlikely(!ev)) || ev->add_row_data(row_data, len); - - /* add_row_data copies row_data */ - if (table->write_row_record == 0) - my_free((gptr)row_data, MYF(MY_WME)); + if (unlikely(ev == 0)) + return HA_ERR_OUT_OF_MEM; - return error; + return ev->add_row_data(row_data, len); } @@ -2776,6 +2874,12 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, #endif /*HAVE_ROW_BASED_REPLICATION*/ switch (qtype) { + case THD::ROW_QUERY_TYPE: +#ifdef HAVE_ROW_BASED_REPLICATION + if (current_stmt_binlog_row_based) + DBUG_RETURN(0); +#endif + /* Otherwise, we fall through */ case THD::MYSQL_QUERY_TYPE: /* Using this query type is a conveniece hack, since we have been @@ -2785,12 +2889,6 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, Make sure to change in check_table_binlog_row_based() according to how you treat this. */ - case THD::ROW_QUERY_TYPE: -#ifdef HAVE_ROW_BASED_REPLICATION - if (current_stmt_binlog_row_based) - DBUG_RETURN(0); -#endif - /* Otherwise, we fall through */ case THD::STMT_QUERY_TYPE: /* The MYSQL_LOG::write() function will set the STMT_END_F flag and diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2a45520af81..5b61a5fb48a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2550,7 +2550,23 @@ mysql_execute_command(THD *thd) { /* we warn the slave SQL thread */ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - reset_one_shot_variables(thd); + if (thd->one_shot_set) + { + /* + It's ok to check thd->one_shot_set here: + + The charsets in a MySQL 5.0 slave can change by both a binlogged + SET ONE_SHOT statement and the event-internal charset setting, + and these two ways to change charsets do not seems to work + together. + + At least there seems to be problems in the rli cache for + charsets if we are using ONE_SHOT. Note that this is normally no + problem because either the >= 5.0 slave reads a 4.1 binlog (with + ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both." + */ + reset_one_shot_variables(thd); + } DBUG_RETURN(0); } } @@ -6317,7 +6333,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->alias= alias_str; if (lower_case_table_names && table->table.length) - my_casedn_str(files_charset_info, table->table.str); + table->table.length= my_casedn_str(files_charset_info, table->table.str); ptr->table_name=table->table.str; ptr->table_name_length=table->table.length; ptr->lock_type= lock_type; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 52489087b02..7ffbb649cb8 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -431,6 +431,12 @@ impossible position"; goto err; } packet->set("\0", 1, &my_charset_bin); + /* + Adding MAX_LOG_EVENT_HEADER_LEN, since a binlog event can become + this larger than the corresponding packet (query) sent + from client to master. + */ + thd->variables.max_allowed_packet+= MAX_LOG_EVENT_HEADER; /* We can set log_lock now, it does not move (it's a member of @@ -805,7 +811,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) sizeof(mi->rli.until_log_name)-1); } else - clear_until_condition(&mi->rli); + mi->rli.clear_until_condition(); if (mi->rli.until_condition != RELAY_LOG_INFO::UNTIL_NONE) { @@ -978,8 +984,8 @@ int reset_slave(THD *thd, MASTER_INFO* mi) Reset errors (the idea is that we forget about the old master). */ - clear_slave_error(&mi->rli); - clear_until_condition(&mi->rli); + mi->rli.clear_slave_error(); + mi->rli.clear_until_condition(); // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 end_master_info(mi); @@ -1244,8 +1250,8 @@ bool change_master(THD* thd, MASTER_INFO* mi) pthread_mutex_lock(&mi->rli.data_lock); mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */ /* Clear the errors, for a clean start */ - clear_slave_error(&mi->rli); - clear_until_condition(&mi->rli); + mi->rli.clear_slave_error(); + mi->rli.clear_until_condition(); /* If we don't write new coordinates to disk now, then old will remain in relay-log.info until START SLAVE is issued; but if mysqld is shutdown diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d40d9f16bb5..6758f9d68d0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8723,6 +8723,7 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, item->collation.collation); else new_field= item->make_string_field(table); + new_field->set_derivation(item->collation.derivation); break; case DECIMAL_RESULT: new_field= new Field_new_decimal(item->max_length, maybe_null, item->name, @@ -8908,7 +8909,9 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, (make_copy_field ? 0 : copy_func), modify_item, convert_blob_length); case Item::TYPE_HOLDER: - return ((Item_type_holder *)item)->make_field_by_type(table); + result= ((Item_type_holder *)item)->make_field_by_type(table); + result->set_derivation(item->collation.derivation); + return result; default: // Dosen't have to be stored return 0; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f6e39fb7913..dc8946df876 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1058,6 +1058,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE ")); else packet->append(STRING_WITH_LEN("CREATE TABLE ")); + if (create_info_arg && + (create_info_arg->options & HA_LEX_CREATE_IF_NOT_EXISTS)) + packet->append(STRING_WITH_LEN("IF NOT EXISTS ")); if (table_list->schema_table) alias= table_list->schema_table->table_name; else diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 6e4d3f2ed0a..29b53560067 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -844,6 +844,162 @@ outp: } +/* + copy a string, + with optional character set conversion, + with optional left padding (for binary -> UCS2 conversion) + + SYNOPSIS + well_formed_copy_nhars() + to Store result here + to_length Maxinum length of "to" string + to_cs Character set of "to" string + from Copy from here + from_length Length of from string + from_cs From character set + nchars Copy not more that nchars characters + well_formed_error_pos Return position when "from" is not well formed + or NULL otherwise. + cannot_convert_error_pos Return position where a not convertable + character met, or NULL otherwise. + from_end_pos Return position where scanning of "from" + string stopped. + NOTES + + RETURN + length of bytes copied to 'to' +*/ + + +uint32 +well_formed_copy_nchars(CHARSET_INFO *to_cs, + char *to, uint to_length, + CHARSET_INFO *from_cs, + const char *from, uint from_length, + uint nchars, + const char **well_formed_error_pos, + const char **cannot_convert_error_pos, + const char **from_end_pos) +{ + uint res; + + if ((to_cs == &my_charset_bin) || + (from_cs == &my_charset_bin) || + (to_cs == from_cs) || + my_charset_same(from_cs, to_cs)) + { + if (to_length < to_cs->mbminlen || !nchars) + { + *from_end_pos= from; + *cannot_convert_error_pos= NULL; + *well_formed_error_pos= NULL; + return 0; + } + + if (to_cs == &my_charset_bin) + { + res= min(min(nchars, to_length), from_length); + memmove(to, from, res); + *from_end_pos= from + res; + *well_formed_error_pos= NULL; + *cannot_convert_error_pos= NULL; + } + else + { + int well_formed_error; + uint from_offset; + + if ((from_offset= (from_length % to_cs->mbminlen)) && + (from_cs == &my_charset_bin)) + { + /* + Copying from BINARY to UCS2 needs to prepend zeros sometimes: + INSERT INTO t1 (ucs2_column) VALUES (0x01); + 0x01 -> 0x0001 + */ + uint pad_length= to_cs->mbminlen - from_offset; + bzero(to, pad_length); + memmove(to + pad_length, from, from_offset); + nchars--; + from+= from_offset; + from_length-= from_offset; + to+= to_cs->mbminlen; + to_length-= to_cs->mbminlen; + } + + set_if_smaller(from_length, to_length); + res= to_cs->cset->well_formed_len(to_cs, from, from + from_length, + nchars, &well_formed_error); + memmove(to, from, res); + *from_end_pos= from + res; + *well_formed_error_pos= well_formed_error ? from + res : NULL; + *cannot_convert_error_pos= NULL; + if (from_offset) + res+= to_cs->mbminlen; + } + } + else + { + int cnvres; + my_wc_t wc; + int (*mb_wc)(struct charset_info_st *, my_wc_t *, + const uchar *, const uchar *)= from_cs->cset->mb_wc; + int (*wc_mb)(struct charset_info_st *, my_wc_t, + uchar *s, uchar *e)= to_cs->cset->wc_mb; + const uchar *from_end= (const uchar*) from + from_length; + uchar *to_end= (uchar*) to + to_length; + char *to_start= to; + *well_formed_error_pos= NULL; + *cannot_convert_error_pos= NULL; + + for ( ; nchars; nchars--) + { + const char *from_prev= from; + if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0) + from+= cnvres; + else if (cnvres == MY_CS_ILSEQ) + { + if (!*well_formed_error_pos) + *well_formed_error_pos= from; + from++; + wc= '?'; + } + else if (cnvres > MY_CS_TOOSMALL) + { + /* + A correct multibyte sequence detected + But it doesn't have Unicode mapping. + */ + if (!*cannot_convert_error_pos) + *cannot_convert_error_pos= from; + from+= (-cnvres); + wc= '?'; + } + else + break; // Not enough characters + +outp: + if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0) + to+= cnvres; + else if (cnvres == MY_CS_ILUNI && wc != '?') + { + if (!*cannot_convert_error_pos) + *cannot_convert_error_pos= from_prev; + wc= '?'; + goto outp; + } + else + break; + } + *from_end_pos= from; + res= to - to_start; + } + return (uint32) res; +} + + + + void String::print(String *str) { char *st= (char*)Ptr, *end= st+str_length; diff --git a/sql/sql_string.h b/sql/sql_string.h index b1d417be2c2..a72b24ae9d0 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -30,6 +30,14 @@ String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, const char *from, uint32 from_length, CHARSET_INFO *from_cs, uint *errors); +uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs, + char *to, uint to_length, + CHARSET_INFO *from_cs, + const char *from, uint from_length, + uint nchars, + const char **well_formed_error_pos, + const char **cannot_convert_error_pos, + const char **from_end_pos); class String { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 5bf67af9271..98226c1651b 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1695,24 +1695,23 @@ mysql_rename_view(THD *thd, const char *new_name, TABLE_LIST *view) { - LEX_STRING pathstr, file; + LEX_STRING pathstr; File_parser *parser; - char view_path[FN_REFLEN]; + char path_buff[FN_REFLEN]; bool error= TRUE; DBUG_ENTER("mysql_rename_view"); - strxnmov(view_path, FN_REFLEN-1, mysql_data_home, "/", view->db, "/", - view->table_name, reg_ext, NullS); - (void) unpack_filename(view_path, view_path); - - pathstr.str= (char *)view_path; - pathstr.length= strlen(view_path); + pathstr.str= (char *) path_buff; + pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1, + view->db, view->table_name, + reg_ext, 0); if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) && is_equal(&view_type, parser->type())) { TABLE_LIST view_def; - char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + char dir_buff[FN_REFLEN]; + LEX_STRING dir, file; /* To be PS-friendly we should either to restore state of @@ -1735,18 +1734,18 @@ mysql_rename_view(THD *thd, view_def.revision - 1, num_view_backups)) goto err; - strxnmov(dir_buff, FN_REFLEN-1, mysql_data_home, "/", view->db, "/", - NullS); - (void) unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1, + view->db, "", "", 0); - pathstr.str= (char*)dir_buff; - pathstr.length= strlen(dir_buff); + pathstr.str= path_buff; + pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1, + view->db, new_name, reg_ext, 0); - file.str= file_buff; - file.length= (strxnmov(file_buff, FN_REFLEN, new_name, reg_ext, NullS) - - file_buff); + file.str= pathstr.str + dir.length; + file.length= pathstr.length - dir.length; - if (sql_create_definition_file(&pathstr, &file, view_file_type, + if (sql_create_definition_file(&dir, &file, view_file_type, (gptr)&view_def, view_parameters, num_view_backups)) { diff --git a/storage/federated/ha_federated.cc b/storage/federated/ha_federated.cc index 51c9f4c192e..53c2ba5a0ee 100644 --- a/storage/federated/ha_federated.cc +++ b/storage/federated/ha_federated.cc @@ -1534,7 +1534,7 @@ int ha_federated::close(void) 0 otherwise */ -inline uint field_in_record_is_null(TABLE *table, +static inline uint field_in_record_is_null(TABLE *table, Field *field, char *record) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index ec86b9a2e68..83266a22255 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2510,7 +2510,7 @@ get_field_offset( /****************************************************************** Checks if a field in a record is SQL NULL. Uses the record format information in table to track the null bit in record. */ -inline +static inline uint field_in_record_is_null( /*====================*/ diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c index 01bd0c43119..c6f9799bd67 100644 --- a/storage/myisam/mi_key.c +++ b/storage/myisam/mi_key.c @@ -52,7 +52,7 @@ static int _mi_put_key_in_record(MI_INFO *info,uint keynr,byte *record); uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key, const byte *record, my_off_t filepos) { - byte *pos,*end; + byte *pos; uchar *start; reg1 HA_KEYSEG *keyseg; my_bool is_ft= info->s->keyinfo[keynr].flag & HA_FULLTEXT; @@ -107,18 +107,17 @@ uint _mi_make_key(register MI_INFO *info, uint keynr, uchar *key, } if (keyseg->flag & HA_SPACE_PACK) { - end= pos + length; if (type != HA_KEYTYPE_NUM) { - while (end > pos && end[-1] == ' ') - end--; + length= cs->cset->lengthsp(cs, pos, length); } else { + byte *end= pos + length; while (pos < end && pos[0] == ' ') pos++; + length=(uint) (end-pos); } - length=(uint) (end-pos); FIX_LENGTH(cs, pos, length, char_length); store_key_length_inc(key,char_length); memcpy((byte*) key,(byte*) pos,(size_t) char_length); @@ -403,8 +402,10 @@ static int _mi_put_key_in_record(register MI_INFO *info, uint keynr, pos= record+keyseg->start; if (keyseg->type != (int) HA_KEYTYPE_NUM) { - memcpy(pos,key,(size_t) length); - bfill(pos+length,keyseg->length-length,' '); + memcpy(pos,key,(size_t) length); + keyseg->charset->cset->fill(keyseg->charset, + pos + length, keyseg->length - length, + ' '); } else { diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index 7d1f18d3906..010f7233c32 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -348,6 +348,8 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) goto err; } } + else if (pos->type == HA_KEYTYPE_BINARY) + pos->charset= &my_charset_bin; } if (share->keyinfo[i].flag & HA_SPATIAL) { diff --git a/storage/ndb/src/ndbapi/Ndbinit.cpp b/storage/ndb/src/ndbapi/Ndbinit.cpp index 5c0fb521c36..3d7d1b768f2 100644 --- a/storage/ndb/src/ndbapi/Ndbinit.cpp +++ b/storage/ndb/src/ndbapi/Ndbinit.cpp @@ -146,12 +146,13 @@ Ndb::~Ndb() } doDisconnect(); - delete theEventBuffer; - + /* Disconnect from transporter to stop signals from coming in */ if (theImpl->m_transporter_facade != NULL && theNdbBlockNumber > 0){ theImpl->m_transporter_facade->close(theNdbBlockNumber, theFirstTransId); } - + + delete theEventBuffer; + releaseTransactionArrays(); delete []theConnectionArray; diff --git a/storage/ndb/src/ndbapi/TransporterFacade.cpp b/storage/ndb/src/ndbapi/TransporterFacade.cpp index 8d0693f17a7..95f614e6699 100644 --- a/storage/ndb/src/ndbapi/TransporterFacade.cpp +++ b/storage/ndb/src/ndbapi/TransporterFacade.cpp @@ -762,7 +762,7 @@ TransporterFacade::init(Uint32 nodeId, const ndb_mgm_configuration* props) void TransporterFacade::for_each(NdbApiSignal* aSignal, LinearSectionPtr ptr[3]) { - DBUG_ENTER("TransporterFacade::connected"); + DBUG_ENTER("TransporterFacade::for_each"); Uint32 sz = m_threads.m_statusNext.size(); TransporterFacade::ThreadData::Object_Execute oe; for (Uint32 i = 0; i < sz ; i ++) diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c index 5758960ef6c..289b76c9e6e 100644 --- a/strings/ctype-bin.c +++ b/strings/ctype-bin.c @@ -211,9 +211,10 @@ static int my_strnncollsp_8bit_bin(CHARSET_INFO * cs __attribute__((unused)), /* This function is used for all conversion functions */ -static void my_case_str_bin(CHARSET_INFO *cs __attribute__((unused)), +static uint my_case_str_bin(CHARSET_INFO *cs __attribute__((unused)), char *str __attribute__((unused))) { + return 0; } static uint my_case_bin(CHARSET_INFO *cs __attribute__((unused)), diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index c945164ac9c..c3848c64219 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -21,40 +21,44 @@ #ifdef USE_MB -void my_caseup_str_mb(CHARSET_INFO * cs, char *str) +uint my_caseup_str_mb(CHARSET_INFO * cs, char *str) { register uint32 l; - register uchar *map=cs->to_upper; + register uchar *map= cs->to_upper; + char *str_orig= str; while (*str) { /* Pointing after the '\0' is safe here. */ - if ((l=my_ismbchar(cs, str, str + cs->mbmaxlen))) - str+=l; + if ((l= my_ismbchar(cs, str, str + cs->mbmaxlen))) + str+= l; else { - *str=(char) map[(uchar)*str]; + *str= (char) map[(uchar)*str]; str++; } } + return str - str_orig; } -void my_casedn_str_mb(CHARSET_INFO * cs, char *str) +uint my_casedn_str_mb(CHARSET_INFO * cs, char *str) { register uint32 l; - register uchar *map=cs->to_lower; + register uchar *map= cs->to_lower; + char *str_orig= str; while (*str) { /* Pointing after the '\0' is safe here. */ - if ((l=my_ismbchar(cs, str, str + cs->mbmaxlen))) - str+=l; + if ((l= my_ismbchar(cs, str, str + cs->mbmaxlen))) + str+= l; else { - *str=(char) map[(uchar)*str]; + *str= (char) map[(uchar)*str]; str++; } } + return str - str_orig; } uint my_caseup_mb(CHARSET_INFO * cs, char *src, uint srclen, diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 9b45d5a03b7..9d10ba82114 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -188,20 +188,26 @@ int my_strnncollsp_simple(CHARSET_INFO * cs, const uchar *a, uint a_length, } -void my_caseup_str_8bit(CHARSET_INFO * cs,char *str) +uint my_caseup_str_8bit(CHARSET_INFO * cs,char *str) { - register uchar *map=cs->to_upper; - while ((*str = (char) map[(uchar) *str]) != 0) + register uchar *map= cs->to_upper; + char *str_orig= str; + while ((*str= (char) map[(uchar) *str]) != 0) str++; + return str - str_orig; } -void my_casedn_str_8bit(CHARSET_INFO * cs,char *str) + +uint my_casedn_str_8bit(CHARSET_INFO * cs,char *str) { - register uchar *map=cs->to_lower; - while ((*str = (char) map[(uchar)*str]) != 0) + register uchar *map= cs->to_lower; + char *str_orig= str; + while ((*str= (char) map[(uchar) *str]) != 0) str++; + return str - str_orig; } + uint my_caseup_8bit(CHARSET_INFO * cs, char *src, uint srclen, char *dst __attribute__((unused)), uint dstlen __attribute__((unused))) diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c index df43eff3d73..5089db6bf48 100644 --- a/strings/ctype-ucs2.c +++ b/strings/ctype-ucs2.c @@ -159,13 +159,13 @@ static void my_hash_sort_ucs2(CHARSET_INFO *cs, const uchar *s, uint slen, } -static void my_caseup_str_ucs2(CHARSET_INFO * cs __attribute__((unused)), +static uint my_caseup_str_ucs2(CHARSET_INFO * cs __attribute__((unused)), char * s __attribute__((unused))) { + return 0; } - static uint my_casedn_ucs2(CHARSET_INFO *cs, char *src, uint srclen, char *dst __attribute__((unused)), uint dstlen __attribute__((unused))) @@ -188,9 +188,11 @@ static uint my_casedn_ucs2(CHARSET_INFO *cs, char *src, uint srclen, return srclen; } -static void my_casedn_str_ucs2(CHARSET_INFO *cs __attribute__((unused)), + +static uint my_casedn_str_ucs2(CHARSET_INFO *cs __attribute__((unused)), char * s __attribute__((unused))) { + return 0; } diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c index 6c3ceaf868b..8a4ed48bef5 100644 --- a/strings/ctype-utf8.c +++ b/strings/ctype-utf8.c @@ -2047,6 +2047,52 @@ static int my_utf8_uni(CHARSET_INFO *cs __attribute__((unused)), return MY_CS_ILSEQ; } + +/* + The same as above, but without range check + for example, for a null-terminated string +*/ +static int my_utf8_uni_no_range(CHARSET_INFO *cs __attribute__((unused)), + my_wc_t * pwc, const uchar *s) +{ + unsigned char c; + + c= s[0]; + if (c < 0x80) + { + *pwc = c; + return 1; + } + + if (c < 0xc2) + return MY_CS_ILSEQ; + + if (c < 0xe0) + { + if (!((s[1] ^ 0x80) < 0x40)) + return MY_CS_ILSEQ; + + *pwc = ((my_wc_t) (c & 0x1f) << 6) | (my_wc_t) (s[1] ^ 0x80); + return 2; + } + + if (c < 0xf0) + { + if (!((s[1] ^ 0x80) < 0x40 && + (s[2] ^ 0x80) < 0x40 && + (c >= 0xe1 || s[1] >= 0xa0))) + return MY_CS_ILSEQ; + + *pwc= ((my_wc_t) (c & 0x0f) << 12) | + ((my_wc_t) (s[1] ^ 0x80) << 6) | + (my_wc_t) (s[2] ^ 0x80); + + return 3; + } + return MY_CS_ILSEQ; +} + + static int my_uni_utf8 (CHARSET_INFO *cs __attribute__((unused)) , my_wc_t wc, uchar *r, uchar *e) { @@ -2093,6 +2139,34 @@ static int my_uni_utf8 (CHARSET_INFO *cs __attribute__((unused)) , } +/* + The same as above, but without range check. +*/ +static int my_uni_utf8_no_range(CHARSET_INFO *cs __attribute__((unused)), + my_wc_t wc, uchar *r) +{ + int count; + + if (wc < 0x80) + count= 1; + else if (wc < 0x800) + count= 2; + else if (wc < 0x10000) + count= 3; + else + return MY_CS_ILUNI; + + switch (count) + { + /* Fall through all cases!!! */ + case 3: r[2]= (uchar) (0x80 | (wc & 0x3f)); wc= wc >> 6; wc |= 0x800; + case 2: r[1]= (uchar) (0x80 | (wc & 0x3f)); wc= wc >> 6; wc |= 0xc0; + case 1: r[0]= (uchar) wc; + } + return count; +} + + static uint my_caseup_utf8(CHARSET_INFO *cs, char *src, uint srclen, char *dst, uint dstlen) { @@ -2143,10 +2217,26 @@ static void my_hash_sort_utf8(CHARSET_INFO *cs, const uchar *s, uint slen, } -static void my_caseup_str_utf8(CHARSET_INFO * cs, char * s) +static uint my_caseup_str_utf8(CHARSET_INFO *cs, char *src) { - uint len= (uint) strlen(s); - my_caseup_utf8(cs, s, len, s, len); + my_wc_t wc; + int srcres, dstres; + char *dst= src, *dst0= src; + MY_UNICASE_INFO **uni_plane= cs->caseinfo; + DBUG_ASSERT(cs->caseup_multiply == 1); + + while (*src && + (srcres= my_utf8_uni_no_range(cs, &wc, (uchar *) src)) > 0) + { + int plane= (wc>>8) & 0xFF; + wc= uni_plane[plane] ? uni_plane[plane][wc & 0xFF].toupper : wc; + if ((dstres= my_uni_utf8_no_range(cs, wc, (uchar*) dst)) <= 0) + break; + src+= srcres; + dst+= dstres; + } + *dst= '\0'; + return (uint) (dst - dst0); } @@ -2172,10 +2262,43 @@ static uint my_casedn_utf8(CHARSET_INFO *cs, char *src, uint srclen, return (uint) (dst - dst0); } -static void my_casedn_str_utf8(CHARSET_INFO *cs, char * s) + +static uint my_casedn_str_utf8(CHARSET_INFO *cs, char *src) { - uint len= (uint) strlen(s); - my_casedn_utf8(cs, s, len, s, len); + my_wc_t wc; + int srcres, dstres; + char *dst= src, *dst0= src; + MY_UNICASE_INFO **uni_plane= cs->caseinfo; + DBUG_ASSERT(cs->casedn_multiply == 1); + + while (*src && + (srcres= my_utf8_uni_no_range(cs, &wc, (uchar *) src)) > 0) + { + int plane= (wc>>8) & 0xFF; + wc= uni_plane[plane] ? uni_plane[plane][wc & 0xFF].tolower : wc; + if ((dstres= my_uni_utf8_no_range(cs, wc, (uchar*) dst)) <= 0) + break; + src+= srcres; + dst+= dstres; + } + + /* + In rare cases lower string can be shorter than + the original string, for example: + + "U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE" + (which is 0xC4B0 in utf8, i.e. two bytes) + + is converted into + + "U+0069 LATIN SMALL LETTER I" + (which is 0x69 in utf8, i.e. one byte) + + So, we need to put '\0' terminator after converting. + */ + + *dst= '\0'; + return (uint) (dst - dst0); } @@ -4051,6 +4174,7 @@ static MY_CHARSET_HANDLER my_charset_filename_handler= my_strntoull_8bit, my_strntod_8bit, my_strtoll10_8bit, + my_strntoull10rnd_8bit, my_scan_8bit }; diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 60548210b7d..d808ade8342 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -318,6 +318,7 @@ BuildMySQL "--enable-shared \ --with-example-storage-engine \ --with-blackhole-storage-engine \ --with-federated-storage-engine \ + --with-partition \ --with-big-tables \ --with-comment=\"MySQL Community Server - Debug (GPL)\"") @@ -328,7 +329,7 @@ then fi (cd mysql-debug-%{mysql_version}/mysql-test ; \ - ./mysql-test-run.pl --comment=debug --skip-rpl --skip-ndbcluster --force ; \ + ./mysql-test-run.pl --comment=debug --skip-rpl --skip-ndbcluster --force --report-features ; \ true) ############################################################################## @@ -348,6 +349,7 @@ BuildMySQL "--enable-shared \ --with-example-storage-engine \ --with-blackhole-storage-engine \ --with-federated-storage-engine \ + --with-partition \ --with-embedded-server \ --with-big-tables \ --with-comment=\"MySQL Community Server (GPL)\"") @@ -358,7 +360,7 @@ then fi cd mysql-release-%{mysql_version}/mysql-test -./mysql-test-run.pl --comment=normal --force --skip-ndbcluster --timer || true +./mysql-test-run.pl --comment=normal --force --skip-ndbcluster --timer --report-features || true ./mysql-test-run.pl --comment=ps --ps-protocol --force --skip-ndbcluster --timer || true ./mysql-test-run.pl --comment=normal+rowrepl --mysqld=--binlog-format=row --force --skip-ndbcluster --timer || true ./mysql-test-run.pl --comment=ps+rowrepl+NDB --ps-protocol --mysqld=--binlog-format=row --force --timer || true @@ -685,6 +687,12 @@ fi # itself - note that they must be ordered by date (important when # merging BK trees) %changelog +* Mon Nov 13 2006 Joerg Bruehe <joerg@mysql.com> + +- Add "--with-partition" to all server builds. + +- Use "--report-features" in one test run per server build. + * Tue Aug 15 2006 Joerg Bruehe <joerg@mysql.com> - The "max" server is removed from packages, effective from 5.1.12-beta. diff --git a/unittest/README.txt b/unittest/README.txt index e862a54e443..ea94a50595b 100644 --- a/unittest/README.txt +++ b/unittest/README.txt @@ -46,6 +46,8 @@ test won't be executed by 'make test' ! Documentation ------------- -There is Doxygen-generated documentation available at: +The generated documentation is temporarily placed at: - https://intranet.mysql.com/~mkindahl/mytap/html/ + http://www.kindahl.net/mytap/doc/ + +I will move it to a better place once I figure out where and how. diff --git a/unittest/mytap/Doxyfile b/unittest/mytap/Doxyfile index 8c23703e925..1b1c82b4c98 100644 --- a/unittest/mytap/Doxyfile +++ b/unittest/mytap/Doxyfile @@ -432,7 +432,7 @@ FILE_PATTERNS = # subdirectories should be searched for input files as well. Possible # values are YES and NO. If left blank NO is used. -RECURSIVE = YES +RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that # should excluded from the INPUT source files. This way you can easily @@ -457,14 +457,14 @@ EXCLUDE_PATTERNS = # directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = e # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. -EXAMPLE_PATTERNS = +EXAMPLE_PATTERNS = *.c # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude @@ -926,7 +926,7 @@ MACRO_EXPANSION = YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. -EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. @@ -939,33 +939,34 @@ SEARCH_INCLUDES = YES INCLUDE_PATH = -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more +# wildcard patterns (like *.h and *.hpp) to filter out the +# header-files in the directories. If left blank, the patterns +# specified with FILE_PATTERNS will be used. INCLUDE_FILE_PATTERNS = -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. +# The PREDEFINED tag can be used to specify one or more macro names +# that are defined before the preprocessor is started (similar to the +# -D option of gcc). The argument of the tag is a list of macros of +# the form: name or name=definition (no spaces). If the definition and +# the = are omitted =1 is assumed. PREDEFINED = -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES +# then this tag can be used to specify a list of macro names that +# should be expanded. The macro definition that is found in the +# sources will be used. Use the PREDEFINED tag if you want to use a +# different macro definition. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = __attribute__ -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse the -# parser if not removed. +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are +# alone on a line, have an all uppercase name, and do not end with a +# semicolon. Such function macros are typically used for boiler-plate +# code, and will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 54292f3b828..29dc765950f 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -29,11 +29,17 @@ #include <signal.h> /** + @defgroup MyTAP_Internal MyTAP Internals + + Internal functions and data structures for the MyTAP implementation. +*/ + +/** Test data structure. Data structure containing all information about the test suite. - @ingroup MyTAP + @ingroup MyTAP_Internal */ static TEST_DATA g_test = { 0, 0, 0, "" }; @@ -41,6 +47,8 @@ static TEST_DATA g_test = { 0, 0, 0, "" }; Output stream for test report message. The macro is just a temporary solution. + + @ingroup MyTAP_Internal */ #define tapout stdout @@ -50,7 +58,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" }; To emit the directive, use the emit_dir() function - @ingroup MyTAP + @ingroup MyTAP_Internal @see emit_dir @@ -59,7 +67,7 @@ static TEST_DATA g_test = { 0, 0, 0, "" }; @param ap Vararg list for the description string above. */ static void -emit_tap(int pass, char const *fmt, va_list ap) +vemit_tap(int pass, char const *fmt, va_list ap) { fprintf(tapout, "%sok %d%s", pass ? "" : "not ", @@ -80,18 +88,22 @@ emit_tap(int pass, char const *fmt, va_list ap) not ok 2 # todo some text explaining what remains @endcode + @ingroup MyTAP_Internal + @param dir Directive as a string - @param exp Explanation string + @param why Explanation string */ static void -emit_dir(const char *dir, const char *exp) +emit_dir(const char *dir, const char *why) { - fprintf(tapout, " # %s %s", dir, exp); + fprintf(tapout, " # %s %s", dir, why); } /** Emit a newline to the TAP output stream. + + @ingroup MyTAP_Internal */ static void emit_endl() @@ -198,7 +210,7 @@ ok(int const pass, char const *fmt, ...) if (!pass && *g_test.todo == '\0') ++g_test.failed; - emit_tap(pass, fmt, ap); + vemit_tap(pass, fmt, ap); va_end(ap); if (*g_test.todo != '\0') emit_dir("todo", g_test.todo); @@ -223,7 +235,7 @@ skip(int how_many, char const *const fmt, ...) while (how_many-- > 0) { va_list ap; - emit_tap(1, NULL, ap); + vemit_tap(1, NULL, ap); emit_dir("skip", reason); emit_endl(); } @@ -316,7 +328,7 @@ int exit_status() { @section UnitTest Writing unit tests The purpose of writing unit tests is to use them to drive component - development towards a solution that the tests. This means that the + development towards a solution that passes the tests. This means that the unit tests has to be as complete as possible, testing at least: - Normal input @@ -325,29 +337,240 @@ int exit_status() { - Error handling - Bad environment - We will go over each case and explain it in more detail. + @subsection NormalSubSec Normal input + + This is to test that the component have the expected behaviour. + This is just plain simple: test that it works. For example, test + that you can unpack what you packed, adding gives the sum, pincing + the duck makes it quack. + + This is what everybody does when they write tests. + + + @subsection BorderlineTests Borderline cases + + If you have a size anywhere for your component, does it work for + size 1? Size 0? Sizes close to <code>UINT_MAX</code>? + + It might not be sensible to have a size 0, so in this case it is + not a borderline case, but rather a faulty input (see @ref + FaultyInputTests). + + + @subsection FaultyInputTests Faulty input + + Does your bitmap handle 0 bits size? Well, it might not be designed + for it, but is should <em>not</em> crash the application, but + rather produce an error. This is called defensive programming. - @subsection NormalSSec Normal input + Unfortunately, adding checks for values that should just not be + entered at all is not always practical: the checks cost cycles and + might cost more than it's worth. For example, some functions are + designed so that you may not give it a null pointer. In those + cases it's not sensible to pass it <code>NULL</code> just to see it + crash. - @subsection BorderlineSSec Borderline cases + Since every experienced programmer add an <code>assert()</code> to + ensure that you get a proper failure for the debug builds when a + null pointer passed (you add asserts too, right?), you will in this + case instead have a controlled (early) crash in the debug build. - @subsection FaultySSec Faulty input - @subsection ErrorSSec Error handling + @subsection ErrorHandlingTests Error handling - @subsection EnvironmentSSec Environment + This is testing that the errors your component is designed to give + actually are produced. For example, testing that trying to open a + non-existing file produces a sensible error code. + + + @subsection BadEnvironmentTests Environment Sometimes, modules has to behave well even when the environment - fails to work correctly. Typical examples are: out of dynamic - memory, disk is full, + fails to work correctly. Typical examples are when the computer is + out of dynamic memory or when the disk is full. You can emulate + this by replacing, e.g., <code>malloc()</code> with your own + version that will work for a while, but then fail. Some things are + worth to keep in mind here: + + - Make sure to make the function fail deterministically, so that + you really can repeat the test. + + - Make sure that it doesn't just fail immediately. The unit might + have checks for the first case, but might actually fail some time + in the near future. - @section UnitTestSec How to structure a unit test + + @section UnitTest How to structure a unit test In this section we will give some advice on how to structure the - unit tests to make the development run smoothly. + unit tests to make the development run smoothly. The basic + structure of a test is: + + - Plan + - Test + - Report + + + @subsection TestPlanning Plan the test + + Planning the test means telling how many tests there are. In the + event that one of the tests causes a crash, it is then possible to + see that there are fewer tests than expected, and print a proper + error message. + + To plan a test, use the @c plan() function in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + . + . + . + } + @endcode + + If you don't call the @c plan() function, the number of tests + executed will be printed at the end. This is intended to be used + while developing the unit and you are constantly adding tests. It + is not indented to be used after the unit has been released. + - @subsection PieceSec Test each piece separately + @subsection TestRunning Execute the test + + To report the status of a test, the @c ok() function is used in the + following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + } + @endcode + + This will print a test result line on the standard output in TAP + format, which allows TAP handling frameworks (like Test::Harness) + to parse the status of the test. + + @subsection TestReport Report the result of the test + + At the end, a complete test report should be written, with some + statistics. If the test returns EXIT_SUCCESS, all tests were + successfull, otherwise at least one test failed. + + To get a TAP complient output and exit status, report the exit + status in the following manner: + + @code + int main(int argc, char *argv[]) + { + plan(5); + ok(ducks == paddling_ducks, + "%d ducks did not paddle", ducks - paddling_ducks); + . + . + . + return exit_status(); + } + @endcode + + @section DontDoThis Ways to not do unit testing + + In this section, we'll go through some quite common ways to write + tests that are <em>not</em> a good idea. + + @subsection BreadthFirstTests Doing breadth-first testing + + If you're writing a library with several functions, don't test all + functions using size 1, then all functions using size 2, etc. If a + test for size 42 fails, you have no easy way of tracking down why + it failed. + + It is better to concentrate on getting one function to work at a + time, which means that you test each function for all sizes that + you think is reasonable. Then you continue with the next function, + doing the same. This is usually also the way that a library is + developed (one function at a time) so stick to testing that is + appropriate for now the unit is developed. + + @subsection JustToBeSafeTest Writing unnecessarily large tests + + Don't write tests that use parameters in the range 1-1024 unless + you have a very good reason to belive that the component will + succeed for 562 but fail for 564 (the numbers picked are just + examples). + + It is very common to write extensive tests "just to be safe." + Having a test suite with a lot of values might give you a warm + fuzzy feeling, but it doesn't really help you find the bugs. Good + tests fail; seriously, if you write a test that you expect to + succeed, you don't need to write it. If you think that it + <em>might</em> fail, <em>then</em> you should write it. + + Don't take this as an excuse to avoid writing any tests at all + "since I make no mistakes" (when it comes to this, there are two + kinds of people: those who admit they make mistakes, and those who + don't); rather, this means that there is no reason to test that + using a buffer with size 100 works when you have a test for buffer + size 96. + + The drawback is that the test suite takes longer to run, for little + or no benefit. It is acceptable to do a exhaustive test if it + doesn't take too long to run and it is quite common to do an + exhaustive test of a function for a small set of values. + Use your judgment to decide what is excessive: your milage may + vary. +*/ + +/** + @example simple.t.c + + This is an simple example of how to write a test using the + library. The output of this program is: + + @code + 1..1 + # Testing basic functions + ok 1 - Testing gcs() + @endcode + + The basic structure is: plan the number of test points using the + plan() function, perform the test and write out the result of each + test point using the ok() function, print out a diagnostics message + using diag(), and report the result of the test by calling the + exit_status() function. Observe that this test does excessive + testing (see @ref JustToBeSafeTest), but the test point doesn't + take very long time. +*/ + +/** + @example todo.t.c + + This example demonstrates how to use the <code>todo_start()</code> + and <code>todo_end()</code> function to mark a sequence of tests to + be done. Observe that the tests are assumed to fail: if any test + succeeds, it is considered a "bonus". +*/ + +/** + @example skip.t.c + + This is an example of how the <code>SKIP_BLOCK_IF</code> can be + used to skip a predetermined number of tests. Observe that the + macro actually skips the following statement, but it's not sensible + to use anything than a block. +*/ + +/** + @example skip_all.t.c - Don't test all functions using size 1, then all functions using - size 2, etc. + Sometimes, you skip an entire test because it's testing a feature + that doesn't exist on the system that you're testing. To skip an + entire test, use the <code>skip_all()</code> function according to + this example. */ diff --git a/unittest/mytap/tap.h b/unittest/mytap/tap.h index 51b8c7df04d..a47060fa3cc 100644 --- a/unittest/mytap/tap.h +++ b/unittest/mytap/tap.h @@ -33,6 +33,8 @@ /** Data about test plan. + @ingroup MyTAP_Internal + @internal We are using the "typedef struct X { ... } X" idiom to create class/struct X both in C and C++. */ @@ -61,6 +63,14 @@ extern "C" { #endif /** + @defgroup MyTAP_API MyTAP API + + MySQL support for performing unit tests according to TAP. + + @{ +*/ + +/** Set number of tests that is planned to execute. The function also accepts the predefined constant @@ -101,11 +111,14 @@ void ok(int pass, char const *fmt, ...) /** Skip a determined number of tests. - Function to print that <em>how_many</em> tests have been - skipped. The reason is printed for each skipped test. Observe - that this function does not do the actual skipping for you, it just - prints information that tests have been skipped. It shall be used - in the following manner: + Function to print that <em>how_many</em> tests have been skipped. + The reason is printed for each skipped test. Observe that this + function does not do the actual skipping for you, it just prints + information that tests have been skipped. This function is not + usually used, but rather the macro @c SKIP_BLOCK_IF, which does the + skipping for you. + + It shall be used in the following manner: @code if (ducks == 0) { @@ -192,8 +205,8 @@ void BAIL_OUT(char const *fmt, ...) return exit_status(); @endcode - @returns EXIT_SUCCESS if all tests passed, EXIT_FAILURE if one or - more tests failed. + @returns @c EXIT_SUCCESS if all tests passed, @c EXIT_FAILURE if + one or more tests failed. */ int exit_status(void); @@ -242,6 +255,7 @@ void todo_start(char const *message, ...) void todo_end(); +/** @} */ #ifdef __cplusplus } |