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.
This commit is contained in:
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:
|
||||
/*
|
||||
|
Reference in New Issue
Block a user