WL#3339 (Issue warnings when statement-based replication may fail):
Replacing binlog_row_based_if_mixed with variable binlog_stmt_flags holding several flags and adding member functions to manipulate the flags. Added code to generate a warning when an attempt to log an unsafe statement to the binary log was made. The warning is both pushed to the SHOW WARNINGS table and written to the error log. The prevent flooding the error log, the warning is just written to the error log once per open session. sql/item_create.cc: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. sql/sp_head.cc: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. sql/sp_head.h: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. sql/sql_base.cc: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. sql/sql_class.cc: Adding THD::binlog_flags to store thread-specific binary log state. Adding code to push a warning and write an entry into the error log when an attempt is made to log an unsafe statement. sql/sql_class.h: Adding THD::binlog_flags to store thread-specific binary log state. Adding BINLOG_FLAG_UNSAFE_STMT_PRINTED to denote that a warning for an unsafe statement has already been generated for this thread. sql/sql_insert.cc: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. sql/sql_lex.cc: Replacing binlog_row_based_if_mixed with a variable binlog_stmt_flags holding several flags. sql/sql_lex.h: Replacing binlog_row_based_if_mixed with a variable binlog_stmt_flags holding several flags. sql/share/errmsg.txt: Adding error message to indicate that an attempt to log an unsafe statement was made. sql/sql_view.cc: Using {is,set,clear}_stmt_unsafe() member functions instead of binlog_row_based_if_mixed member variable. mysql-test/r/binlog_unsafe.result: New BitKeeper file ``mysql-test/r/binlog_unsafe.result'' mysql-test/t/binlog_unsafe.test: New BitKeeper file ``mysql-test/t/binlog_unsafe.test''
This commit is contained in:
parent
f63501a2ca
commit
499a8ecd21
13
mysql-test/r/binlog_unsafe.result
Normal file
13
mysql-test/r/binlog_unsafe.result
Normal file
@ -0,0 +1,13 @@
|
||||
SET BINLOG_FORMAT=STATEMENT;
|
||||
CREATE TABLE t1 (a CHAR(40));
|
||||
CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY);
|
||||
CREATE TABLE t3 (b INT AUTO_INCREMENT PRIMARY KEY);
|
||||
CREATE VIEW v1(a,b) AS SELECT a,b FROM t2,t3;
|
||||
INSERT INTO t1 SELECT UUID();
|
||||
Warnings:
|
||||
Warning 1588 Statement is not safe to log in statement format.
|
||||
SHOW WARNINGS;
|
||||
Level Warning
|
||||
Code 1588
|
||||
Message Statement is not safe to log in statement format.
|
||||
DROP TABLE t1;
|
16
mysql-test/t/binlog_unsafe.test
Normal file
16
mysql-test/t/binlog_unsafe.test
Normal file
@ -0,0 +1,16 @@
|
||||
# Test to check that a warning is generated for unsafe statements
|
||||
# executed under statement mode logging.
|
||||
|
||||
SET BINLOG_FORMAT=STATEMENT;
|
||||
|
||||
CREATE TABLE t1 (a CHAR(40));
|
||||
CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY);
|
||||
CREATE TABLE t3 (b INT AUTO_INCREMENT PRIMARY KEY);
|
||||
CREATE VIEW v1(a,b) AS SELECT a,b FROM t2,t3;
|
||||
|
||||
INSERT INTO t1 SELECT UUID();
|
||||
query_vertical SHOW WARNINGS;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
|
@ -2341,7 +2341,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->binlog_row_based_if_mixed= TRUE;
|
||||
thd->lex->set_stmt_unsafe();
|
||||
|
||||
DBUG_ASSERT( (udf->type == UDFTYPE_FUNCTION)
|
||||
|| (udf->type == UDFTYPE_AGGREGATE));
|
||||
@ -4527,7 +4527,7 @@ Create_func_uuid Create_func_uuid::s_singleton;
|
||||
Item*
|
||||
Create_func_uuid::create(THD *thd)
|
||||
{
|
||||
thd->lex->binlog_row_based_if_mixed= TRUE;
|
||||
thd->lex->set_stmt_unsafe();
|
||||
return new (thd->mem_root) Item_func_uuid();
|
||||
}
|
||||
|
||||
|
@ -6059,3 +6059,6 @@ ER_SLAVE_INCIDENT
|
||||
eng "The incident %s occured on the master. Message: %-.64s"
|
||||
ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT
|
||||
eng "Table has no partition for some existing values"
|
||||
ER_BINLOG_UNSAFE_STATEMENT
|
||||
eng "Statement is not safe to log in statement format."
|
||||
swe "Detta är inte säkert att logga i statement-format."
|
||||
|
@ -1873,7 +1873,7 @@ sp_head::restore_lex(THD *thd)
|
||||
cannot switch from statement-based to row-based only for this
|
||||
substatement).
|
||||
*/
|
||||
if (sublex->binlog_row_based_if_mixed)
|
||||
if (sublex->is_stmt_unsafe())
|
||||
m_flags|= BINLOG_ROW_BASED_IF_MIXED;
|
||||
|
||||
/*
|
||||
|
@ -379,7 +379,7 @@ public:
|
||||
the substatements not).
|
||||
*/
|
||||
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
|
||||
lex->binlog_row_based_if_mixed= TRUE;
|
||||
lex->set_stmt_unsafe();
|
||||
}
|
||||
|
||||
|
||||
|
@ -3591,7 +3591,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
|
||||
/*
|
||||
CREATE ... SELECT UUID() locks no tables, we have to test here.
|
||||
*/
|
||||
if (thd->lex->binlog_row_based_if_mixed)
|
||||
if (thd->lex->is_stmt_unsafe())
|
||||
thd->set_current_stmt_binlog_row_based_if_mixed();
|
||||
|
||||
if (!tables && !thd->lex->requires_prelocking())
|
||||
@ -3632,7 +3632,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->binlog_row_based_if_mixed= TRUE;
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->set_current_stmt_binlog_row_based_if_mixed();
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ THD::THD()
|
||||
Open_tables_state(refresh_version), rli_fake(0),
|
||||
lock_id(&main_lock_id),
|
||||
user_time(0), in_sub_stmt(0),
|
||||
binlog_table_maps(0),
|
||||
binlog_table_maps(0), binlog_flags(0UL),
|
||||
global_read_lock(0), is_fatal_error(0),
|
||||
rand_used(0), time_zone_used(0),
|
||||
arg_of_last_insert_id_function(FALSE),
|
||||
@ -2888,6 +2888,23 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype,
|
||||
to how you treat this.
|
||||
*/
|
||||
case THD::STMT_QUERY_TYPE:
|
||||
if (lex->is_stmt_unsafe())
|
||||
{
|
||||
DBUG_ASSERT(this->query != NULL);
|
||||
push_warning(this, MYSQL_ERROR::WARN_LEVEL_WARN,
|
||||
ER_BINLOG_UNSAFE_STATEMENT,
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT));
|
||||
if (!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
|
||||
{
|
||||
|
||||
char warn_buf[MYSQL_ERRMSG_SIZE];
|
||||
my_snprintf(warn_buf, MYSQL_ERRMSG_SIZE, "%s Statement: %s",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT), this->query);
|
||||
sql_print_warning(warn_buf);
|
||||
binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The MYSQL_LOG::write() function will set the STMT_END_F flag and
|
||||
flush the pending rows event if necessary.
|
||||
|
@ -1048,6 +1048,17 @@ public:
|
||||
|
||||
private:
|
||||
uint binlog_table_maps; // Number of table maps currently in the binlog
|
||||
|
||||
enum enum_binlog_flag {
|
||||
BINLOG_FLAG_UNSAFE_STMT_PRINTED,
|
||||
BINLOG_FLAG_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
Flags with per-thread information regarding the status of the
|
||||
binary log.
|
||||
*/
|
||||
uint32 binlog_flags;
|
||||
public:
|
||||
uint get_binlog_table_maps() const {
|
||||
return binlog_table_maps;
|
||||
@ -1599,6 +1610,7 @@ public:
|
||||
void restore_sub_statement_state(Sub_statement_state *backup);
|
||||
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
|
||||
void restore_active_arena(Query_arena *set, Query_arena *backup);
|
||||
|
||||
inline void set_current_stmt_binlog_row_based_if_mixed()
|
||||
{
|
||||
/*
|
||||
|
@ -1533,6 +1533,7 @@ public:
|
||||
Statement-based replication of INSERT DELAYED has problems with RAND()
|
||||
and user vars, so in mixed mode we go to row-based.
|
||||
*/
|
||||
thd.lex->set_stmt_unsafe();
|
||||
thd.set_current_stmt_binlog_row_based_if_mixed();
|
||||
|
||||
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
|
||||
|
@ -1719,7 +1719,7 @@ void Query_tables_list::reset_query_tables_list(bool init)
|
||||
sroutines_list.empty();
|
||||
sroutines_list_own_last= sroutines_list.next;
|
||||
sroutines_list_own_elements= 0;
|
||||
binlog_row_based_if_mixed= FALSE;
|
||||
binlog_stmt_flags= 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -909,14 +909,6 @@ public:
|
||||
byte **sroutines_list_own_last;
|
||||
uint sroutines_list_own_elements;
|
||||
|
||||
/*
|
||||
Tells if the parsing stage detected that some items require row-based
|
||||
binlogging to give a reliable binlog/replication, or if we will use
|
||||
stored functions or triggers which themselves need require row-based
|
||||
binlogging.
|
||||
*/
|
||||
bool binlog_row_based_if_mixed;
|
||||
|
||||
/*
|
||||
These constructor and destructor serve for creation/destruction
|
||||
of Query_tables_list instances which are used as backup storage.
|
||||
@ -964,6 +956,41 @@ public:
|
||||
query_tables_own_last= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Has the parser/scanner detected that this statement is unsafe?
|
||||
*/
|
||||
inline bool is_stmt_unsafe() const {
|
||||
return binlog_stmt_flags & (1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
}
|
||||
|
||||
/**
|
||||
Flag the current (top-level) statement as unsafe.
|
||||
|
||||
The flag will be reset after the statement has finished.
|
||||
|
||||
*/
|
||||
inline void set_stmt_unsafe() {
|
||||
binlog_stmt_flags|= (1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
}
|
||||
|
||||
inline void clear_stmt_unsafe() {
|
||||
binlog_stmt_flags&= ~(1U << BINLOG_STMT_FLAG_UNSAFE);
|
||||
}
|
||||
|
||||
private:
|
||||
enum enum_binlog_stmt_flag {
|
||||
BINLOG_STMT_FLAG_UNSAFE,
|
||||
BINLOG_STMT_FLAG_COUNT
|
||||
};
|
||||
|
||||
/*
|
||||
Tells if the parsing stage detected properties of the statement,
|
||||
for example: that some items require row-based binlogging to give
|
||||
a reliable binlog/replication, or if we will use stored functions
|
||||
or triggers which themselves need require row-based binlogging.
|
||||
*/
|
||||
uint32 binlog_stmt_flags;
|
||||
};
|
||||
|
||||
|
||||
@ -1299,6 +1326,7 @@ typedef struct st_lex : public Query_tables_list
|
||||
void restore_backup_query_tables_list(Query_tables_list *backup);
|
||||
|
||||
bool table_or_sp_used();
|
||||
|
||||
} LEX;
|
||||
|
||||
struct st_lex_local: public st_lex
|
||||
|
@ -1114,8 +1114,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->binlog_row_based_if_mixed)
|
||||
old_lex->binlog_row_based_if_mixed= TRUE;
|
||||
if (lex->is_stmt_unsafe())
|
||||
old_lex->set_stmt_unsafe();
|
||||
view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
lex->can_be_merged());
|
||||
LINT_INIT(view_main_select_tables);
|
||||
|
Loading…
x
Reference in New Issue
Block a user