mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Replication changes for CREATE OR REPLACE TABLE
- CREATE TABLE is by default executed on the slave as CREATE OR REPLACE - DROP TABLE is by default executed on the slave as DROP TABLE IF NOT EXISTS This means that a slave will by default continue even if we try to create a table that existed on the slave (the table will be deleted and re-created) or if we try to drop a table that didn't exist on the slave. This should be safe as instead of having the slave stop because of an inconsistency between master and slave, it will fix the inconsistency. Those that would prefer to get a stopped slave instead for the above cases can set slave_ddl_exec_mode to STRICT. - Ensure that a CREATE OR REPLACE TABLE which dropped a table is replicated - DROP TABLE that generated an error on master is handled as an identical DROP TABLE on the slave (IF NOT EXISTS is not added in this case) - Added slave_ddl_exec_mode variable to decide how DDL's are replicated New logic for handling BEGIN GTID ... COMMIT from the binary log: - When we find a BEGIN GTID, we start a transaction and set OPTION_GTID_BEGIN - When we find COMMIT, we reset OPTION_GTID_BEGIN and execute the normal COMMIT code. - While OPTION_GTID_BEGIN is set: - We don't generate implict commits before or after statements - All tables are regarded as transactional tables in the binary log (to ensure things are executed exactly as on the master) - We reset OPTION_GTID_BEGIN also on rollback This will help ensuring that we don't get any sporadic commits (and thus new GTID's) on the slave and will help keep the GTID's between master and slave in sync. mysql-test/extra/rpl_tests/rpl_log.test: Added testing of mode slave_ddl_exec_mode=STRICT mysql-test/r/mysqld--help.result: New help messages mysql-test/suite/rpl/r/create_or_replace_mix.result: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/r/create_or_replace_row.result: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/r/create_or_replace_statement.result: Testing replication of create or replace mysql-test/suite/rpl/r/rpl_gtid_startpos.result: Test must be run in slave_ddl_exec_mode=STRICT as part of the test depends on that DROP TABLE should fail on slave. mysql-test/suite/rpl/r/rpl_row_log.result: Updated result mysql-test/suite/rpl/r/rpl_row_log_innodb.result: Updated result mysql-test/suite/rpl/r/rpl_row_show_relaylog_events.result: Updated result mysql-test/suite/rpl/r/rpl_stm_log.result: Updated result mysql-test/suite/rpl/r/rpl_stm_mix_show_relaylog_events.result: Updated result mysql-test/suite/rpl/r/rpl_temp_table_mix_row.result: Updated result mysql-test/suite/rpl/t/create_or_replace.inc: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_mix.cnf: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_mix.test: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_row.cnf: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_row.test: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_statement.cnf: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/create_or_replace_statement.test: Testing of CREATE OR REPLACE TABLE with replication mysql-test/suite/rpl/t/rpl_gtid_startpos.test: Test must be run in slave_ddl_exec_mode=STRICT as part of the test depends on that DROP TABLE should fail on slave. mysql-test/suite/rpl/t/rpl_stm_log.test: Removed some lines mysql-test/suite/sys_vars/r/slave_ddl_exec_mode_basic.result: Testing of slave_ddl_exec_mode mysql-test/suite/sys_vars/t/slave_ddl_exec_mode_basic.test: Testing of slave_ddl_exec_mode sql/handler.cc: Regard all tables as transactional in commit if OPTION_GTID_BEGIN is set. This is to ensure that statments are not commited too early if non transactional tables are used. sql/log.cc: Regard all tables as transactional in commit if OPTION_GTID_BEGIN is set. Also treat 'direct' log events as transactional (to get them logged as they where on the master) sql/log_event.cc: Ensure that the new error from DROP TABLE when trying to drop a view is treated same as the old one. Store error code that slave expects in THD. Set OPTION_GTID_BEGIN if we find a BEGIN. Reset OPTION_GTID_BEGIN if we find a COMMIT. sql/mysqld.cc: Added slave_ddl_exec_mode_options sql/mysqld.h: Added slave_ddl_exec_mode_options sql/rpl_gtid.cc: Reset OPTION_GTID_BEGIN if we record a gtid (safety) sql/sql_class.cc: Regard all tables as transactional in commit if OPTION_GTID_BEGIN is set. sql/sql_class.h: Added to THD: log_current_statement and slave_expected_error sql/sql_insert.cc: Ensure that CREATE OR REPLACE is logged if table was deleted. Don't do implicit commit for CREATE if we are under OPTION_GTID_BEGIN sql/sql_parse.cc: Change CREATE TABLE -> CREATE OR REPLACE TABLE for slaves Change DROP TABLE -> DROP TABLE IF EXISTS for slaves CREATE TABLE doesn't force implicit commit in case of OPTION_GTID_BEGIN Don't do commits before or after any statement if OPTION_GTID_BEGIN was set. sql/sql_priv.h: Added OPTION_GTID_BEGIN sql/sql_show.cc: Enhanced store_create_info() to also be able to handle CREATE OR REPLACE sql/sql_show.h: Updated prototype sql/sql_table.cc: Ensure that CREATE OR REPLACE is logged if table was deleted. sql/sys_vars.cc: Added slave_ddl_exec_mode sql/transaction.cc: Added warning if we got a GTID under OPTION_GTID_BEGIN
This commit is contained in:
@ -195,6 +195,8 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
|
||||
@param thd Thread handle.
|
||||
@param mask Bitmask used for the SQL command match.
|
||||
|
||||
@return 0 No implicit commit
|
||||
@return 1 Do a commit
|
||||
*/
|
||||
static bool stmt_causes_implicit_commit(THD *thd, uint mask)
|
||||
{
|
||||
@ -207,12 +209,22 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask)
|
||||
|
||||
switch (lex->sql_command) {
|
||||
case SQLCOM_DROP_TABLE:
|
||||
skip= lex->drop_temporary;
|
||||
skip= (lex->drop_temporary ||
|
||||
(thd->variables.option_bits & OPTION_GTID_BEGIN));
|
||||
break;
|
||||
case SQLCOM_ALTER_TABLE:
|
||||
/* If ALTER TABLE of non-temporary table, do implicit commit */
|
||||
skip= (lex->create_info.tmp_table());
|
||||
break;
|
||||
case SQLCOM_CREATE_TABLE:
|
||||
/* If CREATE TABLE of non-temporary table, do implicit commit */
|
||||
skip= lex->create_info.tmp_table();
|
||||
/*
|
||||
If CREATE TABLE of non-temporary table and the table is not part
|
||||
if a BEGIN GTID ... COMMIT group, do a implicit commit.
|
||||
This ensures that CREATE ... SELECT will in the same GTID group on the
|
||||
master and slave.
|
||||
*/
|
||||
skip= (lex->create_info.tmp_table() ||
|
||||
(thd->variables.option_bits & OPTION_GTID_BEGIN));
|
||||
break;
|
||||
case SQLCOM_SET_OPTION:
|
||||
skip= lex->autocommit ? FALSE : TRUE;
|
||||
@ -2439,11 +2451,14 @@ mysql_execute_command(THD *thd)
|
||||
DBUG_ASSERT(! thd->in_sub_stmt);
|
||||
/* Statement transaction still should not be started. */
|
||||
DBUG_ASSERT(thd->transaction.stmt.is_empty());
|
||||
/* Commit the normal transaction if one is active. */
|
||||
if (trans_commit_implicit(thd))
|
||||
goto error;
|
||||
/* Release metadata locks acquired in this transaction. */
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
|
||||
{
|
||||
/* Commit the normal transaction if one is active. */
|
||||
if (trans_commit_implicit(thd))
|
||||
goto error;
|
||||
/* Release metadata locks acquired in this transaction. */
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
@ -2869,6 +2884,17 @@ case SQLCOM_PREPARE:
|
||||
*/
|
||||
lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
|
||||
|
||||
/*
|
||||
If we are a slave, we should add OR REPLACE if we don't have
|
||||
IF EXISTS. This will help a slave to recover from
|
||||
CREATE TABLE OR EXISTS failures by dropping the table and
|
||||
retrying the create.
|
||||
*/
|
||||
if (thd->slave_thread &&
|
||||
slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
|
||||
!(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
|
||||
create_info.options|= HA_LEX_CREATE_REPLACE;
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
{
|
||||
partition_info *part_info= thd->lex->part_info;
|
||||
@ -3659,6 +3685,16 @@ end_with_restore_list:
|
||||
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
|
||||
thd->variables.option_bits|= OPTION_KEEP_LOG;
|
||||
}
|
||||
/*
|
||||
If we are a slave, we should add IF EXISTS if the query executed
|
||||
on the master without an error. This will help a slave to
|
||||
recover from multi-table DROP TABLE that was aborted in the
|
||||
middle.
|
||||
*/
|
||||
if (thd->slave_thread && !thd->slave_expected_error &&
|
||||
slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
|
||||
lex->check_exists= 1;
|
||||
|
||||
/* DDL and binlog write order are protected by metadata locks. */
|
||||
res= mysql_rm_table(thd, first_table, lex->check_exists,
|
||||
lex->drop_temporary);
|
||||
@ -4407,6 +4443,7 @@ end_with_restore_list:
|
||||
bool tx_release= (lex->tx_release == TVL_YES ||
|
||||
(thd->variables.completion_type == 2 &&
|
||||
lex->tx_release != TVL_NO));
|
||||
|
||||
if (trans_rollback(thd))
|
||||
goto error;
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
@ -5158,12 +5195,15 @@ finish:
|
||||
{
|
||||
/* No transaction control allowed in sub-statements. */
|
||||
DBUG_ASSERT(! thd->in_sub_stmt);
|
||||
/* If commit fails, we should be able to reset the OK status. */
|
||||
thd->get_stmt_da()->set_overwrite_status(true);
|
||||
/* Commit the normal transaction if one is active. */
|
||||
trans_commit_implicit(thd);
|
||||
thd->get_stmt_da()->set_overwrite_status(false);
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
|
||||
{
|
||||
/* If commit fails, we should be able to reset the OK status. */
|
||||
thd->get_stmt_da()->set_overwrite_status(true);
|
||||
/* Commit the normal transaction if one is active. */
|
||||
trans_commit_implicit(thd);
|
||||
thd->get_stmt_da()->set_overwrite_status(false);
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
}
|
||||
}
|
||||
else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
|
||||
{
|
||||
@ -6106,6 +6146,8 @@ void THD::reset_for_next_command()
|
||||
thd->query_start_used= 0;
|
||||
thd->query_start_sec_part_used= 0;
|
||||
thd->is_fatal_error= thd->time_zone_used= 0;
|
||||
thd->log_current_statement= 0;
|
||||
|
||||
/*
|
||||
Clear the status flag that are expected to be cleared at the
|
||||
beginning of each SQL statement.
|
||||
|
Reference in New Issue
Block a user