diff options
author | unknown <aelkin/elkin@koti.dsl.inet.fi> | 2007-12-12 12:14:59 +0200 |
---|---|---|
committer | unknown <aelkin/elkin@koti.dsl.inet.fi> | 2007-12-12 12:14:59 +0200 |
commit | 96a51b7f39be9bd664a8d242670ad1d6522c2b83 (patch) | |
tree | 84a421ca8e0f200a6b9023670d25f4572cf6a40d /sql | |
parent | 376342f29f7c603b4ca0e8ee4ba17ddc1e51f8c4 (diff) | |
download | mariadb-git-96a51b7f39be9bd664a8d242670ad1d6522c2b83.tar.gz |
Bug#31552 Replication breaks when deleting rows from out-of-sync table
without PK
Bug#31609 Not all RBR slave errors reported as errors
bug#32468 delete rows event on a table with foreign key constraint fails
The first two bugs comprise idempotency issues.
First, there was no error code reported under conditions of the bug
description although the slave sql thread halted.
Second, executions were different with and without presence of prim key in
the table.
Third, there was no way to instruct the slave whether to ignore an error
and skip to the following event or to halt.
Fourth, there are handler errors which might happen due to idempotent
applying of binlog but those were not listed among the "idempotent" error
list.
All the named issues are addressed.
Wrt to the 3rd, there is the new global system variable, changeble at run
time, which controls the slave sql thread behaviour.
The new variable allows further extensions to mimic the sql_mode
session/global variable.
To address the 4th, the new bug#32468 had to be fixed as it was staying
in the way.
include/my_bitmap.h:
basic operations with bits of an integer type are added.
mysql-test/extra/rpl_tests/rpl_foreign_key.test:
regression test for bug#32468
mysql-test/extra/rpl_tests/rpl_row_basic.test:
changes due to bug#31552/31609 idempotency is not default any longer
mysql-test/extra/rpl_tests/rpl_row_tabledefs.test:
changes due to bug#31552/31609 idempotency is not default any longer
mysql-test/suite/rpl/r/rpl_foreign_key_innodb.result:
results changed
mysql-test/suite/rpl/r/rpl_idempotency.result:
results changed
mysql-test/suite/rpl/r/rpl_row_basic_11bugs.result:
results changed
mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result:
results changed
mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result:
results changed
mysql-test/suite/rpl/r/rpl_row_mystery22.result:
results changed
mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result:
results changed
mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result:
results changed
mysql-test/suite/rpl/r/rpl_temporary_errors.result:
results changed
mysql-test/suite/rpl/t/rpl_idempotency.test:
extenstions to the test providing testing of complements to the
idempotent error set and checking how slave halts when it faces an error
from the list when the mode is STRICT.
mysql-test/suite/rpl/t/rpl_row_basic_11bugs.test:
changes due to bug#31552/31609 idempotency is not default any longer.
mysql-test/suite/rpl/t/rpl_row_mystery22.test:
changes due to bug#31552/31609 idempotency is not default any longer
mysql-test/suite/rpl/t/rpl_temporary_errors.test:
changes due to bug#31552/31609 idempotency is not default any longer
mysql-test/suite/rpl_ndb/r/rpl_ndb_dd_advance.result:
results changed
mysql-test/suite/rpl_ndb/r/rpl_row_basic_7ndb.result:
results changed
sql/log_event.cc:
the fix for bug#32468 delete rows event on a table with foreign key constraint fails
ensures the flags are set at proper time so that their values will be caught
by innodb.
reseting the flags is done along the common error and errorless execution
path.
The list of idempotent error is extended with foreign keys related items.
NDB engine write events are designed with the replace sematics in mind.
Therefore the corrsponding ndb handler's flag are (re)set regardless of
the slave's execution mode.
Rows_log_event::write_row() starts using the bool replace argument as its
caller sets it depending on the event's execution mode.
sql/log_event.h:
adding a new member to hold the slave's mode during execution of the event.
sql/mysql_priv.h:
changes to link the command line option with the new global sys var.
sql/mysqld.cc:
introduction of the new command line option.
providing its initialization to a default.
changes to link the command line option with the new global sys var.
sql/rpl_rli.cc:
rli post-event-execution cleanup restores the default bits.
sql/set_var.cc:
The new "standard" sys_var_set class' and the new global system var related
declarations and definitions.
fix_slave_exec_mode() is used as with the update method of a new class so
as at time of the command line arguments parsing.
sql/set_var.h:
new declarations. The class for the new global sys var is based on
yet another new "standard" one.
sql/share/errmsg.txt:
slave_exec_mode setting error;
slave inconsistency error which may be not an error when the intention
is "idempotent". I.e consisting of row-based events binlog is being
applied for the 2nd (more) time.
sql/sql_class.h:
The names for the bits of the new sever slave_exec_mode_options.
mysql-test/suite/rpl/t/rpl_idempotency-master.opt:
innodb is necessary
mysql-test/suite/rpl/t/rpl_idempotency-slave.opt:
innodb is necessary, as well as the tests start with non-default
IDEMPOTENT slave execution mode.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/log_event.cc | 239 | ||||
-rw-r--r-- | sql/log_event.h | 6 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 19 | ||||
-rw-r--r-- | sql/rpl_rli.cc | 5 | ||||
-rw-r--r-- | sql/set_var.cc | 99 | ||||
-rw-r--r-- | sql/set_var.h | 40 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 3 |
9 files changed, 319 insertions, 95 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc index 77cf719fde5..fc8b1904496 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -90,6 +90,28 @@ static const char *HA_ERR(int i) case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE"; case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT"; } + return 0; +} + +/** + macro to call from different branches of Rows_log_event::do_apply_event +*/ +static void inline slave_rows_error_report(enum loglevel level, int ha_error, + Relay_log_info const *rli, THD *thd, + TABLE *table, const char * type, + const char *log_name, ulong pos) +{ + const char *handler_error= HA_ERR(ha_error); + rli->report(level, thd->net.last_errno, + "Could not execute %s event on table %s.%s;" + "%s%s handler error %s; " + "the event's master log %s, end_log_pos %lu", + type, table->s->db.str, + table->s->table_name.str, + thd->net.last_error[0] != 0 ? thd->net.last_error : "", + thd->net.last_error[0] != 0 ? ";" : "", + handler_error == NULL? "<unknown>" : handler_error, + log_name, pos); } #endif @@ -6060,7 +6082,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) { DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)"); int error= 0; - /* If m_table_id == ~0UL, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the @@ -6106,6 +6127,24 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) */ lex_start(thd); + /* + There are a few flags that are replicated with each row event. + Make sure to set/clear them before executing the main body of + the event. + */ + if (get_flags(NO_FOREIGN_KEY_CHECKS_F)) + thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS; + else + thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS; + + if (get_flags(RELAXED_UNIQUE_CHECKS_F)) + thd->options|= OPTION_RELAXED_UNIQUE_CHECKS; + else + thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS; + /* A small test to verify that objects have consistent types */ + DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS)); + + while ((error= lock_tables(thd, rli->tables_to_lock, rli->tables_to_lock_count, &need_reopen))) { @@ -6240,22 +6279,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) So we call set_time(), like in SBR. Presently it changes nothing. */ thd->set_time((time_t)when); - /* - There are a few flags that are replicated with each row event. - Make sure to set/clear them before executing the main body of - the event. - */ - if (get_flags(NO_FOREIGN_KEY_CHECKS_F)) - thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS; - else - thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS; - - if (get_flags(RELAXED_UNIQUE_CHECKS_F)) - thd->options|= OPTION_RELAXED_UNIQUE_CHECKS; - else - thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS; - /* A small test to verify that objects have consistent types */ - DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS)); /* Now we are in a statement and will stay in a statement until we @@ -6288,8 +6311,9 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) if (!get_flags(COMPLETE_ROWS_F)) bitmap_intersect(table->write_set,&m_cols); + this->slave_exec_mode= slave_exec_mode_options; // fix the mode + // Do event specific preparations - error= do_before_row_operations(rli); // row processing loop @@ -6308,22 +6332,41 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) { case 0: break; - - /* Some recoverable errors */ + /* + The following list of "idempotent" errors + means that an error from the list might happen + because of idempotent (more than once) + applying of a binlog file. + Notice, that binlog has a ddl operation its + second applying may cause + + case HA_ERR_TABLE_DEF_CHANGED: + case HA_ERR_CANNOT_ADD_FOREIGN: + + which are not included into to the list. + */ case HA_ERR_RECORD_CHANGED: case HA_ERR_RECORD_DELETED: case HA_ERR_KEY_NOT_FOUND: case HA_ERR_END_OF_FILE: - /* Idempotency support: OK if tuple does not exist */ + case HA_ERR_FOUND_DUPP_KEY: + case HA_ERR_FOUND_DUPP_UNIQUE: + case HA_ERR_FOREIGN_DUPLICATE_KEY: + case HA_ERR_NO_REFERENCED_ROW: + case HA_ERR_ROW_IS_REFERENCED: + DBUG_PRINT("info", ("error: %s", HA_ERR(error))); - error= 0; + if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1) + { + if (global_system_variables.log_warnings) + slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table, + get_type_str(), + RPL_LOG_NAME, (ulong) log_pos); + error= 0; + } break; - + default: - rli->report(ERROR_LEVEL, thd->net.last_errno, - "Error in %s event: row application failed. %s", - get_type_str(), - thd->net.last_error ? thd->net.last_error : ""); thd->query_error= 1; break; } @@ -6367,16 +6410,14 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) */ if (rli->tables_to_lock && get_flags(STMT_END_F)) const_cast<Relay_log_info*>(rli)->clear_tables_to_lock(); - + if (error) { /* error has occured during the transaction */ - rli->report(ERROR_LEVEL, thd->net.last_errno, - "Error in %s event: error during transaction execution " - "on table %s.%s. %s", - get_type_str(), table->s->db.str, - table->s->table_name.str, - thd->net.last_error ? thd->net.last_error : ""); - + slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table, + get_type_str(), RPL_LOG_NAME, (ulong) log_pos); + } + if (error) + { /* If one day we honour --skip-slave-errors in row-based replication, and the error should be skipped, then we would clear mappings, rollback, @@ -6488,6 +6529,7 @@ Rows_log_event::do_update_pos(Relay_log_info *rli) */ thd->reset_current_stmt_binlog_row_based(); + rli->cleanup_context(thd, 0); if (error == 0) { @@ -7170,43 +7212,50 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability { int error= 0; - /* - We are using REPLACE semantics and not INSERT IGNORE semantics - when writing rows, that is: new rows replace old rows. We need to - inform the storage engine that it should use this behaviour. + /** + todo: to introduce a property for the event (handler?) which forces + applying the event in the replace (idempotent) fashion. */ + if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 || + m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER) + { + /* + We are using REPLACE semantics and not INSERT IGNORE semantics + when writing rows, that is: new rows replace old rows. We need to + inform the storage engine that it should use this behaviour. + */ + + /* Tell the storage engine that we are using REPLACE semantics. */ + thd->lex->duplicates= DUP_REPLACE; + + /* + Pretend we're executing a REPLACE command: this is needed for + InnoDB and NDB Cluster since they are not (properly) checking the + lex->duplicates flag. + */ + thd->lex->sql_command= SQLCOM_REPLACE; + /* + Do not raise the error flag in case of hitting to an unique attribute + */ + m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + /* + NDB specific: update from ndb master wrapped as Write_rows + so that the event should be applied to replace slave's row + */ + m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); + /* + NDB specific: if update from ndb master wrapped as Write_rows + does not find the row it's assumed idempotent binlog applying + is taking place; don't raise the error. + */ + m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY); + /* + TODO: the cluster team (Tomas?) says that it's better if the engine knows + how many rows are going to be inserted, then it can allocate needed memory + from the start. + */ + } - /* Tell the storage engine that we are using REPLACE semantics. */ - thd->lex->duplicates= DUP_REPLACE; - - /* - Pretend we're executing a REPLACE command: this is needed for - InnoDB and NDB Cluster since they are not (properly) checking the - lex->duplicates flag. - */ - thd->lex->sql_command= SQLCOM_REPLACE; - /* - Do not raise the error flag in case of hitting to an unique attribute - */ - m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - /* - NDB specific: update from ndb master wrapped as Write_rows - */ - /* - so that the event should be applied to replace slave's row - */ - m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); - /* - NDB specific: if update from ndb master wrapped as Write_rows - does not find the row it's assumed idempotent binlog applying - is taking place; don't raise the error. - */ - m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY); - /* - TODO: the cluster team (Tomas?) says that it's better if the engine knows - how many rows are going to be inserted, then it can allocate needed memory - from the start. - */ m_table->file->ha_start_bulk_insert(0); /* We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill @@ -7228,18 +7277,23 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability } int -Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, +Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const, int error) { int local_error= 0; - m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); - /* - reseting the extra with - table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); - fires bug#27077 - todo: explain or fix - */ + if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 || + m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER) + { + m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); + m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); + /* + resetting the extra with + table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY); + fires bug#27077 + explanation: file->reset() performs this duty + ultimately. Still todo: fix + */ + } if ((local_error= m_table->file->ha_end_bulk_insert())) { m_table->file->print_error(local_error, MYF(0)); @@ -7358,23 +7412,22 @@ Rows_log_event::write_row(const Relay_log_info *const rli, while ((error= table->file->ha_write_row(table->record[0]))) { - if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT) + if (error == HA_ERR_LOCK_DEADLOCK || + error == HA_ERR_LOCK_WAIT_TIMEOUT || + (keynum= table->file->get_dup_key(error)) < 0 || + !overwrite) { - table->file->print_error(error, MYF(0)); /* to check at exec_relay_log_event */ - DBUG_RETURN(error); - } - if ((keynum= table->file->get_dup_key(error)) < 0) - { - DBUG_PRINT("info",("Can't locate duplicate key (get_dup_key returns %d)",keynum)); - table->file->print_error(error, MYF(0)); + DBUG_PRINT("info",("get_dup_key returns %d)", keynum)); /* - We failed to retrieve the duplicate key + Deadlock, waiting for lock or just an error from the handler + such as HA_ERR_FOUND_DUPP_KEY when overwrite is false. + Retrieval of the duplicate key number may fail - either because the error was not "duplicate key" error - or because the information which key is not available */ + table->file->print_error(error, MYF(0)); DBUG_RETURN(error); } - /* We need to retrieve the old row into record[1] to be able to either update or delete the offending record. We either: @@ -7512,11 +7565,13 @@ int Write_rows_log_event::do_exec_row(const Relay_log_info *const rli) { DBUG_ASSERT(m_table != NULL); - int error= write_row(rli, TRUE /* overwrite */); - + int error= + write_row(rli, /* if 1 then overwrite */ + bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1); + if (error && !thd->net.last_errno) thd->net.last_errno= error; - + return error; } diff --git a/sql/log_event.h b/sql/log_event.h index 4bd496af2a4..0275b02dd49 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -809,6 +809,12 @@ public: bool cache_stmt; + /** + A storage to cache the global system variable's value. + Handling of a separate event will be governed its member. + */ + ulong slave_exec_mode; + #ifndef MYSQL_CLIENT THD* thd; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3b88fe0fca8..712352a8c0b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1813,6 +1813,7 @@ extern uint volatile thread_count, thread_running, global_read_lock; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; +extern ulong slave_exec_mode_options; extern my_bool opt_readonly, lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9d74280b11b..eb7ce1de7b6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -445,6 +445,8 @@ ulong thread_stack, what_to_log; ulong query_buff_size, slow_launch_time, slave_open_temp_tables; ulong open_files_limit, max_binlog_size, max_relay_log_size; ulong slave_net_timeout, slave_trans_retries; +ulong slave_exec_mode_options; +const char *slave_exec_mode_str= "STRICT"; ulong thread_cache_size=0, thread_pool_size= 0; ulong binlog_cache_size=0, max_binlog_cache_size=0; ulong query_cache_size=0; @@ -5101,7 +5103,8 @@ enum options_mysqld OPT_SECURE_FILE_PRIV, OPT_MIN_EXAMINED_ROW_LIMIT, OPT_LOG_SLOW_SLAVE_STATEMENTS, - OPT_OLD_MODE + OPT_OLD_MODE, + OPT_SLAVE_EXEC_MODE }; @@ -5799,8 +5802,11 @@ replicating a LOAD DATA INFILE command.", (uchar**) &slave_load_tmpdir, (uchar**) &slave_load_tmpdir, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"slave-skip-errors", OPT_SLAVE_SKIP_ERRORS, - "Tells the slave thread to continue replication when a query returns an error from the provided list.", + "Tells the slave thread to continue replication when a query event returns an error from the provided list.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"slave-exec-mode", OPT_SLAVE_EXEC_MODE, + "Modes for how replication events should be executed. Legal values are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, replication will not stop for operations that are idempotent. In STRICT mode, replication will stop on any unexpected difference between the master and the slave.", + (uchar**) &slave_exec_mode_str, (uchar**) &slave_exec_mode_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif {"slow-query-log", OPT_SLOW_LOG, "Enable|disable slow query log", (uchar**) &opt_slow_log, @@ -7109,6 +7115,9 @@ static void mysql_init_variables(void) /* Things with default values that are not zero */ delay_key_write_options= (uint) DELAY_KEY_WRITE_ON; + slave_exec_mode_options= 0; + slave_exec_mode_options= (uint) + find_bit_type_or_exit(slave_exec_mode_str, &slave_exec_mode_typelib, NULL); opt_specialflag= SPECIAL_ENGLISH; unix_sock= ip_sock= INVALID_SOCKET; mysql_home_ptr= mysql_home; @@ -7318,6 +7327,10 @@ mysqld_get_one_option(int optid, case OPT_SLAVE_SKIP_ERRORS: init_slave_skip_errors(argument); break; + case OPT_SLAVE_EXEC_MODE: + slave_exec_mode_options= (uint) + find_bit_type_or_exit(argument, &slave_exec_mode_typelib, ""); + break; #endif case OPT_SAFEMALLOC_MEM_LIMIT: #if !defined(DBUG_OFF) && defined(SAFEMALLOC) @@ -7863,6 +7876,8 @@ static void get_options(int *argc,char **argv) } /* Set global MyISAM variables from delay_key_write_options */ fix_delay_key_write((THD*) 0, OPT_GLOBAL); + /* Set global slave_exec_mode from its option */ + fix_slave_exec_mode(OPT_GLOBAL); #ifndef EMBEDDED_LIBRARY if (mysqld_chroot) diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 15d7d97affd..c28d1ba97f7 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1160,6 +1160,11 @@ void Relay_log_info::cleanup_context(THD *thd, bool error) close_thread_tables(thd); clear_tables_to_lock(); clear_flag(IN_STMT); + /* + Cleanup for the flags that have been set at do_apply_event. + */ + thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS; + thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS; last_event_start_time= 0; DBUG_VOID_RETURN; } diff --git a/sql/set_var.cc b/sql/set_var.cc index 138c71fa559..460f877c0ab 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -88,6 +88,16 @@ TYPELIB delay_key_write_typelib= delay_key_write_type_names, NULL }; +const char *slave_exec_mode_names[]= +{ "STRICT", "IDEMPOTENT", NullS }; +static const unsigned int slave_exec_mode_names_len[]= +{ sizeof("STRICT") - 1, sizeof("IDEMPOTENT") - 1, 0 }; +TYPELIB slave_exec_mode_typelib= +{ + array_elements(slave_exec_mode_names)-1, "", + slave_exec_mode_names, (unsigned int *) slave_exec_mode_names_len +}; + static int sys_check_ftb_syntax(THD *thd, set_var *var); static bool sys_update_ftb_syntax(THD *thd, set_var * var); static void sys_default_ftb_syntax(THD *thd, enum_var_type type); @@ -408,6 +418,11 @@ static sys_var_const_str_ptr sys_secure_file_priv(&vars, "secure_file_priv", static sys_var_long_ptr sys_server_id(&vars, "server_id", &server_id, fix_server_id); static sys_var_bool_ptr sys_slave_compressed_protocol(&vars, "slave_compressed_protocol", &opt_slave_compressed_protocol); +static sys_var_set_slave_mode slave_exec_mode(&vars, + "slave_exec_mode", + &slave_exec_mode_options, + &slave_exec_mode_typelib, + 0); static sys_var_long_ptr sys_slow_launch_time(&vars, "slow_launch_time", &slow_launch_time); static sys_var_thd_ulong sys_sort_buffer(&vars, "sort_buffer_size", @@ -981,6 +996,79 @@ extern void fix_delay_key_write(THD *thd, enum_var_type type) } } +bool sys_var_set::update(THD *thd, set_var *var) +{ + *value= var->save_result.ulong_value; + return 0; +}; + +uchar *sys_var_set::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + char buff[256]; + String tmp(buff, sizeof(buff), &my_charset_latin1); + ulong length; + ulong val= *value; + + tmp.length(0); + for (uint i= 0; val; val>>= 1, i++) + { + if (val & 1) + { + tmp.append(enum_names->type_names[i], + enum_names->type_lengths[i]); + tmp.append(','); + } + } + + if ((length= tmp.length())) + length--; + return (uchar*) thd->strmake(tmp.ptr(), length); +} + +void sys_var_set_slave_mode::set_default(THD *thd, enum_var_type type) +{ + slave_exec_mode_options= 0; + bit_do_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT); +} + +bool sys_var_set_slave_mode::check(THD *thd, set_var *var) +{ + bool rc= sys_var_set::check(thd, var); + if (!rc && + bit_is_set(var->save_result.ulong_value, SLAVE_EXEC_MODE_STRICT) == 1 && + bit_is_set(var->save_result.ulong_value, SLAVE_EXEC_MODE_IDEMPOTENT) == 1) + { + rc= true; + my_error(ER_SLAVE_AMBIGOUS_EXEC_MODE, MYF(0), ""); + } + return rc; +} + +bool sys_var_set_slave_mode::update(THD *thd, set_var *var) +{ + bool rc; + pthread_mutex_lock(&LOCK_global_system_variables); + rc= sys_var_set::update(thd, var); + pthread_mutex_unlock(&LOCK_global_system_variables); + return rc; +} + +void fix_slave_exec_mode(enum_var_type type) +{ + DBUG_ENTER("fix_slave_exec_mode"); + compile_time_assert(sizeof(slave_exec_mode_options) * CHAR_BIT + > SLAVE_EXEC_MODE_LAST_BIT - 1); + if (bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT) == 1 && + bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT) == 1) + { + sql_print_error("Ambiguous slave modes combination." + " STRICT will be used"); + bit_do_clear(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT); + } + if (bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT) == 0) + bit_do_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT); +} bool sys_var_thd_binlog_format::is_readonly() const { @@ -3165,7 +3253,18 @@ int set_var::light_check(THD *thd) return 0; } +/** + Update variable + @param thd thread handler + @returns 0|1 ok or ERROR + + @note ERROR can be only due to abnormal operations involving + the server's execution evironment such as + out of memory, hard disk failure or the computer blows up. + Consider set_var::check() method if there is a need to return + an error due to logics. +*/ int set_var::update(THD *thd) { if (!value) diff --git a/sql/set_var.h b/sql/set_var.h index eb2c893c89e..6f44f7889d2 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -30,7 +30,8 @@ class sys_var_pluginvar; /* opaque */ typedef struct system_variables SV; typedef struct my_locale_st MY_LOCALE; -extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib; +extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib, + slave_exec_mode_typelib; typedef int (*sys_check_func)(THD *, set_var *); typedef bool (*sys_update_func)(THD *, set_var *); @@ -771,6 +772,42 @@ public: }; +class sys_var_set :public sys_var +{ +protected: + ulong *value; + TYPELIB *enum_names; +public: + sys_var_set(sys_var_chain *chain, const char *name_arg, ulong *value_arg, + TYPELIB *typelib, sys_after_update_func func) + :sys_var(name_arg, func), value(value_arg), enum_names(typelib) + { chain_sys_var(chain); } + virtual bool check(THD *thd, set_var *var) + { + return check_set(thd, var, enum_names); + } + virtual void set_default(THD *thd, enum_var_type type) + { + *value= 0; + } + bool update(THD *thd, set_var *var); + uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + bool check_update_type(Item_result type) { return 0; } + SHOW_TYPE show_type() { return SHOW_CHAR; } +}; + +class sys_var_set_slave_mode :public sys_var_set +{ +public: + sys_var_set_slave_mode(sys_var_chain *chain, const char *name_arg, + ulong *value_arg, + TYPELIB *typelib, sys_after_update_func func) : + sys_var_set(chain, name_arg, value_arg, typelib, func) {} + void set_default(THD *thd, enum_var_type type); + bool check(THD *thd, set_var *var); + bool update(THD *thd, set_var *var); +}; + class sys_var_log_output :public sys_var { ulong *value; @@ -1189,6 +1226,7 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length=0); int sql_set_variables(THD *thd, List<set_var_base> *var_list); bool not_all_support_one_shot(List<set_var_base> *var_list); void fix_delay_key_write(THD *thd, enum_var_type type); +void fix_slave_exec_mode(enum_var_type type); ulong fix_sql_mode(ulong sql_mode); extern sys_var_const_str sys_charset_system; extern sys_var_str sys_init_connect; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 8fad09eb221..ee68096a21d 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6112,3 +6112,5 @@ ER_TRG_CANT_OPEN_TABLE ER_CANT_CREATE_SROUTINE eng "Cannot create stored routine `%-.64s`. Check warnings" +ER_SLAVE_AMBIGOUS_EXEC_MODE + eng "Ambiguous slave modes combination. %s" diff --git a/sql/sql_class.h b/sql/sql_class.h index 0dcab6eff92..a3fe30d85cd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -38,6 +38,9 @@ enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; +enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT, + SLAVE_EXEC_MODE_IDEMPOTENT, + SLAVE_EXEC_MODE_LAST_BIT}; enum enum_mark_columns { MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; |