1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +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:
Sven Sandberg
2009-07-22 18:16:17 +02:00
parent 6f8f024aaf
commit f404c96e82
25 changed files with 1674 additions and 968 deletions

View File

@@ -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;
};