diff options
-rw-r--r-- | mysql-test/r/rpl000009.result | 48 | ||||
-rw-r--r-- | mysql-test/t/rpl000009.test | 56 | ||||
-rw-r--r-- | sql/repl_failsafe.cc | 14 | ||||
-rw-r--r-- | sql/slave.cc | 45 | ||||
-rw-r--r-- | sql/slave.h | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 7 |
6 files changed, 150 insertions, 24 deletions
diff --git a/mysql-test/r/rpl000009.result b/mysql-test/r/rpl000009.result index 002f6843953..4a8057467f2 100644 --- a/mysql-test/r/rpl000009.result +++ b/mysql-test/r/rpl000009.result @@ -49,21 +49,48 @@ show databases; Database mysql test +create database foo; +create table foo.t1(n int, s char(20)); +insert into foo.t1 values (1, 'original foo.t1'); +create table foo.t3(n int, s char(20)); +insert into foo.t3 values (1, 'original foo.t3'); +create database foo2; +create table foo2.t1(n int, s char(20)); +insert into foo2.t1 values (1, 'original foo2.t1'); +create database bar; +create table bar.t1(n int, s char(20)); +insert into bar.t1 values (1, 'original bar.t1'); +create table bar.t3(n int, s char(20)); +insert into bar.t3 values (1, 'original bar.t3'); load data from master; show databases; Database bar foo +foo2 mysql test use foo; show tables; Tables_in_foo +t1 +t3 +select * from t1; +n s +1 original foo.t1 +use foo2; +show tables; +Tables_in_foo2 +t1 +select * from t1; +n s +1 original foo2.t1 use bar; show tables; Tables_in_bar t1 t2 +t3 select * from bar.t1; n s 1 one bar @@ -74,6 +101,9 @@ n s 11 eleven bar 12 twelve bar 13 thirteen bar +select * from bar.t3; +n s +1 original bar.t3 insert into bar.t1 values (4, 'four bar'); select * from bar.t1; n s @@ -81,5 +111,23 @@ n s 2 two bar 3 three bar 4 four bar +insert into bar.t1 values(10, 'should be there'); +flush tables; +load data from master; +Error on delete of './bar/t1.MYI' (Errcode: 13) +select * from bar.t1; +n s +1 one bar +2 two bar +3 three bar +4 four bar +10 should be there +load table bar.t1 from master; +Table 't1' already exists +drop table bar.t1; +load table bar.t1 from master; +start slave; drop database bar; drop database foo; +drop database foo; +drop database foo2; diff --git a/mysql-test/t/rpl000009.test b/mysql-test/t/rpl000009.test index 5f55355271a..975cfbf9a65 100644 --- a/mysql-test/t/rpl000009.test +++ b/mysql-test/t/rpl000009.test @@ -60,16 +60,45 @@ sync_with_master; # This should show that the slave is empty at this point show databases; +# Create foo and foo2 on slave; we expect that LOAD DATA FROM MASTER will +# neither touch database foo nor foo2. +create database foo; +create table foo.t1(n int, s char(20)); +insert into foo.t1 values (1, 'original foo.t1'); +create table foo.t3(n int, s char(20)); +insert into foo.t3 values (1, 'original foo.t3'); +create database foo2; +create table foo2.t1(n int, s char(20)); +insert into foo2.t1 values (1, 'original foo2.t1'); +# Create bar, and bar.t1, to check that it gets replaced, +# and bar.t3 to check that it is not touched (there is no bar.t3 on master) +create database bar; +create table bar.t1(n int, s char(20)); +insert into bar.t1 values (1, 'original bar.t1'); +create table bar.t3(n int, s char(20)); +insert into bar.t3 values (1, 'original bar.t3'); + load data from master; # Now let's check if we have the right tables and the right data in them show databases; use foo; -show tables; +# LOAD DATA FROM MASTER uses only replicate_*_db rules to decide which databases +# have to be copied. So it thinks "foo" has to be copied. Before 4.0.16 it would +# first drop "foo", then create "foo". This "drop" is a bug; in that case t3 +# would disappear. +# So here the effect of this bug (BUG#1248) would be to leave an empty "foo" on +# the slave. +show tables; # should be t1 & t3 +select * from t1; # should be slave's original +use foo2; +show tables; # should be t1 +select * from t1; # should be slave's original use bar; -show tables; +show tables; # should contain master's copied t1&t2, slave's original t3 select * from bar.t1; select * from bar.t2; +select * from bar.t3; # Now let's see if replication works connection master; @@ -79,6 +108,26 @@ connection slave; sync_with_master; select * from bar.t1; +# Check that LOAD DATA FROM MASTER reports the error if it can't drop a +# table to be overwritten. +insert into bar.t1 values(10, 'should be there'); +flush tables; +system chmod 500 var/slave-data/bar/; +--error 6 +load data from master; # should fail (errno 13) +system chmod 700 var/slave-data/bar/; +select * from bar.t1; # should contain the row (10, ...) + + +# Check that LOAD TABLE FROM MASTER fails if the table exists on slave +--error 1050 +load table bar.t1 from master; +drop table bar.t1; +load table bar.t1 from master; + +# as LOAD DATA FROM MASTER failed it did not restart slave threads +start slave; + # Now time for cleanup connection master; drop database bar; @@ -86,4 +135,5 @@ drop database foo; save_master_pos; connection slave; sync_with_master; - +drop database foo; +drop database foo2; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index dc3f3c87dde..8deb23e8586 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -717,7 +717,8 @@ static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db, if (!tables_ok(thd, &table)) continue; } - if ((error= fetch_master_table(thd, db, table_name, mi, mysql))) + /* download master's table and overwrite slave's table */ + if ((error= fetch_master_table(thd, db, table_name, mi, mysql, 1))) return error; } return 0; @@ -819,8 +820,11 @@ int load_master_data(THD* thd) char* db = row[0]; /* - Do not replicate databases excluded by rules - also skip mysql database - in most cases the user will + Do not replicate databases excluded by rules. We also test + replicate_wild_*_table rules (replicate_wild_ignore_table='db1.%' will + be considered as "ignore the 'db1' database as a whole, as it already + works for CREATE DATABASE and DROP DATABASE). + Also skip 'mysql' database - in most cases the user will mess up and not exclude mysql database with the rules when he actually means to - in this case, he is up for a surprise if his priv tables get dropped and downloaded from master @@ -830,14 +834,14 @@ int load_master_data(THD* thd) */ if (!db_ok(db, replicate_do_db, replicate_ignore_db) || + !db_ok_with_wild_table(db) || !strcmp(db,"mysql")) { *cur_table_res = 0; continue; } - if (mysql_rm_db(thd, db, 1,1) || - mysql_create_db(thd, db, 0, 1)) + if (mysql_create_db(thd, db, HA_LEX_CREATE_IF_NOT_EXISTS, 1)) { send_error(&thd->net, 0, 0); cleanup_mysql_results(db_res, cur_table_res - 1, table_res); diff --git a/sql/slave.cc b/sql/slave.cc index 1bc8dfc5b78..10d451a88bc 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -72,7 +72,7 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, void* thread_killed_arg); static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, NET* net, const char* db, - const char* table_name); + const char* table_name, bool overwrite); static int check_master_version(MYSQL* mysql, MASTER_INFO* mi); @@ -1033,12 +1033,22 @@ static int check_master_version(MYSQL* mysql, MASTER_INFO* mi) return 0; } +/* + Used by fetch_master_table (used by LOAD TABLE tblname FROM MASTER and LOAD + DATA FROM MASTER). Drops the table (if 'overwrite' is true) and recreates it + from the dump. Honours replication inclusion/exclusion rules. + + RETURN VALUES + 0 success + 1 error +*/ static int create_table_from_dump(THD* thd, NET* net, const char* db, - const char* table_name) + const char* table_name, bool overwrite) { ulong packet_len = my_net_read(net); // read create table statement char *query; + char* save_db; Vio* save_vio; HA_CHECK_OPT check_opt; TABLE_LIST tables; @@ -1078,13 +1088,24 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, thd->current_tablenr = 0; thd->query_error = 0; thd->net.no_send_ok = 1; + + bzero((char*) &tables,sizeof(tables)); + tables.db = (char*)db; + tables.alias= tables.real_name= (char*)table_name; + /* Drop the table if 'overwrite' is true */ + if (overwrite && mysql_rm_table(thd,&tables,1)) /* drop if exists */ + { + send_error(&thd->net); + sql_print_error("create_table_from_dump: failed to drop the table"); + goto err; + } - /* we do not want to log create table statement */ + /* Create the table. We do not want to log the "create table" statement */ save_options = thd->options; thd->options &= ~(ulong) (OPTION_BIN_LOG); thd->proc_info = "Creating table from master dump"; // save old db in case we are creating in a different database - char* save_db = thd->db; + save_db = thd->db; thd->db = (char*)db; mysql_parse(thd, thd->query, packet_len); // run create table thd->db = save_db; // leave things the way the were before @@ -1093,11 +1114,8 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, if (thd->query_error) goto err; // mysql_parse took care of the error send - bzero((char*) &tables,sizeof(tables)); - tables.db = (char*)db; - tables.alias= tables.real_name= (char*)table_name; - tables.lock_type = TL_WRITE; thd->proc_info = "Opening master dump table"; + tables.lock_type = TL_WRITE; if (!open_ltable(thd, &tables, TL_WRITE)) { send_error(&thd->net,0,0); // Send error from open_ltable @@ -1107,10 +1125,11 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, file = tables.table->file; thd->proc_info = "Reading master dump table data"; + /* Copy the data file */ if (file->net_read_dump(net)) { net_printf(&thd->net, ER_MASTER_NET_READ); - sql_print_error("create_table_from_dump::failed in\ + sql_print_error("create_table_from_dump: failed in\ handler::net_read_dump()"); goto err; } @@ -1125,6 +1144,7 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, */ save_vio = thd->net.vio; thd->net.vio = 0; + /* Rebuild the index file from the copied data file (with REPAIR) */ error=file->repair(thd,&check_opt) != 0; thd->net.vio = save_vio; if (error) @@ -1137,7 +1157,7 @@ err: } int fetch_master_table(THD *thd, const char *db_name, const char *table_name, - MASTER_INFO *mi, MYSQL *mysql) + MASTER_INFO *mi, MYSQL *mysql, bool overwrite) { int error= 1; const char *errmsg=0; @@ -1169,9 +1189,10 @@ int fetch_master_table(THD *thd, const char *db_name, const char *table_name, errmsg= "Failed on table dump request"; goto err; } + if (create_table_from_dump(thd, &mysql->net, db_name, - table_name)) - goto err; // create_table_from_dump will have sent the error already + table_name, overwrite)) + goto err; // create_table_from_dump will have send_error already error = 0; err: diff --git a/sql/slave.h b/sql/slave.h index 67bf009763b..f61891acc91 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -384,9 +384,9 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, int mysql_table_dump(THD* thd, const char* db, const char* tbl_name, int fd = -1); -/* retrieve non-exitent table from master */ +/* retrieve table from master and copy to slave*/ int fetch_master_table(THD* thd, const char* db_name, const char* table_name, - MASTER_INFO* mi, MYSQL* mysql); + MASTER_INFO* mi, MYSQL* mysql, bool overwrite); int show_master_info(THD* thd, MASTER_INFO* mi); int show_binlog_info(THD* thd); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4d010ac9a4b..0c4e3cad763 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1568,9 +1568,12 @@ mysql_execute_command(void) goto error; } LOCK_ACTIVE_MI; - // fetch_master_table will send the error to the client on failure + /* + fetch_master_table will send the error to the client on failure. + Give error if the table already exists. + */ if (!fetch_master_table(thd, tables->db, tables->real_name, - active_mi, 0)) + active_mi, 0, 0)) { send_ok(&thd->net); } |