Commit 520795fa authored by Luis Soares's avatar Luis Soares

BUG#48297: Schema name is ignored when LOAD DATA is written into

binlog, replication aborts

In SBR or MBR, the schema name is not being written to the binlog
when executing a LOAD DATA statement. This becomes a problem when
the current database (lets call it db1) is different from the
table's schema (lets call it db2). For instance, take the
following statements:
  
  use db1;
  load data local infile 'infile.txt' into table db2.t

Should this statement be logged without t's schema (db2), when
replaying it, one can get db1.t populated instead of db2.t (if
db1.t exists). On the other hand, if there is no db1.t at all,
replication will stop.

We fix this by always logging the table (in load file) with fully
qualified name when its schema is different from the current
database or when no default database was selected.
parent db944fda
...@@ -158,4 +158,65 @@ LOAD DATA INFILE "../../std_data/words.dat" INTO TABLE t1; ...@@ -158,4 +158,65 @@ LOAD DATA INFILE "../../std_data/words.dat" INTO TABLE t1;
DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t1;
# BUG#48297: Schema name is ignored when LOAD DATA is written into binlog,
# replication aborts
-- source include/master-slave-reset.inc
-- let $db1= b48297_db1
-- let $db2= b42897_db2
-- connection master
-- disable_warnings
-- eval drop database if exists $db1
-- eval drop database if exists $db2
-- enable_warnings
-- eval create database $db1
-- eval create database $db2
-- eval use $db1
-- eval CREATE TABLE t1 (c1 VARCHAR(256)) engine=$engine_type;
-- eval use $db2
-- echo ### assertion: works with cross-referenced database
-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
-- eval LOAD DATA LOCAL INFILE '$MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE $db1.t1
-- eval use $db1
-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
-- echo ### assertion: works with fully qualified name on current database
-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
-- eval LOAD DATA LOCAL INFILE '$MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE $db1.t1
-- echo ### assertion: works without fully qualified name on current database
-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
-- eval LOAD DATA LOCAL INFILE '$MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE t1
-- echo ### create connection without default database
-- echo ### connect (conn2,localhost,root,,*NO-ONE*);
connect (conn2,localhost,root,,*NO-ONE*);
-- connection conn2
-- echo ### assertion: works without stating the default database
-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
-- eval LOAD DATA LOCAL INFILE '$MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE $db1.t1
-- echo ### disconnect and switch back to master connection
-- disconnect conn2
-- connection master
-- sync_slave_with_master
-- eval use $db1
let $diff_table_1=master:$db1.t1;
let $diff_table_2=slave:$db1.t1;
source include/diff_tables.inc;
-- connection master
-- eval DROP DATABASE $db1
-- eval DROP DATABASE $db2
-- sync_slave_with_master
# End of 4.1 tests # End of 4.1 tests
...@@ -86,3 +86,32 @@ CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=INNODB; ...@@ -86,3 +86,32 @@ CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=INNODB;
LOAD DATA INFILE "../../std_data/words.dat" INTO TABLE t1; LOAD DATA INFILE "../../std_data/words.dat" INTO TABLE t1;
ERROR 23000: Duplicate entry 'Aarhus' for key 'PRIMARY' ERROR 23000: Duplicate entry 'Aarhus' for key 'PRIMARY'
DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t1;
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 b48297_db1;
drop database if exists b42897_db2;
create database b48297_db1;
create database b42897_db2;
use b48297_db1;
CREATE TABLE t1 (c1 VARCHAR(256)) engine=MyISAM;;
use b42897_db2;
### assertion: works with cross-referenced database
LOAD DATA LOCAL INFILE 'MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE b48297_db1.t1;
use b48297_db1;
### assertion: works with fully qualified name on current database
LOAD DATA LOCAL INFILE 'MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE b48297_db1.t1;
### assertion: works without fully qualified name on current database
LOAD DATA LOCAL INFILE 'MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE t1;
### create connection without default database
### connect (conn2,localhost,root,,*NO-ONE*);
### assertion: works without stating the default database
LOAD DATA LOCAL INFILE 'MYSQLTEST_VARDIR/std_data/loaddata5.dat' INTO TABLE b48297_db1.t1;
### disconnect and switch back to master connection
use b48297_db1;
Comparing tables master:b48297_db1.t1 and slave:b48297_db1.t1
DROP DATABASE b48297_db1;
DROP DATABASE b42897_db2;
...@@ -84,7 +84,7 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ...@@ -84,7 +84,7 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
bool ignore_check_option_errors); bool ignore_check_option_errors);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
const char* db_arg, const char* db_arg, /* table's database */
const char* table_name_arg, const char* table_name_arg,
enum enum_duplicates duplicates, enum enum_duplicates duplicates,
bool ignore, bool ignore,
...@@ -501,7 +501,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -501,7 +501,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (thd->transaction.stmt.modified_non_trans_table) if (thd->transaction.stmt.modified_non_trans_table)
write_execute_load_query_log_event(thd, ex, write_execute_load_query_log_event(thd, ex,
tdb, table_list->table_name, table_list->db,
table_list->table_name,
handle_duplicates, ignore, handle_duplicates, ignore,
transactional_table, transactional_table,
errcode); errcode);
...@@ -548,7 +549,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -548,7 +549,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
{ {
int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
write_execute_load_query_log_event(thd, ex, write_execute_load_query_log_event(thd, ex,
tdb, table_list->table_name, table_list->db, table_list->table_name,
handle_duplicates, ignore, handle_duplicates, ignore,
transactional_table, transactional_table,
errcode); errcode);
...@@ -573,7 +574,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -573,7 +574,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
/* Not a very useful function; just to avoid duplication of code */ /* Not a very useful function; just to avoid duplication of code */
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
const char* db_arg, const char* db_arg, /* table's database */
const char* table_name_arg, const char* table_name_arg,
enum enum_duplicates duplicates, enum enum_duplicates duplicates,
bool ignore, bool ignore,
...@@ -590,8 +591,27 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, ...@@ -590,8 +591,27 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
Item *item, *val; Item *item, *val;
String pfield, pfields; String pfield, pfields;
int n; int n;
const char *tbl= table_name_arg;
const char *tdb= (thd->db != NULL ? thd->db : db_arg);
String string_buf;
Load_log_event lle(thd, ex, db_arg, table_name_arg, fv, duplicates, if (!thd->db || strcmp(db_arg, thd->db))
{
/*
If used database differs from table's database,
prefix table name with database name so that it
becomes a FQ name.
*/
string_buf.set_charset(system_charset_info);
string_buf.append(db_arg);
string_buf.append("`");
string_buf.append(".");
string_buf.append("`");
string_buf.append(table_name_arg);
tbl= string_buf.c_ptr_safe();
}
Load_log_event lle(thd, ex, tdb, tbl, fv, duplicates,
ignore, transactional_table); ignore, transactional_table);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment