Commit 6dff8012 authored by Luis Soares's avatar Luis Soares

BUG#39701: Mixed binlog format does not switch to row mode on

LOAD_FILE
            
LOAD_FILE is not safe to replicate in STATEMENT mode, because it
depends on a file (which is loaded on master and may not exist in
slave(s)). This leads to scenarios on which the slave replicates the
statement with 'load_file' and it will try to load the file from local
file system. Given that the file may not exist in the slave filesystem
the operation will not succeed (probably returning NULL), causing
master and slave(s) to diverge. However, when using MIXED mode
replication, this can be made to work, if the statement including
LOAD_FILE is marked as unsafe, triggering a switch to ROW mode,
meaning that the contents of the file are written to binlog as row
events. Consequently, the contents from the file in the master will
reach the slave via the binlog.
           
This patch addresses this bug by marking the load_file function as
unsafe. When in mixed mode and when LOAD_FILE is issued, there will be
a switch to row mode. Furthermore, when in statement mode, the
LOAD_FILE will raise a warning that the statement is unsafe in that
mode.
parent 7b7d5b5b
# Begin clean up test section
--disable_warnings
connection master;
DROP PROCEDURE IF EXISTS test.p1;
DROP TABLE IF EXISTS test.t1;
--enable_warnings
# Section 1 test
CREATE TABLE test.t1 (a INT, blob_column LONGBLOB, PRIMARY KEY(a));
INSERT INTO test.t1 VALUES(1,'test');
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=1;
delimiter |;
create procedure test.p1()
begin
INSERT INTO test.t1 VALUES(2,'test');
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=2;
end|
delimiter ;|
CALL test.p1();
SELECT * FROM test.t1 ORDER BY blob_column;
save_master_pos;
sync_slave_with_master;
connection slave;
# Need to allow some time when NDB engine is used for
# the injector thread to have time to populate binlog
let $wait_condition= SELECT INSTR(blob_column,'aberration') > 0 FROM test.t1 WHERE a = 2;
--source include/wait_condition.inc
SELECT * FROM test.t1 ORDER BY blob_column;
# Cleanup
connection master;
DROP PROCEDURE IF EXISTS test.p1;
DROP TABLE test.t1;
sync_slave_with_master;
...@@ -225,3 +225,21 @@ aberration ...@@ -225,3 +225,21 @@ aberration
DROP PROCEDURE IF EXISTS test.p1; DROP PROCEDURE IF EXISTS test.p1;
DROP TABLE test.t1; DROP TABLE test.t1;
**** Resetting master and slave ****
include/stop_slave.inc
RESET SLAVE;
RESET MASTER;
include/start_slave.inc
SELECT repeat('x',20) INTO OUTFILE 'MYSQLTEST_VARDIR/tmp/bug_39701.data';
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (t text);
CREATE PROCEDURE p(file TEXT)
BEGIN
INSERT INTO t1 VALUES (LOAD_FILE(file));
END|
include/stop_slave.inc
CALL p('MYSQLTEST_VARDIR/tmp/bug_39701.data');
include/start_slave.inc
Comparing tables master:test.t1 and slave:test.t1
DROP TABLE t1;
DROP PROCEDURE p;
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 PROCEDURE IF EXISTS test.p1;
DROP TABLE IF EXISTS test.t1;
CREATE TABLE test.t1 (a INT, blob_column LONGBLOB, PRIMARY KEY(a));
INSERT INTO test.t1 VALUES(1,'test');
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=1;
Warnings:
Warning 1592 Statement is not safe to log in statement format.
create procedure test.p1()
begin
INSERT INTO test.t1 VALUES(2,'test');
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=2;
end|
CALL test.p1();
Warnings:
Warning 1592 Statement is not safe to log in statement format.
SELECT * FROM test.t1 ORDER BY blob_column;
a blob_column
1 abase
abased
abasement
abasements
abases
abash
abashed
abashes
abashing
abasing
abate
abated
abatement
abatements
abater
abates
abating
Abba
abbe
abbey
abbeys
abbot
abbots
Abbott
abbreviate
abbreviated
abbreviates
abbreviating
abbreviation
abbreviations
Abby
abdomen
abdomens
abdominal
abduct
abducted
abduction
abductions
abductor
abductors
abducts
Abe
abed
Abel
Abelian
Abelson
Aberdeen
Abernathy
aberrant
aberration
2 abase
abased
abasement
abasements
abases
abash
abashed
abashes
abashing
abasing
abate
abated
abatement
abatements
abater
abates
abating
Abba
abbe
abbey
abbeys
abbot
abbots
Abbott
abbreviate
abbreviated
abbreviates
abbreviating
abbreviation
abbreviations
Abby
abdomen
abdomens
abdominal
abduct
abducted
abduction
abductions
abductor
abductors
abducts
Abe
abed
Abel
Abelian
Abelson
Aberdeen
Abernathy
aberrant
aberration
SELECT * FROM test.t1 ORDER BY blob_column;
a blob_column
1 abase
abased
abasement
abasements
abases
abash
abashed
abashes
abashing
abasing
abate
abated
abatement
abatements
abater
abates
abating
Abba
abbe
abbey
abbeys
abbot
abbots
Abbott
abbreviate
abbreviated
abbreviates
abbreviating
abbreviation
abbreviations
Abby
abdomen
abdomens
abdominal
abduct
abducted
abduction
abductions
abductor
abductors
abducts
Abe
abed
Abel
Abelian
Abelson
Aberdeen
Abernathy
aberrant
aberration
2 abase
abased
abasement
abasements
abases
abash
abashed
abashes
abashing
abasing
abate
abated
abatement
abatements
abater
abates
abating
Abba
abbe
abbey
abbeys
abbot
abbots
Abbott
abbreviate
abbreviated
abbreviates
abbreviating
abbreviation
abbreviations
Abby
abdomen
abdomens
abdominal
abduct
abducted
abduction
abductions
abductor
abductors
abducts
Abe
abed
Abel
Abelian
Abelson
Aberdeen
Abernathy
aberrant
aberration
DROP PROCEDURE IF EXISTS test.p1;
DROP TABLE test.t1;
...@@ -11,43 +11,105 @@ ...@@ -11,43 +11,105 @@
# Includes # Includes
-- source include/master-slave.inc -- source include/master-slave.inc
-- source include/have_binlog_format_mixed_or_row.inc
-- source extra/rpl_tests/rpl_loadfile.test
# BUG#39701: Mixed binlog format does not switch to row mode on LOAD_FILE
#
# DESCRIPTION
#
# Problem: when using load_file string function and mixed binlogging format
# there was no switch to row based binlogging format. This leads
# to scenarios on which the slave replicates the statement and it
# will try to load the file from local file system, which in most
# likely it will not exist.
#
# Solution:
# Marking this function as unsafe for statement format, makes the
# statement using it to be logged in row based format. As such, data
# replicated from the master, becomes the content of the loaded file.
# Consequently, the slave receives the necessary data to complete
# the load_file instruction correctly.
#
# IMPLEMENTATION
#
# The test is implemented as follows:
#
# On Master,
# i) write to file the desired content.
# ii) create table and stored procedure with load_file
# iii) stop slave
# iii) execute load_file
# iv) remove file
#
# On Slave,
# v) start slave
# vi) sync it with master so that it gets the updates from binlog (which
# should have bin logged in row format).
#
# If the the binlog format does not change to row, then the assertion
# done in the following step fails. This happens because tables differ
# since the file does not exist anymore, meaning that when slave
# attempts to execute LOAD_FILE statement it inserts NULL on table
# instead of the same contents that the master loaded when it executed
# the procedure (which was executed when file existed).
#
# vii) assert that the contents of master and slave
# table are the same
# Begin clean up test section
--disable_warnings
connection master; connection master;
DROP PROCEDURE IF EXISTS test.p1; source include/reset_master_and_slave.inc;
DROP TABLE IF EXISTS test.t1;
--enable_warnings connection master;
let $file= $MYSQLTEST_VARDIR/tmp/bug_39701.data;
# Section 1 test
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
CREATE TABLE test.t1 (a INT, blob_column LONGBLOB, PRIMARY KEY(a)); --eval SELECT repeat('x',20) INTO OUTFILE '$file'
INSERT INTO test.t1 VALUES(1,'test');
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=1; disable_warnings;
delimiter |; DROP TABLE IF EXISTS t1;
create procedure test.p1() enable_warnings;
begin
INSERT INTO test.t1 VALUES(2,'test'); CREATE TABLE t1 (t text);
UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=2; DELIMITER |;
end| CREATE PROCEDURE p(file TEXT)
delimiter ;| BEGIN
INSERT INTO t1 VALUES (LOAD_FILE(file));
CALL test.p1(); END|
SELECT * FROM test.t1 ORDER BY blob_column; DELIMITER ;|
save_master_pos;
sync_slave_with_master; # stop slave before issuing the load_file on master
connection slave; connection slave;
# Need to allow some time when NDB engine is used for source include/stop_slave.inc;
# the injector thread to have time to populate binlog
let $wait_condition= SELECT INSTR(blob_column,'aberration') > 0 FROM test.t1 WHERE a = 2; connection master;
--source include/wait_condition.inc
SELECT * FROM test.t1 ORDER BY blob_column; # test: check that logging falls back to rbr.
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--eval CALL p('$file')
# test: remove the file from the filesystem and assert that slave still
# gets the loaded file
remove_file $file;
# now that the file is removed it is safe (regarding what we want to test)
# to start slave
connection slave;
source include/start_slave.inc;
# Cleanup
connection master; connection master;
DROP PROCEDURE IF EXISTS test.p1;
DROP TABLE test.t1;
sync_slave_with_master; sync_slave_with_master;
# End of 5.0 test case # assertion: assert that the slave got the updates even
# if the file was removed before the slave started,
# meaning that contents were indeed transfered
# through binlog (in row format)
let $diff_table_1=master:test.t1;
let $diff_table_2=slave:test.t1;
source include/diff_tables.inc;
# CLEAN UP
DROP TABLE t1;
DROP PROCEDURE p;
sync_slave_with_master;
#############################################################################
# Original Author: JBM #
# Original Date: Aug/18/2005 #
#############################################################################
# TEST: To test the LOAD_FILE() in rbr #
#############################################################################
# Change Author: JBM
# Change Date: 2006-01-16
# Change: Added Order by for NDB
# Change: Split the original test file. This one forces STATEMENT only because
# when in STATEMENT mode, the load_file will issue a warning, whereas
# in RBR or MIXED mode it does not (by lsoares).
##########
# Includes
-- source include/master-slave.inc
-- source include/have_binlog_format_statement.inc
-- source extra/rpl_tests/rpl_loadfile.test
...@@ -3791,6 +3791,7 @@ Create_func_load_file Create_func_load_file::s_singleton; ...@@ -3791,6 +3791,7 @@ Create_func_load_file Create_func_load_file::s_singleton;
Item* Item*
Create_func_load_file::create(THD *thd, Item *arg1) Create_func_load_file::create(THD *thd, Item *arg1)
{ {
thd->lex->set_stmt_unsafe();
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_load_file(arg1); return new (thd->mem_root) Item_load_file(arg1);
} }
......
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