mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 14:33:32 +03:00 
			
		
		
		
	- detect the need for row-based binlogging not at execution stage but earlier at parsing stage; needed for example for CREATE TABLE SELECT UUID().
- more tests of this mixed mode.
mysql-test/r/rpl_switch_stm_row_mixed.result:
  result update
mysql-test/t/rpl_switch_stm_row_mixed.test:
  testing more scenarios for the mixed replication mode.
  Added support for manual testing of UDFs vs the mixed mode (behind a variable in the test).
  Changing old file names to better ones.
sql/item_create.cc:
  at parse time, when we see a UUID(), put up a flag in LEX to say this binlogs properly only with row-based binlogging.
sql/item_func.cc:
  it's not perfect to put up the flag at this execution stage, better do it at parse stage.
sql/item_strfunc.cc:
  it's not perfect to put up the flag at this execution stage, better do it at parse stage
sql/set_var.cc:
  this assertion is wrong, this piece of code can happen in RBR mode too.
sql/sql_lex.cc:
  when we reinitialize the LEX members before every query, we have to reinitialize the new flag
sql/sql_lex.h:
  A new flag, set at parsing stage, which tells if some items seen during parsing stage require row-based replication to binlog/replicate correctly
  when this statement is later executed.
  It has to be in LEX and not directly in THD, for this to work in prepared statements.
sql/sql_parse.cc:
  Parsing stage happened at some time in the past and set up the flag in LEX, now that we execute the statement we actually turn on row-based binlogging
  if the thread's binlog format is "mixed". We then turn it off when leaving mysql_execute_command().
  Some cleanup code was not executed if leaving mysql_execute_command() at the "error" label, fixing this. A better fix than the "goto end" would be
  to modify each "goto error" to "res=1; goto end" but it required changing many lines which I don't want to do now ("make smallest possible patch").
sql/sql_yacc.yy:
  When at parsing stage we see a UDF we put up a flag to say that row-based binlogging is preferred.
		
	
		
			
				
	
	
		
			217 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
-- source include/have_binlog_format_row.inc
 | 
						|
-- source include/master-slave.inc
 | 
						|
 | 
						|
connection master;
 | 
						|
--disable_warnings
 | 
						|
drop database if exists mysqltest1;
 | 
						|
create database mysqltest1;
 | 
						|
--enable_warnings
 | 
						|
use mysqltest1;
 | 
						|
 | 
						|
show global variables like "binlog_format%";
 | 
						|
show session variables like "binlog_format%";
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
 | 
						|
CREATE TABLE t1 (a varchar(100));
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select concat(UUID(),?)';
 | 
						|
set @string="emergency";
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
insert into t1 values(concat(UUID(),"work"));
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
insert into t1 values(concat("for",UUID()));
 | 
						|
insert into t1 select "yesterday";
 | 
						|
 | 
						|
# verify that temp tables prevent a switch to SBR
 | 
						|
create temporary table tmp(a char(3));
 | 
						|
insert into tmp values("see");
 | 
						|
--error ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR
 | 
						|
set binlog_format=statement;
 | 
						|
insert into t1 select * from tmp;
 | 
						|
drop temporary table tmp;
 | 
						|
 | 
						|
# Now we go to SBR
 | 
						|
set binlog_format=statement;
 | 
						|
show global variables like "binlog_format%";
 | 
						|
show session variables like "binlog_format%";
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
set global binlog_format=statement;
 | 
						|
show global variables like "binlog_format%";
 | 
						|
show session variables like "binlog_format%";
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
set @string="emergency";
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
insert into t1 values("for");
 | 
						|
insert into t1 select "yesterday";
 | 
						|
 | 
						|
# test SET DEFAULT (=statement at this point of test)
 | 
						|
set binlog_format=default;
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
# due to cluster it's hard to set back to default
 | 
						|
--error ER_NO_DEFAULT
 | 
						|
set global binlog_format=default;
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
set @string="emergency";
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
insert into t1 values("for");
 | 
						|
insert into t1 select "yesterday";
 | 
						|
 | 
						|
# and now the mixed mode
 | 
						|
 | 
						|
set binlog_format=mixed;
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
set global binlog_format=mixed;
 | 
						|
select @@global.binlog_format, @@session.binlog_format;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select concat(UUID(),?)';
 | 
						|
set @string="emergency";
 | 
						|
insert into t1 values("work");
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
insert into t1 values(concat(UUID(),"work"));
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
insert into t1 values(concat("for",UUID()));
 | 
						|
insert into t1 select "yesterday";
 | 
						|
 | 
						|
prepare stmt1 from 'insert into t1 select ?';
 | 
						|
insert into t1 values(concat(UUID(),"work"));
 | 
						|
execute stmt1 using @string;
 | 
						|
deallocate prepare stmt1;
 | 
						|
 | 
						|
insert into t1 values(concat("for",UUID()));
 | 
						|
insert into t1 select "yesterday";
 | 
						|
 | 
						|
# Test of CREATE TABLE SELECT
 | 
						|
 | 
						|
create table t2 select UUID();
 | 
						|
create table t3 select 1 union select UUID();
 | 
						|
create table t4 select * from t1 where 3 in (select 1 union select 2 union select UUID() union select 3);
 | 
						|
create table t5 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3);
 | 
						|
# what if UUID() is first:
 | 
						|
insert into t5 select UUID() from t1 where 3 in (select 1 union select 2 union select 3 union select * from t4);
 | 
						|
 | 
						|
# inside a stored procedure (inside a function or trigger won't
 | 
						|
# work)
 | 
						|
 | 
						|
delimiter |;
 | 
						|
create procedure foo()
 | 
						|
begin
 | 
						|
insert into t1 values("work");
 | 
						|
insert into t1 values(concat("for",UUID()));
 | 
						|
insert into t1 select "yesterday";
 | 
						|
end|
 | 
						|
create procedure foo2()
 | 
						|
begin
 | 
						|
insert into t1 values(concat("emergency",UUID()));
 | 
						|
insert into t1 values("work");
 | 
						|
insert into t1 values(concat("for",UUID()));
 | 
						|
set session binlog_format=row; # accepted for stored procs
 | 
						|
insert into t1 values("more work");
 | 
						|
set session binlog_format=mixed;
 | 
						|
end|
 | 
						|
create function foo3() returns bigint unsigned
 | 
						|
begin
 | 
						|
  set session binlog_format=row; # rejected for stored funcs
 | 
						|
  insert into t1 values("alarm");
 | 
						|
  return 100;
 | 
						|
end|
 | 
						|
delimiter ;|
 | 
						|
call foo();
 | 
						|
call foo2();
 | 
						|
 | 
						|
# test that can't SET in a stored function
 | 
						|
--error ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT
 | 
						|
select foo3();
 | 
						|
select * from t1 where a="alarm";
 | 
						|
 | 
						|
# If you want to do manual testing of the mixed mode regarding UDFs (not
 | 
						|
# testable automatically as quite platform- and compiler-dependent),
 | 
						|
# you just need to set the variable below to 1, and to
 | 
						|
# "make udf_example.so" in sql/, and to copy sql/udf_example.so to
 | 
						|
# MYSQL_TEST_DIR/lib/mysql.
 | 
						|
let $you_want_to_test_UDF=0;
 | 
						|
if ($you_want_to_test_UDF)
 | 
						|
{
 | 
						|
  CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so';
 | 
						|
  prepare stmt1 from 'insert into t1 select metaphon(?)';
 | 
						|
  set @string="emergency";
 | 
						|
  insert into t1 values("work");
 | 
						|
  execute stmt1 using @string;
 | 
						|
  deallocate prepare stmt1;
 | 
						|
  prepare stmt1 from 'insert into t1 select ?';
 | 
						|
  insert into t1 values(metaphon("work"));
 | 
						|
  execute stmt1 using @string;
 | 
						|
  deallocate prepare stmt1;
 | 
						|
  insert into t1 values(metaphon("for"));
 | 
						|
  insert into t1 select "yesterday";
 | 
						|
  create table t6 select metaphon("for");
 | 
						|
  create table t7 select 1 union select metaphon("for");
 | 
						|
  create table t8 select * from t1 where 3 in (select 1 union select 2 union select metaphon("for") union select 3);
 | 
						|
  create table t9 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3);
 | 
						|
}
 | 
						|
 | 
						|
# and now compare:
 | 
						|
 | 
						|
# first check that data on master is sensible
 | 
						|
select count(*) from t1;
 | 
						|
select count(*) from t2;
 | 
						|
select count(*) from t3;
 | 
						|
select count(*) from t4;
 | 
						|
select count(*) from t5;
 | 
						|
if ($you_want_to_test_UDF)
 | 
						|
{
 | 
						|
  select count(*) from t6;
 | 
						|
  select count(*) from t7;
 | 
						|
  select count(*) from t8;
 | 
						|
  select count(*) from t9;
 | 
						|
}
 | 
						|
 | 
						|
--replace_column 2 # 5 #
 | 
						|
--replace_regex /table_id: [0-9]+/table_id: #/
 | 
						|
show binlog events from 102;
 | 
						|
sync_slave_with_master;
 | 
						|
# as we're using UUID we don't SELECT but use "diff" like in rpl_row_UUID
 | 
						|
--exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql
 | 
						|
--exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql
 | 
						|
 | 
						|
connection master;
 | 
						|
drop database mysqltest1;
 | 
						|
sync_slave_with_master;
 | 
						|
 | 
						|
# Let's compare. Note: If they match test will pass, if they do not match
 | 
						|
# the test will show that the diff statement failed and not reject file
 | 
						|
# will be created. You will need to go to the mysql-test dir and diff
 | 
						|
# the files your self to see what is not matching
 | 
						|
 | 
						|
--exec diff $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql;
 |