diff options
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r-- | sql/sql_insert.cc | 103 |
1 files changed, 75 insertions, 28 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index ccacd71a614..db89cc091d4 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2277,7 +2277,6 @@ bool delayed_insert::handle_inserts(void) thd.proc_info=0; pthread_mutex_unlock(&mutex); -#ifdef HAVE_ROW_BASED_REPLICATION /* We need to flush the pending event when using row-based replication since the flushing normally done in binlog_query() is @@ -2292,7 +2291,6 @@ bool delayed_insert::handle_inserts(void) */ if (thd.current_stmt_binlog_row_based) thd.binlog_flush_pending_rows_event(TRUE); -#endif /* HAVE_ROW_BASED_REPLICATION */ if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) { // This shouldn't happen @@ -2644,8 +2642,7 @@ void select_insert::send_error(uint errcode,const char *err) If the creation of the table failed (due to a syntax error, for example), no table will have been opened and therefore 'table' will be NULL. In that case, we still need to execute the rollback - and the end of the function to truncate the binary log, but we can - skip all the intermediate steps. + and the end of the function. */ if (table) { @@ -2676,10 +2673,8 @@ void select_insert::send_error(uint errcode,const char *err) if (!table->file->has_transactions()) { if (mysql_bin_log.is_open()) - { thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, table->file->has_transactions(), FALSE); - } if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table && !can_rollback_data()) thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; @@ -2948,7 +2943,24 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_ENTER("select_create::prepare"); TABLEOP_HOOKS *hook_ptr= NULL; -#ifdef HAVE_ROW_BASED_REPLICATION + /* + For row-based replication, the CREATE-SELECT statement is written + in two pieces: the first one contain the CREATE TABLE statement + necessary to create the table and the second part contain the rows + that should go into the table. + + For non-temporary tables, the start of the CREATE-SELECT + implicitly commits the previous transaction, and all events + forming the statement will be stored the transaction cache. At end + of the statement, the entire statement is committed as a + transaction, and all events are written to the binary log. + + On the master, the table is locked for the duration of the + statement, but since the CREATE part is replicated as a simple + statement, there is no way to lock the table for accesses on the + slave. Hence, we have to hold on to the CREATE part of the + statement until the statement has finished. + */ class MY_HOOKS : public TABLEOP_HOOKS { public: MY_HOOKS(select_create *x) : ptr(x) { } @@ -2958,7 +2970,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { TABLE const *const table = *tables; if (ptr->get_thd()->current_stmt_binlog_row_based && - table->s->tmp_table == NO_TMP_TABLE && + !table->s->tmp_table && !ptr->get_create_info()->table_existed) { ptr->binlog_show_create_table(tables, count); @@ -2970,22 +2982,19 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) MY_HOOKS hooks(this); hook_ptr= &hooks; -#endif unit= u; -#ifdef HAVE_ROW_BASED_REPLICATION /* - Start a statement transaction before the create if we are creating - a non-temporary table and are using row-based replication for the - statement. + Start a statement transaction before the create if we are using + row-based replication for the statement. If we are creating a + temporary table, we need to start a statement transaction. */ if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0 && thd->current_stmt_binlog_row_based) { thd->binlog_start_trans_and_stmt(); } -#endif if (!(table= create_table_from_items(thd, create_info, create_table, extra_fields, keys, &values, @@ -3029,8 +3038,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_RETURN(0); } - -#ifdef HAVE_ROW_BASED_REPLICATION void select_create::binlog_show_create_table(TABLE **tables, uint count) { @@ -3071,7 +3078,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) /* is_trans */ TRUE, /* suppress_use */ FALSE); } -#endif // HAVE_ROW_BASED_REPLICATION void select_create::store_values(List<Item> &values) { @@ -3082,13 +3088,35 @@ void select_create::store_values(List<Item> &values) void select_create::send_error(uint errcode,const char *err) { + DBUG_ENTER("select_create::send_error"); + + DBUG_PRINT("info", + ("Current statement %s row-based", + thd->current_stmt_binlog_row_based ? "is" : "is NOT")); + DBUG_PRINT("info", + ("Current table (at 0x%lu) %s a temporary (or non-existant) table", + table, + table && !table->s->tmp_table ? "is NOT" : "is")); + DBUG_PRINT("info", + ("Table %s prior to executing this statement", + get_create_info()->table_existed ? "existed" : "did not exist")); + /* - Disable binlog, because we "roll back" partial inserts in ::abort - by removing the table, even for non-transactional tables. + This will execute any rollbacks that are necessary before writing + the transcation cache. + + We disable the binary log since nothing should be written to the + binary log. This disabling is important, since we potentially do + a "roll back" of non-transactional tables by removing the table, + and the actual rollback might generate events that should not be + written to the binary log. + */ tmp_disable_binlog(thd); select_insert::send_error(errcode, err); reenable_binlog(thd); + + DBUG_VOID_RETURN; } @@ -3099,6 +3127,14 @@ bool select_create::send_eof() abort(); else { + /* + Do an implicit commit at end of statement for non-temporary + tables. This can fail, but we should unlock the table + nevertheless. + */ + if (!table->s->tmp_table) + ha_commit(thd); // Can fail, but we proceed anyway + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); VOID(pthread_mutex_lock(&LOCK_open)); @@ -3117,12 +3153,31 @@ bool select_create::send_eof() void select_create::abort() { + DBUG_ENTER("select_create::abort"); VOID(pthread_mutex_lock(&LOCK_open)); + + /* + We roll back the statement, including truncating the transaction + cache of the binary log, if the statement failed. + + We roll back the statement prior to deleting the table and prior + to releasing the lock on the table, since there might be potential + for failure if the rollback is executed after the drop or after + unlocking the table. + + We also roll back the statement regardless of whether the creation + of the table succeeded or not, since we need to reset the binary + log state. + */ + if (thd->current_stmt_binlog_row_based) + ha_rollback_stmt(thd); + if (thd->extra_lock) { mysql_unlock_tables(thd, thd->extra_lock); thd->extra_lock=0; } + if (table) { table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); @@ -3134,17 +3189,8 @@ void select_create::abort() table->s->version= 0; hash_delete(&open_cache,(byte*) table); if (!create_info->table_existed) - { quick_rm_table(table_type, create_table->db, create_table->table_name, 0); - /* - We roll back the statement, including truncating the - transaction cache of the binary log, if the statement - failed. - */ - if (thd->current_stmt_binlog_row_based) - ha_rollback_stmt(thd); - } /* Tell threads waiting for refresh that something has happened */ if (version != refresh_version) broadcast_refresh(); @@ -3154,6 +3200,7 @@ void select_create::abort() table=0; // Safety } VOID(pthread_mutex_unlock(&LOCK_open)); + DBUG_VOID_RETURN; } |