mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
BUG#39934: Slave stops for engine that only support row-based logging
This is a post-push fix addressing review requests and problems with extra warnings. Problem 1: The sub-statement where an unsafe warning was detected was printed as part of the warning. This was ok for statements that were unsafe due to, e.g., calls to UUID(), but did not make sense for statements that were unsafe because there was more than one autoincrement column (unsafeness in this case comes from the combination of several sub-statements). Fix 1: Instead of printing the sub-statement, print an explanation of why the statement is unsafe. Problem 2: When a recursive construct (i.e., stored proceure, stored function, trigger, view, prepared statement) contained several sub-statements, and at least one of them was unsafe, there would be one unsafeness warning per sub-statement - even for safe sub-statements. Fix 2: Ensure that each type of warning is printed at most once, by remembering throughout the execution of the statement which types of warnings have been printed. mysql-test/extra/rpl_tests/create_recursive_construct.inc: - Clarified comment per review request. - Added checks for the number of warnings in each invocation. mysql-test/extra/rpl_tests/rpl_insert_delayed.test: Per review request, replaced @@session.binlog_format by @@global.binlog_format, since INSERT DELAYED reads the global variable. (In this test case, the two variables have the same value, so the change is cosmetic.) mysql-test/r/sp_trans.result: updated result file mysql-test/suite/binlog/r/binlog_statement_insert_delayed.result: updated result file mysql-test/suite/binlog/r/binlog_stm_ps.result: updated result file mysql-test/suite/binlog/r/binlog_stm_unsafe_warning.result: updated result file mysql-test/suite/binlog/r/binlog_unsafe.result: Updated result file. Note that duplicate warnings are now gone. mysql-test/suite/binlog/t/binlog_unsafe.test: - Added tests for: (1) a statement that is unsafe in many ways; (2) a statement that is unsafe in the same way several times. - Use -- style to invoke mysqltest commands. mysql-test/suite/rpl/r/rpl_stm_found_rows.result: updated result file mysql-test/suite/rpl/r/rpl_stm_loadfile.result: updated result file mysql-test/suite/rpl/t/rpl_mix_found_rows.test: Per review request, added comment explaining what the test case does (copied from rpl_stm_found_rows.test) mysql-test/suite/rpl/t/rpl_stm_found_rows.test: Clarified grammar in comment. mysql-test/suite/rpl_ndb/r/rpl_ndb_binlog_format_errors.result: Updated result file. sql/item_create.cc: Made set_stmt_unsafe take one parameter, describing the type of unsafeness. sql/sp_head.cc: Added unsafe_flags field and made it hold all the unsafe flags. sql/sp_head.h: - Removed the BINLOG_ROW_BASED_IF_MIXED flag from m_flags. Instead, we use the new unsafe_flags field to hold the unsafeness state of the sp. - Made propagate_attributes() copy all unsafe flags. sql/sql_base.cc: - Made LEX::set_stmt_unsafe() take an extra argument. - Made binlog_unsafe_warning_flags store the type of unsafeness. - Per review requests, clarified comments - Added DBUG printouts sql/sql_class.cc: - Made warnings be generated in issue_warnings() and call that from binlog_query(). Wrote issue_warnings(), which prints zero or more warnings, avoiding to print warnings more than once per statement. - Per review request, added @todo so that we remember to assert correct behavior in binlog_query. sql/sql_class.h: - Removed BINLOG_WARNING_PRINTED - Use [set|clear]_current_stmt_binlog_row_based() instead of modifying the flag directly. - added issue_unsafe_warnings() (only called from binlog_unsafe) - Per review request, improved some documentation. sql/sql_insert.cc: Added extra argument to LEX::set_stmt_unsafe() sql/sql_lex.h: - Added enum_binlog_stmt_unsafe, listing all types of unsafe statements. - Per review requests, improved many comments for member functions. - Added [get|set]_stmt_unsafe_flags(), which return/set all the unsafe flags for a statement. sql/sql_parse.cc: - Renamed binlog_warning_flags to binlog_unsafe_warning_flags. - Per review requests, improved comment. sql/sql_view.cc: Made views propagate all the new unsafe flags. sql/sql_yacc.yy: Added parameter to set_stmt_unsafe(). storage/innobase/handler/ha_innodb.cc: Per review requests, replaced DBUG_EXECUTE_IF() by DBUG_EVALUATE_IF().
This commit is contained in:
@ -15,12 +15,15 @@
|
||||
# - With SQL_LOG_BIN = 1 and binlog_format = MIXED, to verify that it
|
||||
# writes row events to the binlog.
|
||||
#
|
||||
# - If the recursive construct can be invoked so that it has no
|
||||
# side-effects but it returns a value that may be nondeterministic,
|
||||
# then it is invoked in such a way that the return value is
|
||||
# discarded, with SQL_LOG_BIN = 1 and binlog_format = STATEMENT.
|
||||
# In this case, no warning should be given and nothing should be
|
||||
# written to the binlog.
|
||||
# - In some cases, the recursive construct can be invoked so that it
|
||||
# has no side-effects but returns a value that may be
|
||||
# nondeterministic. An example is a function that returns UUID().
|
||||
# The function does not have side effects but its a return value
|
||||
# that may differ on slave. Such statements are invoked so that
|
||||
# the return value is discarded (e.g., SELECT func()), with
|
||||
# SQL_LOG_BIN = 1 and binlog_format = STATEMENT. In this case, no
|
||||
# warning should be given and nothing should be written to the
|
||||
# binlog.
|
||||
#
|
||||
# This is an auxiliary file particularly targeted to being used by the
|
||||
# test binlog_unsafe. In this context, the purpose is to check how
|
||||
@ -289,8 +292,15 @@ if (`SELECT '$CRC_RET_stmt_sidef' != ''`) {
|
||||
--eval $CRC_create
|
||||
}
|
||||
|
||||
--echo * binlog_format = STATEMENT: expect warning.
|
||||
--echo * binlog_format = STATEMENT: expect $CRC_ARG_expected_number_of_warnings warnings.
|
||||
--eval $CRC_RET_stmt_sidef
|
||||
--let $n_warnings= `SHOW COUNT(*) WARNINGS`
|
||||
if (`SELECT '$n_warnings' != '$CRC_ARG_expected_number_of_warnings'`) {
|
||||
--echo Failure! Expected $CRC_ARG_expected_number_of_warnings warnings, got $n_warnings warnings.
|
||||
SHOW WARNINGS;
|
||||
SHOW BINLOG EVENTS;
|
||||
--exit
|
||||
}
|
||||
|
||||
# These queries are run without query log, to make result file more
|
||||
# readable. Debug info is only printed if something abnormal
|
||||
@ -301,12 +311,19 @@ if (`SELECT '$CRC_RET_stmt_sidef' != ''`) {
|
||||
SET SQL_LOG_BIN = 0;
|
||||
RESET MASTER;
|
||||
--eval $CRC_RET_stmt_sidef
|
||||
--let $n_warnings= `SHOW COUNT(*) WARNINGS`
|
||||
if (`SELECT '$n_warnings' != '0'`) {
|
||||
--echo Failure! Expected 0 warnings, got $n_warnings warnings.
|
||||
SHOW WARNINGS;
|
||||
SHOW BINLOG EVENTS;
|
||||
--exit
|
||||
}
|
||||
--let $binlog_event= query_get_value(SHOW BINLOG EVENTS, Event_type, 2)
|
||||
if (`SELECT '$binlog_event' != 'No such row'`) {
|
||||
--enable_query_log
|
||||
--echo Failure! Something was written to the binlog despite SQL_LOG_BIN=0:
|
||||
SHOW BINLOG EVENTS;
|
||||
--die
|
||||
--exit
|
||||
}
|
||||
SET SQL_LOG_BIN = 1;
|
||||
|
||||
@ -314,6 +331,13 @@ if (`SELECT '$CRC_RET_stmt_sidef' != ''`) {
|
||||
SET binlog_format = MIXED;
|
||||
RESET MASTER;
|
||||
--eval $CRC_RET_stmt_sidef
|
||||
--let $n_warnings= `SHOW COUNT(*) WARNINGS`
|
||||
if (`SELECT '$n_warnings' != '0'`) {
|
||||
--echo Failure! Expected 0 warnings, got $n_warnings warnings.
|
||||
SHOW WARNINGS;
|
||||
SHOW BINLOG EVENTS;
|
||||
--exit
|
||||
}
|
||||
# The first event is format_description, the second is
|
||||
# Query_event('BEGIN'), and the third should be our Table_map.
|
||||
--let $event_type= query_get_value(SHOW BINLOG EVENTS, Event_type, 3)
|
||||
@ -328,7 +352,7 @@ if (`SELECT '$CRC_RET_stmt_sidef' != ''`) {
|
||||
# we should instead execute:
|
||||
#--enable_query_log
|
||||
#SHOW BINLOG EVENTS;
|
||||
#--die
|
||||
#--exit
|
||||
|
||||
# Here, we should really source
|
||||
# include/show_binlog_events.inc. But due to BUG#41913, that
|
||||
@ -352,6 +376,18 @@ if (`SELECT '$CRC_RET_sel_retval' != ''`) {
|
||||
--disable_result_log
|
||||
--eval $CRC_RET_sel_retval
|
||||
--enable_result_log
|
||||
|
||||
# Currently, due to a bug, we do get warnings here, so we don't
|
||||
# fail. When the bug is fixed, we should execute the following.
|
||||
|
||||
#--let $n_warnings= `SHOW COUNT(*) WARNINGS`
|
||||
#if (`SELECT '$n_warnings' != '0'`) {
|
||||
# --enable_query_log
|
||||
# --echo Failure! Expected 0 warnings, got $n_warnings warnings.
|
||||
# SHOW WARNINGS;
|
||||
# SHOW BINLOG EVENTS;
|
||||
# --exit
|
||||
#}
|
||||
}
|
||||
|
||||
#--echo debug: <<<<EXIT create_recursive_construct
|
||||
|
@ -38,14 +38,14 @@ connection master;
|
||||
truncate table t1;
|
||||
# first scenario: duplicate on first row
|
||||
insert delayed into t1 values(10, "my name");
|
||||
if (`SELECT @@session.binlog_format = 'STATEMENT'`)
|
||||
if (`SELECT @@global.binlog_format = 'STATEMENT'`)
|
||||
{
|
||||
# statement below will be converted to non-delayed INSERT and so
|
||||
# will stop at first error, guaranteeing replication.
|
||||
--error ER_DUP_ENTRY
|
||||
insert delayed into t1 values(10, "is Bond"), (20, "James Bond");
|
||||
}
|
||||
if (`SELECT @@session.binlog_format != 'STATEMENT'`)
|
||||
if (`SELECT @@global.binlog_format != 'STATEMENT'`)
|
||||
{
|
||||
insert delayed into t1 values(10, "is Bond"), (20, "James Bond");
|
||||
}
|
||||
@ -59,7 +59,7 @@ select * from t1;
|
||||
# second scenario: duplicate on second row
|
||||
connection master;
|
||||
delete from t1 where id!=10;
|
||||
if (`SELECT @@session.binlog_format = 'STATEMENT'`)
|
||||
if (`SELECT @@global.binlog_format = 'STATEMENT'`)
|
||||
{
|
||||
# statement below will be converted to non-delayed INSERT and so
|
||||
# will be binlogged with its ER_DUP_ENTRY error code, guaranteeing
|
||||
@ -67,7 +67,7 @@ if (`SELECT @@session.binlog_format = 'STATEMENT'`)
|
||||
--error ER_DUP_ENTRY
|
||||
insert delayed into t1 values(20, "is Bond"), (10, "James Bond");
|
||||
}
|
||||
if (`SELECT @@session.binlog_format != 'STATEMENT'`)
|
||||
if (`SELECT @@global.binlog_format != 'STATEMENT'`)
|
||||
{
|
||||
insert delayed into t1 values(20, "is Bond"), (10, "James Bond");
|
||||
}
|
||||
@ -90,7 +90,7 @@ connection master;
|
||||
# Bug #29571: INSERT DELAYED IGNORE written to binary log on the master but
|
||||
# on the slave
|
||||
#
|
||||
if (`SELECT @@session.binlog_format != 'ROW'`)
|
||||
if (`SELECT @@global.binlog_format != 'ROW'`)
|
||||
{
|
||||
#flush the logs before the test
|
||||
connection slave;
|
||||
@ -104,7 +104,7 @@ INSERT DELAYED IGNORE INTO t1 VALUES(1);
|
||||
INSERT DELAYED IGNORE INTO t1 VALUES(1);
|
||||
flush table t1; # to wait for INSERT DELAYED to be done
|
||||
|
||||
if (`SELECT @@session.binlog_format != 'ROW'`)
|
||||
if (`SELECT @@global.binlog_format != 'ROW'`)
|
||||
{
|
||||
#must show two INSERT DELAYED
|
||||
--replace_column 1 x 2 x 3 x 4 x 5 x
|
||||
@ -115,7 +115,7 @@ select * from t1;
|
||||
|
||||
sync_slave_with_master;
|
||||
echo On slave;
|
||||
if (`SELECT @@session.binlog_format != 'ROW'`)
|
||||
if (`SELECT @@global.binlog_format != 'ROW'`)
|
||||
{
|
||||
#must show two INSERT DELAYED
|
||||
--replace_column 1 x 2 x 3 x 4 x 5 x
|
||||
@ -129,7 +129,7 @@ select * from t1;
|
||||
connection master;
|
||||
drop table t1;
|
||||
sync_slave_with_master;
|
||||
if (`SELECT @@session.binlog_format != 'ROW'`)
|
||||
if (`SELECT @@global.binlog_format != 'ROW'`)
|
||||
{
|
||||
#flush the logs after the test
|
||||
FLUSH LOGS;
|
||||
|
@ -507,16 +507,7 @@ until table_size > max_table_size*2 end repeat;
|
||||
end|
|
||||
call bug14210_fill_table()|
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: delete from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 (a) values (repeat('a', 255))
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t3 select a from t3
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a system variable whose value may differ on slave.
|
||||
drop procedure bug14210_fill_table|
|
||||
create table t4 like t3|
|
||||
create procedure bug14210()
|
||||
|
@ -13,10 +13,10 @@ master-bin.000001 # Query # # use `test`; insert delayed into t1 values (300)
|
||||
master-bin.000001 # Query # # use `test`; FLUSH TABLES
|
||||
insert delayed into t1 values (null),(null),(null),(null);
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert delayed into t1 values (null),(null),(null),(null)
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses INSERT DELAYED. This is unsafe because the time when rows are inserted cannot be predicted.
|
||||
insert delayed into t1 values (null),(null),(400),(null);
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert delayed into t1 values (null),(null),(400),(null)
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses INSERT DELAYED. This is unsafe because the time when rows are inserted cannot be predicted.
|
||||
select * from t1;
|
||||
a
|
||||
207
|
||||
|
@ -11,7 +11,7 @@ prepare s from "insert into t1 select 100 limit ?";
|
||||
set @a=100;
|
||||
execute s using @a;
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: insert into t1 select 100 limit 100
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
show binlog events from <binlog_start>;
|
||||
Log_name Pos Event_type Server_id End_log_pos Info
|
||||
master-bin.000001 # Query # # use `test`; create table t1 (a int)
|
||||
|
@ -4,10 +4,10 @@ CREATE TABLE t1 (a int, b int, primary key (a));
|
||||
INSERT INTO t1 VALUES (1,2), (2,3);
|
||||
UPDATE t1 SET b='4' WHERE a=1 LIMIT 1;
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: UPDATE t1 SET b='4' WHERE a=1 LIMIT 1
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
UPDATE t1 SET b='5' WHERE a=2 ORDER BY a LIMIT 1;
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: UPDATE t1 SET b='5' WHERE a=2 ORDER BY a LIMIT 1
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
DROP TABLE t1;
|
||||
### NOT filtered database => assertion: binlog disabled and warnings ARE NOT shown
|
||||
SET SQL_LOG_BIN= 0;
|
||||
@ -25,6 +25,10 @@ DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1 (a int, b int, primary key (a));
|
||||
INSERT INTO t1 VALUES (1,2), (2,3);
|
||||
UPDATE t1 SET b='4' WHERE a=1 LIMIT 1;
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
UPDATE t1 SET b='5' WHERE a=2 ORDER BY a LIMIT 1;
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
DROP TABLE t1;
|
||||
DROP DATABASE b42851;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -92,105 +92,149 @@
|
||||
# related to logging format (not just 'Unsafe statement binlogged in
|
||||
# statement mode since BINLOG_FORMAT = STATEMENT').
|
||||
|
||||
source include/have_udf.inc;
|
||||
source include/have_log_bin.inc;
|
||||
source include/have_binlog_format_statement.inc;
|
||||
--source include/have_udf.inc
|
||||
--source include/have_log_bin.inc
|
||||
--source include/have_binlog_format_statement.inc
|
||||
|
||||
--echo #### Setup tables ####
|
||||
|
||||
CREATE TABLE t0 (a CHAR(40));
|
||||
CREATE TABLE t1 (a CHAR(40));
|
||||
CREATE TABLE t2 (a CHAR(40));
|
||||
CREATE TABLE t3 (a CHAR(40));
|
||||
CREATE TABLE ta1 (a CHAR(40));
|
||||
CREATE TABLE ta2 (a CHAR(40));
|
||||
CREATE TABLE ta3 (a CHAR(40));
|
||||
CREATE TABLE t0 (a CHAR(100));
|
||||
CREATE TABLE t1 (a CHAR(100));
|
||||
CREATE TABLE t2 (a CHAR(100));
|
||||
CREATE TABLE t3 (a CHAR(100));
|
||||
CREATE TABLE ta0 (a CHAR(100));
|
||||
CREATE TABLE ta1 (a CHAR(100));
|
||||
CREATE TABLE ta2 (a CHAR(100));
|
||||
CREATE TABLE ta3 (a CHAR(100));
|
||||
CREATE TABLE autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT);
|
||||
CREATE TABLE double_autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT);
|
||||
--DELIMITER |
|
||||
CREATE TRIGGER double_autoinc_trig BEFORE INSERT ON double_autoinc_table FOR EACH ROW BEGIN
|
||||
INSERT INTO autoinc_table VALUES (NULL);
|
||||
END|
|
||||
--DELIMITER ;
|
||||
CREATE TABLE data_table (a CHAR(40));
|
||||
CREATE TABLE data_table (a CHAR(100));
|
||||
INSERT INTO data_table VALUES ('foo');
|
||||
CREATE TABLE trigger_table_1 (a INT);
|
||||
CREATE TABLE trigger_table_2 (a INT);
|
||||
CREATE TABLE trigger_table_3 (a INT);
|
||||
CREATE TABLE double_autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT);
|
||||
|
||||
--DELIMITER |
|
||||
CREATE TRIGGER double_autoinc_trig
|
||||
BEFORE INSERT ON double_autoinc_table FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO autoinc_table VALUES (NULL);
|
||||
END|
|
||||
|
||||
CREATE FUNCTION multi_unsafe_func() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO t0 VALUES(CONCAT(@@hostname, @@hostname));
|
||||
INSERT INTO t0 VALUES(0);
|
||||
INSERT INTO t0 VALUES(CONCAT(UUID(), @@hostname));
|
||||
RETURN 1;
|
||||
END|
|
||||
--DELIMITER ;
|
||||
|
||||
--replace_result $UDF_EXAMPLE_LIB UDF_EXAMPLE_LIB
|
||||
eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_LIB";
|
||||
--eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_LIB"
|
||||
|
||||
# In each iteration of this loop, we select one method to make the
|
||||
# statement unsafe.
|
||||
let $unsafe_type= 0;
|
||||
while (`SELECT $unsafe_type < 7`) {
|
||||
--let $unsafe_type= 0
|
||||
while (`SELECT $unsafe_type < 9`) {
|
||||
|
||||
--echo
|
||||
|
||||
if (`SELECT $unsafe_type = 0`) {
|
||||
--echo ==== Testing UUID() unsafeness ====
|
||||
let $desc_0= unsafe UUID() function;
|
||||
let $stmt_sidef_0= INSERT INTO t0 VALUES (UUID());
|
||||
let $value_0= UUID();
|
||||
let $sel_sidef_0=;
|
||||
let $sel_retval_0= SELECT UUID();
|
||||
--let $desc_0= unsafe UUID() function
|
||||
--let $stmt_sidef_0= INSERT INTO t0 VALUES (UUID())
|
||||
--let $value_0= UUID()
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0= SELECT UUID()
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 1`) {
|
||||
--echo ==== Testing @@hostname unsafeness ====
|
||||
let $desc_0= unsafe @@hostname variable;
|
||||
let $stmt_sidef_0= INSERT INTO t0 VALUES (@@hostname);
|
||||
let $value_0= @@hostname;
|
||||
let $sel_sidef_0=;
|
||||
--let $desc_0= unsafe @@hostname variable
|
||||
--let $stmt_sidef_0= INSERT INTO t0 VALUES (@@hostname)
|
||||
--let $value_0= @@hostname
|
||||
--let $sel_sidef_0=
|
||||
# $sel_retval is going to be used in views. Views cannot execute
|
||||
# statements that refer to @@variables. Hence, we set $set_retval
|
||||
# to empty instead of SELECT @@hostname.
|
||||
let $sel_retval_0=;
|
||||
--let $sel_retval_0=
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 2`) {
|
||||
--echo ==== Testing SELECT...LIMIT unsafeness ====
|
||||
let $desc_0= unsafe SELECT...LIMIT statement;
|
||||
let $stmt_sidef_0= INSERT INTO t0 SELECT * FROM data_table LIMIT 1;
|
||||
let $value_0=;
|
||||
let $sel_sidef_0=;
|
||||
let $sel_retval_0= SELECT * FROM data_table LIMIT 1;
|
||||
--let $desc_0= unsafe SELECT...LIMIT statement
|
||||
--let $stmt_sidef_0= INSERT INTO t0 SELECT * FROM data_table LIMIT 1
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0= SELECT * FROM data_table LIMIT 1
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 3`) {
|
||||
--echo ==== Testing INSERT DELAYED unsafeness ====
|
||||
let $desc_0= unsafe INSERT DELAYED statement;
|
||||
let $stmt_sidef_0= INSERT DELAYED INTO t0 VALUES (1), (2);
|
||||
let $value_0=;
|
||||
let $sel_sidef_0=;
|
||||
let $sel_retval_0=;
|
||||
--let $desc_0= unsafe INSERT DELAYED statement
|
||||
--let $stmt_sidef_0= INSERT DELAYED INTO t0 VALUES (1), (2)
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0=
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 4`) {
|
||||
--echo ==== Testing unsafeness of insert of two autoinc values ====
|
||||
let $desc_0= unsafe update of two autoinc columns;
|
||||
let $stmt_sidef_0= INSERT INTO double_autoinc_table VALUES (NULL);
|
||||
let $value_0=;
|
||||
let $sel_sidef_0=;
|
||||
let $sel_retval_0=;
|
||||
--let $desc_0= unsafe update of two autoinc columns
|
||||
--let $stmt_sidef_0= INSERT INTO double_autoinc_table VALUES (NULL)
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0=
|
||||
# Note: we will expect 1 warning when BUG#45827 is fixed.
|
||||
--let $CRC_ARG_expected_number_of_warnings= 0
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 5`) {
|
||||
--echo ==== Testing unsafeness of UDF's ====
|
||||
let $desc_0= unsafe UDF;
|
||||
let $stmt_sidef_0= INSERT INTO t0 VALUES (myfunc_int(10));
|
||||
let $value_0= myfunc_int(10);
|
||||
let $sel_sidef_0= SELECT myfunc_int(10);
|
||||
let $sel_retval_0= ;
|
||||
--let $desc_0= unsafe UDF
|
||||
--let $stmt_sidef_0= INSERT INTO t0 VALUES (myfunc_int(10))
|
||||
--let $value_0= myfunc_int(10)
|
||||
--let $sel_sidef_0= SELECT myfunc_int(10)
|
||||
--let $sel_retval_0=
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 6`) {
|
||||
--echo ==== Testing unsafeness of access to mysql.general_log ====
|
||||
let $desc_0= unsafe use of mysql.general_log;
|
||||
let $stmt_sidef_0= INSERT INTO t0 SELECT COUNT(*) FROM mysql.general_log;
|
||||
let $value_0=;
|
||||
let $sel_sidef_0=;
|
||||
let $sel_retval_0= SELECT COUNT(*) FROM mysql.general_log;
|
||||
--let $desc_0= unsafe use of mysql.general_log
|
||||
--let $stmt_sidef_0= INSERT INTO t0 SELECT COUNT(*) FROM mysql.general_log
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0= SELECT COUNT(*) FROM mysql.general_log
|
||||
--let $CRC_ARG_expected_number_of_warnings= 1
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 7`) {
|
||||
--echo ==== Testing a statement that is unsafe in many ways ====
|
||||
--let $desc_0= statement that is unsafe in many ways
|
||||
# Concatenate three unsafe values, and then concatenate NULL to
|
||||
# that so that the result is NULL and we instead use autoinc.
|
||||
--let $stmt_sidef_0= INSERT DELAYED INTO double_autoinc_table SELECT CONCAT(UUID(), @@hostname, myfunc_int(), NULL) FROM mysql.general_log LIMIT 1
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0=
|
||||
--let $sel_retval_0=
|
||||
# Note: we will expect 7 warnings when BUG#45827 is fixed.
|
||||
--let $CRC_ARG_expected_number_of_warnings= 6
|
||||
}
|
||||
|
||||
if (`SELECT $unsafe_type = 8`) {
|
||||
--echo ==== Testing a statement that is unsafe several times ====
|
||||
--let $desc_0= statement that is unsafe several times
|
||||
--let $stmt_sidef_0= INSERT INTO ta0 VALUES (multi_unsafe_func())
|
||||
--let $value_0=
|
||||
--let $sel_sidef_0= SELECT multi_unsafe_func()
|
||||
--let $sel_retval_0=
|
||||
--let $CRC_ARG_expected_number_of_warnings= 2
|
||||
}
|
||||
|
||||
# In each iteration of the following loop, we select one way to
|
||||
@ -201,24 +245,24 @@ while (`SELECT $unsafe_type < 7`) {
|
||||
# In the last iteration, $call_type_1=7, we don't create a recursive
|
||||
# construct. Instead, we just invoke the unsafe statement directly.
|
||||
|
||||
let $call_type_1= 0;
|
||||
--let $call_type_1= 0
|
||||
while (`SELECT $call_type_1 < 8`) {
|
||||
#--echo debug: level 1, types $call_type_1 -> $unsafe_type
|
||||
let $CRC_ARG_level= 1;
|
||||
let $CRC_ARG_type= $call_type_1;
|
||||
let $CRC_ARG_stmt_sidef= $stmt_sidef_0;
|
||||
let $CRC_ARG_value= $value_0;
|
||||
let $CRC_ARG_sel_sidef= $sel_sidef_0;
|
||||
let $CRC_ARG_sel_retval= $sel_retval_0;
|
||||
let $CRC_ARG_desc= $desc_0;
|
||||
source extra/rpl_tests/create_recursive_construct.inc;
|
||||
let $stmt_sidef_1= $CRC_RET_stmt_sidef;
|
||||
let $value_1= $CRC_RET_value;
|
||||
let $sel_sidef_1= $CRC_RET_sel_sidef;
|
||||
let $sel_retval_1= $CRC_RET_sel_retval;
|
||||
let $is_toplevel_1= $CRC_RET_is_toplevel;
|
||||
let $drop_1= $CRC_RET_drop;
|
||||
let $desc_1= $CRC_RET_desc;
|
||||
--let $CRC_ARG_level= 1
|
||||
--let $CRC_ARG_type= $call_type_1
|
||||
--let $CRC_ARG_stmt_sidef= $stmt_sidef_0
|
||||
--let $CRC_ARG_value= $value_0
|
||||
--let $CRC_ARG_sel_sidef= $sel_sidef_0
|
||||
--let $CRC_ARG_sel_retval= $sel_retval_0
|
||||
--let $CRC_ARG_desc= $desc_0
|
||||
--source extra/rpl_tests/create_recursive_construct.inc
|
||||
--let $stmt_sidef_1= $CRC_RET_stmt_sidef
|
||||
--let $value_1= $CRC_RET_value
|
||||
--let $sel_sidef_1= $CRC_RET_sel_sidef
|
||||
--let $sel_retval_1= $CRC_RET_sel_retval
|
||||
--let $is_toplevel_1= $CRC_RET_is_toplevel
|
||||
--let $drop_1= $CRC_RET_drop
|
||||
--let $desc_1= $CRC_RET_desc
|
||||
|
||||
# Some statements must be top-level statements, i.e., cannot be
|
||||
# called as a sub-statement of any recursive construct. (One
|
||||
@ -232,24 +276,24 @@ while (`SELECT $unsafe_type < 7`) {
|
||||
# the previous recursive construct in another recursive
|
||||
# construct.
|
||||
|
||||
let $call_type_2= 0;
|
||||
--let $call_type_2= 0
|
||||
while (`SELECT $call_type_2 < 7`) {
|
||||
#--echo debug: level 2, types $call_type_2 -> $call_type_1 -> $unsafe_type
|
||||
let $CRC_ARG_level= 2;
|
||||
let $CRC_ARG_type= $call_type_2;
|
||||
let $CRC_ARG_stmt_sidef= $stmt_sidef_1;
|
||||
let $CRC_ARG_value= $value_1;
|
||||
let $CRC_ARG_sel_sidef= $sel_sidef_1;
|
||||
let $CRC_ARG_sel_retval= $sel_retval_1;
|
||||
let $CRC_ARG_desc= $desc_1;
|
||||
source extra/rpl_tests/create_recursive_construct.inc;
|
||||
let $stmt_sidef_2= $CRC_RET_stmt_sidef;
|
||||
let $value_2= $CRC_RET_value;
|
||||
let $sel_sidef_2= $CRC_RET_sel_sidef;
|
||||
let $sel_retval_2= $CRC_RET_sel_retval;
|
||||
let $is_toplevel_2= $CRC_RET_is_toplevel;
|
||||
let $drop_2= $CRC_RET_drop;
|
||||
let $desc_2= $CRC_RET_desc;
|
||||
--let $CRC_ARG_level= 2
|
||||
--let $CRC_ARG_type= $call_type_2
|
||||
--let $CRC_ARG_stmt_sidef= $stmt_sidef_1
|
||||
--let $CRC_ARG_value= $value_1
|
||||
--let $CRC_ARG_sel_sidef= $sel_sidef_1
|
||||
--let $CRC_ARG_sel_retval= $sel_retval_1
|
||||
--let $CRC_ARG_desc= $desc_1
|
||||
--source extra/rpl_tests/create_recursive_construct.inc
|
||||
--let $stmt_sidef_2= $CRC_RET_stmt_sidef
|
||||
--let $value_2= $CRC_RET_value
|
||||
--let $sel_sidef_2= $CRC_RET_sel_sidef
|
||||
--let $sel_retval_2= $CRC_RET_sel_retval
|
||||
--let $is_toplevel_2= $CRC_RET_is_toplevel
|
||||
--let $drop_2= $CRC_RET_drop
|
||||
--let $desc_2= $CRC_RET_desc
|
||||
|
||||
if (!$is_toplevel_2) {
|
||||
|
||||
@ -261,51 +305,52 @@ while (`SELECT $unsafe_type < 7`) {
|
||||
# the previous recursive construct in another recursive
|
||||
# construct.
|
||||
|
||||
let $call_type_3= 0;
|
||||
--let $call_type_3= 0
|
||||
while (`SELECT $call_type_3 < 7`) {
|
||||
#--echo debug: level 3, types $call_type_2 -> $call_type_2 -> $call_type_1 -> $unsafe_type
|
||||
let $CRC_ARG_level= 3;
|
||||
let $CRC_ARG_type= $call_type_3;
|
||||
let $CRC_ARG_stmt_sidef= $stmt_sidef_2;
|
||||
let $CRC_ARG_value= $value_2;
|
||||
let $CRC_ARG_sel_sidef= $sel_sidef_2;
|
||||
let $CRC_ARG_sel_retval= $sel_retval_2;
|
||||
let $CRC_ARG_desc= $desc_2;
|
||||
source extra/rpl_tests/create_recursive_construct.inc;
|
||||
--let $CRC_ARG_level= 3
|
||||
--let $CRC_ARG_type= $call_type_3
|
||||
--let $CRC_ARG_stmt_sidef= $stmt_sidef_2
|
||||
--let $CRC_ARG_value= $value_2
|
||||
--let $CRC_ARG_sel_sidef= $sel_sidef_2
|
||||
--let $CRC_ARG_sel_retval= $sel_retval_2
|
||||
--let $CRC_ARG_desc= $desc_2
|
||||
--source extra/rpl_tests/create_recursive_construct.inc
|
||||
|
||||
# Drop created object.
|
||||
if (`SELECT '$drop_3' != ''`) {
|
||||
eval $drop_3;
|
||||
--eval $drop_3
|
||||
}
|
||||
inc $call_type_3;
|
||||
--inc $call_type_3
|
||||
} # while (call_type_3)
|
||||
} # if (0)
|
||||
} # if (!is_toplevel_2)
|
||||
|
||||
# Drop created object.
|
||||
if (`SELECT '$drop_2' != ''`) {
|
||||
eval $drop_2;
|
||||
--eval $drop_2
|
||||
}
|
||||
inc $call_type_2;
|
||||
--inc $call_type_2
|
||||
} # while (call_type_2)
|
||||
} # if (!is_toplevel_1)
|
||||
|
||||
# Drop created object.
|
||||
if (`SELECT '$drop_1' != ''`) {
|
||||
eval $drop_1;
|
||||
--eval $drop_1
|
||||
}
|
||||
inc $call_type_1;
|
||||
--inc $call_type_1
|
||||
} # while (call_type_1)
|
||||
|
||||
inc $unsafe_type;
|
||||
--inc $unsafe_type
|
||||
} # while (unsafe_type)
|
||||
|
||||
DROP TRIGGER double_autoinc_trig;
|
||||
DROP TABLE t0, t1, t2, t3, ta1, ta2, ta3,
|
||||
DROP TABLE t0, t1, t2, t3, ta0, ta1, ta2, ta3,
|
||||
autoinc_table, double_autoinc_table,
|
||||
data_table,
|
||||
trigger_table_1, trigger_table_2, trigger_table_3;
|
||||
DROP FUNCTION myfunc_int;
|
||||
DROP FUNCTION multi_unsafe_func;
|
||||
|
||||
|
||||
--echo ==== Special system variables that should *not* be unsafe ====
|
||||
|
@ -52,8 +52,8 @@ a
|
||||
a
|
||||
7
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: INSERT INTO logtbl VALUES( NAME_CONST('sect',2), NAME_CONST('test',1), NAME_CONST('cnt',3))
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: INSERT INTO logtbl VALUES( NAME_CONST('sect',2), NAME_CONST('test',1)+1, NAME_CONST('cnt',183))
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a system function whose value may differ on slave.
|
||||
CREATE PROCEDURE just_log(sect INT, test INT, found_rows INT) BEGIN
|
||||
INSERT INTO logtbl VALUES (sect,test,found_rows);
|
||||
END $$
|
||||
|
@ -10,7 +10,7 @@ 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:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=1
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a system function whose value may differ on slave.
|
||||
create procedure test.p1()
|
||||
begin
|
||||
INSERT INTO test.t1 VALUES(2,'test');
|
||||
@ -18,8 +18,7 @@ UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=2;
|
||||
end|
|
||||
CALL test.p1();
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: INSERT INTO test.t1 VALUES(2,'test')
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: UPDATE test.t1 SET blob_column=LOAD_FILE('../../std_data/words2.dat') WHERE a=2
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a system function whose value may differ on slave.
|
||||
SELECT * FROM test.t1 ORDER BY blob_column;
|
||||
a blob_column
|
||||
1 abase
|
||||
|
@ -1,6 +1,14 @@
|
||||
source include/master-slave.inc;
|
||||
source include/have_binlog_format_mixed.inc;
|
||||
|
||||
# It is not possible to replicate FOUND_ROWS() using statement-based
|
||||
# replication, but there is a workaround that stores the result of
|
||||
# FOUND_ROWS() into a user variable and then replicates this instead.
|
||||
#
|
||||
# The purpose of this test case is to test that the workaround works
|
||||
# properly even when inside stored programs (i.e., stored routines and
|
||||
# triggers).
|
||||
|
||||
--echo ==== Initialize ====
|
||||
|
||||
--echo **** On Master ****
|
||||
|
@ -4,10 +4,10 @@ source include/master-slave.inc;
|
||||
# It is not possible to replicate FOUND_ROWS() using statement-based
|
||||
# replication, but there is a workaround that stores the result of
|
||||
# FOUND_ROWS() into a user variable and then replicates this instead.
|
||||
|
||||
# The purpose of this test case is to test that the workaround
|
||||
# function properly even when inside stored programs (i.e., stored
|
||||
# routines and triggers).
|
||||
#
|
||||
# The purpose of this test case is to test that the workaround works
|
||||
# properly even when inside stored programs (i.e., stored routines and
|
||||
# triggers).
|
||||
|
||||
--echo ==== Initialize ====
|
||||
|
||||
|
@ -64,7 +64,7 @@ SET @@session.binlog_format = MIXED;
|
||||
* Unsafe statement and stmt-only engine
|
||||
INSERT INTO t_stmt VALUES (UUID());
|
||||
Warnings:
|
||||
Note 1639 Unsafe statement binlogged as statement since storage engine is limited to statement-logging. Statement: INSERT INTO t_stmt VALUES (UUID())
|
||||
Note 1639 Unsafe statement binlogged as statement since storage engine is limited to statement-logging. Reason: Statement uses a system function whose value may differ on slave.
|
||||
---- binlog_format=statement ----
|
||||
[on slave]
|
||||
include/stop_slave.inc
|
||||
@ -93,7 +93,7 @@ ERROR HY000: Cannot execute row injection: binlogging impossible since BINLOG_FO
|
||||
* Unsafe statement and binlog_format=statement
|
||||
INSERT INTO t VALUES (UUID());
|
||||
Warnings:
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Statement: INSERT INTO t VALUES (UUID())
|
||||
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason: Statement uses a system function whose value may differ on slave.
|
||||
---- master: binlog_format=mixed, slave: binlog_format=statement ----
|
||||
SET @@global.binlog_format = MIXED;
|
||||
SET @@session.binlog_format = MIXED;
|
||||
|
@ -2379,7 +2379,7 @@ Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
|
||||
if (item_list != NULL)
|
||||
arg_count= item_list->elements;
|
||||
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UDF);
|
||||
|
||||
DBUG_ASSERT( (udf->type == UDFTYPE_FUNCTION)
|
||||
|| (udf->type == UDFTYPE_AGGREGATE));
|
||||
@ -3365,7 +3365,7 @@ Item*
|
||||
Create_func_found_rows::create(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Create_func_found_rows::create");
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
thd->lex->safe_to_cache_query= 0;
|
||||
DBUG_RETURN(new (thd->mem_root) Item_func_found_rows());
|
||||
}
|
||||
@ -3794,7 +3794,7 @@ Item*
|
||||
Create_func_load_file::create(THD *thd, Item *arg1)
|
||||
{
|
||||
DBUG_ENTER("Create_func_load_file::create");
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
|
||||
DBUG_RETURN(new (thd->mem_root) Item_load_file(arg1));
|
||||
}
|
||||
@ -4264,7 +4264,7 @@ Item*
|
||||
Create_func_row_count::create(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Create_func_row_count::create");
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
thd->lex->safe_to_cache_query= 0;
|
||||
DBUG_RETURN(new (thd->mem_root) Item_func_row_count());
|
||||
}
|
||||
@ -4574,7 +4574,7 @@ Item*
|
||||
Create_func_uuid::create(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Create_func_uuid::create");
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
thd->lex->safe_to_cache_query= 0;
|
||||
DBUG_RETURN(new (thd->mem_root) Item_func_uuid());
|
||||
}
|
||||
@ -4586,7 +4586,7 @@ Item*
|
||||
Create_func_uuid_short::create(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Create_func_uuid_short::create");
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
thd->lex->safe_to_cache_query= 0;
|
||||
DBUG_RETURN(new (thd->mem_root) Item_func_uuid_short());
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ sp_head::operator delete(void *ptr, size_t size) throw()
|
||||
|
||||
sp_head::sp_head()
|
||||
:Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
|
||||
m_flags(0), m_recursion_level(0), m_next_cached_sp(0),
|
||||
m_flags(0), unsafe_flags(0), m_recursion_level(0), m_next_cached_sp(0),
|
||||
m_cont_level(0)
|
||||
{
|
||||
const LEX_STRING str_reset= { NULL, 0 };
|
||||
@ -2104,13 +2104,10 @@ sp_head::restore_lex(THD *thd)
|
||||
|
||||
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
||||
|
||||
/*
|
||||
If this substatement needs row-based, the entire routine does too (we
|
||||
cannot switch from statement-based to row-based only for this
|
||||
substatement).
|
||||
*/
|
||||
if (sublex->is_stmt_unsafe())
|
||||
m_flags|= BINLOG_ROW_BASED_IF_MIXED;
|
||||
/* If this substatement is unsafe, the entire routine is too. */
|
||||
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags: 0x%x",
|
||||
thd->lex->get_stmt_unsafe_flags()));
|
||||
unsafe_flags|= sublex->get_stmt_unsafe_flags();
|
||||
|
||||
/*
|
||||
Add routines which are used by statement to respective set for
|
||||
|
@ -165,9 +165,8 @@ public:
|
||||
HAS_COMMIT_OR_ROLLBACK= 128,
|
||||
LOG_SLOW_STATEMENTS= 256, // Used by events
|
||||
LOG_GENERAL_LOG= 512, // Used by events
|
||||
BINLOG_ROW_BASED_IF_MIXED= 1024,
|
||||
HAS_SQLCOM_RESET= 2048,
|
||||
HAS_SQLCOM_FLUSH= 4096
|
||||
HAS_SQLCOM_RESET= 1024,
|
||||
HAS_SQLCOM_FLUSH= 2048
|
||||
};
|
||||
|
||||
/** TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
|
||||
@ -198,6 +197,11 @@ public:
|
||||
|
||||
private:
|
||||
Stored_program_creation_ctx *m_creation_ctx;
|
||||
/**
|
||||
Boolean combination of (1<<flag), where flag is a member of
|
||||
LEX::enum_binlog_stmt_unsafe.
|
||||
*/
|
||||
uint32 unsafe_flags;
|
||||
|
||||
public:
|
||||
inline Stored_program_creation_ctx *get_creation_ctx()
|
||||
@ -453,9 +457,8 @@ public:
|
||||
#endif
|
||||
|
||||
/*
|
||||
This method is intended for attributes of a routine which need
|
||||
to propagate upwards to the LEX of the caller (when a property of a
|
||||
sp_head needs to "taint" the caller).
|
||||
This method is intended for attributes of a routine which need to
|
||||
propagate upwards to the LEX of the caller.
|
||||
*/
|
||||
void propagate_attributes(LEX *lex)
|
||||
{
|
||||
@ -466,8 +469,11 @@ public:
|
||||
routine, as in statement-based the top-statement may be binlogged and
|
||||
the substatements not).
|
||||
*/
|
||||
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
|
||||
lex->set_stmt_unsafe();
|
||||
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
|
||||
lex->get_stmt_unsafe_flags()));
|
||||
DBUG_PRINT("info", ("sp_head(0x%p=%s)->unsafe_flags: 0x%x",
|
||||
this, name(), unsafe_flags));
|
||||
lex->set_stmt_unsafe_flags(unsafe_flags);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -5152,12 +5152,17 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
|
||||
int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
{
|
||||
DBUG_ENTER("THD::decide_logging_format");
|
||||
DBUG_PRINT("info", ("query: %s", query));
|
||||
DBUG_PRINT("info", ("variables.binlog_format: %ld",
|
||||
variables.binlog_format));
|
||||
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
|
||||
lex->get_stmt_unsafe_flags()));
|
||||
if (mysql_bin_log.is_open() && (options & OPTION_BIN_LOG))
|
||||
{
|
||||
/*
|
||||
Compute the starting vectors for the computations by creating a
|
||||
set with all the capabilities bits set and one with no
|
||||
capabilities bits set.
|
||||
Compute one bit field with the union of all the engine
|
||||
capabilities, and one with the intersection of all the engine
|
||||
capabilities.
|
||||
*/
|
||||
handler::Table_flags flags_some_set= 0;
|
||||
handler::Table_flags flags_all_set=
|
||||
@ -5180,15 +5185,14 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
|
||||
/*
|
||||
Get the capabilities vector for all involved storage engines and
|
||||
mask out the flags for the binary log. (Currently, the binlog
|
||||
flags only include the capabilities of the storage engines.)
|
||||
mask out the flags for the binary log.
|
||||
*/
|
||||
for (TABLE_LIST *table= tables; table; table= table->next_global)
|
||||
{
|
||||
if (table->placeholder())
|
||||
continue;
|
||||
if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
|
||||
lex->set_stmt_unsafe();
|
||||
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
|
||||
if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
ulonglong const flags= table->table->file->ha_table_flags();
|
||||
@ -5210,12 +5214,11 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
DBUG_PRINT("info", ("flags_some_set: %s%s",
|
||||
FLAGSTR(flags_some_set, HA_BINLOG_STMT_CAPABLE),
|
||||
FLAGSTR(flags_some_set, HA_BINLOG_ROW_CAPABLE)));
|
||||
DBUG_PRINT("info", ("variables.binlog_format: %ld",
|
||||
variables.binlog_format));
|
||||
DBUG_PRINT("info", ("multi_engine: %s",
|
||||
multi_engine ? "TRUE" : "FALSE"));
|
||||
|
||||
int error= 0;
|
||||
int unsafe_flags;
|
||||
|
||||
/*
|
||||
If more than one engine is involved in the statement and at
|
||||
@ -5258,14 +5261,20 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
*/
|
||||
my_error((error= ER_BINLOG_ROW_MODE_AND_STMT_ENGINE), MYF(0));
|
||||
}
|
||||
else if (lex->is_stmt_unsafe())
|
||||
else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
|
||||
{
|
||||
/*
|
||||
3. Warning: Unsafe statement binlogged as statement since
|
||||
storage engine is limited to statement-logging.
|
||||
*/
|
||||
binlog_warning_flags|=
|
||||
(1 << BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_ENGINE);
|
||||
binlog_unsafe_warning_flags|=
|
||||
(1 << BINLOG_STMT_WARNING_UNSAFE_AND_STMT_ENGINE) |
|
||||
(unsafe_flags << BINLOG_STMT_WARNING_COUNT);
|
||||
DBUG_PRINT("info", ("Scheduling warning to be issued by "
|
||||
"binlog_query: %s",
|
||||
ER(ER_BINLOG_UNSAFE_AND_STMT_ENGINE)));
|
||||
DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
|
||||
binlog_unsafe_warning_flags));
|
||||
}
|
||||
/* log in statement format! */
|
||||
}
|
||||
@ -5291,14 +5300,20 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
*/
|
||||
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
|
||||
}
|
||||
else if (lex->is_stmt_unsafe())
|
||||
else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
|
||||
{
|
||||
/*
|
||||
7. Warning: Unsafe statement logged as statement due to
|
||||
binlog_format = STATEMENT
|
||||
*/
|
||||
binlog_warning_flags|=
|
||||
(1 << BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_MODE);
|
||||
binlog_unsafe_warning_flags|=
|
||||
(1 << BINLOG_STMT_WARNING_UNSAFE_AND_STMT_MODE) |
|
||||
(unsafe_flags << BINLOG_STMT_WARNING_COUNT);
|
||||
DBUG_PRINT("info", ("Scheduling warning to be issued by "
|
||||
"binlog_query: '%s'",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT)));
|
||||
DBUG_PRINT("info", ("binlog_stmt_flags: 0x%x",
|
||||
binlog_unsafe_warning_flags));
|
||||
}
|
||||
/* log in statement format! */
|
||||
}
|
||||
@ -5315,9 +5330,21 @@ int THD::decide_logging_format(TABLE_LIST *tables)
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
if (error) {
|
||||
DBUG_PRINT("info", ("decision: no logging since an error was generated"));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
DBUG_PRINT("info", ("decision: logging in %s format",
|
||||
is_current_stmt_binlog_format_row() ?
|
||||
"ROW" : "STATEMENT"));
|
||||
}
|
||||
#ifndef DBUG_OFF
|
||||
else
|
||||
DBUG_PRINT("info", ("decision: no logging since "
|
||||
"mysql_bin_log.is_open() = %d "
|
||||
"and (options & OPTION_BIN_LOG) = 0x%llx",
|
||||
mysql_bin_log.is_open(), (options & OPTION_BIN_LOG)));
|
||||
#endif
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -5401,7 +5428,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
|
||||
if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
|
||||
has_two_write_locked_tables_with_auto_increment(tables))
|
||||
{
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_TWO_AUTOINC_COLUMNS);
|
||||
thd->set_current_stmt_binlog_row_based_if_mixed();
|
||||
}
|
||||
}
|
||||
|
123
sql/sql_class.cc
123
sql/sql_class.cc
@ -39,6 +39,7 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <mysys_err.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "sp_rcontext.h"
|
||||
#include "sp_cache.h"
|
||||
@ -540,7 +541,7 @@ THD::THD()
|
||||
lock_id(&main_lock_id),
|
||||
user_time(0), in_sub_stmt(0),
|
||||
sql_log_bin_toplevel(false),
|
||||
binlog_warning_flags(0UL), binlog_table_maps(0),
|
||||
binlog_unsafe_warning_flags(0), binlog_table_maps(0),
|
||||
table_map_for_update(0),
|
||||
arg_of_last_insert_id_function(FALSE),
|
||||
first_successful_insert_id_in_prev_stmt(0),
|
||||
@ -3633,6 +3634,93 @@ show_query_type(THD::enum_binlog_query_type qtype)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
Auxiliary method used by @c binlog_query() to raise warnings.
|
||||
|
||||
@param err An ER_BINLOG_UNSAFE_* constant; the warning to print.
|
||||
*/
|
||||
void THD::issue_unsafe_warnings()
|
||||
{
|
||||
DBUG_ENTER("issue_unsafe_warnings");
|
||||
/*
|
||||
Ensure that binlog_unsafe_warning_flags is big enough to hold all
|
||||
bits. This is actually a constant expression.
|
||||
*/
|
||||
DBUG_ASSERT(BINLOG_STMT_WARNING_COUNT + 2 * LEX::BINLOG_STMT_UNSAFE_COUNT <=
|
||||
sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
|
||||
|
||||
/**
|
||||
@note The order of the elements of this array must correspond to
|
||||
the order of elements in enum_binlog_stmt_unsafe.
|
||||
*/
|
||||
static const char *explanations[LEX::BINLOG_STMT_UNSAFE_COUNT] =
|
||||
{
|
||||
"Statement uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted.",
|
||||
"Statement uses INSERT DELAYED. This is unsafe because the time when rows are inserted cannot be predicted.",
|
||||
"Statement uses the general_log or slow_log table. This is unsafe because system tables may differ on slave.",
|
||||
"Statement updates two AUTO_INCREMENT columns. This is unsafe because the generated value cannot be predicted by slave.",
|
||||
"Statement uses a UDF. It cannot be determined if the UDF will return the same value on slave.",
|
||||
"Statement uses a system variable whose value may differ on slave.",
|
||||
"Statement uses a system function whose value may differ on slave."
|
||||
};
|
||||
uint32 flags= binlog_unsafe_warning_flags;
|
||||
/* No warnings (yet) for this statement. */
|
||||
if (flags == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
/* Get the types of unsafeness that affect the current statement. */
|
||||
uint32 unsafe_type_flags= flags >> BINLOG_STMT_WARNING_COUNT;
|
||||
DBUG_ASSERT((unsafe_type_flags & LEX::BINLOG_STMT_UNSAFE_ALL_FLAGS) != 0);
|
||||
/*
|
||||
Clear (1) bits above BINLOG_STMT_UNSAFE_COUNT; (2) bits for
|
||||
warnings that have been printed already.
|
||||
*/
|
||||
unsafe_type_flags &= (LEX::BINLOG_STMT_UNSAFE_ALL_FLAGS ^
|
||||
(unsafe_type_flags >> LEX::BINLOG_STMT_UNSAFE_COUNT));
|
||||
/* If all warnings have been printed already, return. */
|
||||
if (unsafe_type_flags == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
/* Figure out which error code to issue. */
|
||||
int err;
|
||||
if (binlog_unsafe_warning_flags &
|
||||
(1 << BINLOG_STMT_WARNING_UNSAFE_AND_STMT_ENGINE))
|
||||
err= ER_BINLOG_UNSAFE_AND_STMT_ENGINE;
|
||||
else {
|
||||
DBUG_ASSERT(binlog_unsafe_warning_flags &
|
||||
(1 << BINLOG_STMT_WARNING_UNSAFE_AND_STMT_MODE));
|
||||
err= ER_BINLOG_UNSAFE_STATEMENT;
|
||||
}
|
||||
|
||||
DBUG_PRINT("info", ("flags: 0x%x err: %d", unsafe_type_flags, err));
|
||||
|
||||
/*
|
||||
For each unsafe_type, check if the statement is unsafe in this way
|
||||
and issue a warning.
|
||||
*/
|
||||
for (int unsafe_type=0;
|
||||
unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
|
||||
unsafe_type++)
|
||||
{
|
||||
if ((unsafe_type_flags & (1 << unsafe_type)) != 0)
|
||||
{
|
||||
push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE, err,
|
||||
"%s Reason: %s",
|
||||
ER(err), explanations[unsafe_type]);
|
||||
sql_print_warning("%s Reason: %s Statement: %s",
|
||||
ER(err), explanations[unsafe_type], query);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Mark these unsafe types as already printed, to avoid printing
|
||||
warnings for them again.
|
||||
*/
|
||||
binlog_unsafe_warning_flags|= unsafe_type_flags <<
|
||||
(BINLOG_STMT_WARNING_COUNT + LEX::BINLOG_STMT_UNSAFE_COUNT);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Log the current query.
|
||||
|
||||
@ -3688,34 +3776,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
|
||||
know for sure if the statement will be logged.
|
||||
*/
|
||||
if (sql_log_bin_toplevel)
|
||||
{
|
||||
if (binlog_warning_flags &
|
||||
(1 << BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_ENGINE))
|
||||
{
|
||||
push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_BINLOG_UNSAFE_AND_STMT_ENGINE,
|
||||
"%s Statement: %.*s",
|
||||
ER(ER_BINLOG_UNSAFE_AND_STMT_ENGINE),
|
||||
MYSQL_ERRMSG_SIZE, query_arg);
|
||||
sql_print_warning("%s Statement: %.*s",
|
||||
ER(ER_BINLOG_UNSAFE_AND_STMT_ENGINE),
|
||||
MYSQL_ERRMSG_SIZE, query_arg);
|
||||
binlog_warning_flags|= 1 << BINLOG_WARNING_FLAG_PRINTED;
|
||||
}
|
||||
else if (binlog_warning_flags &
|
||||
(1 << BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_MODE))
|
||||
{
|
||||
push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_BINLOG_UNSAFE_STATEMENT,
|
||||
"%s Statement: %.*s",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT),
|
||||
MYSQL_ERRMSG_SIZE, query_arg);
|
||||
sql_print_warning("%s Statement: %.*s",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT),
|
||||
MYSQL_ERRMSG_SIZE, query_arg);
|
||||
binlog_warning_flags|= 1 << BINLOG_WARNING_FLAG_PRINTED;
|
||||
}
|
||||
}
|
||||
issue_unsafe_warnings();
|
||||
|
||||
switch (qtype) {
|
||||
/*
|
||||
@ -3738,6 +3799,10 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
|
||||
format; it cannot be logged in row format. This is typically
|
||||
used by DDL statements. It is an error to use this query type
|
||||
if current_stmt_binlog_row_based is set.
|
||||
|
||||
@todo Currently there are places that call this method with
|
||||
STMT_QUERY_TYPE and current_stmt_binlog_row_based. Fix those
|
||||
places and add assert to ensure correct behavior. /Sven
|
||||
*/
|
||||
case THD::STMT_QUERY_TYPE:
|
||||
/*
|
||||
|
100
sql/sql_class.h
100
sql/sql_class.h
@ -1422,38 +1422,79 @@ public:
|
||||
int binlog_flush_pending_rows_event(bool stmt_end);
|
||||
int binlog_remove_pending_rows_event(bool clear_maps);
|
||||
|
||||
int is_current_stmt_binlog_format_row() {
|
||||
/**
|
||||
Determine the binlog format of the current statement.
|
||||
|
||||
@retval 0 if the current statement will be logged in statement
|
||||
format.
|
||||
@retval nonzero if the current statement will be logged in row
|
||||
format.
|
||||
*/
|
||||
int is_current_stmt_binlog_format_row() const {
|
||||
DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
|
||||
current_stmt_binlog_format == BINLOG_FORMAT_ROW);
|
||||
return current_stmt_binlog_format == BINLOG_FORMAT_ROW;
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
Tells if current statement should binlog row-based(1) or stmt-based(0)
|
||||
/**
|
||||
Indicates the format in which the current statement will be
|
||||
logged. This can only be set from @c decide_logging_format().
|
||||
*/
|
||||
enum_binlog_format current_stmt_binlog_format;
|
||||
|
||||
enum enum_binlog_warning_flag {
|
||||
/* ER_BINLOG_UNSAFE_AND_STMT_ENGINE affects current stmt */
|
||||
BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_ENGINE = 0,
|
||||
/* ER_BINLOG_UNSAFE_AND_STMT_MODE affects current stmt */
|
||||
BINLOG_WARNING_FLAG_UNSAFE_AND_STMT_MODE,
|
||||
/* One of the warnings has already been printed */
|
||||
BINLOG_WARNING_FLAG_PRINTED,
|
||||
/* number of elements of this enum; insert new members above */
|
||||
BINLOG_WARNING_FLAG_COUNT
|
||||
};
|
||||
/**
|
||||
Flags holding the status of binlog-related warnings for the
|
||||
current statement. This is a binary combination of (1<<flag),
|
||||
where flag is a member of @c enum_binlog_warning_flag.
|
||||
|
||||
The warnings are determined in @c THD::decide_logging_format, but
|
||||
issued only later, after the statement has been written to the
|
||||
binlog. Hence it must be stored in the @c THD object.
|
||||
Enumeration listing binlog-related warnings that a statement can
|
||||
cause.
|
||||
*/
|
||||
uint32 binlog_warning_flags;
|
||||
enum enum_binlog_stmt_warning {
|
||||
|
||||
/* ER_BINLOG_UNSAFE_AND_STMT_ENGINE affects current stmt */
|
||||
BINLOG_STMT_WARNING_UNSAFE_AND_STMT_ENGINE= 0,
|
||||
|
||||
/* ER_BINLOG_UNSAFE_STATEMENT affects current stmt */
|
||||
BINLOG_STMT_WARNING_UNSAFE_AND_STMT_MODE,
|
||||
|
||||
/** The last element of this enumeration type. */
|
||||
BINLOG_STMT_WARNING_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
Bit field for the state of binlog warnings.
|
||||
|
||||
There are three groups of bits:
|
||||
|
||||
- The low BINLOG_STMT_WARNING_COUNT bits indicate the type of
|
||||
warning that the current (top-level) statement will issue. At
|
||||
most one of these bits should be set (this is ensured by the
|
||||
logic in decide_logging_format).
|
||||
|
||||
- The following Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types
|
||||
of unsafeness that the current statement has.
|
||||
|
||||
- The following Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types
|
||||
of unsafeness that the current statement has issued warnings
|
||||
for.
|
||||
|
||||
Hence, this variable must be big enough to hold
|
||||
BINLOG_STMT_WARNING_COUNT + 2 * Lex::BINLOG_STMT_UNSAFE_COUNT
|
||||
bits. This is asserted in @c issue_unsafe_warnings().
|
||||
|
||||
The first and second groups of bits are set by @c
|
||||
decide_logging_format() when it detects that a warning should be
|
||||
issued. The third group of bits is set from @c binlog_query()
|
||||
when a warning is issued. All bits are cleared at the end of the
|
||||
top-level statement.
|
||||
|
||||
This must be a member of THD and not of LEX, because warnings are
|
||||
detected and issued in different places (@c
|
||||
decide_logging_format() and @c binlog_query(), respectively).
|
||||
Between these calls, the THD->lex object may change; e.g., if a
|
||||
stored routine is invoked. Only THD persists between the calls.
|
||||
*/
|
||||
uint32 binlog_unsafe_warning_flags;
|
||||
|
||||
void issue_unsafe_warnings();
|
||||
|
||||
/*
|
||||
Number of outstanding table maps, i.e., table maps in the
|
||||
@ -2138,6 +2179,14 @@ public:
|
||||
inline void set_current_stmt_binlog_row_based_if_mixed()
|
||||
{
|
||||
DBUG_ENTER("set_current_stmt_binlog_row_based_if_mixed");
|
||||
/*
|
||||
This should only be called from decide_logging_format.
|
||||
|
||||
@todo Once we have ensured this, uncomment the following
|
||||
statement, remove the big comment below that, and remove the
|
||||
in_sub_stmt==0 condition from the following 'if'.
|
||||
*/
|
||||
/* DBUG_ASSERT(in_sub_stmt == 0); */
|
||||
/*
|
||||
If in a stored/function trigger, the caller should already have done the
|
||||
change. We test in_sub_stmt to prevent introducing bugs where people
|
||||
@ -2149,7 +2198,7 @@ public:
|
||||
*/
|
||||
if ((variables.binlog_format == BINLOG_FORMAT_MIXED) &&
|
||||
(in_sub_stmt == 0))
|
||||
current_stmt_binlog_format= BINLOG_FORMAT_ROW;
|
||||
set_current_stmt_binlog_row_based();
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
@ -2189,9 +2238,10 @@ public:
|
||||
show_system_thread(system_thread)));
|
||||
if ((temporary_tables == NULL) && (in_sub_stmt == 0))
|
||||
{
|
||||
current_stmt_binlog_format=
|
||||
(variables.binlog_format == BINLOG_FORMAT_ROW) ?
|
||||
BINLOG_FORMAT_ROW : BINLOG_FORMAT_STMT;
|
||||
if (variables.binlog_format == BINLOG_FORMAT_ROW)
|
||||
set_current_stmt_binlog_row_based();
|
||||
else
|
||||
clear_current_stmt_binlog_row_based();
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
@ -1745,7 +1745,7 @@ public:
|
||||
decide_logging_format is made. We should probably call
|
||||
thd->decide_logging_format() directly instead. /Sven
|
||||
*/
|
||||
thd.lex->set_stmt_unsafe();
|
||||
thd.lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
|
||||
thd.set_current_stmt_binlog_row_based_if_mixed();
|
||||
|
||||
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
|
||||
|
184
sql/sql_lex.h
184
sql/sql_lex.h
@ -1043,49 +1043,143 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Has the parser/scanner detected that this statement is unsafe?
|
||||
|
||||
@retval 0 if the statement is not marked as unsafe
|
||||
@retval nonzero if the statement is marked as unsafe
|
||||
/**
|
||||
Enumeration listing of all types of unsafe statement.
|
||||
|
||||
@note The order of elements of this enumeration type must
|
||||
correspond to the order of the elements of the @c explanations
|
||||
array defined in the body of @c THD::issue_unsafe_warnings.
|
||||
*/
|
||||
enum enum_binlog_stmt_unsafe {
|
||||
/**
|
||||
SELECT..LIMIT is unsafe because the set of rows returned cannot
|
||||
be predicted.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_LIMIT= 0,
|
||||
/**
|
||||
INSERT DELAYED is unsafe because the time when rows are inserted
|
||||
cannot be predicted.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_INSERT_DELAYED,
|
||||
/**
|
||||
Access to log tables is unsafe because slave and master probably
|
||||
log different things.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_SYSTEM_TABLE,
|
||||
/**
|
||||
Update of two autoincrement columns is unsafe. With one
|
||||
autoincrement column, we store the counter in the binlog so that
|
||||
slave can restore the correct value. But we can only store one
|
||||
such counter per statement, so updating more than one
|
||||
autoincrement column is not safe.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_TWO_AUTOINC_COLUMNS,
|
||||
/**
|
||||
Using a UDF (user-defined function) is unsafe.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_UDF,
|
||||
/**
|
||||
Using most system variables is unsafe, because slave may run
|
||||
with different options than master.
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE,
|
||||
/**
|
||||
Using some functions is unsafe (e.g., UUID).
|
||||
*/
|
||||
BINLOG_STMT_UNSAFE_FUNCTION,
|
||||
|
||||
/* The last element of this enumeration type. */
|
||||
BINLOG_STMT_UNSAFE_COUNT
|
||||
};
|
||||
/**
|
||||
This has all flags from 0 (inclusive) to BINLOG_STMT_FLAG_COUNT
|
||||
(exclusive) set.
|
||||
*/
|
||||
static const int BINLOG_STMT_UNSAFE_ALL_FLAGS=
|
||||
((1 << BINLOG_STMT_UNSAFE_COUNT) - 1);
|
||||
|
||||
/**
|
||||
Determine if this statement is marked as unsafe.
|
||||
|
||||
@retval 0 if the statement is not marked as unsafe.
|
||||
@retval nonzero if the statement is marked as unsafe.
|
||||
*/
|
||||
inline bool is_stmt_unsafe() const {
|
||||
return binlog_stmt_flags & (1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
return get_stmt_unsafe_flags() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Is this statement actually a row injection?
|
||||
Flag the current (top-level) statement as unsafe.
|
||||
The flag will be reset after the statement has finished.
|
||||
|
||||
@param unsafe_type The type of unsafety: one of the @c
|
||||
BINLOG_STMT_FLAG_UNSAFE_* flags in @c enum_binlog_stmt_flag.
|
||||
*/
|
||||
inline void set_stmt_unsafe(enum_binlog_stmt_unsafe unsafe_type) {
|
||||
DBUG_ENTER("set_stmt_unsafe");
|
||||
DBUG_ASSERT(unsafe_type >= 0 && unsafe_type < BINLOG_STMT_UNSAFE_COUNT);
|
||||
binlog_stmt_flags|= (1U << unsafe_type);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/**
|
||||
Set the bits of binlog_stmt_flags determining the type of
|
||||
unsafeness of the current statement. No existing bits will be
|
||||
cleared, but new bits may be set.
|
||||
|
||||
@param flags A binary combination of zero or more bits, (1<<flag)
|
||||
where flag is a member of enum_binlog_stmt_unsafe.
|
||||
*/
|
||||
inline void set_stmt_unsafe_flags(uint32 flags) {
|
||||
DBUG_ENTER("set_stmt_unsafe_flags");
|
||||
DBUG_ASSERT((flags & ~BINLOG_STMT_UNSAFE_ALL_FLAGS) == 0);
|
||||
binlog_stmt_flags|= flags;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/**
|
||||
Return a binary combination of all unsafe warnings for the
|
||||
statement. If the statement has been marked as unsafe by the
|
||||
'flag' member of enum_binlog_stmt_unsafe, then the return value
|
||||
from this function has bit (1<<flag) set to 1.
|
||||
*/
|
||||
inline uint32 get_stmt_unsafe_flags() const {
|
||||
DBUG_ENTER("get_stmt_unsafe_flags");
|
||||
DBUG_RETURN(binlog_stmt_flags & BINLOG_STMT_UNSAFE_ALL_FLAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
Mark the current statement as safe; i.e., clear all bits in
|
||||
binlog_stmt_flags that correspond to elements of
|
||||
enum_binlog_stmt_unsafe.
|
||||
*/
|
||||
inline void clear_stmt_unsafe() {
|
||||
DBUG_ENTER("clear_stmt_unsafe");
|
||||
binlog_stmt_flags&= ~BINLOG_STMT_UNSAFE_ALL_FLAGS;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/**
|
||||
Determine if this statement is a row injection.
|
||||
|
||||
@retval 0 if the statement is not a row injection
|
||||
@retval nonzero if the statement is a row injection
|
||||
*/
|
||||
inline bool is_stmt_row_injection() const {
|
||||
return binlog_stmt_flags & (1U << BINLOG_STMT_FLAG_ROW_INJECTION);
|
||||
return binlog_stmt_flags &
|
||||
(1U << (BINLOG_STMT_UNSAFE_COUNT + BINLOG_STMT_TYPE_ROW_INJECTION));
|
||||
}
|
||||
|
||||
/**
|
||||
Flag the statement as a row injection. (A row injection is either
|
||||
Flag the statement as a row injection. A row injection is either
|
||||
a BINLOG statement, or a row event in the relay log executed by
|
||||
the slave SQL thread.)
|
||||
the slave SQL thread.
|
||||
*/
|
||||
inline void set_stmt_row_injection() {
|
||||
DBUG_ENTER("set_stmt_row_injection");
|
||||
binlog_stmt_flags|= (1U << BINLOG_STMT_FLAG_ROW_INJECTION);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
/**
|
||||
Flag the current (top-level) statement as unsafe.
|
||||
The flag will be reset after the statement has finished.
|
||||
*/
|
||||
inline void set_stmt_unsafe() {
|
||||
DBUG_ENTER("set_stmt_unsafe");
|
||||
binlog_stmt_flags|= (1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
inline void clear_stmt_unsafe() {
|
||||
DBUG_ENTER("clear_stmt_unsafe");
|
||||
binlog_stmt_flags&= ~(1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
binlog_stmt_flags|=
|
||||
(1U << (BINLOG_STMT_UNSAFE_COUNT + BINLOG_STMT_TYPE_ROW_INJECTION));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@ -1097,37 +1191,37 @@ public:
|
||||
{ return sroutines_list.elements != 0; }
|
||||
|
||||
private:
|
||||
/**
|
||||
Flags indicating properties of the statement with respect to
|
||||
logging.
|
||||
|
||||
These are combined in a binary manner; e.g., an unsafe statement
|
||||
has the bit (1<<BINLOG_STMT_FLAG_UNSAFE) set.
|
||||
/**
|
||||
Enumeration listing special types of statements.
|
||||
|
||||
Currently, the only possible type is ROW_INJECTION.
|
||||
*/
|
||||
enum enum_binlog_stmt_flag {
|
||||
/** The statement is unsafe to log in statement mode. */
|
||||
BINLOG_STMT_FLAG_UNSAFE= 0,
|
||||
enum enum_binlog_stmt_type {
|
||||
/**
|
||||
The statement is a row injection (i.e., either a BINLOG
|
||||
statement or a row event executed by the slave SQL thread).
|
||||
*/
|
||||
BINLOG_STMT_FLAG_ROW_INJECTION,
|
||||
/**
|
||||
The last element of this enumeration type. Insert new members
|
||||
above.
|
||||
*/
|
||||
BINLOG_STMT_FLAG_COUNT
|
||||
BINLOG_STMT_TYPE_ROW_INJECTION = 0,
|
||||
|
||||
/** The last element of this enumeration type. */
|
||||
BINLOG_STMT_TYPE_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
Indicates the type of statement with respect to binlogging.
|
||||
Bit field indicating the type of statement.
|
||||
|
||||
This is typically zeroed before parsing a statement, set during
|
||||
parsing (depending on the query), and read when deciding the
|
||||
logging format of the current statement.
|
||||
There are two groups of bits:
|
||||
|
||||
This is a binary combination of one or more bits (1<<flag), where
|
||||
flag is a member of enum_binlog_stmt_flag.
|
||||
- The low BINLOG_STMT_UNSAFE_COUNT bits indicate the types of
|
||||
unsafeness that the current statement has.
|
||||
|
||||
- The next BINLOG_STMT_TYPE_COUNT bits indicate if the statement
|
||||
is of some special type.
|
||||
|
||||
This must be a member of LEX, not of THD: each stored procedure
|
||||
needs to remember its unsafeness state between calls and each
|
||||
stored procedure has its own LEX object (but no own THD object).
|
||||
*/
|
||||
uint32 binlog_stmt_flags;
|
||||
};
|
||||
|
@ -5609,19 +5609,18 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
|
||||
|
||||
|
||||
/**
|
||||
Reset THD part responsible for command processing state.
|
||||
Reset the part of THD responsible for the state of command
|
||||
processing.
|
||||
|
||||
This needs to be called before execution of every statement
|
||||
(prepared or conventional).
|
||||
It is not called by substatements of routines.
|
||||
This needs to be called before execution of every statement
|
||||
(prepared or conventional). It is not called by substatements of
|
||||
routines.
|
||||
|
||||
@todo
|
||||
Make it a method of THD and align its name with the rest of
|
||||
reset/end/start/init methods.
|
||||
@todo
|
||||
Call it after we use THD for queries, not before.
|
||||
@todo Remove mysql_reset_thd_for_next_command and only use the
|
||||
member function.
|
||||
|
||||
@todo Call it after we use THD for queries, not before.
|
||||
*/
|
||||
|
||||
void mysql_reset_thd_for_next_command(THD *thd)
|
||||
{
|
||||
thd->reset_for_next_command();
|
||||
@ -5674,7 +5673,7 @@ void THD::reset_for_next_command()
|
||||
thd->sent_row_count= thd->examined_row_count= 0;
|
||||
|
||||
thd->reset_current_stmt_binlog_row_based();
|
||||
thd->binlog_warning_flags= 0;
|
||||
thd->binlog_unsafe_warning_flags= 0;
|
||||
|
||||
DBUG_PRINT("debug",
|
||||
("current_stmt_binlog_row_based: %d",
|
||||
|
@ -1306,8 +1306,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
||||
If the view's body needs row-based binlogging (e.g. the VIEW is created
|
||||
from SELECT UUID()), the top statement also needs it.
|
||||
*/
|
||||
if (lex->is_stmt_unsafe())
|
||||
old_lex->set_stmt_unsafe();
|
||||
old_lex->set_stmt_unsafe_flags(lex->get_stmt_unsafe_flags());
|
||||
|
||||
view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
lex->can_be_merged());
|
||||
LINT_INIT(view_main_select_tables);
|
||||
|
@ -7213,7 +7213,7 @@ function_call_keyword:
|
||||
$$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context());
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
Lex->safe_to_cache_query= 0;
|
||||
}
|
||||
| DATE_SYM '(' expr ')'
|
||||
@ -7368,7 +7368,7 @@ function_call_keyword:
|
||||
$$= new (YYTHD->mem_root) Item_func_user();
|
||||
if ($$ == NULL)
|
||||
MYSQL_YYABORT;
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_FUNCTION);
|
||||
Lex->safe_to_cache_query=0;
|
||||
}
|
||||
| YEAR_SYM '(' expr ')'
|
||||
@ -8098,7 +8098,7 @@ variable_aux:
|
||||
if (!($$= get_system_var(YYTHD, $2, $3, $4)))
|
||||
MYSQL_YYABORT;
|
||||
if (!((Item_func_get_system_var*) $$)->is_written_to_binlog())
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
|
||||
}
|
||||
;
|
||||
|
||||
@ -8944,7 +8944,10 @@ opt_limit_clause:
|
||||
;
|
||||
|
||||
limit_clause:
|
||||
LIMIT limit_options { Lex->set_stmt_unsafe(); }
|
||||
LIMIT limit_options
|
||||
{
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
|
||||
}
|
||||
;
|
||||
|
||||
limit_options:
|
||||
@ -9006,7 +9009,7 @@ delete_limit_clause:
|
||||
{
|
||||
SELECT_LEX *sel= Select;
|
||||
sel->select_limit= $2;
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
|
||||
sel->explicit_limit= 1;
|
||||
}
|
||||
;
|
||||
@ -9463,7 +9466,7 @@ insert_lock_option:
|
||||
| DELAYED_SYM
|
||||
{
|
||||
$$= TL_WRITE_DELAYED;
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
|
||||
}
|
||||
| HIGH_PRIORITY { $$= TL_WRITE; }
|
||||
;
|
||||
@ -9473,7 +9476,7 @@ replace_lock_option:
|
||||
| DELAYED_SYM
|
||||
{
|
||||
$$= TL_WRITE_DELAYED;
|
||||
Lex->set_stmt_unsafe();
|
||||
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -6861,10 +6861,9 @@ ha_innobase::external_lock(
|
||||
if (lock_type == F_WRLCK &&
|
||||
!(table_flags() & HA_BINLOG_STMT_CAPABLE) &&
|
||||
thd_binlog_format(thd) == BINLOG_FORMAT_STMT) {
|
||||
int skip = 0;
|
||||
/* used by test case */
|
||||
DBUG_EXECUTE_IF("no_innodb_binlog_errors", skip = 1;);
|
||||
if (!skip) {
|
||||
/* The error may be suppressed by test cases, by setting
|
||||
the no_innodb_binlog_errors debug symbol. */
|
||||
if (DBUG_EVALUATE_IF("no_innodb_binlog_errors", 0, 1)) {
|
||||
my_error(ER_BINLOG_STMT_MODE_AND_ROW_ENGINE, MYF(0),
|
||||
" InnoDB is limited to row-logging when "
|
||||
"transaction isolation level is "
|
||||
|
Reference in New Issue
Block a user