diff options
200 files changed, 11244 insertions, 1587 deletions
diff --git a/client/mysql.cc b/client/mysql.cc index 2d4b2bcc373..b102c80655a 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1092,6 +1092,17 @@ static int read_and_execute(bool interactive) if (!interactive) { line=batch_readline(status.line_buff); + /* + Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. + Editors like "notepad" put this marker in + the very beginning of a text file when + you save the file using "Unicode UTF-8" format. + */ + if (!line_number && + (uchar) line[0] == 0xEF && + (uchar) line[1] == 0xBB && + (uchar) line[2] == 0xBF) + line+= 3; line_number++; if (!glob_buffer.length()) status.query_start_line=line_number; @@ -2155,7 +2166,7 @@ com_go(String *buffer,char *line __attribute__((unused))) { char buff[200], time_buff[32], *pos; MYSQL_RES *result; - ulong timer, warnings; + ulong timer, warnings= 0; uint error= 0; int err= 0; @@ -2305,7 +2316,8 @@ com_go(String *buffer,char *line __attribute__((unused))) end: - if (show_warnings == 1 && warnings >= 1) /* Show warnings if any */ + /* Show warnings if any or error occured */ + if (show_warnings == 1 && (warnings >= 1 || error)) print_warnings(); if (!error && !status.batch && diff --git a/include/my_base.h b/include/my_base.h index 339554979a8..c2eb6a040c1 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -187,7 +187,13 @@ enum ha_extra_function { Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY. */ - HA_EXTRA_INSERT_WITH_UPDATE + HA_EXTRA_INSERT_WITH_UPDATE, + /* + Orders MERGE handler to attach or detach its child tables. Used at + begin and end of a statement. + */ + HA_EXTRA_ATTACH_CHILDREN, + HA_EXTRA_DETACH_CHILDREN }; /* The following is parameter to ha_panic() */ @@ -407,9 +413,11 @@ enum ha_base_keytype { #define HA_ERR_RECORD_IS_THE_SAME 169 /* row not actually updated : new values same as the old values */ -#define HA_ERR_LOGGING_IMPOSSIBLE 170 /* It is not possible to log this - statement */ -#define HA_ERR_LAST 170 /*Copy last error nr.*/ +#define HA_ERR_LOGGING_IMPOSSIBLE 170 /* It is not possible to log this + statement */ +#define HA_ERR_CORRUPT_EVENT 171 /* The event was corrupt, leading to + illegal data being read */ +#define HA_ERR_LAST 171 /*Copy last error nr.*/ /* Add error numbers before HA_ERR_LAST and change it accordingly. */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) diff --git a/include/myisammrg.h b/include/myisammrg.h index eed50bebaee..0cf3aa222b3 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -69,6 +69,8 @@ typedef struct st_myrg_info uint merge_insert_method; uint tables,options,reclength,keys; my_bool cache_in_use; + /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */ + my_bool children_attached; LIST open_list; QUEUE by_key; ulong *rec_per_key_part; /* for sql optimizing */ @@ -80,6 +82,13 @@ typedef struct st_myrg_info extern int myrg_close(MYRG_INFO *file); extern int myrg_delete(MYRG_INFO *file,const uchar *buff); extern MYRG_INFO *myrg_open(const char *name,int mode,int wait_if_locked); +extern MYRG_INFO *myrg_parent_open(const char *parent_name, + int (*callback)(void*, const char*), + void *callback_param); +extern int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, + MI_INFO *(*callback)(void*), + void *callback_param); +extern int myrg_detach_children(MYRG_INFO *m_info); extern int myrg_panic(enum ha_panic_function function); extern int myrg_rfirst(MYRG_INFO *file,uchar *buf,int inx); extern int myrg_rlast(MYRG_INFO *file,uchar *buf,int inx); diff --git a/mysql-test/extra/binlog_tests/blackhole.test b/mysql-test/extra/binlog_tests/blackhole.test index 1ca59955d76..df2295af4ff 100644 --- a/mysql-test/extra/binlog_tests/blackhole.test +++ b/mysql-test/extra/binlog_tests/blackhole.test @@ -134,6 +134,15 @@ drop table t1,t2,t3; # table # CREATE TABLE t1(a INT) ENGINE=BLACKHOLE; +# NOTE: After exchanging open_ltable() by open_and_lock_tables() in +# handle_delayed_insert() to fix problems with MERGE tables (Bug#26379), +# problems with INSERT DELAYED and BLACKHOLE popped up. open_ltable() +# does not check if the binlogging capabilities of the statement and the +# table match. So the below used to succeed. But since INSERT DELAYED +# switches to row-based logging in mixed-mode and BLACKHOLE cannot do +# row-based logging, it could not really work. Until this problem is +# correctly fixed, we have that error here. +--error ER_BINLOG_LOGGING_IMPOSSIBLE INSERT DELAYED INTO t1 VALUES(1); DROP TABLE t1; diff --git a/mysql-test/extra/binlog_tests/database.test b/mysql-test/extra/binlog_tests/database.test new file mode 100644 index 00000000000..11a8f53a6d7 --- /dev/null +++ b/mysql-test/extra/binlog_tests/database.test @@ -0,0 +1,15 @@ +source include/have_log_bin.inc; +source include/not_embedded.inc; + +# Checking that the drop of a database does not replicate anything in +# addition to the drop of the database + +reset master; +create database testing_1; +use testing_1; +create table t1 (a int); +create function sf1 (a int) returns int return a+1; +create trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a); +create procedure sp1 (a int) insert into t1 values(a); +drop database testing_1; +source include/show_binlog_events.inc; diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test new file mode 100644 index 00000000000..03514bfdb55 --- /dev/null +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test @@ -0,0 +1,298 @@ +# the file to be sourced from binlog.binlog_mix_innodb_myisam + +# +# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack +# bug #28960 non-trans temp table changes with insert .. select +# not binlogged after rollback +# +# testing appearence of insert into temp_table in binlog. +# There are two branches of execution that require different setup. + +# checking binlog content filled with row-based events due to +# a used stored function modifies non-transactional table + +## send_eof() branch + +# prepare + +create temporary table tt (a int unique); +create table ti (a int) engine=innodb; +reset master; +show master status; + +# action + +begin; +insert into ti values (1); +insert into ti values (2) ; +insert into tt select * from ti; +rollback; + +# check + +select count(*) from tt /* 2 */; +show master status; +source include/show_binlog_events.inc; +select count(*) from ti /* zero */; +insert into ti select * from tt; +select * from ti /* that is what slave would miss - bug#28960 */; + + +## send_error() branch +delete from ti; +delete from tt where a=1; +reset master; +show master status; + +# action + +begin; +insert into ti values (1); +insert into ti values (2) /* to make the dup error in the following */; +--error ER_DUP_ENTRY +insert into tt select * from ti /* one affected and error */; +rollback; + +# check + +show master status; +source include/show_binlog_events.inc; # nothing in binlog with row bilog format +select count(*) from ti /* zero */; +insert into ti select * from tt; +select * from tt /* that is what otherwise slave missed - the bug */; + +drop table ti; + + +# +# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack +# +# Testing asserts: if there is a side effect of modifying non-transactional +# table thd->no_trans_update.stmt must be TRUE; +# the assert is active with debug build +# + +--disable_warnings +drop function if exists bug27417; +drop table if exists t1,t2; +--enable_warnings +# side effect table +CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; +# target tables +CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a)); + +delimiter |; +create function bug27417(n int) +RETURNS int(11) +begin + insert into t1 values (null); + return n; +end| +delimiter ;| + +reset master; + +# execute + +insert into t2 values (bug27417(1)); +insert into t2 select bug27417(2); +reset master; + +--error ER_DUP_ENTRY +insert into t2 values (bug27417(2)); +source include/show_binlog_events.inc; #only (!) with fixes for #23333 will show there is the query +select count(*) from t1 /* must be 3 */; + +reset master; +select count(*) from t2; +delete from t2 where a=bug27417(3); +select count(*) from t2 /* nothing got deleted */; +source include/show_binlog_events.inc; # the query must be in regardless of #23333 +select count(*) from t1 /* must be 5 */; + +--enable_info +delete t2 from t2 where t2.a=bug27417(100) /* must not affect t2 */; +--disable_info +select count(*) from t1 /* must be 7 */; + +# function bug27417 remains for the following testing of bug#23333 +drop table t1,t2; + +# +# Bug#23333 using the patch (and the test) for bug#27471 +# throughout the bug tests +# t1 - non-trans side effects gatherer; +# t2 - transactional table; +# +CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; +CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique) ENGINE=MyISAM; +CREATE TABLE t4 (a int, PRIMARY KEY (a), b int unique) ENGINE=Innodb; +CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; + + +# +# INSERT +# + +# prepare + + insert into t2 values (1); + reset master; + +# execute + + --error ER_DUP_ENTRY + insert into t2 values (bug27417(1)); + +# check + + source include/show_binlog_events.inc; # must be event of the query + select count(*) from t1 /* must be 1 */; + +# +# INSERT SELECT +# + +# prepare + delete from t1; + delete from t2; + insert into t2 values (2); + reset master; + +# execute + + --error ER_DUP_ENTRY + insert into t2 select bug27417(1) union select bug27417(2); + +# check + + source include/show_binlog_events.inc; # must be events of the query + select count(*) from t1 /* must be 2 */; + +# +# UPDATE inc multi-update +# + +# prepare + delete from t1; + insert into t3 values (1,1),(2,3),(3,4); + reset master; + +# execute + --error ER_DUP_ENTRY + update t3 set b=b+bug27417(1); + +# check + source include/show_binlog_events.inc; # must be events of the query + select count(*) from t1 /* must be 2 */; + +## multi_update::send_eof() branch + +# prepare + delete from t3; + delete from t4; + insert into t3 values (1,1); + insert into t4 values (1,1),(2,2); + + reset master; + +# execute + --error ER_DUP_ENTRY + UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */; + +# check + source include/show_binlog_events.inc; # the offset must denote there is the query + select count(*) from t1 /* must be 4 */; + +## send_error() branch of multi_update + +# prepare + delete from t1; + delete from t3; + delete from t4; + insert into t3 values (1,1),(2,2); + insert into t4 values (1,1),(2,2); + + reset master; + +# execute + --error ER_DUP_ENTRY + UPDATE t3,t4 SET t3.a=t4.a + bug27417(1); + +# check + select count(*) from t1 /* must be 1 */; + +# cleanup + drop table t4; + + +# +# DELETE incl multi-delete +# + +# prepare + delete from t1; + delete from t2; + delete from t3; + insert into t2 values (1); + insert into t3 values (1,1); + create trigger trg_del before delete on t2 for each row + insert into t3 values (bug27417(1), 2); + reset master; + +# execute + --error ER_DUP_ENTRY + delete from t2; +# check + source include/show_binlog_events.inc; # the offset must denote there is the query + select count(*) from t1 /* must be 1 */; + +# cleanup + drop trigger trg_del; + +# prepare + delete from t1; + delete from t2; + delete from t5; + create trigger trg_del_t2 after delete on t2 for each row + insert into t1 values (1); + insert into t2 values (2),(3); + insert into t5 values (1),(2); + reset master; + +# execute + --error ER_DUP_ENTRY + delete t2.* from t2,t5 where t2.a=t5.a + 1; + +# check + source include/show_binlog_events.inc; # must be events of the query + select count(*) from t1 /* must be 1 */; + + +# +# LOAD DATA +# + +# prepare + delete from t1; + create table t4 (a int default 0, b int primary key) engine=innodb; + insert into t4 values (0, 17); + reset master; + +# execute + --error ER_DUP_ENTRY + load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); +# check + select * from t4; + select count(*) from t1 /* must be 2 */; + source include/show_binlog_events.inc; # must be events of the query + +# +# bug#23333 cleanup +# + + +drop trigger trg_del_t2; +drop table t1,t2,t3,t4,t5; +drop function bug27417; diff --git a/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test b/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test index d3959d10306..cdd828305dc 100644 --- a/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test +++ b/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test @@ -410,7 +410,7 @@ binary data'; select * from t2 order by f1; select * from t3 order by f1; select * from t4 order by f1; - select * from t31 order by f1; + select * from t31 order by f3; connection master; --echo diff --git a/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test index a4ac55cc2e8..6fa2c9ac1b5 100644 --- a/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test +++ b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test @@ -10,7 +10,7 @@ ########### 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; +DROP TABLE IF EXISTS t1, t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t14a,t15,t16,t17; --enable_query_log --enable_warnings @@ -662,6 +662,68 @@ sync_slave_with_master; --replace_column 7 CURRENT_TIMESTAMP SELECT * FROM t14 ORDER BY c1; +#################################################### +# - Alter Master drop column at end of table # +# Expect: column dropped # +#################################################### + +--echo *** Create t14a on slave *** +STOP SLAVE; +RESET SLAVE; +eval CREATE TABLE t14a (c1 INT KEY, c4 BLOB, c5 CHAR(5), + c6 INT DEFAULT '1', + c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP + )ENGINE=$engine_type; + +--echo *** Create t14a on Master *** +connection master; +eval CREATE TABLE t14a (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; +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14a () VALUES(1,@b1,'Kyle'), + (2,@b1,'JOE'), + (3,@b1,'QA'); + +SELECT * FROM t14a ORDER BY c1; +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 5 CURRENT_TIMESTAMP +SELECT * FROM t14a ORDER BY c1; +STOP SLAVE; +RESET SLAVE; + +--echo *** Master Drop c5 *** +connection master; +ALTER TABLE t14a DROP COLUMN c5; +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 t14a () VALUES(4,@b1), + (5,@b1), + (6,@b1); +SELECT * FROM t14a ORDER BY c1; + +--echo *** Select on Slave **** +sync_slave_with_master; +--replace_column 5 CURRENT_TIMESTAMP +SELECT * FROM t14a ORDER BY c1; #################################################### # - Alter Master Dropping columns from the middle. # @@ -736,7 +798,7 @@ connection slave; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 16 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS -SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; --echo *** Try to insert in master **** @@ -744,6 +806,8 @@ connection master; INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); SELECT * FROM t15 ORDER BY c1; +#SHOW BINLOG EVENTS; + --echo *** Try to select from slave **** sync_slave_with_master; --replace_column 7 CURRENT_TIMESTAMP @@ -856,7 +920,10 @@ 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; +connection master; +DROP TABLE IF EXISTS t1, t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t14a,t15,t16,t17; +sync_slave_with_master; +connection master; --enable_query_log --enable_warnings diff --git a/mysql-test/extra/rpl_tests/rpl_row_basic.test b/mysql-test/extra/rpl_tests/rpl_row_basic.test index 6de254d9931..c35a53f15bc 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_basic.test +++ b/mysql-test/extra/rpl_tests/rpl_row_basic.test @@ -215,11 +215,36 @@ sync_slave_with_master; --echo --- on slave --- SELECT * FROM t8 ORDER BY a; -# -# Test conflicting operations when changing in a table referenced by a -# foreign key. We'll reuse the above table and just add a table that -# references it. -# +# BUG#31552: Replication breaks when deleting rows from out-of-sync +# table without PK + +--echo **** Test for BUG#31552 **** + +--echo **** On Master **** +# Clean up t1 so that we can use it. +connection master; +DELETE FROM t1; +sync_slave_with_master; + +# Just to get a clean binary log +source include/reset_master_and_slave.inc; + +--echo **** On Master **** +connection master; +INSERT INTO t1 VALUES ('K','K'), ('L','L'), ('M','M'); +--echo **** On Master **** +sync_slave_with_master; +DELETE FROM t1 WHERE C1 = 'L'; + +connection master; +DELETE FROM t1; +query_vertical SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +sync_slave_with_master; +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1); +disable_query_log; +eval SELECT "$last_error" AS Last_SQL_Error; +enable_query_log; +query_vertical SELECT COUNT(*) FROM t1 ORDER BY c1,c2; # # cleanup @@ -227,3 +252,4 @@ SELECT * FROM t8 ORDER BY a; connection master; DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8; +sync_slave_with_master; diff --git a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test index 64a8de7c6a0..76db74acfa1 100644 --- a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test +++ b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test @@ -1,17 +1,16 @@ - ---disable_query_log ---disable_warnings connection slave; STOP SLAVE; +source include/wait_for_slave_to_stop.inc; connection master; +--disable_warnings DROP TABLE IF EXISTS t1; -RESET MASTER; +--enable_warnings connection slave; +--disable_warnings DROP TABLE IF EXISTS t1; +--enable_warnings RESET SLAVE; START SLAVE; ---enable_warnings ---enable_query_log --echo **** On Master **** connection master; @@ -38,3 +37,6 @@ connection master; DROP TABLE t1; let $SERVER_VERSION=`select version()`; source include/show_binlog_events.inc; + +connection master; +RESET MASTER; diff --git a/mysql-test/include/ctype_regex.inc b/mysql-test/include/ctype_regex.inc new file mode 100644 index 00000000000..0e6b4c41607 --- /dev/null +++ b/mysql-test/include/ctype_regex.inc @@ -0,0 +1,42 @@ +# +# To test a desired collation, set session.collation_connection to +# this collation before including this file +# + +--disable_warnings +drop table if exists t1; +--enable_warnings + +# +# Create a table with two varchar(64) null-able column, +# using current values of +# @@character_set_connection and @@collation_connection. +# + +create table t1 as +select repeat(' ', 64) as s1, repeat(' ',64) as s2 +union +select null, null; +show create table t1; +delete from t1; + +insert into t1 values('aaa','aaa'); +insert into t1 values('aaa|qqq','qqq'); +insert into t1 values('gheis','^[^a-dXYZ]+$'); +insert into t1 values('aab','^aa?b'); +insert into t1 values('Baaan','^Ba*n'); +insert into t1 values('aaa','qqq|aaa'); +insert into t1 values('qqq','qqq|aaa'); + +insert into t1 values('bbb','qqq|aaa'); +insert into t1 values('bbb','qqq'); +insert into t1 values('aaa','aba'); + +insert into t1 values(null,'abc'); +insert into t1 values('def',null); +insert into t1 values(null,null); +insert into t1 values('ghi','ghi['); + +select HIGH_PRIORITY s1 regexp s2 from t1; + +drop table t1; diff --git a/mysql-test/include/innodb_rollback_on_timeout.inc b/mysql-test/include/innodb_rollback_on_timeout.inc index 73c7374c79e..6be47397e4b 100644 --- a/mysql-test/include/innodb_rollback_on_timeout.inc +++ b/mysql-test/include/innodb_rollback_on_timeout.inc @@ -2,6 +2,10 @@ # Bug #24200: Provide backwards compatibility mode for 4.x "rollback on # transaction timeout" # +--disable_warnings +drop table if exists t1; +--enable_warnings + show variables like 'innodb_rollback_on_timeout'; create table t1 (a int unsigned not null primary key) engine = innodb; insert into t1 values (1); diff --git a/mysql-test/include/reset_master_and_slave.inc b/mysql-test/include/reset_master_and_slave.inc new file mode 100644 index 00000000000..c2d4120ddc9 --- /dev/null +++ b/mysql-test/include/reset_master_and_slave.inc @@ -0,0 +1,10 @@ +--echo **** Resetting master and slave **** +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; +RESET SLAVE; +connection master; +RESET MASTER; +connection slave; +START SLAVE; +source include/wait_for_slave_to_start.inc; diff --git a/mysql-test/include/wait_for_slave_sql_error.inc b/mysql-test/include/wait_for_slave_sql_error.inc new file mode 100644 index 00000000000..6780edbe2f0 --- /dev/null +++ b/mysql-test/include/wait_for_slave_sql_error.inc @@ -0,0 +1,33 @@ +################################################### +#Author: Sven +#Date: 2007-10-09 +#Purpose: Wait until the slave has an error in the +# sql thread, as indicated by +# "SHOW SLAVE STATUS", or at most 30 +# seconds. +#Details: +# 1) Fill in and setup variables +# 2) loop, looking for sql error on slave +# 3) If it loops too long, die. +#################################################### +connection slave; +let $row_number= 1; +let $run= 1; +let $counter= 300; + +while ($run) +{ + let $sql_result= query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, $row_number); + let $run= `SELECT '$sql_result' = '0'`; + if ($run) { + real_sleep 0.1; + if (!$counter){ + --echo "Failed while waiting for slave to produce an error in its sql thread" + --replace_result $MASTER_MYPORT MASTER_PORT + --replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # + query_vertical SHOW SLAVE STATUS; + exit; + } + dec $counter; + } +} diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index 3d5752b4ec8..4ef247af3a0 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -214,17 +214,44 @@ sub collect_one_suite($$) mtr_verbose("Collecting: $suite"); + my $combination_file= "combinations"; + my $combinations = []; + my $suitedir= "$::glob_mysql_test_dir"; # Default + my $combination_file= "$::glob_mysql_test_dir/$combination_file"; if ( $suite ne "main" ) { $suitedir= mtr_path_exists("$suitedir/suite/$suite", "$suitedir/$suite"); mtr_verbose("suitedir: $suitedir"); + $combination_file= "$suitedir/$combination_file"; } my $testdir= "$suitedir/t"; my $resdir= "$suitedir/r"; + if (!@::opt_combination) + { + # Read combinations file + if ( open(COMB,$combination_file) ) + { + while (<COMB>) + { + chomp; + s/\ +/ /g; + push (@$combinations, $_) unless ($_ eq ''); + } + close COMB; + } + } + else + { + # take the combination from command-line + @$combinations = @::opt_combination; + } + # Remember last element position + my $begin_index = $#{@$cases} + 1; + # ---------------------------------------------------------------------- # Build a hash of disabled testcases for this suite # ---------------------------------------------------------------------- @@ -335,6 +362,78 @@ sub collect_one_suite($$) closedir TESTDIR; } + # ---------------------------------------------------------------------- + # Proccess combinations only if new tests were added + # ---------------------------------------------------------------------- + if (0 and $combinations && $begin_index <= $#{@$cases}) + { + my $end_index = $#{@$cases}; + my $is_copy; + # Keep original master/slave options + my @orig_opts; + for (my $idx = $begin_index; $idx <= $end_index; $idx++) + { + foreach my $param (('master_opt','slave_opt','slave_mi')) + { + @{$orig_opts[$idx]{$param}} = @{$cases->[$idx]->{$param}}; + } + } + my $comb_index = 1; + # Copy original test cases + foreach my $comb_set (@$combinations) + { + for (my $idx = $begin_index; $idx <= $end_index; $idx++) + { + my $test = $cases->[$idx]; + my $copied_test = {}; + foreach my $param (keys %{$test}) + { + # Scalar. Copy as is. + $copied_test->{$param} = $test->{$param}; + # Array. Copy reference instead itself + if ($param =~ /(master_opt|slave_opt|slave_mi)/) + { + my $new_arr = []; + @$new_arr = @{$orig_opts[$idx]{$param}}; + $copied_test->{$param} = $new_arr; + } + elsif ($param =~ /(comment|combinations)/) + { + $copied_test->{$param} = ''; + } + } + if ($is_copy) + { + push(@$cases, $copied_test); + $test = $cases->[$#{@$cases}]; + } + foreach my $comb_opt (split(/ /,$comb_set)) + { + push(@{$test->{'master_opt'}},$comb_opt); + push(@{$test->{'slave_opt'}},$comb_opt); + # Enable rpl if added option is --binlog-format and test case supports that + if ($comb_opt =~ /^--binlog-format=.+$/) + { + my @opt_pairs = split(/=/, $comb_opt); + if ($test->{'binlog_format'} =~ /^$opt_pairs[1]$/ || $test->{'binlog_format'} eq '') + { + $test->{'skip'} = 0; + $test->{'comment'} = ''; + } + else + { + $test->{'skip'} = 1; + $test->{'comment'} = "Requiring binlog format '$test->{'binlog_format'}'";; + } + } + } + $test->{'combination'} = $comb_set; + } + $is_copy = 1; + $comb_index++; + } + } + return $cases; } diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index c01680162a5..a0a796dddf2 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -352,7 +352,15 @@ sub mtr_report_stats ($) { # BUG#29839 - lowercase_table3.test: Cannot find table test/T1 # from the internal data dictiona - /Cannot find table test\/BUG29839 from the internal data dictionary/ + /Cannot find table test\/BUG29839 from the internal data dictionary/ or + + # rpl_extrColmaster_*.test, the slave thread produces warnings + # when it get updates to a table that has more columns on the + # master + /Slave: Unknown column 'c7' in 't15' Error_code: 1054/ or + /Slave: Can't DROP 'c7'.* 1091/ or + /Slave: Key column 'c6'.* 1072/ + ) { next; # Skip these lines diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f6ea5550007..150247a427a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -164,6 +164,8 @@ our $opt_bench= 0; our $opt_small_bench= 0; our $opt_big_test= 0; +our @opt_combination; + our @opt_extra_mysqld_opt; our $opt_compress; @@ -529,6 +531,7 @@ sub command_line_setup () { 'skip-im' => \$opt_skip_im, 'skip-test=s' => \$opt_skip_test, 'big-test' => \$opt_big_test, + 'combination=s' => \@opt_combination, # Specify ports 'master_port=i' => \$opt_master_myport, @@ -2093,6 +2096,22 @@ sub environment_setup () { ($lib_example_plugin ? "--plugin_dir=" . dirname($lib_example_plugin) : ""); # ---------------------------------------------------- + # Setup env so childs can execute myisampack and myisamchk + # ---------------------------------------------------- + $ENV{'MYISAMCHK'}= mtr_native_path(mtr_exe_exists( + vs_config_dirs('storage/myisam', 'myisamchk'), + vs_config_dirs('myisam', 'myisamchk'), + "$path_client_bindir/myisamchk", + "$glob_basedir/storage/myisam/myisamchk", + "$glob_basedir/myisam/myisamchk")); + $ENV{'MYISAMPACK'}= mtr_native_path(mtr_exe_exists( + vs_config_dirs('storage/myisam', 'myisampack'), + vs_config_dirs('myisam', 'myisampack'), + "$path_client_bindir/myisampack", + "$glob_basedir/storage/myisam/myisampack", + "$glob_basedir/myisam/myisampack")); + + # ---------------------------------------------------- # We are nice and report a bit about our settings # ---------------------------------------------------- if (!$opt_extern) @@ -5143,6 +5162,8 @@ Options to control what test suites or cases to run skip-im Don't start IM, and skip the IM test cases big-test Set the environment variable BIG_TEST, which can be checked from test cases. + combination="ARG1 .. ARG2" Specify a set of "mysqld" arguments for one + combination. Options that specify ports diff --git a/mysql-test/r/analyze.result b/mysql-test/r/analyze.result index 7b476c3cca2..c3dbb846402 100644 --- a/mysql-test/r/analyze.result +++ b/mysql-test/r/analyze.result @@ -56,3 +56,11 @@ show index from t1; Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment t1 1 a 1 a A 5 NULL NULL YES BTREE drop table t1; +End of 4.1 tests +create table t1(a int); +analyze table t1 extended; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'extended' at line 1 +optimize table t1 extended; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'extended' at line 1 +drop table t1; +End of 5.0 tests diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 77d3cba63d5..edd49988a5f 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -2619,7 +2619,7 @@ auto fld1 companynr fld3 fld4 fld5 fld6 INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W'); INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring',''); INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily',''); -OPTIMIZE TABLE t2 EXTENDED; +OPTIMIZE TABLE t2; Table Op Msg_type Msg_text test.t2 optimize status OK SELECT * FROM t2; diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index ca3b344af53..73a9dba4e69 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -594,7 +594,7 @@ create table t1 (a int); create table t1 select * from t1; ERROR HY000: You can't specify target table 't1' for update in FROM clause create table t2 union = (t1) select * from t1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: 'test.t2' is not BASE TABLE flush tables with read lock; unlock tables; drop table t1; diff --git a/mysql-test/r/ctype_euckr.result b/mysql-test/r/ctype_euckr.result index b9619370d4c..ee786202c01 100644 --- a/mysql-test/r/ctype_euckr.result +++ b/mysql-test/r/ctype_euckr.result @@ -178,3 +178,44 @@ hex(a) A2E6 FEF7 DROP TABLE t1; +create table t1 (s1 varchar(5) character set euckr); +insert into t1 values (0xA141); +insert into t1 values (0xA15A); +insert into t1 values (0xA161); +insert into t1 values (0xA17A); +insert into t1 values (0xA181); +insert into t1 values (0xA1FE); +insert into t1 values (0xA140); +Warnings: +Warning 1366 Incorrect string value: '\xA1@' for column 's1' at row 1 +insert into t1 values (0xA15B); +Warnings: +Warning 1366 Incorrect string value: '\xA1[' for column 's1' at row 1 +insert into t1 values (0xA160); +Warnings: +Warning 1366 Incorrect string value: '\xA1`' for column 's1' at row 1 +insert into t1 values (0xA17B); +Warnings: +Warning 1366 Incorrect string value: '\xA1{' for column 's1' at row 1 +insert into t1 values (0xA180); +Warnings: +Warning 1366 Incorrect string value: '\xA1\x80' for column 's1' at row 1 +insert into t1 values (0xA1FF); +Warnings: +Warning 1366 Incorrect string value: '\xA1\xFF' for column 's1' at row 1 +select hex(s1), hex(convert(s1 using utf8)) from t1 order by binary s1; +hex(s1) hex(convert(s1 using utf8)) + + + + + + +A141 ECA2A5 +A15A ECA381 +A161 ECA382 +A17A ECA3A5 +A181 ECA3A6 +A1FE EFBFA2 +drop table t1; +End of 5.0 tests diff --git a/mysql-test/r/ctype_uca.result b/mysql-test/r/ctype_uca.result index e676d5a5ca0..3fff9b9cda8 100644 --- a/mysql-test/r/ctype_uca.result +++ b/mysql-test/r/ctype_uca.result @@ -2767,4 +2767,49 @@ a c ch drop table t1; +set collation_connection=ucs2_unicode_ci; +drop table if exists t1; +create table t1 as +select repeat(' ', 64) as s1, repeat(' ',64) as s2 +union +select null, null; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `s1` varchar(64) CHARACTER SET ucs2 COLLATE ucs2_unicode_ci DEFAULT NULL, + `s2` varchar(64) CHARACTER SET ucs2 COLLATE ucs2_unicode_ci DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +delete from t1; +insert into t1 values('aaa','aaa'); +insert into t1 values('aaa|qqq','qqq'); +insert into t1 values('gheis','^[^a-dXYZ]+$'); +insert into t1 values('aab','^aa?b'); +insert into t1 values('Baaan','^Ba*n'); +insert into t1 values('aaa','qqq|aaa'); +insert into t1 values('qqq','qqq|aaa'); +insert into t1 values('bbb','qqq|aaa'); +insert into t1 values('bbb','qqq'); +insert into t1 values('aaa','aba'); +insert into t1 values(null,'abc'); +insert into t1 values('def',null); +insert into t1 values(null,null); +insert into t1 values('ghi','ghi['); +select HIGH_PRIORITY s1 regexp s2 from t1; +s1 regexp s2 +1 +1 +1 +1 +1 +1 +1 +0 +0 +0 +NULL +NULL +NULL +NULL +drop table t1; +set names utf8; End for 5.0 tests diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 59b1bfa183d..991c3bb82de 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -928,6 +928,51 @@ ERROR HY000: Illegal mix of collations (ascii_general_ci,IMPLICIT) and (ucs2_gen select * from t1 where a=if(b<10,_ucs2 0x0062,_ucs2 0x00C0); ERROR HY000: Illegal mix of collations (ascii_general_ci,IMPLICIT) and (ucs2_general_ci,COERCIBLE) for operation '=' drop table t1; +set collation_connection=ucs2_general_ci; +drop table if exists t1; +create table t1 as +select repeat(' ', 64) as s1, repeat(' ',64) as s2 +union +select null, null; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `s1` varchar(64) CHARACTER SET ucs2 DEFAULT NULL, + `s2` varchar(64) CHARACTER SET ucs2 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +delete from t1; +insert into t1 values('aaa','aaa'); +insert into t1 values('aaa|qqq','qqq'); +insert into t1 values('gheis','^[^a-dXYZ]+$'); +insert into t1 values('aab','^aa?b'); +insert into t1 values('Baaan','^Ba*n'); +insert into t1 values('aaa','qqq|aaa'); +insert into t1 values('qqq','qqq|aaa'); +insert into t1 values('bbb','qqq|aaa'); +insert into t1 values('bbb','qqq'); +insert into t1 values('aaa','aba'); +insert into t1 values(null,'abc'); +insert into t1 values('def',null); +insert into t1 values(null,null); +insert into t1 values('ghi','ghi['); +select HIGH_PRIORITY s1 regexp s2 from t1; +s1 regexp s2 +1 +1 +1 +1 +1 +1 +1 +0 +0 +0 +NULL +NULL +NULL +NULL +drop table t1; +set names latin1; select hex(char(0x41 using ucs2)); hex(char(0x41 using ucs2)) 0041 diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 8243b2bc015..5c90c3b5e0b 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -267,6 +267,51 @@ b select * from t1 where a = 'b' and a != 'b'; a drop table t1; +set collation_connection=utf8_general_ci; +drop table if exists t1; +create table t1 as +select repeat(' ', 64) as s1, repeat(' ',64) as s2 +union +select null, null; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `s1` varchar(64) CHARACTER SET utf8 DEFAULT NULL, + `s2` varchar(64) CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +delete from t1; +insert into t1 values('aaa','aaa'); +insert into t1 values('aaa|qqq','qqq'); +insert into t1 values('gheis','^[^a-dXYZ]+$'); +insert into t1 values('aab','^aa?b'); +insert into t1 values('Baaan','^Ba*n'); +insert into t1 values('aaa','qqq|aaa'); +insert into t1 values('qqq','qqq|aaa'); +insert into t1 values('bbb','qqq|aaa'); +insert into t1 values('bbb','qqq'); +insert into t1 values('aaa','aba'); +insert into t1 values(null,'abc'); +insert into t1 values('def',null); +insert into t1 values(null,null); +insert into t1 values('ghi','ghi['); +select HIGH_PRIORITY s1 regexp s2 from t1; +s1 regexp s2 +1 +1 +1 +1 +1 +1 +1 +0 +0 +0 +NULL +NULL +NULL +NULL +drop table t1; +set names utf8; set names utf8; select 'вася' rlike '[[:<:]]вася[[:>:]]'; 'вася' rlike '[[:<:]]вася[[:>:]]' diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index c520ab52ab3..95f7fe5aa61 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -73,13 +73,13 @@ a 13 DROP TABLE t1; SET @bug20627_old_auto_increment_offset= -@@auto_increment_offset= 2; +@@auto_increment_offset; SET @bug20627_old_auto_increment_increment= -@@auto_increment_increment= 3; +@@auto_increment_increment; SET @bug20627_old_session_auto_increment_offset= -@@session.auto_increment_offset= 4; +@@session.auto_increment_offset; SET @bug20627_old_session_auto_increment_increment= -@@session.auto_increment_increment= 5; +@@session.auto_increment_increment; SET @@auto_increment_offset= 2; SET @@auto_increment_increment= 3; SET @@session.auto_increment_offset= 4; @@ -116,13 +116,13 @@ SET @@session.auto_increment_offset= SET @@session.auto_increment_increment= @bug20627_old_session_auto_increment_increment; SET @bug20830_old_auto_increment_offset= -@@auto_increment_offset= 2; +@@auto_increment_offset; SET @bug20830_old_auto_increment_increment= -@@auto_increment_increment= 3; +@@auto_increment_increment; SET @bug20830_old_session_auto_increment_offset= -@@session.auto_increment_offset= 4; +@@session.auto_increment_offset; SET @bug20830_old_session_auto_increment_increment= -@@session.auto_increment_increment= 5; +@@session.auto_increment_increment; SET @@auto_increment_offset= 2; SET @@auto_increment_increment= 3; SET @@session.auto_increment_offset= 4; @@ -250,11 +250,6 @@ SELECT HEX(a) FROM t1; HEX(a) 1 DROP TABLE t1; -CREATE TABLE t1(c1 INT) ENGINE=MyISAM; -CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1); -INSERT DELAYED INTO t2 VALUES(1); -ERROR HY000: Table storage engine for 't2' doesn't have this option -DROP TABLE t1, t2; DROP TABLE IF EXISTS t1,t2; SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'; CREATE TABLE `t1` ( diff --git a/mysql-test/r/func_regexp.result b/mysql-test/r/func_regexp.result index 05e56f05457..9d85b1d3e7f 100644 --- a/mysql-test/r/func_regexp.result +++ b/mysql-test/r/func_regexp.result @@ -1,5 +1,17 @@ drop table if exists t1; -create table t1 (s1 char(64),s2 char(64)); +set names latin1; +drop table if exists t1; +create table t1 as +select repeat(' ', 64) as s1, repeat(' ',64) as s2 +union +select null, null; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `s1` varchar(64) DEFAULT NULL, + `s2` varchar(64) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +delete from t1; insert into t1 values('aaa','aaa'); insert into t1 values('aaa|qqq','qqq'); insert into t1 values('gheis','^[^a-dXYZ]+$'); diff --git a/mysql-test/r/func_set.result b/mysql-test/r/func_set.result index 5f6ddb020f2..4dd11cafdcd 100644 --- a/mysql-test/r/func_set.result +++ b/mysql-test/r/func_set.result @@ -73,3 +73,33 @@ find_in_set(binary 'a', 'A,B,C') select find_in_set('1','3,1,'); find_in_set('1','3,1,') 2 +End of 4.1 tests +SELECT INTERVAL(0.0, NULL); +INTERVAL(0.0, NULL) +1 +SELECT INTERVAL(0.0, CAST(NULL AS DECIMAL)); +INTERVAL(0.0, CAST(NULL AS DECIMAL)) +1 +SELECT INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL)); +INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL)) +1 +SELECT INTERVAL(0.0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +INTERVAL(0.0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) +8 +SELECT INTERVAL(0.0, CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), +CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), +CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL)); +INTERVAL(0.0, CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), +CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), +CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL)) +8 +SELECT INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL)); +INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), +CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL)) +8 +End of 5.0 tests diff --git a/mysql-test/r/innodb-semi-consistent.result b/mysql-test/r/innodb-semi-consistent.result index ad7b70d0497..f1139390f20 100644 --- a/mysql-test/r/innodb-semi-consistent.result +++ b/mysql-test/r/innodb-semi-consistent.result @@ -1,3 +1,4 @@ +drop table if exists t1; set session transaction isolation level read committed; create table t1(a int not null) engine=innodb DEFAULT CHARSET=latin1; insert into t1 values (1),(2),(3),(4),(5),(6),(7); diff --git a/mysql-test/r/innodb-ucs2.result b/mysql-test/r/innodb-ucs2.result index a1c73c912b2..b6bff7d5f42 100644 --- a/mysql-test/r/innodb-ucs2.result +++ b/mysql-test/r/innodb-ucs2.result @@ -1,3 +1,4 @@ +drop table if exists t1, t2; create table t1 ( a int, b char(10), c char(10), filler char(10), primary key(a, b(2)), unique key (a, c(2)) ) character set utf8 engine = innodb; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 051a9e1dc0b..4379959b60d 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1049,6 +1049,19 @@ n d 1 30 2 20 drop table t1,t2; +drop table if exists t1, t2; +CREATE TABLE t1 (a int, PRIMARY KEY (a)); +CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +create trigger trg_del_t2 after delete on t2 for each row +insert into t1 values (1); +insert into t1 values (1); +insert into t2 values (1),(2); +delete t2 from t2; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +select count(*) from t2 /* must be 2 as restored after rollback caused by the error */; +count(*) +2 +drop table t1, t2; create table t1 (a int, b int) engine=innodb; insert into t1 values(20,null); select t2.b, ifnull(t2.b,"this is null") from t1 as t2 left join t1 as t3 on @@ -1714,10 +1727,10 @@ Variable_name Value Innodb_page_size 16384 show status like "Innodb_rows_deleted"; Variable_name Value -Innodb_rows_deleted 69 +Innodb_rows_deleted 70 show status like "Innodb_rows_inserted"; Variable_name Value -Innodb_rows_inserted 1080 +Innodb_rows_inserted 1082 show status like "Innodb_rows_updated"; Variable_name Value Innodb_rows_updated 885 diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index c80aa7fee05..029cd049a13 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -391,6 +391,7 @@ id select_type table type possible_keys key key_len ref rows Extra SELECT DISTINCT t1.name, t1.dept FROM t1 WHERE t1.name='rs5'; name dept DROP TABLE t1; +drop table if exists t1; show variables like 'innodb_rollback_on_timeout'; Variable_name Value innodb_rollback_on_timeout OFF @@ -453,6 +454,7 @@ tes 1234 drop table test; set global query_cache_type=@save_qcache_type; set global query_cache_size=@save_qcache_size; +drop table if exists t1; show variables like 'innodb_rollback_on_timeout'; Variable_name Value innodb_rollback_on_timeout OFF @@ -777,6 +779,7 @@ EXPLAIN SELECT SQL_BIG_RESULT b, SUM(c) FROM t1 GROUP BY b; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 128 Using filesort DROP TABLE t1; +drop table if exists t1; show variables like 'innodb_rollback_on_timeout'; Variable_name Value innodb_rollback_on_timeout OFF diff --git a/mysql-test/r/innodb_timeout_rollback.result b/mysql-test/r/innodb_timeout_rollback.result index b25a2bbd815..e2da6ba8af7 100644 --- a/mysql-test/r/innodb_timeout_rollback.result +++ b/mysql-test/r/innodb_timeout_rollback.result @@ -1,3 +1,4 @@ +drop table if exists t1; show variables like 'innodb_rollback_on_timeout'; Variable_name Value innodb_rollback_on_timeout ON diff --git a/mysql-test/r/merge-big.result b/mysql-test/r/merge-big.result new file mode 100644 index 00000000000..82fedc1df73 --- /dev/null +++ b/mysql-test/r/merge-big.result @@ -0,0 +1,77 @@ +drop table if exists t1,t2,t3,t4,t5,t6; +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE +# corrupts a MERGE table +# Problem #3 +# +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +LOCK TABLE t1 WRITE; +# connection con1 +SET SESSION debug="+d,sleep_open_and_lock_after_open"; +INSERT INTO t1 VALUES (1); +# connection default +# Let INSERT go into thr_multi_lock(). +# Kick INSERT out of thr_multi_lock(). +FLUSH TABLES; +# Let INSERT go through open_tables() where it sleeps. +# Unlock and close table and wait for con1 to close too. +FLUSH TABLES; +# This should give no result. +SELECT * FROM t1; +c1 +UNLOCK TABLES; +# connection con1 +SET SESSION debug="-d,sleep_open_and_lock_after_open"; +# connection default +DROP TABLE t1; +# +# Extra tests for Bug#26379 - Combination of FLUSH TABLE and +# REPAIR TABLE corrupts a MERGE table +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +# +# CREATE ... SELECT +# try to access parent from another thread. +# +# connection con1 +SET SESSION debug="+d,sleep_create_select_before_lock"; +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=FIRST SELECT * FROM t3; +# connection default +# Now try to access the parent. +# If 3 is in table, SELECT had to wait. +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +# connection con1 +SET SESSION debug="-d,sleep_create_select_before_lock"; +# connection default +# Cleanup for next test. +DROP TABLE t4; +DELETE FROM t1 WHERE c1 != 1; +# +# CREATE ... SELECT +# try to access child from another thread. +# +# connection con1 +SET SESSION debug="+d,sleep_create_select_before_lock"; +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=FIRST SELECT * FROM t3; +# connection default +# Now try to access a child. +# If 3 is in table, SELECT had to wait. +SELECT * FROM t1 ORDER BY c1; +c1 +1 +3 +# connection con1 +SET SESSION debug="-d,sleep_create_select_before_lock"; +# connection default +DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index d6e19107ec4..ba1680f2cac 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -584,9 +584,7 @@ insert into t1 values (1); insert into t2 values (2); create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); select * from t3; -a -1 -2 +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist create temporary table t4 (a int not null); create temporary table t5 (a int not null); insert into t4 values (1); @@ -597,6 +595,54 @@ a 1 2 drop table t6, t3, t1, t2, t4, t5; +create temporary table t1 (a int not null); +create temporary table t2 (a int not null); +insert into t1 values (1); +insert into t2 values (2); +create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +select * from t3; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +drop table t3, t2, t1; +create table t1 (a int not null); +create temporary table t2 (a int not null); +insert into t1 values (1); +insert into t2 values (2); +create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +select * from t3; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +drop table t3; +create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +select * from t3; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +drop table t3, t2, t1; +# CREATE...SELECT is not implemented for MERGE tables. +CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t2 (c1 INT NOT NULL); +CREATE TABLE t3 (c1 INT NOT NULL); +INSERT INTO t3 VALUES (3), (33); +LOCK TABLES t3 READ; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2) +INSERT_METHOD=LAST SELECT * FROM t3; +ERROR HY000: 'test.t4' is not BASE TABLE +SELECT * FROM t4; +ERROR HY000: Table 't4' was not locked with LOCK TABLES +UNLOCK TABLES; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t4 SELECT * FROM t3; +# Alter temporary MERGE table. +ALTER TABLE t4 UNION=(t1); +LOCK TABLES t4 WRITE; +# Alter temporary MERGE table under LOCk tables. +ALTER TABLE t4 UNION=(t1,t2); +UNLOCK TABLES; +# MERGE table and function. +CREATE FUNCTION f1 () RETURNS INT RETURN (SELECT max(c1) FROM t3); +SELECT * FROM t4 WHERE c1 < f1(); +c1 +3 +DROP FUNCTION f1; +DROP TABLE t4, t3, t2, t1; CREATE TABLE t1 ( fileset_id tinyint(3) unsigned NOT NULL default '0', file_code varchar(32) NOT NULL default '', @@ -650,11 +696,11 @@ create table t2 (a int); insert into t1 values (0); insert into t2 values (1); create table t3 engine=merge union=(t1, t2) select * from t1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: 'test.t3' is not BASE TABLE create table t3 engine=merge union=(t1, t2) select * from t2; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: 'test.t3' is not BASE TABLE create table t3 engine=merge union=(t1, t2) select (select max(a) from t2); -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: 'test.t3' is not BASE TABLE drop table t1, t2; create table t1 ( a double(14,4), @@ -784,7 +830,7 @@ ERROR HY000: Unable to open underlying table which is differently defined or of DROP TABLE t1, t2; CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); SELECT * FROM t2; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ERROR 42S02: Table 'test.t3' doesn't exist DROP TABLE t2; CREATE TABLE t1(a INT, b TEXT); CREATE TABLE tm1(a TEXT, b INT) ENGINE=MERGE UNION=(t1); @@ -849,20 +895,17 @@ drop table t2; drop table t1; CREATE TABLE tm1(a INT) ENGINE=MERGE UNION=(t1, t2); SELECT * FROM tm1; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ERROR 42S02: Table 'test.t1' doesn't exist CHECK TABLE tm1; Table Op Msg_type Msg_text -test.tm1 check Error Table 'test.t1' is differently defined or of non-MyISAM type or doesn't exist -test.tm1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist -test.tm1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.tm1 check Error Table 'test.t1' doesn't exist test.tm1 check error Corrupt CREATE TABLE t1(a INT); SELECT * FROM tm1; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ERROR 42S02: Table 'test.t2' doesn't exist CHECK TABLE tm1; Table Op Msg_type Msg_text -test.tm1 check Error Table 'test.t2' is differently defined or of non-MyISAM type or doesn't exist -test.tm1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +test.tm1 check Error Table 'test.t2' doesn't exist test.tm1 check error Corrupt CREATE TABLE t2(a BLOB); SELECT * FROM tm1; @@ -885,3 +928,1022 @@ CREATE TABLE IF NOT EXISTS t1 SELECT * FROM t2; ERROR HY000: You can't specify target table 't1' for update in FROM clause DROP TABLE t1, t2; End of 5.0 tests +create table t1 (c1 int, index(c1)); +create table t2 (c1 int, index(c1)) engine=merge union=(t1); +insert into t1 values (1); +flush tables; +select * from t2; +c1 +1 +flush tables; +truncate table t1; +insert into t1 values (1); +flush tables; +select * from t2; +c1 +1 +truncate table t1; +insert into t1 values (1); +drop table t1,t2; +# +# Extra tests for TRUNCATE. +# +# Truncate MERGE table. +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)); +CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +c1 +1 +2 +TRUNCATE TABLE t3; +SELECT * FROM t3; +c1 +# +# Truncate child table. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +TRUNCATE TABLE t1; +SELECT * FROM t3; +c1 +2 +# +# Truncate MERGE table under locked tables. +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +TRUNCATE TABLE t3; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3; +c1 +1 +2 +# +# Truncate child table under locked tables. +TRUNCATE TABLE t1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3; +c1 +1 +2 +UNLOCK TABLES; +DROP TABLE t1, t2, t3; +# +# Truncate temporary MERGE table. +CREATE TEMPORARY TABLE t1 (c1 INT, INDEX(c1)); +CREATE TEMPORARY TABLE t2 (c1 INT, INDEX(c1)); +CREATE TEMPORARY TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +c1 +1 +2 +TRUNCATE TABLE t3; +SELECT * FROM t3; +c1 +# +# Truncate temporary child table. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +TRUNCATE TABLE t1; +SELECT * FROM t3; +c1 +2 +# +# Truncate temporary MERGE table under locked tables. +INSERT INTO t1 VALUES (1); +CREATE TABLE t4 (c1 INT, INDEX(c1)); +LOCK TABLE t4 WRITE; +TRUNCATE TABLE t3; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3; +c1 +1 +2 +# +# Truncate temporary child table under locked tables. +TRUNCATE TABLE t1; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3; +c1 +1 +2 +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST; +REPAIR TABLE t1; +INSERT INTO t2 VALUES (1); +Table Op Msg_type Msg_text +test.t1 repair status OK +DROP TABLE t1, t2; +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST; +LOCK TABLE t1 WRITE; +INSERT INTO t2 VALUES (1); +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +UNLOCK TABLES; +DROP TABLE t1, t2; +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +LOCK TABLE t1 WRITE; +INSERT INTO t1 VALUES (1); +FLUSH TABLES; +FLUSH TABLES; +SELECT * FROM t1; +c1 +UNLOCK TABLES; +DROP TABLE t1; +# +# Extra tests for Bug#26379 - Combination of FLUSH TABLE and +# REPAIR TABLE corrupts a MERGE table +# +# CREATE ... SELECT is disabled for MERGE tables. +# +CREATE TABLE t1(c1 INT); +INSERT INTO t1 VALUES (1); +CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +CREATE TABLE t3 ENGINE=MRG_MYISAM INSERT_METHOD=LAST SELECT * FROM t2; +ERROR HY000: Table 't3' is read only +SHOW CREATE TABLE t3; +ERROR 42S02: Table 'test.t3' doesn't exist +CREATE TABLE t3 ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST +SELECT * FROM t2; +ERROR HY000: 'test.t3' is not BASE TABLE +SHOW CREATE TABLE t3; +ERROR 42S02: Table 'test.t3' doesn't exist +DROP TABLE t1, t2; +# +# CREATE ... LIKE +# +# 1. Create like. +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +CREATE TABLE t4 LIKE t3; +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `c1` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=() +INSERT INTO t4 VALUES (4); +ERROR HY000: Table 't4' is read only +DROP TABLE t4; +# +# 1. Create like with locked tables. +LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; +CREATE TABLE t4 LIKE t3; +SHOW CREATE TABLE t4; +ERROR HY000: Table 't4' was not locked with LOCK TABLES +INSERT INTO t4 VALUES (4); +ERROR HY000: Table 't4' was not locked with LOCK TABLES +UNLOCK TABLES; +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `c1` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=() +INSERT INTO t4 VALUES (4); +ERROR HY000: Table 't4' is read only +DROP TABLE t4; +# +# Rename child. +# +# 1. Normal rename of non-MERGE table. +CREATE TABLE t4 (c1 INT); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +c1 +4 +RENAME TABLE t4 TO t5; +SELECT * FROM t5 ORDER BY c1; +c1 +4 +RENAME TABLE t5 TO t4; +SELECT * FROM t4 ORDER BY c1; +c1 +4 +DROP TABLE t4; +# +# 2. Normal rename. +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +RENAME TABLE t2 TO t5; +SELECT * FROM t3 ORDER BY c1; +ERROR 42S02: Table 'test.t2' doesn't exist +RENAME TABLE t5 TO t2; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +# +# 3. Normal rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +RENAME TABLE t2 TO t5; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +RENAME TABLE t5 TO t2; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# +# 4. Alter table rename. +ALTER TABLE t2 RENAME TO t5; +SELECT * FROM t3 ORDER BY c1; +ERROR 42S02: Table 'test.t2' doesn't exist +ALTER TABLE t5 RENAME TO t2; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +# +# 5. Alter table rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +ALTER TABLE t2 RENAME TO t5; +SELECT * FROM t3 ORDER BY c1; +ERROR HY000: Table 't3' was not locked with LOCK TABLES +ALTER TABLE t5 RENAME TO t2; +ERROR HY000: Table 't5' was not locked with LOCK TABLES +UNLOCK TABLES; +ALTER TABLE t5 RENAME TO t2; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +# +# Rename parent. +# +# 1. Normal rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +RENAME TABLE t3 TO t5; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +RENAME TABLE t5 TO t3; +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +# +# 5. Alter table rename with locked tables. +ALTER TABLE t3 RENAME TO t5; +SELECT * FROM t5 ORDER BY c1; +ERROR HY000: Table 't5' was not locked with LOCK TABLES +ALTER TABLE t5 RENAME TO t3; +ERROR HY000: Table 't5' was not locked with LOCK TABLES +UNLOCK TABLES; +ALTER TABLE t5 RENAME TO t3; +SELECT * FROM t3 ORDER BY c1; +c1 +1 +2 +3 +DROP TABLE t1, t2, t3; +# +# Drop locked tables. +# +# 1. Drop parent. +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t1 VALUES (1); +DROP TABLE t2; +SELECT * FROM t2; +ERROR HY000: Table 't2' was not locked with LOCK TABLES +SELECT * FROM t1; +c1 +1 +UNLOCK TABLES; +# 2. Drop child. +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t1 VALUES (1); +DROP TABLE t1; +SELECT * FROM t2; +ERROR 42S02: Table 'test.t1' doesn't exist +SELECT * FROM t1; +ERROR 42S02: Table 'test.t1' doesn't exist +UNLOCK TABLES; +DROP TABLE t2; +# +# ALTER TABLE. Change child list. +# +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)); +CREATE TABLE t3 (c1 INT, INDEX(c1)); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t3,t2) +INSERT_METHOD=LAST; +# Shrink child list. +ALTER TABLE t4 UNION=(t3); +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `c1` int(11) DEFAULT NULL, + KEY `c1` (`c1`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`) +SELECT * FROM t4 ORDER BY c1; +c1 +3 +# Extend child list. +ALTER TABLE t4 UNION=(t3,t2); +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `c1` int(11) DEFAULT NULL, + KEY `c1` (`c1`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`,`t2`) +SELECT * FROM t4 ORDER BY c1; +c1 +2 +3 +# +# ALTER TABLE under LOCK TABLES. Change child list. +# +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE; +# Shrink child list. +ALTER TABLE t4 UNION=(t3); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# Extend child list within locked tables. +ALTER TABLE t4 UNION=(t3,t2); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +# Extend child list beyond locked tables. +ALTER TABLE t4 UNION=(t3,t2,t1); +ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `c1` int(11) DEFAULT NULL, + KEY `c1` (`c1`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t3`,`t2`) +SELECT * FROM t4 ORDER BY c1; +c1 +2 +3 +UNLOCK TABLES; +DROP TABLE t4; +# +# ALTER TABLE under LOCK TABLES. Grave change, table re-creation. +# +CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3) +INSERT_METHOD=LAST; +# Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; +# +# ALTER TABLE under LOCK TABLES. Simple change, no re-creation. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +# Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# +# FLUSH TABLE under LOCK TABLES. +# +# Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +UNLOCK TABLES; +# +# Triggers +# +# Trigger on parent +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +@a +1 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +DROP TRIGGER t4_ai; +# Trigger on parent under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +@a +1 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +DROP TRIGGER t4_ai; +UNLOCK TABLES; +# +# Trigger on child +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +@a +0 +INSERT INTO t3 VALUES (33); +SELECT @a; +@a +1 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +33 +DROP TRIGGER t3_ai; +# Trigger on child under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +@a +0 +INSERT INTO t3 VALUES (33); +SELECT @a; +@a +1 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +33 +33 +DELETE FROM t4 WHERE c1 = 33; +DROP TRIGGER t3_ai; +# +# Trigger with table use on child +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +22 +33 +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; +DROP TRIGGER t3_ai; +# Trigger with table use on child under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +22 +33 +DROP TRIGGER t3_ai; +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; +UNLOCK TABLES; +# +# Repair +# +REPAIR TABLE t4; +Table Op Msg_type Msg_text +test.t4 repair note The storage engine for the table doesn't support repair +REPAIR TABLE t2; +Table Op Msg_type Msg_text +test.t2 repair status OK +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +REPAIR TABLE t4; +Table Op Msg_type Msg_text +test.t4 repair note The storage engine for the table doesn't support repair +REPAIR TABLE t2; +Table Op Msg_type Msg_text +test.t2 repair status OK +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +UNLOCK TABLES; +# +# Optimize +# +OPTIMIZE TABLE t4; +Table Op Msg_type Msg_text +test.t4 optimize note The storage engine for the table doesn't support optimize +OPTIMIZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 optimize status OK +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +OPTIMIZE TABLE t4; +Table Op Msg_type Msg_text +test.t4 optimize note The storage engine for the table doesn't support optimize +OPTIMIZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 optimize status Table is already up to date +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +UNLOCK TABLES; +# +# Checksum +# +CHECKSUM TABLE t4; +Table Checksum +test.t4 46622073 +CHECKSUM TABLE t2; +Table Checksum +test.t2 3700403066 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CHECKSUM TABLE t4; +Table Checksum +test.t4 46622073 +CHECKSUM TABLE t2; +Table Checksum +test.t2 3700403066 +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +UNLOCK TABLES; +# +# Insert delayed +# +INSERT DELAYED INTO t4 VALUES(44); +DELETE FROM t4 WHERE c1 = 44; +INSERT DELAYED INTO t3 VALUES(33); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +33 +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +INSERT DELAYED INTO t4 VALUES(444); +Got one of the listed errors +INSERT DELAYED INTO t3 VALUES(333); +Got one of the listed errors +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +4 +33 +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; +# +# Recursive inclusion of merge tables in their union clauses. +# +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=LAST; +CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t2,t1) +INSERT_METHOD=LAST; +ALTER TABLE t2 UNION=(t3,t1); +SELECT * FROM t2; +ERROR HY000: Table 't3' is differently defined or of non-MyISAM type or doesn't exist +DROP TABLE t1, t2, t3; +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +c1 +1 +2 +TRUNCATE TABLE t1; +SELECT * FROM t3; +c1 +2 +DROP TABLE t1, t2, t3; +CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER); +SET @rnd_max= 2147483647; +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +SET @rnd= RAND(); +SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); +SET @id_rev= @rnd_max - @id; +SET @grp= CAST(127.0 * @rnd AS UNSIGNED); +INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); +set @@read_buffer_size=2*1024*1024; +CREATE TABLE t2 SELECT * FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER) +ENGINE= MRG_MYISAM UNION= (t1, t2); +SELECT COUNT(*) FROM t1; +COUNT(*) +130 +SELECT COUNT(*) FROM t2; +COUNT(*) +80 +SELECT COUNT(*) FROM t3; +COUNT(*) +210 +SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2 +WHERE a1.id = a2.id GROUP BY a2.grp; +TRUNCATE TABLE t1; +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +SELECT COUNT(*) FROM t2; +COUNT(*) +80 +SELECT COUNT(*) FROM t3; +COUNT(*) +80 +DROP TABLE t1, t2, t3; +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO t2 VALUES (1); +SELECT * FROM t2; +c1 +1 +LOCK TABLES t2 WRITE, t1 WRITE; +FLUSH TABLES; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +UNLOCK TABLES; +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +LOCK TABLES t2 WRITE, t1 WRITE; +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +UNLOCK TABLES; +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1, t2; +CREATE TABLE t1 ( a INT ) ENGINE=MyISAM; +CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1); +LOCK TABLES t1 WRITE, m1 WRITE; +FLUSH TABLE t1; +UNLOCK TABLES; +DROP TABLE m1, t1; +CREATE TABLE t1 ( a INT ) ENGINE=MyISAM; +CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1); +LOCK TABLES m1 WRITE, t1 WRITE; +FLUSH TABLE t1; +UNLOCK TABLES; +DROP TABLE m1, t1; +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE= MyISAM; +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE= MRG_MYISAM UNION(t1, t2); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (2, 2); +SELECT * FROM t3; +c1 c2 +1 1 +2 2 +ALTER TABLE t1 ENGINE= MEMORY; +INSERT INTO t1 VALUES (0, 0); +SELECT * FROM t3; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +DROP TABLE t1, t2, t3; +CREATE TABLE t1 (c1 INT, KEY(c1)); +CREATE TABLE t2 (c1 INT, KEY(c1)) ENGINE=MRG_MYISAM UNION=(t1) +INSERT_METHOD=FIRST; +LOCK TABLE t1 WRITE, t2 WRITE; +FLUSH TABLES t2, t1; +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status Table is already up to date +FLUSH TABLES t1; +UNLOCK TABLES; +FLUSH TABLES; +INSERT INTO t1 VALUES (1); +LOCK TABLE t1 WRITE, t2 WRITE; +FLUSH TABLES t2, t1; +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +FLUSH TABLES t1; +UNLOCK TABLES; +DROP TABLE t1, t2; +CREATE TABLE t1 (ID INT) ENGINE=MYISAM; +CREATE TABLE m1 (ID INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=FIRST; +INSERT INTO t1 VALUES (); +INSERT INTO m1 VALUES (); +LOCK TABLE t1 WRITE, m1 WRITE; +FLUSH TABLES m1, t1; +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +FLUSH TABLES m1, t1; +UNLOCK TABLES; +DROP TABLE t1, m1; diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index 55e47756312..0bc01e95d2d 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -614,6 +614,7 @@ CREATE TABLE `t2` ( `b` int(11) default NULL, PRIMARY KEY (`a`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; +set @sav_binlog_format= @@session.binlog_format; set @@session.binlog_format= mixed; insert into t1 values (1,1),(2,2); insert into t2 values (1,1),(4,4); @@ -626,7 +627,7 @@ a b 4 4 show master status /* there must be the UPDATE query event */; File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 197 +master-bin.000001 268 delete from t1; delete from t2; insert into t1 values (1,2),(3,4),(4,4); @@ -636,6 +637,24 @@ UPDATE t2,t1 SET t2.a=t2.b where t2.a=t1.a; ERROR 23000: Duplicate entry '4' for key 'PRIMARY' show master status /* there must be the UPDATE query event */; File Position Binlog_Do_DB Binlog_Ignore_DB -master-bin.000001 212 +master-bin.000001 283 drop table t1, t2; +set @@session.binlog_format= @sav_binlog_format; +drop table if exists t1, t2, t3; +CREATE TABLE t1 (a int, PRIMARY KEY (a)); +CREATE TABLE t2 (a int, PRIMARY KEY (a)); +CREATE TABLE t3 (a int, PRIMARY KEY (a)) ENGINE=MyISAM; +create trigger trg_del_t3 before delete on t3 for each row insert into t1 values (1); +insert into t2 values (1),(2); +insert into t3 values (1),(2); +reset master; +delete t3.* from t2,t3 where t2.a=t3.a; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +select count(*) from t1 /* must be 1 */; +count(*) +1 +select count(*) from t3 /* must be 1 */; +count(*) +1 +drop table t1, t2, t3; end of tests diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 3125660643c..353d66b1ad5 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -606,24 +606,6 @@ select count(*) from t1 where a is null; count(*) 2 drop table t1; -create table t1 (c1 int, index(c1)); -create table t2 (c1 int, index(c1)) engine=merge union=(t1); -insert into t1 values (1); -flush tables; -select * from t2; -c1 -1 -flush tables; -truncate table t1; -insert into t1 values (1); -flush tables; -select * from t2; -c1 -1 -truncate table t1; -ERROR HY000: MyISAM table 't1' is in use (most likely by a MERGE table). Try FLUSH TABLES. -insert into t1 values (1); -drop table t1,t2; create table t1 (c1 int, c2 varchar(4) not null default '', key(c2(3))) default charset=utf8; insert into t1 values (1,'A'), (2, 'B'), (3, 'A'); @@ -1818,6 +1800,14 @@ ALTER TABLE t1 ENABLE KEYS; SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 MyISAM 10 Fixed 1 # # # 3072 # # # # # # # +# Enable keys with parallel repair +SET @@myisam_repair_threads=2; +ALTER TABLE t1 DISABLE KEYS; +ALTER TABLE t1 ENABLE KEYS; +SET @@myisam_repair_threads=1; +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK DROP TABLE t1; End of 5.0 tests create table t1 (a int not null, key `a` (a) key_block_size=1024); diff --git a/mysql-test/r/myisampack.result b/mysql-test/r/myisampack.result new file mode 100644 index 00000000000..5f39d318234 --- /dev/null +++ b/mysql-test/r/myisampack.result @@ -0,0 +1,29 @@ +CREATE TABLE t1(c1 DOUBLE, c2 DOUBLE, c3 DOUBLE, c4 DOUBLE, c5 DOUBLE, +c6 DOUBLE, c7 DOUBLE, c8 DOUBLE, c9 DOUBLE, a INT PRIMARY KEY); +INSERT INTO t1 VALUES +(-3.31168791059336e-06,-3.19054655887874e-06,-1.06528081684847e-05,-1.227278240089e-06,-1.66718069164799e-06,-2.59038972510885e-06,-2.83145227805303e-06,-4.09678491270648e-07,-2.22610091291797e-06,6), +(0.0030743000272545,2.53222044316438e-05,2.78674650061845e-05,1.95914465544536e-05,1.7347572525984e-05,1.87513810069614e-05,1.69882826885005e-05,2.44449336987598e-05,1.89914629921774e-05,9), +(2.85229319423495e-05,3.05970988282259e-05,3.77161100113133e-05,2.3055238978766e-05,2.08241267364615e-05,2.28009504270553e-05,2.12070165658947e-05,2.84350091565409e-05,2.3366822910704e-05,3), +(0,0,0,0,0,0,0,0,0,12), +(3.24544577570754e-05,3.44619021870993e-05,4.37561613201124e-05,2.57556808726748e-05,2.3195354640561e-05,2.58532400758869e-05,2.34934241667179e-05,3.1621640063232e-05,2.58229982746189e-05,19), +(2.53222044316438e-05,0.00445071933455582,2.97447268116016e-05,2.12379514059868e-05,1.86777776502663e-05,2.0170058676712e-05,1.8946030385445e-05,2.66040037173511e-05,2.09161899668946e-05,20), +(3.03462382611645e-05,3.26517930083994e-05,3.5242025468662e-05,2.53219745106391e-05,2.24384532945004e-05,2.4052346047657e-05,2.23865572957053e-05,3.1634313969082e-05,2.48285463481801e-05,21), +(1.95914465544536e-05,2.12379514059868e-05,2.27808649037128e-05,0.000341724375366877,1.4512761275113e-05,1.56475828693953e-05,1.44372366441415e-05,2.07952121981765e-05,1.61488256935919e-05,28), +(1.7347572525984e-05,1.86777776502663e-05,2.04116907052727e-05,1.4512761275113e-05,0.000432162526082388,1.38116514014465e-05,1.2712914948904e-05,1.82503165178506e-05,1.43043075345922e-05,30), +(1.68339762136661e-05,1.77836497166611e-05,2.36328309295222e-05,1.30183423732016e-05,1.18674654241553e-05,1.32467273128652e-05,1.24581739117775e-05,1.55624190959406e-05,1.33010638508213e-05,31), +(1.89643062824415e-05,2.06997140070717e-05,2.29045490159364e-05,1.57918175731019e-05,1.39864987449492e-05,1.50580274578455e-05,1.45908734129609e-05,1.95329296993327e-05,1.5814709481221e-05,32), +(1.69882826885005e-05,1.8946030385445e-05,2.00820439721439e-05,1.44372366441415e-05,1.2712914948904e-05,1.35209686474184e-05,0.00261563314789896,1.78285095864627e-05,1.46699314500019e-05,34), +(2.0278186540684e-05,2.18923409729654e-05,2.39981539939738e-05,1.71774589459438e-05,1.54654355357383e-05,1.62731485707636e-05,1.49253140625051e-05,2.18229800160297e-05,1.71923561673718e-05,35), +(2.44449336987598e-05,2.66040037173511e-05,2.84860148925308e-05,2.07952121981765e-05,1.82503165178506e-05,1.97667730441441e-05,1.78285095864627e-05,0.00166478601822712,2.0299952103232e-05,36), +(1.89914629921774e-05,2.09161899668946e-05,2.26026841007872e-05,1.61488256935919e-05,1.43043075345922e-05,1.52609063290127e-05,1.46699314500019e-05,2.0299952103232e-05,0.00306670170971682,39), +(0,0,0,0,0,0,0,0,0,41), +(0,0,0,0,0,0,0,0,0,17), +(0,0,0,0,0,0,0,0,0,18), +(2.51880677333017e-05,2.63051795435778e-05,2.79874748974906e-05,2.02888886670845e-05,1.8178636318197e-05,1.91308527003585e-05,1.83260023644133e-05,2.4422300558171e-05,1.96411467520551e-05,44), +(2.22402118719591e-05,2.37546284320705e-05,2.58463051055541e-05,1.83391609130854e-05,1.6300720519646e-05,1.74559091886791e-05,1.63733785575587e-05,2.26616253279828e-05,1.79541237435621e-05,45), +(3.01092775359837e-05,3.23865212934412e-05,4.09444584045994e-05,0,2.15470966302776e-05,2.39082636344032e-05,2.28296706429177e-05,2.9007671511595e-05,2.44201138973326e-05,46); +FLUSH TABLES; +CHECK TABLE t1 EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; diff --git a/mysql-test/r/mysql.result b/mysql-test/r/mysql.result index 9a920f3e196..a4d96c1c243 100644 --- a/mysql-test/r/mysql.result +++ b/mysql-test/r/mysql.result @@ -178,11 +178,13 @@ ERROR at line 1: DELIMITER cannot contain a backslash character 1 1 1 +This is a file starting with UTF8 BOM 0xEFBBBF +This is a file starting with UTF8 BOM 0xEFBBBF End of 5.0 tests WARNING: --server-arg option not supported in this configuration. Warning (Code 1286): Unknown table engine 'nonexistent' Warning (Code 1266): Using storage engine MyISAM for table 't2' -Warning (Code 1286): Unknown table engine 'nonexistent' +Warning (Code 1286): Unknown table engine 'nonexistent2' Warning (Code 1266): Using storage engine MyISAM for table 't2' Error (Code 1050): Table 't2' already exists drop tables t1, t2; diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 1deb9401aa1..e6485720c49 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -326,6 +326,7 @@ flush logs; drop table t1; 1 drop table t1; +shell> mysqlbinlog std_data/corrupt-relay-bin.000624 > var/tmp/bug31793.sql End of 5.0 tests flush logs; BUG#31611: Security risk with BINLOG statement diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 63c9590401c..4540c9d5218 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -726,3 +726,11 @@ count(a) 3 drop table t1; ############################################################## +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(0); +SELECT 1 FROM t1 GROUP BY (DATE(NULL)) WITH ROLLUP; +1 +1 +1 +DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index d646226daf8..7c25e948d6c 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -289,6 +289,13 @@ select * from t1 where a = 4; a b 4 4 drop table t1; +CREATE TABLE t1 (c1 INT, c2 INT, PRIMARY KEY USING BTREE (c1,c2)) ENGINE=MEMORY +PARTITION BY KEY(c2,c1) PARTITIONS 4; +INSERT INTO t1 VALUES (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6); +SELECT * FROM t1 WHERE c1 = 4; +c1 c2 +4 4 +DROP TABLE t1; CREATE TABLE t1 (a int) PARTITION BY LIST (a) PARTITIONS 1 @@ -1290,4 +1297,14 @@ create table t1 partition by key(s1) partitions 3; insert into t1 values (null,null); drop table t1; +CREATE TABLE t1(a int) +PARTITION BY RANGE (a) ( +PARTITION p1 VALUES LESS THAN (10), +PARTITION p2 VALUES LESS THAN (20) +); +ALTER TABLE t1 OPTIMIZE PARTITION p1 EXTENDED; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'EXTENDED' at line 1 +ALTER TABLE t1 ANALYZE PARTITION p1 EXTENDED; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'EXTENDED' at line 1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 47ffc90e3cd..d322d4cda76 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1979,8 +1979,6 @@ a drop table table_25411_a; drop table table_25411_b; DROP TRIGGER IF EXISTS trg; -Warnings: -Note 1360 Trigger does not exist SHOW CREATE TRIGGER trg; ERROR HY000: Trigger does not exist End of 5.1 tests. diff --git a/mysql-test/r/type_date.result b/mysql-test/r/type_date.result index 904db1a14d0..392260edb55 100644 --- a/mysql-test/r/type_date.result +++ b/mysql-test/r/type_date.result @@ -224,3 +224,10 @@ ERROR 22007: Incorrect date value: '0000-00-00' for column 'a' at row 1 SET SQL_MODE=DEFAULT; DROP TABLE t1,t2; End of 5.0 tests +create table t1 (a date, primary key (a))engine=memory; +insert into t1 values ('0000-01-01'), ('0000-00-01'), ('0001-01-01'); +select * from t1 where a between '0000-00-01' and '0000-00-02'; +a +0000-00-01 +drop table t1; +End of 5.1 tests diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result index 6fd7b39f226..431134b03c7 100644 --- a/mysql-test/r/user_var.result +++ b/mysql-test/r/user_var.result @@ -353,3 +353,14 @@ select @a:=f4, count(f4) from t1 group by 1 desc; 2.6 1 1.6 4 drop table t1; +create table t1(a int); +insert into t1 values(5),(4),(4),(3),(2),(2),(2),(1); +set @rownum := 0; +set @rank := 0; +set @prev_score := NULL; +select @rownum := @rownum + 1 as row, +@rank := IF(@prev_score!=a, @rownum, @rank) as rank, +@prev_score := a as score +from t1 order by score desc; +drop table t1; +End of 5.1 tests diff --git a/mysql-test/r/xml.result b/mysql-test/r/xml.result index d98173dbe15..56c884343e3 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -1022,4 +1022,11 @@ NULL NULL NULL select updatexml(NULL, NULL, NULL); updatexml(NULL, NULL, NULL) NULL +CREATE TABLE t1(a INT NOT NULL); +INSERT INTO t1 VALUES (0), (0); +SELECT 1 FROM t1 ORDER BY(UPDATEXML(a, '1', '1')); +1 +1 +1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/std_data/corrupt-relay-bin.000624 b/mysql-test/std_data/corrupt-relay-bin.000624 Binary files differnew file mode 100644 index 00000000000..21b4901211c --- /dev/null +++ b/mysql-test/std_data/corrupt-relay-bin.000624 diff --git a/mysql-test/suite/binlog/r/binlog_database.result b/mysql-test/suite/binlog/r/binlog_database.result new file mode 100644 index 00000000000..7deffb86244 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_database.result @@ -0,0 +1,56 @@ +set binlog_format=statement; +reset master; +create database testing_1; +use testing_1; +create table t1 (a int); +create function sf1 (a int) returns int return a+1; +create trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a); +create procedure sp1 (a int) insert into t1 values(a); +drop database testing_1; +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # create database testing_1 +master-bin.000001 # Query # # use `testing_1`; create table t1 (a int) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` function sf1 (a int) returns int return a+1 +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` procedure sp1 (a int) insert into t1 values(a) +master-bin.000001 # Query # # drop database testing_1 +set binlog_format=mixed; +reset master; +create database testing_1; +use testing_1; +create table t1 (a int); +create function sf1 (a int) returns int return a+1; +create trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a); +create procedure sp1 (a int) insert into t1 values(a); +drop database testing_1; +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # create database testing_1 +master-bin.000001 # Query # # use `testing_1`; create table t1 (a int) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` function sf1 (a int) returns int return a+1 +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` procedure sp1 (a int) insert into t1 values(a) +master-bin.000001 # Query # # drop database testing_1 +set binlog_format=row; +reset master; +create database testing_1; +use testing_1; +create table t1 (a int); +create function sf1 (a int) returns int return a+1; +create trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a); +create procedure sp1 (a int) insert into t1 values(a); +drop database testing_1; +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # create database testing_1 +master-bin.000001 # Query # # use `testing_1`; create table t1 (a int) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` function sf1 (a int) returns int return a+1 +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` trigger tr1 before insert on t1 for each row insert into t2 values (2*new.a) +master-bin.000001 # Query # # use `testing_1`; CREATE DEFINER=`root`@`localhost` procedure sp1 (a int) insert into t1 values(a) +master-bin.000001 # Query # # drop database testing_1 +show databases; +Database +information_schema +mysql +test diff --git a/mysql-test/suite/binlog/r/binlog_killed.result b/mysql-test/suite/binlog/r/binlog_killed.result index ba4f38fb4c1..72fda535b6f 100644 --- a/mysql-test/suite/binlog/r/binlog_killed.result +++ b/mysql-test/suite/binlog/r/binlog_killed.result @@ -9,4 +9,135 @@ insert into t2 values (null, null), (null, get_lock("a", 10)); select @result /* must be zero either way */; @result 0 +select RELEASE_LOCK("a"); +RELEASE_LOCK("a") +1 +delete from t1; +delete from t2; +insert into t1 values (1,1),(2,2); +begin; +update t1 set b=11 where a=2; +begin; +update t1 set b=b+10; +kill query ID; +rollback; +rollback; +select * from t1 order by a /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +begin; +delete from t1 where a=2; +begin; +delete from t1 where a=2; +kill query ID; +rollback; +rollback; +select * from t1 order by a /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +drop table if exists t4; +create table t4 (a int, b int) engine=innodb; +insert into t4 values (3, 3); +begin; +insert into t1 values (3, 3); +begin; +insert into t1 select * from t4 for update; +kill query ID; +rollback; +rollback; +select * from t1 /* must be the same as before (1,1),(2,2) */; +a b +1 1 +2 2 +drop table t4; +create table t4 (a int, b int) ENGINE=MyISAM /* for killing update and delete */; +create function bug27563(n int) +RETURNS int(11) +DETERMINISTIC +begin +if @b > 0 then +select get_lock("a", 20) into @a; +else +set @b= 1; +end if; +return n; +end| +delete from t4; +insert into t4 values (1,1), (1,1); +reset master; +select get_lock("a", 20); +get_lock("a", 20) +1 +set @b= 0; +update t4 set b=b + bug27563(b); +select count(*) FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; +count(*) +1 +kill query ID; +ERROR 70100: Query execution was interrupted +select * from t4 order by b /* must be (1,1), (1,2) */; +a b +1 1 +1 2 +select @b /* must be 1 at the end of a stmt calling bug27563() */; +@b +1 +must have the update query event more to FD +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # User var # # @`b`=0 +master-bin.000001 # Query # # use `test`; update t4 set b=b + bug27563(b) +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 0 /* must return 0 to mean the killed query is in */; +0 +0 +select RELEASE_LOCK("a"); +RELEASE_LOCK("a") +1 +delete from t4; +insert into t4 values (1,1), (2,2); +reset master; +select get_lock("a", 20); +get_lock("a", 20) +1 +set @b= 0; +delete from t4 where b=bug27563(1) or b=bug27563(2); +select count(*) FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; +count(*) +1 +kill query ID; +ERROR 70100: Query execution was interrupted +select count(*) from t4 /* must be 1 */; +count(*) +1 +select @b /* must be 1 at the end of a stmt calling bug27563() */; +@b +1 +must have the delete query event more to FD +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # User var # # @`b`=0 +master-bin.000001 # Query # # use `test`; delete from t4 where b=bug27563(1) or b=bug27563(2) +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 0 /* must return 0 to mean the killed query is in */; +0 +0 +select RELEASE_LOCK("a"); +RELEASE_LOCK("a") +1 +drop table t4; +drop function bug27563; drop table t1,t2,t3; +end of the tests diff --git a/mysql-test/suite/binlog/r/binlog_killed_simulate.result b/mysql-test/suite/binlog/r/binlog_killed_simulate.result new file mode 100644 index 00000000000..f6a5ddade51 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_killed_simulate.result @@ -0,0 +1,33 @@ +drop table if exists t1,t2; +create table t1 (a int) engine=MyISAM; +insert into t1 set a=1; +reset master; +update t1 set a=2 /* will be "killed" after work has been done */; +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 1 /* must return 1 as query completed before got killed*/; +1 +1 +create table t2 (a int, b int) ENGINE=MyISAM; +reset master; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2 /* will be "killed" in the middle */; +ERROR 70100: Query execution was interrupted +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Begin_load_query # # ;file_id=1;block_len=12 +master-bin.000001 # Execute_load_query # # use `test`; load data infile '../std_data_ln/rpl_loaddata.dat' into table t2 /* will be "killed" in the middle */ ;file_id=1 +select +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +(@a:=load_file("MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null +1 +select 0 /* must return 0 to mean the killed query is in */; +0 +0 +drop table t1,t2; +end of the tests diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index 6ac942176c7..f69d5717a1f 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -413,3 +413,236 @@ select @a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%" @a not like "%#%error_code=%error_code=%" 1 1 drop table t1, t2; +create temporary table tt (a int unique); +create table ti (a int) engine=innodb; +reset master; +show master status; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 106 +begin; +insert into ti values (1); +insert into ti values (2) ; +insert into tt select * from ti; +rollback; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +select count(*) from tt /* 2 */; +count(*) +2 +show master status; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 395 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Table_map # # table_id: # (test.ti) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (test.ti) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from ti /* zero */; +count(*) +0 +insert into ti select * from tt; +select * from ti /* that is what slave would miss - bug#28960 */; +a +1 +2 +delete from ti; +delete from tt where a=1; +reset master; +show master status; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 106 +begin; +insert into ti values (1); +insert into ti values (2) /* to make the dup error in the following */; +insert into tt select * from ti /* one affected and error */; +ERROR 23000: Duplicate entry '2' for key 'a' +rollback; +show master status; +File Position Binlog_Do_DB Binlog_Ignore_DB +master-bin.000001 106 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +select count(*) from ti /* zero */; +count(*) +0 +insert into ti select * from tt; +select * from tt /* that is what otherwise slave missed - the bug */; +a +1 +2 +drop table ti; +drop function if exists bug27417; +drop table if exists t1,t2; +CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; +CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a)); +create function bug27417(n int) +RETURNS int(11) +begin +insert into t1 values (null); +return n; +end| +reset master; +insert into t2 values (bug27417(1)); +insert into t2 select bug27417(2); +reset master; +insert into t2 values (bug27417(2)); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=3 +master-bin.000001 # Query # # use `test`; insert into t2 values (bug27417(2)) +select count(*) from t1 /* must be 3 */; +count(*) +3 +reset master; +select count(*) from t2; +count(*) +2 +delete from t2 where a=bug27417(3); +select count(*) from t2 /* nothing got deleted */; +count(*) +2 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=4 +master-bin.000001 # Query # # use `test`; delete from t2 where a=bug27417(3) +select count(*) from t1 /* must be 5 */; +count(*) +5 +delete t2 from t2 where t2.a=bug27417(100) /* must not affect t2 */; +affected rows: 0 +select count(*) from t1 /* must be 7 */; +count(*) +7 +drop table t1,t2; +CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; +CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique) ENGINE=MyISAM; +CREATE TABLE t4 (a int, PRIMARY KEY (a), b int unique) ENGINE=Innodb; +CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +insert into t2 values (1); +reset master; +insert into t2 values (bug27417(1)); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=1 +master-bin.000001 # Query # # use `test`; insert into t2 values (bug27417(1)) +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +delete from t1; +delete from t2; +insert into t2 values (2); +reset master; +insert into t2 select bug27417(1) union select bug27417(2); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=2 +master-bin.000001 # Query # # use `test`; insert into t2 select bug27417(1) union select bug27417(2) +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 2 */; +count(*) +2 +delete from t1; +insert into t3 values (1,1),(2,3),(3,4); +reset master; +update t3 set b=b+bug27417(1); +ERROR 23000: Duplicate entry '4' for key 'b' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=4 +master-bin.000001 # Query # # use `test`; update t3 set b=b+bug27417(1) +select count(*) from t1 /* must be 2 */; +count(*) +2 +delete from t3; +delete from t4; +insert into t3 values (1,1); +insert into t4 values (1,1),(2,2); +reset master; +UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */; +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=6 +master-bin.000001 # Query # # use `test`; UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */ +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 4 */; +count(*) +4 +delete from t1; +delete from t3; +delete from t4; +insert into t3 values (1,1),(2,2); +insert into t4 values (1,1),(2,2); +reset master; +UPDATE t3,t4 SET t3.a=t4.a + bug27417(1); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +select count(*) from t1 /* must be 1 */; +count(*) +1 +drop table t4; +delete from t1; +delete from t2; +delete from t3; +insert into t2 values (1); +insert into t3 values (1,1); +create trigger trg_del before delete on t2 for each row +insert into t3 values (bug27417(1), 2); +reset master; +delete from t2; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=9 +master-bin.000001 # Query # # use `test`; delete from t2 +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +drop trigger trg_del; +delete from t1; +delete from t2; +delete from t5; +create trigger trg_del_t2 after delete on t2 for each row +insert into t1 values (1); +insert into t2 values (2),(3); +insert into t5 values (1),(2); +reset master; +delete t2.* from t2,t5 where t2.a=t5.a + 1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; delete t2.* from t2,t5 where t2.a=t5.a + 1 +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +delete from t1; +create table t4 (a int default 0, b int primary key) engine=innodb; +insert into t4 values (0, 17); +reset master; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); +ERROR 23000: Duplicate entry '17' for key 'PRIMARY' +select * from t4; +a b +0 17 +select count(*) from t1 /* must be 2 */; +count(*) +2 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=10 +master-bin.000001 # Begin_load_query # # ;file_id=1;block_len=12 +master-bin.000001 # Intvar # # INSERT_ID=10 +master-bin.000001 # Execute_load_query # # use `test`; load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2) ;file_id=1 +master-bin.000001 # Query # # use `test`; ROLLBACK +drop trigger trg_del_t2; +drop table t1,t2,t3,t4,t5; +drop function bug27417; diff --git a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result index a1c83ffc73d..a79642a9204 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result +++ b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result @@ -124,6 +124,7 @@ master-bin.000001 # Query # # use `test`; replace into t1 select * from t3 drop table t1,t2,t3; CREATE TABLE t1(a INT) ENGINE=BLACKHOLE; INSERT DELAYED INTO t1 VALUES(1); +ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines DROP TABLE t1; CREATE TABLE t1(a INT, b INT) ENGINE=BLACKHOLE; DELETE FROM t1 WHERE a=10; diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result index 5ef36861c30..c15478bc826 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result @@ -380,7 +380,8 @@ select @a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%" @a not like "%#%error_code=%error_code=%" 1 1 drop table t1, t2; -create table tt (a int unique); +set @@session.binlog_format=statement; +create temporary table tt (a int unique); create table ti (a int) engine=innodb; reset master; show master status; @@ -399,18 +400,18 @@ count(*) show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 515 -show binlog events from 106; +show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; BEGIN -master-bin.000001 # Query 1 # use `test`; insert into ti values (1) -master-bin.000001 # Query 1 # use `test`; insert into ti values (2) -master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti -master-bin.000001 # Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # use `test`; insert into ti values (1) +master-bin.000001 # Query # # use `test`; insert into ti values (2) +master-bin.000001 # Query # # use `test`; insert into tt select * from ti +master-bin.000001 # Query # # use `test`; ROLLBACK select count(*) from ti /* zero */; count(*) 0 insert into ti select * from tt; -select * from ti /* that is what slave would miss - a bug */; +select * from ti /* that is what slave would miss - bug#28960 */; a 1 2 @@ -431,13 +432,13 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back show master status; File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 589 -show binlog events from 106; +show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; BEGIN -master-bin.000001 # Query 1 # use `test`; insert into ti values (1) -master-bin.000001 # Query 1 # use `test`; insert into ti values (2) /* to make the dup error in the following */ -master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti /* one affected and error */ -master-bin.000001 # Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # use `test`; insert into ti values (1) +master-bin.000001 # Query # # use `test`; insert into ti values (2) /* to make the dup error in the following */ +master-bin.000001 # Query # # use `test`; insert into tt select * from ti /* one affected and error */ +master-bin.000001 # Query # # use `test`; ROLLBACK select count(*) from ti /* zero */; count(*) 0 @@ -446,7 +447,7 @@ select * from tt /* that is what otherwise slave missed - the bug */; a 1 2 -drop table ti,tt; +drop table ti; drop function if exists bug27417; drop table if exists t1,t2; CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; @@ -463,6 +464,10 @@ insert into t2 select bug27417(2); reset master; insert into t2 values (bug27417(2)); ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=3 +master-bin.000001 # Query # # use `test`; insert into t2 values (bug27417(2)) select count(*) from t1 /* must be 3 */; count(*) 3 @@ -474,6 +479,10 @@ delete from t2 where a=bug27417(3); select count(*) from t2 /* nothing got deleted */; count(*) 2 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=4 +master-bin.000001 # Query # # use `test`; delete from t2 where a=bug27417(3) select count(*) from t1 /* must be 5 */; count(*) 5 @@ -482,6 +491,134 @@ affected rows: 0 select count(*) from t1 /* must be 7 */; count(*) 7 -drop function bug27417; drop table t1,t2; +CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; +CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +CREATE TABLE t3 (a int, PRIMARY KEY (a), b int unique) ENGINE=MyISAM; +CREATE TABLE t4 (a int, PRIMARY KEY (a), b int unique) ENGINE=Innodb; +CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +insert into t2 values (1); +reset master; +insert into t2 values (bug27417(1)); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=1 +master-bin.000001 # Query # # use `test`; insert into t2 values (bug27417(1)) +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +delete from t1; +delete from t2; +insert into t2 values (2); +reset master; +insert into t2 select bug27417(1) union select bug27417(2); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=2 +master-bin.000001 # Query # # use `test`; insert into t2 select bug27417(1) union select bug27417(2) +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 2 */; +count(*) +2 +delete from t1; +insert into t3 values (1,1),(2,3),(3,4); +reset master; +update t3 set b=b+bug27417(1); +ERROR 23000: Duplicate entry '4' for key 'b' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=4 +master-bin.000001 # Query # # use `test`; update t3 set b=b+bug27417(1) +select count(*) from t1 /* must be 2 */; +count(*) +2 +delete from t3; +delete from t4; +insert into t3 values (1,1); +insert into t4 values (1,1),(2,2); +reset master; +UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */; +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=6 +master-bin.000001 # Query # # use `test`; UPDATE t4,t3 SET t4.a=t3.a + bug27417(1) /* top level non-ta table */ +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 4 */; +count(*) +4 +delete from t1; +delete from t3; +delete from t4; +insert into t3 values (1,1),(2,2); +insert into t4 values (1,1),(2,2); +reset master; +UPDATE t3,t4 SET t3.a=t4.a + bug27417(1); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +select count(*) from t1 /* must be 1 */; +count(*) +1 +drop table t4; +delete from t1; +delete from t2; +delete from t3; +insert into t2 values (1); +insert into t3 values (1,1); +create trigger trg_del before delete on t2 for each row +insert into t3 values (bug27417(1), 2); +reset master; +delete from t2; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=9 +master-bin.000001 # Query # # use `test`; delete from t2 +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +drop trigger trg_del; +delete from t1; +delete from t2; +delete from t5; +create trigger trg_del_t2 after delete on t2 for each row +insert into t1 values (1); +insert into t2 values (2),(3); +insert into t5 values (1),(2); +reset master; +delete t2.* from t2,t5 where t2.a=t5.a + 1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; delete t2.* from t2,t5 where t2.a=t5.a + 1 +master-bin.000001 # Query # # use `test`; ROLLBACK +select count(*) from t1 /* must be 1 */; +count(*) +1 +delete from t1; +create table t4 (a int default 0, b int primary key) engine=innodb; +insert into t4 values (0, 17); +reset master; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); +ERROR 23000: Duplicate entry '17' for key 'PRIMARY' +select * from t4; +a b +0 17 +select count(*) from t1 /* must be 2 */; +count(*) +2 +show binlog events from <binlog_start>; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Intvar # # INSERT_ID=10 +master-bin.000001 # Begin_load_query # # ;file_id=1;block_len=12 +master-bin.000001 # Intvar # # INSERT_ID=10 +master-bin.000001 # Execute_load_query # # use `test`; load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2) ;file_id=1 +master-bin.000001 # Query # # use `test`; ROLLBACK +drop trigger trg_del_t2; +drop table t1,t2,t3,t4,t5; +drop function bug27417; +set @@session.binlog_format=@@global.binlog_format; end of tests diff --git a/mysql-test/suite/binlog/t/binlog_database.test b/mysql-test/suite/binlog/t/binlog_database.test new file mode 100644 index 00000000000..ee236b4e5ea --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_database.test @@ -0,0 +1,12 @@ +# A wrapper to test that dropping a database is binlogged +# correctly. We test all three modes in the same file to avoid +# unecessary server restarts. + +set binlog_format=statement; +source extra/binlog_tests/database.test; +set binlog_format=mixed; +source extra/binlog_tests/database.test; +set binlog_format=row; +source extra/binlog_tests/database.test; + +show databases; diff --git a/mysql-test/suite/binlog/t/binlog_killed.test b/mysql-test/suite/binlog/t/binlog_killed.test index 6c0b4b46a4e..e5f7288b17c 100644 --- a/mysql-test/suite/binlog/t/binlog_killed.test +++ b/mysql-test/suite/binlog/t/binlog_killed.test @@ -55,194 +55,277 @@ enable_result_log; select @result /* must be zero either way */; -# the functions are either *insensitive* to killing or killing can cause -# strange problmes with the error propagation out of SF's stack -# Bug#27563, Bug#27565, BUG#24971 -# -# TODO: use if's block as regression test for the bugs or remove -# -if (0) -{ -delimiter |; -create function bug27563() -RETURNS int(11) -DETERMINISTIC -begin - select get_lock("a", 10) into @a; - return 1; -end| -delimiter ;| -# the function is sensitive to killing requiring innodb though with wrong client error -# TO FIX in BUG#27565; TODO: remove --error 1105 afterwards -delimiter |; -create function bug27565() -RETURNS int(11) -DETERMINISTIC -begin - select a from t1 where a=1 into @a for update; - return 1; -end| -delimiter ;| +--remove_file $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog +connection con1; +select RELEASE_LOCK("a"); -reset master; +# +# bug#27571 asynchronous setting mysql_`query`::error and Query_log_e::error_code +# +# checking that killing inside of select loops is safe as before +# killing after the loop can be only simulated - another test -### ta table case: killing causes rollback +delete from t1; +delete from t2; +insert into t1 values (1,1),(2,2); -# A. autocommit ON +# +# simple update +# connection con1; -select get_lock("a", 20); +begin; update t1 set b=11 where a=2; connection con2; let $ID= `select connection_id()`; -send insert into t1 values (bug27563(),1); +begin; +send update t1 set b=b+10; connection con1; +--replace_result $ID ID eval kill query $ID; +rollback; + +# Bug #32148 killi query may be ineffective +# forced to comment out the test's outcome +# and mask out ineffective ER_QUERY_INTERRUPTED +# todo1: revert back upon fixing bug#32148 +# todo2: the tests need refining in that +# killing should wait till the victim requested +# its lock (wait_condition available in 5.1 tests) connection con2; -# todo (re-record test): after bugs 27563,27565 got fixed affected rows will report zero ---enable_info -# todo: remove 0 return after fixing Bug#27563 --error 0,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error ---disable_info -###--replace_column 2 # 5 # -### show binlog events from 98 /* nothing in binlog unless Bug#27563 */; -show master status /* must be only FD event unless Bug#27563 */; -select count(*) from t1 /* must be zero unless Bug#27563 */; - -# M. multi-statement-ta +reap; +rollback; +select * from t1 order by a /* must be the same as before (1,1),(2,2) */; + +# +# multi update +# commented out as Bug #31807 multi-update,delete killing does not report with ER_QUERY_INTERRUPTED +# in the way +# +# connection con1; +# begin; update t1 set b=b+10; + +# connection con2; +# send update t1 as t_1,t1 as t_2 set t_1.b=11 where t_2.a=2; + +# connection con1; +# --replace_result $ID ID +# eval kill query $ID; +# rollback; + +# disable_abort_on_error; + +# connection con2; +# --error HY000,ER_QUERY_INTERRUPTED +# reap; +# select * from t1 /* must be the same as before (1,1),(2,2) */; + +# enable_abort_on_error; +# +# simple delete +# +connection con1; +begin; delete from t1 where a=2; + connection con2; let $ID= `select connection_id()`; begin; -send insert into t1 values (bug27563(),1); +send delete from t1 where a=2; connection con1; +--replace_result $ID ID eval kill query $ID; +rollback; + connection con2; -# todo (re-record test): after bugs 27563,27565 got fixed affected rows will report zero ---enable_info -# todo: remove 0 return after fixing Bug#27563 --error 0,ER_QUERY_INTERRUPTED reap; ---disable_info -select count(*) from t1 /* must be zero unless Bug#27563 */; -commit; +rollback; +# todo1,2 above +select * from t1 order by a /* must be the same as before (1,1),(2,2) */; -### non-ta table case: killing must be recorded in binlog +# +# multi delete +# the same as for multi-update +# +# connection con1; +# begin; delete from t1 where a=2; -reset master; +# connection con2; +# send delete t1 from t1 where t1.a=2; + +# connection con1; +# --replace_result $ID ID +# eval kill query $ID; +# rollback; + +# connection con2; +# --error 0,ER_QUERY_INTERRUPTED +# reap; +# select * from t1 /* must be the same as before (1,1),(2,2) */; +# +# insert select +# +connection con1; +--disable_warnings +drop table if exists t4; +--enable_warnings +create table t4 (a int, b int) engine=innodb; +insert into t4 values (3, 3); +begin; insert into t1 values (3, 3); connection con2; let $ID= `select connection_id()`; -send insert into t2 values (bug27563(),1); +begin; +send insert into t1 select * from t4 for update; connection con1; +--replace_result $ID ID eval kill query $ID; +rollback; connection con2; -# todo: remove 0 return after fixing Bug#27563 --error 0,ER_QUERY_INTERRUPTED reap; -select count(*) from t2 /* must be one */; -#show binlog events from 98 /* must have the insert on non-ta table */; -show master status /* must have the insert event more to FD */; -# the value of the error flag of KILLED_QUERY is tested further +# todo 1,2 above +rollback; +select * from t1 /* must be the same as before (1,1),(2,2) */; -connection con1; -select RELEASE_LOCK("a"); +drop table t4; # cleanup for the sub-case -### test with effective killing of SF() +### +## non-ta table case: killing must be recorded in binlog +### +create table t4 (a int, b int) ENGINE=MyISAM /* for killing update and delete */; -delete from t1; -delete from t2; -insert into t1 values (1,1); -insert into t2 values (1,1); +delimiter |; +create function bug27563(n int) +RETURNS int(11) +DETERMINISTIC +begin + if @b > 0 then + select get_lock("a", 20) into @a; + else + set @b= 1; + end if; + return n; +end| +delimiter ;| + +# +# update +# -# -# Bug#27565 -# test where KILL is propagated as error to the top level -# still another bug with the error message to the user -# todo: fix reexecute the result file after fixing -# -begin; update t1 set b=0 where a=1; +delete from t4; +insert into t4 values (1,1), (1,1); +reset master; +connection con1; +select get_lock("a", 20); connection con2; let $ID= `select connection_id()`; -send update t2 set b=bug27565()-1 where a=1; +set @b= 0; +send update t4 set b=b + bug27563(b); connection con1; +let $wait_condition= select count(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; +source include/wait_condition.inc; +select count(*) FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; + +--replace_result $ID ID eval kill query $ID; -commit; connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error -select * from t1 /* must be: (1,0) */; -select * from t2 /* must be as before: (1,1) */; +--error ER_QUERY_INTERRUPTED +reap; +select * from t4 order by b /* must be (1,1), (1,2) */; +select @b /* must be 1 at the end of a stmt calling bug27563() */; +--echo must have the update query event more to FD +source include/show_binlog_events.inc; -## bug#22725 with effective and propagating killing -# -# top-level ta-table +# a proof the query is binlogged with an error + +--exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; + +# cleanup for the sub-case connection con1; -delete from t3; +select RELEASE_LOCK("a"); +--remove_file $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog + +# +# delete +# + +delete from t4; +insert into t4 values (1,1), (2,2); reset master; -begin; update t1 set b=0 where a=1; +connection con1; +select get_lock("a", 20); connection con2; let $ID= `select connection_id()`; -# the query won't perform completely since the function gets interrupted -send insert into t3 values (0,0),(1,bug27565()); +set @b= 0; +send delete from t4 where b=bug27563(1) or b=bug27563(2); connection con1; +let $wait_condition= select count(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; +source include/wait_condition.inc; +select count(*) FROM INFORMATION_SCHEMA.PROCESSLIST where state='User lock'; +--replace_result $ID ID eval kill query $ID; -rollback; connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error -select count(*) from t3 /* must be zero */; -show master status /* nothing in binlog */; - -# top-level non-ta-table -connection con1; -delete from t2; -reset master; -begin; update t1 set b=0 where a=1; +--error ER_QUERY_INTERRUPTED +reap; +select count(*) from t4 /* must be 1 */; +select @b /* must be 1 at the end of a stmt calling bug27563() */; +--echo must have the delete query event more to FD +source include/show_binlog_events.inc; -connection con2; -let $ID= `select connection_id()`; -# the query won't perform completely since the function gets intrurrupted -send insert into t2 values (0,0),(1,bug27565()) /* non-ta t2 */; +# a proof the query is binlogged with an error + +--exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; +# cleanup for the sub-case connection con1; -eval kill query $ID; -rollback; +select RELEASE_LOCK("a"); +--remove_file $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog -connection con2; -# todo: fix Bug #27565 killed query of SF() is not reported correctly and -# remove 1105 (wrong) -#--error ER_QUERY_INTERRUPTED ---error 1105,ER_QUERY_INTERRUPTED -reap; ### pb: wrong error +drop table t4; -select count(*) from t2 /* count must be one */; -show master status /* insert into non-ta must be in binlog */; +# +# load data - see simulation tests +# + + +# bug#27571 cleanup drop function bug27563; -drop function bug27565; -} -system rm $MYSQLTEST_VARDIR/tmp/kill_query_calling_sp.binlog ; + +# +# common cleanup +# drop table t1,t2,t3; +--echo end of the tests diff --git a/mysql-test/suite/binlog/t/binlog_killed_simulate-master.opt b/mysql-test/suite/binlog/t/binlog_killed_simulate-master.opt new file mode 100644 index 00000000000..90c70ecee29 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_killed_simulate-master.opt @@ -0,0 +1 @@ +--loose-debug=d,simulate_kill_bug27571 diff --git a/mysql-test/suite/binlog/t/binlog_killed_simulate.test b/mysql-test/suite/binlog/t/binlog_killed_simulate.test new file mode 100644 index 00000000000..2121a90dc8c --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_killed_simulate.test @@ -0,0 +1,69 @@ +-- source include/have_debug.inc +-- source include/have_binlog_format_mixed_or_statement.inc +# +# bug#27571 asynchronous setting mysql_$query()'s local error and +# Query_log_event::error_code +# + +--disable_warnings +drop table if exists t1,t2; +--enable_warnings + +# +# Checking that killing upon successful row-loop does not affect binlogging +# + +create table t1 (a int) engine=MyISAM; +insert into t1 set a=1; +reset master; + +update t1 set a=2 /* will be "killed" after work has been done */; + +# a proof the query is binlogged with no error +#todo: introduce a suite private macro that provides numeric values +# for some constants like the offset of the first real event +# that is different between severs versions. +--exec $MYSQL_BINLOG --start-position=106 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 1 */`; +eval select $error_code /* must return 1 as query completed before got killed*/; + +# cleanup for the sub-case +system rm $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog; + + +# +# Checking that killing inside of row-loop for LOAD DATA into +# non-transactional table affects binlogging +# + +create table t2 (a int, b int) ENGINE=MyISAM; +reset master; +--error ER_QUERY_INTERRUPTED +load data infile '../std_data_ln/rpl_loaddata.dat' into table t2 /* will be "killed" in the middle */; + + +# a proof the query is binlogged with an error + +source include/show_binlog_events.inc; + +--exec $MYSQL_BINLOG --start-position=98 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval select +(@a:=load_file("$MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +let $error_code= `select @a like "%#%error_code=0%" /* must return 0*/`; +eval select $error_code /* must return 0 to mean the killed query is in */; + +# cleanup for the sub-case +system rm $MYSQLTEST_VARDIR/tmp/binlog_killed_bug27571.binlog; + + +drop table t1,t2; + +--echo end of the tests diff --git a/mysql-test/suite/binlog/t/binlog_row_mix_innodb_myisam.test b/mysql-test/suite/binlog/t/binlog_row_mix_innodb_myisam.test index 7b7753a487e..3148cc50fd0 100644 --- a/mysql-test/suite/binlog/t/binlog_row_mix_innodb_myisam.test +++ b/mysql-test/suite/binlog/t/binlog_row_mix_innodb_myisam.test @@ -31,3 +31,5 @@ eval select @a like "%#%error_code=0%ROLLBACK/*!*/;%ROLLBACK /* added by mysqlbinlog */;%", @a not like "%#%error_code=%error_code=%"; drop table t1, t2; + +-- source extra/binlog_tests/mix_innodb_myisam_side_effects.test diff --git a/mysql-test/suite/binlog/t/binlog_stm_mix_innodb_myisam.test b/mysql-test/suite/binlog/t/binlog_stm_mix_innodb_myisam.test index 1815f3deb34..e7149e03b87 100644 --- a/mysql-test/suite/binlog/t/binlog_stm_mix_innodb_myisam.test +++ b/mysql-test/suite/binlog/t/binlog_stm_mix_innodb_myisam.test @@ -24,123 +24,9 @@ eval select drop table t1, t2; -# -# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack -# bug #28960 non-trans temp table changes with insert .. select -# not binlogged after rollback -# -# testing appearence of insert into temp_table in binlog. -# There are two branches of execution that require different setup. +set @@session.binlog_format=statement; +-- source extra/binlog_tests/mix_innodb_myisam_side_effects.test +set @@session.binlog_format=@@global.binlog_format; -## send_eof() branch - -# prepare - -create table tt (a int unique); -create table ti (a int) engine=innodb; -reset master; -show master status; - -# action - -begin; -insert into ti values (1); -insert into ti values (2) ; -insert into tt select * from ti; -rollback; - -# check - -select count(*) from tt /* 2 */; -show master status; ---replace_column 2 # 5 # -show binlog events from 106; -select count(*) from ti /* zero */; -insert into ti select * from tt; -select * from ti /* that is what slave would miss - a bug */; - - -## send_error() branch -delete from ti; -delete from tt where a=1; -reset master; -show master status; - -# action - -begin; -insert into ti values (1); -insert into ti values (2) /* to make the dup error in the following */; ---error ER_DUP_ENTRY -insert into tt select * from ti /* one affected and error */; -rollback; - -# check - -show master status; ---replace_column 2 # 5 # -show binlog events from 106; -select count(*) from ti /* zero */; -insert into ti select * from tt; -select * from tt /* that is what otherwise slave missed - the bug */; - -drop table ti,tt; - - -# -# Bug #27417 thd->no_trans_update.stmt lost value inside of SF-exec-stack -# -# Testing asserts: if there is a side effect of modifying non-transactional -# table thd->no_trans_update.stmt must be TRUE; -# the assert is active with debug build -# - ---disable_warnings -drop function if exists bug27417; -drop table if exists t1,t2; ---enable_warnings -# side effect table -CREATE TABLE t1 (a int NOT NULL auto_increment primary key) ENGINE=MyISAM; -# target tables -CREATE TABLE t2 (a int NOT NULL auto_increment, PRIMARY KEY (a)); - -delimiter |; -create function bug27417(n int) -RETURNS int(11) -begin - insert into t1 values (null); - return n; -end| -delimiter ;| - -reset master; - -# execute - -insert into t2 values (bug27417(1)); -insert into t2 select bug27417(2); -reset master; - ---error ER_DUP_ENTRY -insert into t2 values (bug27417(2)); -#TODO: Andrei: enable this test after 23333 is pushed -#show master status; /* only (!) with fixes for #23333 will show there is the query */; -select count(*) from t1 /* must be 3 */; - -reset master; -select count(*) from t2; -delete from t2 where a=bug27417(3); -select count(*) from t2 /* nothing got deleted */; -#TODO: Andrei: enable this test after 23333 is pushed -#show master status; /* the query must be in regardless of #23333 */; -select count(*) from t1 /* must be 5 */; - ---enable_info -delete t2 from t2 where t2.a=bug27417(100) /* must not affect t2 */; ---disable_info -select count(*) from t1 /* must be 7 */; - -drop function bug27417; -drop table t1,t2; --echo end of tests diff --git a/mysql-test/suite/binlog/t/disabled.def b/mysql-test/suite/binlog/t/disabled.def index 888298bbb09..a6e73fa31d8 100644 --- a/mysql-test/suite/binlog/t/disabled.def +++ b/mysql-test/suite/binlog/t/disabled.def @@ -9,3 +9,4 @@ # Do not use any TAB characters for whitespace. # ############################################################################## +binlog_multi_engine : Bug#32663 binlog_multi_engine.test fails randomly diff --git a/mysql-test/suite/manual/r/rpl_replication_delay.result b/mysql-test/suite/manual/r/rpl_replication_delay.result new file mode 100644 index 00000000000..a8fa6ce8265 --- /dev/null +++ b/mysql-test/suite/manual/r/rpl_replication_delay.result @@ -0,0 +1,136 @@ +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; +show slave status /* Second_behind reports 0 */;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port 9306 +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 106 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 106 +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 0 +Master_SSL_Verify_Server_Cert No +Last_IO_Errno 0 +Last_IO_Error +Last_SQL_Errno 0 +Last_SQL_Error +drop table if exists t1; +Warnings: +Note 1051 Unknown table 't1' +create table t1 (f1 int); +flush logs /* contaminate rli->last_master_timestamp */; +lock table t1 write; +insert into t1 values (1); +show slave status /* bug emulated: reports slave threads starting time about 3*3 not 3 secs */;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port 9306 +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 367 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 279 +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 9 +Master_SSL_Verify_Server_Cert No +Last_IO_Errno 0 +Last_IO_Error +Last_SQL_Errno 0 +Last_SQL_Error +unlock tables; +flush logs /* this time rli->last_master_timestamp is not affected */; +lock table t1 write; +insert into t1 values (2); +show slave status /* reports the correct diff with master query time about 3+3 secs */;; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port 9306 +Connect_Retry 1 +Master_Log_File master-bin.000001 +Read_Master_Log_Pos 455 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 367 +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 7 +Master_SSL_Verify_Server_Cert No +Last_IO_Errno 0 +Last_IO_Error +Last_SQL_Errno 0 +Last_SQL_Error +unlock tables; +drop table t1; diff --git a/mysql-test/suite/manual/t/rpl_replication_delay-slave.opt b/mysql-test/suite/manual/t/rpl_replication_delay-slave.opt new file mode 100644 index 00000000000..24a4c5952fe --- /dev/null +++ b/mysql-test/suite/manual/t/rpl_replication_delay-slave.opt @@ -0,0 +1 @@ +--loose-debug=d,let_first_flush_log_change_timestamp diff --git a/mysql-test/suite/manual/t/rpl_replication_delay.test b/mysql-test/suite/manual/t/rpl_replication_delay.test new file mode 100644 index 00000000000..8230698c8f9 --- /dev/null +++ b/mysql-test/suite/manual/t/rpl_replication_delay.test @@ -0,0 +1,71 @@ +# +# Testing replication delay reporting (bug#29309) +# there is an unavoidable non-determinism in the test +# please compare the results with the comments +# + + +source include/master-slave.inc; + +connection master; +#connection slave; +sync_slave_with_master; +--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT +--replace_column 1 # 8 # 9 # 23 # +--query_vertical show slave status /* Second_behind reports 0 */; +sleep 3; + +### bug emulation + +connection master; +drop table if exists t1; +create table t1 (f1 int); +sleep 3; + +#connection slave; +sync_slave_with_master; +flush logs /* contaminate rli->last_master_timestamp */; + +connection slave; +lock table t1 write; + +connection master; +insert into t1 values (1); + +sleep 3; + +connection slave; +--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT +--replace_column 1 # 8 # 9 # 23 # +--query_vertical show slave status /* bug emulated: reports slave threads starting time about 3*3 not 3 secs */; +unlock tables; + +connection master; +sync_slave_with_master; + +### bugfix + + +connection slave; +flush logs /* this time rli->last_master_timestamp is not affected */; +lock table t1 write; + +connection master; +insert into t1 values (2); +sleep 3; + +connection slave; +--replace_result $DEFAULT_MASTER_PORT DEFAULT_MASTER_PORT +--replace_column 1 # 8 # 9 # 23 # +--query_vertical show slave status /* reports the correct diff with master query time about 3+3 secs */; +unlock tables; + +connection master; +drop table t1; + +#connection slave; +sync_slave_with_master; + + +# End of tests + diff --git a/mysql-test/suite/ndb/t/disabled.def b/mysql-test/suite/ndb/t/disabled.def index f876039a042..1752314ea47 100644 --- a/mysql-test/suite/ndb/t/disabled.def +++ b/mysql-test/suite/ndb/t/disabled.def @@ -12,7 +12,10 @@ partition_03ndb : BUG#16385 2006-03-24 mikael Partitions: crash when updating a range partitioned NDB table ndb_partition_error2 : HF is not sure if the test can work as internded on all the platforms +ndb_binlog_basic : Bug #32759 2007-11-27 mats ndb_binlog_basic assert failure 'thd->transaction.stmt.modified_non_trans_table' # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open #ndb_binlog_ddl_multi : BUG#18976 2006-04-10 kent CRBR: multiple binlog, second binlog may miss schema log events #ndb_binlog_discover : bug#21806 2006-08-24 +ndb_backup_print : Bug#32357: ndb_backup_print test fails sometimes in pushbuild +ndb_dd_backuprestore : Bug#32659 ndb_dd_backuprestore.test fails randomly diff --git a/mysql-test/suite/rpl/data/rpl_bug28618.dat b/mysql-test/suite/rpl/data/rpl_bug28618.dat new file mode 100644 index 00000000000..b800c4dd39d --- /dev/null +++ b/mysql-test/suite/rpl/data/rpl_bug28618.dat @@ -0,0 +1,3 @@ +1|master only +2|master only +3|master only diff --git a/mysql-test/suite/rpl/include/rpl_mixed_check_select.inc b/mysql-test/suite/rpl/include/rpl_mixed_check_select.inc index 5d3b80e077b..b3e0cefbbd7 100644 --- a/mysql-test/suite/rpl/include/rpl_mixed_check_select.inc +++ b/mysql-test/suite/rpl/include/rpl_mixed_check_select.inc @@ -7,15 +7,15 @@ --echo ==========MASTER========== SELECT COUNT(*) FROM t1; -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; SELECT COUNT(*) FROM t2; -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; sync_slave_with_master; --echo ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; SELECT COUNT(*) FROM t2; -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; connection master; diff --git a/mysql-test/suite/rpl/include/rpl_mixed_check_view.inc b/mysql-test/suite/rpl/include/rpl_mixed_check_view.inc index 43feedfe64a..a9f7ad8cd68 100644 --- a/mysql-test/suite/rpl/include/rpl_mixed_check_view.inc +++ b/mysql-test/suite/rpl/include/rpl_mixed_check_view.inc @@ -7,11 +7,11 @@ --echo ==========MASTER========== SHOW CREATE VIEW v1; -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; sync_slave_with_master; --echo ==========SLAVE=========== USE test_rpl; SHOW CREATE VIEW v1; -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; connection master; diff --git a/mysql-test/suite/rpl/include/rpl_mixed_dml.inc b/mysql-test/suite/rpl/include/rpl_mixed_dml.inc index e210ccd907f..d7041d82a2a 100644 --- a/mysql-test/suite/rpl/include/rpl_mixed_dml.inc +++ b/mysql-test/suite/rpl/include/rpl_mixed_dml.inc @@ -54,7 +54,7 @@ DELETE FROM t2 WHERE a = 2; --copy_file ./suite/rpl/data/rpl_mixed.dat $MYSQLTEST_VARDIR/tmp/rpl_mixed.dat LOAD DATA INFILE '../tmp/rpl_mixed.dat' INTO TABLE t1 FIELDS TERMINATED BY '|' ; --remove_file $MYSQLTEST_VARDIR/tmp/rpl_mixed.dat -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; --source suite/rpl/include/rpl_mixed_check_select.inc --source suite/rpl/include/rpl_mixed_clear_tables.inc @@ -75,7 +75,7 @@ DELETE FROM t1 WHERE a = 2; --echo --echo ******************** SELECT ******************** INSERT INTO t1 VALUES(1, 't1, text 1'); -SELECT * FROM t1 WHERE b <> UUID(); +SELECT * FROM t1 WHERE b <> UUID() ORDER BY a; --source suite/rpl/include/rpl_mixed_clear_tables.inc # JOIN @@ -85,8 +85,8 @@ INSERT INTO t1 VALUES(1, 'CCC'); INSERT INTO t1 VALUES(2, 'DDD'); INSERT INTO t2 VALUES(1, 'DDD'); INSERT INTO t2 VALUES(2, 'CCC'); -SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a; -SELECT * FROM t1 INNER JOIN t2 ON t1.b = t2.b; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a ORDER BY t1.a,t2.a; +SELECT * FROM t1 INNER JOIN t2 ON t1.b = t2.b ORDER BY t1.a,t2.a; --source suite/rpl/include/rpl_mixed_clear_tables.inc # UNION diff --git a/mysql-test/suite/rpl/r/rpl_binlog_grant.result b/mysql-test/suite/rpl/r/rpl_binlog_grant.result index 43a21913cf6..72dc58effa1 100644 --- a/mysql-test/suite/rpl/r/rpl_binlog_grant.result +++ b/mysql-test/suite/rpl/r/rpl_binlog_grant.result @@ -1,3 +1,9 @@ +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 d1; create database d1; use d1; diff --git a/mysql-test/suite/rpl/r/rpl_extraCol_innodb.result b/mysql-test/suite/rpl/r/rpl_extraCol_innodb.result index 007cd5a252c..e268e4c2e51 100644 --- a/mysql-test/suite/rpl/r/rpl_extraCol_innodb.result +++ b/mysql-test/suite/rpl/r/rpl_extraCol_innodb.result @@ -615,6 +615,66 @@ 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 +*** Create t14a on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14a (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='InnoDB'; +*** Create t14a on Master *** +CREATE TABLE t14a (c1 INT PRIMARY KEY, 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 t14a () VALUES(1,@b1,'Kyle'), +(2,@b1,'JOE'), +(3,@b1,'QA'); +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +STOP SLAVE; +RESET SLAVE; +*** Master Drop c5 *** +ALTER TABLE t14a DROP COLUMN c5; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14a () VALUES(4,@b1), +(5,@b1), +(6,@b1); +SELECT * FROM t14a ORDER BY c1; +c1 c4 +1 b1b1b1b1b1b1b1b1 +2 b1b1b1b1b1b1b1b1 +3 b1b1b1b1b1b1b1b1 +4 b1b1b1b1b1b1b1b1 +5 b1b1b1b1b1b1b1b1 +6 b1b1b1b1b1b1b1b1 +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +4 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP +5 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP +6 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP *** connect to master and drop columns *** ALTER TABLE t14 DROP COLUMN c2; ALTER TABLE t14 DROP COLUMN c4; @@ -707,7 +767,7 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 1060 Last_SQL_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' -SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; *** Try to insert in master **** INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); @@ -723,6 +783,7 @@ 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 *** diff --git a/mysql-test/suite/rpl/r/rpl_extraCol_myisam.result b/mysql-test/suite/rpl/r/rpl_extraCol_myisam.result index 873fc5cddbc..364354d3a17 100644 --- a/mysql-test/suite/rpl/r/rpl_extraCol_myisam.result +++ b/mysql-test/suite/rpl/r/rpl_extraCol_myisam.result @@ -615,6 +615,66 @@ 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 +*** Create t14a on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14a (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='MyISAM'; +*** Create t14a on Master *** +CREATE TABLE t14a (c1 INT PRIMARY KEY, 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 t14a () VALUES(1,@b1,'Kyle'), +(2,@b1,'JOE'), +(3,@b1,'QA'); +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +STOP SLAVE; +RESET SLAVE; +*** Master Drop c5 *** +ALTER TABLE t14a DROP COLUMN c5; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14a () VALUES(4,@b1), +(5,@b1), +(6,@b1); +SELECT * FROM t14a ORDER BY c1; +c1 c4 +1 b1b1b1b1b1b1b1b1 +2 b1b1b1b1b1b1b1b1 +3 b1b1b1b1b1b1b1b1 +4 b1b1b1b1b1b1b1b1 +5 b1b1b1b1b1b1b1b1 +6 b1b1b1b1b1b1b1b1 +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP +4 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP +5 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP +6 b1b1b1b1b1b1b1b1 NULL 1 CURRENT_TIMESTAMP *** connect to master and drop columns *** ALTER TABLE t14 DROP COLUMN c2; ALTER TABLE t14 DROP COLUMN c4; @@ -707,7 +767,7 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 1060 Last_SQL_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' -SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; *** Try to insert in master **** INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); @@ -723,6 +783,7 @@ 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 *** diff --git a/mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result b/mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result index a7d9e12aabd..af460ded1e7 100644 --- a/mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result +++ b/mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result @@ -440,7 +440,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -563,7 +563,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -581,7 +581,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -638,7 +638,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -656,7 +656,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -1580,7 +1580,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -1703,7 +1703,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -1721,7 +1721,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -1778,7 +1778,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -1796,7 +1796,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -2720,7 +2720,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -2843,7 +2843,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -2861,7 +2861,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -2918,7 +2918,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -2936,7 +2936,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; diff --git a/mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result b/mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result index a8762d447d6..f0613c16825 100644 --- a/mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result +++ b/mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result @@ -440,7 +440,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -563,7 +563,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -581,7 +581,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -638,7 +638,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -656,7 +656,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -1580,7 +1580,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -1703,7 +1703,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -1721,7 +1721,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -1778,7 +1778,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -1796,7 +1796,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -2720,7 +2720,7 @@ f1 f2 f3 f4 select * from t4 order by f1; f1 f2 f3 f4 1 1 1 first -select * from t31 order by f1; +select * from t31 order by f3; f1 f2 f3 f4 1 1 1 first 1 1 2 second @@ -2843,7 +2843,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 Skip_Counter 0 Exec_Master_Log_Pos # @@ -2861,7 +2861,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 5, test.t10 has type 254 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -2918,7 +2918,7 @@ Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table -Last_Errno 1534 +Last_Errno 1535 Last_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 Skip_Counter 0 Exec_Master_Log_Pos # @@ -2936,7 +2936,7 @@ Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No Last_IO_Errno # Last_IO_Error # -Last_SQL_Errno 1534 +Last_SQL_Errno 1535 Last_SQL_Error Table definition on master and slave does not match: Column 2 type mismatch - received type 252, test.t11 has type 15 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; diff --git a/mysql-test/suite/rpl/r/rpl_found_rows.result b/mysql-test/suite/rpl/r/rpl_found_rows.result new file mode 100644 index 00000000000..7e757a1d141 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_found_rows.result @@ -0,0 +1,233 @@ +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; +==== 0. Setting it all up ==== +SET BINLOG_FORMAT=STATEMENT; +**** On Master **** +CREATE TABLE t1 (a INT); +CREATE TABLE logtbl (sect INT, test INT, count INT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +#### 1. Using statement mode #### +==== 1.1. Simple test ==== +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +SELECT FOUND_ROWS() INTO @a; +INSERT INTO logtbl VALUES(1,1,@a); +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +a +1 +SELECT FOUND_ROWS() INTO @a; +INSERT INTO logtbl VALUES(1,2,@a); +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +sect test count +1 1 183 +1 2 3 +**** On Slave **** +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +sect test count +1 1 183 +1 2 3 +==== 1.2. Stored procedure ==== +**** On Master **** +CREATE PROCEDURE calc_and_log(sect INT, test INT) BEGIN +DECLARE cnt INT; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +SELECT FOUND_ROWS() INTO cnt; +INSERT INTO logtbl VALUES(sect,test,cnt); +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +SELECT FOUND_ROWS() INTO cnt; +INSERT INTO logtbl VALUES(sect,test+1,cnt); +END $$ +CALL calc_and_log(2,1); +a +1 +a +7 +CREATE PROCEDURE just_log(sect INT, test INT, found_rows INT) BEGIN +INSERT INTO logtbl VALUES (sect,test,found_rows); +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +SELECT FOUND_ROWS() INTO @found_rows; +CALL just_log(2,3,@found_rows); +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +sect test count +2 1 3 +2 2 183 +2 3 183 +**** On Slave **** +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +sect test count +2 1 3 +2 2 183 +2 3 183 +==== 1.3. Stored functions ==== +**** On Master **** +CREATE FUNCTION log_rows(sect INT, test INT, found_rows INT) +RETURNS INT +BEGIN +INSERT INTO logtbl VALUES(sect,test,found_rows); +RETURN found_rows; +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +SELECT FOUND_ROWS() INTO @found_rows; +SELECT log_rows(3,1,@found_rows), log_rows(3,2,@found_rows); +log_rows(3,1,@found_rows) log_rows(3,2,@found_rows) +183 183 +SELECT * FROM logtbl WHERE sect = 3 ORDER BY sect,test; +sect test count +3 1 183 +3 2 183 +**** On Slave **** +SELECT * FROM logtbl WHERE sect = 3 ORDER BY sect,test; +sect test count +3 1 183 +3 2 183 +==== 1.9. Cleanup ==== +**** On Master **** +DELETE FROM logtbl; +DROP PROCEDURE just_log; +DROP PROCEDURE calc_and_log; +DROP FUNCTION log_rows; +**** Resetting master and slave **** +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +#### 2. Using mixed mode #### +==== 2.1. Checking a procedure ==== +**** On Master **** +SET BINLOG_FORMAT=MIXED; +CREATE PROCEDURE just_log(sect INT, test INT) BEGIN +INSERT INTO logtbl VALUES (sect,test,FOUND_ROWS()); +END $$ +**** On Master 1 **** +SET BINLOG_FORMAT=MIXED; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +CALL just_log(1,1); +**** On Master **** +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +CALL just_log(1,2); +**** On Master 1 **** +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +a +1 +CALL just_log(1,3); +**** On Master **** +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +CALL just_log(1,4); +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +sect test count +1 1 183 +1 2 183 +1 3 3 +1 4 183 +**** On Slave **** +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +sect test count +1 1 183 +1 2 183 +1 3 3 +1 4 183 +==== 2.1. Checking a stored function ==== +**** On Master **** +CREATE FUNCTION log_rows(sect INT, test INT) +RETURNS INT +BEGIN +DECLARE found_rows INT; +SELECT FOUND_ROWS() INTO found_rows; +INSERT INTO logtbl VALUES(sect,test,found_rows); +RETURN found_rows; +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +a +1 +SELECT log_rows(2,1), log_rows(2,2); +log_rows(2,1) log_rows(2,2) +3 3 +CREATE TABLE t2 (a INT, b INT); +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN +INSERT INTO logtbl VALUES (NEW.a, NEW.b, FOUND_ROWS()); +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +a +1 +INSERT INTO t2 VALUES (2,3), (2,4); +DROP TRIGGER t2_tr; +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN +DECLARE dummy INT; +SELECT log_rows(NEW.a, NEW.b) INTO dummy; +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +INSERT INTO t2 VALUES (2,5), (2,6); +DROP TRIGGER t2_tr; +CREATE PROCEDURE log_me_inner(sect INT, test INT) +BEGIN +DECLARE dummy INT; +SELECT log_rows(sect, test) INTO dummy; +SELECT log_rows(sect, test+1) INTO dummy; +END $$ +CREATE PROCEDURE log_me(sect INT, test INT) +BEGIN +CALL log_me_inner(sect,test); +END $$ +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN +CALL log_me(NEW.a, NEW.b); +END $$ +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +a +7 +INSERT INTO t2 VALUES (2,5), (2,6); +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +sect test count +2 1 3 +2 2 3 +2 3 3 +2 4 3 +2 5 183 +2 5 183 +2 6 183 +2 6 0 +2 6 183 +2 7 0 +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +sect test count +2 1 3 +2 2 3 +2 3 3 +2 4 3 +2 5 183 +2 5 183 +2 6 183 +2 6 0 +2 6 183 +2 7 0 +DROP TABLE t1, logtbl; +DROP PROCEDURE just_log; +DROP PROCEDURE log_me; +DROP PROCEDURE log_me_inner; +DROP FUNCTION log_rows; diff --git a/mysql-test/suite/rpl/r/rpl_idempotency.result b/mysql-test/suite/rpl/r/rpl_idempotency.result new file mode 100644 index 00000000000..f17fbd82c44 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_idempotency.result @@ -0,0 +1,71 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT); +INSERT INTO t1 VALUES (-1),(-2),(-3); +INSERT INTO t2 VALUES (-1),(-2),(-3); +DELETE FROM t1 WHERE a = -2; +DELETE FROM t2 WHERE a = -2; +DELETE FROM t1 WHERE a = -2; +DELETE FROM t2 WHERE a = -2; +SELECT * FROM t1 ORDER BY a; +a +-3 +-1 +SELECT * FROM t2 ORDER BY a; +a +-3 +-1 +SELECT * FROM t1 ORDER BY a; +a +-3 +-1 +SELECT * FROM t2 ORDER BY a; +a +-3 +-1 +Last_SQL_Error +0 +INSERT IGNORE INTO t1 VALUES (-2); +INSERT IGNORE INTO t1 VALUES (-2); +SELECT * FROM t1 ORDER BY a; +a +-3 +-2 +-1 +SELECT * FROM t1 ORDER BY a; +a +-3 +-2 +-1 +Last_SQL_Error +0 +UPDATE t1 SET a = 1 WHERE a = -1; +UPDATE t2 SET a = 1 WHERE a = -1; +UPDATE t1 SET a = 1 WHERE a = -1; +UPDATE t2 SET a = 1 WHERE a = -1; +SELECT * FROM t1 ORDER BY a; +a +-3 +-2 +1 +SELECT * FROM t2 ORDER BY a; +a +-3 +1 +SELECT * FROM t1 ORDER BY a; +a +-3 +-2 +1 +SELECT * FROM t2 ORDER BY a; +a +-3 +1 +Last_SQL_Error +0 +DROP TABLE t1, t2; diff --git a/mysql-test/suite/rpl/r/rpl_innodb_bug28430.result b/mysql-test/suite/rpl/r/rpl_innodb_bug28430.result index fb2782ed9f4..c46b4016715 100644 --- a/mysql-test/suite/rpl/r/rpl_innodb_bug28430.result +++ b/mysql-test/suite/rpl/r/rpl_innodb_bug28430.result @@ -114,30 +114,30 @@ Create Table CREATE TABLE `byrange_tbl` ( `filler` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1 /*!50100 PARTITION BY RANGE (id) SUBPARTITION BY HASH (id) SUBPARTITIONS 2 (PARTITION pa1 VALUES LESS THAN (10) ENGINE = InnoDB, PARTITION pa2 VALUES LESS THAN (20) ENGINE = InnoDB, PARTITION pa3 VALUES LESS THAN (30) ENGINE = InnoDB, PARTITION pa4 VALUES LESS THAN (40) ENGINE = InnoDB, PARTITION pa5 VALUES LESS THAN (50) ENGINE = InnoDB, PARTITION pa6 VALUES LESS THAN (60) ENGINE = InnoDB, PARTITION pa7 VALUES LESS THAN (70) ENGINE = InnoDB, PARTITION pa8 VALUES LESS THAN (80) ENGINE = InnoDB, PARTITION pa9 VALUES LESS THAN (90) ENGINE = InnoDB, PARTITION pa10 VALUES LESS THAN (100) ENGINE = InnoDB, PARTITION pa11 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ -show slave status; -Slave_IO_State Waiting for master to send event +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 945470 -Relay_Log_File slave-relay-bin.000003 -Relay_Log_Pos 945616 +Relay_Log_File # +Relay_Log_Pos # Relay_Master_Log_File master-bin.000001 Slave_IO_Running Yes Slave_SQL_Running Yes Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table -Replicate_Ignore_Table +Replicate_Ignore_Table # Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno 0 Last_Error Skip_Counter 0 Exec_Master_Log_Pos 945470 -Relay_Log_Space 945771 +Relay_Log_Space # Until_Condition None Until_Log_File Until_Log_Pos 0 @@ -149,8 +149,8 @@ Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master # Master_SSL_Verify_Server_Cert No -Last_IO_Errno 0 -Last_IO_Error +Last_IO_Errno # +Last_IO_Error # Last_SQL_Errno 0 Last_SQL_Error SELECT count(*) "Slave norm" FROM test.regular_tbl; diff --git a/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result b/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result index 19c5299df25..0e11d132cc4 100644 --- a/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result +++ b/mysql-test/suite/rpl/r/rpl_innodb_mixed_dml.result @@ -41,26 +41,26 @@ DELETE FROM t2 WHERE b <> UUID(); SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 2 t1, text 2 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 2 t1, text 2 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DELETE FROM t1; DELETE FROM t2; @@ -76,13 +76,13 @@ DELETE FROM t2 WHERE a = 2; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 t2, text 1 ==========SLAVE=========== @@ -90,13 +90,13 @@ USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 t2, text 1 DELETE FROM t1; @@ -104,7 +104,7 @@ DELETE FROM t2; ******************** LOAD DATA INFILE ******************** LOAD DATA INFILE '../tmp/rpl_mixed.dat' INTO TABLE t1 FIELDS TERMINATED BY '|' ; -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 10 line A 20 line B @@ -113,7 +113,7 @@ a b SELECT COUNT(*) FROM t1; COUNT(*) 3 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 10 line A 20 line B @@ -121,14 +121,14 @@ a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 3 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 10 line A 20 line B @@ -136,7 +136,7 @@ a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DELETE FROM t1; DELETE FROM t2; @@ -153,35 +153,35 @@ DELETE FROM t1 WHERE a = 2; SELECT COUNT(*) FROM t1; COUNT(*) 2 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 11 3 t1, text 33 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 2 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 11 3 t1, text 33 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DELETE FROM t1; DELETE FROM t2; ******************** SELECT ******************** INSERT INTO t1 VALUES(1, 't1, text 1'); -SELECT * FROM t1 WHERE b <> UUID(); +SELECT * FROM t1 WHERE b <> UUID() ORDER BY a; a b 1 t1, text 1 DELETE FROM t1; @@ -192,11 +192,11 @@ INSERT INTO t1 VALUES(1, 'CCC'); INSERT INTO t1 VALUES(2, 'DDD'); INSERT INTO t2 VALUES(1, 'DDD'); INSERT INTO t2 VALUES(2, 'CCC'); -SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a; +SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a ORDER BY t1.a,t2.a; a b a b 1 CCC 1 DDD 2 DDD 2 CCC -SELECT * FROM t1 INNER JOIN t2 ON t1.b = t2.b; +SELECT * FROM t1 INNER JOIN t2 ON t1.b = t2.b ORDER BY t1.a,t2.a; a b a b 1 CCC 2 CCC 2 DDD 1 DDD @@ -219,50 +219,50 @@ INSERT INTO t1 VALUES(1, 't1, text 1'); SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b TRUNCATE t1; ==========MASTER========== SELECT COUNT(*) FROM t1; COUNT(*) 0 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 0 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DELETE FROM t1; DELETE FROM t2; @@ -275,13 +275,13 @@ UPDATE t1 SET b = 't1, text 1 updated' WHERE a = 1; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 updated SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 t2, text 1 ==========SLAVE=========== @@ -289,13 +289,13 @@ USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 t1, text 1 updated SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 t2, text 1 UPDATE t1, t2 SET t1.b = 'test', t2.b = 'test'; @@ -303,13 +303,13 @@ UPDATE t1, t2 SET t1.b = 'test', t2.b = 'test'; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 test ==========SLAVE=========== @@ -317,13 +317,13 @@ USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 test DELETE FROM t1; @@ -349,26 +349,26 @@ COMMIT; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b START TRANSACTION; INSERT INTO t1 VALUES (2, 'rollback'); @@ -377,26 +377,26 @@ ROLLBACK; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b START TRANSACTION; INSERT INTO t1 VALUES (3, 'before savepoint s1'); @@ -407,27 +407,27 @@ ROLLBACK TO SAVEPOINT s1; SELECT COUNT(*) FROM t1; COUNT(*) 2 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start 3 before savepoint s1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b START TRANSACTION; INSERT INTO t1 VALUES (5, 'before savepoint s2'); @@ -441,7 +441,7 @@ DELETE FROM t1 WHERE a = 7; SELECT COUNT(*) FROM t1; COUNT(*) 4 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start 3 before savepoint s1 @@ -450,14 +450,14 @@ a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 4 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 start 3 before savepoint s1 @@ -466,7 +466,7 @@ a b SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DELETE FROM t1; DELETE FROM t2; @@ -610,28 +610,28 @@ DELETE FROM t1 WHERE a = 202; SELECT COUNT(*) FROM t1; COUNT(*) 2 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 100 test 201 test SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 2 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 100 test 201 test SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ALTER PROCEDURE p1 COMMENT 'p1'; DROP PROCEDURE p1; @@ -649,13 +649,13 @@ INSERT INTO t1 VALUES (1, 'test'); SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 test ==========SLAVE=========== @@ -663,13 +663,13 @@ USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test SELECT COUNT(*) FROM t2; COUNT(*) 1 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b 1 test DELETE FROM t1; @@ -694,51 +694,51 @@ test_rpl e1 @ SYSTEM RECURRING NULL 1 # # NULL SLAVESIDE_DISABLED 1 latin1 latin SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========MASTER========== SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ALTER EVENT e1 RENAME TO e2; ==========MASTER========== @@ -754,26 +754,26 @@ test_rpl e2 @ SYSTEM RECURRING NULL 1 # # NULL SLAVESIDE_DISABLED 1 latin1 latin SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b ==========SLAVE=========== USE test_rpl; SELECT COUNT(*) FROM t1; COUNT(*) 1 -SELECT * FROM t1; +SELECT * FROM t1 ORDER BY a; a b 1 test1 SELECT COUNT(*) FROM t2; COUNT(*) 0 -SELECT * FROM t2; +SELECT * FROM t2 ORDER BY a; a b DROP EVENT e2; ==========MASTER========== @@ -795,7 +795,7 @@ CREATE VIEW v2 AS SELECT * FROM t1 WHERE b <> UUID(); SHOW CREATE VIEW v1; View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where (`t1`.`a` = 1) latin1 latin1_swedish_ci -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; a b 1 test1 ==========SLAVE=========== @@ -803,7 +803,7 @@ USE test_rpl; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where (`t1`.`a` = 1) latin1 latin1_swedish_ci -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; a b 1 test1 ALTER VIEW v1 AS SELECT * FROM t1 WHERE a = 2; @@ -811,7 +811,7 @@ ALTER VIEW v1 AS SELECT * FROM t1 WHERE a = 2; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where (`t1`.`a` = 2) latin1 latin1_swedish_ci -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; a b 2 test2 ==========SLAVE=========== @@ -819,7 +819,7 @@ USE test_rpl; SHOW CREATE VIEW v1; View Create View character_set_client collation_connection v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` where (`t1`.`a` = 2) latin1 latin1_swedish_ci -SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; a b 2 test2 DROP VIEW v1; diff --git a/mysql-test/suite/rpl/r/rpl_invoked_features.result b/mysql-test/suite/rpl/r/rpl_invoked_features.result index 3bcef762497..502bb040218 100644 --- a/mysql-test/suite/rpl/r/rpl_invoked_features.result +++ b/mysql-test/suite/rpl/r/rpl_invoked_features.result @@ -17,13 +17,13 @@ DROP EVENT IF EXISTS e11; CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT, c VARCHAR(64)) ENGINE=myisam; INSERT INTO t1 VALUES (1,1,'1'); INSERT INTO t1 VALUES (2,2,UUID()); -CREATE TABLE t2 (a INT, b INT, c VARCHAR(64)) ENGINE=myisam; +CREATE TABLE t2 (a INT UNIQUE, b INT, c VARCHAR(64)) ENGINE=myisam; INSERT INTO t2 VALUES (1,1,'1'); INSERT INTO t2 VALUES (2,2,UUID()); CREATE TABLE t11 (a INT NOT NULL PRIMARY KEY, b INT, c VARCHAR(64)) ENGINE=innodb; INSERT INTO t11 VALUES (1,1,'1'); INSERT INTO t11 VALUES (2,2,UUID()); -CREATE TABLE t12 (a INT, b INT, c VARCHAR(64)) ENGINE=innodb; +CREATE TABLE t12 (a INT UNIQUE, b INT, c VARCHAR(64)) ENGINE=innodb; INSERT INTO t12 VALUES (1,1,'1'); INSERT INTO t12 VALUES (2,2,UUID()); @@ -49,21 +49,15 @@ BEGIN UPDATE t12 SET c = ''; UPDATE t13 SET c = ''; END| -CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND ENABLE DO +CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND DISABLE DO BEGIN -DECLARE c INT; -SELECT a INTO c FROM t1 WHERE a < 11 ORDER BY a DESC LIMIT 1; -IF c = 7 THEN +ALTER EVENT e1 DISABLE; CALL p1(10, ''); -END IF; END| -CREATE EVENT e11 ON SCHEDULE EVERY 1 SECOND ENABLE DO +CREATE EVENT e11 ON SCHEDULE EVERY 1 SECOND DISABLE DO BEGIN -DECLARE c INT; -SELECT a INTO c FROM t11 WHERE a < 11 ORDER BY a DESC LIMIT 1; -IF c = 7 THEN +ALTER EVENT e11 DISABLE; CALL p11(10, ''); -END IF; END| CREATE FUNCTION f1 (x INT) RETURNS VARCHAR(64) BEGIN @@ -78,11 +72,11 @@ RETURN f1(x); END| CREATE PROCEDURE p1 (IN x INT, IN y VARCHAR(64)) BEGIN -INSERT INTO t1 VALUES (x,x,y); +INSERT IGNORE INTO t1 VALUES (x,x,y); END| CREATE PROCEDURE p11 (IN x INT, IN y VARCHAR(64)) BEGIN -INSERT INTO t11 VALUES (x,x,y); +INSERT IGNORE INTO t11 VALUES (x,x,y); END| CREATE TABLE t3 SELECT * FROM v1; @@ -110,6 +104,8 @@ INSERT INTO t11 VALUES(7,7,f2(7)); INSERT INTO t11 VALUES (103,103,''); SET GLOBAL EVENT_SCHEDULER = on; +ALTER EVENT e1 ENABLE; +ALTER EVENT e11 ENABLE; SET GLOBAL EVENT_SCHEDULER = off; SHOW TABLES LIKE 't%'; @@ -138,8 +134,8 @@ PROCEDURE p1 PROCEDURE p11 SELECT event_name, status FROM information_schema.events WHERE event_schema='test'; event_name status -e1 ENABLED -e11 ENABLED +e1 DISABLED +e11 DISABLED SELECT COUNT(*) FROM t1; COUNT(*) @@ -438,6 +434,8 @@ UPDATE t3 SET c=''; UPDATE t11 SET c=''; UPDATE t12 SET c=''; UPDATE t13 SET c=''; +ALTER TABLE t3 ORDER BY a; +ALTER TABLE t13 ORDER BY a; diff --git a/mysql-test/suite/rpl/r/rpl_packet.result b/mysql-test/suite/rpl/r/rpl_packet.result index 981c234d380..dd56eb0471c 100644 --- a/mysql-test/suite/rpl/r/rpl_packet.result +++ b/mysql-test/suite/rpl/r/rpl_packet.result @@ -27,6 +27,42 @@ STOP SLAVE; START SLAVE; CREATE TABLe `t1` (`f1` LONGTEXT) ENGINE=MyISAM; INSERT INTO `t1`(`f1`) VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2048'); -SHOW STATUS LIKE 'Slave_running'; -Variable_name Value -Slave_running OFF +show slave status; +Slave_IO_State # +Master_Host 127.0.0.1 +Master_User root +Master_Port MASTER_MYPORT +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 No +Slave_SQL_Running # +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +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 # +Master_SSL_Verify_Server_Cert No +Last_IO_Errno 0 +Last_IO_Error +Last_SQL_Errno 0 +Last_SQL_Error diff --git a/mysql-test/suite/rpl/r/rpl_row_basic_11bugs.result b/mysql-test/suite/rpl/r/rpl_row_basic_11bugs.result index 7dc9926522b..c3fd663fad8 100644 --- a/mysql-test/suite/rpl/r/rpl_row_basic_11bugs.result +++ b/mysql-test/suite/rpl/r/rpl_row_basic_11bugs.result @@ -242,3 +242,34 @@ a b 3 1 4 4 drop table t1,t2; +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; +**** On Master **** +SET SESSION BINLOG_FORMAT=ROW; +CREATE TABLE t1 (a INT PRIMARY KEY, b SET('master','slave')); +INSERT INTO t1 VALUES (1,'master,slave'), (2,'master,slave'); +**** On Slave **** +UPDATE t1 SET a = 5, b = 'slave' WHERE a = 1; +SELECT * FROM t1 ORDER BY a; +a b +2 master,slave +5 slave +**** On Master **** +UPDATE t1 SET a = 5, b = 'master' WHERE a = 1; +SELECT * FROM t1 ORDER BY a; +a b +2 master,slave +5 master +**** On Slave **** +Last_SQL_Error + +SELECT * FROM t1 ORDER BY a; +a b +2 master,slave +5 slave +DROP TABLE t1; +**** On Master **** +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result b/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result index a6877b27b95..2efe3a3e486 100644 --- a/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result +++ b/mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result @@ -415,4 +415,23 @@ a b c 2 4 8 3 6 9 99 99 99 +**** Test for BUG#31552 **** +**** On Master **** +DELETE FROM t1; +**** Resetting master and slave **** +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +**** On Master **** +INSERT INTO t1 VALUES ('K','K'), ('L','L'), ('M','M'); +**** On Master **** +DELETE FROM t1 WHERE C1 = 'L'; +DELETE FROM t1; +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 +Last_SQL_Error +0 +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8; diff --git a/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result b/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result index 4c6ec627db5..fc78abfbe2e 100644 --- a/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result +++ b/mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result @@ -415,4 +415,23 @@ a b c 2 4 8 3 6 9 99 99 99 +**** Test for BUG#31552 **** +**** On Master **** +DELETE FROM t1; +**** Resetting master and slave **** +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +**** On Master **** +INSERT INTO t1 VALUES ('K','K'), ('L','L'), ('M','M'); +**** On Master **** +DELETE FROM t1 WHERE C1 = 'L'; +DELETE FROM t1; +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 +Last_SQL_Error +0 +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8; diff --git a/mysql-test/suite/rpl/r/rpl_skip_error.result b/mysql-test/suite/rpl/r/rpl_skip_error.result index 525909387b3..ed4c4a6b3bb 100644 --- a/mysql-test/suite/rpl/r/rpl_skip_error.result +++ b/mysql-test/suite/rpl/r/rpl_skip_error.result @@ -29,8 +29,7 @@ select * from t1; a 1 2 -3 show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master Master_SSL_Verify_Server_Cert Last_IO_Errno Last_IO_Error Last_SQL_Errno Last_SQL_Error -# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 786 # # master-bin.000001 Yes Yes 0 0 786 # None 0 No # No 0 0 +# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 851 # # master-bin.000001 Yes Yes 0 0 851 # None 0 No # No 0 0 drop table t1; diff --git a/mysql-test/suite/rpl/r/rpl_slave_skip.result b/mysql-test/suite/rpl/r/rpl_slave_skip.result index 8e492fe4732..f89fa34e319 100644 --- a/mysql-test/suite/rpl/r/rpl_slave_skip.result +++ b/mysql-test/suite/rpl/r/rpl_slave_skip.result @@ -142,3 +142,211 @@ Last_SQL_Errno 0 Last_SQL_Error **** On Master **** DROP TABLE t1, t2; +SET SESSION BINLOG_FORMAT=ROW; +SET AUTOCOMMIT=0; +CREATE TABLE t1 (a INT, b VARCHAR(20)) ENGINE=myisam; +CREATE TABLE t2 (a INT, b VARCHAR(20)) ENGINE=myisam; +CREATE TABLE t3 (a INT, b VARCHAR(20)) ENGINE=myisam; +INSERT INTO t1 VALUES (1,'master/slave'); +INSERT INTO t2 VALUES (1,'master/slave'); +INSERT INTO t3 VALUES (1,'master/slave'); +CREATE TRIGGER tr1 AFTER UPDATE on t1 FOR EACH ROW +BEGIN +INSERT INTO t2 VALUES (NEW.a,NEW.b); +DELETE FROM t2 WHERE a < NEW.a; +END| +CREATE TRIGGER tr2 AFTER INSERT on t2 FOR EACH ROW +BEGIN +UPDATE t3 SET a =2, b = 'master only'; +END| +**** On Slave **** +STOP SLAVE; +**** On Master **** +UPDATE t1 SET a = 2, b = 'master only' WHERE a = 1; +DROP TRIGGER tr1; +DROP TRIGGER tr2; +INSERT INTO t1 VALUES (3,'master/slave'); +INSERT INTO t2 VALUES (3,'master/slave'); +INSERT INTO t3 VALUES (3,'master/slave'); +SELECT * FROM t1 ORDER BY a; +a b +2 master only +3 master/slave +SELECT * FROM t2 ORDER BY a; +a b +2 master only +3 master/slave +SELECT * FROM t3 ORDER BY a; +a b +2 master only +3 master/slave +*** On Slave *** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT * FROM t1 ORDER BY a; +a b +1 master/slave +3 master/slave +SELECT * FROM t2 ORDER BY a; +a b +1 master/slave +3 master/slave +SELECT * FROM t3 ORDER BY a; +a b +1 master/slave +3 master/slave +DROP TABLE t1, t2, t3; +**** Case 2: Row binlog format and transactional tables **** +*** On Master *** +CREATE TABLE t4 (a INT, b VARCHAR(20)) ENGINE=innodb; +CREATE TABLE t5 (a INT, b VARCHAR(20)) ENGINE=innodb; +CREATE TABLE t6 (a INT, b VARCHAR(20)) ENGINE=innodb; +**** On Slave **** +STOP SLAVE; +*** On Master *** +BEGIN; +INSERT INTO t4 VALUES (2, 'master only'); +INSERT INTO t5 VALUES (2, 'master only'); +INSERT INTO t6 VALUES (2, 'master only'); +COMMIT; +BEGIN; +INSERT INTO t4 VALUES (3, 'master/slave'); +INSERT INTO t5 VALUES (3, 'master/slave'); +INSERT INTO t6 VALUES (3, 'master/slave'); +COMMIT; +SELECT * FROM t4 ORDER BY a; +a b +2 master only +3 master/slave +SELECT * FROM t5 ORDER BY a; +a b +2 master only +3 master/slave +SELECT * FROM t6 ORDER BY a; +a b +2 master only +3 master/slave +*** On Slave *** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT * FROM t4 ORDER BY a; +a b +3 master/slave +SELECT * FROM t5 ORDER BY a; +a b +3 master/slave +SELECT * FROM t6 ORDER BY a; +a b +3 master/slave +**** On Slave **** +STOP SLAVE; +*** On Master *** +BEGIN; +INSERT INTO t4 VALUES (6, 'master only'); +INSERT INTO t5 VALUES (6, 'master only'); +INSERT INTO t6 VALUES (6, 'master only'); +COMMIT; +BEGIN; +INSERT INTO t4 VALUES (7, 'master only'); +INSERT INTO t5 VALUES (7, 'master only'); +INSERT INTO t6 VALUES (7, 'master only'); +COMMIT; +SELECT * FROM t4 ORDER BY a; +a b +2 master only +3 master/slave +6 master only +7 master only +SELECT * FROM t5 ORDER BY a; +a b +2 master only +3 master/slave +6 master only +7 master only +SELECT * FROM t6 ORDER BY a; +a b +2 master only +3 master/slave +6 master only +7 master only +*** On Slave *** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=10; +START SLAVE; +SELECT * FROM t4 ORDER BY a; +a b +3 master/slave +SELECT * FROM t5 ORDER BY a; +a b +3 master/slave +SELECT * FROM t6 ORDER BY a; +a b +3 master/slave +STOP SLAVE; +SET AUTOCOMMIT=0; +INSERT INTO t4 VALUES (4, 'master only'); +INSERT INTO t5 VALUES (4, 'master only'); +INSERT INTO t6 VALUES (4, 'master only'); +COMMIT; +INSERT INTO t4 VALUES (5, 'master/slave'); +INSERT INTO t5 VALUES (5, 'master/slave'); +INSERT INTO t6 VALUES (5, 'master/slave'); +COMMIT; +SELECT * FROM t4 ORDER BY a; +a b +2 master only +3 master/slave +4 master only +5 master/slave +6 master only +7 master only +SELECT * FROM t5 ORDER BY a; +a b +2 master only +3 master/slave +4 master only +5 master/slave +6 master only +7 master only +SELECT * FROM t6 ORDER BY a; +a b +2 master only +3 master/slave +4 master only +5 master/slave +6 master only +7 master only +*** On Slave *** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT * FROM t4 ORDER BY a; +a b +3 master/slave +5 master/slave +SELECT * FROM t5 ORDER BY a; +a b +3 master/slave +5 master/slave +SELECT * FROM t6 ORDER BY a; +a b +3 master/slave +5 master/slave +DROP TABLE t4, t5, t6; +**** Case 3: Statement logging format and LOAD DATA with non-transactional table **** +*** On Master *** +CREATE TABLE t10 (a INT, b VARCHAR(20)) ENGINE=myisam; +*** On Slave *** +STOP SLAVE; +*** On Master *** +SET SESSION BINLOG_FORMAT=STATEMENT; +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/rpl_bug28618.dat' INTO TABLE t10 FIELDS TERMINATED BY '|'; +SELECT * FROM t10 ORDER BY a; +a b +1 master only +2 master only +3 master only +*** On Slave *** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT * FROM t10 ORDER BY a; +a b +DROP TABLE t10; diff --git a/mysql-test/suite/rpl/r/rpl_sp_effects.result b/mysql-test/suite/rpl/r/rpl_sp_effects.result index c2c44b06972..a39d189aa3a 100644 --- a/mysql-test/suite/rpl/r/rpl_sp_effects.result +++ b/mysql-test/suite/rpl/r/rpl_sp_effects.result @@ -235,4 +235,45 @@ drop table t1; drop function f1; drop function f2; drop procedure p1; +create table t2 (b BIT(7)); +create procedure sp_bug26199(bitvalue BIT(7)) +begin +insert into t2 set b = bitvalue; +end // +create function sf_bug26199(b BIT(7)) returns int +begin +insert into t2 values(b); +return 0; +end// +call sp_bug26199(b'1110'); +call sp_bug26199('\0'); +select sf_bug26199(b'1111111'); +sf_bug26199(b'1111111') +0 +select sf_bug26199(b'101111111'); +sf_bug26199(b'101111111') +0 +Warnings: +Warning 1264 Out of range value for column 'b' at row 1 +select sf_bug26199('\''); +sf_bug26199('\'') +0 +select hex(b) from t2; +hex(b) +E +0 +7F +7F +27 +select hex(b) from t2; +hex(b) +E +0 +7F +7F +27 +drop table t2; +drop procedure sp_bug26199; +drop function sf_bug26199; SET GLOBAL log_bin_trust_function_creators = 0; +end of the tests diff --git a/mysql-test/suite/rpl/r/rpl_switch_stm_row_mixed.result b/mysql-test/suite/rpl/r/rpl_switch_stm_row_mixed.result index c3f0c07b92c..8ed9ff5dc2f 100644 --- a/mysql-test/suite/rpl/r/rpl_switch_stm_row_mixed.result +++ b/mysql-test/suite/rpl/r/rpl_switch_stm_row_mixed.result @@ -405,6 +405,26 @@ CREATE TABLE t12 (data LONG); LOCK TABLES t12 WRITE; INSERT INTO t12 VALUES(UUID()); UNLOCK TABLES; +CREATE FUNCTION my_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT USER() INTO user; +RETURN user; +END $$ +CREATE FUNCTION my_current_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT CURRENT_USER() INTO user; +RETURN user; +END $$ +DROP TABLE IF EXISTS t13; +CREATE TABLE t13 (data CHAR(64)); +INSERT INTO t13 VALUES (USER()); +INSERT INTO t13 VALUES (my_user()); +INSERT INTO t13 VALUES (CURRENT_USER()); +INSERT INTO t13 VALUES (my_current_user()); show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query # # drop database if exists mysqltest1 @@ -709,6 +729,30 @@ master-bin.000001 # Query # # use `mysqltest1`; DROP TABLE IF EXISTS t12 master-bin.000001 # Query # # use `mysqltest1`; CREATE TABLE t12 (data LONG) master-bin.000001 # Table_map # # table_id: # (mysqltest1.t12) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` FUNCTION my_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT USER() INTO user; +RETURN user; +END +master-bin.000001 # Query # # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` FUNCTION my_current_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT CURRENT_USER() INTO user; +RETURN user; +END +master-bin.000001 # Query # # use `mysqltest1`; DROP TABLE IF EXISTS t13 +master-bin.000001 # Query # # use `mysqltest1`; CREATE TABLE t13 (data CHAR(64)) +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query # # drop database if exists mysqltest1 @@ -1013,5 +1057,29 @@ master-bin.000001 # Query # # use `mysqltest1`; DROP TABLE IF EXISTS t12 master-bin.000001 # Query # # use `mysqltest1`; CREATE TABLE t12 (data LONG) master-bin.000001 # Table_map # # table_id: # (mysqltest1.t12) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` FUNCTION my_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT USER() INTO user; +RETURN user; +END +master-bin.000001 # Query # # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` FUNCTION my_current_user() +RETURNS CHAR(64) +BEGIN +DECLARE user CHAR(64); +SELECT CURRENT_USER() INTO user; +RETURN user; +END +master-bin.000001 # Query # # use `mysqltest1`; DROP TABLE IF EXISTS t13 +master-bin.000001 # Query # # use `mysqltest1`; CREATE TABLE t13 (data CHAR(64)) +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Table_map # # table_id: # (mysqltest1.t13) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F drop database mysqltest1; set global binlog_format =@my_binlog_format; diff --git a/mysql-test/suite/rpl/r/rpl_temporary_errors.result b/mysql-test/suite/rpl/r/rpl_temporary_errors.result new file mode 100644 index 00000000000..5c681683b08 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_temporary_errors.result @@ -0,0 +1,81 @@ +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; +**** On Master **** +SET SESSION BINLOG_FORMAT=ROW; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4); +**** On Slave **** +SHOW STATUS LIKE 'Slave_retried_transactions'; +Variable_name Value +Slave_retried_transactions 0 +UPDATE t1 SET a = 5, b = 47 WHERE a = 1; +SELECT * FROM t1; +a b +5 47 +2 2 +3 3 +4 4 +**** On Master **** +UPDATE t1 SET a = 5, b = 5 WHERE a = 1; +SELECT * FROM t1; +a b +5 5 +2 2 +3 3 +4 4 +**** On Slave **** +SHOW STATUS LIKE 'Slave_retried_transactions'; +Variable_name Value +Slave_retried_transactions 0 +SELECT * FROM t1; +a b +5 47 +2 2 +3 3 +4 4 +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 408 +Relay_Log_File # +Relay_Log_Pos # +Relay_Master_Log_File master-bin.000001 +Slave_IO_Running Yes +Slave_SQL_Running Yes +Replicate_Do_DB +Replicate_Ignore_DB +Replicate_Do_Table +Replicate_Ignore_Table # +Replicate_Wild_Do_Table +Replicate_Wild_Ignore_Table +Last_Errno 0 +Last_Error +Skip_Counter 0 +Exec_Master_Log_Pos 408 +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 # +Master_SSL_Verify_Server_Cert No +Last_IO_Errno # +Last_IO_Error # +Last_SQL_Errno 0 +Last_SQL_Error +DROP TABLE t1; +**** On Master **** +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_truncate_2myisam.result b/mysql-test/suite/rpl/r/rpl_truncate_2myisam.result index c7ef28ba56b..7eee31dab7a 100644 --- a/mysql-test/suite/rpl/r/rpl_truncate_2myisam.result +++ b/mysql-test/suite/rpl/r/rpl_truncate_2myisam.result @@ -4,6 +4,11 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=STATEMENT; SET GLOBAL BINLOG_FORMAT=STATEMENT; @@ -31,10 +36,17 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=MIXED; SET GLOBAL BINLOG_FORMAT=MIXED; @@ -62,10 +74,17 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=ROW; SET GLOBAL BINLOG_FORMAT=ROW; @@ -93,11 +112,18 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=STATEMENT; SET GLOBAL BINLOG_FORMAT=STATEMENT; @@ -125,10 +151,17 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Query # # use `test`; DELETE FROM t1 master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=MIXED; SET GLOBAL BINLOG_FORMAT=MIXED; @@ -156,10 +189,17 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Query # # use `test`; DELETE FROM t1 master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=ROW; SET GLOBAL BINLOG_FORMAT=ROW; @@ -188,9 +228,11 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=MyISAM master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Delete_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; diff --git a/mysql-test/suite/rpl/r/rpl_truncate_3innodb.result b/mysql-test/suite/rpl/r/rpl_truncate_3innodb.result index 7ce48c2e983..a6580a5685b 100644 --- a/mysql-test/suite/rpl/r/rpl_truncate_3innodb.result +++ b/mysql-test/suite/rpl/r/rpl_truncate_3innodb.result @@ -4,6 +4,11 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=STATEMENT; SET GLOBAL BINLOG_FORMAT=STATEMENT; @@ -31,12 +36,19 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=MIXED; SET GLOBAL BINLOG_FORMAT=MIXED; @@ -64,12 +76,19 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=ROW; SET GLOBAL BINLOG_FORMAT=ROW; @@ -97,6 +116,7 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F @@ -104,6 +124,12 @@ master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; TRUNCATE TABLE t1 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=STATEMENT; SET GLOBAL BINLOG_FORMAT=STATEMENT; @@ -131,12 +157,19 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DELETE FROM t1 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=MIXED; SET GLOBAL BINLOG_FORMAT=MIXED; @@ -164,12 +197,19 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1,1), (2,2) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DELETE FROM t1 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; +STOP SLAVE; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t1; +RESET SLAVE; +START SLAVE; **** On Master **** SET SESSION BINLOG_FORMAT=ROW; SET GLOBAL BINLOG_FORMAT=ROW; @@ -198,6 +238,7 @@ a b DROP TABLE t1; show binlog events from <binlog_start>; Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; DROP TABLE IF EXISTS t1 master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b LONG) ENGINE=InnoDB master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F @@ -206,3 +247,4 @@ master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Delete_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; DROP TABLE t1 +RESET MASTER; diff --git a/mysql-test/suite/rpl/t/disabled.def b/mysql-test/suite/rpl/t/disabled.def index 426bf93ae97..20c3ccf0486 100644 --- a/mysql-test/suite/rpl/t/disabled.def +++ b/mysql-test/suite/rpl/t/disabled.def @@ -11,7 +11,8 @@ ############################################################################## rpl_ddl : BUG#26418 2007-03-01 mleich Slave out of sync after CREATE/DROP TEMPORARY TABLE + ROLLBACK on master -rpl_invoked_features : BUG#29020 2007-06-21 Lars Non-deterministic test case -rpl_auto_increment_11932 : Bug#29809 2007-07-16 ingo Slave SQL errors in warnings file -rpl_extraColmaster_innodb : BUG#30854 : Tables name show as binary in slave err msg on vm-win2003-64-b and Solaris -rpl_extraColmaster_myisam : BUG#30854 +rpl_stm_extraColmaster_ndb : WL#3915 : Statement-based replication not supported in ndb. Enable test when supported. +rpl_innodb_bug28430 : Bug #32247 2007-11-27 mats Test reports wrong value of "AUTO_INCREMENT" (on a partitioned InnoDB table) +rpl_view : Bug#32654: rpl_view.test fails randomly +rpl_ndb_multi : Bug#30751: rpl_ndb_multi missing row in output +rpl_log_pos : Bug#8693 Test 'rpl_log_pos' fails sometimes diff --git a/mysql-test/suite/rpl/t/rpl_binlog_grant.test b/mysql-test/suite/rpl/t/rpl_binlog_grant.test index 42af33c2e05..e95f69a3f99 100644 --- a/mysql-test/suite/rpl/t/rpl_binlog_grant.test +++ b/mysql-test/suite/rpl/t/rpl_binlog_grant.test @@ -1,3 +1,4 @@ +source include/master-slave.inc; -- source include/have_innodb.inc -- source include/not_embedded.inc -- source include/have_binlog_format_mixed_or_statement.inc diff --git a/mysql-test/suite/rpl/t/rpl_bug31076.test b/mysql-test/suite/rpl/t/rpl_bug31076.test index cc8b26ac8f4..1c7b0ca0fd1 100644 --- a/mysql-test/suite/rpl/t/rpl_bug31076.test +++ b/mysql-test/suite/rpl/t/rpl_bug31076.test @@ -114,4 +114,5 @@ SELECT * FROM visits_events; # Cleanup DROP DATABASE track; +sync_slave_with_master; --echo End of 5.1 tests diff --git a/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test b/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test index 27c920a3186..83c15d691be 100644 --- a/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test +++ b/mysql-test/suite/rpl/t/rpl_dual_pos_advance.test @@ -106,9 +106,3 @@ connection slave; sync_with_master; # End of 4.1 tests - -# Cleanup -# The A->B->A replication causes the master to start writing relay logs -# in var/run, remove them -remove_file $MYSQLTEST_VARDIR/run/master-relay-bin.000001; -remove_file $MYSQLTEST_VARDIR/run/master-relay-bin.index; diff --git a/mysql-test/suite/rpl/t/rpl_found_rows.test b/mysql-test/suite/rpl/t/rpl_found_rows.test new file mode 100644 index 00000000000..f868061c951 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_found_rows.test @@ -0,0 +1,256 @@ +source include/master-slave.inc; + +# It is not possible to replicate FOUND_ROWS() using statement-based +# replication, but there is a workaround that stores the result of +# FOUND_ROWS() into a user variable and then replicates this instead. + +# The purpose of this test case is to test that the workaround +# function properly even when inside stored programs (i.e., stored +# routines and triggers). + +--echo ==== 0. Setting it all up ==== + +SET BINLOG_FORMAT=STATEMENT; + +--echo **** On Master **** +connection master; +CREATE TABLE t1 (a INT); +CREATE TABLE logtbl (sect INT, test INT, count INT); + +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; +INSERT INTO t1 SELECT 2*a+3 FROM t1; + +--echo #### 1. Using statement mode #### + +--echo ==== 1.1. Simple test ==== + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; + +# Instead of +# INSERT INTO logtbl VALUES(1, 1, FOUND_ROWS()); +# we write +SELECT FOUND_ROWS() INTO @a; +INSERT INTO logtbl VALUES(1,1,@a); + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +# Instead of +# INSERT INTO logtbl VALUES(1, 2, FOUND_ROWS()); +# we write +SELECT FOUND_ROWS() INTO @a; +INSERT INTO logtbl VALUES(1,2,@a); + +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +--echo **** On Slave **** +sync_slave_with_master; +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; + +--echo ==== 1.2. Stored procedure ==== + +# Here we do both the calculation and the logging. We also do it twice +# to make sure that there are no limitations on how many times it can +# be used. + +--echo **** On Master **** +connection master; +--delimiter $$ +CREATE PROCEDURE calc_and_log(sect INT, test INT) BEGIN + DECLARE cnt INT; + SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; + SELECT FOUND_ROWS() INTO cnt; + INSERT INTO logtbl VALUES(sect,test,cnt); + SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; + SELECT FOUND_ROWS() INTO cnt; + INSERT INTO logtbl VALUES(sect,test+1,cnt); +END $$ +--delimiter ; + +CALL calc_and_log(2,1); + +--delimiter $$ +CREATE PROCEDURE just_log(sect INT, test INT, found_rows INT) BEGIN + INSERT INTO logtbl VALUES (sect,test,found_rows); +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +SELECT FOUND_ROWS() INTO @found_rows; +CALL just_log(2,3,@found_rows); + +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +--echo **** On Slave **** +sync_slave_with_master; +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; + +--echo ==== 1.3. Stored functions ==== +--echo **** On Master **** +connection master; +--delimiter $$ +CREATE FUNCTION log_rows(sect INT, test INT, found_rows INT) + RETURNS INT +BEGIN + INSERT INTO logtbl VALUES(sect,test,found_rows); + RETURN found_rows; +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +SELECT FOUND_ROWS() INTO @found_rows; +SELECT log_rows(3,1,@found_rows), log_rows(3,2,@found_rows); + +SELECT * FROM logtbl WHERE sect = 3 ORDER BY sect,test; +--echo **** On Slave **** +sync_slave_with_master; +SELECT * FROM logtbl WHERE sect = 3 ORDER BY sect,test; + +--echo ==== 1.9. Cleanup ==== +--echo **** On Master **** +connection master; +DELETE FROM logtbl; +DROP PROCEDURE just_log; +DROP PROCEDURE calc_and_log; +DROP FUNCTION log_rows; +sync_slave_with_master; + +source include/reset_master_and_slave.inc; + +--echo #### 2. Using mixed mode #### + +--echo ==== 2.1. Checking a procedure ==== + +--echo **** On Master **** +connection master; +SET BINLOG_FORMAT=MIXED; + +# We will now check some stuff that will not work in statement-based +# replication, but which should cause the binary log to switch to +# row-based logging. + +--delimiter $$ +CREATE PROCEDURE just_log(sect INT, test INT) BEGIN + INSERT INTO logtbl VALUES (sect,test,FOUND_ROWS()); +END $$ +--delimiter ; +sync_slave_with_master; + +--echo **** On Master 1 **** +connection master1; +SET BINLOG_FORMAT=MIXED; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +CALL just_log(1,1); + +--echo **** On Master **** +connection master; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +CALL just_log(1,2); + +--echo **** On Master 1 **** + +connection master1; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +CALL just_log(1,3); +sync_slave_with_master; + +--echo **** On Master **** +connection master; +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +CALL just_log(1,4); +sync_slave_with_master; + +connection master; +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; +--echo **** On Slave **** +sync_slave_with_master; +SELECT * FROM logtbl WHERE sect = 1 ORDER BY sect,test; + +--echo ==== 2.1. Checking a stored function ==== +--echo **** On Master **** +connection master; +--delimiter $$ +CREATE FUNCTION log_rows(sect INT, test INT) + RETURNS INT +BEGIN + DECLARE found_rows INT; + SELECT FOUND_ROWS() INTO found_rows; + INSERT INTO logtbl VALUES(sect,test,found_rows); + RETURN found_rows; +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +SELECT log_rows(2,1), log_rows(2,2); + +CREATE TABLE t2 (a INT, b INT); + +# Trying with referencing FOUND_ROWS() directly in the trigger. + +--delimiter $$ +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN + INSERT INTO logtbl VALUES (NEW.a, NEW.b, FOUND_ROWS()); +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a < 5 ORDER BY a LIMIT 1; +INSERT INTO t2 VALUES (2,3), (2,4); + +# Referencing FOUND_ROWS() indirectly. + +DROP TRIGGER t2_tr; + +--delimiter $$ +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN + DECLARE dummy INT; + SELECT log_rows(NEW.a, NEW.b) INTO dummy; +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +INSERT INTO t2 VALUES (2,5), (2,6); + +# Putting FOUND_ROWS() even lower in the call chain. + +connection master; +DROP TRIGGER t2_tr; + +--delimiter $$ +CREATE PROCEDURE log_me_inner(sect INT, test INT) +BEGIN + DECLARE dummy INT; + SELECT log_rows(sect, test) INTO dummy; + SELECT log_rows(sect, test+1) INTO dummy; +END $$ + +CREATE PROCEDURE log_me(sect INT, test INT) +BEGIN + CALL log_me_inner(sect,test); +END $$ +--delimiter ; + +--delimiter $$ +CREATE TRIGGER t2_tr BEFORE INSERT ON t2 FOR EACH ROW +BEGIN + CALL log_me(NEW.a, NEW.b); +END $$ +--delimiter ; + +SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a > 5 ORDER BY a LIMIT 1; +INSERT INTO t2 VALUES (2,5), (2,6); + +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; +sync_slave_with_master; +SELECT * FROM logtbl WHERE sect = 2 ORDER BY sect,test; + +connection master; +DROP TABLE t1, logtbl; +DROP PROCEDURE just_log; +DROP PROCEDURE log_me; +DROP PROCEDURE log_me_inner; +DROP FUNCTION log_rows; +sync_slave_with_master; + diff --git a/mysql-test/suite/rpl/t/rpl_idempotency.test b/mysql-test/suite/rpl/t/rpl_idempotency.test new file mode 100644 index 00000000000..44956b7b459 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_idempotency.test @@ -0,0 +1,79 @@ +# Testing various forms of idempotency for replication that should +# work the same way under statement based as under row based. + +source include/master-slave.inc; + +connection master; +CREATE TABLE t1 (a INT PRIMARY KEY); +CREATE TABLE t2 (a INT); +INSERT INTO t1 VALUES (-1),(-2),(-3); +INSERT INTO t2 VALUES (-1),(-2),(-3); +sync_slave_with_master; + +# A delete for a row that does not exist, the statement is +# deliberately written to be idempotent for statement-based +# replication as well. We test this towards both a table with a +# primary key and without a primary key. + +connection slave; +DELETE FROM t1 WHERE a = -2; +DELETE FROM t2 WHERE a = -2; +connection master; +DELETE FROM t1 WHERE a = -2; +DELETE FROM t2 WHERE a = -2; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +sync_slave_with_master; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1); +disable_query_log; +eval SELECT "$last_error" AS Last_SQL_Error; +enable_query_log; + +# An insert of a row that already exists. Since we are replacing the +# row if it already exists, the most apropriate representation is +# INSERT IGNORE. We only test this towards a table with a primary key, +# since the other case does not make sense. + +INSERT IGNORE INTO t1 VALUES (-2); +connection master; +INSERT IGNORE INTO t1 VALUES (-2); +SELECT * FROM t1 ORDER BY a; +sync_slave_with_master; +SELECT * FROM t1 ORDER BY a; +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1); +disable_query_log; +eval SELECT "$last_error" AS Last_SQL_Error; +enable_query_log; + +# BUG#19958: RBR idempotency issue for UPDATE and DELETE + +# Statement-based and row-based replication have different behaviour +# when updating a row with an explicit WHERE-clause that matches +# exactly one row (or no row at all). For statement-based replication, +# the statement is idempotent since the first time it is executed, it +# will update exactly one row, and the second time it will not update +# any row at all. This was not the case for row-based replication, so +# we test under both row-based and statement-based replication both +# for tables with and without primary keys. + +connection slave; +UPDATE t1 SET a = 1 WHERE a = -1; +UPDATE t2 SET a = 1 WHERE a = -1; +connection master; +UPDATE t1 SET a = 1 WHERE a = -1; +UPDATE t2 SET a = 1 WHERE a = -1; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +sync_slave_with_master; +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1); +disable_query_log; +eval SELECT "$last_error" AS Last_SQL_Error; +enable_query_log; + +connection master; +DROP TABLE t1, t2; +sync_slave_with_master; diff --git a/mysql-test/suite/rpl/t/rpl_innodb_bug28430.test b/mysql-test/suite/rpl/t/rpl_innodb_bug28430.test index 74954299920..eb828f07415 100644 --- a/mysql-test/suite/rpl/t/rpl_innodb_bug28430.test +++ b/mysql-test/suite/rpl/t/rpl_innodb_bug28430.test @@ -136,8 +136,7 @@ SELECT count(*) as "Master byrange" FROM test.byrange_tbl; --sync_slave_with_master connection slave; show create table test.byrange_tbl; ---replace_column 4 MASTER_PORT 33 # -show slave status; +source include/show_slave_status.inc; SELECT count(*) "Slave norm" FROM test.regular_tbl; SELECT count(*) "Slave bykey" FROM test.bykey_tbl; SELECT count(*) "Slave byrange" FROM test.byrange_tbl; diff --git a/mysql-test/suite/rpl/t/rpl_invoked_features.test b/mysql-test/suite/rpl/t/rpl_invoked_features.test index e797e0552ef..2e69c0fabd9 100644 --- a/mysql-test/suite/rpl/t/rpl_invoked_features.test +++ b/mysql-test/suite/rpl/t/rpl_invoked_features.test @@ -8,10 +8,9 @@ --source include/master-slave.inc --source include/have_innodb.inc - -# -# Define variables used by test case -# +# --disable_warnings/--enable_warnings added before/after query +# if one uses UUID() function because we need to avoid warnings +# for STATEMENT binlog format # Non-transactional engine --let $engine_type= myisam @@ -45,20 +44,24 @@ DROP EVENT IF EXISTS e11; --echo eval CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT, c VARCHAR(64)) ENGINE=$engine_type; ---disable_warnings INSERT INTO t1 VALUES (1,1,'1'); +--disable_warnings INSERT INTO t1 VALUES (2,2,UUID()); -eval CREATE TABLE t2 (a INT, b INT, c VARCHAR(64)) ENGINE=$engine_type; +--enable_warnings +eval CREATE TABLE t2 (a INT UNIQUE, b INT, c VARCHAR(64)) ENGINE=$engine_type; INSERT INTO t2 VALUES (1,1,'1'); +--disable_warnings INSERT INTO t2 VALUES (2,2,UUID()); --enable_warnings eval CREATE TABLE t11 (a INT NOT NULL PRIMARY KEY, b INT, c VARCHAR(64)) ENGINE=$engine_type2; ---disable_warnings INSERT INTO t11 VALUES (1,1,'1'); +--disable_warnings INSERT INTO t11 VALUES (2,2,UUID()); -eval CREATE TABLE t12 (a INT, b INT, c VARCHAR(64)) ENGINE=$engine_type2; +--enable_warnings +eval CREATE TABLE t12 (a INT UNIQUE, b INT, c VARCHAR(64)) ENGINE=$engine_type2; INSERT INTO t12 VALUES (1,1,'1'); +--disable_warnings INSERT INTO t12 VALUES (2,2,UUID()); --enable_warnings @@ -96,22 +99,16 @@ BEGIN END| # Create events which will run every 1 sec -CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND ENABLE DO +CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND DISABLE DO BEGIN - DECLARE c INT; - SELECT a INTO c FROM t1 WHERE a < 11 ORDER BY a DESC LIMIT 1; - IF c = 7 THEN - CALL p1(10, ''); - END IF; + ALTER EVENT e1 DISABLE; + CALL p1(10, ''); END| -CREATE EVENT e11 ON SCHEDULE EVERY 1 SECOND ENABLE DO +CREATE EVENT e11 ON SCHEDULE EVERY 1 SECOND DISABLE DO BEGIN - DECLARE c INT; - SELECT a INTO c FROM t11 WHERE a < 11 ORDER BY a DESC LIMIT 1; - IF c = 7 THEN - CALL p11(10, ''); - END IF; + ALTER EVENT e11 DISABLE; + CALL p11(10, ''); END| # Create functions and procedures used for events @@ -130,12 +127,12 @@ END| CREATE PROCEDURE p1 (IN x INT, IN y VARCHAR(64)) BEGIN - INSERT INTO t1 VALUES (x,x,y); + INSERT IGNORE INTO t1 VALUES (x,x,y); END| CREATE PROCEDURE p11 (IN x INT, IN y VARCHAR(64)) BEGIN - INSERT INTO t11 VALUES (x,x,y); + INSERT IGNORE INTO t11 VALUES (x,x,y); END| DELIMITER ;| @@ -147,17 +144,24 @@ DELIMITER ;| # Do some actions for non-transactional tables --echo ---disable_warnings CREATE TABLE t3 SELECT * FROM v1; INSERT INTO t1 VALUES (3,3,''); UPDATE t1 SET c='2' WHERE a = 1; +--disable_warnings INSERT INTO t1 VALUES(4,4,f1(4)); +--enable_warnings INSERT INTO t1 VALUES (100,100,''); +--disable_warnings CALL p1(5, UUID()); +--enable_warnings INSERT INTO t1 VALUES (101,101,''); +--disable_warnings INSERT INTO t1 VALUES(6,6,f1(6)); +--enable_warnings INSERT INTO t1 VALUES (102,102,''); +--disable_warnings INSERT INTO t1 VALUES(7,7,f2(7)); +--enable_warnings INSERT INTO t1 VALUES (103,103,''); # Do some actions for transactional tables @@ -165,21 +169,34 @@ INSERT INTO t1 VALUES (103,103,''); CREATE TABLE t13 SELECT * FROM v11; INSERT INTO t11 VALUES (3,3,''); UPDATE t11 SET c='2' WHERE a = 1; +--disable_warnings INSERT INTO t11 VALUES(4,4,f1(4)); +--enable_warnings INSERT INTO t11 VALUES (100,100,''); +--disable_warnings CALL p11(5, UUID()); +--enable_warnings INSERT INTO t11 VALUES (101,101,''); +--disable_warnings INSERT INTO t11 VALUES(6,6,f1(6)); +--enable_warnings INSERT INTO t11 VALUES (102,102,''); +--disable_warnings INSERT INTO t11 VALUES(7,7,f2(7)); -INSERT INTO t11 VALUES (103,103,''); --enable_warnings +INSERT INTO t11 VALUES (103,103,''); # Scheduler is on --echo +# Temporally events fire sequentally due Bug#29020. SET GLOBAL EVENT_SCHEDULER = on; -# Wait 2 sec while events will executed ---sleep 2 +# Wait while events will executed +ALTER EVENT e1 ENABLE; +let $wait_condition= SELECT COUNT(*) = 1 FROM t1 WHERE t1.a = 10; +--source include/wait_condition.inc +ALTER EVENT e11 ENABLE; +let $wait_condition= SELECT COUNT(*) = 1 FROM t11 WHERE t11.a = 10; +--source include/wait_condition.inc SET GLOBAL EVENT_SCHEDULER = off; # Check original objects @@ -234,7 +251,7 @@ SELECT COUNT(*) FROM t13; SELECT a,b FROM t13 ORDER BY a; SELECT a,b FROM v11 ORDER BY a; -# Remove UUID() before comparing +# Remove UUID() before comparing and sort tables --connection master --echo @@ -245,6 +262,9 @@ UPDATE t11 SET c=''; UPDATE t12 SET c=''; UPDATE t13 SET c=''; +ALTER TABLE t3 ORDER BY a; +ALTER TABLE t13 ORDER BY a; + --sync_slave_with_master slave # Compare a data from master and slave @@ -260,13 +280,12 @@ UPDATE t13 SET c=''; # Remove dumps --echo ---exec rm $MYSQLTEST_VARDIR/tmp/rpl_invoked_features_master.sql ---exec rm $MYSQLTEST_VARDIR/tmp/rpl_invoked_features_slave.sql +--remove_file $MYSQLTEST_VARDIR/tmp/rpl_invoked_features_master.sql +--remove_file $MYSQLTEST_VARDIR/tmp/rpl_invoked_features_slave.sql # Remove tables,views,procedures,functions --connection master --echo ---disable_warnings DROP VIEW IF EXISTS v1,v11; DROP TABLE IF EXISTS t1,t2,t3,t11,t12,t13; DROP PROCEDURE IF EXISTS p1; @@ -275,7 +294,6 @@ DROP FUNCTION IF EXISTS f1; DROP FUNCTION IF EXISTS f2; DROP EVENT IF EXISTS e1; DROP EVENT IF EXISTS e11; ---enable_warnings --sync_slave_with_master slave diff --git a/mysql-test/suite/rpl/t/rpl_packet.test b/mysql-test/suite/rpl/t/rpl_packet.test index 316278cb75d..0e17ae3144c 100644 --- a/mysql-test/suite/rpl/t/rpl_packet.test +++ b/mysql-test/suite/rpl/t/rpl_packet.test @@ -66,16 +66,11 @@ CREATE TABLe `t1` (`f1` LONGTEXT) ENGINE=MyISAM; INSERT INTO `t1`(`f1`) VALUES ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2048'); # The slave I/O thread must stop after trying to read the above event -connection slave; -sleep 2; ---source include/wait_for_slave_io_to_stop.inc -SHOW STATUS LIKE 'Slave_running'; - -# cleanup -#connection master; -#drop table t1; -#connection slave; -#drop table t1; - +connection slave; +--source include/wait_for_slave_io_to_stop.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT +# import is only the 11th column Slave_IO_Running +--replace_column 1 # 7 # 8 # 9 # 12 # 22 # 23 # 33 # +query_vertical show slave status; # End of tests diff --git a/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test b/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test index fb43664f121..07fe763eb3c 100644 --- a/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test +++ b/mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test @@ -223,3 +223,38 @@ connection master; drop table t1,t2; sync_slave_with_master; + +# +# BUG#31702: Missing row on slave causes assertion failure under +# row-based replication +# + +disable_query_log; +source include/master-slave-reset.inc; +enable_query_log; + +--echo **** On Master **** +connection master; +SET SESSION BINLOG_FORMAT=ROW; +CREATE TABLE t1 (a INT PRIMARY KEY, b SET('master','slave')); +INSERT INTO t1 VALUES (1,'master,slave'), (2,'master,slave'); +--echo **** On Slave **** +sync_slave_with_master; +UPDATE t1 SET a = 5, b = 'slave' WHERE a = 1; +SELECT * FROM t1 ORDER BY a; +--echo **** On Master **** +connection master; +UPDATE t1 SET a = 5, b = 'master' WHERE a = 1; +SELECT * FROM t1 ORDER BY a; +--echo **** On Slave **** +sync_slave_with_master; +let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); +disable_query_log; +eval SELECT "$last_error" AS Last_SQL_Error; +enable_query_log; +SELECT * FROM t1 ORDER BY a; +DROP TABLE t1; + +--echo **** On Master **** +connection master; +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/t/rpl_row_charset_innodb.test b/mysql-test/suite/rpl/t/rpl_row_charset_innodb.test index 1465500d0eb..2d48af65581 100644 --- a/mysql-test/suite/rpl/t/rpl_row_charset_innodb.test +++ b/mysql-test/suite/rpl/t/rpl_row_charset_innodb.test @@ -4,6 +4,7 @@ ######################################################## -- source include/not_ndb_default.inc -- 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_row_charset.test diff --git a/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test b/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test index dd46d64f684..be76ac9f3f6 100644 --- a/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test +++ b/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test @@ -344,5 +344,6 @@ FLUSH LOGS; --exec rm $MYSQLTEST_VARDIR/tmp/local.sql DROP TABLE IF EXISTS t1, t2, t3, t04, t05, t4, t5; +sync_slave_with_master; # End of 4.1 tests diff --git a/mysql-test/suite/rpl/t/rpl_row_until.test b/mysql-test/suite/rpl/t/rpl_row_until.test index 9464e5cfadd..610eec305df 100644 --- a/mysql-test/suite/rpl/t/rpl_row_until.test +++ b/mysql-test/suite/rpl/t/rpl_row_until.test @@ -13,6 +13,8 @@ save_master_pos; connection slave; sync_with_master; stop slave; +# Make sure the slave sql and io thread has stopped +--source include/wait_for_slave_to_stop.inc connection master; # create some events on master @@ -52,6 +54,8 @@ save_master_pos; connection slave; sync_with_master; stop slave; +# Make sure the slave sql and io thread has stopped +--source include/wait_for_slave_to_stop.inc # this should stop immediately as we are already there start slave until master_log_file='master-bin.000001', master_log_pos=740; diff --git a/mysql-test/suite/rpl/t/rpl_skip_error-slave.opt b/mysql-test/suite/rpl/t/rpl_skip_error-slave.opt index c84171976a1..a8f5deaa30b 100644 --- a/mysql-test/suite/rpl/t/rpl_skip_error-slave.opt +++ b/mysql-test/suite/rpl/t/rpl_skip_error-slave.opt @@ -1 +1 @@ ---slave-skip-error=1062,1582 +--slave-skip-error=1062 diff --git a/mysql-test/suite/rpl/t/rpl_skip_error.test b/mysql-test/suite/rpl/t/rpl_skip_error.test index b68b637b3b0..72d434d9c9b 100644 --- a/mysql-test/suite/rpl/t/rpl_skip_error.test +++ b/mysql-test/suite/rpl/t/rpl_skip_error.test @@ -3,6 +3,14 @@ ######################################### # Note that errors are ignored by opt file. source include/master-slave.inc; +source include/have_binlog_format_mixed_or_statement.inc; + +# +# Bug #30594 +# Skipping error due to applying Row-based repliation events +# should be checked with another test file +# consider names like rpl_row_skip_error +# create table t1 (n int not null primary key); save_master_pos; diff --git a/mysql-test/suite/rpl/t/rpl_slave_skip-slave.opt b/mysql-test/suite/rpl/t/rpl_slave_skip-slave.opt new file mode 100644 index 00000000000..627becdbfb5 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_slave_skip-slave.opt @@ -0,0 +1 @@ +--innodb diff --git a/mysql-test/suite/rpl/t/rpl_slave_skip.test b/mysql-test/suite/rpl/t/rpl_slave_skip.test index b19d6a2730b..6783098fd7c 100644 --- a/mysql-test/suite/rpl/t/rpl_slave_skip.test +++ b/mysql-test/suite/rpl/t/rpl_slave_skip.test @@ -1,7 +1,9 @@ source include/master-slave.inc; +source include/have_innodb.inc; --echo **** On Slave **** connection slave; +source include/have_innodb.inc; STOP SLAVE; --echo **** On Master **** @@ -69,3 +71,240 @@ query_vertical SHOW SLAVE STATUS; connection master; DROP TABLE t1, t2; sync_slave_with_master; + +# +# More tests for BUG#28618 +# +# Case 1. +# ROW binlog format and non-transactional tables. +# Create the group of events via triggers and try to skip +# some items of that group. +# + +connection master; +SET SESSION BINLOG_FORMAT=ROW; +SET AUTOCOMMIT=0; + +CREATE TABLE t1 (a INT, b VARCHAR(20)) ENGINE=myisam; +CREATE TABLE t2 (a INT, b VARCHAR(20)) ENGINE=myisam; +CREATE TABLE t3 (a INT, b VARCHAR(20)) ENGINE=myisam; + +INSERT INTO t1 VALUES (1,'master/slave'); +INSERT INTO t2 VALUES (1,'master/slave'); +INSERT INTO t3 VALUES (1,'master/slave'); + +DELIMITER |; + +CREATE TRIGGER tr1 AFTER UPDATE on t1 FOR EACH ROW +BEGIN + INSERT INTO t2 VALUES (NEW.a,NEW.b); + DELETE FROM t2 WHERE a < NEW.a; +END| + +CREATE TRIGGER tr2 AFTER INSERT on t2 FOR EACH ROW +BEGIN + UPDATE t3 SET a =2, b = 'master only'; +END| + +DELIMITER ;| + +--echo **** On Slave **** +sync_slave_with_master; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo **** On Master **** +connection master; +UPDATE t1 SET a = 2, b = 'master only' WHERE a = 1; +DROP TRIGGER tr1; +DROP TRIGGER tr2; +INSERT INTO t1 VALUES (3,'master/slave'); +INSERT INTO t2 VALUES (3,'master/slave'); +INSERT INTO t3 VALUES (3,'master/slave'); + +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +SELECT * FROM t3 ORDER BY a; + +save_master_pos; + +--echo *** On Slave *** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; + +SELECT * FROM t1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +SELECT * FROM t3 ORDER BY a; + +connection master; +DROP TABLE t1, t2, t3; +sync_slave_with_master; + +--echo **** Case 2: Row binlog format and transactional tables **** + +# Create the transaction and try to skip some +# queries from one. + +--echo *** On Master *** +connection master; +CREATE TABLE t4 (a INT, b VARCHAR(20)) ENGINE=innodb; +CREATE TABLE t5 (a INT, b VARCHAR(20)) ENGINE=innodb; +CREATE TABLE t6 (a INT, b VARCHAR(20)) ENGINE=innodb; + +--echo **** On Slave **** +sync_slave_with_master; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo *** On Master *** +connection master; +BEGIN; +INSERT INTO t4 VALUES (2, 'master only'); +INSERT INTO t5 VALUES (2, 'master only'); +INSERT INTO t6 VALUES (2, 'master only'); +COMMIT; + +BEGIN; +INSERT INTO t4 VALUES (3, 'master/slave'); +INSERT INTO t5 VALUES (3, 'master/slave'); +INSERT INTO t6 VALUES (3, 'master/slave'); +COMMIT; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +save_master_pos; + +--echo *** On Slave *** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +# Test skipping two groups + +--echo **** On Slave **** +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo *** On Master *** +connection master; +BEGIN; +INSERT INTO t4 VALUES (6, 'master only'); +INSERT INTO t5 VALUES (6, 'master only'); +INSERT INTO t6 VALUES (6, 'master only'); +COMMIT; + +BEGIN; +INSERT INTO t4 VALUES (7, 'master only'); +INSERT INTO t5 VALUES (7, 'master only'); +INSERT INTO t6 VALUES (7, 'master only'); +COMMIT; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +save_master_pos; + +--echo *** On Slave *** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=10; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +# +# And the same, but with autocommit = 0 +# +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +connection master; +SET AUTOCOMMIT=0; + +INSERT INTO t4 VALUES (4, 'master only'); +INSERT INTO t5 VALUES (4, 'master only'); +INSERT INTO t6 VALUES (4, 'master only'); +COMMIT; + +INSERT INTO t4 VALUES (5, 'master/slave'); +INSERT INTO t5 VALUES (5, 'master/slave'); +INSERT INTO t6 VALUES (5, 'master/slave'); +COMMIT; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +save_master_pos; + +--echo *** On Slave *** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; + +SELECT * FROM t4 ORDER BY a; +SELECT * FROM t5 ORDER BY a; +SELECT * FROM t6 ORDER BY a; + +connection master; +DROP TABLE t4, t5, t6; +sync_slave_with_master; + +--echo **** Case 3: Statement logging format and LOAD DATA with non-transactional table **** + +# LOAD DATA creates two events in binary log for statement binlog format. +# Try to skip the first. + +--echo *** On Master *** +connection master; +CREATE TABLE t10 (a INT, b VARCHAR(20)) ENGINE=myisam; + +--echo *** On Slave *** +sync_slave_with_master; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo *** On Master *** +connection master; +SET SESSION BINLOG_FORMAT=STATEMENT; +exec cp ./suite/rpl/data/rpl_bug28618.dat $MYSQLTEST_VARDIR/tmp/; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/rpl_bug28618.dat' INTO TABLE t10 FIELDS TERMINATED BY '|'; +remove_file $MYSQLTEST_VARDIR/tmp/rpl_bug28618.dat; + +SELECT * FROM t10 ORDER BY a; + +save_master_pos; + +--echo *** On Slave *** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; + +SELECT * FROM t10 ORDER BY a; + +connection master; +DROP TABLE t10; +sync_slave_with_master; + diff --git a/mysql-test/suite/rpl/t/rpl_sp_effects.test b/mysql-test/suite/rpl/t/rpl_sp_effects.test index 027bfd69f36..c1092e3260f 100644 --- a/mysql-test/suite/rpl/t/rpl_sp_effects.test +++ b/mysql-test/suite/rpl/t/rpl_sp_effects.test @@ -201,6 +201,10 @@ sync_slave_with_master; connection slave; SELECT 'slave', a FROM t1 ORDER BY a; +# +# cleanup +# + connection master; drop table t1; drop function f1; @@ -208,4 +212,50 @@ drop function f2; drop procedure p1; sync_slave_with_master; +# +# bug#26199 Replication Failure on Slave when using stored procs +# with bit-type parameters + +connection master; + +create table t2 (b BIT(7)); +delimiter //; +create procedure sp_bug26199(bitvalue BIT(7)) +begin + insert into t2 set b = bitvalue; +end // + +create function sf_bug26199(b BIT(7)) returns int +begin + insert into t2 values(b); + return 0; +end// + +DELIMITER ;// + + + +call sp_bug26199(b'1110'); +call sp_bug26199('\0'); +select sf_bug26199(b'1111111'); +select sf_bug26199(b'101111111'); +select sf_bug26199('\''); +select hex(b) from t2; + +sync_slave_with_master; +#connection slave; +select hex(b) from t2; + +# +# cleanup bug#26199 +# +connection master; +drop table t2; +drop procedure sp_bug26199; +drop function sf_bug26199; + +sync_slave_with_master; + SET GLOBAL log_bin_trust_function_creators = 0; + +--echo end of the tests diff --git a/mysql-test/suite/rpl/t/rpl_ssl.test b/mysql-test/suite/rpl/t/rpl_ssl.test index c1b7bc2097b..be730b35c13 100644 --- a/mysql-test/suite/rpl/t/rpl_ssl.test +++ b/mysql-test/suite/rpl/t/rpl_ssl.test @@ -42,6 +42,10 @@ select * from t1; # Do the same thing a number of times disable_query_log; disable_result_log; +# 2007-11-27 mats Bug #32756 Starting and stopping the slave in a loop can lose rows +# After discussions with Engineering, I'm disabling this part of the test to avoid it causing +# red trees. +disable_parsing; let $i= 100; while ($i) { @@ -54,7 +58,8 @@ while ($i) stop slave; dec $i; } -start slave; +enable_parsing; +START SLAVE; enable_query_log; enable_result_log; connection master; diff --git a/mysql-test/suite/rpl/t/rpl_stm_mystery22.test b/mysql-test/suite/rpl/t/rpl_stm_mystery22.test index 017593fdfba..b43a734fffc 100644 --- a/mysql-test/suite/rpl/t/rpl_stm_mystery22.test +++ b/mysql-test/suite/rpl/t/rpl_stm_mystery22.test @@ -28,7 +28,7 @@ insert into t1 values(NULL,'new'); save_master_pos; connection slave; # wait until the slave tries to run the query, fails and aborts slave thread -wait_for_slave_to_stop; +source include/wait_for_slave_sql_error.inc; select * from t1 order by n; delete from t1 where n = 2; --disable_warnings diff --git a/mysql-test/suite/rpl/t/rpl_stm_until.test b/mysql-test/suite/rpl/t/rpl_stm_until.test index 98e7e0e5eac..c8d3cb1823d 100644 --- a/mysql-test/suite/rpl/t/rpl_stm_until.test +++ b/mysql-test/suite/rpl/t/rpl_stm_until.test @@ -12,6 +12,8 @@ save_master_pos; connection slave; sync_with_master; stop slave; +# Make sure the slave sql and io thread has stopped +--source include/wait_for_slave_to_stop.inc connection master; # create some events on master @@ -51,6 +53,8 @@ save_master_pos; connection slave; sync_with_master; stop slave; +# Make sure the slave sql and io thread has stopped +--source include/wait_for_slave_to_stop.inc # this should stop immediately as we are already there start slave until master_log_file='master-bin.000001', master_log_pos=776; diff --git a/mysql-test/suite/rpl/t/rpl_switch_stm_row_mixed.test b/mysql-test/suite/rpl/t/rpl_switch_stm_row_mixed.test index b0012827db8..05dcb91ca28 100644 --- a/mysql-test/suite/rpl/t/rpl_switch_stm_row_mixed.test +++ b/mysql-test/suite/rpl/t/rpl_switch_stm_row_mixed.test @@ -518,6 +518,42 @@ CREATE TABLE t12 (data LONG); LOCK TABLES t12 WRITE; INSERT INTO t12 VALUES(UUID()); UNLOCK TABLES; +sync_slave_with_master; + +# +# BUG#28086: SBR of USER() becomes corrupted on slave +# + +connection master; + +# Just to get something that is non-trivial, albeit still simple, we +# stuff the result of USER() and CURRENT_USER() into a variable. +--delimiter $$ +CREATE FUNCTION my_user() + RETURNS CHAR(64) +BEGIN + DECLARE user CHAR(64); + SELECT USER() INTO user; + RETURN user; +END $$ +--delimiter ; + +--delimiter $$ +CREATE FUNCTION my_current_user() + RETURNS CHAR(64) +BEGIN + DECLARE user CHAR(64); + SELECT CURRENT_USER() INTO user; + RETURN user; +END $$ +--delimiter ; + +DROP TABLE IF EXISTS t13; +CREATE TABLE t13 (data CHAR(64)); +INSERT INTO t13 VALUES (USER()); +INSERT INTO t13 VALUES (my_user()); +INSERT INTO t13 VALUES (CURRENT_USER()); +INSERT INTO t13 VALUES (my_current_user()); source include/show_binlog_events.inc; sync_slave_with_master; diff --git a/mysql-test/suite/rpl/t/rpl_temporary.test b/mysql-test/suite/rpl/t/rpl_temporary.test index 09b8b83f25f..852dfdbc25c 100644 --- a/mysql-test/suite/rpl/t/rpl_temporary.test +++ b/mysql-test/suite/rpl/t/rpl_temporary.test @@ -208,8 +208,9 @@ select * from t1; connection master; drop table t1; +--remove_file $MYSQLTEST_VARDIR/tmp/bug14157.sql # Delete the anonymous users source include/delete_anonymous_users.inc; -# End of 5.1 tests +# End of tests diff --git a/mysql-test/suite/rpl/t/rpl_temporary_errors-slave.opt b/mysql-test/suite/rpl/t/rpl_temporary_errors-slave.opt new file mode 100644 index 00000000000..80c171170f6 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_temporary_errors-slave.opt @@ -0,0 +1,3 @@ +--loose-debug="+d,all_errors_are_temporary_errors" --slave-transaction-retries=2 + + diff --git a/mysql-test/suite/rpl/t/rpl_temporary_errors.test b/mysql-test/suite/rpl/t/rpl_temporary_errors.test new file mode 100644 index 00000000000..6a57f3cc167 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_temporary_errors.test @@ -0,0 +1,27 @@ +source include/master-slave.inc; + +--echo **** On Master **** +connection master; +SET SESSION BINLOG_FORMAT=ROW; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4); +--echo **** On Slave **** +sync_slave_with_master; +SHOW STATUS LIKE 'Slave_retried_transactions'; +UPDATE t1 SET a = 5, b = 47 WHERE a = 1; +SELECT * FROM t1; +--echo **** On Master **** +connection master; +UPDATE t1 SET a = 5, b = 5 WHERE a = 1; +SELECT * FROM t1; +#SHOW BINLOG EVENTS; +--echo **** On Slave **** +sync_slave_with_master; +SHOW STATUS LIKE 'Slave_retried_transactions'; +SELECT * FROM t1; +source include/show_slave_status.inc; +DROP TABLE t1; + +--echo **** On Master **** +connection master; +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/t/rpl_trigger.test b/mysql-test/suite/rpl/t/rpl_trigger.test index 9f5f6fc9b4c..4a496ea4923 100644 --- a/mysql-test/suite/rpl/t/rpl_trigger.test +++ b/mysql-test/suite/rpl/t/rpl_trigger.test @@ -316,8 +316,13 @@ SELECT * FROM t2; # 2. Check that the trigger is non-SUID on the slave; # 3. Check that the trigger can be activated on the slave. +# +# We disable warnings here since it affects the result file in +# different ways depending on the mode being used. +disable_warnings; INSERT INTO t1 VALUES(2); +enable_warnings; SELECT * FROM t1; SELECT * FROM t2; diff --git a/mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result b/mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result index 54056ac613b..685fdbf0a6e 100644 --- a/mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result +++ b/mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result @@ -615,6 +615,66 @@ c1 c2 c3 c4 c5 c6 c7 1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP 2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP 3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP +*** Create t14a on slave *** +STOP SLAVE; +RESET SLAVE; +CREATE TABLE t14a (c1 INT KEY, c4 BLOB, c5 CHAR(5), +c6 INT DEFAULT '1', +c7 TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP +)ENGINE='NDB'; +*** Create t14a on Master *** +CREATE TABLE t14a (c1 INT PRIMARY KEY, 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 t14a () VALUES(1,@b1,'Kyle'), +(2,@b1,'JOE'), +(3,@b1,'QA'); +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 +1 b1b1b1b1b1b1b1b1 Kyle +2 b1b1b1b1b1b1b1b1 JOE +3 b1b1b1b1b1b1b1b1 QA +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP +STOP SLAVE; +RESET SLAVE; +*** Master Drop c5 *** +ALTER TABLE t14a DROP COLUMN c5; +RESET MASTER; +*** Start Slave *** +START SLAVE; +*** Master Data Insert *** +set @b1 = 'b1b1b1b1'; +set @b1 = concat(@b1,@b1); +INSERT INTO t14a () VALUES(4,@b1), +(5,@b1), +(6,@b1); +SELECT * FROM t14a ORDER BY c1; +c1 c4 +1 b1b1b1b1b1b1b1b1 +2 b1b1b1b1b1b1b1b1 +3 b1b1b1b1b1b1b1b1 +4 b1b1b1b1b1b1b1b1 +5 b1b1b1b1b1b1b1b1 +6 b1b1b1b1b1b1b1b1 +*** Select on Slave **** +SELECT * FROM t14a ORDER BY c1; +c1 c4 c5 c6 c7 +1 b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP +2 b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP +3 b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP +4 b1b1b1b1b1b1b1b1 NULL NULL CURRENT_TIMESTAMP +5 b1b1b1b1b1b1b1b1 NULL NULL CURRENT_TIMESTAMP +6 b1b1b1b1b1b1b1b1 NULL NULL CURRENT_TIMESTAMP *** connect to master and drop columns *** ALTER TABLE t14 DROP COLUMN c2; ALTER TABLE t14 DROP COLUMN c4; @@ -707,7 +767,7 @@ Last_IO_Errno # Last_IO_Error # Last_SQL_Errno 1060 Last_SQL_Error Error 'Duplicate column name 'c6'' on query. Default database: 'test'. Query: 'ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5' -SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; *** Try to insert in master **** INSERT INTO t15 () VALUES(5,2.00,'Replication Testing',@b1,'Buda',2); diff --git a/mysql-test/suite/rpl_ndb/r/rpl_row_basic_7ndb.result b/mysql-test/suite/rpl_ndb/r/rpl_row_basic_7ndb.result index 5519e0dcd0c..abd5bad8e49 100644 --- a/mysql-test/suite/rpl_ndb/r/rpl_row_basic_7ndb.result +++ b/mysql-test/suite/rpl_ndb/r/rpl_row_basic_7ndb.result @@ -415,4 +415,23 @@ a b c 2 4 8 3 6 9 99 99 99 +**** Test for BUG#31552 **** +**** On Master **** +DELETE FROM t1; +**** Resetting master and slave **** +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +**** On Master **** +INSERT INTO t1 VALUES ('K','K'), ('L','L'), ('M','M'); +**** On Master **** +DELETE FROM t1 WHERE C1 = 'L'; +DELETE FROM t1; +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 +Last_SQL_Error +0 +SELECT COUNT(*) FROM t1 ORDER BY c1,c2; +COUNT(*) 0 DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8; diff --git a/mysql-test/suite/rpl_ndb/t/disabled.def b/mysql-test/suite/rpl_ndb/t/disabled.def index 05e8297bf0e..60bfa559953 100644 --- a/mysql-test/suite/rpl_ndb/t/disabled.def +++ b/mysql-test/suite/rpl_ndb/t/disabled.def @@ -11,16 +11,13 @@ ############################################################################## -rpl_ndb_2innodb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue -rpl_ndb_2myisam : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue -rpl_ndb_2other : BUG#21842 2007-08-30 tsmith test has never worked on bigendian (sol10-sparc-a, powermacg5 -rpl_ndb_dd_partitions : BUG#19259 2006-04-21 rpl_ndb_dd_partitions fails on s/AMD -rpl_ndb_innodb2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue -rpl_ndb_myisam2ndb : Bug#29549 rpl_ndb_myisam2ndb,rpl_ndb_innodb2ndb failed on Solaris for pack_length issue +rpl_ndb_2innodb : Bug #32648 Test failure between NDB Cluster and other engines +rpl_ndb_2myisam : Bug #32648 Test failure between NDB Cluster and other engines +rpl_ndb_2other : Bug #32648 Test failure between NDB Cluster and other engines rpl_ndb_ddl : BUG#28798 2007-05-31 lars Valgrind failure in NDB -rpl_ndb_mix_innodb : BUG#28123 rpl_ndb_mix_innodb.test casue slave to core on sol10-sparc-a rpl_ndb_ctype_ucs2_def : BUG#27404 util thd mysql_parse sig11 when mysqld default multibyte charset rpl_ndb_extraColMaster : BUG#30854 : Tables name show as binary in slave err msg on vm-win2003-64-b and Solaris +rpl_ndb_mix_innodb : Bug #32720 Test rpl_ndb_mix_innodb fails on SPARC and PowerPC # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open diff --git a/mysql-test/t/analyze.test b/mysql-test/t/analyze.test index 7c9830bb468..0903db1eca4 100644 --- a/mysql-test/t/analyze.test +++ b/mysql-test/t/analyze.test @@ -72,4 +72,16 @@ analyze table t1; show index from t1; drop table t1; -# End of 4.1 tests +--echo End of 4.1 tests + +# +# Bug #30495: optimize table t1,t2,t3 extended errors +# +create table t1(a int); +--error 1064 +analyze table t1 extended; +--error 1064 +optimize table t1 extended; +drop table t1; + +--echo End of 5.0 tests diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index f8eff10e30a..a1158dbfd3b 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1320,7 +1320,7 @@ SELECT * FROM t2; INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W'); INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring',''); INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily',''); -OPTIMIZE TABLE t2 EXTENDED; +OPTIMIZE TABLE t2; SELECT * FROM t2; REPAIR TABLE t2; SELECT * FROM t2; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 023e55ea418..45ee4c1c88d 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -483,7 +483,7 @@ drop table t1,t2; create table t1 (a int); --error 1093 create table t1 select * from t1; ---error 1093 +--error ER_WRONG_OBJECT create table t2 union = (t1) select * from t1; flush tables with read lock; unlock tables; diff --git a/mysql-test/t/ctype_euckr.test b/mysql-test/t/ctype_euckr.test index 56939817b2f..05e4b04eded 100644 --- a/mysql-test/t/ctype_euckr.test +++ b/mysql-test/t/ctype_euckr.test @@ -31,3 +31,26 @@ SELECT hex(a) FROM t1 ORDER BY a; DROP TABLE t1; # End of 4.1 tests + +# +#Bug #30315 Character sets: insertion of euckr code value 0xa141 fails +# +create table t1 (s1 varchar(5) character set euckr); +# Insert some valid characters +insert into t1 values (0xA141); +insert into t1 values (0xA15A); +insert into t1 values (0xA161); +insert into t1 values (0xA17A); +insert into t1 values (0xA181); +insert into t1 values (0xA1FE); +# Insert some invalid characters +insert into t1 values (0xA140); +insert into t1 values (0xA15B); +insert into t1 values (0xA160); +insert into t1 values (0xA17B); +insert into t1 values (0xA180); +insert into t1 values (0xA1FF); +select hex(s1), hex(convert(s1 using utf8)) from t1 order by binary s1; +drop table t1; + +--echo End of 5.0 tests diff --git a/mysql-test/t/ctype_uca.test b/mysql-test/t/ctype_uca.test index 695a21adbf5..0d917428efb 100644 --- a/mysql-test/t/ctype_uca.test +++ b/mysql-test/t/ctype_uca.test @@ -538,4 +538,8 @@ alter table t1 convert to character set ucs2 collate ucs2_czech_ci; select * from t1 where a like 'c%'; drop table t1; +set collation_connection=ucs2_unicode_ci; +--source include/ctype_regex.inc +set names utf8; + -- echo End for 5.0 tests diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index 7827ff0d31e..de2033321b9 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -659,6 +659,9 @@ select * from t1 where a=if(b<10,_ucs2 0x00C0,_ucs2 0x0062); select * from t1 where a=if(b<10,_ucs2 0x0062,_ucs2 0x00C0); drop table t1; +set collation_connection=ucs2_general_ci; +--source include/ctype_regex.inc +set names latin1; # # Bug#30981 CHAR(0x41 USING ucs2) doesn't add leading zero # diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 5c35bf82343..d18a7d22a0e 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -186,6 +186,13 @@ select * from t1 where a = 'b' and a != 'b'; drop table t1; # +# Testing regexp +# +set collation_connection=utf8_general_ci; +--source include/ctype_regex.inc +set names utf8; + +# # Bug #3928 regexp [[:>:]] and UTF-8 # set names utf8; diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 03b4f8b3013..b542027207f 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -103,13 +103,13 @@ DROP TABLE t1; # Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables # SET @bug20627_old_auto_increment_offset= - @@auto_increment_offset= 2; + @@auto_increment_offset; SET @bug20627_old_auto_increment_increment= - @@auto_increment_increment= 3; + @@auto_increment_increment; SET @bug20627_old_session_auto_increment_offset= - @@session.auto_increment_offset= 4; + @@session.auto_increment_offset; SET @bug20627_old_session_auto_increment_increment= - @@session.auto_increment_increment= 5; + @@session.auto_increment_increment; SET @@auto_increment_offset= 2; SET @@auto_increment_increment= 3; SET @@session.auto_increment_offset= 4; @@ -151,13 +151,13 @@ SET @@session.auto_increment_increment= # Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID # SET @bug20830_old_auto_increment_offset= - @@auto_increment_offset= 2; + @@auto_increment_offset; SET @bug20830_old_auto_increment_increment= - @@auto_increment_increment= 3; + @@auto_increment_increment; SET @bug20830_old_session_auto_increment_offset= - @@session.auto_increment_offset= 4; + @@session.auto_increment_offset; SET @bug20830_old_session_auto_increment_increment= - @@session.auto_increment_increment= 5; + @@session.auto_increment_increment; SET @@auto_increment_offset= 2; SET @@auto_increment_increment= 3; SET @@session.auto_increment_offset= 4; @@ -244,14 +244,6 @@ SELECT HEX(a) FROM t1; DROP TABLE t1; # -# Bug#26464 - insert delayed + update + merge = corruption -# -CREATE TABLE t1(c1 INT) ENGINE=MyISAM; -CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1); ---error 1031 -INSERT DELAYED INTO t2 VALUES(1); -DROP TABLE t1, t2; -# # Bug#27358 INSERT DELAYED does not honour SQL_MODE of the client # --disable_warnings diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index a39de913659..3746d20ebd8 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -15,3 +15,11 @@ concurrent_innodb : BUG#21579 2006-08-11 mleich innodb_concurrent random ctype_big5 : BUG#26711 2007-06-21 Lars Test has never worked on Double Whopper federated_transactions : Bug#29523 Transactions do not work +events : Bug#32664 events.test fails randomly +events_scheduling : Bug#29830 Test case 'events_scheduling' fails on Mac OS X and Windows +lowercase_table3 : Bug#32667 lowercase_table3.test reports to error log +kill : Bug#29149: Test "kill" fails on Windows +grant3 : Bug#32723: grant3.test fails +innodb_mysql : Bug#32724: innodb_mysql.test fails randomly +wait_timeout : Bug#32801 wait_timeout.test fails randomly +kill : Bug#29149 Test "kill" fails on Windows diff --git a/mysql-test/t/func_regexp.test b/mysql-test/t/func_regexp.test index 5eff404bc0f..1b35fab9d54 100644 --- a/mysql-test/t/func_regexp.test +++ b/mysql-test/t/func_regexp.test @@ -6,28 +6,9 @@ drop table if exists t1; --enable_warnings -create table t1 (s1 char(64),s2 char(64)); +set names latin1; +--source include/ctype_regex.inc -insert into t1 values('aaa','aaa'); -insert into t1 values('aaa|qqq','qqq'); -insert into t1 values('gheis','^[^a-dXYZ]+$'); -insert into t1 values('aab','^aa?b'); -insert into t1 values('Baaan','^Ba*n'); -insert into t1 values('aaa','qqq|aaa'); -insert into t1 values('qqq','qqq|aaa'); - -insert into t1 values('bbb','qqq|aaa'); -insert into t1 values('bbb','qqq'); -insert into t1 values('aaa','aba'); - -insert into t1 values(null,'abc'); -insert into t1 values('def',null); -insert into t1 values(null,null); -insert into t1 values('ghi','ghi['); - -select HIGH_PRIORITY s1 regexp s2 from t1; - -drop table t1; # # This test a bug in regexp on Alpha diff --git a/mysql-test/t/func_set.test b/mysql-test/t/func_set.test index 710b9b90a05..e4fde6e0e0e 100644 --- a/mysql-test/t/func_set.test +++ b/mysql-test/t/func_set.test @@ -54,4 +54,21 @@ select find_in_set(binary 'a', 'A,B,C'); # select find_in_set('1','3,1,'); -# End of 4.1 tests +--echo End of 4.1 tests + +# +# Bug #32560: crash with interval function and count(*) +# +SELECT INTERVAL(0.0, NULL); +SELECT INTERVAL(0.0, CAST(NULL AS DECIMAL)); +SELECT INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL)); +SELECT INTERVAL(0.0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +SELECT INTERVAL(0.0, CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), + CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), + CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL), CAST(NULL AS DECIMAL)); +SELECT INTERVAL(0.0, CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), + CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), + CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL), + CAST(DATE(NULL) AS DECIMAL), CAST(DATE(NULL) AS DECIMAL)); + +--echo End of 5.0 tests diff --git a/mysql-test/t/innodb-semi-consistent.test b/mysql-test/t/innodb-semi-consistent.test index 7a9231b508f..c33126b93ff 100644 --- a/mysql-test/t/innodb-semi-consistent.test +++ b/mysql-test/t/innodb-semi-consistent.test @@ -1,6 +1,10 @@ -- source include/not_embedded.inc -- source include/have_innodb.inc +--disable_warnings +drop table if exists t1; +--enable_warnings + # basic tests of semi-consistent reads connect (a,localhost,root,,); diff --git a/mysql-test/t/innodb-ucs2.test b/mysql-test/t/innodb-ucs2.test index 6647a9d0845..7b91ef37d3f 100644 --- a/mysql-test/t/innodb-ucs2.test +++ b/mysql-test/t/innodb-ucs2.test @@ -1,6 +1,10 @@ -- source include/have_innodb.inc -- source include/have_ucs2.inc +--disable_warnings +drop table if exists t1, t2; +--enable_warnings + # # BUG 14056 Column prefix index on UTF-8 primary key column causes: Can't find record.. # diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index f68bb87655e..8fdbbfcde79 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -721,6 +721,38 @@ select * from t2; drop table t1,t2; # +# Bug #29136 erred multi-delete on trans table does not rollback +# + +# prepare +--disable_warnings +drop table if exists t1, t2; +--enable_warnings +CREATE TABLE t1 (a int, PRIMARY KEY (a)); +CREATE TABLE t2 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; +create trigger trg_del_t2 after delete on t2 for each row + insert into t1 values (1); +insert into t1 values (1); +insert into t2 values (1),(2); + + +# exec cases A, B - see multi_update.test + +# A. send_error() w/o send_eof() branch + +--error ER_DUP_ENTRY +delete t2 from t2; + +# check + +select count(*) from t2 /* must be 2 as restored after rollback caused by the error */; + +# cleanup bug#29136 + +drop table t1, t2; + + +# # Testing of IFNULL # create table t1 (a int, b int) engine=innodb; diff --git a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test new file mode 100644 index 00000000000..eddcbb59ed4 --- /dev/null +++ b/mysql-test/t/merge-big.test @@ -0,0 +1,150 @@ +# +# Test of MERGE tables with multisession and many waits. +# +# This test takes rather long time so let us run it only in --big-test mode +--source include/big_test.inc +# We are using some debug-only features in this test +--source include/have_debug.inc + +--disable_warnings +drop table if exists t1,t2,t3,t4,t5,t6; +--enable_warnings + +--echo # +--echo # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE +--echo # corrupts a MERGE table +--echo # Problem #3 +--echo # +# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock. +# This did *not* require a MERGE table. +# +# To increase reproducibility it was necessary to enter a sleep of 2 +# seconds at the end of wait_for_tables() after unlock of LOCK_open. In +# 5.0 and 5.1 the sleep must be inserted in open_and_lock_tables() after +# open_tables() instead. wait_for_tables() is not used in this case. The +# problem was that FLUSH TABLES releases LOCK_open while having unlocked +# and closed all tables. When this happened while a thread was in the +# loop in mysql_lock_tables() right after wait_for_tables() +# (open_tables()) and before retrying to lock, the thread got the lock. +# And it did not notice that the table needed a refresh after the +# [re-]open. So it executed its statement on the table. +# +# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and +# let it wait in wait_for_tables() (open_table()). The second FLUSH +# TABLES must happen while the INSERT was on its way from +# wait_for_tables() (open_table()) to the next call of thr_multi_lock(). +# This needed to be supported by a sleep to make it repeatable. +# +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +LOCK TABLE t1 WRITE; +#SELECT NOW(); + --echo # connection con1 + connect (con1,localhost,root,,); + let $con1_id= `SELECT CONNECTION_ID()`; + SET SESSION debug="+d,sleep_open_and_lock_after_open"; + send INSERT INTO t1 VALUES (1); +--echo # connection default +connection default; +--echo # Let INSERT go into thr_multi_lock(). +let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST + WHERE ID = $con1_id AND STATE = 'Locked'; +--source include/wait_condition.inc +#SELECT NOW(); +--echo # Kick INSERT out of thr_multi_lock(). +FLUSH TABLES; +#SELECT NOW(); +--echo # Let INSERT go through open_tables() where it sleeps. +let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST + WHERE ID = $con1_id AND STATE = 'DBUG sleep'; +--source include/wait_condition.inc +#SELECT NOW(); +--echo # Unlock and close table and wait for con1 to close too. +FLUSH TABLES; +#SELECT NOW(); +--echo # This should give no result. +SELECT * FROM t1; +#SELECT NOW(); +UNLOCK TABLES; + --echo # connection con1 + connection con1; + reap; + SET SESSION debug="-d,sleep_open_and_lock_after_open"; + disconnect con1; +--echo # connection default +connection default; +DROP TABLE t1; + +--echo # +--echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and +--echo # REPAIR TABLE corrupts a MERGE table +--echo # +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +--echo # +--echo # CREATE ... SELECT +--echo # try to access parent from another thread. +--echo # +#SELECT NOW(); + --echo # connection con1 + connect (con1,localhost,root,,); + let $con1_id= `SELECT CONNECTION_ID()`; + SET SESSION debug="+d,sleep_create_select_before_lock"; + send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=FIRST SELECT * FROM t3; +--echo # connection default +connection default; +# wait for the other query to start executing +let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST + WHERE ID = $con1_id AND STATE = 'DBUG sleep'; +--source include/wait_condition.inc +#SELECT NOW(); +--echo # Now try to access the parent. +--echo # If 3 is in table, SELECT had to wait. +SELECT * FROM t4 ORDER BY c1; +#SELECT NOW(); + --echo # connection con1 + connection con1; + reap; + #SELECT NOW(); + SET SESSION debug="-d,sleep_create_select_before_lock"; + disconnect con1; +--echo # connection default +connection default; +--echo # Cleanup for next test. +DROP TABLE t4; +DELETE FROM t1 WHERE c1 != 1; +--echo # +--echo # CREATE ... SELECT +--echo # try to access child from another thread. +--echo # +#SELECT NOW(); + --echo # connection con1 + connect (con1,localhost,root,,); + let $con1_id= `SELECT CONNECTION_ID()`; + SET SESSION debug="+d,sleep_create_select_before_lock"; + send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=FIRST SELECT * FROM t3; +--echo # connection default +connection default; +# wait for the other query to start executing +let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST + WHERE ID = $con1_id AND STATE = 'DBUG sleep'; +--source include/wait_condition.inc +#SELECT NOW(); +--echo # Now try to access a child. +--echo # If 3 is in table, SELECT had to wait. +SELECT * FROM t1 ORDER BY c1; +#SELECT NOW(); + --echo # connection con1 + connection con1; + reap; + #SELECT NOW(); + SET SESSION debug="-d,sleep_create_select_before_lock"; + disconnect con1; +--echo # connection default +connection default; +DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index a50588b1e78..3b71dc6fde1 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -221,6 +221,7 @@ create table t2 (a int not null); insert into t1 values (1); insert into t2 values (2); create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +--error ER_WRONG_MRG_TABLE select * from t3; create temporary table t4 (a int not null); create temporary table t5 (a int not null); @@ -229,6 +230,58 @@ insert into t5 values (2); create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5); select * from t6; drop table t6, t3, t1, t2, t4, t5; +# +# Bug#19627 - temporary merge table locking +# MERGE table and its children must match in temporary type. +# Forbid temporary merge on non-temporary children: shown above. +# Forbid non-temporary merge on temporary children: +create temporary table t1 (a int not null); +create temporary table t2 (a int not null); +insert into t1 values (1); +insert into t2 values (2); +create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +--error ER_WRONG_MRG_TABLE +select * from t3; +drop table t3, t2, t1; +# Forbid children mismatch in temporary: +create table t1 (a int not null); +create temporary table t2 (a int not null); +insert into t1 values (1); +insert into t2 values (2); +create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +--error ER_WRONG_MRG_TABLE +select * from t3; +drop table t3; +create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); +--error ER_WRONG_MRG_TABLE +select * from t3; +drop table t3, t2, t1; +--echo # CREATE...SELECT is not implemented for MERGE tables. +CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL); +CREATE TEMPORARY TABLE t2 (c1 INT NOT NULL); +CREATE TABLE t3 (c1 INT NOT NULL); +INSERT INTO t3 VALUES (3), (33); +LOCK TABLES t3 READ; +--error ER_WRONG_OBJECT +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2) + INSERT_METHOD=LAST SELECT * FROM t3; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM t4; +UNLOCK TABLES; +CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t4 SELECT * FROM t3; +--echo # Alter temporary MERGE table. +ALTER TABLE t4 UNION=(t1); +LOCK TABLES t4 WRITE; +--echo # Alter temporary MERGE table under LOCk tables. +ALTER TABLE t4 UNION=(t1,t2); +UNLOCK TABLES; +--echo # MERGE table and function. +CREATE FUNCTION f1 () RETURNS INT RETURN (SELECT max(c1) FROM t3); +SELECT * FROM t4 WHERE c1 < f1(); +DROP FUNCTION f1; +DROP TABLE t4, t3, t2, t1; # # testing merge::records_in_range and optimizer @@ -284,11 +337,11 @@ create table t1 (a int); create table t2 (a int); insert into t1 values (0); insert into t2 values (1); ---error 1093 +--error ER_WRONG_OBJECT create table t3 engine=merge union=(t1, t2) select * from t1; ---error 1093 +--error ER_WRONG_OBJECT create table t3 engine=merge union=(t1, t2) select * from t2; ---error 1093 +--error ER_WRONG_OBJECT create table t3 engine=merge union=(t1, t2) select (select max(a) from t2); drop table t1, t2; @@ -403,7 +456,7 @@ CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); SELECT * FROM t2; DROP TABLE t1, t2; CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); ---error 1168 +--error ER_NO_SUCH_TABLE SELECT * FROM t2; DROP TABLE t2; @@ -495,11 +548,11 @@ drop table t1; # CREATE TABLE fails # CREATE TABLE tm1(a INT) ENGINE=MERGE UNION=(t1, t2); ---error 1168 +--error ER_NO_SUCH_TABLE SELECT * FROM tm1; CHECK TABLE tm1; CREATE TABLE t1(a INT); ---error 1168 +--error ER_NO_SUCH_TABLE SELECT * FROM tm1; CHECK TABLE tm1; CREATE TABLE t2(a BLOB); @@ -526,3 +579,783 @@ CREATE TABLE IF NOT EXISTS t1 SELECT * FROM t2; DROP TABLE t1, t2; --echo End of 5.0 tests + +# +# Bug #8306: TRUNCATE leads to index corruption +# +create table t1 (c1 int, index(c1)); +create table t2 (c1 int, index(c1)) engine=merge union=(t1); +insert into t1 values (1); +# Close all tables. +flush tables; +# Open t2 and (implicitly) t1. +select * from t2; +# Truncate after flush works (unless another threads reopens t2 in between). +flush tables; +truncate table t1; +insert into t1 values (1); +# Close all tables. +flush tables; +# Open t2 and (implicitly) t1. +select * from t2; +# Truncate t1, wich was not recognized as open without the bugfix. +# After fix for Bug#8306 and before fix for Bug#26379, +# it should fail with a table-in-use error message, otherwise succeed. +truncate table t1; +# The insert used to fail on the crashed table. +insert into t1 values (1); +drop table t1,t2; +--echo # +--echo # Extra tests for TRUNCATE. +--echo # +--echo # Truncate MERGE table. +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)); +CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +TRUNCATE TABLE t3; +SELECT * FROM t3; +--echo # +--echo # Truncate child table. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +TRUNCATE TABLE t1; +SELECT * FROM t3; +--echo # +--echo # Truncate MERGE table under locked tables. +LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE; +INSERT INTO t1 VALUES (1); +--error ER_LOCK_OR_ACTIVE_TRANSACTION +TRUNCATE TABLE t3; +SELECT * FROM t3; +--echo # +--echo # Truncate child table under locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +TRUNCATE TABLE t1; +SELECT * FROM t3; +UNLOCK TABLES; +DROP TABLE t1, t2, t3; +--echo # +--echo # Truncate temporary MERGE table. +CREATE TEMPORARY TABLE t1 (c1 INT, INDEX(c1)); +CREATE TEMPORARY TABLE t2 (c1 INT, INDEX(c1)); +CREATE TEMPORARY TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +TRUNCATE TABLE t3; +SELECT * FROM t3; +--echo # +--echo # Truncate temporary child table. +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +TRUNCATE TABLE t1; +SELECT * FROM t3; +--echo # +--echo # Truncate temporary MERGE table under locked tables. +INSERT INTO t1 VALUES (1); +CREATE TABLE t4 (c1 INT, INDEX(c1)); +LOCK TABLE t4 WRITE; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +TRUNCATE TABLE t3; +SELECT * FROM t3; +--echo # +--echo # Truncate temporary child table under locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +TRUNCATE TABLE t1; +SELECT * FROM t3; +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; + +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table +# Preparation +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +connection default; +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table +# Problem #1 +# A thread trying to lock a MERGE table performed busy waiting while +# REPAIR TABLE or a similar table administration task was ongoing on one or +# more of its MyISAM tables. +# To allow for observability it was necessary to enter a multi-second sleep +# in mysql_admin_table() after remove_table_from_cache(), which comes after +# mysql_abort_lock(). The sleep faked a long running operation. One could +# watch a high CPU load during the sleep time. +# The problem was that mysql_abort_lock() upgrades the write lock to +# TL_WRITE_ONLY. This lock type persisted until the final unlock at the end +# of the administration task. The effect of TL_WRITE_ONLY is to reject any +# attempt to lock the table. The trying thread must close the table and wait +# until it is no longer used. Unfortunately there is no way to detect that +# one of the MyISAM tables of a MERGE table is in use. When trying to lock +# the MERGE table, all MyISAM tables are locked. If one fails on +# TL_WRITE_ONLY, all locks are aborted and wait_for_tables() is entered. +# But this doesn't see the MERGE table as used, so it seems appropriate to +# retry a lock... +# +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST; +send REPAIR TABLE t1; + connection con1; + sleep 1; # let repair run into its sleep + INSERT INTO t2 VALUES (1); +connection default; +reap; +DROP TABLE t1, t2; +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table +# Problem #2 +# A thread trying to lock a MERGE table performed busy waiting until all +# threads that did REPAIR TABLE or similar table administration tasks on +# one or more of its MyISAM tables in LOCK TABLES segments did +# UNLOCK TABLES. +# The difference against problem #1 is that the busy waiting took place +# *after* the administration task. It was terminated by UNLOCK TABLES only. +# +# This is the same test case as for +# Bug#26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging +# +# +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST; +LOCK TABLE t1 WRITE; + connection con1; + send INSERT INTO t2 VALUES (1); +connection default; +sleep 1; # Let INSERT go into thr_multi_lock(). +REPAIR TABLE t1; +sleep 2; # con1 performs busy waiting during this sleep. +UNLOCK TABLES; + connection con1; + reap; +connection default; +DROP TABLE t1, t2; +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table +# Problem #3 +# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock. +# This did *not* require a MERGE table. +# To increase reproducibility it was necessary to enter a sleep of 2 seconds +# at the end of wait_for_tables() after unlock of LOCK_open. In 5.0 and 5.1 +# the sleep must be inserted in open_and_lock_tables() after open_tables() +# instead. wait_for_tables() is not used in this case. +# The problem was that FLUSH TABLES releases LOCK_open while having unlocked +# and closed all tables. When this happened while a thread was in the loop in +# mysql_lock_tables() right after wait_for_tables() and before retrying to +# lock, the thread got the lock. (Translate to similar code places in 5.0 +# and 5.1). And it did not notice that the table needed a refresh. So it +# executed its statement on the table. +# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and let +# it wait in wait_for_tables(). (open_table() in 5.0 and 5.1). The second +# FLUSH TABLES must happen while the INSERT was on its way from +# wait_for_tables() to the next call of thr_multi_lock(). This needed to be +# supported by a sleep to make it repeatable. +# +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +LOCK TABLE t1 WRITE; + connection con1; + send INSERT INTO t1 VALUES (1); +connection default; +sleep 1; # Let INSERT go into thr_multi_lock(). +FLUSH TABLES; +sleep 1; # Let INSERT go through wait_for_tables() where it sleeps. +FLUSH TABLES; +# This should give no result. But it will with sleep(2) at the right place. +SELECT * FROM t1; +UNLOCK TABLES; + connection con1; + reap; +connection default; +DROP TABLE t1; +# +# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table +# Cleanup +disconnect con1; +disconnect con2; +# +--echo # +--echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and +--echo # REPAIR TABLE corrupts a MERGE table +# +--echo # +--echo # CREATE ... SELECT is disabled for MERGE tables. +--echo # +CREATE TABLE t1(c1 INT); +INSERT INTO t1 VALUES (1); +CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +--error ER_OPEN_AS_READONLY +CREATE TABLE t3 ENGINE=MRG_MYISAM INSERT_METHOD=LAST SELECT * FROM t2; +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE t3; +--error ER_WRONG_OBJECT +CREATE TABLE t3 ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST + SELECT * FROM t2; +--error ER_NO_SUCH_TABLE +SHOW CREATE TABLE t3; +DROP TABLE t1, t2; +# +--echo # +--echo # CREATE ... LIKE +--echo # +--echo # 1. Create like. +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +CREATE TABLE t4 LIKE t3; +SHOW CREATE TABLE t4; +--error ER_OPEN_AS_READONLY +INSERT INTO t4 VALUES (4); +DROP TABLE t4; +--echo # +--echo # 1. Create like with locked tables. +LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE; +CREATE TABLE t4 LIKE t3; +--error ER_TABLE_NOT_LOCKED +SHOW CREATE TABLE t4; +--error ER_TABLE_NOT_LOCKED +INSERT INTO t4 VALUES (4); +UNLOCK TABLES; +SHOW CREATE TABLE t4; +--error ER_OPEN_AS_READONLY +INSERT INTO t4 VALUES (4); +DROP TABLE t4; +# +--echo # +--echo # Rename child. +--echo # +--echo # 1. Normal rename of non-MERGE table. +CREATE TABLE t4 (c1 INT); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +RENAME TABLE t4 TO t5; +SELECT * FROM t5 ORDER BY c1; +RENAME TABLE t5 TO t4; +SELECT * FROM t4 ORDER BY c1; +DROP TABLE t4; +--echo # +--echo # 2. Normal rename. +SELECT * FROM t3 ORDER BY c1; +RENAME TABLE t2 TO t5; +--error ER_NO_SUCH_TABLE +SELECT * FROM t3 ORDER BY c1; +RENAME TABLE t5 TO t2; +SELECT * FROM t3 ORDER BY c1; +--echo # +--echo # 3. Normal rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +SELECT * FROM t3 ORDER BY c1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +RENAME TABLE t2 TO t5; +SELECT * FROM t3 ORDER BY c1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +RENAME TABLE t5 TO t2; +SELECT * FROM t3 ORDER BY c1; +UNLOCK TABLES; +--echo # +--echo # 4. Alter table rename. +ALTER TABLE t2 RENAME TO t5; +--error ER_NO_SUCH_TABLE +SELECT * FROM t3 ORDER BY c1; +ALTER TABLE t5 RENAME TO t2; +SELECT * FROM t3 ORDER BY c1; +--echo # +--echo # 5. Alter table rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +ALTER TABLE t2 RENAME TO t5; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM t3 ORDER BY c1; +--error ER_TABLE_NOT_LOCKED +ALTER TABLE t5 RENAME TO t2; +UNLOCK TABLES; +ALTER TABLE t5 RENAME TO t2; +SELECT * FROM t3 ORDER BY c1; +# +--echo # +--echo # Rename parent. +--echo # +--echo # 1. Normal rename with locked tables. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; +SELECT * FROM t3 ORDER BY c1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +RENAME TABLE t3 TO t5; +SELECT * FROM t3 ORDER BY c1; +--error ER_LOCK_OR_ACTIVE_TRANSACTION +RENAME TABLE t5 TO t3; +SELECT * FROM t3 ORDER BY c1; +--echo # +--echo # 5. Alter table rename with locked tables. +ALTER TABLE t3 RENAME TO t5; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM t5 ORDER BY c1; +--error ER_TABLE_NOT_LOCKED +ALTER TABLE t5 RENAME TO t3; +UNLOCK TABLES; +ALTER TABLE t5 RENAME TO t3; +SELECT * FROM t3 ORDER BY c1; +DROP TABLE t1, t2, t3; +# +--echo # +--echo # Drop locked tables. +--echo # +--echo # 1. Drop parent. +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t1 VALUES (1); +DROP TABLE t2; +--error ER_TABLE_NOT_LOCKED +SELECT * FROM t2; +SELECT * FROM t1; +UNLOCK TABLES; +--echo # 2. Drop child. +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +LOCK TABLES t1 WRITE, t2 WRITE; +INSERT INTO t1 VALUES (1); +DROP TABLE t1; +--error ER_NO_SUCH_TABLE +SELECT * FROM t2; +--error ER_NO_SUCH_TABLE +SELECT * FROM t1; +UNLOCK TABLES; +DROP TABLE t2; +# +--echo # +--echo # ALTER TABLE. Change child list. +--echo # +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)); +CREATE TABLE t3 (c1 INT, INDEX(c1)); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t3,t2) + INSERT_METHOD=LAST; +--echo # Shrink child list. +ALTER TABLE t4 UNION=(t3); +SHOW CREATE TABLE t4; +SELECT * FROM t4 ORDER BY c1; +--echo # Extend child list. +ALTER TABLE t4 UNION=(t3,t2); +SHOW CREATE TABLE t4; +SELECT * FROM t4 ORDER BY c1; +# +--echo # +--echo # ALTER TABLE under LOCK TABLES. Change child list. +--echo # +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE; +--echo # Shrink child list. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +ALTER TABLE t4 UNION=(t3); +--echo # Extend child list within locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +ALTER TABLE t4 UNION=(t3,t2); +--echo # Extend child list beyond locked tables. +--error ER_LOCK_OR_ACTIVE_TRANSACTION +ALTER TABLE t4 UNION=(t3,t2,t1); +SHOW CREATE TABLE t4; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +DROP TABLE t4; +# +--echo # +--echo # ALTER TABLE under LOCK TABLES. Grave change, table re-creation. +--echo # +CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3) + INSERT_METHOD=LAST; +--echo # Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1); +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; +# +--echo # +--echo # ALTER TABLE under LOCK TABLES. Simple change, no re-creation. +--echo # +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +INSERT INTO t3 VALUES (3); +--echo # Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44; +SELECT * FROM t4 ORDER BY c1; +ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +# +--echo # +--echo # FLUSH TABLE under LOCK TABLES. +--echo # +--echo # Lock parent first and then children. +LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock children first and then parent. +LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +--echo # Lock parent between children. +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +FLUSH TABLE t4; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLE t2; +SELECT * FROM t4 ORDER BY c1; +FLUSH TABLES; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +# +--echo # +--echo # Triggers +--echo # +--echo # Trigger on parent +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +SELECT * FROM t4 ORDER BY c1; +DROP TRIGGER t4_ai; +--echo # Trigger on parent under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +SELECT * FROM t4 ORDER BY c1; +DROP TRIGGER t4_ai; +UNLOCK TABLES; +--echo # +--echo # Trigger on child +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +INSERT INTO t3 VALUES (33); +SELECT @a; +SELECT * FROM t4 ORDER BY c1; +DROP TRIGGER t3_ai; +--echo # Trigger on child under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +SET @a=0; +INSERT INTO t4 VALUES (4); +SELECT @a; +INSERT INTO t3 VALUES (33); +SELECT @a; +SELECT * FROM t4 ORDER BY c1; +DELETE FROM t4 WHERE c1 = 33; +DROP TRIGGER t3_ai; +--echo # +--echo # Trigger with table use on child +DELETE FROM t4 WHERE c1 = 4; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; +DROP TRIGGER t3_ai; +--echo # Trigger with table use on child under LOCK TABLES +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +DROP TRIGGER t3_ai; +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; +UNLOCK TABLES; +# +--echo # +--echo # Repair +--echo # +REPAIR TABLE t4; +REPAIR TABLE t2; +SELECT * FROM t4 ORDER BY c1; +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +REPAIR TABLE t4; +REPAIR TABLE t2; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +# +--echo # +--echo # Optimize +--echo # +OPTIMIZE TABLE t4; +OPTIMIZE TABLE t2; +SELECT * FROM t4 ORDER BY c1; +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +OPTIMIZE TABLE t4; +OPTIMIZE TABLE t2; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +# +--echo # +--echo # Checksum +--echo # +CHECKSUM TABLE t4; +CHECKSUM TABLE t2; +SELECT * FROM t4 ORDER BY c1; +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +CHECKSUM TABLE t4; +CHECKSUM TABLE t2; +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +# +--echo # +--echo # Insert delayed +--echo # +# See also Bug#26464 - insert delayed + update + merge = corruption +# Succeeds in embedded server - is converted to normal insert +# Fails in normal server, ps-protocol - not supported by engine +# Fails in normal server, normal protocol - not a base table +--error 0, ER_ILLEGAL_HA, ER_WRONG_OBJECT +INSERT DELAYED INTO t4 VALUES(44); +# Get rid of row in embedded server +DELETE FROM t4 WHERE c1 = 44; +INSERT DELAYED INTO t3 VALUES(33); + let $wait_cmd= SHOW STATUS LIKE 'Not_flushed_delayed_rows'; + let $run= query_get_value($wait_cmd, Value, 1); + while ($run) + { + let $run= query_get_value($wait_cmd, Value, 1); + } +SELECT * FROM t4 ORDER BY c1; +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; +--error ER_DELAYED_INSERT_TABLE_LOCKED, ER_ILLEGAL_HA +INSERT DELAYED INTO t4 VALUES(444); +--error ER_DELAYED_INSERT_TABLE_LOCKED, ER_ILLEGAL_HA +INSERT DELAYED INTO t3 VALUES(333); +SELECT * FROM t4 ORDER BY c1; +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4; +# +--echo # +--echo # Recursive inclusion of merge tables in their union clauses. +--echo # +CREATE TABLE t1 (c1 INT, INDEX(c1)); +CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=LAST; +CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t2,t1) + INSERT_METHOD=LAST; +ALTER TABLE t2 UNION=(t3,t1); +--error ER_ADMIN_WRONG_MRG_TABLE +SELECT * FROM t2; +DROP TABLE t1, t2, t3; + + +# +# Bug#25038 - Waiting TRUNCATE +# +# Show that truncate of child table after use of parent table works. +CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE= MyISAM; +CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2); +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2); +SELECT * FROM t3; +TRUNCATE TABLE t1; +SELECT * FROM t3; +DROP TABLE t1, t2, t3; +# +# Show that truncate of child table waits while parent table is used. +# (test partly borrowed from count_distinct3.) +CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER); +SET @rnd_max= 2147483647; +let $1 = 10; +while ($1) +{ + SET @rnd= RAND(); + SET @id = CAST(@rnd * @rnd_max AS UNSIGNED); + SET @id_rev= @rnd_max - @id; + SET @grp= CAST(127.0 * @rnd AS UNSIGNED); + INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev); + dec $1; +} +set @@read_buffer_size=2*1024*1024; +CREATE TABLE t2 SELECT * FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1; +INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2; +CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER) + ENGINE= MRG_MYISAM UNION= (t1, t2); +SELECT COUNT(*) FROM t1; +SELECT COUNT(*) FROM t2; +SELECT COUNT(*) FROM t3; +connect (con1,localhost,root,,); + # As t3 contains random numbers, results are different from test to test. + # That's okay, because we test only that select doesn't yield an + # error. Note, that --disable_result_log doesn't suppress error output. + --disable_result_log + send SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2 + WHERE a1.id = a2.id GROUP BY a2.grp; +connection default; +sleep 1; +TRUNCATE TABLE t1; + connection con1; + reap; + --enable_result_log + disconnect con1; +connection default; +SELECT COUNT(*) FROM t1; +SELECT COUNT(*) FROM t2; +SELECT COUNT(*) FROM t3; +DROP TABLE t1, t2, t3; + +# +# Bug#25700 - merge base tables get corrupted by optimize/analyze/repair table +# +# Using FLUSH TABLES before REPAIR. +CREATE TABLE t1 (c1 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST; +INSERT INTO t2 VALUES (1); +SELECT * FROM t2; +LOCK TABLES t2 WRITE, t1 WRITE; +FLUSH TABLES; +REPAIR TABLE t1; +CHECK TABLE t1; +REPAIR TABLE t1; +UNLOCK TABLES; +CHECK TABLE t1 EXTENDED; +# +# Not using FLUSH TABLES before REPAIR. +LOCK TABLES t2 WRITE, t1 WRITE; +REPAIR TABLE t1; +CHECK TABLE t1; +REPAIR TABLE t1; +UNLOCK TABLES; +CHECK TABLE t1 EXTENDED; +DROP TABLE t1, t2; + +# +# Bug#26377 - Deadlock with MERGE and FLUSH TABLE +# +CREATE TABLE t1 ( a INT ) ENGINE=MyISAM; +CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1); +# Lock t1 first. This did always work. +LOCK TABLES t1 WRITE, m1 WRITE; +FLUSH TABLE t1; +UNLOCK TABLES; +DROP TABLE m1, t1; +# +CREATE TABLE t1 ( a INT ) ENGINE=MyISAM; +CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1); +# Lock m1 first. This did deadlock. +LOCK TABLES m1 WRITE, t1 WRITE; +FLUSH TABLE t1; +UNLOCK TABLES; +DROP TABLE m1, t1; + +# +# Bug#27660 - Falcon: merge table possible +# +# Normal MyISAM MERGE operation. +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE= MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE= MyISAM; +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE= MRG_MYISAM UNION(t1, t2); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (2, 2); +SELECT * FROM t3; +# Try an unsupported engine. +ALTER TABLE t1 ENGINE= MEMORY; +INSERT INTO t1 VALUES (0, 0); +# Before fixing, this succeeded, but (0, 0) was missing. +--error 1168 +SELECT * FROM t3; +DROP TABLE t1, t2, t3; + +# +# Bug#30275 - Merge tables: flush tables or unlock tables causes server to crash +# +CREATE TABLE t1 (c1 INT, KEY(c1)); +CREATE TABLE t2 (c1 INT, KEY(c1)) ENGINE=MRG_MYISAM UNION=(t1) + INSERT_METHOD=FIRST; +LOCK TABLE t1 WRITE, t2 WRITE; +FLUSH TABLES t2, t1; +OPTIMIZE TABLE t1; +FLUSH TABLES t1; +UNLOCK TABLES; +# +FLUSH TABLES; +INSERT INTO t1 VALUES (1); +LOCK TABLE t1 WRITE, t2 WRITE; +FLUSH TABLES t2, t1; +OPTIMIZE TABLE t1; +FLUSH TABLES t1; +UNLOCK TABLES; +DROP TABLE t1, t2; + +# +# Test derived from test program for +# Bug#30273 - merge tables: Can't lock file (errno: 155) +# +CREATE TABLE t1 (ID INT) ENGINE=MYISAM; +CREATE TABLE m1 (ID INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=FIRST; +INSERT INTO t1 VALUES (); +INSERT INTO m1 VALUES (); +LOCK TABLE t1 WRITE, m1 WRITE; +FLUSH TABLES m1, t1; +OPTIMIZE TABLE t1; +FLUSH TABLES m1, t1; +UNLOCK TABLES; +DROP TABLE t1, m1; + diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 6f5ac70a34b..331663dceb5 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -588,6 +588,7 @@ CREATE TABLE `t2` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; # as the test is about to see erroed queries in binlog +set @sav_binlog_format= @@session.binlog_format; set @@session.binlog_format= mixed; @@ -614,5 +615,42 @@ show master status /* there must be the UPDATE query event */; # cleanup bug#27716 drop table t1, t2; +set @@session.binlog_format= @sav_binlog_format; + +# +# Bug #29136 erred multi-delete on trans table does not rollback +# + +# prepare +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +CREATE TABLE t1 (a int, PRIMARY KEY (a)); +CREATE TABLE t2 (a int, PRIMARY KEY (a)); +CREATE TABLE t3 (a int, PRIMARY KEY (a)) ENGINE=MyISAM; +create trigger trg_del_t3 before delete on t3 for each row insert into t1 values (1); + +insert into t2 values (1),(2); +insert into t3 values (1),(2); +reset master; + +# exec cases B, A - see innodb.test + +# B. send_eof() and send_error() afterward + +--error ER_DUP_ENTRY +delete t3.* from t2,t3 where t2.a=t3.a; + +# check +select count(*) from t1 /* must be 1 */; +select count(*) from t3 /* must be 1 */; + +# cleanup bug#29136 +drop table t1, t2, t3; + +# +# Add further tests from here +# + --echo end of tests diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 6f24d84f4c0..cadab8b3b70 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -576,32 +576,6 @@ select count(*) from t1 where a is null; drop table t1; # -# Bug #8306: TRUNCATE leads to index corruption -# -create table t1 (c1 int, index(c1)); -create table t2 (c1 int, index(c1)) engine=merge union=(t1); -insert into t1 values (1); -# Close all tables. -flush tables; -# Open t2 and (implicitly) t1. -select * from t2; -# Truncate after flush works (unless another threads reopens t2 in between). -flush tables; -truncate table t1; -insert into t1 values (1); -# Close all tables. -flush tables; -# Open t2 and (implicitly) t1. -select * from t2; -# Truncate t1, wich was not recognized as open without the bugfix. -# Now, it should fail with a table-in-use error message. ---error 1105 -truncate table t1; -# The insert used to fail on the crashed table. -insert into t1 values (1); -drop table t1,t2; - -# # bug9188 - Corruption Can't open file: 'table.MYI' (errno: 145) # create table t1 (c1 int, c2 varchar(4) not null default '', @@ -1170,6 +1144,12 @@ SHOW TABLE STATUS LIKE 't1'; #--exec ls -log var/master-data/test/t1.MYI #--exec myisamchk -dvv var/master-data/test/t1.MYI #--exec myisamchk -iev var/master-data/test/t1.MYI +--echo # Enable keys with parallel repair +SET @@myisam_repair_threads=2; +ALTER TABLE t1 DISABLE KEYS; +ALTER TABLE t1 ENABLE KEYS; +SET @@myisam_repair_threads=1; +CHECK TABLE t1 EXTENDED; DROP TABLE t1; --echo End of 5.0 tests diff --git a/mysql-test/t/myisampack.test b/mysql-test/t/myisampack.test new file mode 100644 index 00000000000..6598af6318a --- /dev/null +++ b/mysql-test/t/myisampack.test @@ -0,0 +1,33 @@ +# +# BUG#31277 - myisamchk --unpack corrupts a table +# +CREATE TABLE t1(c1 DOUBLE, c2 DOUBLE, c3 DOUBLE, c4 DOUBLE, c5 DOUBLE, + c6 DOUBLE, c7 DOUBLE, c8 DOUBLE, c9 DOUBLE, a INT PRIMARY KEY); +INSERT INTO t1 VALUES +(-3.31168791059336e-06,-3.19054655887874e-06,-1.06528081684847e-05,-1.227278240089e-06,-1.66718069164799e-06,-2.59038972510885e-06,-2.83145227805303e-06,-4.09678491270648e-07,-2.22610091291797e-06,6), +(0.0030743000272545,2.53222044316438e-05,2.78674650061845e-05,1.95914465544536e-05,1.7347572525984e-05,1.87513810069614e-05,1.69882826885005e-05,2.44449336987598e-05,1.89914629921774e-05,9), +(2.85229319423495e-05,3.05970988282259e-05,3.77161100113133e-05,2.3055238978766e-05,2.08241267364615e-05,2.28009504270553e-05,2.12070165658947e-05,2.84350091565409e-05,2.3366822910704e-05,3), +(0,0,0,0,0,0,0,0,0,12), +(3.24544577570754e-05,3.44619021870993e-05,4.37561613201124e-05,2.57556808726748e-05,2.3195354640561e-05,2.58532400758869e-05,2.34934241667179e-05,3.1621640063232e-05,2.58229982746189e-05,19), +(2.53222044316438e-05,0.00445071933455582,2.97447268116016e-05,2.12379514059868e-05,1.86777776502663e-05,2.0170058676712e-05,1.8946030385445e-05,2.66040037173511e-05,2.09161899668946e-05,20), +(3.03462382611645e-05,3.26517930083994e-05,3.5242025468662e-05,2.53219745106391e-05,2.24384532945004e-05,2.4052346047657e-05,2.23865572957053e-05,3.1634313969082e-05,2.48285463481801e-05,21), +(1.95914465544536e-05,2.12379514059868e-05,2.27808649037128e-05,0.000341724375366877,1.4512761275113e-05,1.56475828693953e-05,1.44372366441415e-05,2.07952121981765e-05,1.61488256935919e-05,28), +(1.7347572525984e-05,1.86777776502663e-05,2.04116907052727e-05,1.4512761275113e-05,0.000432162526082388,1.38116514014465e-05,1.2712914948904e-05,1.82503165178506e-05,1.43043075345922e-05,30), +(1.68339762136661e-05,1.77836497166611e-05,2.36328309295222e-05,1.30183423732016e-05,1.18674654241553e-05,1.32467273128652e-05,1.24581739117775e-05,1.55624190959406e-05,1.33010638508213e-05,31), +(1.89643062824415e-05,2.06997140070717e-05,2.29045490159364e-05,1.57918175731019e-05,1.39864987449492e-05,1.50580274578455e-05,1.45908734129609e-05,1.95329296993327e-05,1.5814709481221e-05,32), +(1.69882826885005e-05,1.8946030385445e-05,2.00820439721439e-05,1.44372366441415e-05,1.2712914948904e-05,1.35209686474184e-05,0.00261563314789896,1.78285095864627e-05,1.46699314500019e-05,34), +(2.0278186540684e-05,2.18923409729654e-05,2.39981539939738e-05,1.71774589459438e-05,1.54654355357383e-05,1.62731485707636e-05,1.49253140625051e-05,2.18229800160297e-05,1.71923561673718e-05,35), +(2.44449336987598e-05,2.66040037173511e-05,2.84860148925308e-05,2.07952121981765e-05,1.82503165178506e-05,1.97667730441441e-05,1.78285095864627e-05,0.00166478601822712,2.0299952103232e-05,36), +(1.89914629921774e-05,2.09161899668946e-05,2.26026841007872e-05,1.61488256935919e-05,1.43043075345922e-05,1.52609063290127e-05,1.46699314500019e-05,2.0299952103232e-05,0.00306670170971682,39), +(0,0,0,0,0,0,0,0,0,41), +(0,0,0,0,0,0,0,0,0,17), +(0,0,0,0,0,0,0,0,0,18), +(2.51880677333017e-05,2.63051795435778e-05,2.79874748974906e-05,2.02888886670845e-05,1.8178636318197e-05,1.91308527003585e-05,1.83260023644133e-05,2.4422300558171e-05,1.96411467520551e-05,44), +(2.22402118719591e-05,2.37546284320705e-05,2.58463051055541e-05,1.83391609130854e-05,1.6300720519646e-05,1.74559091886791e-05,1.63733785575587e-05,2.26616253279828e-05,1.79541237435621e-05,45), +(3.01092775359837e-05,3.23865212934412e-05,4.09444584045994e-05,0,2.15470966302776e-05,2.39082636344032e-05,2.28296706429177e-05,2.9007671511595e-05,2.44201138973326e-05,46); +FLUSH TABLES; +--exec $MYISAMPACK -s $MYSQLTEST_VARDIR/master-data/test/t1 +--exec $MYISAMCHK -srq $MYSQLTEST_VARDIR/master-data/test/t1 +--exec $MYISAMCHK -s --unpack $MYSQLTEST_VARDIR/master-data/test/t1 +CHECK TABLE t1 EXTENDED; +DROP TABLE t1; diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index 31cca441da8..528337da77b 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -282,6 +282,15 @@ remove_file $MYSQLTEST_VARDIR/tmp/bug21412.sql; # --exec $MYSQL test -e "/*! \C latin1 */ select 1;" +# +# Bug#29323 mysql client only accetps ANSI encoded files +# +--write_file $MYSQLTEST_VARDIR/tmp/bug29323.sql +select "This is a file starting with UTF8 BOM 0xEFBBBF"; +EOF +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/bug29323.sql 2>&1 +remove_file $MYSQLTEST_VARDIR/tmp/bug29323.sql; + --echo End of 5.0 tests # @@ -303,7 +312,7 @@ remove_file $MYSQLTEST_VARDIR/tmp/bug21412.sql; # This should fail, with warnings as well --error 1 ---exec $MYSQL --show-warnings test -e "create table t2 (id int) engine=nonexistent" +--exec $MYSQL --show-warnings test -e "create table t2 (id int) engine=nonexistent2" drop tables t1, t2; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index edaf07a64db..1afc105e34e 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -240,6 +240,10 @@ let $c= `select $a=$b`; --echo $c drop table t1; +echo shell> mysqlbinlog std_data/corrupt-relay-bin.000624 > var/tmp/bug31793.sql; +error 1; +exec $MYSQL_BINLOG $MYSQL_TEST_DIR/std_data/corrupt-relay-bin.000624 > $MYSQLTEST_VARDIR/tmp/bug31793.sql; + --echo End of 5.0 tests # diff --git a/mysql-test/t/olap.test b/mysql-test/t/olap.test index 1ac99d9c39f..d1e40024733 100644 --- a/mysql-test/t/olap.test +++ b/mysql-test/t/olap.test @@ -367,3 +367,12 @@ select count(a) from t1 group by null with rollup; drop table t1; --echo ############################################################## +# +# Bug #32558: group by null-returning expression with rollup causes crash +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(0); +SELECT 1 FROM t1 GROUP BY (DATE(NULL)) WITH ROLLUP; +DROP TABLE t1; + +--echo End of 5.0 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 65e78a5e740..f2fed63c833 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -373,6 +373,16 @@ select * from t1 where a = 4; drop table t1; # +# Bug#22351 - handler::index_next_same() call to key_cmp_if_same() +# uses the wrong buffer +# +CREATE TABLE t1 (c1 INT, c2 INT, PRIMARY KEY USING BTREE (c1,c2)) ENGINE=MEMORY + PARTITION BY KEY(c2,c1) PARTITIONS 4; +INSERT INTO t1 VALUES (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6); +SELECT * FROM t1 WHERE c1 = 4; +DROP TABLE t1; + +# # Bug #13438: Engine clause in PARTITION clause causes crash # CREATE TABLE t1 (a int) @@ -1531,4 +1541,19 @@ while ($cnt) --enable_query_log drop table t1; + +# +# Bug #30495: optimize table t1,t2,t3 extended errors +# +CREATE TABLE t1(a int) +PARTITION BY RANGE (a) ( + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (20) +); +--error 1064 +ALTER TABLE t1 OPTIMIZE PARTITION p1 EXTENDED; +--error 1064 +ALTER TABLE t1 ANALYZE PARTITION p1 EXTENDED; +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 1c98a0f8d29..e1037d8bf9c 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2250,9 +2250,9 @@ drop table table_25411_b; # Bug #31866: MySQL Server crashes on SHOW CREATE TRIGGER statement # ---disable-warnings +--disable_warnings DROP TRIGGER IF EXISTS trg; ---enable-warnings +--enable_warnings --error ER_TRG_DOES_NOT_EXIST SHOW CREATE TRIGGER trg; diff --git a/mysql-test/t/type_date.test b/mysql-test/t/type_date.test index b21f21d2f3d..14854406eb8 100644 --- a/mysql-test/t/type_date.test +++ b/mysql-test/t/type_date.test @@ -194,3 +194,13 @@ SET SQL_MODE=DEFAULT; DROP TABLE t1,t2; --echo End of 5.0 tests + +# +# Bug#32021: Using Date 000-00-01 in WHERE causes wrong result +# +create table t1 (a date, primary key (a))engine=memory; +insert into t1 values ('0000-01-01'), ('0000-00-01'), ('0001-01-01'); +select * from t1 where a between '0000-00-01' and '0000-00-02'; +drop table t1; + +--echo End of 5.1 tests diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test index 3a3e8f88f83..a2f12bb495c 100644 --- a/mysql-test/t/user_var.test +++ b/mysql-test/t/user_var.test @@ -237,3 +237,24 @@ select @a:=f2, count(f2) from t1 group by 1 desc; select @a:=f3, count(f3) from t1 group by 1 desc; select @a:=f4, count(f4) from t1 group by 1 desc; drop table t1; + +# +# Bug #32260: User variables in query cause server crash +# +create table t1(a int); +insert into t1 values(5),(4),(4),(3),(2),(2),(2),(1); +set @rownum := 0; +set @rank := 0; +set @prev_score := NULL; +# Disable the result log as we assign a value to a user variable in one part +# of a statement and use the same variable in other part of the same statement, +# so we can get unexpected results. +--disable_result_log +select @rownum := @rownum + 1 as row, + @rank := IF(@prev_score!=a, @rownum, @rank) as rank, + @prev_score := a as score +from t1 order by score desc; +--enable_result_log +drop table t1; + +--echo End of 5.1 tests diff --git a/mysql-test/t/xml.test b/mysql-test/t/xml.test index 6c7d9af1b63..5ca9c7afd76 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -543,4 +543,12 @@ select updatexml(NULL, NULL, 1), updatexml(1, NULL, NULL), updatexml(NULL, 1, NULL); select updatexml(NULL, NULL, NULL); +# +# Bug #32557: order by updatexml causes assertion in filesort +# +CREATE TABLE t1(a INT NOT NULL); +INSERT INTO t1 VALUES (0), (0); +SELECT 1 FROM t1 ORDER BY(UPDATEXML(a, '1', '1')); +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/mysys/my_delete.c b/mysys/my_delete.c index bac3e2513e1..cff00bf7e08 100644 --- a/mysys/my_delete.c +++ b/mysys/my_delete.c @@ -56,16 +56,20 @@ int nt_share_delete(const char *name, myf MyFlags) ulong cnt; DBUG_ENTER("nt_share_delete"); DBUG_PRINT("my",("name %s MyFlags %d", name, MyFlags)); - + for (cnt= GetTickCount(); cnt; cnt--) { sprintf(buf, "%s.%08X.deleted", name, cnt); if (MoveFile(name, buf)) break; - + if ((errno= GetLastError()) == ERROR_ALREADY_EXISTS) continue; - + + /* This happened during tests with MERGE tables. */ + if (errno == ERROR_ACCESS_DENIED) + continue; + DBUG_PRINT("warning", ("Failed to rename %s to %s, errno: %d", name, buf, errno)); break; diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index a81ed925562..7f7be4835a5 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; my_bool can_deadlock= test(data->owner->info->n_cursors); + DBUG_ENTER("wait_for_lock"); if (!in_wait_list) { @@ -431,13 +432,21 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, if the predicate is true. */ if (data->cond == 0) + { + DBUG_PRINT("thr_lock", ("lock granted/aborted")); break; + } if (rc == ETIMEDOUT || rc == ETIME) { + /* purecov: begin inspected */ + DBUG_PRINT("thr_lock", ("lock timed out")); result= THR_LOCK_WAIT_TIMEOUT; break; + /* purecov: end */ } } + DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d", + thread_var->abort, in_wait_list)); if (data->cond || data->type == TL_UNLOCK) { @@ -453,6 +462,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, } else { + DBUG_PRINT("thr_lock", ("lock aborted")); check_locks(data->lock, "aborted wait_for_lock", 0); } } @@ -471,7 +481,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, thread_var->current_mutex= 0; thread_var->current_cond= 0; pthread_mutex_unlock(&thread_var->mutex); - return result; + DBUG_RETURN(result); } @@ -509,7 +519,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, and the read lock is not TL_READ_NO_INSERT */ - DBUG_PRINT("lock",("write locked by thread: 0x%lx", + DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx", lock->write.data->owner->info->thread_id)); if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && @@ -598,10 +608,14 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, { if (lock->write.data->type == TL_WRITE_ONLY) { - /* We are not allowed to get a lock in this case */ - data->type=TL_UNLOCK; - result= THR_LOCK_ABORTED; /* Can't wait for this one */ - goto end; + /* Allow lock owner to bypass TL_WRITE_ONLY. */ + if (!thr_lock_owner_equal(data->owner, lock->write.data->owner)) + { + /* We are not allowed to get a lock in this case */ + data->type=TL_UNLOCK; + result= THR_LOCK_ABORTED; /* Can't wait for this one */ + goto end; + } } /* @@ -631,10 +645,8 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } - /* purecov: begin inspected */ - DBUG_PRINT("lock",("write locked by thread: 0x%lx", + DBUG_PRINT("lock",("write locked 2 by thread: 0x%lx", lock->write.data->owner->info->thread_id)); - /* purecov: end */ } else { @@ -669,7 +681,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, goto end; } } - DBUG_PRINT("lock",("write locked by thread: 0x%lx type: %d", + DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d", lock->read.data->owner->info->thread_id, data->type)); } wait_queue= &lock->write_wait; @@ -683,6 +695,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, lock_owner= lock->read.data ? lock->read.data : lock->write.data; if (lock_owner && lock_owner->owner->info == owner->info) { + DBUG_PRINT("lock",("deadlock")); result= THR_LOCK_DEADLOCK; goto end; } diff --git a/sql/field.cc b/sql/field.cc index 36ba6e6f12c..cd8228305e4 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1394,20 +1394,85 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs, /** + Pack the field into a format suitable for storage and transfer. + + To implement packing functionality, only the virtual function + should be overridden. The other functions are just convenience + functions and hence should not be overridden. + + The value of <code>low_byte_first</code> is dependent on how the + packed data is going to be used: for local use, e.g., temporary + store on disk or in memory, use the native format since that is + faster. For data that is going to be transfered to other machines + (e.g., when writing data to the binary log), data should always be + stored in little-endian format. + + @note The default method for packing fields just copy the raw bytes + of the record into the destination, but never more than + <code>max_length</code> characters. + + @param to + Pointer to memory area where representation of field should be put. + + @param from + Pointer to memory area where record representation of field is + stored. + + @param max_length + Maximum length of the field, as given in the column definition. For + example, for <code>CHAR(1000)</code>, the <code>max_length</code> + is 1000. This information is sometimes needed to decide how to pack + the data. + + @param low_byte_first + @c TRUE if integers should be stored little-endian, @c FALSE if + native format should be used. Note that for little-endian machines, + the value of this flag is a moot point since the native format is + little-endian. +*/ +uchar * +Field::pack(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) +{ + uint32 length= pack_length(); + set_if_smaller(length, max_length); + memcpy(to, from, length); + return to+length; +} + +/** Unpack a field from row data. - This method is used to unpack a field from a master whose size - of the field is less than that of the slave. - + This method is used to unpack a field from a master whose size of + the field is less than that of the slave. + + The <code>param_data</code> parameter is a two-byte integer (stored + in the least significant 16 bits of the unsigned integer) usually + consisting of two parts: the real type in the most significant byte + and a original pack length in the least significant byte. + + The exact layout of the <code>param_data</code> field is given by + the <code>Table_map_log_event::save_field_metadata()</code>. + + This is the default method for unpacking a field. It just copies + the memory block in byte order (of original pack length bytes or + length of field, whichever is smaller). + @param to Destination of the data @param from Source of the data - @param param_data Pack length of the field data + @param param_data Real type and original pack length of the field + data + + @param low_byte_first + If this flag is @c true, all composite entities (e.g., lengths) + should be unpacked in little-endian format; otherwise, the entities + are unpacked in native order. @return New pointer into memory based on from + length of the data */ -const uchar *Field::unpack(uchar* to, - const uchar *from, - uint param_data) +const uchar * +Field::unpack(uchar* to, const uchar *from, uint param_data, + bool low_byte_first __attribute__((unused))) { uint length=pack_length(); int from_type= 0; @@ -1420,19 +1485,18 @@ const uchar *Field::unpack(uchar* to, from_type= (param_data & 0xff00) >> 8U; // real_type. param_data= param_data & 0x00ff; // length. } + + if ((param_data == 0) || + (length == param_data) || + (from_type != real_type())) + { + memcpy(to, from, length); + return from+length; + } + uint len= (param_data && (param_data < length)) ? param_data : length; - /* - If the length is the same, use old unpack method. - If the param_data is 0, use the old unpack method. - This is possible if the table map was generated from a down-level - master or if the data was not available on the master. - If the real_types are not the same, use the old unpack method. - */ - if ((length == param_data) || - (param_data == 0) || - (from_type != real_type())) - return(unpack(to, from)); + memcpy(to, from, param_data > length ? length : len); return from+len; } @@ -2814,10 +2878,15 @@ uint Field_new_decimal::is_equal(Create_field *new_field) @return New pointer into memory based on from + length of the data */ -const uchar *Field_new_decimal::unpack(uchar* to, - const uchar *from, - uint param_data) +const uchar * +Field_new_decimal::unpack(uchar* to, + const uchar *from, + uint param_data, + bool low_byte_first) { + if (param_data == 0) + return Field::unpack(to, from, param_data, low_byte_first); + uint from_precision= (param_data & 0xff00) >> 8U; uint from_decimal= param_data & 0x00ff; uint length=pack_length(); @@ -3959,6 +4028,49 @@ void Field_longlong::sql_type(String &res) const } +/* + Floating-point numbers + */ + +uchar * +Field_real::pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first) +{ + DBUG_ENTER("Field_real::pack"); + DBUG_ASSERT(max_length >= pack_length()); + DBUG_PRINT("debug", ("pack_length(): %u", pack_length())); +#ifdef WORDS_BIGENDIAN + if (low_byte_first != table->s->db_low_byte_first) + { + const uchar *dptr= from + pack_length(); + while (dptr-- > from) + *to++ = *dptr; + DBUG_RETURN(to); + } + else +#endif + DBUG_RETURN(Field::pack(to, from, max_length, low_byte_first)); +} + +const uchar * +Field_real::unpack(uchar *to, const uchar *from, + uint param_data, bool low_byte_first) +{ + DBUG_ENTER("Field_real::unpack"); + DBUG_PRINT("debug", ("pack_length(): %u", pack_length())); +#ifdef WORDS_BIGENDIAN + if (low_byte_first != table->s->db_low_byte_first) + { + const uchar *dptr= from + pack_length(); + while (dptr-- > from) + *to++ = *dptr; + DBUG_RETURN(from + pack_length()); + } + else +#endif + DBUG_RETURN(Field::unpack(to, from, param_data, low_byte_first)); +} + /**************************************************************************** single precision float ****************************************************************************/ @@ -6367,6 +6479,11 @@ int Field_longstr::store_decimal(const my_decimal *d) return store(str.ptr(), str.length(), str.charset()); } +uint32 Field_longstr::max_data_length() const +{ + return field_length + (field_length > 255 ? 2 : 1); +} + double Field_string::val_real(void) { @@ -6511,7 +6628,9 @@ void Field_string::sql_type(String &res) const } -uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) +uchar *Field_string::pack(uchar *to, const uchar *from, + uint max_length, + bool low_byte_first __attribute__((unused))) { uint length= min(field_length,max_length); uint local_char_length= max_length/field_charset->mbmaxlen; @@ -6519,11 +6638,15 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) local_char_length= my_charpos(field_charset, from, from+length, local_char_length); set_if_smaller(length, local_char_length); - while (length && from[length-1] == ' ') + while (length && from[length-1] == field_charset->pad_char) length--; + + // Length always stored little-endian *to++= (uchar) length; if (field_length > 255) *to++= (uchar) (length >> 8); + + // Store the actual bytes of the string memcpy(to, from, length); return to+length; } @@ -6545,34 +6668,27 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) @return New pointer into memory based on from + length of the data */ -const uchar *Field_string::unpack(uchar *to, - const uchar *from, - uint param_data) -{ - uint from_len= param_data & 0x00ff; // length. - uint length= 0; - uint f_length; - f_length= (from_len < field_length) ? from_len : field_length; - DBUG_ASSERT(f_length <= 255); - length= (uint) *from++; - bitmap_set_bit(table->write_set,field_index); - store((const char *)from, length, system_charset_info); - return from+length; -} - - -const uchar *Field_string::unpack(uchar *to, const uchar *from) -{ +const uchar * +Field_string::unpack(uchar *to, + const uchar *from, + uint param_data, + bool low_byte_first __attribute__((unused))) +{ + uint from_length= + param_data ? min(param_data & 0x00ff, field_length) : field_length; uint length; - if (field_length > 255) + + if (from_length > 255) { length= uint2korr(from); from+= 2; } else length= (uint) *from++; - memcpy(to, from, (int) length); - bfill(to+length, field_length - length, ' '); + + memcpy(to, from, length); + // Pad the string with the pad character of the fields charset + bfill(to + length, field_length - length, field_charset->pad_char); return from+length; } @@ -6762,6 +6878,7 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16; int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) { char *ptr= (char *)metadata_ptr; + DBUG_ASSERT(field_length <= 65535); int2store(ptr, field_length); return 2; } @@ -6989,22 +7106,30 @@ uint32 Field_varstring::data_length() Here the number of length bytes are depending on the given max_length */ -uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length) +uchar *Field_varstring::pack(uchar *to, const uchar *from, + uint max_length, + bool low_byte_first __attribute__((unused))) { uint length= length_bytes == 1 ? (uint) *from : uint2korr(from); set_if_smaller(max_length, field_length); if (length > max_length) length=max_length; - *to++= (char) (length & 255); + + /* Length always stored little-endian */ + *to++= length & 0xFF; if (max_length > 255) - *to++= (char) (length >> 8); - if (length) + *to++= (length >> 8) & 0xFF; + + /* Store bytes of string */ + if (length > 0) memcpy(to, from+length_bytes, length); return to+length; } -uchar *Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length) +uchar * +Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length, + bool low_byte_first __attribute__((unused))) { uint length= length_bytes == 1 ? (uint) *key : uint2korr(key); uint local_char_length= ((field_charset->mbmaxlen > 1) ? @@ -7043,8 +7168,9 @@ uchar *Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length) Pointer to end of 'key' (To the next key part if multi-segment key) */ -const uchar *Field_varstring::unpack_key(uchar *to, const uchar *key, - uint max_length) +const uchar * +Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length, + bool low_byte_first __attribute__((unused))) { /* get length of the blob key */ uint32 length= *key++; @@ -7073,8 +7199,9 @@ const uchar *Field_varstring::unpack_key(uchar *to, const uchar *key, end of key storage */ -uchar *Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, - uint max_length) +uchar * +Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) { /* Key length is always stored as 2 bytes */ uint length= uint2korr(from); @@ -7094,6 +7221,9 @@ uchar *Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, This method is used to unpack a varstring field from a master whose size of the field is less than that of the slave. + + @note + The string length is always packed little-endian. @param to Destination of the data @param from Source of the data @@ -7101,9 +7231,10 @@ uchar *Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from, @return New pointer into memory based on from + length of the data */ -const uchar *Field_varstring::unpack(uchar *to, - const uchar *from, - uint param_data) +const uchar * +Field_varstring::unpack(uchar *to, const uchar *from, + uint param_data, + bool low_byte_first __attribute__((unused))) { uint length; uint l_bytes= (param_data && (param_data < field_length)) ? @@ -7115,28 +7246,7 @@ const uchar *Field_varstring::unpack(uchar *to, if (length_bytes == 2) to[1]= 0; } - else - { - length= uint2korr(from); - to[0]= *from++; - to[1]= *from++; - } - if (length) - memcpy(to+ length_bytes, from, length); - return from+length; -} - - -/* - unpack field packed with Field_varstring::pack() -*/ - -const uchar *Field_varstring::unpack(uchar *to, const uchar *from) -{ - uint length; - if (length_bytes == 1) - length= (uint) (*to= *from++); - else + else /* l_bytes == 2 */ { length= uint2korr(from); to[0]= *from++; @@ -7385,9 +7495,9 @@ void Field_blob::store_length(uchar *i_ptr, } -uint32 Field_blob::get_length(const uchar *pos, bool low_byte_first) +uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg, bool low_byte_first) { - switch (packlength) { + switch (packlength_arg) { case 1: return (uint32) pos[0]; case 2: @@ -7818,26 +7928,37 @@ void Field_blob::sql_type(String &res) const } } - -uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) +uchar *Field_blob::pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first) { + DBUG_ENTER("Field_blob::pack"); + DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;" + " max_length: %u; low_byte_first: %d", + (ulong) to, (ulong) from, + max_length, low_byte_first)); + DBUG_DUMP("record", from, table->s->reclength); uchar *save= ptr; ptr= (uchar*) from; uint32 length=get_length(); // Length of from string - if (length > max_length) - { - length=max_length; - store_length(to,packlength,length,TRUE); - } - else - memcpy(to,from,packlength); // Copy length - if (length) + + /* + Store max length, which will occupy packlength bytes. If the max + length given is smaller than the actual length of the blob, we + just store the initial bytes of the blob. + */ + store_length(to, packlength, min(length, max_length), low_byte_first); + + /* + Store the actual blob data, which will occupy 'length' bytes. + */ + if (length > 0) { get_ptr((uchar**) &from); memcpy(to+packlength, from,length); } ptr=save; // Restore org row pointer - return to+packlength+length; + DBUG_DUMP("packed", to, packlength + length); + DBUG_RETURN(to+packlength+length); } @@ -7852,28 +7973,30 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length) @param to Destination of the data @param from Source of the data - @param param_data not used + @param param_data @c TRUE if base types should be stored in little- + endian format, @c FALSE if native format should + be used. @return New pointer into memory based on from + length of the data */ const uchar *Field_blob::unpack(uchar *to, const uchar *from, - uint param_data) -{ - return unpack(to, from); -} - - -const uchar *Field_blob::unpack(uchar *to, const uchar *from) -{ - uint32 length=get_length(from); - memcpy(to,from,packlength); - from+=packlength; - if (length) - memcpy_fixed(to+packlength, &from, sizeof(from)); - else - bzero(to+packlength,sizeof(from)); - return from+length; + uint param_data, + bool low_byte_first) +{ + DBUG_ENTER("Field_blob::unpack"); + DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx;" + " param_data: %u; low_byte_first: %d", + (ulong) to, (ulong) from, param_data, low_byte_first)); + uint const master_packlength= + param_data > 0 ? param_data & 0xFF : packlength; + uint32 const length= get_length(from, master_packlength, low_byte_first); + DBUG_DUMP("packed", from, length + master_packlength); + bitmap_set_bit(table->write_set, field_index); + store(reinterpret_cast<const char*>(from) + master_packlength, + length, field_charset); + DBUG_DUMP("record", to, table->s->reclength); + DBUG_RETURN(from + master_packlength + length); } /* Keys for blobs are like keys on varchars */ @@ -7923,7 +8046,9 @@ int Field_blob::pack_cmp(const uchar *b, uint key_length_arg, /* Create a packed key that will be used for storage from a MySQL row */ -uchar *Field_blob::pack_key(uchar *to, const uchar *from, uint max_length) +uchar * +Field_blob::pack_key(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) { uchar *save= ptr; ptr= (uchar*) from; @@ -7968,8 +8093,9 @@ uchar *Field_blob::pack_key(uchar *to, const uchar *from, uint max_length) Pointer into 'from' past the last byte copied from packed key. */ -const uchar *Field_blob::unpack_key(uchar *to, const uchar *from, - uint max_length) +const uchar * +Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) { /* get length of the blob key */ uint32 length= *from++; @@ -7992,8 +8118,9 @@ const uchar *Field_blob::unpack_key(uchar *to, const uchar *from, /* Create a packed key that will be used for storage from a MySQL key */ -uchar *Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, - uint max_length) +uchar * +Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) { uint length=uint2korr(from); if (length > max_length) @@ -8940,9 +9067,11 @@ void Field_bit::sql_type(String &res) const } -uchar *Field_bit::pack(uchar *to, const uchar *from, uint max_length) +uchar * +Field_bit::pack(uchar *to, const uchar *from, uint max_length, + bool low_byte_first __attribute__((unused))) { - DBUG_ASSERT(max_length); + DBUG_ASSERT(max_length > 0); uint length; if (bit_len > 0) { @@ -8977,28 +9106,44 @@ uchar *Field_bit::pack(uchar *to, const uchar *from, uint max_length) /** Unpack a bit field from row data. - This method is used to unpack a bit field from a master whose size + This method is used to unpack a bit field from a master whose size of the field is less than that of the slave. - + @param to Destination of the data @param from Source of the data @param param_data Bit length (upper) and length (lower) values @return New pointer into memory based on from + length of the data */ -const uchar *Field_bit::unpack(uchar *to, - const uchar *from, - uint param_data) +const uchar * +Field_bit::unpack(uchar *to, const uchar *from, uint param_data, + bool low_byte_first __attribute__((unused))) { uint const from_len= (param_data >> 8U) & 0x00ff; uint const from_bit_len= param_data & 0x00ff; /* - If the master and slave have the same sizes, then use the old - unpack() method. + If the parameter data is zero (i.e., undefined), or if the master + and slave have the same sizes, then use the old unpack() method. */ - if ((from_bit_len == bit_len) && - (from_len == bytes_in_rec)) - return(unpack(to, from)); + if (param_data == 0 || + (from_bit_len == bit_len) && (from_len == bytes_in_rec)) + { + if (bit_len > 0) + { + /* + set_rec_bits is a macro, don't put the post-increment in the + argument since that might cause strange side-effects. + + For the choice of the second argument, see the explanation for + Field_bit::pack(). + */ + set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len); + from++; + } + memcpy(to, from, bytes_in_rec); + return from + bytes_in_rec; + } + /* We are converting a smaller bit field to a larger one here. To do that, we first need to construct a raw value for the original @@ -9026,25 +9171,6 @@ const uchar *Field_bit::unpack(uchar *to, } -const uchar *Field_bit::unpack(uchar *to, const uchar *from) -{ - if (bit_len > 0) - { - /* - set_rec_bits is a macro, don't put the post-increment in the - argument since that might cause strange side-effects. - - For the choice of the second argument, see the explanation for - Field_bit::pack(). - */ - set_rec_bits(*from, bit_ptr + (to - ptr), bit_ofs, bit_len); - from++; - } - memcpy(to, from, bytes_in_rec); - return from + bytes_in_rec; -} - - void Field_bit::set_default() { if (bit_len > 0) diff --git a/sql/field.h b/sql/field.h index d0d867d0b32..4b09f50a59a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -175,6 +175,17 @@ public: */ virtual uint32 data_length() { return pack_length(); } virtual uint32 sort_length() const { return pack_length(); } + + /** + Get the maximum size of the data in packed format. + + @return Maximum data length of the field when packed using the + Field::pack() function. + */ + virtual uint32 max_data_length() const { + return pack_length(); + }; + virtual int reset(void) { bzero(ptr,pack_length()); return 0; } virtual void reset_fields() {} virtual void set_default() @@ -357,32 +368,45 @@ public: return str; } virtual bool send_binary(Protocol *protocol); - virtual uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0) + + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); + /** + @overload Field::pack(uchar*, const uchar*, uint, bool) + */ + uchar *pack(uchar *to, const uchar *from) { - uint32 length=pack_length(); - memcpy(to,from,length); - return to+length; + DBUG_ENTER("Field::pack"); + uchar *result= this->pack(to, from, UINT_MAX, table->s->db_low_byte_first); + DBUG_RETURN(result); } - virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); - virtual const uchar *unpack(uchar* to, const uchar *from) + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first); + /** + @overload Field::unpack(uchar*, const uchar*, uint, bool) + */ + const uchar *unpack(uchar* to, const uchar *from) { - uint length=pack_length(); - memcpy(to,from,length); - return from+length; + DBUG_ENTER("Field::unpack"); + const uchar *result= unpack(to, from, 0U, table->s->db_low_byte_first); + DBUG_RETURN(result); } - virtual uchar *pack_key(uchar* to, const uchar *from, uint max_length) + + virtual uchar *pack_key(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) { - return pack(to,from,max_length); + return pack(to, from, max_length, low_byte_first); } virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length) + uint max_length, bool low_byte_first) { - return pack(to,from,max_length); + return pack(to, from, max_length, low_byte_first); } virtual const uchar *unpack_key(uchar* to, const uchar *from, - uint max_length) + uint max_length, bool low_byte_first) { - return unpack(to,from); + return unpack(to, from, max_length, low_byte_first); } virtual uint packed_col_length(const uchar *to, uint length) { return length;} @@ -567,6 +591,7 @@ public: {} int store_decimal(const my_decimal *d); + uint32 max_data_length() const; }; /* base class for float and double and decimal (old one) */ @@ -587,6 +612,10 @@ public: int truncate(double *nr, double max_length); uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first); + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first); }; @@ -615,6 +644,16 @@ public: void overflow(bool negative); bool zero_pack() const { return 0; } void sql_type(String &str) const; + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + return Field::unpack(to, from, param_data, low_byte_first); + } + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + return Field::pack(to, from, max_length, low_byte_first); + } }; @@ -665,7 +704,8 @@ public: uint row_pack_length() { return pack_length(); } int compatible_field_size(uint field_metadata); uint is_equal(Create_field *new_field); - virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first); }; @@ -696,6 +736,20 @@ public: uint32 pack_length() const { return 1; } void sql_type(String &str) const; uint32 max_display_length() { return 4; } + + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + *to= *from; + return to + 1; + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + *to= *from; + return from + 1; + } }; @@ -731,8 +785,47 @@ public: uint32 pack_length() const { return 2; } void sql_type(String &str) const; uint32 max_display_length() { return 6; } -}; + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + int16 val; +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + val = sint2korr(from); + else +#endif + shortget(val, from); + +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + int2store(to, val); + else +#endif + shortstore(to, val); + return to + sizeof(val); + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + int16 val; +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + val = sint2korr(from); + else +#endif + shortget(val, from); + +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + int2store(to, val); + else +#endif + shortstore(to, val); + return from + sizeof(val); + } +}; class Field_medium :public Field_num { public: @@ -761,6 +854,18 @@ public: uint32 pack_length() const { return 3; } void sql_type(String &str) const; uint32 max_display_length() { return 8; } + + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + return Field::pack(to, from, max_length, low_byte_first); + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + return Field::unpack(to, from, param_data, low_byte_first); + } }; @@ -796,6 +901,45 @@ public: uint32 pack_length() const { return 4; } void sql_type(String &str) const; uint32 max_display_length() { return MY_INT32_NUM_DECIMAL_DIGITS; } + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + int32 val; +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + val = sint4korr(from); + else +#endif + longget(val, from); + +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + int4store(to, val); + else +#endif + longstore(to, val); + return to + sizeof(val); + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + int32 val; +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + val = sint4korr(from); + else +#endif + longget(val, from); + +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + int4store(to, val); + else +#endif + longstore(to, val); + return from + sizeof(val); + } }; @@ -838,6 +982,45 @@ public: void sql_type(String &str) const; bool can_be_compared_as_longlong() const { return TRUE; } uint32 max_display_length() { return 20; } + virtual uchar *pack(uchar* to, const uchar *from, + uint max_length, bool low_byte_first) + { + int64 val; +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + val = sint8korr(from); + else +#endif + longlongget(val, from); + +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + int8store(to, val); + else +#endif + longlongstore(to, val); + return to + sizeof(val); + } + + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first) + { + int64 val; +#ifdef WORDS_BIGENDIAN + if (low_byte_first) + val = sint8korr(from); + else +#endif + longlongget(val, from); + +#ifdef WORDS_BIGENDIAN + if (table->s->db_low_byte_first) + int8store(to, val); + else +#endif + longlongstore(to, val); + return from + sizeof(val); + } }; #endif @@ -1218,9 +1401,10 @@ public: int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); void sql_type(String &str) const; - uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0); - virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); - const uchar *unpack(uchar* to, const uchar *from); + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first); uint pack_length_from_metadata(uint field_metadata) { return (field_metadata & 0x00ff); } uint row_pack_length() { return (field_length + 1); } @@ -1298,13 +1482,15 @@ public: uint get_key_image(uchar *buff,uint length, imagetype type); void set_key_image(const uchar *buff,uint length); void sql_type(String &str) const; - uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0); - uchar *pack_key(uchar *to, const uchar *from, uint max_length); + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); + uchar *pack_key(uchar *to, const uchar *from, uint max_length, bool low_byte_first); uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length); - virtual const uchar *unpack(uchar* to, const uchar *from, uint param_data); - const uchar *unpack(uchar* to, const uchar *from); - const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); + uint max_length, bool low_byte_first); + virtual const uchar *unpack(uchar* to, const uchar *from, + uint param_data, bool low_byte_first); + const uchar *unpack_key(uchar* to, const uchar *from, + uint max_length, bool low_byte_first); int pack_cmp(const uchar *a, const uchar *b, uint key_length, my_bool insert_or_update); int pack_cmp(const uchar *b, uint key_length,my_bool insert_or_update); @@ -1397,7 +1583,7 @@ public: { return (uint32) (packlength); } uint row_pack_length() { return pack_length_no_ptr(); } uint32 sort_length() const; - inline uint32 max_data_length() const + virtual uint32 max_data_length() const { return (uint32) (((ulonglong) 1 << (packlength*8)) -1); } @@ -1425,13 +1611,13 @@ public: @returns The length in the row plus the size of the data. */ uint32 get_packed_size(const uchar *ptr_arg, bool low_byte_first) - {return packlength + get_length(ptr_arg, low_byte_first);} + {return packlength + get_length(ptr_arg, packlength, low_byte_first);} inline uint32 get_length(uint row_offset= 0) - { return get_length(ptr+row_offset, table->s->db_low_byte_first); } - uint32 get_length(const uchar *ptr, bool low_byte_first); + { return get_length(ptr+row_offset, this->packlength, table->s->db_low_byte_first); } + uint32 get_length(const uchar *ptr, uint packlength, bool low_byte_first); uint32 get_length(const uchar *ptr_arg) - { return get_length(ptr_arg, table->s->db_low_byte_first); } + { return get_length(ptr_arg, this->packlength, table->s->db_low_byte_first); } void put_length(uchar *pos, uint32 length); inline void get_ptr(uchar **str) { @@ -1472,13 +1658,16 @@ public: memcpy_fixed(ptr+packlength,&tmp,sizeof(char*)); return 0; } - uchar *pack(uchar *to, const uchar *from, uint max_length= ~(uint) 0); - uchar *pack_key(uchar *to, const uchar *from, uint max_length); + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); + uchar *pack_key(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); uchar *pack_key_from_key_image(uchar* to, const uchar *from, - uint max_length); - virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); - const uchar *unpack(uchar *to, const uchar *from); - const uchar *unpack_key(uchar* to, const uchar *from, uint max_length); + uint max_length, bool low_byte_first); + virtual const uchar *unpack(uchar *to, const uchar *from, + uint param_data, bool low_byte_first); + const uchar *unpack_key(uchar* to, const uchar *from, + uint max_length, bool low_byte_first); int pack_cmp(const uchar *a, const uchar *b, uint key_length, my_bool insert_or_update); int pack_cmp(const uchar *b, uint key_length,my_bool insert_or_update); @@ -1630,6 +1819,7 @@ public: enum_field_types type() const { return MYSQL_TYPE_BIT; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } uint32 key_length() const { return (uint32) (field_length + 7) / 8; } + uint32 max_data_length() const { return (field_length + 7) / 8; } uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } Item_result result_type () const { return INT_RESULT; } @@ -1671,9 +1861,10 @@ public: { return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); } int compatible_field_size(uint field_metadata); void sql_type(String &str) const; - uchar *pack(uchar *to, const uchar *from, uint max_length=~(uint) 0); - virtual const uchar *unpack(uchar *to, const uchar *from, uint param_data); - const uchar *unpack(uchar* to, const uchar *from); + virtual uchar *pack(uchar *to, const uchar *from, + uint max_length, bool low_byte_first); + virtual const uchar *unpack(uchar *to, const uchar *from, + uint param_data, bool low_byte_first); virtual void set_default(); Field *new_key_field(MEM_ROOT *root, struct st_table *new_table, diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 55af0c38aed..be75eff2575 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -321,7 +321,7 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, DBUG_ENTER("ndbcluster_binlog_open_table"); safe_mutex_assert_owner(&LOCK_open); - init_tmp_table_share(table_share, share->db, 0, share->table_name, + init_tmp_table_share(thd, table_share, share->db, 0, share->table_name, share->key); if ((error= open_table_def(thd, table_share, 0))) { diff --git a/sql/handler.cc b/sql/handler.cc index 8a2355c8a87..a4926071598 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2643,7 +2643,7 @@ int ha_create_table(THD *thd, const char *path, TABLE_SHARE share; DBUG_ENTER("ha_create_table"); - init_tmp_table_share(&share, db, 0, table_name, path); + init_tmp_table_share(thd, &share, db, 0, table_name, path); if (open_table_def(thd, &share, 0) || open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table, TRUE)) @@ -2709,7 +2709,7 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name) if (error) DBUG_RETURN(2); - init_tmp_table_share(&share, db, 0, name, path); + init_tmp_table_share(thd, &share, db, 0, name, path); if (open_table_def(thd, &share, 0)) { DBUG_RETURN(3); @@ -3717,11 +3717,12 @@ int handler::ha_reset() int handler::ha_write_row(uchar *buf) { int error; + DBUG_ENTER("handler::ha_write_row"); if (unlikely(error= write_row(buf))) - return error; + DBUG_RETURN(error); if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf))) - return error; - return 0; + DBUG_RETURN(error); /* purecov: inspected */ + DBUG_RETURN(0); } diff --git a/sql/item.h b/sql/item.h index 6990d9ed021..379eb8a24be 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2620,8 +2620,20 @@ class Item_cache: public Item protected: Item *example; table_map used_table_map; + enum enum_field_types cached_field_type; public: - Item_cache(): example(0), used_table_map(0) {fixed= 1; null_value= 1;} + Item_cache(): + example(0), used_table_map(0), cached_field_type(MYSQL_TYPE_STRING) + { + fixed= 1; + null_value= 1; + } + Item_cache(enum_field_types field_type_arg): + example(0), used_table_map(0), cached_field_type(field_type_arg) + { + fixed= 1; + null_value= 1; + } void set_used_tables(table_map map) { used_table_map= map; } @@ -2637,6 +2649,7 @@ public: }; virtual void store(Item *)= 0; enum Type type() const { return CACHE_ITEM; } + enum_field_types field_type() const { return cached_field_type; } static Item_cache* get_cache(const Item *item); table_map used_tables() const { return used_table_map; } virtual void keep_array() {} @@ -2652,6 +2665,8 @@ protected: longlong value; public: Item_cache_int(): Item_cache(), value(0) {} + Item_cache_int(enum_field_types field_type_arg): + Item_cache(field_type_arg), value(0) {} void store(Item *item); void store(Item *item, longlong val_arg); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 639788d65bc..633db8367c1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -933,12 +933,15 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, { value= item->val_int(); *is_null= item->null_value; + enum_field_types f_type= item->field_type(); /* Item_date_add_interval may return MYSQL_TYPE_STRING as the result field type. To detect that the DATE value has been returned we - compare it with 1000000L - any DATE value should be less than it. + compare it with 100000000L - any DATE value should be less than it. + Don't shift cached DATETIME values up for the second time. */ - if (item->field_type() == MYSQL_TYPE_DATE || value < 100000000L) + if (f_type == MYSQL_TYPE_DATE || + (f_type != MYSQL_TYPE_DATETIME && value < 100000000L)) value*= 1000000L; } else @@ -975,7 +978,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, if (item->const_item() && cache_arg && (item->type() != Item::FUNC_ITEM || ((Item_func*)item)->functype() != Item_func::GUSERVAR_FUNC)) { - Item_cache_int *cache= new Item_cache_int(); + Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME); /* Mark the cache as non-const to prevent re-caching. */ cache->set_used_tables(1); cache->store(item, value); @@ -1691,26 +1694,29 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const void Item_func_interval::fix_length_and_dec() { + uint rows= row->cols(); + use_decimal_comparison= ((row->element_index(0)->result_type() == DECIMAL_RESULT) || (row->element_index(0)->result_type() == INT_RESULT)); - if (row->cols() > 8) + if (rows > 8) { - bool consts=1; + bool not_null_consts= TRUE; - for (uint i=1 ; consts && i < row->cols() ; i++) + for (uint i= 1; not_null_consts && i < rows; i++) { - consts&= row->element_index(i)->const_item(); + Item *el= row->element_index(i); + not_null_consts&= el->const_item() & !el->is_null(); } - if (consts && + if (not_null_consts && (intervals= - (interval_range*) sql_alloc(sizeof(interval_range)*(row->cols()-1)))) + (interval_range*) sql_alloc(sizeof(interval_range) * (rows - 1)))) { if (use_decimal_comparison) { - for (uint i=1 ; i < row->cols(); i++) + for (uint i= 1; i < rows; i++) { Item *el= row->element_index(i); interval_range *range= intervals + (i-1); @@ -1735,7 +1741,7 @@ void Item_func_interval::fix_length_and_dec() } else { - for (uint i=1 ; i < row->cols(); i++) + for (uint i= 1; i < rows; i++) { intervals[i-1].dbl= row->element_index(i)->val_real(); } @@ -1826,12 +1832,22 @@ longlong Item_func_interval::val_int() ((el->result_type() == DECIMAL_RESULT) || (el->result_type() == INT_RESULT))) { - my_decimal e_dec_buf, *e_dec= row->element_index(i)->val_decimal(&e_dec_buf); + my_decimal e_dec_buf, *e_dec= el->val_decimal(&e_dec_buf); + /* Skip NULL ranges. */ + if (el->null_value) + continue; if (my_decimal_cmp(e_dec, dec) > 0) - return i-1; + return i - 1; + } + else + { + double val= el->val_real(); + /* Skip NULL ranges. */ + if (el->null_value) + continue; + if (val > value) + return i - 1; } - else if (row->element_index(i)->val_real() > value) - return i-1; } return i-1; } @@ -4394,6 +4410,51 @@ void Item_func_like::cleanup() #ifdef USE_REGEX bool +Item_func_regex::regcomp(bool send_error) +{ + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff),&my_charset_bin); + String *res= args[1]->val_str(&tmp); + int error; + + if (args[1]->null_value) + return TRUE; + + if (regex_compiled) + { + if (!stringcmp(res, &prev_regexp)) + return FALSE; + prev_regexp.copy(*res); + my_regfree(&preg); + regex_compiled= 0; + } + + if (cmp_collation.collation != regex_lib_charset) + { + /* Convert UCS2 strings to UTF8 */ + uint dummy_errors; + if (conv.copy(res->ptr(), res->length(), res->charset(), + regex_lib_charset, &dummy_errors)) + return TRUE; + res= &conv; + } + + if ((error= my_regcomp(&preg, res->c_ptr_safe(), + regex_lib_flags, regex_lib_charset))) + { + if (send_error) + { + (void) my_regerror(error, &preg, buff, sizeof(buff)); + my_error(ER_REGEXP_ERROR, MYF(0), buff); + } + return TRUE; + } + regex_compiled= 1; + return FALSE; +} + + +bool Item_func_regex::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); @@ -4409,35 +4470,34 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) if (agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV, 1)) return TRUE; + regex_lib_flags= (cmp_collation.collation->state & + (MY_CS_BINSORT | MY_CS_CSSORT)) ? + REG_EXTENDED | REG_NOSUB : + REG_EXTENDED | REG_NOSUB | REG_ICASE; + /* + If the case of UCS2 and other non-ASCII character sets, + we will convert patterns and strings to UTF8. + */ + regex_lib_charset= (cmp_collation.collation->mbminlen > 1) ? + &my_charset_utf8_general_ci : + cmp_collation.collation; + used_tables_cache=args[0]->used_tables() | args[1]->used_tables(); not_null_tables_cache= (args[0]->not_null_tables() | args[1]->not_null_tables()); const_item_cache=args[0]->const_item() && args[1]->const_item(); if (!regex_compiled && args[1]->const_item()) { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff),&my_charset_bin); - String *res=args[1]->val_str(&tmp); if (args[1]->null_value) { // Will always return NULL maybe_null=1; fixed= 1; return FALSE; } - int error; - if ((error= my_regcomp(&preg,res->c_ptr(), - ((cmp_collation.collation->state & - (MY_CS_BINSORT | MY_CS_CSSORT)) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE), - cmp_collation.collation))) - { - (void) my_regerror(error,&preg,buff,sizeof(buff)); - my_error(ER_REGEXP_ERROR, MYF(0), buff); + if (regcomp(TRUE)) return TRUE; - } - regex_compiled=regex_is_const=1; - maybe_null=args[0]->maybe_null; + regex_is_const= 1; + maybe_null= args[0]->maybe_null; } else maybe_null=1; @@ -4450,47 +4510,25 @@ longlong Item_func_regex::val_int() { DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH]; - String *res, tmp(buff,sizeof(buff),&my_charset_bin); + String tmp(buff,sizeof(buff),&my_charset_bin); + String *res= args[0]->val_str(&tmp); - res=args[0]->val_str(&tmp); - if (args[0]->null_value) - { - null_value=1; + if ((null_value= (args[0]->null_value || + (!regex_is_const && regcomp(FALSE))))) return 0; - } - if (!regex_is_const) - { - char buff2[MAX_FIELD_WIDTH]; - String *res2, tmp2(buff2,sizeof(buff2),&my_charset_bin); - res2= args[1]->val_str(&tmp2); - if (args[1]->null_value) + if (cmp_collation.collation != regex_lib_charset) + { + /* Convert UCS2 strings to UTF8 */ + uint dummy_errors; + if (conv.copy(res->ptr(), res->length(), res->charset(), + regex_lib_charset, &dummy_errors)) { - null_value=1; + null_value= 1; return 0; } - if (!regex_compiled || stringcmp(res2,&prev_regexp)) - { - prev_regexp.copy(*res2); - if (regex_compiled) - { - my_regfree(&preg); - regex_compiled=0; - } - if (my_regcomp(&preg,res2->c_ptr_safe(), - ((cmp_collation.collation->state & - (MY_CS_BINSORT | MY_CS_CSSORT)) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE), - cmp_collation.collation)) - { - null_value=1; - return 0; - } - regex_compiled=1; - } + res= &conv; } - null_value=0; return my_regexec(&preg,res->c_ptr_safe(),0,(my_regmatch_t*) 0,0) ? 0 : 1; } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index e9aeef7fc3e..8df0e1af331 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1382,6 +1382,10 @@ class Item_func_regex :public Item_bool_func bool regex_is_const; String prev_regexp; DTCollation cmp_collation; + CHARSET_INFO *regex_lib_charset; + int regex_lib_flags; + String conv; + bool regcomp(bool send_error); public: Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b), regex_compiled(0),regex_is_const(0) {} diff --git a/sql/item_func.cc b/sql/item_func.cc index 809319d9056..3b108812127 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3843,7 +3843,8 @@ Item_func_set_user_var::fix_length_and_dec() bool Item_func_set_user_var::register_field_in_read_map(uchar *arg) { TABLE *table= (TABLE *) arg; - if (result_field->table == table || !table) + if (result_field && + (!table || result_field->table == table)) bitmap_set_bit(result_field->table->read_set, result_field->field_index); return 0; } diff --git a/sql/item_func.h b/sql/item_func.h index 4d7c96a18c1..629d0dbfdbc 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -789,7 +789,7 @@ public: max_length= args[0]->max_length; decimals=args[0]->decimals; /* The item could be a NULL constant. */ - null_value= args[0]->null_value; + null_value= args[0]->is_null(); } }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 1a6c15a4d2e..68d85418324 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -2612,35 +2612,27 @@ typedef struct uint level; String *pxml; // parsed XML uint pos[MAX_LEVEL]; // Tag position stack + uint parent; // Offset of the parent of the current node } MY_XML_USER_DATA; -/* - Find the parent node - - SYNOPSYS - Find the parent node, i.e. a tag or attrubute node on the given level. - - RETURN - 1 - success - 0 - failure -*/ -static uint xml_parent_tag(MY_XML_NODE *items, uint nitems, uint level) +static bool +append_node(String *str, MY_XML_NODE *node) { - if (!nitems) - return 0; - - MY_XML_NODE *p, *last= &items[nitems-1]; - for (p= last; p >= items; p--) - { - if (p->level == level && - (p->type == MY_XML_NODE_TAG || - p->type == MY_XML_NODE_ATTR)) - { - return p - items; - } - } - return 0; + /* + If "str" doesn't have space for a new node, + it will allocate two times more space that it has had so far. + (2*len+512) is a heuristic value, + which gave the best performance during tests. + The ideas behind this formula are: + - It allows to have a very small number of reallocs: + about 10 reallocs on a 1Mb-long XML value. + - At the same time, it avoids excessive memory use. + */ + if (str->reserve(sizeof(MY_XML_NODE), 2 * str->length() + 512)) + return TRUE; + str->q_append((const char*) node, sizeof(MY_XML_NODE)); + return FALSE; } @@ -2662,19 +2654,17 @@ extern "C" int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len); int xml_enter(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; - MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr(); uint numnodes= data->pxml->length() / sizeof(MY_XML_NODE); - uint parent= xml_parent_tag(nodes, numnodes, data->level - 1); MY_XML_NODE node; + node.parent= data->parent; // Set parent for the new node to old parent + data->parent= numnodes; // Remember current node as new parent data->pos[data->level]= numnodes; node.level= data->level++; node.type= st->current_node_type; // TAG or ATTR node.beg= attr; node.end= attr + len; - node.parent= parent; - data->pxml->append((const char*) &node, sizeof(MY_XML_NODE)); - return MY_XML_OK; + return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK; } @@ -2695,18 +2685,14 @@ extern "C" int xml_value(MY_XML_PARSER *st,const char *attr, size_t len); int xml_value(MY_XML_PARSER *st,const char *attr, size_t len) { MY_XML_USER_DATA *data= (MY_XML_USER_DATA*)st->user_data; - MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr(); - uint numnodes= data->pxml->length() / sizeof(MY_XML_NODE); - uint parent= xml_parent_tag(nodes, numnodes, data->level - 1); MY_XML_NODE node; + node.parent= data->parent; // Set parent for the new text node to old parent node.level= data->level; node.type= MY_XML_NODE_TEXT; node.beg= attr; node.end= attr + len; - node.parent= parent; - data->pxml->append((const char*) &node, sizeof(MY_XML_NODE)); - return MY_XML_OK; + return append_node(data->pxml, &node) ? MY_XML_ERROR : MY_XML_OK; } @@ -2731,6 +2717,7 @@ int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len) data->level--; MY_XML_NODE *nodes= (MY_XML_NODE*) data->pxml->ptr(); + data->parent= nodes[data->parent].parent; nodes+= data->pos[data->level]; nodes->tagend= st->cur; @@ -2761,6 +2748,7 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf) p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION; user_data.level= 0; user_data.pxml= parsed_xml_buf; + user_data.parent= 0; my_xml_set_enter_handler(&p, xml_enter); my_xml_set_value_handler(&p, xml_value); my_xml_set_leave_handler(&p, xml_leave); diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index 278c98baf7c..dadbb5ccf42 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -28,8 +28,16 @@ protected: String tmp_value, pxml; Item *nodeset_func; public: - Item_xml_str_func(Item *a, Item *b): Item_str_func(a,b) {} - Item_xml_str_func(Item *a, Item *b, Item *c): Item_str_func(a,b,c) {} + Item_xml_str_func(Item *a, Item *b): + Item_str_func(a,b) + { + maybe_null= TRUE; + } + Item_xml_str_func(Item *a, Item *b, Item *c): + Item_str_func(a,b,c) + { + maybe_null= TRUE; + } void fix_length_and_dec(); String *parse_xml(String *raw_xml, String *parsed_xml_buf); }; diff --git a/sql/log.cc b/sql/log.cc index 688ed03d5d1..9fdede9ef2c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2158,13 +2158,9 @@ const char *MYSQL_LOG::generate_name(const char *log_name, { if (!log_name || !log_name[0]) { - /* - TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. - */ - strmake(buff, pidfile_name, FN_REFLEN - 5); - strmov(fn_ext(buff), suffix); - return (const char *)buff; + strmake(buff, pidfile_name, FN_REFLEN - strlen(suffix) - 1); + return (const char *) + fn_format(buff, buff, "", suffix, MYF(MY_REPLACE_EXT|MY_REPLACE_DIR)); } // get rid of extension if the log is binary to avoid problems if (strip_ext) @@ -3569,9 +3565,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) (!binlog_filter->db_ok(local_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); - DBUG_PRINT("info",("OPTION_BIN_LOG is %s, db_ok('%s') == %d", - (thd->options & OPTION_BIN_LOG) ? "set" : "clear", - local_db, binlog_filter->db_ok(local_db))); DBUG_RETURN(0); } #endif /* HAVE_REPLICATION */ diff --git a/sql/log.h b/sql/log.h index bef0101c8b5..20a1b7e8e6d 100644 --- a/sql/log.h +++ b/sql/log.h @@ -130,7 +130,13 @@ typedef struct st_log_info my_off_t pos; bool fatal; // if the purge happens to give us a negative offset pthread_mutex_t lock; - st_log_info():fatal(0) { pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);} + st_log_info() + : index_file_offset(0), index_file_start_offset(0), + pos(0), fatal(0) + { + log_file_name[0] = '\0'; + pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST); + } ~st_log_info() { pthread_mutex_destroy(&lock);} } LOG_INFO; diff --git a/sql/log_event.cc b/sql/log_event.cc index a6d07e72033..2b3037aedcc 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -36,6 +36,64 @@ #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) && !defined(DBUG_OFF) && !defined(_lint) +static const char *HA_ERR(int i) +{ + switch (i) { + case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND"; + case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY"; + case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED"; + case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX"; + case HA_ERR_CRASHED: return "HA_ERR_CRASHED"; + case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD"; + case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM"; + case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE"; + case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND"; + case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE"; + case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD"; + case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED"; + case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL"; + case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL"; + case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE"; + case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED"; + case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW"; + case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION"; + case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE"; + case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET"; + case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF"; + case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR"; + case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE"; + case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT"; + case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL"; + case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION"; + case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK"; + case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN"; + case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW"; + case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED"; + case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT"; + case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE"; + case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE"; + case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST"; + case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION"; + case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL"; + case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED"; + case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND"; + case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED"; + case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK"; + case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY"; + case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE"; + case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY"; + case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED"; + case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE"; + case HA_ERR_GENERIC: return "HA_ERR_GENERIC"; + case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME"; + case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE"; + case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT"; + } + return "<unknown error>"; +} +#endif + /* Cache that will automatically be written to a dedicated file on destruction. @@ -114,6 +172,9 @@ private: flag_set m_flags; }; +#ifndef DBUG_OFF +uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation +#endif /* pretty_print_str() @@ -555,8 +616,32 @@ int Log_event::do_update_pos(Relay_log_info *rli) Matz: I don't think we will need this check with this refactoring. */ if (rli) - rli->stmt_done(log_pos, when); - + { + /* + bug#29309 simulation: resetting the flag to force + wrong behaviour of artificial event to update + rli->last_master_timestamp for only one time - + the first FLUSH LOGS in the test. + */ + DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp", + if (debug_not_change_ts_if_art_event == 1 + && is_artificial_event()) + { + debug_not_change_ts_if_art_event= 0; + }); +#ifndef DBUG_OFF + rli->stmt_done(log_pos, + is_artificial_event() && + debug_not_change_ts_if_art_event > 0 ? 0 : when); +#else + rli->stmt_done(log_pos, is_artificial_event()? 0 : when); +#endif + DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp", + if (debug_not_change_ts_if_art_event == 0) + { + debug_not_change_ts_if_art_event= 2; + }); + } return 0; // Cannot fail currently } @@ -570,7 +655,8 @@ Log_event::do_shall_skip(Relay_log_info *rli) (ulong) server_id, (ulong) ::server_id, rli->replicate_same_server_id, rli->slave_skip_counter)); - if (server_id == ::server_id && !rli->replicate_same_server_id) + if (server_id == ::server_id && !rli->replicate_same_server_id || + rli->slave_skip_counter == 1 && rli->is_in_group()) return EVENT_SKIP_IGNORE; else if (rli->slave_skip_counter > 0) return EVENT_SKIP_COUNT; @@ -1227,6 +1313,16 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts) #endif /* MYSQL_CLIENT */ +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) +inline Log_event::enum_skip_reason +Log_event::continue_group(Relay_log_info *rli) +{ + if (rli->slave_skip_counter == 1) + return Log_event::EVENT_SKIP_IGNORE; + return Log_event::do_shall_skip(rli); +} +#endif + /************************************************************************** Query_log_event methods **************************************************************************/ @@ -1290,6 +1386,11 @@ static void write_str_with_code_and_len(char **dst, const char *src, bool Query_log_event::write(IO_CACHE* file) { + /** + @todo if catalog can be of length FN_REFLEN==512, then we are not + replicating it correctly, since the length is stored in a byte + /sven + */ uchar buf[QUERY_HEADER_LEN+ 1+4+ // code of flags2 and flags2 1+8+ // code of sql_mode and sql_mode @@ -1516,6 +1617,10 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, time(&end_time); exec_time = (ulong) (end_time - thd_arg->start_time); + /** + @todo this means that if we have no catalog, then it is replicated + as an existing catalog of length zero. is that safe? /sven + */ catalog_len = (catalog) ? (uint32) strlen(catalog) : 0; /* status_vars_len is set just before writing the event */ db_len = (db) ? (uint32) strlen(db) : 0; @@ -1525,7 +1630,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, /* If we don't use flags2 for anything else than options contained in thd_arg->options, it would be more efficient to flags2=thd_arg->options - (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time). + (OPTIONS_WRITTEN_TO_BIN_LOG would be used only at reading time). But it's likely that we don't want to use 32 bits for 3 bits; in the future we will probably want to reclaim the 29 bits. So we need the &. */ @@ -1556,18 +1661,48 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, /* 2 utility functions for the next method */ -/* - Get the pointer for a string (src) that contains the length in - the first byte. Set the output string (dst) to the string value - and place the length of the string in the byte after the string. +/** + Read a string with length from memory. + + This function reads the string-with-length stored at + <code>src</code> and extract the length into <code>*len</code> and + a pointer to the start of the string into <code>*dst</code>. The + string can then be copied using <code>memcpy()</code> with the + number of bytes given in <code>*len</code>. + + @param src Pointer to variable holding a pointer to the memory to + read the string from. + @param dst Pointer to variable holding a pointer where the actual + string starts. Starting from this position, the string + can be copied using @c memcpy(). + @param len Pointer to variable where the length will be stored. + @param end One-past-the-end of the memory where the string is + stored. + + @return Zero if the entire string can be copied successfully, + @c UINT_MAX if the length could not be read from memory + (that is, if <code>*src >= end</code>), otherwise the + number of bytes that are missing to read the full + string, which happends <code>*dst + *len >= end</code>. */ -static void get_str_len_and_pointer(const Log_event::Byte **src, - const char **dst, - uint *len) -{ - if ((*len= **src)) - *dst= (char *)*src + 1; // Will be copied later - (*src)+= *len + 1; +static int +get_str_len_and_pointer(const Log_event::Byte **src, + const char **dst, + uint *len, + const Log_event::Byte *end) +{ + if (*src >= end) + return -1; // Will be UINT_MAX in two-complement arithmetics + uint length= **src; + if (length > 0) + { + if (*src + length >= end) + return *src + length - end + 1; // Number of bytes missing + *dst= (char *)*src + 1; // Will be copied later + } + *len= length; + *src+= length + 1; + return 0; } static void copy_str_and_move(const char **src, @@ -1580,6 +1715,46 @@ static void copy_str_and_move(const char **src, *(*dst)++= 0; } + +#ifndef DBUG_OFF +static char const * +code_name(int code) +{ + static char buf[255]; + switch (code) { + case Q_FLAGS2_CODE: return "Q_FLAGS2_CODE"; + case Q_SQL_MODE_CODE: return "Q_SQL_MODE_CODE"; + case Q_CATALOG_CODE: return "Q_CATALOG_CODE"; + case Q_AUTO_INCREMENT: return "Q_AUTO_INCREMENT"; + case Q_CHARSET_CODE: return "Q_CHARSET_CODE"; + case Q_TIME_ZONE_CODE: return "Q_TIME_ZONE_CODE"; + case Q_CATALOG_NZ_CODE: return "Q_CATALOG_NZ_CODE"; + case Q_LC_TIME_NAMES_CODE: return "Q_LC_TIME_NAMES_CODE"; + case Q_CHARSET_DATABASE_CODE: return "Q_CHARSET_DATABASE_CODE"; + } + sprintf(buf, "CODE#%d", code); + return buf; +} +#endif + +/** + Macro to check that there is enough space to read from memory. + + @param PTR Pointer to memory + @param END End of memory + @param CNT Number of bytes that should be read. + */ +#define CHECK_SPACE(PTR,END,CNT) \ + do { \ + DBUG_PRINT("info", ("Read %s", code_name(pos[-1]))); \ + DBUG_ASSERT((PTR) + (CNT) <= (END)); \ + if ((PTR) + (CNT) > (END)) { \ + DBUG_PRINT("info", ("query= 0")); \ + query= 0; \ + DBUG_VOID_RETURN; \ + } \ + } while (0) + /* Query_log_event::Query_log_event() This is used by the SQL slave thread to prepare the event before execution. @@ -1632,6 +1807,19 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, if (tmp) { status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET); + /* + Check if status variable length is corrupt and will lead to very + wrong data. We could be even more strict and require data_len to + be even bigger, but this will suffice to catch most corruption + errors that can lead to a crash. + */ + if (status_vars_len > min(data_len, MAX_SIZE_LOG_EVENT_STATUS)) + { + DBUG_PRINT("info", ("status_vars_len (%u) > data_len (%lu); query= 0", + status_vars_len, data_len)); + query= 0; + DBUG_VOID_RETURN; + } data_len-= status_vars_len; DBUG_PRINT("info", ("Query_log_event has status_vars_len: %u", (uint) status_vars_len)); @@ -1651,6 +1839,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, { switch (*pos++) { case Q_FLAGS2_CODE: + CHECK_SPACE(pos, end, 4); flags2_inited= 1; flags2= uint4korr(pos); DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", (ulong) flags2)); @@ -1661,6 +1850,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, #ifndef DBUG_OFF char buff[22]; #endif + CHECK_SPACE(pos, end, 8); sql_mode_inited= 1; sql_mode= (ulong) uint8korr(pos); // QQ: Fix when sql_mode is ulonglong DBUG_PRINT("info",("In Query_log_event, read sql_mode: %s", @@ -1669,15 +1859,24 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, break; } case Q_CATALOG_NZ_CODE: - get_str_len_and_pointer(&pos, &catalog, &catalog_len); + DBUG_PRINT("info", ("case Q_CATALOG_NZ_CODE; pos: 0x%lx; end: 0x%lx", + (ulong) pos, (ulong) end)); + if (get_str_len_and_pointer(&pos, &catalog, &catalog_len, end)) + { + DBUG_PRINT("info", ("query= 0")); + query= 0; + DBUG_VOID_RETURN; + } break; case Q_AUTO_INCREMENT: + CHECK_SPACE(pos, end, 4); auto_increment_increment= uint2korr(pos); auto_increment_offset= uint2korr(pos+2); pos+= 4; break; case Q_CHARSET_CODE: { + CHECK_SPACE(pos, end, 6); charset_inited= 1; memcpy(charset, pos, 6); pos+= 6; @@ -1685,20 +1884,29 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, } case Q_TIME_ZONE_CODE: { - get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len); + if (get_str_len_and_pointer(&pos, &time_zone_str, &time_zone_len, end)) + { + DBUG_PRINT("info", ("Q_TIME_ZONE_CODE: query= 0")); + query= 0; + DBUG_VOID_RETURN; + } break; } case Q_CATALOG_CODE: /* for 5.0.x where 0<=x<=3 masters */ + CHECK_SPACE(pos, end, 1); if ((catalog_len= *pos)) catalog= (char*) pos+1; // Will be copied later + CHECK_SPACE(pos, end, catalog_len + 2); pos+= catalog_len+2; // leap over end 0 catalog_nz= 0; // catalog has end 0 in event break; case Q_LC_TIME_NAMES_CODE: + CHECK_SPACE(pos, end, 2); lc_time_names_number= uint2korr(pos); pos+= 2; break; case Q_CHARSET_DATABASE_CODE: + CHECK_SPACE(pos, end, 2); charset_database_number= uint2korr(pos); pos+= 2; break; @@ -1726,6 +1934,11 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, DBUG_VOID_RETURN; if (catalog_len) // If catalog is given { + /** + @todo we should clean up and do only copy_str_and_move; it + works for both cases. Then we can remove the catalog_nz + flag. /sven + */ if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3. copy_str_and_move(&catalog, &start, catalog_len); else @@ -1738,6 +1951,13 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, if (time_zone_len) copy_str_and_move(&time_zone_str, &start, time_zone_len); + /** + if time_zone_len or catalog_len are 0, then time_zone and catalog + are uninitialized at this point. shouldn't they point to the + zero-length null-terminated strings we allocated space for in the + my_alloc call above? /sven + */ + /* A 2nd variable part; this is common to all versions */ memcpy((char*) start, end, data_len); // Copy db and query start[data_len]= '\0'; // End query with \0 (For safetly) @@ -2200,6 +2420,7 @@ end: */ thd->catalog= 0; thd->set_db(NULL, 0); /* will free the current database */ + DBUG_PRINT("info", ("end: query= 0")); thd->query= 0; // just to be sure thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -2235,6 +2456,30 @@ int Query_log_event::do_update_pos(Relay_log_info *rli) } +Log_event::enum_skip_reason +Query_log_event::do_shall_skip(Relay_log_info *rli) +{ + DBUG_ENTER("Query_log_event::do_shall_skip"); + DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len)); + DBUG_ASSERT(query && q_len > 0); + + if (rli->slave_skip_counter > 0) + { + if (strcmp("BEGIN", query) == 0) + { + thd->options|= OPTION_BEGIN; + DBUG_RETURN(Log_event::continue_group(rli)); + } + + if (strcmp("COMMIT", query) == 0 || strcmp("ROLLBACK", query) == 0) + { + thd->options&= ~OPTION_BEGIN; + DBUG_RETURN(Log_event::EVENT_SKIP_COUNT); + } + } + DBUG_RETURN(Log_event::do_shall_skip(rli)); +} + #endif @@ -2774,7 +3019,7 @@ uint Load_log_event::get_query_buffer_length() 21 + sql_ex.field_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'" 23 + sql_ex.enclosed_len*4 + 2 + // " OPTIONALLY ENCLOSED BY 'str'" 12 + sql_ex.escaped_len*4 + 2 + // " ESCAPED BY 'str'" - 21 + sql_ex.line_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'" + 21 + sql_ex.line_term_len*4 + 2 + // " LINES TERMINATED BY 'str'" 19 + sql_ex.line_start_len*4 + 2 + // " LINES STARTING BY 'str'" 15 + 22 + // " IGNORE xxx LINES" 3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)" @@ -3871,10 +4116,7 @@ Intvar_log_event::do_shall_skip(Relay_log_info *rli) that we do not change the value of the slave skip counter since it will be decreased by the following insert event. */ - if (rli->slave_skip_counter == 1) - return Log_event::EVENT_SKIP_IGNORE; - else - return Log_event::do_shall_skip(rli); + return continue_group(rli); } #endif @@ -3970,10 +4212,7 @@ Rand_log_event::do_shall_skip(Relay_log_info *rli) that we do not change the value of the slave skip counter since it will be decreased by the following insert event. */ - if (rli->slave_skip_counter == 1) - return Log_event::EVENT_SKIP_IGNORE; - else - return Log_event::do_shall_skip(rli); + return continue_group(rli); } #endif /* !MYSQL_CLIENT */ @@ -4049,6 +4288,17 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli) "COMMIT /* implicit, from Xid_log_event */"); return end_trans(thd, COMMIT); } + +Log_event::enum_skip_reason +Xid_log_event::do_shall_skip(Relay_log_info *rli) +{ + DBUG_ENTER("Xid_log_event::do_shall_skip"); + if (rli->slave_skip_counter > 0) { + thd->options&= ~OPTION_BEGIN; + DBUG_RETURN(Log_event::EVENT_SKIP_COUNT); + } + DBUG_RETURN(Log_event::do_shall_skip(rli)); +} #endif /* !MYSQL_CLIENT */ @@ -4427,10 +4677,7 @@ User_var_log_event::do_shall_skip(Relay_log_info *rli) that we do not change the value of the slave skip counter since it will be decreased by the following insert event. */ - if (rli->slave_skip_counter == 1) - return Log_event::EVENT_SKIP_IGNORE; - else - return Log_event::do_shall_skip(rli); + return continue_group(rli); } #endif /* !MYSQL_CLIENT */ @@ -5366,6 +5613,19 @@ int Begin_load_query_log_event::get_create_or_append() const #endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) +Log_event::enum_skip_reason +Begin_load_query_log_event::do_shall_skip(Relay_log_info *rli) +{ + /* + If the slave skip counter is 1, then we should not start executing + on the next event. + */ + return continue_group(rli); +} +#endif + + /************************************************************************** Execute_load_query_log_event methods **************************************************************************/ @@ -5374,12 +5634,13 @@ int Begin_load_query_log_event::get_create_or_append() const #ifndef MYSQL_CLIENT Execute_load_query_log_event:: Execute_load_query_log_event(THD *thd_arg, const char* query_arg, - ulong query_length_arg, uint fn_pos_start_arg, - uint fn_pos_end_arg, - enum_load_dup_handling dup_handling_arg, - bool using_trans, bool suppress_use): + ulong query_length_arg, uint fn_pos_start_arg, + uint fn_pos_end_arg, + enum_load_dup_handling dup_handling_arg, + bool using_trans, bool suppress_use, + THD::killed_state killed_err_arg): Query_log_event(thd_arg, query_arg, query_length_arg, using_trans, - suppress_use), + suppress_use, killed_err_arg), file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg), fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg) { @@ -5577,6 +5838,10 @@ bool sql_ex_info::write_data(IO_CACHE* file) } else { + /** + @todo This is sensitive to field padding. We should write a + char[7], not an old_sql_ex. /sven + */ old_sql_ex old_ex; old_ex.field_term= *field_term; old_ex.enclosed= *enclosed; @@ -6146,14 +6411,19 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) table->in_use = old_thd; switch (error) { - /* Some recoverable errors */ - case HA_ERR_RECORD_CHANGED: - case HA_ERR_KEY_NOT_FOUND: /* Idempotency support: OK if - tuple does not exist */ - error= 0; case 0: break; + /* Some recoverable errors */ + case HA_ERR_RECORD_CHANGED: + case HA_ERR_RECORD_DELETED: + case HA_ERR_KEY_NOT_FOUND: + case HA_ERR_END_OF_FILE: + /* Idempotency support: OK if tuple does not exist */ + DBUG_PRINT("info", ("error: %s", HA_ERR(error))); + error= 0; + break; + default: rli->report(ERROR_LEVEL, thd->net.last_errno, "Error in %s event: row application failed. %s", @@ -6170,6 +6440,10 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) m_curr_row_end. */ + DBUG_PRINT("info", ("error: %d", error)); + DBUG_PRINT("info", ("curr_row: 0x%lu; curr_row_end: 0x%lu; rows_end: 0x%lu", + (ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end)); + if (!m_curr_row_end && !error) unpack_current_row(rli); @@ -6469,6 +6743,16 @@ void Rows_log_event::print_helper(FILE *file, data) in the table map are initialized as zero (0). The array size is the same as the columns for the table on the slave. + Additionally, values saved for field metadata on the master are saved as a + string of bytes (uchar) in the binlog. A field may require 1 or more bytes + to store the information. In cases where values require multiple bytes + (e.g. values > 255), the endian-safe methods are used to properly encode + the values on the master and decode them on the slave. When the field + metadata values are captured on the slave, they are stored in an array of + type uint16. This allows the least number of casts to prevent casting bugs + when the field metadata is used in comparisons of field attributes. When + the field metadata is used for calculating addresses in pointer math, the + type used is uint32. */ /** @@ -6866,10 +7150,7 @@ Table_map_log_event::do_shall_skip(Relay_log_info *rli) If the slave skip counter is 1, then we should not start executing on the next event. */ - if (rli->slave_skip_counter == 1) - return Log_event::EVENT_SKIP_IGNORE; - else - return Log_event::do_shall_skip(rli); + return continue_group(rli); } int Table_map_log_event::do_update_pos(Relay_log_info *rli) @@ -7383,6 +7664,9 @@ static bool record_compare(TABLE *table) records. Check that the other engines also return correct records. */ + DBUG_DUMP("record[0]", table->record[0], table->s->reclength); + DBUG_DUMP("record[1]", table->record[1], table->s->reclength); + bool result= FALSE; uchar saved_x[2], saved_filler[2]; @@ -7471,7 +7755,7 @@ record_compare_exit: int Rows_log_event::find_row(const Relay_log_info *rli) { - DBUG_ENTER("find_row"); + DBUG_ENTER("Rows_log_event::find_row"); DBUG_ASSERT(m_table && m_table->in_use != NULL); @@ -7700,7 +7984,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli) DBUG_DUMP("record found", table->record[0], table->s->reclength); table->file->ha_rnd_end(); - DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0); + DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == HA_ERR_RECORD_DELETED || error == 0); DBUG_RETURN(error); } @@ -7900,7 +8184,15 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli) int error= find_row(rli); if (error) + { + /* + We need to read the second image in the event of error to be + able to skip to the next pair of updates + */ + m_curr_row= m_curr_row_end; + unpack_current_row(rli); return error; + } /* This is the situation after locating BI: diff --git a/sql/log_event.h b/sql/log_event.h index 0c66d1b190f..4bd496af2a4 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -18,8 +18,10 @@ @{ @file - - Binary log event definitions. + + @brief Binary log event definitions. This includes generic code + common to all types of log events, as well as specific code for each + type of log event. */ @@ -37,6 +39,23 @@ #include "rpl_reporting.h" #endif +/** + Either assert or return an error. + + In debug build, the condition will be checked, but in non-debug + builds, the error code given will be returned instead. + + @param COND Condition to check + @param ERRNO Error number to return in non-debug builds +*/ +#ifdef DBUG_OFF +#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) \ + do { if (!(COND)) return ERRNO; } while (0) +#else +#define ASSERT_OR_RETURN_ERROR(COND, ERRNO) \ + DBUG_ASSERT(COND) +#endif + #define LOG_READ_EOF -1 #define LOG_READ_BOGUS -2 #define LOG_READ_IO -3 @@ -394,15 +413,19 @@ struct sql_ex_info #define LOG_EVENT_BINLOG_IN_USE_F 0x1 -/* - If the query depends on the thread (for example: TEMPORARY TABLE). - Currently this is used by mysqlbinlog to know it must print - SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it - for every query but this would be slow). +/** + @def LOG_EVENT_THREAD_SPECIFIC_F + + If the query depends on the thread (for example: TEMPORARY TABLE). + Currently this is used by mysqlbinlog to know it must print + SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it + for every query but this would be slow). */ #define LOG_EVENT_THREAD_SPECIFIC_F 0x4 -/* +/** + @def LOG_EVENT_SUPPRESS_USE_F + Suppress the generation of 'USE' statements before the actual statement. This flag should be set for any events that does not need the current database set to function correctly. Most notable cases @@ -421,23 +444,26 @@ struct sql_ex_info */ #define LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F 0x10 -/* - OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must be - written to the binlog. OPTIONS_WRITTEN_TO_BINLOG could be written - into the Format_description_log_event, so that if later we don't want - to replicate a variable we did replicate, or the contrary, it's - doable. But it should not be too hard to decide once for all of what - we replicate and what we don't, among the fixed 32 bits of - thd->options. - I (Guilhem) have read through every option's usage, and it looks like - OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones - which alter how the query modifies the table. It's good to replicate - OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the slave may - insert data slower than the master, in InnoDB. - OPTION_BIG_SELECTS is not needed (the slave thread runs with - max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed - either, as the manual says (because a too big in-memory temp table is - automatically written to disk). +/** + @def OPTIONS_WRITTEN_TO_BIN_LOG + + OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must + be written to the binlog. OPTIONS_WRITTEN_TO_BIN_LOG could be + written into the Format_description_log_event, so that if later we + don't want to replicate a variable we did replicate, or the + contrary, it's doable. But it should not be too hard to decide once + for all of what we replicate and what we don't, among the fixed 32 + bits of thd->options. + + I (Guilhem) have read through every option's usage, and it looks + like OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only + ones which alter how the query modifies the table. It's good to + replicate OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the + slave may insert data slower than the master, in InnoDB. + OPTION_BIG_SELECTS is not needed (the slave thread runs with + max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed + either, as the manual says (because a too big in-memory temp table + is automatically written to disk). */ #define OPTIONS_WRITTEN_TO_BIN_LOG \ (OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS | \ @@ -452,6 +478,11 @@ struct sql_ex_info #endif #undef EXPECTED_OPTIONS /* You shouldn't use this one */ +/** + @enum Log_event_type + + Enumeration type for the different types of log events. +*/ enum Log_event_type { /* @@ -612,13 +643,90 @@ typedef struct st_print_event_info #endif -/***************************************************************************** - - Log_event class +/** + @class Log_event This is the abstract base class for binary log events. - - ****************************************************************************/ + + @section Log_event_binary_format Binary Format + + Any Log_event saved on disk consists of the following three + components. + + @li Common-Header + @li Post-Header + @li Body + + The Common-Header, documented below, always has the same form and + length within one version of MySQL. Each event type specifies a + form and length of the Post-Header common to all events of the type. + The Body may be of different form and length even for different + events of the same type. The binary formats of Post-Header and Body + are documented separately in each subclass. The binary format of + Common-Header is as follows. + + <table> + <caption>Common-Header</caption> + + <tr> + <th>Name</th> + <th>Format<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>timestamp</td> + <td>4 byte unsigned integer</td> + <td>The number of seconds since 1970. + </td> + </tr> + + <tr> + <td>type</td> + <td>1 byte enumeration</td> + <td>See enum #Log_event_type.</td> + </tr> + + <tr> + <td>master_id</td> + <td>4 byte integer</td> + <td>Server ID of the server that created the event.</td> + </tr> + + <tr> + <td>total_size</td> + <td>4 byte integer</td> + <td>The total size of this event, in bytes. In other words, this + is the sum of the sizes of Common-Header, Post-Header, and Body. + </td> + </tr> + + <tr> + <td>master_position</td> + <td>4 byte integer</td> + <td>The position of the next event in the master binary log, in + bytes from the beginning of the file. + </td> + </tr> + + <tr> + <td>flags</td> + <td>2 byte bitfield</td> + <td>See Log_event::flags.</td> + </tr> + </table> + + Summing up the numbers above, we see that the total size of the + common header is 19 bytes. + + @subsection Log_event_endianness_and_string_formats Endianness and String Formats + + All numbers, whether they are 16-, 32-, or 64-bit, are stored in + little endian, i.e., the least significant byte first. + + Strings are stored in various formats. The format of each string is + documented separately. +*/ class Log_event { public: @@ -692,8 +800,8 @@ public: */ uint32 server_id; - /* - Some 16 flags. Look above for LOG_EVENT_TIME_F, + /** + Some 16 flags. See the definitions above for LOG_EVENT_TIME_F, LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F, and LOG_EVENT_SUPPRESS_USE_F for notes. */ @@ -871,6 +979,25 @@ public: protected: /** + Helper function to ignore an event w.r.t. the slave skip counter. + + This function can be used inside do_shall_skip() for functions + that cannot end a group. If the slave skip counter is 1 when + seeing such an event, the event shall be ignored, the counter + left intact, and processing continue with the next event. + + A typical usage is: + @code + enum_skip_reason do_shall_skip(Relay_log_info *rli) { + return continue_group(rli); + } + @endcode + + @return Skip reason + */ + enum_skip_reason continue_group(Relay_log_info *rli); + + /** Primitive to apply an event to the database. This is where the change to the database is made. @@ -950,6 +1077,7 @@ protected: #endif }; + /* One class for each type of event. Two constructors for each class: @@ -963,13 +1091,332 @@ protected: mysqlbinlog. This constructor must be format-tolerant. */ -/***************************************************************************** - - Query Log Event class - - Logs SQL queries +/** + @class Query_log_event + + Logs SQL queries. + + @section Query_log_event_binary_format Binary format + + The Post-Header has five components: + + <table> + <caption>Post-Header for Query_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>slave_proxy_id</td> + <td>4 byte unsigned integer</td> + <td>An integer identifying the client thread, which is unique on + the server. (Note, however, that two threads on different servers + may have the same slave_proxy_id.) This is used when a client + thread creates a temporary table. Temporary tables are local to + the client, and the slave_proxy_id is used to distinguish + temporary tables belonging to different clients. + </td> + </tr> + + <tr> + <td>exec_time</td> + <td>4 byte integer</td> + <td>???TODO</td> + </tr> + + <tr> + <td>db_len</td> + <td>1 byte integer</td> + <td>The length of the name of the currently selected + database. + </td> + </tr> + + <tr> + <td>error_code</td> + <td>2 byte integer</td> + <td>Error code generated by the master. If the master fails, the + slave will fail with the same error code, except for the error + codes ER_DB_CREATE_EXISTS==1007 and ER_DB_DROP_EXISTS==1008. + </td> + </tr> + + <tr> + <td>status_vars_len</td> + <td>2 byte integer</td> + <td>The length of the status_vars block of the Body, in bytes. See + <a href="#query_log_event_status_vars">below</a>. + </td> + </tr> + + <tr> + <td>Post-Header-For-Derived</td> + <td>0 bytes</td> + <td>This field is only written by the subclass + Execute_load_query_log_event. In this base class, it takes 0 + bytes. See separate documentation for + Execute_load_query_log_event. + </td> + </tr> + </table> + + The Body has the following components: + + <table> + <caption>Body for Query_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td><a name="query_log_event_status_vars" /> status_vars</td> + <td>variable length</td> + <td>Zero or more status variables. Each status variable consists + of one byte identifying the variable stored, followed by the value + of the variable. The possible variables are listed separately in + the table below. MySQL always writes events in the order defined + below; however, it is capable of reading them in any order. + </td> + </tr> + + <tr> + <td>db</td> + <td>db_len+1</td> + <td>The currently selected database, as a null-terminated string. + + (The trailing zero is redundant since the length is already known; + it is db_len from Post-Header.) + </td> + </tr> + + <tr> + <td>query</td> + <td>variable length string without trailing zero, extending to the + end of the event (determined by the length field of the + Common-Header) + </td> + <td>The SQL query.</td> + </tr> + </table> + + The following table lists the status variables that may appear in + the status_vars field. + + <table> + <caption>Status variables for Query_log_event</caption> + + <tr> + <th>Status variable</th> + <th>1-byte identifier</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>flags2</td> + <td>Q_FLAGS2_CODE == 0</td> + <td>4 byte bitfield</td> + <td>The flags in thd->options, binary AND-ed with + OPTIONS_WRITTEN_TO_BIN_LOG. The thd->options bitfield contains + options for SELECT. OPTIONS_WRITTEN identifies those options that + need to be written to the binlog (not all do). Specifically, + OPTIONS_WRITTEN_TO_BIN_LOG equals (OPTION_AUTO_IS_NULL | + OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS | + OPTION_NOT_AUTOCOMMIT), or 0x0c084000 in hex. + + These flags correspond to the SQL variables SQL_AUTO_IS_NULL, + FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, and AUTOCOMMIT, documented in + the "SET Syntax" section of the MySQL Manual. + + This field is always written to the binlog in version >= 5.0, and + never written in version < 5.0. + </td> + </tr> + + <tr> + <td>sql_mode</td> + <td>Q_SQL_MODE_CODE == 1</td> + <td>8 byte integer</td> + <td>The sql_mode variable. See the section "SQL Modes" in the + MySQL manual, and see mysql_priv.h for a list of the possible + flags. Currently (2007-10-04), the following flags are available: + <pre> + MODE_REAL_AS_FLOAT==0x1 + MODE_PIPES_AS_CONCAT==0x2 + MODE_ANSI_QUOTES==0x4 + MODE_IGNORE_SPACE==0x8 + MODE_NOT_USED==0x10 + MODE_ONLY_FULL_GROUP_BY==0x20 + MODE_NO_UNSIGNED_SUBTRACTION==0x40 + MODE_NO_DIR_IN_CREATE==0x80 + MODE_POSTGRESQL==0x100 + MODE_ORACLE==0x200 + MODE_MSSQL==0x400 + MODE_DB2==0x800 + MODE_MAXDB==0x1000 + MODE_NO_KEY_OPTIONS==0x2000 + MODE_NO_TABLE_OPTIONS==0x4000 + MODE_NO_FIELD_OPTIONS==0x8000 + MODE_MYSQL323==0x10000 + MODE_MYSQL323==0x20000 + MODE_MYSQL40==0x40000 + MODE_ANSI==0x80000 + MODE_NO_AUTO_VALUE_ON_ZERO==0x100000 + MODE_NO_BACKSLASH_ESCAPES==0x200000 + MODE_STRICT_TRANS_TABLES==0x400000 + MODE_STRICT_ALL_TABLES==0x800000 + MODE_NO_ZERO_IN_DATE==0x1000000 + MODE_NO_ZERO_DATE==0x2000000 + MODE_INVALID_DATES==0x4000000 + MODE_ERROR_FOR_DIVISION_BY_ZERO==0x8000000 + MODE_TRADITIONAL==0x10000000 + MODE_NO_AUTO_CREATE_USER==0x20000000 + MODE_HIGH_NOT_PRECEDENCE==0x40000000 + MODE_PAD_CHAR_TO_FULL_LENGTH==0x80000000 + </pre> + All these flags are replicated from the server. However, all + flags except MODE_NO_DIR_IN_CREATE are honored by the slave; the + slave always preserves its old value of MODE_NO_DIR_IN_CREATE. + For a rationale, see comment in Query_log_event::do_apply_event in + log_event.cc. + + This field is always written to the binlog. + </td> + </tr> + + <tr> + <td>catalog</td> + <td>Q_CATALOG_NZ_CODE == 6</td> + <td>Variable-length string: the length in bytes (1 byte) followed + by the characters (at most 255 bytes) + </td> + <td>Stores the client's current catalog. Every database belongs + to a catalog, the same way that every table belongs to a + database. Currently, there is only one catalog, 'std'. + + This field is written if the length of the catalog is > 0; + otherwise it is not written. + </td> + </tr> + + <tr> + <td>auto_increment</td> + <td>Q_AUTO_INCREMENT == 3</td> + <td>two 2 byte unsigned integers, totally 2+2=4 bytes</td> + + <td>The two variables auto_increment_increment and + auto_increment_offset, in that order. For more information, see + "System variables" in the MySQL manual. + + This field is written if auto_increment>1; otherwise it is not + written. + </td> + </tr> + + <tr> + <td>charset</td> + <td>Q_CHARSET_CODE == 4</td> + <td>three 2-byte unsigned integers (i.e., 6 bytes)</td> + <td>The three variables character_set_client, + collation_connection, and collation_server, in that order. + `character_set_client' is a code identifying the character set and + collation used by the client to encode the query. + `collation_connection' identifies the character set and collation + that the master converts the query to when it receives it; this is + useful when comparing literal strings. `collation_server' is the + default character set and collation used when a new database is + created. + + See also "Connection Character Sets and Collations" in the MySQL + 5.1 manual. + + All three variables are codes identifying a (character set, + collation) pair. To see which codes map to which pairs, run the + query "SELECT id, character_set_name, collation_name FROM + COLLATIONS". + + Cf. Q_CHARSET_DATABASE_NUMBER below. + + This field is always written. + </td> + </tr> + + <tr> + <td>time_zone</td> + <td>Q_TIME_ZONE_CODE == 5</td> + <td>Variable-length string: the length in bytes (1 byte) followed + by the characters (at most 255 bytes). + <td>The time_zone of the master. + + See also "System Variables" and "MySQL Server Time Zone Support" + in the MySQL manual. + + This field is written if the length of the time zone string is > + 0; otherwise, it is not written. + </td> + </tr> + + <tr> + <td>lc_time_names_number</td> + <td>Q_LC_TIME_NAMES_CODE == 7</td> + <td>2 byte integer</td> + <td>A code identifying a table of month and day names. The + mapping from codes to languages is defined in sql_locale.cc. + + This field is written if it is != 0, i.e., if the locale is not + en_US. + </td> + </tr> + + <tr> + <td>charset_database_number</td> + <td>Q_CHARSET_DATABASE_NUMBER == 8</td> + <td>2 byte integer</td> + + <td>The value of the collation_database system variable (in the + source code stored in thd->variables.collation_database), which + holds the code for a (character set, collation) pair as described + above (see Q_CHARSET_CODE). + + `collation_database' was used in old versions (???WHEN). Its + value was loaded when issuing a "use db" command and could be + changed by issuing a "SET collation_database=xxx" command. It + used to affect the "LOAD DATA INFILE" and "CREATE TABLE" commands. + + In newer versions, "CREATE TABLE" has been changed to take the + character set from the database of the created table, rather than + the database of the current database. This makes a difference + when creating a table in another database than the current one. + "LOAD DATA INFILE" has not yet changed to do this, but there are + plans to eventually do it, and to make collation_database + read-only. + + This field is written if it is not 0. + </td> + </tr> + </table> + + @subsection Query_log_event_notes_on_previous_versions Notes on Previous Versions + + @li Status vars were introduced in version 5.0. To read earlier + versions correctly, check the length of the Post-Header. + + @li The status variable Q_CATALOG_CODE == 2 existed in MySQL 5.0.x, + where 0<=x<=3. It was identical to Q_CATALOG_CODE, except that the + string had a trailing '\0'. The '\0' was removed in 5.0.4 since it + was redundant (the string length is stored before the string). The + Q_CATALOG_CODE will never be written by a new master, but can still + be understood by a new slave. + + @li See Q_CHARSET_DATABASE_NUMBER in the table above. - ****************************************************************************/ +*/ class Query_log_event: public Log_event { protected: @@ -1027,7 +1474,7 @@ public: /* 'flags2' is a second set of flags (on top of those in Log_event), for session variables. These are thd->options which is & against a mask - (OPTIONS_WRITTEN_TO_BINLOG). + (OPTIONS_WRITTEN_TO_BIN_LOG). flags2_inited helps make a difference between flags2==0 (3.23 or 4.x master, we don't know flags2, so use the slave server's global options) and flags2==0 (5.0 master, we know this has a meaning of flags all down which @@ -1086,6 +1533,7 @@ public: public: /* !!! Public in this patch to allow old usage */ #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); virtual int do_apply_event(Relay_log_info const *rli); virtual int do_update_pos(Relay_log_info *rli); @@ -1096,13 +1544,16 @@ public: /* !!! Public in this patch to allow old usage */ }; -/***************************************************************************** +/** + @class Muted_query_log_event - Muted Query Log Event class + Pretends to log SQL queries, but doesn't actually do so. - Pretends to Log SQL queries, but doesn't actually do so. + @section Muted_query_log_event_binary_format Binary Format - ****************************************************************************/ + This log event is not stored, and thus the binary format is 0 bytes + long. Note that not even the Common-Header is stored. +*/ class Muted_query_log_event: public Query_log_event { public: @@ -1119,14 +1570,54 @@ public: #ifdef HAVE_REPLICATION -/***************************************************************************** +/** + @class Slave_log_event - Slave Log Event class Note that this class is currently not used at all; no code writes a - Slave_log_event (though some code in repl_failsafe.cc reads Slave_log_event). - So it's not a problem if this code is not maintained. - - ****************************************************************************/ + Slave_log_event (though some code in repl_failsafe.cc reads + Slave_log_event). So it's not a problem if this code is not + maintained. + + @section Slave_log_event_binary_format Binary Format + + This event type has no Post-Header. The Body has the following + four components. + + <table> + <caption>Body for Slave_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>master_pos</td> + <td>8 byte integer</td> + <td>???TODO + </td> + </tr> + + <tr> + <td>master_port</td> + <td>2 byte integer</td> + <td>???TODO</td> + </tr> + + <tr> + <td>master_host</td> + <td>null-terminated string</td> + <td>???TODO</td> + </tr> + + <tr> + <td>master_log</td> + <td>null-terminated string</td> + <td>???TODO</td> + </tr> + </table> +*/ class Slave_log_event: public Log_event { protected: @@ -1165,11 +1656,202 @@ private: #endif /* HAVE_REPLICATION */ -/***************************************************************************** - - Load Log Event class +/** + @class Load_log_event + + This log event corresponds to a "LOAD DATA INFILE" SQL query on the + following form: + + @verbatim + (1) USE db; + (2) LOAD DATA [LOCAL] INFILE 'file_name' + (3) [REPLACE | IGNORE] + (4) INTO TABLE 'table_name' + (5) [FIELDS + (6) [TERMINATED BY 'field_term'] + (7) [[OPTIONALLY] ENCLOSED BY 'enclosed'] + (8) [ESCAPED BY 'escaped'] + (9) ] + (10) [LINES + (11) [TERMINATED BY 'line_term'] + (12) [LINES STARTING BY 'line_start'] + (13) ] + (14) [IGNORE skip_lines LINES] + (15) (field_1, field_2, ..., field_n)@endverbatim + + @section Load_log_event_binary_format Binary Format + + The Post-Header consists of the following six components. + + <table> + <caption>Post-Header for Load_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>slave_proxy_id</td> + <td>4 byte unsigned integer</td> + <td>An integer identifying the client thread, which is unique on + the server. (Note, however, that the same slave_proxy_id may + appear on different servers.) This is used when a client thread + creates a temporary table. Temporary tables are local to the + client, and the slave_proxy_id is used to distinguish temporary + tables belonging to different clients. + </td> + </tr> + + <tr> + <td>exec_time</td> + <td>4 byte unsigned integer</td> + <td>???TODO</td> + </tr> + + <tr> + <td>skip_lines</td> + <td>4 byte unsigned integer</td> + <td>The number on line (14) above, if present, or 0 if line (14) + is left out. + </td> + </tr> + + <tr> + <td>table_name_len</td> + <td>1 byte unsigned integer</td> + <td>The length of 'table_name' on line (4) above.</td> + </tr> + + <tr> + <td>db_len</td> + <td>1 byte unsigned integer</td> + <td>The length of 'db' on line (1) above.</td> + </tr> + + <tr> + <td>num_fields</td> + <td>4 byte unsigned integer</td> + <td>The number n of fields on line (15) above.</td> + </tr> + </table> + + The Body contains the following components. + + <table> + <caption>Body of Load_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>sql_ex</td> + <td>variable length</td> + + <td>Describes the part of the query on lines (3) and + (5)–(13) above. More precisely, it stores the five strings + (on lines) field_term (6), enclosed (7), escaped (8), line_term + (11), and line_start (12); as well as a bitfield indicating the + presence of the keywords REPLACE (3), IGNORE (3), and OPTIONALLY + (7). + + The data is stored in one of two formats, called "old" and "new". + The type field of Common-Header determines which of these two + formats is used: type LOAD_EVENT means that the old format is + used, and type NEW_LOAD_EVENT means that the new format is used. + When MySQL writes a Load_log_event, it uses the new format if at + least one of the five strings is two or more bytes long. + Otherwise (i.e., if all strings are 0 or 1 bytes long), the old + format is used. + + The new and old format differ in the way the five strings are + stored. + + <ul> + <li> In the new format, the strings are stored in the order + field_term, enclosed, escaped, line_term, line_start. Each string + consists of a length (1 byte), followed by a sequence of + characters (0-255 bytes). Finally, a boolean combination of the + following flags is stored in 1 byte: REPLACE_FLAG==0x4, + IGNORE_FLAG==0x8, and OPT_ENCLOSED_FLAG==0x2. If a flag is set, + it indicates the presence of the corresponding keyword in the SQL + query. + + <li> In the old format, we know that each string has length 0 or + 1. Therefore, only the first byte of each string is stored. The + order of the strings is the same as in the new format. These five + bytes are followed by the same 1-byte bitfield as in the new + format. Finally, a 1 byte bitfield called empty_flags is stored. + The low 5 bits of empty_flags indicate which of the five strings + have length 0. For each of the following flags that is set, the + corresponding string has length 0; for the flags that are not set, + the string has length 1: FIELD_TERM_EMPTY==0x1, + ENCLOSED_EMPTY==0x2, LINE_TERM_EMPTY==0x4, LINE_START_EMPTY==0x8, + ESCAPED_EMPTY==0x10. + </ul> + + Thus, the size of the new format is 6 bytes + the sum of the sizes + of the five strings. The size of the old format is always 7 + bytes. + </td> + </tr> + + <tr> + <td>field_lens</td> + <td>num_fields 1-byte unsigned integers</td> + <td>An array of num_fields integers representing the length of + each field in the query. (num_fields is from the Post-Header). + </td> + </tr> + + <tr> + <td>fields</td> + <td>num_fields null-terminated strings</td> + <td>An array of num_fields null-terminated strings, each + representing a field in the query. (The trailing zero is + redundant, since the length are stored in the num_fields array.) + The total length of all strings equals to the sum of all + field_lens, plus num_fields bytes for all the trailing zeros. + </td> + </tr> + + <tr> + <td>table_name</td> + <td>null-terminated string of length table_len+1 bytes</td> + <td>The 'table_name' from the query, as a null-terminated string. + (The trailing zero is actually redundant since the table_len is + known from Post-Header.) + </td> + </tr> + + <tr> + <td>db</td> + <td>null-terminated string of length db_len+1 bytes</td> + <td>The 'db' from the query, as a null-terminated string. + (The trailing zero is actually redundant since the db_len is known + from Post-Header.) + </td> + </tr> + + <tr> + <td>file_name</td> + <td>variable length string without trailing zero, extending to the + end of the event (determined by the length field of the + Common-Header) + </td> + <td>The 'file_name' from the query. + </td> + </tr> + + </table> + + @subsection Load_log_event_notes_on_previous_versions Notes on Previous Versions - ****************************************************************************/ +*/ class Load_log_event: public Log_event { private: @@ -1276,9 +1958,8 @@ public: /* !!! Public in this patch to allow old usage */ extern char server_version[SERVER_VERSION_LENGTH]; -/***************************************************************************** - - Start Log Event_v3 class +/** + @class Start_log_event_v3 Start_log_event_v3 is the Start_log_event of binlog format 3 (MySQL 3.23 and 4.x). @@ -1288,8 +1969,8 @@ extern char server_version[SERVER_VERSION_LENGTH]; MySQL 5.0 whenever it starts sending a new binlog if the requested position is >4 (otherwise if ==4 the event will be sent naturally). - ****************************************************************************/ - + @section Start_log_event_v3_binary_format Binary Format +*/ class Start_log_event_v3: public Log_event { public: @@ -1372,10 +2053,14 @@ protected: }; -/* - For binlog version 4. - This event is saved by threads which read it, as they need it for future - use (to decode the ordinary events). +/** + @class Format_description_log_event + + For binlog version 4. + This event is saved by threads which read it, as they need it for future + use (to decode the ordinary events). + + @section Format_description_log_event_binary_format Binary Format */ class Format_description_log_event: public Start_log_event_v3 @@ -1429,13 +2114,41 @@ protected: }; -/***************************************************************************** +/** + @class Intvar_log_event - Intvar Log Event class + Logs special variables related to auto_increment values. - Logs special variables such as auto_increment values + @section Intvar_log_event_binary_format Binary Format - ****************************************************************************/ + The Post-Header has two components: + + <table> + <caption>Post-Header for Intvar_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>Type</td> + <td>1 byte enumeration</td> + <td>One byte identifying the type of variable stored. Currently, + two identifiers are supported: LAST_INSERT_ID_EVENT==1 and + INSERT_ID_EVENT==2. + </td> + </tr> + + <tr> + <td>value</td> + <td>8 byte unsigned integer</td> + <td>The value of the variable.</td> + </tr> + + </table> +*/ class Intvar_log_event: public Log_event { @@ -1474,16 +2187,24 @@ private: }; -/***************************************************************************** - - Rand Log Event class +/** + @class Rand_log_event Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0. 4.1.1 does not need it (it's repeatable again) so this event needn't be written in 4.1.1 for PASSWORD() (but the fact that it is written is just a waste, it does not cause bugs). - ****************************************************************************/ + @section Rand_log_event_binary_format Binary Format + This event type has no Post-Header. The Body of this event type has + two components: + + @li seed1 (8 bytes): 64 bit random seed1. + @li seed2 (8 bytes): 64 bit random seed2. + + The state of the random number generation consists of 128 bits, + which are stored internally as two 64-bit numbers. +*/ class Rand_log_event: public Log_event { @@ -1520,14 +2241,14 @@ private: #endif }; -/***************************************************************************** - - Xid Log Event class +/** + @class Xid_log_event Logs xid of the transaction-to-be-committed in the 2pc protocol. Has no meaning in replication, slaves ignore it. - ****************************************************************************/ + @section Xid_log_event_binary_format Binary Format +*/ #ifdef MYSQL_CLIENT typedef ulonglong my_xid; // this line is the same as in handler.h #endif @@ -1559,17 +2280,18 @@ class Xid_log_event: public Log_event private: #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) virtual int do_apply_event(Relay_log_info const *rli); + enum_skip_reason do_shall_skip(Relay_log_info *rli); #endif }; -/***************************************************************************** - - User var Log Event class +/** + @class User_var_log_event Every time a query uses the value of a user variable, a User_var_log_event is written before the Query_log_event, to set the user variable. - ****************************************************************************/ + @section User_var_log_event_binary_format Binary Format +*/ class User_var_log_event: public Log_event { @@ -1611,11 +2333,14 @@ private: }; -/***************************************************************************** +/** + @class Stop_log_event - Stop Log Event class + @section Stop_log_event_binary_format Binary Format - ****************************************************************************/ + The Post-Header and Body for this event type are empty; it only has + the Common-Header. +*/ class Stop_log_event: public Log_event { public: @@ -1651,13 +2376,54 @@ private: #endif }; -/***************************************************************************** - - Rotate Log Event class +/** + @class Rotate_log_event This will be deprecated when we move to using sequence ids. - ****************************************************************************/ + @section Rotate_log_event_binary_format Binary Format + + The Post-Header has one component: + + <table> + <caption>Post-Header for Rotate_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>pos</td> + <td>8 byte integer</td> + <td>???TODO</td> + </tr> + + </table> + + The Body has one component: + + <table> + <caption>Body for Rotate_log_event</caption> + + <tr> + <th>Name</th> + <th>Size<br/></th> + <th>Description</th> + </tr> + + <tr> + <td>new_log_ident</td> + <td>variable length string without trailing zero, extending to the + end of the event (determined by the length field of the + Common-Header) + </td> + <td>???TODO</td> + </tr> + + </table> +*/ class Rotate_log_event: public Log_event { @@ -1704,9 +2470,11 @@ private: /* the classes below are for the new LOAD DATA INFILE logging */ -/***************************************************************************** - Create File Log Event class - ****************************************************************************/ +/** + @class Create_file_log_event + + @section Create_file_log_event_binary_format Binary Format +*/ class Create_file_log_event: public Load_log_event { @@ -1775,11 +2543,11 @@ private: }; -/***************************************************************************** - - Append Block Log Event class +/** + @class Append_block_log_event - ****************************************************************************/ + @section Append_block_log_event_binary_format Binary Format +*/ class Append_block_log_event: public Log_event { @@ -1830,11 +2598,11 @@ private: }; -/***************************************************************************** - - Delete File Log Event class +/** + @class Delete_file_log_event - ****************************************************************************/ + @section Delete_file_log_event_binary_format Binary Format +*/ class Delete_file_log_event: public Log_event { @@ -1871,11 +2639,11 @@ private: }; -/***************************************************************************** - - Execute Load Log Event class +/** + @class Execute_load_log_event - ****************************************************************************/ + @section Delete_file_log_event_binary_format Binary Format +*/ class Execute_load_log_event: public Log_event { @@ -1911,15 +2679,15 @@ private: }; -/*************************************************************************** - - Begin load query Log Event class +/** + @class Begin_load_query_log_event Event for the first block of file to be loaded, its only difference from Append_block event is that this event creates or truncates existing file before writing data. -****************************************************************************/ + @section Begin_load_query_log_event_binary_format Binary Format +*/ class Begin_load_query_log_event: public Append_block_log_event { public: @@ -1937,6 +2705,10 @@ public: *description_event); ~Begin_load_query_log_event() {} Log_event_type get_type_code() { return BEGIN_LOAD_QUERY_EVENT; } +private: +#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) + virtual enum_skip_reason do_shall_skip(Relay_log_info *rli); +#endif }; @@ -1946,15 +2718,15 @@ public: enum enum_load_dup_handling { LOAD_DUP_ERROR= 0, LOAD_DUP_IGNORE, LOAD_DUP_REPLACE }; -/**************************************************************************** - - Execute load query Log Event class +/** + @class Execute_load_query_log_event Event responsible for LOAD DATA execution, it similar to Query_log_event but before executing the query it substitutes original filename in LOAD DATA query with name of temporary file. -****************************************************************************/ + @section Execute_load_query_log_event_binary_format Binary Format +*/ class Execute_load_query_log_event: public Query_log_event { public: @@ -1972,10 +2744,12 @@ public: #ifndef MYSQL_CLIENT Execute_load_query_log_event(THD* thd, const char* query_arg, - ulong query_length, uint fn_pos_start_arg, - uint fn_pos_end_arg, - enum_load_dup_handling dup_handling_arg, - bool using_trans, bool suppress_use); + ulong query_length, uint fn_pos_start_arg, + uint fn_pos_end_arg, + enum_load_dup_handling dup_handling_arg, + bool using_trans, bool suppress_use, + THD::killed_state + killed_err_arg= THD::KILLED_NO_VALUE); #ifdef HAVE_REPLICATION void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ @@ -2006,6 +2780,11 @@ private: #ifdef MYSQL_CLIENT +/** + @class Unknown_log_event + + @section Unknown_log_event_binary_format Binary Format +*/ class Unknown_log_event: public Log_event { public: @@ -2026,14 +2805,14 @@ public: #endif char *str_to_hex(char *to, const char *from, uint len); -/***************************************************************************** - - Table map log event class +/** + @class Table_map_log_event Create a mapping from a (database name, table name) couple to a table identifier (an integer number). - ****************************************************************************/ + @section Table_map_log_event_binary_format Binary Format +*/ class Table_map_log_event : public Log_event { public: @@ -2143,9 +2922,8 @@ private: }; -/***************************************************************************** - - Row level log event class. +/** + @class Rows_log_event Common base class for all row-containing log events. @@ -2155,7 +2933,8 @@ private: - Write data header and data body to an IO_CACHE. - Provide an interface for adding an individual row to the event. - ****************************************************************************/ + @section Rows_log_event_binary_format Binary Format +*/ class Rows_log_event : public Log_event @@ -2300,7 +3079,7 @@ protected: uchar *m_rows_cur; /* One-after the end of the data */ uchar *m_rows_end; /* One-after the end of the allocated space */ - flag_set m_flags; /* Flags for row-level events */ + flag_set m_flags; /* Flags for row-level events */ /* helper functions */ @@ -2316,8 +3095,11 @@ protected: int unpack_current_row(const Relay_log_info *const rli) { DBUG_ASSERT(m_table); - return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols, - &m_curr_row_end, &m_master_reclength); + ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT); + int const result= ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols, + &m_curr_row_end, &m_master_reclength); + ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT); + return result; } #endif @@ -2383,15 +3165,15 @@ private: friend class Old_rows_log_event; }; -/***************************************************************************** - - Write row log event class +/** + @class Write_rows_log_event Log row insertions and updates. The event contain several insert/update rows for a table. Note that each event contains only rows for one table. - ****************************************************************************/ + @section Write_rows_log_event_binary_format Binary Format +*/ class Write_rows_log_event : public Rows_log_event { public: @@ -2438,9 +3220,8 @@ private: }; -/***************************************************************************** - - Update rows log event class +/** + @class Update_rows_log_event Log row updates with a before image. The event contain several update rows for a table. Note that each event contains only rows for @@ -2449,7 +3230,8 @@ private: Also note that the row data consists of pairs of row data: one row for the old data and one row for the new data. - ****************************************************************************/ + @section Update_rows_log_event_binary_format Binary Format +*/ class Update_rows_log_event : public Rows_log_event { public: @@ -2511,9 +3293,8 @@ protected: #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ }; -/***************************************************************************** - - Delete rows log event class. +/** + @class Delete_rows_log_event Log row deletions. The event contain several delete rows for a table. Note that each event contains only rows for one table. @@ -2530,7 +3311,8 @@ protected: Row_reader Extract the rows from the event. - ****************************************************************************/ + @section Delete_rows_log_event_binary_format Binary Format +*/ class Delete_rows_log_event : public Rows_log_event { public: @@ -2580,6 +3362,8 @@ protected: #include "log_event_old.h" /** + @class Incident_log_event + Class representing an incident, an occurance out of the ordinary, that happened on the master. @@ -2591,7 +3375,7 @@ protected: <caption>Incident event format</caption> <tr> <th>Symbol</th> - <th>Size<br>(bytes)</th> + <th>Size<br/>(bytes)</th> <th>Description</th> </tr> <tr> @@ -2610,7 +3394,9 @@ protected: <td>The message, if present. Not null terminated.</td> </tr> </table> - */ + + @section Delete_rows_log_event_binary_format Binary Format +*/ class Incident_log_event : public Log_event { public: #ifndef MYSQL_CLIENT diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 12838317646..2663de0b16a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -686,7 +686,6 @@ bool check_single_table_access(THD *thd, ulong privilege, bool check_routine_access(THD *thd,ulong want_access,char *db,char *name, bool is_proc, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); -bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables); @@ -1148,6 +1147,9 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key, bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, TABLE **table); TABLE *find_locked_table(THD *thd, const char *db,const char *table_name); +void detach_merge_children(TABLE *table, bool clear_refs); +bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, + TABLE_LIST *new_child_list, TABLE_LIST **new_last); bool reopen_table(TABLE *table); bool reopen_tables(THD *thd,bool get_locks,bool in_refresh); void close_data_files_and_morph_locks(THD *thd, const char *db, @@ -1398,8 +1400,21 @@ int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond); int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags); -int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); -bool open_and_lock_tables(THD *thd,TABLE_LIST *tables); +/* open_and_lock_tables with optional derived handling */ +bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); +/* simple open_and_lock_tables without derived handling */ +inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) +{ + return open_and_lock_tables_derived(thd, tables, FALSE); +} +/* open_and_lock_tables with derived handling */ +inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) +{ + return open_and_lock_tables_derived(thd, tables, TRUE); +} +/* simple open_and_lock_tables without derived handling for single table */ +TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, + thr_lock_type lock_type); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen); int decide_logging_format(THD *thd, TABLE_LIST *tables); @@ -1976,7 +1991,8 @@ int format_number(uint inputflag,uint max_length,char * pos,uint length, /* table.cc */ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, uint key_length); -void init_tmp_table_share(TABLE_SHARE *share, const char *key, uint key_length, +void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, + uint key_length, const char *table_name, const char *path); void free_table_share(TABLE_SHARE *share); int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4cf6e05751f..9e947470ca1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2936,7 +2936,6 @@ static int init_common_variables(const char *conf_file_name, int argc, global_system_variables.collation_connection= default_charset_info; global_system_variables.character_set_results= default_charset_info; global_system_variables.character_set_client= default_charset_info; - global_system_variables.collation_connection= default_charset_info; if (!(character_set_filesystem= get_charset_by_csname(character_set_filesystem_name, diff --git a/sql/records.cc b/sql/records.cc index 81c26da4b4d..0bf815e4073 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -55,6 +55,7 @@ static int rr_index(READ_RECORD *info); void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bool print_error, uint idx) { + empty_record(table); bzero((char*) info,sizeof(*info)); info->table= table; info->file= table->file; @@ -161,6 +162,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } else { + empty_record(table); info->record= table->record[0]; info->ref_length= table->file->ref_length; } diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 65c8e106112..ed0dc82cf01 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -65,6 +65,8 @@ pack_row(TABLE *table, MY_BITMAP const* cols, my_ptrdiff_t const rec_offset= record - table->record[0]; my_ptrdiff_t const def_offset= table->s->default_values - table->record[0]; + DBUG_ENTER("pack_row"); + /* We write the null bits and the packed records using one pass through all the fields. The null bytes are written little-endian, @@ -96,26 +98,17 @@ pack_row(TABLE *table, MY_BITMAP const* cols, For big-endian machines, we have to make sure that the length is stored in little-endian format, since this is the format used for the binlog. - - We do this by setting the db_low_byte_first, which is used - inside some store_length() to decide what order to write the - bytes in. - - In reality, db_log_byte_first is only set for legacy table - type Isam, but in the event of a bug, we need to guarantee - the endianess when writing to the binlog. - - This is currently broken for NDB due to BUG#29549, so we - will fix it when NDB has fixed their way of handling BLOBs. */ -#if 0 - bool save= table->s->db_low_byte_first; - table->s->db_low_byte_first= TRUE; -#endif - pack_ptr= field->pack(pack_ptr, field->ptr + offset); -#if 0 - table->s->db_low_byte_first= save; +#ifndef DBUG_OFF + const uchar *old_pack_ptr= pack_ptr; #endif + pack_ptr= field->pack(pack_ptr, field->ptr + offset, + field->max_data_length(), TRUE); + DBUG_PRINT("debug", ("field: %s; pack_ptr: 0x%lx;" + " pack_ptr':0x%lx; bytes: %d", + field->field_name, (ulong) old_pack_ptr, + (ulong) pack_ptr, + (int) (pack_ptr - old_pack_ptr))); } null_mask <<= 1; @@ -143,8 +136,8 @@ pack_row(TABLE *table, MY_BITMAP const* cols, packed data. If it doesn't, something is very wrong. */ DBUG_ASSERT(null_ptr == row_data + null_byte_count); - - return static_cast<size_t>(pack_ptr - row_data); + DBUG_DUMP("row_data", row_data, pack_ptr - row_data); + DBUG_RETURN(static_cast<size_t>(pack_ptr - row_data)); } #endif @@ -242,18 +235,16 @@ unpack_row(Relay_log_info const *rli, Use the master's size information if available else call normal unpack operation. */ -#if 0 - bool save= table->s->db_low_byte_first; - table->s->db_low_byte_first= TRUE; -#endif uint16 const metadata= tabledef->field_metadata(i); - if (tabledef && metadata) - pack_ptr= f->unpack(f->ptr, pack_ptr, metadata); - else - pack_ptr= f->unpack(f->ptr, pack_ptr); -#if 0 - table->s->db_low_byte_first= save; +#ifndef DBUG_OFF + uchar const *const old_pack_ptr= pack_ptr; #endif + pack_ptr= f->unpack(f->ptr, pack_ptr, metadata, TRUE); + DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;" + " pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d", + f->field_name, metadata, + (ulong) old_pack_ptr, (ulong) pack_ptr, + (int) (pack_ptr - old_pack_ptr))); } null_mask <<= 1; @@ -289,6 +280,8 @@ unpack_row(Relay_log_info const *rli, */ DBUG_ASSERT(null_ptr == row_data + master_null_byte_count); + DBUG_DUMP("row_data", row_data, pack_ptr - row_data); + *row_end = pack_ptr; if (master_reclength) { diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 867d55a60a3..15d7d97affd 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1082,6 +1082,9 @@ bool Relay_log_info::cached_charset_compare(char *charset) const void Relay_log_info::stmt_done(my_off_t event_master_log_pos, time_t event_creation_time) { +#ifndef DBUG_OFF + extern uint debug_not_change_ts_if_art_event; +#endif clear_flag(IN_STMT); /* @@ -1121,7 +1124,12 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, is that value may take some time to display in Seconds_Behind_Master - not critical). */ - last_master_timestamp= event_creation_time; +#ifndef DBUG_OFF + if (!(event_creation_time == 0 && debug_not_change_ts_if_art_event > 0)) +#else + if (event_creation_time != 0) +#endif + last_master_timestamp= event_creation_time; } } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 10ecf1a43d4..a3a57ad4ce9 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -366,6 +366,18 @@ public: } /** + Get the value of a replication state flag. + + @param flag Flag to get value of + + @return @c true if the flag was set, @c false otherwise. + */ + bool get_flag(enum_state_flag flag) + { + return m_flags & (1UL << flag); + } + + /** Clear the value of a replication state flag. @param flag Flag to clear diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index d1ce5bf3b7b..b3ca26d4c2c 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -31,31 +31,34 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const switch (type(col)) { case MYSQL_TYPE_NEWDECIMAL: length= my_decimal_get_binary_size(m_field_metadata[col] >> 8, - m_field_metadata[col] - ((m_field_metadata[col] >> 8) << 8)); + m_field_metadata[col] & 0xff); break; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: length= m_field_metadata[col]; break; + /* + The cases for SET and ENUM are include for completeness, however + both are mapped to type MYSQL_TYPE_STRING and their real types + are encoded in the field metadata. + */ case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_STRING: { - if (((m_field_metadata[col] & 0xff00) == (MYSQL_TYPE_SET << 8)) || - ((m_field_metadata[col] & 0xff00) == (MYSQL_TYPE_ENUM << 8))) + uchar type= m_field_metadata[col] >> 8U; + if ((type == MYSQL_TYPE_SET) || (type == MYSQL_TYPE_ENUM)) length= m_field_metadata[col] & 0x00ff; else { - length= m_field_metadata[col] & 0x00ff; - DBUG_ASSERT(length > 0); - if (length > 255) - { - DBUG_ASSERT(uint2korr(master_data) > 0); - length= uint2korr(master_data) + 2; - } - else - length= (uint) *master_data + 1; + /* + We are reading the actual size from the master_data record + because this field has the actual lengh stored in the first + byte. + */ + length= (uint) *master_data + 1; + DBUG_ASSERT(length != 0); } break; } @@ -95,6 +98,13 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const break; case MYSQL_TYPE_BIT: { + /* + Decode the size of the bit field from the master. + from_len is the length in bytes from the master + from_bit_len is the number of extra bits stored in the master record + If from_bit_len is not 0, add 1 to the length to account for accurate + number of bytes needed. + */ uint from_len= (m_field_metadata[col] >> 8U) & 0x00ff; uint from_bit_len= m_field_metadata[col] & 0x00ff; DBUG_ASSERT(from_bit_len <= 7); @@ -136,7 +146,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const length= *master_data; break; case 2: - length= sint2korr(master_data); + length= uint2korr(master_data); break; case 3: length= uint3korr(master_data); diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 26edbdd1405..375715c7858 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -99,7 +99,7 @@ public: /* These types store a single byte. */ - m_field_metadata[i]= (uchar)field_metadata[index]; + m_field_metadata[i]= field_metadata[index]; index++; break; } @@ -107,14 +107,14 @@ public: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_STRING: { - short int x= field_metadata[index++] << 8U; // real_type - x = x + field_metadata[index++]; // pack or field length + uint16 x= field_metadata[index++] << 8U; // real_type + x+= field_metadata[index++]; // pack or field length m_field_metadata[i]= x; break; } case MYSQL_TYPE_BIT: { - short int x= field_metadata[index++]; + uint16 x= field_metadata[index++]; x = x + (field_metadata[index++] << 8U); m_field_metadata[i]= x; break; @@ -125,14 +125,14 @@ public: These types store two bytes. */ char *ptr= (char *)&field_metadata[index]; - m_field_metadata[i]= sint2korr(ptr); + m_field_metadata[i]= uint2korr(ptr); index= index + 2; break; } case MYSQL_TYPE_NEWDECIMAL: { - short int x= field_metadata[index++] << 8U; // precision - x = x + field_metadata[index++]; // decimals + uint16 x= field_metadata[index++] << 8U; // precision + x+= field_metadata[index++]; // decimals m_field_metadata[i]= x; break; } diff --git a/sql/slave.cc b/sql/slave.cc index 2512954f805..0421c567c65 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1013,6 +1013,12 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, goto err; // mysql_parse took care of the error send thd->proc_info = "Opening master dump table"; + /* + Note: If this function starts to fail for MERGE tables, + change the next two lines to these: + tables.table= NULL; // was set by mysql_rm_table() + if (!open_n_lock_single_table(thd, &tables, TL_WRITE)) + */ tables.lock_type = TL_WRITE; if (!open_ltable(thd, &tables, TL_WRITE, 0)) { @@ -1715,7 +1721,14 @@ static int has_temporary_error(THD *thd) DBUG_ENTER("has_temporary_error"); if (thd->is_fatal_error) + { + DBUG_PRINT("info", ("thd->net.last_errno: %s", ER(thd->net.last_errno))); DBUG_RETURN(0); + } + + DBUG_EXECUTE_IF("all_errors_are_temporary_errors", + if (thd->net.last_errno) + thd->net.last_errno= ER_LOCK_DEADLOCK;); /* Temporary error codes: @@ -1724,7 +1737,10 @@ static int has_temporary_error(THD *thd) */ if (thd->net.last_errno == ER_LOCK_DEADLOCK || thd->net.last_errno == ER_LOCK_WAIT_TIMEOUT) + { + DBUG_PRINT("info", ("thd->net.last_errno: %s", ER(thd->net.last_errno))); DBUG_RETURN(1); + } #ifdef HAVE_NDB_BINLOG /* @@ -1795,9 +1811,6 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) int const type_code= ev->get_type_code(); int exec_res= 0; - /* - */ - DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)", ev->get_type_str(), type_code, ev->server_id)); DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu", @@ -1806,7 +1819,6 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) rli->last_event_start_time)); - /* Execute the event to change the database and update the binary log coordinates, but first we set some data that is needed for @@ -1854,10 +1866,13 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) // EVENT_SKIP_NOT, "not skipped", // EVENT_SKIP_IGNORE, - "skipped because event originated from this server", + "skipped because event should be ignored", // EVENT_SKIP_COUNT "skipped because event skip counter was non-zero" }; + DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d", + thd->options & OPTION_BEGIN ? 1 : 0, + rli->get_flag(Relay_log_info::IN_STMT))); DBUG_PRINT("skip_event", ("%s event was %s", ev->get_type_str(), explain[reason])); #endif @@ -1906,7 +1921,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) } if (slave_trans_retries) { - if (exec_res && has_temporary_error(thd)) + int temp_err; + if (exec_res && (temp_err= has_temporary_error(thd))) { const char *errmsg; /* @@ -1954,15 +1970,19 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) "the slave_transaction_retries variable.", slave_trans_retries); } - else if (!((thd->options & OPTION_BEGIN) && opt_using_transactions)) + else if (exec_res && !temp_err || + (opt_using_transactions && + rli->group_relay_log_pos == rli->event_relay_log_pos)) { /* - Only reset the retry counter if the event succeeded or - failed with a non-transient error. On a successful event, - the execution will proceed as usual; in the case of a + Only reset the retry counter if the entire group succeeded + or failed with a non-transient error. On a successful + event, the execution will proceed as usual; in the case of a non-transient error, the slave will stop with an error. */ rli->trans_retries= 0; // restart from fresh + DBUG_PRINT("info", ("Resetting retry counter, rli->trans_retries: %lu", + rli->trans_retries)); } } DBUG_RETURN(exec_res); @@ -2451,6 +2471,7 @@ pthread_handler_t handle_slave_sql(void *arg) rli->ignore_log_space_limit= 0; pthread_mutex_unlock(&rli->log_space_lock); rli->trans_retries= 0; // start from "no error" + DBUG_PRINT("info", ("rli->trans_retries: %lu", rli->trans_retries)); if (init_relay_log_pos(rli, rli->group_relay_log_name, @@ -3582,7 +3603,16 @@ static Log_event* next_event(Relay_log_info* rli) a new event and is queuing it; the false "0" will exist until SQL finishes executing the new event; it will be look abnormal only if the events have old timestamps (then you get "many", 0, "many"). - Transient phases like this can't really be fixed. + + Transient phases like this can be fixed with implemeting + Heartbeat event which provides the slave the status of the + master at time the master does not have any new update to send. + Seconds_Behind_Master would be zero only when master has no + more updates in binlog for slave. The heartbeat can be sent + in a (small) fraction of slave_net_timeout. Until it's done + rli->last_master_timestamp is temporarely (for time of + waiting for the following event) reset whenever EOF is + reached. */ time_t save_timestamp= rli->last_master_timestamp; rli->last_master_timestamp= 0; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index c0ea73a6c00..9babbcd49d8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -102,8 +102,9 @@ sp_get_item_value(THD *thd, Item *item, String *str) case REAL_RESULT: case INT_RESULT: case DECIMAL_RESULT: - return item->val_str(str); - + if (item->field_type() != MYSQL_TYPE_BIT) + return item->val_str(str); + else {/* Bit type is handled as binary string */} case STRING_RESULT: { String *result= item->val_str(str); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 6896ccd1b8c..a7d5848dd66 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -684,6 +684,9 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) to open the table thd->killed will be set if we run out of memory + + If closing a MERGE child, the calling function has to take care for + closing the parent too, if necessary. */ @@ -712,6 +715,12 @@ void close_handle_and_leave_table_as_lock(TABLE *table) share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table() } + /* + When closing a MERGE parent or child table, detach the children first. + Do not clear child table references to allow for reopen. + */ + if (table->child_l || table->parent) + detach_merge_children(table, FALSE); table->file->close(); table->db_stat= 0; // Mark file closed release_table_share(table->s, RELEASE_NORMAL); @@ -812,6 +821,10 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) void intern_close_table(TABLE *table) { // Free all structures DBUG_ENTER("intern_close_table"); + DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", + table->s ? table->s->db.str : "?", + table->s ? table->s->table_name.str : "?", + (long) table)); free_io_cache(table); delete table->triggers; @@ -835,6 +848,9 @@ static void free_cache_entry(TABLE *table) { DBUG_ENTER("free_cache_entry"); + /* Assert that MERGE children are not attached before final close. */ + DBUG_ASSERT(!table->is_children_attached()); + intern_close_table(table); if (!table->in_use) { @@ -901,6 +917,54 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, pthread_mutex_lock(&oldest_unused_share->mutex); VOID(hash_delete(&table_def_cache, (uchar*) oldest_unused_share)); } + DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", + refresh_version)); + if (if_wait_for_refresh) + { + /* + Other threads could wait in a loop in open_and_lock_tables(), + trying to lock one or more of our tables. + + If they wait for the locks in thr_multi_lock(), their lock + request is aborted. They loop in open_and_lock_tables() and + enter open_table(). Here they notice the table is refreshed and + wait for COND_refresh. Then they loop again in + open_and_lock_tables() and this time open_table() succeeds. At + this moment, if we (the FLUSH TABLES thread) are scheduled and + on another FLUSH TABLES enter close_cached_tables(), they could + awake while we sleep below, waiting for others threads (us) to + close their open tables. If this happens, the other threads + would find the tables unlocked. They would get the locks, one + after the other, and could do their destructive work. This is an + issue if we have LOCK TABLES in effect. + + The problem is that the other threads passed all checks in + open_table() before we refresh the table. + + The fix for this problem is to set some_tables_deleted for all + threads with open tables. These threads can still get their + locks, but will immediately release them again after checking + this variable. They will then loop in open_and_lock_tables() + again. There they will wait until we update all tables version + below. + + Setting some_tables_deleted is done by remove_table_from_cache() + in the other branch. + + In other words (reviewer suggestion): You need this setting of + some_tables_deleted for the case when table was opened and all + related checks were passed before incrementing refresh_version + (which you already have) but attempt to lock the table happened + after the call to close_old_data_files() i.e. after removal of + current thread locks. + */ + for (uint idx=0 ; idx < open_cache.records ; idx++) + { + TABLE *table=(TABLE*) hash_element(&open_cache,idx); + if (table->in_use) + table->in_use->some_tables_deleted= 1; + } + } } else { @@ -1073,6 +1137,14 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd) { table->query_id= 0; table->file->ha_reset(); + /* + Detach temporary MERGE children from temporary parent to allow new + attach at next open. Do not do the detach, if close_thread_tables() + is called from a sub-statement. The temporary table might still be + used in the top-level statement. + */ + if (table->child_l || table->parent) + detach_merge_children(table, TRUE); } } } @@ -1170,9 +1242,17 @@ static void close_open_tables(THD *thd) void close_thread_tables(THD *thd) { + TABLE *table; prelocked_mode_type prelocked_mode= thd->prelocked_mode; DBUG_ENTER("close_thread_tables"); +#ifdef EXTRA_DEBUG + DBUG_PRINT("tcache", ("open tables:")); + for (table= thd->open_tables; table; table= table->next) + DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); +#endif + /* We are assuming here that thd->derived_tables contains ONLY derived tables for this substatement. i.e. instead of approach which uses @@ -1186,7 +1266,7 @@ void close_thread_tables(THD *thd) */ if (thd->derived_tables) { - TABLE *table, *next; + TABLE *next; /* Close all derived tables generated in queries like SELECT * FROM (SELECT * FROM t1) @@ -1266,6 +1346,13 @@ void close_thread_tables(THD *thd) if (!thd->active_transaction()) thd->transaction.xid_state.xid.null(); + /* + Note that we need to hold LOCK_open while changing the + open_tables list. Another thread may work on it. + (See: remove_table_from_cache(), mysql_wait_completed_table()) + Closing a MERGE child before the parent would be fatal if the + other thread tries to abort the MERGE lock in between. + */ if (thd->open_tables) close_open_tables(thd); @@ -1292,8 +1379,17 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) DBUG_ENTER("close_thread_table"); DBUG_ASSERT(table->key_read == 0); DBUG_ASSERT(!table->file || table->file->inited == handler::NONE); + DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); *table_ptr=table->next; + /* + When closing a MERGE parent or child table, detach the children first. + Clear child table references to force new assignment at next open. + */ + if (table->child_l || table->parent) + detach_merge_children(table, TRUE); + if (table->needs_reopen_or_name_lock() || thd->version != refresh_version || !table->db_stat) { @@ -1308,6 +1404,9 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) */ DBUG_ASSERT(!table->open_placeholder); + /* Assert that MERGE children are not attached in unused_tables. */ + DBUG_ASSERT(!table->is_children_attached()); + /* Free memory and reset for next loop */ table->file->ha_reset(); table->in_use=0; @@ -1729,6 +1828,8 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list) { TABLE *table; DBUG_ENTER("drop_temporary_table"); + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", + table_list->db, table_list->table_name)); if (!(table= find_temporary_table(thd, table_list))) DBUG_RETURN(1); @@ -1756,6 +1857,24 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list) void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table) { + DBUG_ENTER("close_temporary_table"); + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx alias: '%s'", + table->s->db.str, table->s->table_name.str, + (long) table, table->alias)); + + /* + When closing a MERGE parent or child table, detach the children + first. Clear child table references as MERGE table cannot be + reopened after final close of one of its tables. + + This is necessary here because it is sometimes called with attached + tables and without prior close_thread_tables(). E.g. in + mysql_alter_table(), mysql_rm_table_part2(), mysql_truncate(), + drop_open_table(). + */ + if (table->child_l || table->parent) + detach_merge_children(table, TRUE); + if (table->prev) { table->prev->next= table->next; @@ -1782,6 +1901,7 @@ void close_temporary_table(THD *thd, TABLE *table, slave_open_temp_tables--; } close_temporary(table, free_share, delete_table); + DBUG_VOID_RETURN; } @@ -1797,6 +1917,8 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table) { handlerton *table_type= table->s->db_type(); DBUG_ENTER("close_temporary"); + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", + table->s->db.str, table->s->table_name.str)); free_io_cache(table); closefrm(table, 0); @@ -1843,6 +1965,9 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, static void relink_unused(TABLE *table) { + /* Assert that MERGE children are not attached in unused_tables. */ + DBUG_ASSERT(!table->is_children_attached()); + if (table != unused_tables) { table->prev->next=table->next; /* Remove from unused list */ @@ -1858,6 +1983,77 @@ static void relink_unused(TABLE *table) /** + @brief Prepare an open merge table for close. + + @param[in] thd thread context + @param[in] table table to prepare + @param[in,out] prev_pp pointer to pointer of previous table + + @detail + If the table is a MERGE parent, just detach the children. + If the table is a MERGE child, close the parent (incl. detach). +*/ + +static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp) +{ + DBUG_ENTER("unlink_open_merge"); + + if (table->parent) + { + /* + If MERGE child, close parent too. Closing includes detaching. + + This is used for example in ALTER TABLE t1 RENAME TO t5 under + LOCK TABLES where t1 is a MERGE child: + CREATE TABLE t1 (c1 INT); + CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); + LOCK TABLES t1 WRITE, t2 WRITE; + ALTER TABLE t1 RENAME TO t5; + */ + TABLE *parent= table->parent; + TABLE **prv_p; + + /* Find parent in open_tables list. */ + for (prv_p= &thd->open_tables; + *prv_p && (*prv_p != parent); + prv_p= &(*prv_p)->next) {} + if (*prv_p) + { + /* Special treatment required if child follows parent in list. */ + if (*prev_pp == &parent->next) + *prev_pp= prv_p; + /* + Remove parent from open_tables list and close it. + This includes detaching and hence clearing parent references. + */ + close_thread_table(thd, prv_p); + } + } + else if (table->child_l) + { + /* + When closing a MERGE parent, detach the children first. It is + not necessary to clear the child or parent table reference of + this table because the TABLE is freed. But we need to clear + the child or parent references of the other belonging tables + so that they cannot be moved into the unused_tables chain with + these pointers set. + + This is used for example in ALTER TABLE t2 RENAME TO t5 under + LOCK TABLES where t2 is a MERGE parent: + CREATE TABLE t1 (c1 INT); + CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1); + LOCK TABLES t1 WRITE, t2 WRITE; + ALTER TABLE t2 RENAME TO t5; + */ + detach_merge_children(table, TRUE); + } + + DBUG_VOID_RETURN; +} + + +/** @brief Remove all instances of table from thread's open list and table cache. @@ -1868,7 +2064,7 @@ static void relink_unused(TABLE *table) FALSE - otherwise @note When unlock parameter is FALSE or current thread doesn't have - any tables locked with LOCK TABLES tables are assumed to be + any tables locked with LOCK TABLES, tables are assumed to be not locked (for example already unlocked). */ @@ -1876,31 +2072,45 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) { char key[MAX_DBKEY_LENGTH]; uint key_length= find->s->table_cache_key.length; - TABLE *list, **prev, *next; + TABLE *list, **prev; DBUG_ENTER("unlink_open_table"); safe_mutex_assert_owner(&LOCK_open); - list= thd->open_tables; - prev= &thd->open_tables; memcpy(key, find->s->table_cache_key.str, key_length); - for (; list ; list=next) + /* + Note that we need to hold LOCK_open while changing the + open_tables list. Another thread may work on it. + (See: remove_table_from_cache(), mysql_wait_completed_table()) + Closing a MERGE child before the parent would be fatal if the + other thread tries to abort the MERGE lock in between. + */ + for (prev= &thd->open_tables; *prev; ) { - next=list->next; + list= *prev; + if (list->s->table_cache_key.length == key_length && !memcmp(list->s->table_cache_key.str, key, key_length)) { if (unlock && thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables, list, TRUE); + mysql_lock_remove(thd, thd->locked_tables, + list->parent ? list->parent : list, TRUE); + + /* Prepare MERGE table for close. Close parent if necessary. */ + unlink_open_merge(thd, list, &prev); + + /* Remove table from open_tables list. */ + *prev= list->next; + /* Close table. */ VOID(hash_delete(&open_cache,(uchar*) list)); // Close table } else { - *prev=list; // put in use list + /* Step to next entry in open_tables list. */ prev= &list->next; } } - *prev=0; + // Notify any 'refresh' threads broadcast_refresh(); DBUG_VOID_RETURN; @@ -2383,9 +2593,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->s->table_name.str); DBUG_RETURN(0); } + /* + When looking for a usable TABLE, ignore MERGE children, as they + belong to their parent and cannot be used explicitly. + */ if (!my_strcasecmp(system_charset_info, table->alias, alias) && table->query_id != thd->query_id && /* skip tables already used */ - !(thd->prelocked_mode && table->query_id)) + !(thd->prelocked_mode && table->query_id) && + !table->parent) { int distance= ((int) table->reginfo.lock_type - (int) table_list->lock_type); @@ -2534,6 +2749,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length, &state)) { + DBUG_PRINT("tcache", ("in_use table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); /* Here we flush tables marked for flush. Normally, table->s->version contains the value of @@ -2622,6 +2839,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } if (table) { + DBUG_PRINT("tcache", ("unused table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); /* Unlink the table from "unused_tables" list. */ if (table == unused_tables) { // First unused @@ -2637,6 +2856,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { /* Insert a new TABLE instance into the open cache */ int error; + DBUG_PRINT("tcache", ("opening new table")); /* Free cache if too big */ while (open_cache.records > table_cache_size && unused_tables) VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */ @@ -2703,7 +2923,9 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(0); // VIEW } - DBUG_PRINT("info", ("inserting table 0x%lx into the cache", (long) table)); + DBUG_PRINT("info", ("inserting table '%s'.'%s' 0x%lx into the cache", + table->s->db.str, table->s->table_name.str, + (long) table)); VOID(my_hash_insert(&open_cache,(uchar*) table)); } @@ -2793,9 +3015,12 @@ bool reopen_table(TABLE *table) TABLE_LIST table_list; THD *thd= table->in_use; DBUG_ENTER("reopen_table"); + DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); DBUG_ASSERT(table->s->ref_count == 0); DBUG_ASSERT(!table->sort.io_cache); + DBUG_ASSERT(!table->children_attached); #ifdef EXTRA_DEBUG if (table->db_stat) @@ -2836,6 +3061,17 @@ bool reopen_table(TABLE *table) tmp.next= table->next; tmp.prev= table->prev; + /* Preserve MERGE parent. */ + tmp.parent= table->parent; + /* Fix MERGE child list and check for unchanged union. */ + if ((table->child_l || tmp.child_l) && + fix_merge_after_open(table->child_l, table->child_last_l, + tmp.child_l, tmp.child_last_l)) + { + VOID(closefrm(&tmp, 1)); // close file, free everything + goto end; + } + delete table->triggers; if (table->file) VOID(closefrm(table, 1)); // close file, free everything @@ -2857,6 +3093,11 @@ bool reopen_table(TABLE *table) } if (table->triggers) table->triggers->set_table(table); + /* + Do not attach MERGE children here. The children might be reopened + after the parent. Attach children after reopening all tables that + require reopen. See for example reopen_tables(). + */ broadcast_refresh(); error=0; @@ -2909,7 +3150,22 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, !strcmp(table->s->db.str, db)) { if (thd->locked_tables) - mysql_lock_remove(thd, thd->locked_tables, table, TRUE); + { + if (table->parent) + { + /* + If MERGE child, need to reopen parent too. This means that + the first child to be closed will detach all children from + the parent and close it. OTOH in most cases a MERGE table + won't have multiple children with the same db.table_name. + */ + mysql_lock_remove(thd, thd->locked_tables, table->parent, TRUE); + table->parent->open_placeholder= 1; + close_handle_and_leave_table_as_lock(table->parent); + } + else + mysql_lock_remove(thd, thd->locked_tables, table, TRUE); + } table->open_placeholder= 1; close_handle_and_leave_table_as_lock(table); } @@ -2919,6 +3175,62 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, /** + @brief Reattach MERGE children after reopen. + + @param[in] thd thread context + @param[in,out] err_tables_p pointer to pointer of tables in error + + @return status + @retval FALSE OK, err_tables_p unchanged + @retval TRUE Error, err_tables_p contains table(s) +*/ + +static bool reattach_merge(THD *thd, TABLE **err_tables_p) +{ + TABLE *table; + TABLE *next; + TABLE **prv_p= &thd->open_tables; + bool error= FALSE; + DBUG_ENTER("reattach_merge"); + + for (table= thd->open_tables; table; table= next) + { + next= table->next; + DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx next: 0x%lx", + table->s->db.str, table->s->table_name.str, + (long) table, (long) next)); + /* Reattach children for MERGE tables with "closed data files" only. */ + if (table->child_l && !table->children_attached) + { + DBUG_PRINT("tcache", ("MERGE parent, attach children")); + if(table->file->extra(HA_EXTRA_ATTACH_CHILDREN)) + { + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); + error= TRUE; + /* Remove table from open_tables. */ + *prv_p= next; + if (next) + prv_p= &next->next; + /* Stack table on error list. */ + table->next= *err_tables_p; + *err_tables_p= table; + continue; + } + else + { + table->children_attached= TRUE; + DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx", + table->s->db.str, + table->s->table_name.str, (long) table)); + } + } + prv_p= &table->next; + } + DBUG_RETURN(error); +} + + +/** @brief Reopen all tables with closed data files. @param thd Thread context @@ -2942,7 +3254,9 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) { TABLE *table,*next,**prev; TABLE **tables,**tables_ptr; // For locks + TABLE *err_tables= NULL; bool error=0, not_used; + bool merge_table_found= FALSE; DBUG_ENTER("reopen_tables"); if (!thd->open_tables) @@ -2951,10 +3265,15 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) safe_mutex_assert_owner(&LOCK_open); if (get_locks) { - /* The ptr is checked later */ + /* + The ptr is checked later + Do not handle locks of MERGE children. + */ uint opens=0; for (table= thd->open_tables; table ; table=table->next) - opens++; + if (!table->parent) + opens++; + DBUG_PRINT("tcache", ("open tables to lock: %u", opens)); tables= (TABLE**) my_alloca(sizeof(TABLE*)*opens); } else @@ -2966,17 +3285,37 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) { uint db_stat=table->db_stat; next=table->next; + DBUG_PRINT("tcache", ("open table: '%s'.'%s' 0x%lx " + "parent: 0x%lx db_stat: %u", + table->s->db.str, table->s->table_name.str, + (long) table, (long) table->parent, db_stat)); + if (table->child_l && !db_stat) + merge_table_found= TRUE; if (!tables || (!db_stat && reopen_table(table))) { my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); + /* + If we could not allocate 'tables', we may close open tables + here. If a MERGE table is affected, detach the children first. + It is not necessary to clear the child or parent table reference + of this table because the TABLE is freed. But we need to clear + the child or parent references of the other belonging tables so + that they cannot be moved into the unused_tables chain with + these pointers set. + */ + if (table->child_l || table->parent) + detach_merge_children(table, TRUE); VOID(hash_delete(&open_cache,(uchar*) table)); error=1; } else { + DBUG_PRINT("tcache", ("opened. need lock: %d", + get_locks && !db_stat && !table->parent)); *prev= table; prev= &table->next; - if (get_locks && !db_stat) + /* Do not handle locks of MERGE children. */ + if (get_locks && !db_stat && !table->parent) *tables_ptr++= table; // need new lock on this if (in_refresh) { @@ -2985,25 +3324,52 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) } } } + *prev=0; + /* + When all tables are open again, we can re-attach MERGE children to + their parents. All TABLE objects are still present. + */ + DBUG_PRINT("tcache", ("re-attaching MERGE tables: %d", merge_table_found)); + if (!error && merge_table_found && reattach_merge(thd, &err_tables)) + { + while (err_tables) + { + VOID(hash_delete(&open_cache, (uchar*) err_tables)); + err_tables= err_tables->next; + } + } + DBUG_PRINT("tcache", ("open tables to lock: %u", + (uint) (tables_ptr - tables))); if (tables != tables_ptr) // Should we get back old locks { MYSQL_LOCK *lock; - /* We should always get these locks */ + /* + We should always get these locks. Anyway, we must not go into + wait_for_tables() as it tries to acquire LOCK_open, which is + already locked. + */ thd->some_tables_deleted=0; if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), - 0, ¬_used))) + MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, ¬_used))) { thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock); } else + { + /* + This case should only happen if there is a bug in the reopen logic. + Need to issue error message to have a reply for the application. + Not exactly what happened though, but close enough. + */ + my_error(ER_LOCK_DEADLOCK, MYF(0)); error=1; + } } if (get_locks && tables) { my_afree((uchar*) tables); } broadcast_refresh(); - *prev=0; DBUG_RETURN(error); } @@ -3030,6 +3396,11 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, for (; table ; table=table->next) { + DBUG_PRINT("tcache", ("checking table: '%s'.'%s' 0x%lx", + table->s->db.str, table->s->table_name.str, + (long) table)); + DBUG_PRINT("tcache", ("needs refresh: %d is open: %u", + table->needs_reopen_or_name_lock(), table->db_stat)); /* Reopen marked for flush. */ @@ -3041,13 +3412,33 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, if (morph_locks) { /* - Wake up threads waiting for table-level lock on this table - so they won't sneak in when we will temporarily remove our - lock on it. This will also give them a chance to close their - instances of this table. + Forward lock handling to MERGE parent. But unlock parent + once only. */ - mysql_lock_abort(thd, table, TRUE); - mysql_lock_remove(thd, thd->locked_tables, table, TRUE); + TABLE *ulcktbl= table->parent ? table->parent : table; + if (ulcktbl->lock_count) + { + /* + Wake up threads waiting for table-level lock on this table + so they won't sneak in when we will temporarily remove our + lock on it. This will also give them a chance to close their + instances of this table. + */ + mysql_lock_abort(thd, ulcktbl, TRUE); + mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE); + ulcktbl->lock_count= 0; + } + if ((ulcktbl != table) && ulcktbl->db_stat) + { + /* + Close the parent too. Note that parent can come later in + the list of tables. It will then be noticed as closed and + as a placeholder. When this happens, do not clear the + placeholder flag. See the branch below ("***"). + */ + ulcktbl->open_placeholder= 1; + close_handle_and_leave_table_as_lock(ulcktbl); + } /* We want to protect the table from concurrent DDL operations (like RENAME TABLE) until we will re-open and re-lock it. @@ -3056,7 +3447,7 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, } close_handle_and_leave_table_as_lock(table); } - else if (table->open_placeholder) + else if (table->open_placeholder && !morph_locks) { /* We come here only in close-for-back-off scenario. So we have to @@ -3064,8 +3455,11 @@ void close_old_data_files(THD *thd, TABLE *table, bool morph_locks, in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2 and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will probably want to let it stay. + + Note "***": We must not enter this branch if the placeholder + flag has been set because of a former close through a child. + See above the comment that refers to this note. */ - DBUG_ASSERT(!morph_locks); table->open_placeholder= 0; } } @@ -3186,13 +3580,29 @@ TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name) prev= &thd->open_tables; DBUG_ENTER("drop_locked_tables"); + /* + Note that we need to hold LOCK_open while changing the + open_tables list. Another thread may work on it. + (See: remove_table_from_cache(), mysql_wait_completed_table()) + Closing a MERGE child before the parent would be fatal if the + other thread tries to abort the MERGE lock in between. + */ for (table= thd->open_tables; table ; table=next) { next=table->next; if (!strcmp(table->s->table_name.str, table_name) && !strcmp(table->s->db.str, db)) { - mysql_lock_remove(thd, thd->locked_tables, table, TRUE); + /* If MERGE child, forward lock handling to parent. */ + mysql_lock_remove(thd, thd->locked_tables, + table->parent ? table->parent : table, TRUE); + /* + When closing a MERGE parent or child table, detach the children first. + Clear child table references in case this object is opened again. + */ + if (table->child_l || table->parent) + detach_merge_children(table, TRUE); + if (!found) { found= table; @@ -3241,7 +3651,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) if (!strcmp(table->s->table_name.str, table_name) && !strcmp(table->s->db.str, db)) { - mysql_lock_abort(thd,table, TRUE); + /* If MERGE child, forward lock handling to parent. */ + mysql_lock_abort(thd, table->parent ? table->parent : table, TRUE); break; } } @@ -3272,7 +3683,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) share->table_map_id is given a value that with a high certainty is not used by any other table (the only case where a table id can be reused is on wrap-around, which means more than 4 billion table - shares open at the same time). + share opens have been executed while one table was open all the + time). share->table_map_id is not ~0UL. */ @@ -3511,6 +3923,340 @@ err: } +/** + @brief Add list of MERGE children to a TABLE_LIST list. + + @param[in] tlist the parent TABLE_LIST object just opened + + @return status + @retval 0 OK + @retval != 0 Error + + @detail + When a MERGE parent table has just been opened, insert the + TABLE_LIST chain from the MERGE handle into the table list used for + opening tables for this statement. This lets the children be opened + too. +*/ + +static int add_merge_table_list(TABLE_LIST *tlist) +{ + TABLE *parent= tlist->table; + TABLE_LIST *child_l; + DBUG_ENTER("add_merge_table_list"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str, + parent->s->table_name.str, (long) parent)); + + /* Must not call this with attached children. */ + DBUG_ASSERT(!parent->children_attached); + /* Must not call this with children list in place. */ + DBUG_ASSERT(tlist->next_global != parent->child_l); + /* Prevent inclusion of another MERGE table. Could make infinite recursion. */ + if (tlist->parent_l) + { + my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), tlist->alias); + DBUG_RETURN(1); + } + + /* Fix children.*/ + for (child_l= parent->child_l; ; child_l= child_l->next_global) + { + /* + Note: child_l->table may still be set if this parent was taken + from the unused_tables chain. Ignore this fact here. The + reference will be replaced by the handler in + ::extra(HA_EXTRA_ATTACH_CHILDREN). + */ + + /* Set lock type. */ + child_l->lock_type= tlist->lock_type; + + /* Set parent reference. */ + child_l->parent_l= tlist; + + /* Break when this was the last child. */ + if (&child_l->next_global == parent->child_last_l) + break; + } + + /* Insert children into the table list. */ + *parent->child_last_l= tlist->next_global; + tlist->next_global= parent->child_l; + + /* + Do not fix the prev_global pointers. We will remove the + chain soon anyway. + */ + + DBUG_RETURN(0); +} + + +/** + @brief Attach MERGE children to the parent. + + @param[in] tlist the child TABLE_LIST object just opened + + @return status + @retval 0 OK + @retval != 0 Error + + @note + This is called when the last MERGE child has just been opened, let + the handler attach the MyISAM tables to the MERGE table. Remove + MERGE TABLE_LIST chain from the statement list so that it cannot be + changed or freed. +*/ + +static int attach_merge_children(TABLE_LIST *tlist) +{ + TABLE *parent= tlist->parent_l->table; + int error; + DBUG_ENTER("attach_merge_children"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str, + parent->s->table_name.str, (long) parent)); + + /* Must not call this with attached children. */ + DBUG_ASSERT(!parent->children_attached); + /* Must call this with children list in place. */ + DBUG_ASSERT(tlist->parent_l->next_global == parent->child_l); + + /* Attach MyISAM tables to MERGE table. */ + error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN); + + /* + Remove children from the table list. Even in case of an error. + This should prevent tampering with them. + */ + tlist->parent_l->next_global= *parent->child_last_l; + + /* + Do not fix the last childs next_global pointer. It is needed for + stepping to the next table in the enclosing loop in open_tables(). + Do not fix prev_global pointers. We did not set them. + */ + + if (error) + { + DBUG_PRINT("error", ("attaching MERGE children failed: %d", my_errno)); + parent->file->print_error(error, MYF(0)); + DBUG_RETURN(1); + } + + parent->children_attached= TRUE; + DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx", parent->s->db.str, + parent->s->table_name.str, (long) parent)); + + /* + Note that we have the cildren in the thd->open_tables list at this + point. + */ + + DBUG_RETURN(0); +} + + +/** + @brief Detach MERGE children from the parent. + + @note + Call this before the first table of a MERGE table (parent or child) + is closed. + + When closing thread tables at end of statement, both parent and + children are in thd->open_tables and will be closed. In most cases + the children will be closed before the parent. They are opened after + the parent and thus stacked into thd->open_tables before it. + + To avoid that we touch a closed children in any way, we must detach + the children from the parent when the first belonging table is + closed (parent or child). + + All references to the children should be removed on handler level + and optionally on table level. + + @note + Assure that you call it for a MERGE parent or child only. + Either table->child_l or table->parent must be set. + + @param[in] table the TABLE object of the parent + @param[in] clear_refs if to clear TABLE references + this must be true when called from + close_thread_tables() to enable fresh + open in open_tables() + it must be false when called in preparation + for reopen_tables() +*/ + +void detach_merge_children(TABLE *table, bool clear_refs) +{ + TABLE_LIST *child_l; + TABLE *parent= table->child_l ? table : table->parent; + bool first_detach; + DBUG_ENTER("detach_merge_children"); + /* + Either table->child_l or table->parent must be set. Parent must have + child_l set. + */ + DBUG_ASSERT(parent && parent->child_l); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx clear_refs: %d", + table->s->db.str, table->s->table_name.str, + (long) table, clear_refs)); + DBUG_PRINT("myrg", ("parent: '%s'.'%s' 0x%lx", parent->s->db.str, + parent->s->table_name.str, (long) parent)); + + /* + In a open_tables() loop it can happen that not all tables have their + children attached yet. Also this is called for every child and the + parent from close_thread_tables(). + */ + if ((first_detach= parent->children_attached)) + { + VOID(parent->file->extra(HA_EXTRA_DETACH_CHILDREN)); + parent->children_attached= FALSE; + DBUG_PRINT("myrg", ("detached parent: '%s'.'%s' 0x%lx", parent->s->db.str, + parent->s->table_name.str, (long) parent)); + } + else + DBUG_PRINT("myrg", ("parent is already detached")); + + if (clear_refs) + { + /* In any case clear the own parent reference. (***) */ + table->parent= NULL; + + /* + On the first detach, clear all references. If this table is the + parent, we still may need to clear the child references. The first + detach might not have done this. + */ + if (first_detach || (table == parent)) + { + /* Clear TABLE references to force new assignment at next open. */ + for (child_l= parent->child_l; ; child_l= child_l->next_global) + { + /* + Do not DBUG_ASSERT(child_l->table); open_tables might be + incomplete. + + Clear the parent reference of the children only on the first + detach. The children might already be closed. They will clear + it themseves when this function is called for them with + 'clear_refs' true. See above "(***)". + */ + if (first_detach && child_l->table) + child_l->table->parent= NULL; + + /* Clear the table reference to force new assignment at next open. */ + child_l->table= NULL; + + /* Break when this was the last child. */ + if (&child_l->next_global == parent->child_last_l) + break; + } + } + } + + DBUG_VOID_RETURN; +} + + +/** + @brief Fix MERGE children after open. + + @param[in] old_child_list first list member from original table + @param[in] old_last pointer to &next_global of last list member + @param[in] new_child_list first list member from freshly opened table + @param[in] new_last pointer to &next_global of last list member + + @return mismatch + @retval FALSE OK, no mismatch + @retval TRUE Error, lists mismatch + + @detail + Main action is to copy TABLE reference for each member of original + child list to new child list. After a fresh open these references + are NULL. Assign the old children to the new table. Some of them + might also be reopened or will be reopened soon. + + Other action is to verify that the table definition with respect to + the UNION list did not change. + + @note + This function terminates the child list if the respective '*_last' + pointer is non-NULL. Do not call it from a place where the list is + embedded in another list and this would break it. + + Terminating the list is required for example in the first + reopen_table() after open_tables(). open_tables() requires the end + of the list not to be terminated because other tables could follow + behind the child list. + + If a '*_last' pointer is NULL, the respective list is assumed to be + NULL terminated. +*/ + +bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, + TABLE_LIST *new_child_list, TABLE_LIST **new_last) +{ + bool mismatch= FALSE; + DBUG_ENTER("fix_merge_after_open"); + DBUG_PRINT("myrg", ("old last addr: 0x%lx new last addr: 0x%lx", + (long) old_last, (long) new_last)); + + /* Terminate the lists for easier check of list end. */ + if (old_last) + *old_last= NULL; + if (new_last) + *new_last= NULL; + + for (;;) + { + DBUG_PRINT("myrg", ("old list item: 0x%lx new list item: 0x%lx", + (long) old_child_list, (long) new_child_list)); + /* Break if one of the list is at its end. */ + if (!old_child_list || !new_child_list) + break; + /* Old table has references to child TABLEs. */ + DBUG_ASSERT(old_child_list->table); + /* New table does not yet have references to child TABLEs. */ + DBUG_ASSERT(!new_child_list->table); + DBUG_PRINT("myrg", ("old table: '%s'.'%s' new table: '%s'.'%s'", + old_child_list->db, old_child_list->table_name, + new_child_list->db, new_child_list->table_name)); + /* Child db.table names must match. */ + if (strcmp(old_child_list->table_name, new_child_list->table_name) || + strcmp(old_child_list->db, new_child_list->db)) + break; + /* + Copy TABLE reference. Child TABLE objects are still in place + though not necessarily open yet. + */ + DBUG_PRINT("myrg", ("old table ref: 0x%lx replaces new table ref: 0x%lx", + (long) old_child_list->table, + (long) new_child_list->table)); + new_child_list->table= old_child_list->table; + /* Step both lists. */ + old_child_list= old_child_list->next_global; + new_child_list= new_child_list->next_global; + } + DBUG_PRINT("myrg", ("end of list, mismatch: %d", mismatch)); + /* + If the list pointers are not both NULL after the loop, then the + lists differ. If the are both identical, but not NULL, then they + have at least one table in common and hence the rest of the list + would be identical too. But in this case the loop woul run until the + list end, where both pointers would become NULL. + */ + if (old_child_list != new_child_list) + mismatch= TRUE; + if (mismatch) + my_error(ER_TABLE_DEF_CHANGED, MYF(0)); + + DBUG_RETURN(mismatch); +} + + /* Open all tables in list @@ -3541,7 +4287,7 @@ err: int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { - TABLE_LIST *tables; + TABLE_LIST *tables= NULL; bool refresh; int result=0; MEM_ROOT new_frm_mem; @@ -3601,6 +4347,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ for (tables= *start; tables ;tables= tables->next_global) { + DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", + tables->db, tables->table_name, (long) tables)); + safe_to_ignore_table= FALSE; /* @@ -3652,6 +4401,10 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) else tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags); } + else + DBUG_PRINT("tcache", ("referenced table: '%s'.'%s' 0x%lx", + tables->db, tables->table_name, + (long) tables->table)); if (!tables->table) { @@ -3683,6 +4436,19 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) goto process_view_routines; } + /* + If in a MERGE table open, we need to remove the children list + from statement table list before restarting. Otherwise the list + will be inserted another time. + */ + if (tables->parent_l) + { + TABLE_LIST *parent_l= tables->parent_l; + /* The parent table should be correctly open at this point. */ + DBUG_ASSERT(parent_l->table); + parent_l->next_global= *parent_l->table->child_last_l; + } + if (refresh) // Refresh in progress { /* @@ -3751,6 +4517,24 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) thd->update_lock_default : tables->lock_type; tables->table->grant= tables->grant; + /* Attach MERGE children if not locked already. */ + DBUG_PRINT("tcache", ("is parent: %d is child: %d", + test(tables->table->child_l), + test(tables->parent_l))); + DBUG_PRINT("tcache", ("in lock tables: %d in prelock mode: %d", + test(thd->locked_tables), test(thd->prelocked_mode))); + if (((!thd->locked_tables && !thd->prelocked_mode) || + tables->table->s->tmp_table) && + ((tables->table->child_l && + add_merge_table_list(tables)) || + (tables->parent_l && + (&tables->next_global == tables->parent_l->table->child_last_l) && + attach_merge_children(tables)))) + { + result= -1; + goto err; + } + process_view_routines: /* Again we may need cache all routines used by this view and add @@ -3783,6 +4567,18 @@ process_view_routines: if (query_tables_last_own) thd->lex->mark_as_requiring_prelocking(query_tables_last_own); + if (result && tables) + { + /* + Some functions determine success as (tables->table != NULL). + tables->table is in thd->open_tables. It won't go lost. If the + error happens on a MERGE child, clear the parents TABLE reference. + */ + if (tables->parent_l) + tables->parent_l->table= NULL; + tables->table= NULL; + } + DBUG_PRINT("tcache", ("returning: %d", result)); DBUG_RETURN(result); } @@ -3822,6 +4618,63 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table, } +/** + @brief Open and lock one table + + @param[in] thd thread handle + @param[in] table_l table to open is first table in this list + @param[in] lock_type lock to use for table + + @return table + @retval != NULL OK, opened table returned + @retval NULL Error + + @note + If ok, the following are also set: + table_list->lock_type lock_type + table_list->table table + + @note + If table_l is a list, not a single table, the list is temporarily + broken. + + @detail + This function is meant as a replacement for open_ltable() when + MERGE tables can be opened. open_ltable() cannot open MERGE tables. + + There may be more differences between open_n_lock_single_table() and + open_ltable(). One known difference is that open_ltable() does + neither call decide_logging_format() nor handle some other logging + and locking issues because it does not call lock_tables(). +*/ + +TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, + thr_lock_type lock_type) +{ + TABLE_LIST *save_next_global; + DBUG_ENTER("open_n_lock_single_table"); + + /* Remember old 'next' pointer. */ + save_next_global= table_l->next_global; + /* Break list. */ + table_l->next_global= NULL; + + /* Set requested lock type. */ + table_l->lock_type= lock_type; + /* Allow to open real tables only. */ + table_l->required_type= FRMTYPE_TABLE; + + /* Open the table. */ + if (simple_open_n_lock_tables(thd, table_l)) + table_l->table= NULL; /* Just to be sure. */ + + /* Restore list. */ + table_l->next_global= save_next_global; + + DBUG_RETURN(table_l->table); +} + + /* Open and lock one table @@ -3863,6 +4716,17 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, if (table) { + if (table->child_l) + { + /* A MERGE table must not come here. */ + /* purecov: begin tested */ + my_error(ER_WRONG_OBJECT, MYF(0), table->s->db.str, + table->s->table_name.str, "BASE TABLE"); + table= 0; + goto end; + /* purecov: end */ + } + table_list->lock_type= lock_type; table_list->table= table; table->grant= table_list->grant; @@ -3880,56 +4744,21 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table= 0; } } + + end: thd->proc_info=0; DBUG_RETURN(table); } /* - Open all tables in list and locks them for read without derived - tables processing. - - SYNOPSIS - simple_open_n_lock_tables() - thd - thread handler - tables - list of tables for open&locking - - RETURN - 0 - ok - -1 - error - - NOTE - The lock will automaticaly be freed by close_thread_tables() -*/ - -int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) -{ - uint counter; - bool need_reopen; - DBUG_ENTER("simple_open_n_lock_tables"); - - for ( ; ; ) - { - if (open_tables(thd, &tables, &counter, 0)) - DBUG_RETURN(-1); - if (!lock_tables(thd, tables, counter, &need_reopen)) - break; - if (!need_reopen) - DBUG_RETURN(-1); - close_tables_for_reopen(thd, &tables); - } - DBUG_RETURN(0); -} - - -/* - Open all tables in list, locks them and process derived tables - tables processing. + Open all tables in list, locks them and optionally process derived tables. SYNOPSIS - open_and_lock_tables() + open_and_lock_tables_derived() thd - thread handler tables - list of tables for open&locking + derived - if to handle derived tables RETURN FALSE - ok @@ -3937,27 +4766,43 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) NOTE The lock will automaticaly be freed by close_thread_tables() + + NOTE + There are two convenience functions: + - simple_open_n_lock_tables(thd, tables) without derived handling + - open_and_lock_tables(thd, tables) with derived handling + Both inline functions call open_and_lock_tables_derived() with + the third argument set appropriately. */ -bool open_and_lock_tables(THD *thd, TABLE_LIST *tables) +bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived) { uint counter; bool need_reopen; - DBUG_ENTER("open_and_lock_tables"); + DBUG_ENTER("open_and_lock_tables_derived"); + DBUG_PRINT("enter", ("derived handling: %d", derived)); for ( ; ; ) { if (open_tables(thd, &tables, &counter, 0)) DBUG_RETURN(-1); + + DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { + const char *old_proc_info= thd->proc_info; + thd->proc_info= "DBUG sleep"; + my_sleep(6000000); + thd->proc_info= old_proc_info;}); + if (!lock_tables(thd, tables, counter, &need_reopen)) break; if (!need_reopen) DBUG_RETURN(-1); close_tables_for_reopen(thd, &tables); } - if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || - (thd->fill_derived_tables() && - mysql_handle_derived(thd->lex, &mysql_derived_filling))) + if (derived && + (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || + (thd->fill_derived_tables() && + mysql_handle_derived(thd->lex, &mysql_derived_filling)))) DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(0); } @@ -4271,7 +5116,17 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) thd->lock= 0; thd->in_lock_tables=0; - for (table= tables; table != first_not_own; table= table->next_global) + /* + When open_and_lock_tables() is called for a single table out of + a table list, the 'next_global' chain is temporarily broken. We + may not find 'first_not_own' before the end of the "list". + Look for example at those places where open_n_lock_single_table() + is called. That function implements the temporary breaking of + a table list for opening a single table. + */ + for (table= tables; + table && table != first_not_own; + table= table->next_global) { if (!table->placeholder()) { @@ -4298,7 +5153,17 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen) else { TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); - for (table= tables; table != first_not_own; table= table->next_global) + /* + When open_and_lock_tables() is called for a single table out of + a table list, the 'next_global' chain is temporarily broken. We + may not find 'first_not_own' before the end of the "list". + Look for example at those places where open_n_lock_single_table() + is called. That function implements the temporary breaking of + a table list for opening a single table. + */ + for (table= tables; + table && table != first_not_own; + table= table->next_global) { if (!table->placeholder() && check_lock_and_start_stmt(thd, table->table, table->lock_type)) @@ -4401,7 +5266,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, saved_cache_key= strmov(tmp_path, path)+1; memcpy(saved_cache_key, cache_key, key_length); - init_tmp_table_share(share, saved_cache_key, key_length, + init_tmp_table_share(thd, share, saved_cache_key, key_length, strend(saved_cache_key)+1, tmp_path); if (open_table_def(thd, share, 0) || @@ -4434,6 +5299,8 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, slave_open_temp_tables++; } tmp_table->pos_in_table_list= 0; + DBUG_PRINT("tmptable", ("opened table: '%s'.'%s' 0x%lx", tmp_table->s->db.str, + tmp_table->s->table_name.str, (long) tmp_table)); DBUG_RETURN(tmp_table); } @@ -7115,7 +7982,7 @@ my_bool mysql_rm_tmp_tables(void) /* We should cut file extention before deleting of table */ memcpy(filePathCopy, filePath, filePath_len - ext_len); filePathCopy[filePath_len - ext_len]= 0; - init_tmp_table_share(&share, "", 0, "", filePathCopy); + init_tmp_table_share(thd, &share, "", 0, "", filePathCopy); if (!open_table_def(thd, &share, 0) && ((handler_file= get_new_handler(&share, thd->mem_root, share.db_type())))) @@ -7217,7 +8084,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, TABLE_SHARE *share; bool result= 0, signalled= 0; DBUG_ENTER("remove_table_from_cache"); - DBUG_PRINT("enter", ("Table: '%s.%s' flags: %u", db, table_name, flags)); + DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) @@ -7232,6 +8099,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, &state)) { THD *in_use; + DBUG_PRINT("tcache", ("found table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); table->s->version=0L; /* Free when thread is ready */ if (!(in_use=table->in_use)) @@ -7270,13 +8139,19 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, } /* Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table + as the thread may be waiting to get a lock for another table. + Note that we need to hold LOCK_open while going through the + list. So that the other thread cannot change it. The other + thread must also hold LOCK_open whenever changing the + open_tables list. Aborting the MERGE lock after a child was + closed and before the parent is closed would be fatal. */ for (TABLE *thd_table= in_use->open_tables; thd_table ; thd_table= thd_table->next) { - if (thd_table->db_stat) // If table is open + /* Do not handle locks of MERGE children. */ + if (thd_table->db_stat && !thd_table->parent) // If table is open signalled|= mysql_lock_abort_for_thread(thd, thd_table); } } @@ -7479,7 +8354,9 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->old_lock_type= lpt->table->reginfo.lock_type; VOID(pthread_mutex_lock(&LOCK_open)); - mysql_lock_abort(lpt->thd, lpt->table, TRUE); + /* If MERGE child, forward lock handling to parent. */ + mysql_lock_abort(lpt->thd, lpt->table->parent ? lpt->table->parent : + lpt->table, TRUE); VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags)); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(0); @@ -7500,14 +8377,18 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) We also downgrade locks after the upgrade to WRITE_ONLY */ +/* purecov: begin unused */ void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) { VOID(pthread_mutex_lock(&LOCK_open)); remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, RTFC_WAIT_OTHER_THREAD_FLAG); VOID(pthread_mutex_unlock(&LOCK_open)); - mysql_lock_downgrade_write(lpt->thd, lpt->table, lpt->old_lock_type); + /* If MERGE child, forward lock handling to parent. */ + mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent : + lpt->table, lpt->old_lock_type); } +/* purecov: end */ /* @@ -7573,13 +8454,19 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table } /* Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table + as the thread may be waiting to get a lock for another table. + Note that we need to hold LOCK_open while going through the + list. So that the other thread cannot change it. The other + thread must also hold LOCK_open whenever changing the + open_tables list. Aborting the MERGE lock after a child was + closed and before the parent is closed would be fatal. */ for (TABLE *thd_table= in_use->open_tables; thd_table ; thd_table= thd_table->next) { - if (thd_table->db_stat) // If table is open + /* Do not handle locks of MERGE children. */ + if (thd_table->db_stat && !thd_table->parent) // If table is open mysql_lock_abort_for_thread(lpt->thd, thd_table); } } @@ -7589,8 +8476,10 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table those in use for removal after completion. Now we also need to abort all that are locked and are not progressing due to being locked by our lock. We don't upgrade our lock here. + If MERGE child, forward lock handling to parent. */ - mysql_lock_abort(lpt->thd, my_table, FALSE); + mysql_lock_abort(lpt->thd, my_table->parent ? my_table->parent : my_table, + FALSE); VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_VOID_RETURN; } @@ -7849,6 +8738,13 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) pthread_mutex_lock(&LOCK_open); found_old_table= false; + /* + Note that we need to hold LOCK_open while changing the + open_tables list. Another thread may work on it. + (See: remove_table_from_cache(), mysql_wait_completed_table()) + Closing a MERGE child before the parent would be fatal if the + other thread tries to abort the MERGE lock in between. + */ while (thd->open_tables) found_old_table|= close_thread_table(thd, &thd->open_tables); diff --git a/sql/sql_class.h b/sql/sql_class.h index f09759ddde6..bbe6c589c97 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1262,14 +1262,16 @@ public: We follow this logic: - when stmt starts, first_successful_insert_id_in_prev_stmt contains the first insert id successfully inserted by the previous stmt. - - as stmt makes progress, handler::insert_id_for_cur_row changes; every - time get_auto_increment() is called, auto_inc_intervals_for_binlog is - augmented with the reserved interval (if statement-based binlogging). + - as stmt makes progress, handler::insert_id_for_cur_row changes; + every time get_auto_increment() is called, + auto_inc_intervals_in_cur_stmt_for_binlog is augmented with the + reserved interval (if statement-based binlogging). - at first successful insertion of an autogenerated value, first_successful_insert_id_in_cur_stmt is set to handler::insert_id_for_cur_row. - - when stmt goes to binlog, auto_inc_intervals_for_binlog is - binlogged if non-empty. + - when stmt goes to binlog, + auto_inc_intervals_in_cur_stmt_for_binlog is binlogged if + non-empty. - when stmt ends, first_successful_insert_id_in_prev_stmt is set to first_successful_insert_id_in_cur_stmt. */ @@ -2489,6 +2491,11 @@ class multi_delete :public select_result_interceptor /* True if at least one table we delete from is not transactional */ bool normal_tables; bool delete_while_scanning; + /* + error handling (rollback and binlogging) can happen in send_eof() + so that afterward send_error() needs to find out that. + */ + bool error_handled; public: multi_delete(TABLE_LIST *dt, uint num_of_tables); @@ -2524,6 +2531,11 @@ class multi_update :public select_result_interceptor /* True if the update operation has made a change in a transactional table */ bool transactional_tables; bool ignore; + /* + error handling (rollback and binlogging) can happen in send_eof() + so that afterward send_error() needs to find out that. + */ + bool error_handled; public: multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list, diff --git a/sql/sql_db.cc b/sql/sql_db.cc index abbf2131957..ad4e0d803eb 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -883,6 +883,13 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + thd->clear_current_stmt_binlog_row_based(); + length= build_table_filename(path, sizeof(path), db, "", "", 0); strmov(path+length, MY_DB_OPT_FILE); // Append db option file name del_dbopt(path); // Remove dboption hash entry diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f183cb3142f..509e736f6e7 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -39,6 +39,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows deleted= 0; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list)) @@ -307,7 +308,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else table->file->unlock_row(); // Row failed selection, release lock on it } - if (thd->killed || thd->is_error()) + killed_status= thd->killed; + if (killed_status != THD::NOT_KILLED || thd->is_error()) error= 1; // Aborted if (will_batch && (loc_error= table->file->end_bulk_delete())) { @@ -352,13 +354,12 @@ cleanup: thd->transaction.stmt.modified_non_trans_table= TRUE; /* See similar binlogging code in sql_update.cc, for comments */ - if ((error < 0) || (deleted && !transactional_table)) + if ((error < 0) || thd->transaction.stmt.modified_non_trans_table) { if (mysql_bin_log.is_open()) { if (error < 0) thd->clear_error(); - /* [binlog]: If 'handler::delete_all_rows()' was called and the storage engine does not inject the rows itself, we replicate @@ -367,7 +368,7 @@ cleanup: */ int log_result= thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_table, FALSE); + transactional_table, FALSE, killed_status); if (log_result && transactional_table) { @@ -548,7 +549,7 @@ bool mysql_multi_delete_prepare(THD *thd) multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg) : delete_tables(dt), deleted(0), found(0), num_of_tables(num_of_tables_arg), error(0), - do_delete(0), transactional_tables(0), normal_tables(0) + do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0) { tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables); } @@ -727,12 +728,14 @@ void multi_delete::send_error(uint errcode,const char *err) /* First send error what ever it is ... */ my_message(errcode, err, MYF(0)); - /* If nothing deleted return */ - if (!deleted) + /* the error was handled or nothing deleted and no side effects return */ + if (error_handled || + !thd->transaction.stmt.modified_non_trans_table && !deleted) DBUG_VOID_RETURN; /* Something already deleted so we have to invalidate cache */ - query_cache_invalidate3(thd, delete_tables, 1); + if (deleted) + query_cache_invalidate3(thd, delete_tables, 1); /* If rows from the first table only has been deleted and it is @@ -752,12 +755,30 @@ void multi_delete::send_error(uint errcode,const char *err) */ error= 1; send_eof(); + DBUG_ASSERT(error_handled); + DBUG_VOID_RETURN; } - DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); + + if (thd->transaction.stmt.modified_non_trans_table) + { + /* + there is only side effects; to binlog with the error + */ + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::ROW_QUERY_TYPE, + thd->query, thd->query_length, + transactional_tables, FALSE); + } + thd->transaction.all.modified_non_trans_table= true; + } + DBUG_ASSERT(!normal_tables || !deleted || + thd->transaction.stmt.modified_non_trans_table); DBUG_VOID_RETURN; } + /* Do delete from other tables. Returns values: @@ -850,6 +871,7 @@ int multi_delete::do_deletes() bool multi_delete::send_eof() { + THD::killed_state killed_status= THD::NOT_KILLED; thd->proc_info="deleting from reference tables"; /* Does deletes for the last n - 1 tables, returns 0 if ok */ @@ -857,7 +879,7 @@ bool multi_delete::send_eof() /* compute a total error to know if something failed */ local_error= local_error || error; - + killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; /* reset used flags */ thd->proc_info="end"; @@ -869,7 +891,9 @@ bool multi_delete::send_eof() { query_cache_invalidate3(thd, delete_tables, 1); } - if ((local_error == 0) || (deleted && normal_tables)) + DBUG_ASSERT(!normal_tables || !deleted || + thd->transaction.stmt.modified_non_trans_table); + if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table) { if (mysql_bin_log.is_open()) { @@ -877,7 +901,7 @@ bool multi_delete::send_eof() thd->clear_error(); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_tables, FALSE) && + transactional_tables, FALSE, killed_status) && !normal_tables) { local_error=1; // Log write failed: roll back the SQL statement @@ -886,7 +910,8 @@ bool multi_delete::send_eof() if (thd->transaction.stmt.modified_non_trans_table) thd->transaction.all.modified_non_trans_table= TRUE; } - DBUG_ASSERT(!normal_tables || !deleted || thd->transaction.stmt.modified_non_trans_table); + if (local_error != 0) + error_handled= TRUE; // to force early leave from ::send_error() /* Commit or rollback the current SQL statement */ if (transactional_tables) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 11e70a2e5da..425c5a33329 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -837,59 +837,58 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, } transactional_table= table->file->has_transactions(); - if ((changed= (info.copied || info.deleted || info.updated)) || - was_insert_delayed) + if ((changed= (info.copied || info.deleted || info.updated))) { /* Invalidate the table in the query cache if something changed. For the transactional algorithm to work the invalidation must be before binlog writing and ha_autocommit_or_rollback */ - if (changed) - query_cache_invalidate3(thd, table_list, 1); - if (error <= 0 || !transactional_table) + query_cache_invalidate3(thd, table_list, 1); + } + if (changed && error <= 0 || thd->transaction.stmt.modified_non_trans_table + || was_insert_delayed) + { + if (mysql_bin_log.is_open()) { - if (mysql_bin_log.is_open()) + if (error <= 0) { - if (error <= 0) - { - /* - [Guilhem wrote] Temporary errors may have filled - thd->net.last_error/errno. For example if there has - been a disk full error when writing the row, and it was - MyISAM, then thd->net.last_error/errno will be set to - "disk full"... and the my_pwrite() will wait until free - space appears, and so when it finishes then the - write_row() was entirely successful - */ - /* todo: consider removing */ - thd->clear_error(); - } - /* bug#22725: - - A query which per-row-loop can not be interrupted with - KILLED, like INSERT, and that does not invoke stored - routines can be binlogged with neglecting the KILLED error. - - If there was no error (error == zero) until after the end of - inserting loop the KILLED flag that appeared later can be - disregarded since previously possible invocation of stored - routines did not result in any error due to the KILLED. In - such case the flag is ignored for constructing binlog event. - */ - DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0); - if (thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query, thd->query_length, - transactional_table, FALSE, - (error>0) ? thd->killed : THD::NOT_KILLED) && - transactional_table) - { - error=1; - } - } - if (thd->transaction.stmt.modified_non_trans_table) - thd->transaction.all.modified_non_trans_table= TRUE; + /* + [Guilhem wrote] Temporary errors may have filled + thd->net.last_error/errno. For example if there has + been a disk full error when writing the row, and it was + MyISAM, then thd->net.last_error/errno will be set to + "disk full"... and the my_pwrite() will wait until free + space appears, and so when it finishes then the + write_row() was entirely successful + */ + /* todo: consider removing */ + thd->clear_error(); + } + /* bug#22725: + + A query which per-row-loop can not be interrupted with + KILLED, like INSERT, and that does not invoke stored + routines can be binlogged with neglecting the KILLED error. + + If there was no error (error == zero) until after the end of + inserting loop the KILLED flag that appeared later can be + disregarded since previously possible invocation of stored + routines did not result in any error due to the KILLED. In + such case the flag is ignored for constructing binlog event. + */ + DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0); + if (thd->binlog_query(THD::ROW_QUERY_TYPE, + thd->query, thd->query_length, + transactional_table, FALSE, + (error>0) ? thd->killed : THD::NOT_KILLED) && + transactional_table) + { + error=1; + } } + if (thd->transaction.stmt.modified_non_trans_table) + thd->transaction.all.modified_non_trans_table= TRUE; } DBUG_ASSERT(transactional_table || !changed || thd->transaction.stmt.modified_non_trans_table); @@ -2273,7 +2272,17 @@ pthread_handler_t handle_delayed_insert(void *arg) parsed using a lex, that depends on initialized thd->lex. */ lex_start(thd); - if (!(di->table=open_ltable(thd, &di->table_list, TL_WRITE_DELAYED, 0))) + thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock() + /* + Statement-based replication of INSERT DELAYED has problems with RAND() + and user vars, so in mixed mode we go to row-based. + */ + thd->lex->set_stmt_unsafe(); + thd->set_current_stmt_binlog_row_based_if_mixed(); + + /* Open table */ + if (!(di->table= open_n_lock_single_table(thd, &di->table_list, + TL_WRITE_DELAYED))) { thd->fatal_error(); // Abort waiting inserts goto err; @@ -3094,6 +3103,7 @@ bool select_insert::send_eof() bool const trans_table= table->file->has_transactions(); ulonglong id; bool changed; + THD::killed_state killed_status= thd->killed; DBUG_ENTER("select_insert::send_eof"); DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", trans_table, table->file->table_type())); @@ -3128,7 +3138,7 @@ bool select_insert::send_eof() thd->clear_error(); thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - trans_table, FALSE); + trans_table, FALSE, killed_status); } /* We will call ha_autocommit_or_rollback() also for @@ -3180,6 +3190,7 @@ void select_insert::abort() { */ if (table) { + bool changed, transactional_table; /* If we are not in prelocked mode, we end the bulk insert started before. @@ -3201,20 +3212,20 @@ void select_insert::abort() { If table creation failed, the number of rows modified will also be zero, so no check for that is made. */ - if (info.copied || info.deleted || info.updated) + changed= (info.copied || info.deleted || info.updated); + transactional_table= table->file->has_transactions(); + if (thd->transaction.stmt.modified_non_trans_table) { - DBUG_ASSERT(table != NULL); - if (!table->file->has_transactions()) - { if (mysql_bin_log.is_open()) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - table->file->has_transactions(), FALSE); - if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table && - !can_rollback_data()) + transactional_table, FALSE); + if (!thd->current_stmt_binlog_row_based && !can_rollback_data()) thd->transaction.all.modified_non_trans_table= TRUE; - query_cache_invalidate3(thd, table, 1); - } + if (changed) + query_cache_invalidate3(thd, table, 1); } + DBUG_ASSERT(transactional_table || !changed || + thd->transaction.stmt.modified_non_trans_table); table->file->ha_release_auto_increment(); } @@ -3309,7 +3320,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, tmp_table.alias= 0; tmp_table.timestamp_field= 0; tmp_table.s= &share; - init_tmp_table_share(&share, "", 0, "", ""); + init_tmp_table_share(thd, &share, "", 0, "", ""); tmp_table.s->db_create_options=0; tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 8bbe1e413b3..c96fbb80b0c 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -85,7 +85,8 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, #ifndef EMBEDDED_LIBRARY static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, - bool transactional_table); + bool transactional_table, + THD::killed_state killed_status); #endif /* EMBEDDED_LIBRARY */ /* @@ -134,6 +135,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, char *tdb= thd->db ? thd->db : db; // Result is never null ulong skip_lines= ex->skip_lines; bool transactional_table; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_load"); #ifdef EMBEDDED_LIBRARY @@ -403,7 +405,16 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, free_blobs(table); /* if pack_blob was used */ table->copy_blobs=0; thd->count_cuted_fields= CHECK_FIELD_IGNORE; - + /* + simulated killing in the middle of per-row loop + must be effective for binlogging + */ + DBUG_EXECUTE_IF("simulate_kill_bug27571", + { + error=1; + thd->killed= THD::KILL_QUERY; + };); + killed_status= (error == 0)? THD::NOT_KILLED : thd->killed; /* We must invalidate the table in query cache before binlog writing and ha_autocommit_... @@ -445,9 +456,10 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, /* If the file was not empty, wrote_create_file is true */ if (lf_info.wrote_create_file) { - if ((info.copied || info.deleted) && !transactional_table) + if (thd->transaction.stmt.modified_non_trans_table) write_execute_load_query_log_event(thd, handle_duplicates, - ignore, transactional_table); + ignore, transactional_table, + killed_status); else { Delete_file_log_event d(thd, db, transactional_table); @@ -492,8 +504,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, read_info.end_io_cache(); if (lf_info.wrote_create_file) { - write_execute_load_query_log_event(thd, handle_duplicates, - ignore, transactional_table); + write_execute_load_query_log_event(thd, handle_duplicates, ignore, + transactional_table,killed_status); } } } @@ -523,7 +535,8 @@ err: /* Not a very useful function; just to avoid duplication of code */ static bool write_execute_load_query_log_event(THD *thd, bool duplicates, bool ignore, - bool transactional_table) + bool transactional_table, + THD::killed_state killed_err_arg) { Execute_load_query_log_event e(thd, thd->query, thd->query_length, @@ -531,7 +544,7 @@ static bool write_execute_load_query_log_event(THD *thd, (char*)thd->lex->fname_end - (char*)thd->query, (duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE : (ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR), - transactional_table, FALSE); + transactional_table, FALSE, killed_err_arg); e.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F; return mysql_bin_log.write(&e); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7e194ac76dc..8ed658df90d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -470,6 +470,46 @@ end: } +/** + @brief Check access privs for a MERGE table and fix children lock types. + + @param[in] thd thread handle + @param[in] db database name + @param[in,out] table_list list of child tables (merge_list) + lock_type and optionally db set per table + + @return status + @retval 0 OK + @retval != 0 Error + + @detail + This function is used for write access to MERGE tables only + (CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for + every child. Set 'db' for every child if not present. +*/ + +static bool check_merge_table_access(THD *thd, char *db, + TABLE_LIST *table_list) +{ + int error= 0; + + if (table_list) + { + /* Check that all tables use the current database */ + TABLE_LIST *tlist; + + for (tlist= table_list; tlist; tlist= tlist->next_local) + { + if (!tlist->db || !tlist->db[0]) + tlist->db= db; /* purecov: inspected */ + } + error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, + table_list,0); + } + return error; +} + + /* This works because items are allocated with sql_alloc() */ void free_items(Item *item) @@ -2080,7 +2120,16 @@ mysql_execute_command(THD *thd) if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; pthread_mutex_lock(&LOCK_active_mi); - res = show_master_info(thd,active_mi); + if (active_mi != NULL) + { + res = show_master_info(thd, active_mi); + } + else + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, + "the master info structure does not exist"); + send_ok(thd); + } pthread_mutex_unlock(&LOCK_active_mi); break; } @@ -2254,6 +2303,19 @@ mysql_execute_command(THD *thd) select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex); + /* + Disable non-empty MERGE tables with CREATE...SELECT. Too + complicated. See Bug #26379. Empty MERGE tables are read-only + and don't allow CREATE...SELECT anyway. + */ + if (create_info.used_fields & HA_CREATE_USED_UNION) + { + my_error(ER_WRONG_OBJECT, MYF(0), create_table->db, + create_table->table_name, "BASE TABLE"); + res= 1; + goto end_with_restore_list; + } + if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); @@ -2935,6 +2997,13 @@ end_with_restore_list: SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, del_result, unit, select_lex); + res|= thd->net.report_error; + if (unlikely(res)) + { + /* If we had a another error reported earlier then this will be ignored */ + del_result->send_error(ER_UNKNOWN_ERROR, "Execution of the query failed"); + del_result->abort(); + } delete del_result; } else @@ -5094,26 +5163,6 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) } -bool check_merge_table_access(THD *thd, char *db, - TABLE_LIST *table_list) -{ - int error=0; - if (table_list) - { - /* Check that all tables use the current database */ - TABLE_LIST *tmp; - for (tmp= table_list; tmp; tmp= tmp->next_local) - { - if (!tmp->db || !tmp->db[0]) - tmp->db=db; - } - error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list,0); - } - return error; -} - - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index a06ad0a4612..64f96f342df 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4951,7 +4951,7 @@ the generated partition syntax in a correct manner. We use the old partitioning also for the new table. We do this by assigning the partition_info from the table loaded in - open_ltable to the partition_info struct used by mysql_create_table + open_table to the partition_info struct used by mysql_create_table later in this method. Case IIb: diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 0249af147b0..88040e2933c 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -369,7 +369,6 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, name=0; // Find first log linfo.index_file_offset = 0; - thd->current_linfo = &linfo; if (mysql_bin_log.find_log_pos(&linfo, name, 1)) { @@ -378,6 +377,10 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, goto err; } + pthread_mutex_lock(&LOCK_thread_count); + thd->current_linfo = &linfo; + pthread_mutex_unlock(&LOCK_thread_count); + if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0) { my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; @@ -1359,7 +1362,6 @@ bool mysql_show_binlog_events(THD* thd) name=0; // Find first log linfo.index_file_offset = 0; - thd->current_linfo = &linfo; if (mysql_bin_log.find_log_pos(&linfo, name, 1)) { @@ -1367,6 +1369,10 @@ bool mysql_show_binlog_events(THD* thd) goto err; } + pthread_mutex_lock(&LOCK_thread_count); + thd->current_linfo = &linfo; + pthread_mutex_unlock(&LOCK_thread_count); + if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0) goto err; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 60f004cda71..952b94e6b6d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9624,7 +9624,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, table->keys_in_use_for_query.init(); table->s= share; - init_tmp_table_share(share, "", 0, tmpname, tmpname); + init_tmp_table_share(thd, share, "", 0, tmpname, tmpname); share->blob_field= blob_field; share->blob_ptr_size= mi_portable_sizeof_char_ptr; share->db_low_byte_first=1; // True for HEAP and MyISAM diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a347482859f..9a7d7c59af3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -29,6 +29,8 @@ #include "event_data_objects.h" #include <my_dir.h> +#define STR_OR_NIL(S) ((S) ? (S) : "<nil>") + #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif @@ -3135,8 +3137,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) goto err; } DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'", - lookup_field_vals.db_value.str, - lookup_field_vals.table_value.str)); + STR_OR_NIL(lookup_field_vals.db_value.str), + STR_OR_NIL(lookup_field_vals.table_value.str))); if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bf4a02ae5cc..91acd8d20a3 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1556,13 +1556,18 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, /* Don't give warnings for not found errors, as we already generate notes */ thd->no_warnings_for_error= 1; + /* Remove the tables from the HANDLER list, if they are in it. */ + mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, 1); + for (table= tables; table; table= table->next_local) { char *db=table->db; handlerton *table_type; enum legacy_db_type frm_db_type; - mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1); + DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx", + table->db, table->table_name, (long) table->table, + table->table ? (long) table->table->s : (long) -1)); error= drop_temporary_table(thd, table); @@ -1690,6 +1695,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, wrong_tables.append(','); wrong_tables.append(String(table->table_name,system_charset_info)); } + DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table, + table->table ? (long) table->table->s : (long) -1)); } /* It's safe to unlock LOCK_open: we have an exclusive lock @@ -3836,6 +3843,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, DBUG_RETURN(send_check_errmsg(thd, table, "restore", "Failed to open partially restored table")); } + /* A MERGE table must not come here. */ + DBUG_ASSERT(!table->table || !table->table->child_l); pthread_mutex_unlock(&LOCK_open); DBUG_RETURN(0); } @@ -3878,6 +3887,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, table= &tmp_table; pthread_mutex_unlock(&LOCK_open); } + + /* A MERGE table must not come here. */ + DBUG_ASSERT(!table->child_l); + /* REPAIR TABLE ... USE_FRM for temporary tables makes little sense. */ @@ -4032,6 +4045,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, char* db = table->db; bool fatal_error=0; + DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name)); + DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options)); strxmov(table_name, db, ".", table->table_name, NullS); thd->open_options|= extra_open_options; table->lock_type= lock_type; @@ -4062,16 +4077,24 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, table->next_local= save_next_local; thd->open_options&= ~extra_open_options; } + DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table)); + if (prepare_func) { + DBUG_PRINT("admin", ("calling prepare_func")); switch ((*prepare_func)(thd, table, check_opt)) { case 1: // error, message written to net ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); + DBUG_PRINT("admin", ("simple error, admin next table")); continue; case -1: // error, message could be written to net + /* purecov: begin inspected */ + DBUG_PRINT("admin", ("severe error, stop")); goto err; + /* purecov: end */ default: // should be 0 otherwise + DBUG_PRINT("admin", ("prepare_func succeeded")); ; } } @@ -4086,6 +4109,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, */ if (!table->table) { + DBUG_PRINT("admin", ("open table failed")); if (!thd->warn_list.elements) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE)); @@ -4100,14 +4124,17 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (table->view) { + DBUG_PRINT("admin", ("calling view_operator_func")); result_code= (*view_operator_func)(thd, table); goto send_result; } if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify) { + /* purecov: begin inspected */ char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE]; uint length; + DBUG_PRINT("admin", ("sending error message")); protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); protocol->store(operator_name, system_charset_info); @@ -4122,11 +4149,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (protocol->write()) goto err; continue; + /* purecov: end */ } /* Close all instances of the table to allow repair to rename files */ if (lock_type == TL_WRITE && table->table->s->version) { + DBUG_PRINT("admin", ("removing table from cache")); pthread_mutex_lock(&LOCK_open); const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting to get writelock"); @@ -4146,6 +4175,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (table->table->s->crashed && operator_func == &handler::ha_check) { + /* purecov: begin inspected */ + DBUG_PRINT("admin", ("sending crashed warning")); protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); protocol->store(operator_name, system_charset_info); @@ -4154,6 +4185,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, system_charset_info); if (protocol->write()) goto err; + /* purecov: end */ } if (operator_func == &handler::ha_repair && @@ -4164,6 +4196,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_ADMIN_NEEDS_ALTER)) { my_bool save_no_send_ok= thd->net.no_send_ok; + DBUG_PRINT("admin", ("recreating table")); ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); tmp_disable_binlog(thd); // binlogging is done by caller if wanted @@ -4176,7 +4209,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } + DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); result_code = (table->table->file->*operator_func)(thd, check_opt); + DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); send_result: @@ -5834,10 +5869,25 @@ view_err: start_waiting_global_read_lock(thd); DBUG_RETURN(error); } - if (!(table=open_ltable(thd, table_list, TL_WRITE_ALLOW_READ, 0))) + + if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ))) DBUG_RETURN(TRUE); table->use_all_columns(); + /* + Prohibit changing of the UNION list of a non-temporary MERGE table + under LOCK tables. It would be quite difficult to reuse a shrinked + set of tables from the old table or to open a new TABLE object for + an extended list and verify that they belong to locked tables. + */ + if (thd->locked_tables && + (create_info->used_fields & HA_CREATE_USED_UNION) && + (table->s->tmp_table == NO_TMP_TABLE)) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + DBUG_RETURN(TRUE); + } + /* Check that we are not trying to rename to an existing table */ if (new_name) { @@ -6305,6 +6355,7 @@ view_err: goto err; /* Open the table if we need to copy the data. */ + DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table)); if (need_copy_table != ALTER_TABLE_METADATA_ONLY) { if (table->s->tmp_table) @@ -6328,6 +6379,10 @@ view_err: } if (!new_table) goto err1; + /* + Note: In case of MERGE table, we do not attach children. We do not + copy data for MERGE tables. Only the children have data. + */ } /* Copy the data if necessary. */ @@ -6335,6 +6390,10 @@ view_err: thd->cuted_fields=0L; thd->proc_info="copy to tmp table"; copied=deleted=0; + /* + We do not copy data for MERGE tables. Only the children have data. + MERGE tables have HA_NO_COPY_ON_ALTER set. + */ if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { /* We don't want update TIMESTAMP fields during ALTER TABLE. */ @@ -6472,7 +6531,10 @@ view_err: if (new_table) { - /* Close the intermediate table that will be the new table */ + /* + Close the intermediate table that will be the new table. + Note that MERGE tables do not have their children attached here. + */ intern_close_table(new_table); my_free(new_table,MYF(0)); } @@ -6565,6 +6627,7 @@ view_err: /* Now we have to inform handler that new .FRM file is in place. To do this we need to obtain a handler object for it. + NO need to tamper with MERGE tables. The real open is done later. */ TABLE *t_table; if (new_name != table_name || new_db != db) @@ -6632,7 +6695,7 @@ view_err: /* For the alter table to be properly flushed to the logs, we have to open the new table. If not, we get a problem on server - shutdown. + shutdown. But we do not need to attach MERGE children. */ char path[FN_REFLEN]; TABLE *t_table; @@ -6962,6 +7025,12 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) Alter_info alter_info; DBUG_ENTER("mysql_recreate_table"); + DBUG_ASSERT(!table_list->next_global); + /* + table_list->table has been closed and freed. Do not reference + uninitialized data. open_tables() could fail. + */ + table_list->table= NULL; bzero((char*) &create_info, sizeof(create_info)); create_info.db_type= 0; @@ -6993,6 +7062,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); + /* Open one table after the other to keep lock time as short as possible. */ for (table= tables; table; table= table->next_local) { char table_name[NAME_LEN*2+2]; @@ -7000,7 +7070,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_ltable(thd, table, TL_READ, 0); + t= table->table= open_n_lock_single_table(thd, table, TL_READ); thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ce26b025430..3129bd81572 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -436,13 +436,37 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (lock_table_names(thd, tables)) goto end; - /* We also don't allow creation of triggers on views. */ - tables->required_type= FRMTYPE_TABLE; - - if (reopen_name_locked_table(thd, tables, TRUE)) + /* + If the table is under LOCK TABLES, lock_table_names() does not set + tables->table. Find the table in open_tables. + */ + if (!tables->table && thd->locked_tables) + { + for (table= thd->open_tables; + table && (strcmp(table->s->table_name.str, tables->table_name) || + strcmp(table->s->db.str, tables->db)); + table= table->next) {} + tables->table= table; + } + if (!tables->table) { - unlock_table_name(thd, tables); + /* purecov: begin inspected */ + my_error(ER_TABLE_NOT_LOCKED, MYF(0), tables->alias); goto end; + /* purecov: end */ + } + + /* No need to reopen the table if it is locked with LOCK TABLES. */ + if (!thd->locked_tables || (tables->table->in_use != thd)) + { + /* We also don't allow creation of triggers on views. */ + tables->required_type= FRMTYPE_TABLE; + + if (reopen_name_locked_table(thd, tables, TRUE)) + { + unlock_table_name(thd, tables); + goto end; + } } table= tables->table; @@ -462,6 +486,19 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) table->triggers->create_trigger(thd, tables, &stmt_query): table->triggers->drop_trigger(thd, tables, &stmt_query)); + /* Under LOCK TABLES we must reopen the table to activate the trigger. */ + if (!result && thd->locked_tables) + { + /* + Must not use table->s->db.str or table->s->table_name.str here. + The strings are used in a loop even after the share may be freed. + */ + close_data_files_and_morph_locks(thd, tables->db, tables->table_name); + thd->in_lock_tables= 1; + result= reopen_tables(thd, 1, 0); + thd->in_lock_tables= 0; + } + end: if (!result) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 46022c9f743..ecb7acda61b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -203,6 +203,7 @@ int mysql_update(THD *thd, bool need_reopen; ulonglong id; List<Item> all_fields; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -714,45 +715,25 @@ int mysql_update(THD *thd, thd->row_count++; } dup_key_found= 0; - - if (!transactional_table && updated > 0) - thd->transaction.stmt.modified_non_trans_table= TRUE; - - /* - todo bug#27571: to avoid asynchronization of `error' and - `error_code' of binlog event constructor - - The concept, which is a bit different for insert(!), is to - replace `error' assignment with the following lines - - killed_status= thd->killed; // get the status of the volatile - - Notice: thd->killed is type of "state" whereas the lhs has - "status" the suffix which translates according to WordNet: a state - at a particular time - at the time of the end of per-row loop in - our case. Binlogging ops are conducted with the status. - - error= (killed_status == THD::NOT_KILLED)? error : 1; - - which applies to most mysql_$query functions. - Event's constructor will accept `killed_status' as an argument: - - Query_log_event qinfo(..., killed_status); - - thd->killed might be changed after killed_status had got cached and this - won't affect binlogging event but other effects remain. - - Open issue: In a case the error happened not because of KILLED - - and then KILLED was caught later still within the loop - we shall - do something to avoid binlogging of incorrect ER_SERVER_SHUTDOWN - error_code. + Caching the killed status to pass as the arg to query event constuctor; + The cached value can not change whereas the killed status can + (externally) since this point and change of the latter won't affect + binlogging. + It's assumed that if an error was set in combination with an effective + killed status then the error is due to killing. */ - - if (thd->killed && !error) - error= 1; // Aborted - else if (will_batch && - (loc_error= table->file->exec_bulk_update(&dup_key_found))) + killed_status= thd->killed; // get the status of the volatile + // simulated killing after the loop must be ineffective for binlogging + DBUG_EXECUTE_IF("simulate_kill_bug27571", + { + thd->killed= THD::KILL_QUERY; + };); + error= (killed_status == THD::NOT_KILLED)? error : 1; + + if (error && + will_batch && + (loc_error= table->file->exec_bulk_update(&dup_key_found))) /* An error has occurred when a batched update was performed and returned an error indication. It cannot be an allowed duplicate key error since @@ -774,6 +755,10 @@ int mysql_update(THD *thd, if (will_batch) table->file->end_bulk_update(); table->file->try_semi_consistent_read(0); + + if (!transactional_table && updated > 0) + thd->transaction.stmt.modified_non_trans_table= TRUE; + end_read_record(&info); delete select; thd->proc_info= "end"; @@ -797,7 +782,7 @@ int mysql_update(THD *thd, Sometimes we want to binlog even if we updated no rows, in case user used it to be sure master and slave are in same state. */ - if ((error < 0) || (updated && !transactional_table)) + if ((error < 0) || thd->transaction.stmt.modified_non_trans_table) { if (mysql_bin_log.is_open()) { @@ -805,7 +790,7 @@ int mysql_update(THD *thd, thd->clear_error(); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_table, FALSE) && + transactional_table, FALSE, killed_status) && transactional_table) { error=1; // Rollback update @@ -1215,8 +1200,8 @@ multi_update::multi_update(TABLE_LIST *table_list, :all_tables(table_list), leaves(leaves_list), update_tables(0), tmp_tables(0), updated(0), found(0), fields(field_list), values(value_list), table_count(0), copy_field(0), - handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0), - transactional_tables(1), ignore(ignore_arg) + handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), + transactional_tables(1), ignore(ignore_arg), error_handled(0) {} @@ -1418,7 +1403,6 @@ multi_update::initialize_tables(JOIN *join) if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) DBUG_RETURN(1); main_table=join->join_tab->table; - trans_safe= transactional_tables= main_table->file->has_transactions(); table_to_update= 0; /* Any update has at least one pair (field, value) */ @@ -1713,12 +1697,14 @@ void multi_update::send_error(uint errcode,const char *err) /* First send error what ever it is ... */ my_error(errcode, MYF(0), err); - /* If nothing updated return */ - if (updated == 0) /* the counter might be reset in send_eof */ - return; /* and then the query has been binlogged */ + /* the error was handled or nothing deleted and no side effects return */ + if (error_handled || + !thd->transaction.stmt.modified_non_trans_table && !updated) + return; /* Something already updated so we have to invalidate cache */ - query_cache_invalidate3(thd, update_tables, 1); + if (updated) + query_cache_invalidate3(thd, update_tables, 1); /* If all tables that has been updated are trans safe then just do rollback. If not attempt to do remaining updates. @@ -1750,12 +1736,16 @@ void multi_update::send_error(uint errcode,const char *err) */ if (mysql_bin_log.is_open()) { + /* + THD::killed status might not have been set ON at time of an error + got caught and if happens later the killed error is written + into repl event. + */ thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, transactional_tables, FALSE); } - if (!trans_safe) - thd->transaction.all.modified_non_trans_table= TRUE; + thd->transaction.all.modified_non_trans_table= TRUE; } DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table); @@ -1947,11 +1937,20 @@ bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; ulonglong id; + THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("multi_update::send_eof"); thd->proc_info="updating reference tables"; - /* Does updates for the last n - 1 tables, returns 0 if ok */ + /* + Does updates for the last n - 1 tables, returns 0 if ok; + error takes into account killed status gained in do_updates() + */ int local_error = (table_count) ? do_updates(0) : 0; + /* + if local_error is not set ON until after do_updates() then + later carried out killing should not affect binlogging. + */ + killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; thd->proc_info= "end"; /* We must invalidate the query cache before binlog writing and @@ -1978,11 +1977,9 @@ bool multi_update::send_eof() { if (local_error == 0) thd->clear_error(); - else - updated= 0; /* if there's an error binlog it here not in ::send_error */ if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - transactional_tables, FALSE) && + transactional_tables, FALSE, killed_status) && trans_safe) { local_error= 1; // Rollback update @@ -1991,6 +1988,8 @@ bool multi_update::send_eof() if (thd->transaction.stmt.modified_non_trans_table) thd->transaction.all.modified_non_trans_table= TRUE; } + if (local_error != 0) + error_handled= TRUE; // to force early leave from ::send_error() if (transactional_tables) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 66316d1ab5c..5de142ca83a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5439,7 +5439,7 @@ alter_commands: lex->no_write_to_binlog= $3; lex->check_opt.init(); } - opt_no_write_to_binlog opt_mi_check_type + opt_no_write_to_binlog | ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog all_or_alt_part_name_list { @@ -5448,7 +5448,6 @@ alter_commands: lex->no_write_to_binlog= $3; lex->check_opt.init(); } - opt_mi_check_type | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list { LEX *lex= Lex; @@ -5931,7 +5930,7 @@ analyze: lex->no_write_to_binlog= $2; lex->check_opt.init(); } - table_list opt_mi_check_type + table_list {} ; @@ -5987,7 +5986,7 @@ optimize: lex->no_write_to_binlog= $2; lex->check_opt.init(); } - table_list opt_mi_check_type + table_list {} ; @@ -6751,6 +6750,7 @@ function_call_keyword: | CURRENT_USER optional_braces { $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context()); + Lex->set_stmt_unsafe(); Lex->safe_to_cache_query= 0; } | DATE_SYM '(' expr ')' @@ -6796,6 +6796,7 @@ function_call_keyword: | USER '(' ')' { $$= new (YYTHD->mem_root) Item_func_user(); + Lex->set_stmt_unsafe(); Lex->safe_to_cache_query=0; } | YEAR_SYM '(' expr ')' diff --git a/sql/table.cc b/sql/table.cc index c3ddb809b9e..2143faaff5c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -329,6 +329,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, SYNOPSIS init_tmp_table_share() + thd thread handle share Share to fill key Table_cache_key, as generated from create_table_def_key. must start with db name. @@ -346,7 +347,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, use key_length= 0 as neither table_cache_key or key_length will be used). */ -void init_tmp_table_share(TABLE_SHARE *share, const char *key, +void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, uint key_length, const char *table_name, const char *path) { @@ -373,9 +374,14 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key, anyway to be able to catch errors. */ share->table_map_version= ~(ulonglong)0; - share->table_map_id= ~0UL; share->cached_row_logging_check= -1; + /* + table_map_id is also used for MERGE tables to suppress repeated + compatibility checks. + */ + share->table_map_id= (ulong) thd->query_id; + DBUG_VOID_RETURN; } @@ -4477,6 +4483,25 @@ void st_table::mark_columns_needed_for_insert() mark_auto_increment_column(); } + +/** + @brief Check if this is part of a MERGE table with attached children. + + @return status + @retval TRUE children are attached + @retval FALSE no MERGE part or children not attached + + @detail + A MERGE table consists of a parent TABLE and zero or more child + TABLEs. Each of these TABLEs is called a part of a MERGE table. +*/ + +bool st_table::is_children_attached(void) +{ + return((child_l && children_attached) || + (parent && parent->children_attached)); +} + /* Cleanup this table for re-execution. diff --git a/sql/table.h b/sql/table.h index 2bbd71b70c6..7bf0f6bb792 100644 --- a/sql/table.h +++ b/sql/table.h @@ -431,6 +431,12 @@ typedef struct st_table_share { return (table_category == TABLE_CATEGORY_PERFORMANCE); } + + inline ulong get_table_def_version() + { + return table_map_id; + } + } TABLE_SHARE; @@ -455,6 +461,11 @@ struct st_table { #endif struct st_table *next, *prev; + /* For the below MERGE related members see top comment in ha_myisammrg.cc */ + struct st_table *parent; /* Set in MERGE child. Ptr to parent */ + TABLE_LIST *child_l; /* Set in MERGE parent. List of children */ + TABLE_LIST **child_last_l; /* Set in MERGE parent. End of list */ + THD *in_use; /* Which thread uses this */ Field **field; /* Pointer to fields */ @@ -622,6 +633,8 @@ struct st_table { my_bool insert_or_update; /* Can be used by the handler */ my_bool alias_name_used; /* true if table_name is alias */ my_bool get_fields_in_item_tree; /* Signal to fix_field */ + /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */ + my_bool children_attached; REGINFO reginfo; /* field connections */ MEM_ROOT mem_root; @@ -673,6 +686,7 @@ struct st_table { */ inline bool needs_reopen_or_name_lock() { return s->version != refresh_version; } + bool is_children_attached(void); }; enum enum_schema_table_state @@ -996,6 +1010,8 @@ struct TABLE_LIST (non-zero only for merged underlying tables of a view). */ TABLE_LIST *referencing_view; + /* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */ + TABLE_LIST *parent_l; /* Security context (non-zero only for tables which belong to view with SQL SECURITY DEFINER) @@ -1176,6 +1192,20 @@ struct TABLE_LIST */ bool process_index_hints(TABLE *table); + /* Access MERGE child def version. See top comment in ha_myisammrg.cc */ + inline ulong get_child_def_version() + { + return child_def_version; + } + inline void set_child_def_version(ulong version) + { + child_def_version= version; + } + inline void init_child_def_version() + { + child_def_version= ~0UL; + } + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); @@ -1183,6 +1213,9 @@ private: Cleanup for re-execution in a prepared statement or a stored procedure. */ + + /* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */ + ulong child_def_version; }; class Item; diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index ca4c40547ee..dcedcf03f65 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -119,6 +119,10 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, definition for further use in mi_create or for a check for underlying table conformance in merge engine. + The caller needs to free *recinfo_out after use. Since *recinfo_out + and *keydef_out are allocated with a my_multi_malloc, *keydef_out + is freed automatically when *recinfo_out is freed. + RETURN VALUE 0 OK !0 error code diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h index e8594fc9039..ca44ae9ad87 100644 --- a/storage/myisam/ha_myisam.h +++ b/storage/myisam/ha_myisam.h @@ -140,4 +140,8 @@ class ha_myisam: public handler *engine_callback, ulonglong *engine_data); #endif + MI_INFO *file_ptr(void) + { + return file; + } }; diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 7cd35295cb0..d9b7033455c 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -941,7 +941,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) ha_rows records,del_blocks; my_off_t used,empty,pos,splits,start_recpos, del_length,link_used,start_block; - uchar *record,*to; + uchar *record= 0, *to; char llbuff[22],llbuff2[22],llbuff3[22]; ha_checksum intern_record_checksum; ha_checksum key_checksum[MI_MAX_POSSIBLE_KEY]; @@ -958,7 +958,7 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) puts("- check record links"); } - if (!(record= (uchar*) my_malloc(info->s->base.pack_reclength,MYF(0)))) + if (!mi_alloc_rec_buff(info, -1, &record)) { mi_check_print_error(param,"Not enough memory for record"); DBUG_RETURN(-1); @@ -1365,12 +1365,12 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) printf("Lost space: %12s Linkdata: %10s\n", llstr(empty,llbuff),llstr(link_used,llbuff2)); } - my_free((uchar*) record,MYF(0)); + my_free(mi_get_rec_buff_ptr(info, record), MYF(0)); DBUG_RETURN (error); err: mi_check_print_error(param,"got error: %d when reading datafile at record: %s",my_errno, llstr(records,llbuff)); err2: - my_free((uchar*) record,MYF(0)); + my_free(mi_get_rec_buff_ptr(info, record), MYF(0)); param->testflag|=T_RETRY_WITHOUT_QUICK; DBUG_RETURN(1); } /* chk_data_link */ @@ -1488,7 +1488,7 @@ static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force) /* Remove all key blocks of this index file from key cache. */ if ((error= flush_key_blocks(share->key_cache, share->kfile, FLUSH_IGNORE_CHANGED))) - goto end; + goto end; /* purecov: inspected */ /* Clear index root block pointers. */ for (i= 0; i < share->base.keys; i++) @@ -1561,8 +1561,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, MYF(MY_WME | MY_WAIT_IF_FULL))) goto err; info->opt_flag|=WRITE_CACHE_USED; - if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength, - MYF(0))) || + if (!mi_alloc_rec_buff(info, -1, &sort_param.record) || !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff)) { mi_check_print_error(param, "Not enough memory for extra record"); @@ -1754,7 +1753,8 @@ err: } my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff), MYF(MY_ALLOW_ZERO_PTR)); - my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mi_get_rec_buff_ptr(info, sort_param.record), + MYF(MY_ALLOW_ZERO_PTR)); my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); VOID(end_io_cache(¶m->read_cache)); info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); @@ -2261,8 +2261,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, info->opt_flag|=WRITE_CACHE_USED; info->rec_cache.file=info->dfile; /* for sort_delete_record */ - if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength, - MYF(0))) || + if (!mi_alloc_rec_buff(info, -1, &sort_param.record) || !mi_alloc_rec_buff(info, -1, &sort_param.rec_buff)) { mi_check_print_error(param, "Not enough memory for extra record"); @@ -2571,7 +2570,8 @@ err: my_free(mi_get_rec_buff_ptr(info, sort_param.rec_buff), MYF(MY_ALLOW_ZERO_PTR)); - my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mi_get_rec_buff_ptr(info, sort_param.record), + MYF(MY_ALLOW_ZERO_PTR)); my_free((uchar*) sort_info.key_block,MYF(MY_ALLOW_ZERO_PTR)); my_free((uchar*) sort_info.ft_buf, MYF(MY_ALLOW_ZERO_PTR)); my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); @@ -2649,6 +2649,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, SORT_INFO sort_info; ulonglong key_map; pthread_attr_t thr_attr; + ulong max_pack_reclength; DBUG_ENTER("mi_repair_parallel"); LINT_INIT(key_map); @@ -2795,10 +2796,13 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, del=info->state->del; param->glob_crc=0; - + /* for compressed tables */ + max_pack_reclength= share->base.pack_reclength; + if (share->options & HA_OPTION_COMPRESS_RECORD) + set_if_bigger(max_pack_reclength, share->max_pack_length); if (!(sort_param=(MI_SORT_PARAM *) my_malloc((uint) share->base.keys * - (sizeof(MI_SORT_PARAM) + share->base.pack_reclength), + (sizeof(MI_SORT_PARAM) + max_pack_reclength), MYF(MY_ZEROFILL)))) { mi_check_print_error(param,"Not enough memory for key!"); @@ -2854,7 +2858,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, sort_param[i].max_pos=sort_param[i].pos=share->pack.header_length; sort_param[i].record= (((uchar *)(sort_param+share->base.keys))+ - (share->base.pack_reclength * i)); + (max_pack_reclength * i)); if (!mi_alloc_rec_buff(info, -1, &sort_param[i].rec_buff)) { mi_check_print_error(param,"Not enough memory!"); @@ -4469,7 +4473,7 @@ err: void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, my_bool repair_only) { - uchar *record; + uchar *record= 0; DBUG_ENTER("update_auto_increment_key"); if (!info->s->base.auto_key || @@ -4488,8 +4492,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, We have to use an allocated buffer instead of info->rec_buff as _mi_put_key_in_record() may use info->rec_buff */ - if (!(record= (uchar*) my_malloc((uint) info->s->base.pack_reclength, - MYF(0)))) + if (!mi_alloc_rec_buff(info, -1, &record)) { mi_check_print_error(param,"Not enough memory for extra record"); DBUG_VOID_RETURN; @@ -4501,7 +4504,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, if (my_errno != HA_ERR_END_OF_FILE) { mi_extra(info,HA_EXTRA_NO_KEYREAD,0); - my_free((char*) record, MYF(0)); + my_free(mi_get_rec_buff_ptr(info, record), MYF(0)); mi_check_print_error(param,"%d when reading last record",my_errno); DBUG_VOID_RETURN; } @@ -4516,7 +4519,7 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, set_if_bigger(info->s->state.auto_increment, param->auto_increment_value); } mi_extra(info,HA_EXTRA_NO_KEYREAD,0); - my_free((char*) record, MYF(0)); + my_free(mi_get_rec_buff_ptr(info, record), MYF(0)); update_state_info(param, info, UPDATE_AUTO_INC); DBUG_VOID_RETURN; } diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index b848c822f75..d7bbfc6a3dd 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -697,8 +697,11 @@ uchar *mi_alloc_rec_buff(MI_INFO *info, ulong length, uchar **buf) /* to simplify initial init of info->rec_buf in mi_open and mi_extra */ if (length == (ulong) -1) { - length= max(info->s->base.pack_reclength, - info->s->base.max_key_length); + if (info->s->options & HA_OPTION_COMPRESS_RECORD) + length= max(info->s->base.pack_reclength, info->s->max_pack_length); + else + length= info->s->base.pack_reclength; + length= max(length, info->s->base.max_key_length); /* Avoid unnecessary realloc */ if (newptr && length == old_length) return newptr; diff --git a/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c index 305b7e5532c..59c98c978ce 100644 --- a/storage/myisam/mi_packrec.c +++ b/storage/myisam/mi_packrec.c @@ -166,7 +166,6 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) share->pack.header_length= uint4korr(header+4); share->min_pack_length=(uint) uint4korr(header+8); share->max_pack_length=(uint) uint4korr(header+12); - set_if_bigger(share->base.pack_reclength,share->max_pack_length); elements=uint4korr(header+16); intervall_length=uint4korr(header+20); trees=uint2korr(header+24); @@ -565,7 +564,7 @@ static void fill_quick_table(uint16 *table, uint bits, uint max_bits, */ value|= (max_bits - bits) << 8 | IS_CHAR; - for (end= table + (uint) (((uint) 1 << bits)); table < end; table++) + for (end= table + ((my_ptrdiff_t) 1 << bits); table < end; table++) { *table= (uint16) value; } diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c index 567e1057e5d..2300d1e7c23 100644 --- a/storage/myisam/myisamchk.c +++ b/storage/myisam/myisamchk.c @@ -1536,8 +1536,8 @@ static int mi_sort_records(MI_CHECK *param, mi_check_print_error(param,"Not enough memory for key block"); goto err; } - if (!(sort_param.record=(uchar*) my_malloc((uint) share->base.pack_reclength, - MYF(0)))) + + if (!mi_alloc_rec_buff(info, -1, &sort_param.record)) { mi_check_print_error(param,"Not enough memory for record"); goto err; @@ -1632,7 +1632,8 @@ err: { my_afree((uchar*) temp_buff); } - my_free(sort_param.record,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mi_get_rec_buff_ptr(info, sort_param.record), + MYF(MY_ALLOW_ZERO_PTR)); info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); VOID(end_io_cache(&info->rec_cache)); my_free(sort_info.buff,MYF(MY_ALLOW_ZERO_PTR)); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 8a914e8a2de..f91b0dc7a92 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -14,6 +14,82 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + MyISAM MERGE tables + + A MyISAM MERGE table is kind of a union of zero or more MyISAM tables. + + Besides the normal form file (.frm) a MERGE table has a meta file + (.MRG) with a list of tables. These are paths to the MyISAM table + files. The last two components of the path contain the database name + and the table name respectively. + + When a MERGE table is open, there exists an TABLE object for the MERGE + table itself and a TABLE object for each of the MyISAM tables. For + abbreviated writing, I call the MERGE table object "parent" and the + MyISAM table objects "children". + + A MERGE table is almost always opened through open_and_lock_tables() + and hence through open_tables(). When the parent appears in the list + of tables to open, the initial open of the handler does nothing but + read the meta file and collect a list of TABLE_LIST objects for the + children. This list is attached to the parent TABLE object as + TABLE::child_l. The end of the children list is saved in + TABLE::child_last_l. + + Back in open_tables(), add_merge_table_list() is called. It updates + each list member with the lock type and a back pointer to the parent + TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in + the list of tables to open, right behind the parent. Consequently, + open_tables() opens the children, one after the other. The TABLE + references of the TABLE_LIST objects are implicitly set to the open + tables. The children are opened as independent MyISAM tables, right as + if they are used by the SQL statement. + + TABLE_LIST::parent_l is required to find the parent 1. when the last + child has been opened and children are to be attached, and 2. when an + error happens during child open and the child list must be removed + from the queuery list. In these cases the current child does not have + TABLE::parent set or does not have a TABLE at all respectively. + + When the last child is open, attach_merge_children() is called. It + removes the list of children from the open list. Then the children are + "attached" to the parent. All required references between parent and + children are set up. + + The MERGE storage engine sets up an array with references to the + low-level MyISAM table objects (MI_INFO). It remembers the state of + the table in MYRG_INFO::children_attached. + + Every child TABLE::parent references the parent TABLE object. That way + TABLE objects belonging to a MERGE table can be identified. + TABLE::parent is required because the parent and child TABLE objects + can live longer than the parent TABLE_LIST object. So the path + child->pos_in_table_list->parent_l->table can be broken. + + If necessary, the compatibility of parent and children is checked. + This check is necessary when any of the objects are reopend. This is + detected by comparing the current table def version against the + remembered child def version. On parent open, the list members are + initialized to an "impossible"/"undefined" version value. So the check + is always executed on the first attach. + + The version check is done in myisammrg_attach_children_callback(), + which is called for every child. ha_myisammrg::attach_children() + initializes 'need_compat_check' to FALSE and + myisammrg_attach_children_callback() sets it ot TRUE if a table + def version mismatches the remembered child def version. + + Finally the parent TABLE::children_attached is set. + + --- + + On parent open the storage engine structures are allocated and initialized. + They stay with the open table until its final close. + + +*/ + #ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif @@ -22,14 +98,11 @@ #include "mysql_priv.h" #include <mysql/plugin.h> #include <m_ctype.h> +#include "../myisam/ha_myisam.h" #include "ha_myisammrg.h" #include "myrg_def.h" -/***************************************************************************** -** MyISAM MERGE tables -*****************************************************************************/ - static handler *myisammrg_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root) @@ -38,10 +111,23 @@ static handler *myisammrg_create_handler(handlerton *hton, } +/** + @brief Constructor +*/ + ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg) :handler(hton, table_arg), file(0) {} + +/** + @brief Destructor +*/ + +ha_myisammrg::~ha_myisammrg(void) +{} + + static const char *ha_myisammrg_exts[] = { ".MRG", NullS @@ -89,25 +175,311 @@ const char *ha_myisammrg::index_type(uint key_number) } -int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) +/** + @brief Callback function for open of a MERGE parent table. + + @detail This function adds a TABLE_LIST object for a MERGE child table + to a list of tables of the parent TABLE object. It is called for + each child table. + + The list of child TABLE_LIST objects is kept in the TABLE object of + the parent for the whole life time of the MERGE table. It is + inserted in the statement list behind the MERGE parent TABLE_LIST + object when the MERGE table is opened. It is removed from the + statement list after the last child is opened. + + All memeory used for the child TABLE_LIST objects and the strings + referred by it are taken from the parent TABLE::mem_root. Thus they + are all freed implicitly at the final close of the table. + + TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global + # # ^ # ^ + # # | # | + # # +--------- TABLE_LIST::prev_global + # # | + # |<--- TABLE_LIST::prev_global | + # | + TABLE::child_last_l -----------------------------------------+ + + @param[in] callback_param data pointer as given to myrg_parent_open() + @param[in] filename file name of MyISAM table + without extension. + + @return status + @retval 0 OK + @retval != 0 Error +*/ + +static int myisammrg_parent_open_callback(void *callback_param, + const char *filename) +{ + ha_myisammrg *ha_myrg; + TABLE *parent; + TABLE_LIST *child_l; + const char *db; + const char *table_name; + uint dirlen; + char dir_path[FN_REFLEN]; + DBUG_ENTER("myisammrg_parent_open_callback"); + + /* Extract child table name and database name from filename. */ + dirlen= dirname_length(filename); + if (dirlen >= FN_REFLEN) + { + /* purecov: begin inspected */ + DBUG_PRINT("error", ("name too long: '%.64s'", filename)); + my_errno= ENAMETOOLONG; + DBUG_RETURN(1); + /* purecov: end */ + } + table_name= filename + dirlen; + dirlen--; /* Strip off trailing '/'. */ + memcpy(dir_path, filename, dirlen); + dir_path[dirlen]= '\0'; + db= base_name(dir_path); + dirlen-= db - dir_path; /* This is now the length of 'db'. */ + DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name)); + + ha_myrg= (ha_myisammrg*) callback_param; + parent= ha_myrg->table_ptr(); + + /* Get a TABLE_LIST object. */ + if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root, + sizeof(TABLE_LIST)))) + { + /* purecov: begin inspected */ + DBUG_PRINT("error", ("my_malloc error: %d", my_errno)); + DBUG_RETURN(1); + /* purecov: end */ + } + bzero((char*) child_l, sizeof(TABLE_LIST)); + + /* Set database (schema) name. */ + child_l->db_length= dirlen; + child_l->db= strmake_root(&parent->mem_root, db, dirlen); + /* Set table name. */ + child_l->table_name_length= strlen(table_name); + child_l->table_name= strmake_root(&parent->mem_root, table_name, + child_l->table_name_length); + /* Convert to lowercase if required. */ + if (lower_case_table_names && child_l->table_name_length) + child_l->table_name_length= my_casedn_str(files_charset_info, + child_l->table_name); + /* Set alias. */ + child_l->alias= child_l->table_name; + + /* Initialize table map to 'undefined'. */ + child_l->init_child_def_version(); + + /* Link TABLE_LIST object into the parent list. */ + if (!parent->child_last_l) + { + /* Initialize parent->child_last_l when handling first child. */ + parent->child_last_l= &parent->child_l; + } + *parent->child_last_l= child_l; + child_l->prev_global= parent->child_last_l; + parent->child_last_l= &child_l->next_global; + + DBUG_RETURN(0); +} + + +/** + @brief Callback function for attaching a MERGE child table. + + @detail This function retrieves the MyISAM table handle from the + next child table. It is called for each child table. + + @param[in] callback_param data pointer as given to + myrg_attach_children() + + @return pointer to open MyISAM table structure + @retval !=NULL OK, returning pointer + @retval NULL, my_errno == 0 Ok, no more child tables + @retval NULL, my_errno != 0 error +*/ + +static MI_INFO *myisammrg_attach_children_callback(void *callback_param) +{ + ha_myisammrg *ha_myrg; + TABLE *parent; + TABLE *child; + TABLE_LIST *child_l; + MI_INFO *myisam; + DBUG_ENTER("myisammrg_attach_children_callback"); + + my_errno= 0; + ha_myrg= (ha_myisammrg*) callback_param; + parent= ha_myrg->table_ptr(); + + /* Get child list item. */ + child_l= ha_myrg->next_child_attach; + if (!child_l) + { + DBUG_PRINT("myrg", ("No more children to attach")); + DBUG_RETURN(NULL); + } + child= child_l->table; + DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str, + child->s->table_name.str, (long) child)); + /* + Prepare for next child. Used as child_l in next call to this function. + We cannot rely on a NULL-terminated chain. + */ + if (&child_l->next_global == parent->child_last_l) + { + DBUG_PRINT("myrg", ("attaching last child")); + ha_myrg->next_child_attach= NULL; + } + else + ha_myrg->next_child_attach= child_l->next_global; + + /* Set parent reference. */ + child->parent= parent; + + /* + Do a quick compatibility check. The table def version is set when + the table share is created. The child def version is copied + from the table def version after a sucessful compatibility check. + We need to repeat the compatibility check only if a child is opened + from a different share than last time it was used with this MERGE + table. + */ + DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu", + (ulong) child_l->get_child_def_version(), + (ulong) child->s->get_table_def_version())); + if (child_l->get_child_def_version() != child->s->get_table_def_version()) + ha_myrg->need_compat_check= TRUE; + + /* + If parent is temporary, children must be temporary too and vice + versa. This check must be done for every child on every open because + the table def version can overlap between temporary and + non-temporary tables. We need to detect the case where a + non-temporary table has been replaced with a temporary table of the + same version. Or vice versa. A very unlikely case, but it could + happen. + */ + if (child->s->tmp_table != parent->s->tmp_table) + { + DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d", + parent->s->tmp_table, child->s->tmp_table)); + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; + goto err; + } + + /* Extract the MyISAM table structure pointer from the handler object. */ + if ((child->file->ht->db_type != DB_TYPE_MYISAM) || + !(myisam= ((ha_myisam*) child->file)->file_ptr())) + { + DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx", + child->s->db.str, child->s->table_name.str, + (long) child)); + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; + } + DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d", + (long) myisam, my_errno)); + + err: + DBUG_RETURN(my_errno ? NULL : myisam); +} + + +/** + @brief Open a MERGE parent table, not its children. + + @detail This function initializes the MERGE storage engine structures + and adds a child list of TABLE_LIST to the parent TABLE. + + @param[in] name MERGE table path name + @param[in] mode read/write mode, unused + @param[in] test_if_locked open flags + + @return status + @retval 0 OK + @retval -1 Error, my_errno gives reason +*/ + +int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), + uint test_if_locked) +{ + DBUG_ENTER("ha_myisammrg::open"); + DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table)); + DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked)); + + /* Save for later use. */ + this->test_if_locked= test_if_locked; + + /* retrieve children table list. */ + my_errno= 0; + if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this))) + { + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + } + DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file)); + DBUG_RETURN(0); +} + + +/** + @brief Attach children to a MERGE table. + + @detail Let the storage engine attach its children through a callback + function. Check table definitions for consistency. + + @note Special thd->open_options may be in effect. We can make use of + them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names + of mismatching child tables. We cannot transport these options in + ha_myisammrg::test_if_locked because they may change after the + parent is opened. The parent is kept open in the table cache over + multiple statements and can be used by other threads. Open options + can change over time. + + @return status + @retval 0 OK + @retval != 0 Error, my_errno gives reason +*/ + +int ha_myisammrg::attach_children(void) { - MI_KEYDEF *keyinfo; - MI_COLUMNDEF *recinfo; - MYRG_TABLE *u_table; - uint recs; - uint keys= table->s->keys; - int error; - char name_buff[FN_REFLEN]; + MYRG_TABLE *u_table; + MI_COLUMNDEF *recinfo; + MI_KEYDEF *keyinfo; + uint recs; + uint keys= table->s->keys; + int error; + DBUG_ENTER("ha_myisammrg::attach_children"); + DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str, + table->s->table_name.str, (long) table)); + DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked)); + DBUG_ASSERT(!this->file->children_attached); + + /* + Initialize variables that are used, modified, and/or set by + myisammrg_attach_children_callback(). + 'next_child_attach' traverses the chain of TABLE_LIST objects + that has been compiled during myrg_parent_open(). Every call + to myisammrg_attach_children_callback() moves the pointer to + the next object. + 'need_compat_check' is set by myisammrg_attach_children_callback() + if a child fails the table def version check. + 'my_errno' is set by myisammrg_attach_children_callback() in + case of an error. + */ + next_child_attach= table->child_l; + need_compat_check= FALSE; + my_errno= 0; - DBUG_PRINT("info", ("ha_myisammrg::open")); - if (!(file=myrg_open(fn_format(name_buff,name,"","", - MY_UNPACK_FILENAME|MY_APPEND_EXT), - mode, test_if_locked))) + if (myrg_attach_children(this->file, this->test_if_locked | + current_thd->open_options, + myisammrg_attach_children_callback, this)) { - DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno)); - return (my_errno ? my_errno : -1); + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); } - DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc...")); + DBUG_PRINT("myrg", ("calling myrg_extrafunc")); myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED || test_if_locked == HA_OPEN_ABORT_IF_LOCKED)) @@ -116,69 +488,147 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) myrg_extra(file,HA_EXTRA_WAIT_LOCK,0); - if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length) - { - DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu", - table->s->reclength, stats.mean_rec_length)); - if (test_if_locked & HA_OPEN_FOR_REPAIR) - myrg_print_wrong_table(file->open_tables->table->filename); - error= HA_ERR_WRONG_MRG_TABLE_DEF; - goto err; - } - if ((error= table2myisam(table, &keyinfo, &recinfo, &recs))) - { - /* purecov: begin inspected */ - DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM " - "key and column definition")); - goto err; - /* purecov: end */ - } - for (u_table= file->open_tables; u_table < file->end_table; u_table++) + /* + The compatibility check is required only if one or more children do + not match their table def version from the last check. This will + always happen at the first attach because the reference child def + version is initialized to 'undefined' at open. + */ + DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check)); + if (need_compat_check) { - if (check_definition(keyinfo, recinfo, keys, recs, - u_table->table->s->keyinfo, u_table->table->s->rec, - u_table->table->s->base.keys, - u_table->table->s->base.fields, false)) + TABLE_LIST *child_l; + + if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length) { - error= HA_ERR_WRONG_MRG_TABLE_DEF; + DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu", + table->s->reclength, stats.mean_rec_length)); if (test_if_locked & HA_OPEN_FOR_REPAIR) - myrg_print_wrong_table(u_table->table->filename); - else + myrg_print_wrong_table(file->open_tables->table->filename); + error= HA_ERR_WRONG_MRG_TABLE_DEF; + goto err; + } + /* + Both recinfo and keyinfo are allocated by my_multi_malloc(), thus + only recinfo must be freed. + */ + if ((error= table2myisam(table, &keyinfo, &recinfo, &recs))) + { + /* purecov: begin inspected */ + DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM " + "key and column definition")); + goto err; + /* purecov: end */ + } + for (u_table= file->open_tables; u_table < file->end_table; u_table++) + { + if (check_definition(keyinfo, recinfo, keys, recs, + u_table->table->s->keyinfo, u_table->table->s->rec, + u_table->table->s->base.keys, + u_table->table->s->base.fields, false)) { - my_free((uchar*) recinfo, MYF(0)); - goto err; + DBUG_PRINT("error", ("table definition mismatch: '%s'", + u_table->table->filename)); + error= HA_ERR_WRONG_MRG_TABLE_DEF; + if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR)) + { + my_free((uchar*) recinfo, MYF(0)); + goto err; + } + myrg_print_wrong_table(u_table->table->filename); } } + my_free((uchar*) recinfo, MYF(0)); + if (error == HA_ERR_WRONG_MRG_TABLE_DEF) + goto err; + + /* All checks passed so far. Now update child def version. */ + for (child_l= table->child_l; ; child_l= child_l->next_global) + { + child_l->set_child_def_version( + child_l->table->s->get_table_def_version()); + + if (&child_l->next_global == table->child_last_l) + break; + } } - my_free((uchar*) recinfo, MYF(0)); - if (error == HA_ERR_WRONG_MRG_TABLE_DEF) - goto err; #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 /* Merge table has more than 2G rows */ if (table->s->crashed) { + DBUG_PRINT("error", ("MERGE table marked crashed")); error= HA_ERR_WRONG_MRG_TABLE_DEF; goto err; } #endif - return (0); + DBUG_RETURN(0); + err: - myrg_close(file); - file=0; - return (my_errno= error); + myrg_detach_children(file); + DBUG_RETURN(my_errno= error); } + +/** + @brief Detach all children from a MERGE table. + + @note Detach must not touch the children in any way. + They may have been closed at ths point already. + All references to the children should be removed. + + @return status + @retval 0 OK + @retval != 0 Error, my_errno gives reason +*/ + +int ha_myisammrg::detach_children(void) +{ + DBUG_ENTER("ha_myisammrg::detach_children"); + DBUG_ASSERT(this->file && this->file->children_attached); + + if (myrg_detach_children(this->file)) + { + /* purecov: begin inspected */ + DBUG_PRINT("error", ("my_errno %d", my_errno)); + DBUG_RETURN(my_errno ? my_errno : -1); + /* purecov: end */ + } + DBUG_RETURN(0); +} + + +/** + @brief Close a MERGE parent table, not its children. + + @note The children are expected to be closed separately by the caller. + + @return status + @retval 0 OK + @retval != 0 Error, my_errno gives reason +*/ + int ha_myisammrg::close(void) { - return myrg_close(file); + int rc; + DBUG_ENTER("ha_myisammrg::close"); + /* + Children must not be attached here. Unless the MERGE table has no + children. In this case children_attached is always true. + */ + DBUG_ASSERT(!this->file->children_attached || !this->file->tables); + rc= myrg_close(file); + file= 0; + DBUG_RETURN(rc); } int ha_myisammrg::write_row(uchar * buf) { + DBUG_ENTER("ha_myisammrg::write_row"); + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_write_count); if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables) - return (HA_ERR_TABLE_READONLY); + DBUG_RETURN(HA_ERR_TABLE_READONLY); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); @@ -186,13 +636,14 @@ int ha_myisammrg::write_row(uchar * buf) { int error; if ((error= update_auto_increment())) - return error; + DBUG_RETURN(error); /* purecov: inspected */ } - return myrg_write(file,buf); + DBUG_RETURN(myrg_write(file,buf)); } int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_update_count); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) table->timestamp_field->set_time(); @@ -201,6 +652,7 @@ int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data) int ha_myisammrg::delete_row(const uchar * buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_delete_count); return myrg_delete(file,buf); } @@ -209,6 +661,7 @@ int ha_myisammrg::index_read_map(uchar * buf, const uchar * key, key_part_map keypart_map, enum ha_rkey_function find_flag) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_key_count); int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; @@ -219,6 +672,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key, key_part_map keypart_map, enum ha_rkey_function find_flag) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_key_count); int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag); table->status=error ? STATUS_NOT_FOUND: 0; @@ -228,6 +682,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key, int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key, key_part_map keypart_map) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_key_count); int error=myrg_rkey(file,buf,active_index, key, keypart_map, HA_READ_PREFIX_LAST); @@ -237,6 +692,7 @@ int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key, int ha_myisammrg::index_next(uchar * buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_next_count); int error=myrg_rnext(file,buf,active_index); table->status=error ? STATUS_NOT_FOUND: 0; @@ -245,6 +701,7 @@ int ha_myisammrg::index_next(uchar * buf) int ha_myisammrg::index_prev(uchar * buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_prev_count); int error=myrg_rprev(file,buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; @@ -253,6 +710,7 @@ int ha_myisammrg::index_prev(uchar * buf) int ha_myisammrg::index_first(uchar * buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_first_count); int error=myrg_rfirst(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; @@ -261,6 +719,7 @@ int ha_myisammrg::index_first(uchar * buf) int ha_myisammrg::index_last(uchar * buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_last_count); int error=myrg_rlast(file, buf, active_index); table->status=error ? STATUS_NOT_FOUND: 0; @@ -271,6 +730,7 @@ int ha_myisammrg::index_next_same(uchar * buf, const uchar *key __attribute__((unused)), uint length __attribute__((unused))) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_next_count); int error=myrg_rnext_same(file,buf); table->status=error ? STATUS_NOT_FOUND: 0; @@ -280,12 +740,14 @@ int ha_myisammrg::index_next_same(uchar * buf, int ha_myisammrg::rnd_init(bool scan) { + DBUG_ASSERT(this->file->children_attached); return myrg_reset(file); } int ha_myisammrg::rnd_next(uchar *buf) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_rnd_next_count); int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR); table->status=error ? STATUS_NOT_FOUND: 0; @@ -295,6 +757,7 @@ int ha_myisammrg::rnd_next(uchar *buf) int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos) { + DBUG_ASSERT(this->file->children_attached); ha_statistic_increment(&SSV::ha_read_rnd_count); int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length)); table->status=error ? STATUS_NOT_FOUND: 0; @@ -303,6 +766,7 @@ int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos) void ha_myisammrg::position(const uchar *record) { + DBUG_ASSERT(this->file->children_attached); ulonglong row_position= myrg_position(file); my_store_ptr(ref, ref_length, (my_off_t) row_position); } @@ -311,6 +775,7 @@ void ha_myisammrg::position(const uchar *record) ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key, key_range *max_key) { + DBUG_ASSERT(this->file->children_attached); return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key); } @@ -318,6 +783,7 @@ ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key, int ha_myisammrg::info(uint flag) { MYMERGE_INFO mrg_info; + DBUG_ASSERT(this->file->children_attached); (void) myrg_status(file,&mrg_info,flag); /* The following fails if one has not compiled MySQL with -DBIG_TABLES @@ -387,6 +853,23 @@ int ha_myisammrg::info(uint flag) int ha_myisammrg::extra(enum ha_extra_function operation) { + if (operation == HA_EXTRA_ATTACH_CHILDREN) + { + int rc= attach_children(); + if (!rc) + (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL + return(rc); + } + else if (operation == HA_EXTRA_DETACH_CHILDREN) + { + /* + Note that detach must not touch the children in any way. + They may have been closed at ths point already. + */ + int rc= detach_children(); + return(rc); + } + /* As this is just a mapping, we don't have to force the underlying tables to be closed */ if (operation == HA_EXTRA_FORCE_REOPEN || @@ -404,6 +887,7 @@ int ha_myisammrg::reset(void) int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size) { + DBUG_ASSERT(this->file->children_attached); if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE) return 0; return myrg_extra(file, operation, (void*) &cache_size); @@ -411,11 +895,13 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size) int ha_myisammrg::external_lock(THD *thd, int lock_type) { + DBUG_ASSERT(this->file->children_attached); return myrg_lock_database(file,lock_type); } uint ha_myisammrg::lock_count(void) const { + DBUG_ASSERT(this->file->children_attached); return file->tables; } @@ -425,6 +911,7 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, enum thr_lock_type lock_type) { MYRG_TABLE *open_table; + DBUG_ASSERT(this->file->children_attached); for (open_table=file->open_tables ; open_table != file->end_table ; @@ -519,47 +1006,50 @@ int ha_myisammrg::create(const char *name, register TABLE *form, uint dirlgt= dirname_length(name); DBUG_ENTER("ha_myisammrg::create"); + /* Allocate a table_names array in thread mem_root. */ if (!(table_names= (const char**) thd->alloc((create_info->merge_list.elements+1) * sizeof(char*)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + /* Create child path names. */ for (pos= table_names; tables; tables= tables->next_local) { const char *table_name; - TABLE *tbl= 0; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) - tbl= find_temporary_table(thd, tables); - if (!tbl) - { - /* - Construct the path to the MyISAM table. Try to meet two conditions: - 1.) Allow to include MyISAM tables from different databases, and - 2.) allow for moving DATADIR around in the file system. - The first means that we need paths in the .MRG file. The second - means that we should not have absolute paths in the .MRG file. - The best, we can do, is to use 'mysql_data_home', which is '.' - in mysqld and may be an absolute path in an embedded server. - This means that it might not be possible to move the DATADIR of - an embedded server without changing the paths in the .MRG file. - */ - uint length= build_table_filename(buff, sizeof(buff), - tables->db, tables->table_name, "", 0); - /* - If a MyISAM table is in the same directory as the MERGE table, - we use the table name without a path. This means that the - DATADIR can easily be moved even for an embedded server as long - as the MyISAM tables are from the same database as the MERGE table. - */ - if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt)) - table_name= tables->table_name; - else - if (! (table_name= thd->strmake(buff, length))) - DBUG_RETURN(HA_ERR_OUT_OF_MEM); - } + + /* + Construct the path to the MyISAM table. Try to meet two conditions: + 1.) Allow to include MyISAM tables from different databases, and + 2.) allow for moving DATADIR around in the file system. + The first means that we need paths in the .MRG file. The second + means that we should not have absolute paths in the .MRG file. + The best, we can do, is to use 'mysql_data_home', which is '.' + in mysqld and may be an absolute path in an embedded server. + This means that it might not be possible to move the DATADIR of + an embedded server without changing the paths in the .MRG file. + + Do the same even for temporary tables. MERGE children are now + opened through the table cache. They are opened by db.table_name, + not by their path name. + */ + uint length= build_table_filename(buff, sizeof(buff), + tables->db, tables->table_name, "", 0); + /* + If a MyISAM table is in the same directory as the MERGE table, + we use the table name without a path. This means that the + DATADIR can easily be moved even for an embedded server as long + as the MyISAM tables are from the same database as the MERGE table. + */ + if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt)) + table_name= tables->table_name; else - table_name= tbl->s->path.str; + if (! (table_name= thd->strmake(buff, length))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */ + *pos++= table_name; } *pos=0; + + /* Create a MERGE meta file from the table_names array. */ DBUG_RETURN(myrg_create(fn_format(buff,name,"","", MY_RESOLVE_SYMLINKS| MY_UNPACK_FILENAME|MY_APPEND_EXT), @@ -642,7 +1132,7 @@ static int myisammrg_init(void *p) myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM; myisammrg_hton->create= myisammrg_create_handler; myisammrg_hton->panic= myisammrg_panic; - myisammrg_hton->flags= HTON_CAN_RECREATE|HTON_NO_PARTITION; + myisammrg_hton->flags= HTON_NO_PARTITION; return 0; } diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index 91aabe277f7..977c45d1435 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -27,8 +27,12 @@ class ha_myisammrg: public handler MYRG_INFO *file; public: + TABLE_LIST *next_child_attach; /* next child to attach */ + uint test_if_locked; /* flags from ::open() */ + bool need_compat_check; /* if need compatibility check */ + ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg); - ~ha_myisammrg() {} + ~ha_myisammrg(); const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; const char *index_type(uint key_number); @@ -53,6 +57,8 @@ class ha_myisammrg: public handler { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; } int open(const char *name, int mode, uint test_if_locked); + int attach_children(void); + int detach_children(void); int close(void); int write_row(uchar * buf); int update_row(const uchar * old_data, uchar * new_data); @@ -85,6 +91,7 @@ class ha_myisammrg: public handler void update_create_info(HA_CREATE_INFO *create_info); void append_create_info(String *packet); MYRG_INFO *myrg_info() { return file; } + TABLE *table_ptr() { return table; } bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); int check(THD* thd, HA_CHECK_OPT* check_opt); }; diff --git a/storage/myisammrg/myrg_close.c b/storage/myisammrg/myrg_close.c index baae24634b3..b402a35a253 100644 --- a/storage/myisammrg/myrg_close.c +++ b/storage/myisammrg/myrg_close.c @@ -23,9 +23,37 @@ int myrg_close(MYRG_INFO *info) MYRG_TABLE *file; DBUG_ENTER("myrg_close"); - for (file=info->open_tables ; file != info->end_table ; file++) - if ((new_error=mi_close(file->table))) - error=new_error; + /* + Assume that info->children_attached means that this is called from + direct use of MERGE, not from a MySQL server. In this case the + children must be closed and info->rec_per_key_part is part of the + 'info' multi_alloc. + If info->children_attached is false, this is called from a MySQL + server. Children are closed independently but info->rec_per_key_part + must be freed. + Just in case of a server panic (myrg_panic()) info->children_attached + might be true. We would close the children though they should be + closed independently and info->rec_per_key_part is not freed. + This should be acceptable for a panic. + In case of a MySQL server and no children, children_attached is + always true. In this case no rec_per_key_part has been allocated. + So it is correct to use the branch where an empty list of tables is + (not) closed. + */ + if (info->children_attached) + { + for (file= info->open_tables; file != info->end_table; file++) + { + /* purecov: begin inspected */ + if ((new_error= mi_close(file->table))) + error= new_error; + else + file->table= NULL; + /* purecov: end */ + } + } + else + my_free((uchar*) info->rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR)); delete_queue(&info->by_key); pthread_mutex_lock(&THR_LOCK_open); myrg_open_list=list_delete(myrg_open_list,&info->open_list); diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c index a1e6e3f8d4d..3d14f6a56e6 100644 --- a/storage/myisammrg/myrg_extra.c +++ b/storage/myisammrg/myrg_extra.c @@ -29,6 +29,8 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function, DBUG_ENTER("myrg_extra"); DBUG_PRINT("info",("function: %lu", (ulong) function)); + if (!info->children_attached) + DBUG_RETURN(1); if (function == HA_EXTRA_CACHE) { info->cache_in_use=1; @@ -73,6 +75,8 @@ int myrg_reset(MYRG_INFO *info) MYRG_TABLE *file; DBUG_ENTER("myrg_reset"); + if (!info->children_attached) + DBUG_RETURN(1); info->cache_in_use=0; info->current_table=0; info->last_used_table= info->open_tables; diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 500d3a29327..6fb1888f84d 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -26,8 +26,14 @@ open a MyISAM MERGE table if handle_locking is 0 then exit with error if some table is locked if handle_locking is 1 then wait if table is locked -*/ + NOTE: This function is not used in the MySQL server. It is for + MERGE use independent from MySQL. Currently there is some code + duplication between myrg_open() and myrg_parent_open() + + myrg_attach_children(). Please duplicate changes in these + functions or make common sub-functions. +*/ +/* purecov: begin unused */ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { @@ -107,13 +113,11 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) key_parts*sizeof(long), MYF(MY_WME|MY_ZEROFILL)))) goto err; - if (files) - { - m_info->open_tables=(MYRG_TABLE *) (m_info+1); - m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files); - m_info->tables= files; - files= 0; - } + DBUG_ASSERT(files); + m_info->open_tables=(MYRG_TABLE *) (m_info+1); + m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files); + m_info->tables= files; + files= 0; m_info->reclength=isam->s->base.reclength; min_keys= isam->s->base.keys; errpos=3; @@ -163,6 +167,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) /* this works ok if the table list is empty */ m_info->end_table=m_info->open_tables+files; m_info->last_used_table=m_info->open_tables; + m_info->children_attached= TRUE; VOID(my_close(fd,MYF(0))); end_io_cache(&file); @@ -189,3 +194,316 @@ err: my_errno=save_errno; DBUG_RETURN (NULL); } +/* purecov: end */ + + +/** + @brief Open parent table of a MyISAM MERGE table. + + @detail Open MERGE meta file to get the table name paths for the child + tables. Count the children. Allocate and initialize MYRG_INFO + structure. Call a callback function for each child table. + + @param[in] parent_name merge table name path as "database/table" + @param[in] callback function to call for each child table + @param[in] callback_param data pointer to give to the callback + + @return MYRG_INFO pointer + @retval != NULL OK + @retval NULL Error + + @note: Currently there is some code duplication between myrg_open() + and myrg_parent_open() + myrg_attach_children(). Please duplicate + changes in these functions or make common sub-functions. +*/ + +MYRG_INFO *myrg_parent_open(const char *parent_name, + int (*callback)(void*, const char*), + void *callback_param) +{ + MYRG_INFO *m_info; + int rc; + int errpos; + int save_errno; + int insert_method; + uint length; + uint dir_length; + uint child_count; + size_t name_buff_length; + File fd; + IO_CACHE file_cache; + char parent_name_buff[FN_REFLEN * 2]; + char child_name_buff[FN_REFLEN]; + DBUG_ENTER("myrg_parent_open"); + + rc= 1; + errpos= 0; + bzero((char*) &file_cache, sizeof(file_cache)); + + /* Open MERGE meta file. */ + if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT, + MY_UNPACK_FILENAME|MY_APPEND_EXT), + O_RDONLY | O_SHARE, MYF(0))) < 0) + goto err; /* purecov: inspected */ + errpos= 1; + + if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0, + MYF(MY_WME | MY_NABP))) + goto err; /* purecov: inspected */ + errpos= 2; + + /* Count children. Determine insert method. */ + child_count= 0; + insert_method= 0; + while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1))) + { + /* Remove line terminator. */ + if (child_name_buff[length - 1] == '\n') + child_name_buff[--length]= '\0'; + + /* Skip empty lines. */ + if (!child_name_buff[0]) + continue; /* purecov: inspected */ + + /* Skip comments, but evaluate insert method. */ + if (child_name_buff[0] == '#') + { + if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14)) + { + /* Compare buffer with global methods list: merge_insert_method. */ + insert_method= find_type(child_name_buff + 15, + &merge_insert_method, 2); + } + continue; + } + + /* Count the child. */ + child_count++; + } + + /* Allocate MERGE parent table structure. */ + if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) + + child_count * sizeof(MYRG_TABLE), + MYF(MY_WME | MY_ZEROFILL)))) + goto err; /* purecov: inspected */ + errpos= 3; + m_info->open_tables= (MYRG_TABLE*) (m_info + 1); + m_info->tables= child_count; + m_info->merge_insert_method= insert_method > 0 ? insert_method : 0; + /* This works even if the table list is empty. */ + m_info->end_table= m_info->open_tables + child_count; + if (!child_count) + { + /* Do not attach/detach an empty child list. */ + m_info->children_attached= TRUE; + } + + /* Call callback for each child. */ + dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length); + my_b_seek(&file_cache, 0); + while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1))) + { + /* Remove line terminator. */ + if (child_name_buff[length - 1] == '\n') + child_name_buff[--length]= '\0'; + + /* Skip empty lines and comments. */ + if (!child_name_buff[0] || (child_name_buff[0] == '#')) + continue; + + if (!has_path(child_name_buff)) + { + VOID(strmake(parent_name_buff + dir_length, child_name_buff, + sizeof(parent_name_buff) - 1 - dir_length)); + VOID(cleanup_dirname(child_name_buff, parent_name_buff)); + } + else + fn_format(child_name_buff, child_name_buff, "", "", 0); + DBUG_PRINT("info", ("child: '%s'", child_name_buff)); + + /* Callback registers child with handler table. */ + if ((rc= (*callback)(callback_param, child_name_buff))) + goto err; /* purecov: inspected */ + } + + end_io_cache(&file_cache); + VOID(my_close(fd, MYF(0))); + + m_info->open_list.data= (void*) m_info; + pthread_mutex_lock(&THR_LOCK_open); + myrg_open_list= list_add(myrg_open_list, &m_info->open_list); + pthread_mutex_unlock(&THR_LOCK_open); + + DBUG_RETURN(m_info); + + /* purecov: begin inspected */ + err: + save_errno= my_errno; + switch (errpos) { + case 3: + my_free((char*) m_info, MYF(0)); + /* Fall through */ + case 2: + end_io_cache(&file_cache); + /* Fall through */ + case 1: + VOID(my_close(fd, MYF(0))); + } + my_errno= save_errno; + DBUG_RETURN (NULL); + /* purecov: end */ +} + + +/** + @brief Attach children to a MyISAM MERGE parent table. + + @detail Call a callback function for each child table. + The callback returns the MyISAM table handle of the child table. + Check table definition match. + + @param[in] m_info MERGE parent table structure + @param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about + incompatible child tables, but continue + @param[in] callback function to call for each child table + @param[in] callback_param data pointer to give to the callback + + @return status + @retval 0 OK + @retval != 0 Error + + @note: Currently there is some code duplication between myrg_open() + and myrg_parent_open() + myrg_attach_children(). Please duplicate + changes in these functions or make common sub-functions. +*/ + +int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, + MI_INFO *(*callback)(void*), + void *callback_param) +{ + ulonglong file_offset; + MI_INFO *myisam; + int rc; + int errpos; + int save_errno; + uint idx; + uint child_nr; + uint key_parts; + uint min_keys; + DBUG_ENTER("myrg_attach_children"); + DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking)); + + rc= 1; + errpos= 0; + file_offset= 0; + LINT_INIT(key_parts); + min_keys= 0; + child_nr= 0; + while ((myisam= (*callback)(callback_param))) + { + DBUG_PRINT("myrg", ("child_nr: %u table: '%s'", + child_nr, myisam->filename)); + DBUG_ASSERT(child_nr < m_info->tables); + + /* Special handling when the first child is attached. */ + if (!child_nr) + { + m_info->reclength= myisam->s->base.reclength; + min_keys= myisam->s->base.keys; + key_parts= myisam->s->base.key_parts; + if (!m_info->rec_per_key_part) + { + if(!(m_info->rec_per_key_part= (ulong*) + my_malloc(key_parts * sizeof(long), MYF(MY_WME|MY_ZEROFILL)))) + goto err; /* purecov: inspected */ + errpos= 1; + } + } + + /* Add MyISAM table info. */ + m_info->open_tables[child_nr].table= myisam; + m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset; + file_offset+= myisam->state->data_file_length; + + /* Check table definition match. */ + if (m_info->reclength != myisam->s->base.reclength) + { + DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d", + myisam->filename, + (handle_locking & HA_OPEN_FOR_REPAIR))); + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; + if (handle_locking & HA_OPEN_FOR_REPAIR) + { + myrg_print_wrong_table(myisam->filename); + continue; + } + goto err; + } + + m_info->options|= myisam->s->options; + m_info->records+= myisam->state->records; + m_info->del+= myisam->state->del; + m_info->data_file_length+= myisam->state->data_file_length; + if (min_keys > myisam->s->base.keys) + min_keys= myisam->s->base.keys; /* purecov: inspected */ + for (idx= 0; idx < key_parts; idx++) + m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] / + m_info->tables); + child_nr++; + } + + if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF) + goto err; + if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L) + { + my_errno= HA_ERR_RECORD_FILE_FULL; + goto err; + } + /* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */ + m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA); + m_info->keys= min_keys; + m_info->last_used_table= m_info->open_tables; + m_info->children_attached= TRUE; + DBUG_RETURN(0); + +err: + save_errno= my_errno; + switch (errpos) { + case 1: + my_free((char*) m_info->rec_per_key_part, MYF(0)); + m_info->rec_per_key_part= NULL; + } + my_errno= save_errno; + DBUG_RETURN(1); +} + + +/** + @brief Detach children from a MyISAM MERGE parent table. + + @param[in] m_info MERGE parent table structure + + @note Detach must not touch the children in any way. + They may have been closed at ths point already. + All references to the children should be removed. + + @return status + @retval 0 OK +*/ + +int myrg_detach_children(MYRG_INFO *m_info) +{ + DBUG_ENTER("myrg_detach_children"); + if (m_info->tables) + { + /* Do not attach/detach an empty child list. */ + m_info->children_attached= FALSE; + bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE)); + } + m_info->records= 0; + m_info->del= 0; + m_info->data_file_length= 0; + m_info->options= 0; + DBUG_RETURN(0); +} + diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c index fd783015bf4..247fb041896 100644 --- a/strings/ctype-euc_kr.c +++ b/strings/ctype-euc_kr.c @@ -179,20 +179,40 @@ static uchar NEAR sort_order_euc_kr[]= /* Support for Korean(EUC_KR) characters, by powerm90@tinc.co.kr and mrpark@tinc.co.kr */ -#define iseuc_kr(c) ((0xa1<=(uchar)(c) && (uchar)(c)<=0xfe)) +/* + Unicode mapping is done according to: + ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/KSC/KSC5601.TXT + + Valid multi-byte characters: + + [A1..FE][41..5A,61..7A,81..FE] + + Note, 0x5C is not a valid MB tail, + so escape_with_backslash_is_dangerous is not set. +*/ + +#define iseuc_kr_head(c) ((0xa1<=(uchar)(c) && (uchar)(c)<=0xfe)) + +#define iseuc_kr_tail1(c) ((uchar) (c) >= 0x41 && (uchar) (c) <= 0x5A) +#define iseuc_kr_tail2(c) ((uchar) (c) >= 0x61 && (uchar) (c) <= 0x7A) +#define iseuc_kr_tail3(c) ((uchar) (c) >= 0x81 && (uchar) (c) <= 0xFE) + +#define iseuc_kr_tail(c) (iseuc_kr_tail1(c) || \ + iseuc_kr_tail2(c) || \ + iseuc_kr_tail3(c)) static uint ismbchar_euc_kr(CHARSET_INFO *cs __attribute__((unused)), const char* p, const char *e) { return ((*(uchar*)(p)<0x80)? 0:\ - iseuc_kr(*(p)) && (e)-(p)>1 && iseuc_kr(*((p)+1))? 2:\ + iseuc_kr_head(*(p)) && (e)-(p)>1 && iseuc_kr_tail(*((p)+1))? 2:\ 0); } static uint mbcharlen_euc_kr(CHARSET_INFO *cs __attribute__((unused)),uint c) { - return (iseuc_kr(c) ? 2 : 1); + return (iseuc_kr_head(c) ? 2 : 1); } @@ -8654,7 +8674,7 @@ my_well_formed_len_euckr(CHARSET_INFO *cs __attribute__((unused)), /* Single byte ascii character */ b++; } - else if (b < emb && iseuc_kr(*b) && iseuc_kr(b[1])) + else if (b < emb && iseuc_kr_head(*b) && iseuc_kr_tail(b[1])) { /* Double byte character */ b+= 2; |