mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
merge mysql-5.1-rep+2-delivery1 --> mysql-5.1-rpl-merge
Conflicts: Text conflict in .bzr-mysql/default.conf Text conflict in mysql-test/extra/rpl_tests/rpl_loaddata.test Text conflict in mysql-test/r/mysqlbinlog2.result Text conflict in mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result Text conflict in mysql-test/suite/binlog/r/binlog_unsafe.result Text conflict in mysql-test/suite/rpl/r/rpl_insert_id.result Text conflict in mysql-test/suite/rpl/r/rpl_loaddata.result Text conflict in mysql-test/suite/rpl/r/rpl_stm_auto_increment_bug33029.result Text conflict in mysql-test/suite/rpl/r/rpl_udf.result Text conflict in mysql-test/suite/rpl/t/rpl_slow_query_log.test Text conflict in sql/field.h Text conflict in sql/log.cc Text conflict in sql/log_event.cc Text conflict in sql/log_event_old.cc Text conflict in sql/mysql_priv.h Text conflict in sql/share/errmsg.txt Text conflict in sql/sp.cc Text conflict in sql/sql_acl.cc Text conflict in sql/sql_base.cc Text conflict in sql/sql_class.h Text conflict in sql/sql_db.cc Text conflict in sql/sql_delete.cc Text conflict in sql/sql_insert.cc Text conflict in sql/sql_lex.cc Text conflict in sql/sql_lex.h Text conflict in sql/sql_load.cc Text conflict in sql/sql_table.cc Text conflict in sql/sql_update.cc Text conflict in sql/sql_view.cc Conflict adding files to storage/innobase. Created directory. Conflict because storage/innobase is not versioned, but has versioned children. Versioned directory. Conflict adding file storage/innobase. Moved existing file to storage/innobase.moved. Conflict adding files to storage/innobase/handler. Created directory. Conflict because storage/innobase/handler is not versioned, but has versioned children. Versioned directory. Contents conflict in storage/innobase/handler/ha_innodb.cc
This commit is contained in:
599
sql/sql_class.cc
599
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"
|
||||
@ -449,7 +450,7 @@ THD::THD()
|
||||
lock_id(&main_lock_id),
|
||||
user_time(0), in_sub_stmt(0),
|
||||
sql_log_bin_toplevel(false),
|
||||
binlog_table_maps(0), binlog_flags(0UL),
|
||||
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),
|
||||
@ -910,14 +911,15 @@ void THD::init(void)
|
||||
else
|
||||
options &= ~OPTION_BIG_SELECTS;
|
||||
|
||||
transaction.all.modified_non_trans_table= transaction.stmt.modified_non_trans_table= FALSE;
|
||||
transaction.all.modified_non_trans_table=
|
||||
transaction.stmt.modified_non_trans_table= FALSE;
|
||||
open_options=ha_open_options;
|
||||
update_lock_default= (variables.low_priority_updates ?
|
||||
TL_WRITE_LOW_PRIORITY :
|
||||
TL_WRITE);
|
||||
session_tx_isolation= (enum_tx_isolation) variables.tx_isolation;
|
||||
update_charset();
|
||||
reset_current_stmt_binlog_row_based();
|
||||
reset_current_stmt_binlog_format_row();
|
||||
bzero((char *) &status_var, sizeof(status_var));
|
||||
sql_log_bin_toplevel= options & OPTION_BIN_LOG;
|
||||
|
||||
@ -3176,13 +3178,13 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
|
||||
first_successful_insert_id_in_cur_stmt;
|
||||
|
||||
if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
|
||||
!current_stmt_binlog_row_based)
|
||||
!is_current_stmt_binlog_format_row())
|
||||
{
|
||||
options&= ~OPTION_BIN_LOG;
|
||||
}
|
||||
|
||||
if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&&
|
||||
!current_stmt_binlog_row_based)
|
||||
!is_current_stmt_binlog_format_row())
|
||||
mysql_bin_log.start_union_events(this, this->query_id);
|
||||
|
||||
/* Disable result sets */
|
||||
@ -3244,7 +3246,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
|
||||
is_fatal_sub_stmt_error= FALSE;
|
||||
|
||||
if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
|
||||
!current_stmt_binlog_row_based)
|
||||
!is_current_stmt_binlog_format_row())
|
||||
mysql_bin_log.stop_union_events(this);
|
||||
|
||||
/*
|
||||
@ -3407,6 +3409,392 @@ void xid_cache_delete(XID_STATE *xid_state)
|
||||
pthread_mutex_unlock(&LOCK_xid_cache);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Decide on logging format to use for the statement and issue errors
|
||||
or warnings as needed. The decision depends on the following
|
||||
parameters:
|
||||
|
||||
- The logging mode, i.e., the value of binlog_format. Can be
|
||||
statement, mixed, or row.
|
||||
|
||||
- The type of statement. There are three types of statements:
|
||||
"normal" safe statements; unsafe statements; and row injections.
|
||||
An unsafe statement is one that, if logged in statement format,
|
||||
might produce different results when replayed on the slave (e.g.,
|
||||
INSERT DELAYED). A row injection is either a BINLOG statement, or
|
||||
a row event executed by the slave's SQL thread.
|
||||
|
||||
- The capabilities of tables modified by the statement. The
|
||||
*capabilities vector* for a table is a set of flags associated
|
||||
with the table. Currently, it only includes two flags: *row
|
||||
capability flag* and *statement capability flag*.
|
||||
|
||||
The row capability flag is set if and only if the engine can
|
||||
handle row-based logging. The statement capability flag is set if
|
||||
and only if the table can handle statement-based logging.
|
||||
|
||||
Decision table for logging format
|
||||
---------------------------------
|
||||
|
||||
The following table summarizes how the format and generated
|
||||
warning/error depends on the tables' capabilities, the statement
|
||||
type, and the current binlog_format.
|
||||
|
||||
Row capable N NNNNNNNNN YYYYYYYYY YYYYYYYYY
|
||||
Statement capable N YYYYYYYYY NNNNNNNNN YYYYYYYYY
|
||||
|
||||
Statement type * SSSUUUIII SSSUUUIII SSSUUUIII
|
||||
|
||||
binlog_format * SMRSMRSMR SMRSMRSMR SMRSMRSMR
|
||||
|
||||
Logged format - SS-S----- -RR-RR-RR SRRSRR-RR
|
||||
Warning/Error 1 --2732444 5--5--6-- ---7--6--
|
||||
|
||||
Legend
|
||||
------
|
||||
|
||||
Row capable: N - Some table not row-capable, Y - All tables row-capable
|
||||
Stmt capable: N - Some table not stmt-capable, Y - All tables stmt-capable
|
||||
Statement type: (S)afe, (U)nsafe, or Row (I)njection
|
||||
binlog_format: (S)TATEMENT, (M)IXED, or (R)OW
|
||||
Logged format: (S)tatement or (R)ow
|
||||
Warning/Error: Warnings and error messages are as follows:
|
||||
|
||||
1. Error: Cannot execute statement: binlogging impossible since both
|
||||
row-incapable engines and statement-incapable engines are
|
||||
involved.
|
||||
|
||||
2. Error: Cannot execute statement: binlogging impossible since
|
||||
BINLOG_FORMAT = ROW and at least one table uses a storage engine
|
||||
limited to statement-logging.
|
||||
|
||||
3. Error: Cannot execute statement: binlogging of unsafe statement
|
||||
is impossible when storage engine is limited to statement-logging
|
||||
and BINLOG_FORMAT = MIXED.
|
||||
|
||||
4. Error: Cannot execute row injection: binlogging impossible since
|
||||
at least one table uses a storage engine limited to
|
||||
statement-logging.
|
||||
|
||||
5. Error: Cannot execute statement: binlogging impossible since
|
||||
BINLOG_FORMAT = STATEMENT and at least one table uses a storage
|
||||
engine limited to row-logging.
|
||||
|
||||
6. Error: Cannot execute row injection: binlogging impossible since
|
||||
BINLOG_FORMAT = STATEMENT.
|
||||
|
||||
7. Warning: Unsafe statement binlogged in statement format since
|
||||
BINLOG_FORMAT = STATEMENT.
|
||||
|
||||
In addition, we can produce the following error (not depending on
|
||||
the variables of the decision diagram):
|
||||
|
||||
8. Error: Cannot execute statement: binlogging impossible since more
|
||||
than one engine is involved and at least one engine is
|
||||
self-logging.
|
||||
|
||||
For each error case above, the statement is prevented from being
|
||||
logged, we report an error, and roll back the statement. For
|
||||
warnings, we set the thd->binlog_flags variable: the warning will be
|
||||
printed only if the statement is successfully logged.
|
||||
|
||||
@see THD::binlog_query
|
||||
|
||||
@param[in] thd Client thread
|
||||
@param[in] tables Tables involved in the query
|
||||
|
||||
@retval 0 No error; statement can be logged.
|
||||
@retval -1 One of the error conditions above applies (1, 2, 4, 5, or 6).
|
||||
*/
|
||||
|
||||
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()));
|
||||
|
||||
/*
|
||||
We should not decide logging format if the binlog is closed or
|
||||
binlogging is off, or if the statement is filtered out from the
|
||||
binlog by filtering rules.
|
||||
*/
|
||||
if (mysql_bin_log.is_open() && (options & OPTION_BIN_LOG) &&
|
||||
!(variables.binlog_format == BINLOG_FORMAT_STMT &&
|
||||
!binlog_filter->db_ok(db)))
|
||||
{
|
||||
/*
|
||||
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=
|
||||
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
|
||||
|
||||
my_bool multi_engine= FALSE;
|
||||
my_bool mixed_engine= FALSE;
|
||||
my_bool all_trans_engines= TRUE;
|
||||
TABLE* prev_write_table= NULL;
|
||||
TABLE* prev_access_table= NULL;
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
{
|
||||
static const char *prelocked_mode_name[] = {
|
||||
"NON_PRELOCKED",
|
||||
"PRELOCKED",
|
||||
"PRELOCKED_UNDER_LOCK_TABLES",
|
||||
};
|
||||
DBUG_PRINT("debug", ("prelocked_mode: %s",
|
||||
prelocked_mode_name[prelocked_mode]));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Get the capabilities vector for all involved storage engines and
|
||||
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::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
|
||||
if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
handler::Table_flags const flags= table->table->file->ha_table_flags();
|
||||
DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
|
||||
table->table_name, flags));
|
||||
if (prev_write_table && prev_write_table->file->ht !=
|
||||
table->table->file->ht)
|
||||
multi_engine= TRUE;
|
||||
all_trans_engines= all_trans_engines &&
|
||||
table->table->file->has_transactions();
|
||||
prev_write_table= table->table;
|
||||
flags_all_set &= flags;
|
||||
flags_some_set |= flags;
|
||||
}
|
||||
if (prev_access_table && prev_access_table->file->ht != table->table->file->ht)
|
||||
mixed_engine= mixed_engine || (prev_access_table->file->has_transactions() !=
|
||||
table->table->file->has_transactions());
|
||||
prev_access_table= table->table;
|
||||
}
|
||||
|
||||
/*
|
||||
Set the statement as unsafe if:
|
||||
|
||||
. it is a mixed statement, i.e. access transactional and non-transactional
|
||||
tables, and updates at least one;
|
||||
or
|
||||
. an early statement updated a transactional table;
|
||||
. and, the current statement updates a non-transactional table.
|
||||
|
||||
Any mixed statement is classified as unsafe to ensure that mixed mode is
|
||||
completely safe. Consider the following example to understand why we
|
||||
decided to do this:
|
||||
|
||||
Note that mixed statements such as
|
||||
|
||||
1: INSERT INTO myisam_t SELECT * FROM innodb_t;
|
||||
|
||||
2: INSERT INTO innodb_t SELECT * FROM myisam_t;
|
||||
|
||||
are classified as unsafe to ensure that in mixed mode the execution is
|
||||
completely safe and equivalent to the row mode. Consider the following
|
||||
statements and sessions (connections) to understand the reason:
|
||||
|
||||
con1: INSERT INTO innodb_t VALUES (1);
|
||||
con1: INSERT INTO innodb_t VALUES (100);
|
||||
|
||||
con1: BEGIN
|
||||
con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
|
||||
con1: INSERT INTO innodb_t VALUES (200);
|
||||
con1: COMMIT;
|
||||
|
||||
The point is that the concurrent statements may be written into the binary log
|
||||
in a way different from the execution. For example,
|
||||
|
||||
BINARY LOG:
|
||||
|
||||
con2: BEGIN;
|
||||
con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
|
||||
con2: COMMIT;
|
||||
con1: BEGIN
|
||||
con1: INSERT INTO innodb_t VALUES (200);
|
||||
con1: COMMIT;
|
||||
|
||||
....
|
||||
|
||||
or
|
||||
|
||||
BINARY LOG:
|
||||
|
||||
con1: BEGIN
|
||||
con1: INSERT INTO innodb_t VALUES (200);
|
||||
con1: COMMIT;
|
||||
con2: BEGIN;
|
||||
con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
|
||||
con2: COMMIT;
|
||||
|
||||
Clearly, this may become a problem in STMT mode and setting the statement
|
||||
as unsafe will make rows to be written into the binary log in MIXED mode
|
||||
and as such the problem will not stand.
|
||||
|
||||
In STMT mode, although such statement is classified as unsafe, i.e.
|
||||
|
||||
INSERT INTO myisam_t SELECT * FROM innodb_t;
|
||||
|
||||
there is no enough information to avoid writing it outside the boundaries
|
||||
of a transaction. This is not a problem if we are considering snapshot
|
||||
isolation level but if we have pure repeatable read or serializable the
|
||||
lock history on the slave will be different from the master.
|
||||
*/
|
||||
if (mixed_engine ||
|
||||
trans_has_updated_trans_table(this) && !all_trans_engines)
|
||||
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
|
||||
|
||||
DBUG_PRINT("info", ("flags_all_set: 0x%llx", flags_all_set));
|
||||
DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set));
|
||||
DBUG_PRINT("info", ("multi_engine: %d", multi_engine));
|
||||
|
||||
int error= 0;
|
||||
int unsafe_flags;
|
||||
|
||||
/*
|
||||
If more than one engine is involved in the statement and at
|
||||
least one is doing it's own logging (is *self-logging*), the
|
||||
statement cannot be logged atomically, so we generate an error
|
||||
rather than allowing the binlog to become corrupt.
|
||||
*/
|
||||
if (multi_engine &&
|
||||
(flags_some_set & HA_HAS_OWN_BINLOGGING))
|
||||
{
|
||||
my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
|
||||
MYF(0));
|
||||
}
|
||||
|
||||
/* both statement-only and row-only engines involved */
|
||||
if ((flags_all_set & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE)) == 0)
|
||||
{
|
||||
/*
|
||||
1. Error: Binary logging impossible since both row-incapable
|
||||
engines and statement-incapable engines are involved
|
||||
*/
|
||||
my_error((error= ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE), MYF(0));
|
||||
}
|
||||
/* statement-only engines involved */
|
||||
else if ((flags_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
|
||||
{
|
||||
if (lex->is_stmt_row_injection())
|
||||
{
|
||||
/*
|
||||
4. Error: Cannot execute row injection since table uses
|
||||
storage engine limited to statement-logging
|
||||
*/
|
||||
my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
|
||||
}
|
||||
else if (variables.binlog_format == BINLOG_FORMAT_ROW)
|
||||
{
|
||||
/*
|
||||
2. Error: Cannot modify table that uses a storage engine
|
||||
limited to statement-logging when BINLOG_FORMAT = ROW
|
||||
*/
|
||||
my_error((error= ER_BINLOG_ROW_MODE_AND_STMT_ENGINE), MYF(0));
|
||||
}
|
||||
else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
|
||||
{
|
||||
/*
|
||||
3. Error: Cannot execute statement: binlogging of unsafe
|
||||
statement is impossible when storage engine is limited to
|
||||
statement-logging and BINLOG_FORMAT = MIXED.
|
||||
*/
|
||||
for (int unsafe_type= 0;
|
||||
unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
|
||||
unsafe_type++)
|
||||
if (unsafe_flags & (1 << unsafe_type))
|
||||
my_error((error= ER_BINLOG_UNSAFE_AND_STMT_ENGINE), MYF(0),
|
||||
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
|
||||
}
|
||||
/* log in statement format! */
|
||||
}
|
||||
/* no statement-only engines */
|
||||
else
|
||||
{
|
||||
/* binlog_format = STATEMENT */
|
||||
if (variables.binlog_format == BINLOG_FORMAT_STMT)
|
||||
{
|
||||
if (lex->is_stmt_row_injection())
|
||||
{
|
||||
/*
|
||||
6. Error: Cannot execute row injection since
|
||||
BINLOG_FORMAT = STATEMENT
|
||||
*/
|
||||
my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
|
||||
}
|
||||
else if ((flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
|
||||
{
|
||||
/*
|
||||
5. Error: Cannot modify table that uses a storage engine
|
||||
limited to row-logging when binlog_format = STATEMENT
|
||||
*/
|
||||
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
|
||||
}
|
||||
else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
|
||||
{
|
||||
/*
|
||||
7. Warning: Unsafe statement logged as statement due to
|
||||
binlog_format = STATEMENT
|
||||
*/
|
||||
binlog_unsafe_warning_flags|= unsafe_flags;
|
||||
DBUG_PRINT("info", ("Scheduling warning to be issued by "
|
||||
"binlog_query: '%s'",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT)));
|
||||
DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
|
||||
binlog_unsafe_warning_flags));
|
||||
}
|
||||
/* log in statement format! */
|
||||
}
|
||||
/* No statement-only engines and binlog_format != STATEMENT.
|
||||
I.e., nothing prevents us from row logging if needed. */
|
||||
else
|
||||
{
|
||||
if (lex->is_stmt_unsafe() || lex->is_stmt_row_injection()
|
||||
|| (flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
|
||||
{
|
||||
/* log in row format! */
|
||||
set_current_stmt_binlog_format_row_if_mixed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 "
|
||||
"and binlog_format = %ld "
|
||||
"and binlog_filter->db_ok(db) = %d",
|
||||
mysql_bin_log.is_open(),
|
||||
(options & OPTION_BIN_LOG),
|
||||
variables.binlog_format,
|
||||
binlog_filter->db_ok(db)));
|
||||
#endif
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Implementation of interface to write rows to the binary log through the
|
||||
thread. The thread is responsible for writing the rows it has
|
||||
@ -3458,7 +3846,7 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
|
||||
if (binlog_setup_trx_data())
|
||||
DBUG_RETURN(NULL);
|
||||
|
||||
Rows_log_event* pending= binlog_get_pending_rows_event();
|
||||
Rows_log_event* pending= binlog_get_pending_rows_event(is_transactional);
|
||||
|
||||
if (unlikely(pending && !pending->is_valid()))
|
||||
DBUG_RETURN(NULL);
|
||||
@ -3492,7 +3880,9 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
|
||||
flush the pending event and replace it with the newly created
|
||||
event...
|
||||
*/
|
||||
if (unlikely(mysql_bin_log.flush_and_set_pending_rows_event(this, ev)))
|
||||
if (unlikely(
|
||||
mysql_bin_log.flush_and_set_pending_rows_event(this, ev,
|
||||
is_transactional)))
|
||||
{
|
||||
delete ev;
|
||||
DBUG_RETURN(NULL);
|
||||
@ -3715,7 +4105,7 @@ int THD::binlog_write_row(TABLE* table, bool is_trans,
|
||||
MY_BITMAP const* cols, size_t colcnt,
|
||||
uchar const *record)
|
||||
{
|
||||
DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
|
||||
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
|
||||
|
||||
/*
|
||||
Pack records into format for transfer. We are allocating more
|
||||
@ -3745,7 +4135,7 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
|
||||
const uchar *before_record,
|
||||
const uchar *after_record)
|
||||
{
|
||||
DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
|
||||
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
|
||||
|
||||
size_t const before_maxlen = max_row_length(table, before_record);
|
||||
size_t const after_maxlen = max_row_length(table, after_record);
|
||||
@ -3790,7 +4180,7 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
|
||||
MY_BITMAP const* cols, size_t colcnt,
|
||||
uchar const *record)
|
||||
{
|
||||
DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
|
||||
DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
|
||||
|
||||
/*
|
||||
Pack records into format for transfer. We are allocating more
|
||||
@ -3816,14 +4206,15 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
|
||||
}
|
||||
|
||||
|
||||
int THD::binlog_remove_pending_rows_event(bool clear_maps)
|
||||
int THD::binlog_remove_pending_rows_event(bool clear_maps,
|
||||
bool is_transactional)
|
||||
{
|
||||
DBUG_ENTER("THD::binlog_remove_pending_rows_event");
|
||||
|
||||
if (!mysql_bin_log.is_open())
|
||||
DBUG_RETURN(0);
|
||||
|
||||
mysql_bin_log.remove_pending_rows_event(this);
|
||||
mysql_bin_log.remove_pending_rows_event(this, is_transactional);
|
||||
|
||||
if (clear_maps)
|
||||
binlog_table_maps= 0;
|
||||
@ -3831,7 +4222,7 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
int THD::binlog_flush_pending_rows_event(bool stmt_end)
|
||||
int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
|
||||
{
|
||||
DBUG_ENTER("THD::binlog_flush_pending_rows_event");
|
||||
/*
|
||||
@ -3847,7 +4238,7 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end)
|
||||
flag is set.
|
||||
*/
|
||||
int error= 0;
|
||||
if (Rows_log_event *pending= binlog_get_pending_rows_event())
|
||||
if (Rows_log_event *pending= binlog_get_pending_rows_event(is_transactional))
|
||||
{
|
||||
if (stmt_end)
|
||||
{
|
||||
@ -3856,7 +4247,8 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end)
|
||||
binlog_table_maps= 0;
|
||||
}
|
||||
|
||||
error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0);
|
||||
error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0,
|
||||
is_transactional);
|
||||
}
|
||||
|
||||
DBUG_RETURN(error);
|
||||
@ -3872,8 +4264,6 @@ show_query_type(THD::enum_binlog_query_type qtype)
|
||||
return "ROW";
|
||||
case THD::STMT_QUERY_TYPE:
|
||||
return "STMT";
|
||||
case THD::MYSQL_QUERY_TYPE:
|
||||
return "MYSQL";
|
||||
case THD::QUERY_TYPE_COUNT:
|
||||
default:
|
||||
DBUG_ASSERT(0 <= qtype && qtype < THD::QUERY_TYPE_COUNT);
|
||||
@ -3885,32 +4275,97 @@ show_query_type(THD::enum_binlog_query_type qtype)
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
Member function that will log query, either row-based or
|
||||
statement-based depending on the value of the 'current_stmt_binlog_row_based'
|
||||
the value of the 'qtype' flag.
|
||||
/**
|
||||
Auxiliary method used by @c binlog_query() to raise warnings.
|
||||
|
||||
This function should be called after the all calls to ha_*_row()
|
||||
functions have been issued, but before tables are unlocked and
|
||||
closed.
|
||||
The type of warning and the type of unsafeness is stored in
|
||||
THD::binlog_unsafe_warning_flags.
|
||||
*/
|
||||
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(2 * LEX::BINLOG_STMT_UNSAFE_COUNT <=
|
||||
sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
|
||||
|
||||
OBSERVE
|
||||
There shall be no writes to any system table after calling
|
||||
binlog_query(), so these writes has to be moved to before the call
|
||||
of binlog_query() for correct functioning.
|
||||
uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
|
||||
|
||||
This is necessesary not only for RBR, but the master might crash
|
||||
after binlogging the query but before changing the system tables.
|
||||
This means that the slave and the master are not in the same state
|
||||
(after the master has restarted), so therefore we have to
|
||||
eliminate this problem.
|
||||
/*
|
||||
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;
|
||||
|
||||
RETURN VALUE
|
||||
Error code, or 0 if no error.
|
||||
DBUG_PRINT("info", ("unsafe_type_flags: 0x%x", unsafe_type_flags));
|
||||
|
||||
/*
|
||||
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,
|
||||
ER_BINLOG_UNSAFE_STATEMENT,
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT),
|
||||
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
|
||||
if (global_system_variables.log_warnings)
|
||||
{
|
||||
char buf[MYSQL_ERRMSG_SIZE * 2];
|
||||
sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
|
||||
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
|
||||
sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query());
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Mark these unsafe types as already printed, to avoid printing
|
||||
warnings for them again.
|
||||
*/
|
||||
binlog_unsafe_warning_flags|=
|
||||
unsafe_type_flags << LEX::BINLOG_STMT_UNSAFE_COUNT;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Log the current query.
|
||||
|
||||
The query will be logged in either row format or statement format
|
||||
depending on the value of @c current_stmt_binlog_format_row field and
|
||||
the value of the @c qtype parameter.
|
||||
|
||||
This function must be called:
|
||||
|
||||
- After the all calls to ha_*_row() functions have been issued.
|
||||
|
||||
- After any writes to system tables. Rationale: if system tables
|
||||
were written after a call to this function, and the master crashes
|
||||
after the call to this function and before writing the system
|
||||
tables, then the master and slave get out of sync.
|
||||
|
||||
- Before tables are unlocked and closed.
|
||||
|
||||
@see decide_logging_format
|
||||
|
||||
@retval 0 Success
|
||||
|
||||
@retval nonzero If there is a failure when writing the query (e.g.,
|
||||
write failure), then the error code is returned.
|
||||
*/
|
||||
int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
|
||||
ulong query_len, bool is_trans, bool suppress_use,
|
||||
int errcode)
|
||||
ulong query_len, bool is_trans, bool direct,
|
||||
bool suppress_use, int errcode)
|
||||
{
|
||||
DBUG_ENTER("THD::binlog_query");
|
||||
DBUG_PRINT("enter", ("qtype: %s query: '%s'",
|
||||
@ -3927,59 +4382,53 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
|
||||
top-most close_thread_tables().
|
||||
*/
|
||||
if (this->prelocked_mode == NON_PRELOCKED)
|
||||
if (int error= binlog_flush_pending_rows_event(TRUE))
|
||||
if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
|
||||
DBUG_RETURN(error);
|
||||
|
||||
/*
|
||||
If we are in statement mode and trying to log an unsafe statement,
|
||||
we should print a warning.
|
||||
Warnings for unsafe statements logged in statement format are
|
||||
printed here instead of in decide_logging_format(). This is
|
||||
because the warnings should be printed only if the statement is
|
||||
actually logged. When executing decide_logging_format(), we cannot
|
||||
know for sure if the statement will be logged.
|
||||
*/
|
||||
if (sql_log_bin_toplevel && lex->is_stmt_unsafe() &&
|
||||
variables.binlog_format == BINLOG_FORMAT_STMT &&
|
||||
binlog_filter->db_ok(this->db))
|
||||
{
|
||||
/*
|
||||
A warning can be elevated a error when STRICT sql mode.
|
||||
But we don't want to elevate binlog warning to error here.
|
||||
*/
|
||||
push_warning(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_BINLOG_UNSAFE_STATEMENT,
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT));
|
||||
if (global_system_variables.log_warnings &&
|
||||
!(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
|
||||
{
|
||||
sql_print_warning("%s Statement: %.*s",
|
||||
ER(ER_BINLOG_UNSAFE_STATEMENT),
|
||||
MYSQL_ERRMSG_SIZE, query_arg);
|
||||
binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
|
||||
}
|
||||
}
|
||||
if (sql_log_bin_toplevel)
|
||||
issue_unsafe_warnings();
|
||||
|
||||
switch (qtype) {
|
||||
/*
|
||||
ROW_QUERY_TYPE means that the statement may be logged either in
|
||||
row format or in statement format. If
|
||||
current_stmt_binlog_format is row, it means that the
|
||||
statement has already been logged in row format and hence shall
|
||||
not be logged again.
|
||||
*/
|
||||
case THD::ROW_QUERY_TYPE:
|
||||
DBUG_PRINT("debug",
|
||||
("current_stmt_binlog_row_based: %d",
|
||||
current_stmt_binlog_row_based));
|
||||
if (current_stmt_binlog_row_based)
|
||||
("is_current_stmt_binlog_format_row: %d",
|
||||
is_current_stmt_binlog_format_row()));
|
||||
if (is_current_stmt_binlog_format_row())
|
||||
DBUG_RETURN(0);
|
||||
/* Otherwise, we fall through */
|
||||
case THD::MYSQL_QUERY_TYPE:
|
||||
/*
|
||||
Using this query type is a conveniece hack, since we have been
|
||||
moving back and forth between using RBR for replication of
|
||||
system tables and not using it.
|
||||
/* Fall through */
|
||||
|
||||
Make sure to change in check_table_binlog_row_based() according
|
||||
to how you treat this.
|
||||
/*
|
||||
STMT_QUERY_TYPE means that the query must be logged in statement
|
||||
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_format_row is row.
|
||||
|
||||
@todo Currently there are places that call this method with
|
||||
STMT_QUERY_TYPE and current_stmt_binlog_format is row. Fix those
|
||||
places and add assert to ensure correct behavior. /Sven
|
||||
*/
|
||||
case THD::STMT_QUERY_TYPE:
|
||||
/*
|
||||
The MYSQL_LOG::write() function will set the STMT_END_F flag and
|
||||
flush the pending rows event if necessary.
|
||||
*/
|
||||
*/
|
||||
{
|
||||
Query_log_event qinfo(this, query_arg, query_len, is_trans, suppress_use,
|
||||
errcode);
|
||||
Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
|
||||
suppress_use, errcode);
|
||||
qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
|
||||
/*
|
||||
Binlog table maps will be irrelevant after a Query_log_event
|
||||
|
Reference in New Issue
Block a user