Commit b9f6f9bc authored by unknown's avatar unknown
Browse files

Fixes to the replication mixed mode (patch approved by Monty):

- 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.
parent 08ef9214
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -110,6 +110,11 @@ execute stmt1 using @string;
deallocate prepare stmt1;
insert into t1 values(concat("for",UUID()));
insert into t1 select "yesterday";
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);
insert into t5 select UUID() from t1 where 3 in (select 1 union select 2 union select 3 union select * from t4);
create procedure foo()
begin
insert into t1 values("work");
@@ -137,6 +142,21 @@ select foo3();
ERROR HY000: Cannot change the binary logging format inside a stored function or trigger
select * from t1 where a="alarm";
a
select count(*) from t1;
count(*)
36
select count(*) from t2;
count(*)
1
select count(*) from t3;
count(*)
2
select count(*) from t4;
count(*)
29
select count(*) from t5;
count(*)
58
show binlog events from 102;
Log_name	Pos	Event_type	Server_id	End_log_pos	Info
master-bin.000001	#	Query	1	#	drop database if exists mysqltest1
@@ -179,17 +199,44 @@ master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 select @'string'
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 values("for")
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select "yesterday"
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 values("work")
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	User var	1	#	@`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select @'string'
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select "yesterday"
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	User var	1	#	@`string`=_latin1 0x656D657267656E6379 COLLATE latin1_swedish_ci
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select @'string'
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select "yesterday"
master-bin.000001	#	Query	1	#	use `mysqltest1`; CREATE TABLE `t2` (
  `UUID()` varchar(36) CHARACTER SET utf8 NOT NULL DEFAULT ''
)
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t2)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; COMMIT
master-bin.000001	#	Query	1	#	use `mysqltest1`; CREATE TABLE `t3` (
  `1` varbinary(108) NOT NULL DEFAULT ''
)
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t3)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; COMMIT
master-bin.000001	#	Query	1	#	use `mysqltest1`; CREATE TABLE `t4` (
  `a` varchar(100) DEFAULT NULL
)
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t4)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; COMMIT
master-bin.000001	#	Query	1	#	use `mysqltest1`; create table t5 select * from t1 where 3 in (select 1 union select 2 union select curdate() union select 3)
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t5)
master-bin.000001	#	Write_rows	1	#	table_id: #
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo()
begin
insert into t1 values("work");
@@ -212,10 +259,13 @@ insert into t1 values("alarm");
return 100;
end
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 values("work")
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 select "yesterday"
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Query	1	#	use `mysqltest1`; insert into t1 values("work")
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
master-bin.000001	#	Table_map	1	#	table_id: # (mysqltest1.t1)
master-bin.000001	#	Write_rows	1	#	table_id: # flags: STMT_END_F
+53 −4
Original line number Diff line number Diff line
@@ -111,6 +111,15 @@ 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)

@@ -140,20 +149,60 @@ delimiter ;|
call foo();
call foo2();

# test that can't SET in a stored function if not in row-based mode
# 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_row_UDF_master.sql
--exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert --no-create-info mysqltest1 > $MYSQLTEST_VARDIR/tmp/rpl_row_UDF_slave.sql
--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;
@@ -164,4 +213,4 @@ sync_slave_with_master;
# 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_row_UDF_master.sql $MYSQLTEST_VARDIR/tmp/rpl_row_UDF_slave.sql;
--exec diff $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_master.sql $MYSQLTEST_VARDIR/tmp/rpl_switch_stm_row_mixed_slave.sql;
+3 −1
Original line number Diff line number Diff line
@@ -431,7 +431,9 @@ Item *create_func_unhex(Item* a)

Item *create_func_uuid(void)
{
  return new Item_func_uuid();
  THD *thd= current_thd;
  thd->lex->binlog_row_based_if_mixed= 1;
  return new(thd->mem_root) Item_func_uuid();
}

Item *create_func_version(void)
+0 −1
Original line number Diff line number Diff line
@@ -2657,7 +2657,6 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
             u_d->name.str, ER(ER_UNKNOWN_ERROR));
    DBUG_RETURN(TRUE);
  }
  thd->set_current_stmt_binlog_row_based_if_mixed();
  DBUG_RETURN(FALSE);
}

+0 −1
Original line number Diff line number Diff line
@@ -3002,7 +3002,6 @@ String *Item_func_uuid::val_str(String *str)
  char *s;
  THD *thd= current_thd;

  thd->set_current_stmt_binlog_row_based_if_mixed();
  pthread_mutex_lock(&LOCK_uuid_generator);
  if (! uuid_time) /* first UUID() call. initializing data */
  {
Loading