diff options
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 284 |
1 files changed, 122 insertions, 162 deletions
diff --git a/sql/handler.cc b/sql/handler.cc index 56e7da6430d..a24f18f4863 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -50,6 +50,9 @@ #include "../storage/maria/ha_maria.h" #endif +#include "wsrep_mysqld.h" +#include "wsrep.h" + /* While we have legacy_db_type, we have this array to check for dups and to find handlerton from legacy_db_type. @@ -74,7 +77,6 @@ ulong savepoint_alloc_size= 0; static const LEX_STRING sys_table_aliases[]= { { C_STRING_WITH_LEN("INNOBASE") }, { C_STRING_WITH_LEN("INNODB") }, - { C_STRING_WITH_LEN("NDB") }, { C_STRING_WITH_LEN("NDBCLUSTER") }, { C_STRING_WITH_LEN("HEAP") }, { C_STRING_WITH_LEN("MEMORY") }, { C_STRING_WITH_LEN("MERGE") }, { C_STRING_WITH_LEN("MRG_MYISAM") }, { C_STRING_WITH_LEN("Maria") }, { C_STRING_WITH_LEN("Aria") }, @@ -1141,6 +1143,25 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg) DBUG_VOID_RETURN; } + +static int prepare_or_error(handlerton *ht, THD *thd, bool all) +{ + int err= ht->prepare(ht, thd, all); + status_var_increment(thd->status_var.ha_prepare_count); + if (err) + { + /* avoid sending error, if we're going to replay the transaction */ +#ifdef WITH_WSREP + if (ht == wsrep_hton && + err != WSREP_TRX_SIZE_EXCEEDED && + thd->wsrep_conflict_state != MUST_REPLAY) +#endif + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + } + return err; +} + + /** @retval 0 ok @@ -1158,14 +1179,11 @@ int ha_prepare(THD *thd) { for (; ha_info; ha_info= ha_info->next()) { - int err; handlerton *ht= ha_info->ht(); - status_var_increment(thd->status_var.ha_prepare_count); if (ht->prepare) { - if ((err= ht->prepare(ht, thd, all))) + if (prepare_or_error(ht, thd, all)) { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); ha_rollback_trans(thd, all); error=1; break; @@ -1366,8 +1384,9 @@ int ha_commit_trans(THD *thd, bool all) mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE, MDL_EXPLICIT); - if (thd->mdl_context.acquire_lock(&mdl_request, - thd->variables.lock_wait_timeout)) + if (!WSREP(thd) && + thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) { ha_rollback_trans(thd, all); thd->wakeup_subsequent_commits(1); @@ -1398,7 +1417,6 @@ int ha_commit_trans(THD *thd, bool all) for (Ha_trx_info *hi= ha_info; hi; hi= hi->next()) { - int err; handlerton *ht= hi->ht(); /* Do not call two-phase commit if this particular @@ -1411,12 +1429,7 @@ int ha_commit_trans(THD *thd, bool all) Sic: we know that prepare() is not NULL since otherwise trans->no_2pc would have been set. */ - err= ht->prepare(ht, thd, all); - status_var_increment(thd->status_var.ha_prepare_count); - if (err) - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - - if (err) + if (prepare_or_error(ht, thd, all)) goto err; need_prepare_ordered|= (ht->prepare_ordered != NULL); @@ -1425,6 +1438,12 @@ int ha_commit_trans(THD *thd, bool all) DEBUG_SYNC(thd, "ha_commit_trans_after_prepare"); DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE();); + if (!error && WSREP_ON && wsrep_is_wsrep_xid(&thd->transaction.xid_state.xid)) + { + // xid was rewritten by wsrep + xid= wsrep_xid_seqno(&thd->transaction.xid_state.xid); + } + if (!is_real_trans) { error= commit_one_phase_2(thd, all, trans, is_real_trans); @@ -1801,7 +1820,9 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, got, hton_name(hton)->str); for (int i=0; i < got; i ++) { - my_xid x=info->list[i].get_my_xid(); + my_xid x= WSREP_ON && wsrep_is_wsrep_xid(&info->list[i]) ? + wsrep_xid_seqno(&info->list[i]) : + info->list[i].get_my_xid(); if (!x) // not "mine" - that is generated by external TM { #ifndef DBUG_OFF @@ -3086,10 +3107,12 @@ int handler::update_auto_increment() variables->auto_increment_increment); auto_inc_intervals_count++; /* Row-based replication does not need to store intervals in binlog */ - if (mysql_bin_log.is_open() && !thd->is_current_stmt_binlog_format_row()) - thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(), - auto_inc_interval_for_cur_row.values(), - variables->auto_increment_increment); + if (((WSREP(thd) && wsrep_emulate_bin_log ) || mysql_bin_log.is_open()) + && !thd->is_current_stmt_binlog_format_row()) + thd->auto_inc_intervals_in_cur_stmt_for_binlog. + append(auto_inc_interval_for_cur_row.minimum(), + auto_inc_interval_for_cur_row.values(), + variables->auto_increment_increment); } /* @@ -4387,10 +4410,10 @@ handler::ha_rename_partitions(const char *path) /** Tell the storage engine that it is allowed to "disable transaction" in the - handler. It is a hint that ACID is not required - it is used in NDB for + handler. It is a hint that ACID is not required - it was used in NDB for ALTER TABLE, for example, when data are copied to temporary table. A storage engine may treat this hint any way it likes. NDB for example - starts to commit every now and then automatically. + started to commit every now and then automatically. This hint can be safely ignored. */ int ha_enable_transaction(THD *thd, bool on) @@ -5203,145 +5226,6 @@ int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, } -#ifdef HAVE_NDB_BINLOG -/* - TODO: change this into a dynamic struct - List<handlerton> does not work as - 1. binlog_end is called when MEM_ROOT is gone - 2. cannot work with thd MEM_ROOT as memory should be freed -*/ -#define MAX_HTON_LIST_ST 63 -struct hton_list_st -{ - handlerton *hton[MAX_HTON_LIST_ST]; - uint sz; -}; - -struct binlog_func_st -{ - enum_binlog_func fn; - void *arg; -}; - -/** @brief - Listing handlertons first to avoid recursive calls and deadlock -*/ -static my_bool binlog_func_list(THD *thd, plugin_ref plugin, void *arg) -{ - hton_list_st *hton_list= (hton_list_st *)arg; - handlerton *hton= plugin_hton(plugin); - if (hton->state == SHOW_OPTION_YES && hton->binlog_func) - { - uint sz= hton_list->sz; - if (sz == MAX_HTON_LIST_ST-1) - { - /* list full */ - return FALSE; - } - hton_list->hton[sz]= hton; - hton_list->sz= sz+1; - } - return FALSE; -} - -static my_bool binlog_func_foreach(THD *thd, binlog_func_st *bfn) -{ - hton_list_st hton_list; - uint i, sz; - - hton_list.sz= 0; - plugin_foreach(thd, binlog_func_list, - MYSQL_STORAGE_ENGINE_PLUGIN, &hton_list); - - for (i= 0, sz= hton_list.sz; i < sz ; i++) - hton_list.hton[i]->binlog_func(hton_list.hton[i], thd, bfn->fn, bfn->arg); - return FALSE; -} - -int ha_reset_logs(THD *thd) -{ - binlog_func_st bfn= {BFN_RESET_LOGS, 0}; - binlog_func_foreach(thd, &bfn); - return 0; -} - -void ha_reset_slave(THD* thd) -{ - binlog_func_st bfn= {BFN_RESET_SLAVE, 0}; - binlog_func_foreach(thd, &bfn); -} - -void ha_binlog_wait(THD* thd) -{ - binlog_func_st bfn= {BFN_BINLOG_WAIT, 0}; - binlog_func_foreach(thd, &bfn); -} - -int ha_binlog_end(THD* thd) -{ - binlog_func_st bfn= {BFN_BINLOG_END, 0}; - binlog_func_foreach(thd, &bfn); - return 0; -} - -int ha_binlog_index_purge_file(THD *thd, const char *file) -{ - binlog_func_st bfn= {BFN_BINLOG_PURGE_FILE, (void *)file}; - binlog_func_foreach(thd, &bfn); - return 0; -} - -struct binlog_log_query_st -{ - enum_binlog_command binlog_command; - const char *query; - uint query_length; - const char *db; - const char *table_name; -}; - -static my_bool binlog_log_query_handlerton2(THD *thd, - handlerton *hton, - void *args) -{ - struct binlog_log_query_st *b= (struct binlog_log_query_st*)args; - if (hton->state == SHOW_OPTION_YES && hton->binlog_log_query) - hton->binlog_log_query(hton, thd, - b->binlog_command, - b->query, - b->query_length, - b->db, - b->table_name); - return FALSE; -} - -static my_bool binlog_log_query_handlerton(THD *thd, - plugin_ref plugin, - void *args) -{ - return binlog_log_query_handlerton2(thd, plugin_hton(plugin), args); -} - -void ha_binlog_log_query(THD *thd, handlerton *hton, - enum_binlog_command binlog_command, - const char *query, uint query_length, - const char *db, const char *table_name) -{ - struct binlog_log_query_st b; - b.binlog_command= binlog_command; - b.query= query; - b.query_length= query_length; - b.db= db; - b.table_name= table_name; - if (hton == 0) - plugin_foreach(thd, binlog_log_query_handlerton, - MYSQL_STORAGE_ENGINE_PLUGIN, &b); - else - binlog_log_query_handlerton2(thd, hton, &b); -} -#endif - - /** Read first row between two ranges. Store ranges for future calls to read_range_next. @@ -5704,10 +5588,13 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) DBUG_ASSERT(table->s->cached_row_logging_check == 0 || table->s->cached_row_logging_check == 1); - return (thd->is_current_stmt_binlog_format_row() && + return thd->is_current_stmt_binlog_format_row() && table->s->cached_row_logging_check && (thd->variables.option_bits & OPTION_BIN_LOG) && - mysql_bin_log.is_open()); + /* applier and replayer should not binlog */ + ((IF_WSREP(WSREP_EMULATE_BINLOG(thd) && + thd->wsrep_exec_mode != REPL_RECV, 0)) || + mysql_bin_log.is_open()); } @@ -5807,6 +5694,11 @@ static int binlog_log_row(TABLE* table, bool error= 0; THD *const thd= table->in_use; + /* only InnoDB tables will be replicated through binlog emulation */ + if (WSREP_EMULATE_BINLOG(thd) && + table->file->partition_ht()->db_type != DB_TYPE_INNODB) + return 0; + if (check_table_binlog_row_based(thd, table)) { MY_BITMAP cols; @@ -6136,6 +6028,74 @@ void handler::set_lock_type(enum thr_lock_type lock) table->reginfo.lock_type= lock; } +#ifdef WITH_WSREP +/** + @details + This function makes the storage engine to force the victim transaction + to abort. Currently, only innodb has this functionality, but any SE + implementing the wsrep API should provide this service to support + multi-master operation. + + @param bf_thd brute force THD asking for the abort + @param victim_thd victim THD to be aborted + + @return + always 0 +*/ + +int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal) +{ + DBUG_ENTER("ha_abort_transaction"); + if (!WSREP(bf_thd) && + !(wsrep_OSU_method_options == WSREP_OSU_RSU && + bf_thd->wsrep_exec_mode == TOTAL_ORDER)) { + DBUG_RETURN(0); + } + + THD_TRANS *trans= &victim_thd->transaction.all; + Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; + + for (; ha_info; ha_info= ha_info_next) + { + handlerton *hton= ha_info->ht(); + if (!hton->abort_transaction) + WSREP_WARN("cannot abort transaction"); + else + hton->abort_transaction(hton, bf_thd, victim_thd, signal); + ha_info_next= ha_info->next(); + ha_info->reset(); /* keep it conveniently zero-filled */ + } + DBUG_RETURN(0); +} + +void ha_fake_trx_id(THD *thd) +{ + DBUG_ENTER("ha_fake_trx_id"); + if (!WSREP(thd)) + { + DBUG_VOID_RETURN; + } + + THD_TRANS *trans= &thd->transaction.all; + Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; + + for (; ha_info; ha_info= ha_info_next) + { + handlerton *hton= ha_info->ht(); + if (!hton->fake_trx_id) + { + WSREP_WARN("cannot get fake InnoDB transaction ID"); + } + else + hton->fake_trx_id(hton, thd); + ha_info_next= ha_info->next(); + ha_info->reset(); /* keep it conveniently zero-filled */ + } + DBUG_VOID_RETURN; +} +#endif /* WITH_WSREP */ + + #ifdef TRANS_LOG_MGM_EXAMPLE_CODE /* Example of transaction log management functions based on assumption that logs |