Fix for BUG#14769 "Function fails to replicate if fails half-way (slave stops)":
if the function, invoked in a non-binlogged caller (e.g. SELECT, DO), failed half-way on the master, slave would stop and complain that error code between him and master mismatch. To solve this, when a stored function is invoked in a non-binlogged caller (e.g. SELECT, DO), we binlog the function call as SELECT instead of as DO (see revision comment of sp_head.cc for more). And: minor wording change in the help text. This cset will cause conflicts in 5.1, I'll merge. mysql-test/r/rpl_sp.result: result update mysql-test/t/rpl_sp-slave.opt: bug just fixed so option not needed mysql-test/t/rpl_sp.test: test for more half-failed functions with DO and SELECT, to test the bug of this changeset. cleanup at the end. sql/mysqld.cc: function -> stored function (change suggested by Paul) sql/sp_head.cc: When a function updates data and is called from a non-binlogged statement (SELECT, DO), we binlog it as SELECT myfunc(), and not DO myfunc() like before.
This commit is contained in:
parent
cefc0b6dff
commit
8470ae9cb1
@ -233,20 +233,25 @@ end @ # #
|
|||||||
delete from t2;
|
delete from t2;
|
||||||
alter table t2 add unique (a);
|
alter table t2 add unique (a);
|
||||||
drop function fn1;
|
drop function fn1;
|
||||||
create function fn1()
|
create function fn1(x int)
|
||||||
returns int
|
returns int
|
||||||
begin
|
begin
|
||||||
insert into t2 values(20),(20);
|
insert into t2 values(x),(x);
|
||||||
return 10;
|
return 10;
|
||||||
end|
|
end|
|
||||||
select fn1();
|
do fn1(100);
|
||||||
|
Warnings:
|
||||||
|
Error 1062 Duplicate entry '100' for key 1
|
||||||
|
select fn1(20);
|
||||||
ERROR 23000: Duplicate entry '20' for key 1
|
ERROR 23000: Duplicate entry '20' for key 1
|
||||||
select * from t2;
|
select * from t2;
|
||||||
a
|
a
|
||||||
20
|
20
|
||||||
|
100
|
||||||
select * from t2;
|
select * from t2;
|
||||||
a
|
a
|
||||||
20
|
20
|
||||||
|
100
|
||||||
create trigger trg before insert on t1 for each row set new.a= 10;
|
create trigger trg before insert on t1 for each row set new.a= 10;
|
||||||
ERROR 42000: Access denied; you need the SUPER privilege for this operation
|
ERROR 42000: Access denied; you need the SUPER privilege for this operation
|
||||||
delete from t1;
|
delete from t1;
|
||||||
@ -324,7 +329,7 @@ insert into t1 values (x);
|
|||||||
return x+2;
|
return x+2;
|
||||||
end
|
end
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2
|
master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; DO `fn1`(20)
|
master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20)
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21))
|
master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21))
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1
|
master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1()
|
master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1()
|
||||||
@ -351,13 +356,14 @@ end
|
|||||||
master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2
|
master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a)
|
master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a)
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1
|
master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1()
|
master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int)
|
||||||
returns int
|
returns int
|
||||||
begin
|
begin
|
||||||
insert into t2 values(20),(20);
|
insert into t2 values(x),(x);
|
||||||
return 10;
|
return 10;
|
||||||
end
|
end
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; DO `fn1`()
|
master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(100)
|
||||||
|
master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20)
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1
|
master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` trigger trg before insert on t1 for each row set new.a= 10
|
master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` trigger trg before insert on t1 for each row set new.a= 10
|
||||||
master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (1)
|
||||||
@ -415,4 +421,3 @@ col
|
|||||||
test
|
test
|
||||||
DROP PROCEDURE p1;
|
DROP PROCEDURE p1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
reset master;
|
|
||||||
|
@ -1 +1 @@
|
|||||||
--log_bin_trust_routine_creators=0 --slave-skip-errors=1062
|
--log_bin_trust_routine_creators=0
|
||||||
|
@ -294,21 +294,19 @@ alter table t2 add unique (a);
|
|||||||
drop function fn1;
|
drop function fn1;
|
||||||
|
|
||||||
delimiter |;
|
delimiter |;
|
||||||
create function fn1()
|
create function fn1(x int)
|
||||||
returns int
|
returns int
|
||||||
begin
|
begin
|
||||||
insert into t2 values(20),(20);
|
insert into t2 values(x),(x);
|
||||||
return 10;
|
return 10;
|
||||||
end|
|
end|
|
||||||
|
|
||||||
delimiter ;|
|
delimiter ;|
|
||||||
|
|
||||||
# Because of BUG#14769 the following statement requires that we start
|
do fn1(100);
|
||||||
# slave with --slave-skip-errors=1062. When that bug is fixed, that
|
|
||||||
# option can be removed.
|
|
||||||
|
|
||||||
--error 1062
|
--error 1062
|
||||||
select fn1();
|
select fn1(20);
|
||||||
|
|
||||||
select * from t2;
|
select * from t2;
|
||||||
sync_slave_with_master;
|
sync_slave_with_master;
|
||||||
@ -440,4 +438,4 @@ DROP PROCEDURE p1;
|
|||||||
# cleanup
|
# cleanup
|
||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
reset master;
|
sync_slave_with_master;
|
||||||
|
@ -4923,8 +4923,8 @@ Disable with --skip-innodb-doublewrite.", (gptr*) &innobase_use_doublewrite,
|
|||||||
*/
|
*/
|
||||||
{"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
|
{"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
|
||||||
"If equal to 0 (the default), then when --log-bin is used, creation of "
|
"If equal to 0 (the default), then when --log-bin is used, creation of "
|
||||||
"a function is allowed only to users having the SUPER privilege and only "
|
"a stored function is allowed only to users having the SUPER privilege and"
|
||||||
"if this function may not break binary logging.",
|
" only if this function may not break binary logging.",
|
||||||
(gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
|
(gptr*) &trust_function_creators, (gptr*) &trust_function_creators, 0,
|
||||||
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||||
{"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
|
{"log-error", OPT_ERROR_LOG_FILE, "Error log file.",
|
||||||
|
@ -736,13 +736,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
|
|||||||
Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
|
Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
|
||||||
written into binary log. Instead we catch function calls the statement
|
written into binary log. Instead we catch function calls the statement
|
||||||
makes and write it into binary log separately (see #3).
|
makes and write it into binary log separately (see #3).
|
||||||
|
|
||||||
We actually can easily write SELECT statements into the binary log in the
|
|
||||||
right order (we don't have issues with const tables being unlocked early
|
|
||||||
because SELECTs that use FUNCTIONs unlock all tables at once) We don't do
|
|
||||||
it because replication slave thread currently can't execute SELECT
|
|
||||||
statements. Fixing this is on the TODO.
|
|
||||||
|
|
||||||
2. PROCEDURE calls
|
2. PROCEDURE calls
|
||||||
|
|
||||||
CALL statements are not written into binary log. Instead
|
CALL statements are not written into binary log. Instead
|
||||||
@ -763,7 +757,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
|
|||||||
function execution (grep for start_union_events and stop_union_events)
|
function execution (grep for start_union_events and stop_union_events)
|
||||||
|
|
||||||
If the answers are No and Yes, we write the function call into the binary
|
If the answers are No and Yes, we write the function call into the binary
|
||||||
log as "DO spfunc(<param1value>, <param2value>, ...)"
|
log as "SELECT spfunc(<param1value>, <param2value>, ...)".
|
||||||
|
|
||||||
|
|
||||||
4. Miscellaneous issues.
|
4. Miscellaneous issues.
|
||||||
@ -1310,7 +1304,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
|||||||
char buf[256];
|
char buf[256];
|
||||||
String bufstr(buf, sizeof(buf), &my_charset_bin);
|
String bufstr(buf, sizeof(buf), &my_charset_bin);
|
||||||
bufstr.length(0);
|
bufstr.length(0);
|
||||||
bufstr.append(STRING_WITH_LEN("DO "));
|
bufstr.append(STRING_WITH_LEN("SELECT "));
|
||||||
append_identifier(thd, &bufstr, m_name.str, m_name.length);
|
append_identifier(thd, &bufstr, m_name.str, m_name.length);
|
||||||
bufstr.append('(');
|
bufstr.append('(');
|
||||||
for (uint i=0; i < argcount; i++)
|
for (uint i=0; i < argcount; i++)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user