diff options
author | unknown <lars@dl145j.mysql.com> | 2006-06-19 12:14:38 +0200 |
---|---|---|
committer | unknown <lars@dl145j.mysql.com> | 2006-06-19 12:14:38 +0200 |
commit | 51b9d38194c3cbb4a01e6dcb6d6b1bd2db5f36e8 (patch) | |
tree | f7f153cbe53cd47fffdda663829590342a28861f /sql | |
parent | 9f8c532f0c7d8472cb809be6f80bb596d38b336b (diff) | |
parent | c991087c4620b3d29208c390d4afa2613e508b8f (diff) | |
download | mariadb-git-51b9d38194c3cbb4a01e6dcb6d6b1bd2db5f36e8.tar.gz |
Merge mysql.com:/users/lthalmann/bkroot/mysql-5.1-new-rpl
into mysql.com:/users/lthalmann/bk/MERGE/mysql-5.1-merge
mysql-test/t/archive.test:
Auto merged
sql/log.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_table.cc:
Auto merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/ha_innodb.cc | 199 | ||||
-rw-r--r-- | sql/handler.cc | 174 | ||||
-rw-r--r-- | sql/log.cc | 13 | ||||
-rw-r--r-- | sql/log_event.cc | 37 | ||||
-rw-r--r-- | sql/set_var.cc | 14 | ||||
-rw-r--r-- | sql/set_var.h | 9 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 31 | ||||
-rw-r--r-- | sql/sql_class.h | 40 | ||||
-rw-r--r-- | sql/sql_delete.cc | 37 | ||||
-rw-r--r-- | sql/sql_insert.cc | 65 |
11 files changed, 427 insertions, 194 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 742f9ce7631..ec504cc1266 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -135,6 +135,7 @@ extern "C" { #include "../storage/innobase/include/fil0fil.h" #include "../storage/innobase/include/trx0xa.h" #include "../storage/innobase/include/thr0loc.h" +#include "../storage/innobase/include/ha_prototypes.h" } #define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */ @@ -668,6 +669,61 @@ innobase_get_cset_width( } /********************************************************************** +Converts an identifier to a table name. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_table_id( +/*===========================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len) /* in: length of 'to', in bytes */ +{ + uint errors; + + strconvert(current_thd->charset(), from, + &my_charset_filename, to, len, &errors); +} + +/********************************************************************** +Converts an identifier to UTF-8. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_id( +/*=====================*/ + char* to, /* out: converted identifier */ + const char* from, /* in: identifier to convert */ + ulint len) /* in: length of 'to', in bytes */ +{ + uint errors; + + strconvert(current_thd->charset(), from, + system_charset_info, to, len, &errors); +} + +/********************************************************************** +Removes the filename encoding of a table or database name. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +void +innobase_convert_from_filename( +/*===========================*/ + char* s) /* in: identifier; out: decoded identifier */ +{ + uint errors; + + strconvert(&my_charset_filename, s, + system_charset_info, s, strlen(s), &errors); +} + +/********************************************************************** Compares NUL-terminated UTF-8 strings case insensitively. NOTE that the exact prototype of this function has to be in @@ -697,6 +753,21 @@ innobase_casedn_str( my_casedn_str(system_charset_info, a); } +/************************************************************************** +Determines the connection character set. + +NOTE that the exact prototype of this function has to be in +/innobase/dict/dict0dict.c! */ +extern "C" +struct charset_info_st* +innobase_get_charset( +/*=================*/ + /* out: connection character set */ + void* mysql_thd) /* in: MySQL thread handle */ +{ + return(((THD*) mysql_thd)->charset()); +} + /************************************************************************* Creates a temporary file. */ extern "C" @@ -743,6 +814,25 @@ innobase_mysql_tmpfile(void) } /************************************************************************* +Wrapper around MySQL's copy_and_convert function, see it for +documentation. */ +extern "C" +ulint +innobase_convert_string( +/*====================*/ + void* to, + ulint to_length, + CHARSET_INFO* to_cs, + const void* from, + ulint from_length, + CHARSET_INFO* from_cs, + uint* errors) +{ + return(copy_and_convert((char*)to, to_length, to_cs, + (const char*)from, from_length, from_cs, errors)); +} + +/************************************************************************* Gets the InnoDB transaction handle for a MySQL handler object, creates an InnoDB transaction struct if the corresponding MySQL thread struct still lacks one. */ @@ -1076,23 +1166,69 @@ innobase_invalidate_query_cache( } /********************************************************************* -Get the quote character to be used in SQL identifiers. +Display an SQL identifier. This definition must match the one in innobase/ut/ut0ut.c! */ extern "C" -int -mysql_get_identifier_quote_char( -/*============================*/ - /* out: quote character to be - used in SQL identifiers; EOF if none */ +void +innobase_print_identifier( +/*======================*/ + FILE* f, /* in: output stream */ trx_t* trx, /* in: transaction */ + ibool table_id,/* in: TRUE=decode table name */ const char* name, /* in: name to print */ ulint namelen)/* in: length of name */ { + const char* s = name; + char* qname = NULL; + int q; + + if (table_id) { + /* Decode the table name. The filename_to_tablename() + function expects a NUL-terminated string. The input and + output strings buffers must not be shared. The function + only produces more output when the name contains other + characters than [0-9A-Z_a-z]. */ + char* temp_name = my_malloc(namelen + 1, MYF(MY_WME)); + uint qnamelen = namelen + + (1 + sizeof srv_mysql50_table_name_prefix); + + if (temp_name) { + qname = my_malloc(qnamelen, MYF(MY_WME)); + if (qname) { + memcpy(temp_name, name, namelen); + temp_name[namelen] = 0; + s = qname; + namelen = filename_to_tablename(temp_name, + qname, qnamelen); + } + my_free(temp_name, MYF(0)); + } + } + if (!trx || !trx->mysql_thd) { - return(EOF); + + q = '"'; + } else { + q = get_quote_char_for_identifier((THD*) trx->mysql_thd, + s, (int) namelen); + } + + if (q == EOF) { + fwrite(s, 1, namelen, f); + } else { + const char* e = s + namelen; + putc(q, f); + while (s < e) { + int c = *s++; + if (c == q) { + putc(c, f); + } + putc(c, f); + } + putc(q, f); } - return(get_quote_char_for_identifier((THD*) trx->mysql_thd, - name, (int) namelen)); + + my_free(qname, MYF(MY_ALLOW_ZERO_PTR)); } /************************************************************************** @@ -1232,6 +1368,24 @@ innobase_init(void) ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR); +#ifdef UNIV_DEBUG + static const char test_filename[] = "-@"; + char test_tablename[sizeof test_filename + + sizeof srv_mysql50_table_name_prefix]; + if ((sizeof test_tablename) - 1 + != filename_to_tablename(test_filename, test_tablename, + sizeof test_tablename) + || strncmp(test_tablename, + srv_mysql50_table_name_prefix, + sizeof srv_mysql50_table_name_prefix) + || strcmp(test_tablename + + sizeof srv_mysql50_table_name_prefix, + test_filename)) { + sql_print_error("tablename encoding has been changed"); + goto error; + } +#endif /* UNIV_DEBUG */ + /* Check that values don't overflow on 32-bit systems. */ if (sizeof(ulint) == 4) { if (innobase_buffer_pool_size > UINT_MAX32) { @@ -2200,8 +2354,7 @@ ha_innobase::open( /* Get pointer to a table object in InnoDB dictionary cache */ - ib_table = dict_table_get_and_increment_handle_count( - norm_name, NULL); + ib_table = dict_table_get_and_increment_handle_count(norm_name); if (NULL == ib_table) { ut_print_timestamp(stderr); @@ -4116,6 +4269,9 @@ ha_innobase::index_prev( mysql_byte* buf) /* in/out: buffer for previous row in MySQL format */ { + statistic_increment(current_thd->status_var.ha_read_prev_count, + &LOCK_status); + return(general_fetch(buf, ROW_SEL_PREV, 0)); } @@ -4652,7 +4808,7 @@ ha_innobase::create( /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ @@ -4748,20 +4904,9 @@ ha_innobase::create( } } - if (current_thd->query != NULL) { - LEX_STRING q; - - if (thd->convert_string(&q, system_charset_info, - current_thd->query, - current_thd->query_length, - current_thd->charset())) { - error = HA_ERR_OUT_OF_MEM; - - goto cleanup; - } - + if (thd->query != NULL) { error = row_table_add_foreign_constraints(trx, - q.str, norm_name, + thd->query, norm_name, create_info->options & HA_LEX_CREATE_TMP_TABLE); error = convert_error_code_to_mysql(error, NULL); @@ -4781,7 +4926,7 @@ ha_innobase::create( log_buffer_flush_to_disk(); - innobase_table = dict_table_get(norm_name, NULL); + innobase_table = dict_table_get(norm_name); DBUG_ASSERT(innobase_table != 0); @@ -4917,7 +5062,7 @@ ha_innobase::delete_table( /* Get the transaction associated with the current thd, or create one if not yet created */ - parent_trx = check_trx_exists(current_thd); + parent_trx = check_trx_exists(thd); /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads */ diff --git a/sql/handler.cc b/sql/handler.cc index 47bcf1caba1..0895c6cf454 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -53,6 +53,8 @@ st_plugin_int *hton2plugin[MAX_HA]; static handlerton *installed_htons[128]; +#define BITMAP_STACKBUF_SIZE (128/8) + KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} }; /* static functions defined in this file */ @@ -3234,35 +3236,128 @@ namespace { } } -template<class RowsEventT> int binlog_log_row(TABLE *table, - const byte *before_record, - const byte *after_record) +/* + Write table maps for all (manually or automatically) locked tables + to the binary log. + + SYNOPSIS + write_locked_table_maps() + thd Pointer to THD structure + + DESCRIPTION + This function will generate and write table maps for all tables + that are locked by the thread 'thd'. Either manually locked + (stored in THD::locked_tables) and automatically locked (stored + in THD::lock) are considered. + + RETURN VALUE + 0 All OK + 1 Failed to write all table maps + + SEE ALSO + THD::lock + THD::locked_tables + */ +namespace { - if (table->file->is_injective()) - return 0; - bool error= 0; + int write_locked_table_maps(THD *thd) + { + DBUG_ENTER("write_locked_table_maps"); + DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p", + thd, thd->lock, thd->locked_tables)); + + if (thd->get_binlog_table_maps() == 0) + { + /* + Exactly one table has to be locked, otherwise this code is not + guaranteed to work. + */ + DBUG_ASSERT((thd->lock != NULL) + (thd->locked_tables != NULL) == 1); + + MYSQL_LOCK *lock= thd->lock ? thd->lock : thd->locked_tables; + DBUG_ASSERT(lock->table_count > 0); + TABLE **const end_ptr= lock->table + lock->table_count; + for (TABLE **table_ptr= lock->table ; + table_ptr != end_ptr ; + ++table_ptr) + { + TABLE *const table= *table_ptr; + DBUG_PRINT("info", ("Checking table %s", table->s->table_name)); + if (table->current_lock == F_WRLCK && + check_table_binlog_row_based(thd, table)) + { + int const has_trans= table->file->has_transactions(); + int const error= thd->binlog_write_table_map(table, has_trans); + /* + If an error occurs, it is the responsibility of the caller to + roll back the transaction. + */ + if (unlikely(error)) + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); + } - if (check_table_binlog_row_based(table->in_use, table)) + template<class RowsEventT> int + binlog_log_row(TABLE* table, + const byte *before_record, + const byte *after_record) { - error= - RowsEventT::binlog_row_logging_function(table->in_use, table, - table->file->has_transactions(), - &table->s->all_set, - table->s->fields, - before_record, after_record); + if (table->file->is_injective()) + return 0; + bool error= 0; + THD *const thd= table->in_use; + + if (check_table_binlog_row_based(thd, table)) + { + MY_BITMAP cols; + /* Potential buffer on the stack for the bitmap */ + uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)]; + uint n_fields= table->s->fields; + my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8; + + /* + If there are no table maps written to the binary log, this is + the first row handled in this statement. In that case, we need + to write table maps for all locked tables to the binary log. + */ + if (likely(!(error= bitmap_init(&cols, + use_bitbuf ? bitbuf : NULL, + (n_fields + 7) & ~7UL, + false)))) + { + bitmap_set_all(&cols); + if (likely(!(error= write_locked_table_maps(thd)))) + { + error= + RowsEventT::binlog_row_logging_function(thd, table, + table->file->has_transactions(), + &cols, table->s->fields, + before_record, after_record); + } + if (!use_bitbuf) + bitmap_free(&cols); + } + } + return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } - return error ? HA_ERR_RBR_LOGGING_FAILED : 0; -} + /* + Instantiate the versions we need for the above template function, + because we have -fno-implicit-template as compiling option. + */ -/* - Instantiate the versions we need for the above template function, because we - have -fno-implicit-template as compiling option. -*/ + template int + binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *); + + template int + binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Write_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Delete_rows_log_event>(TABLE *, const byte *, const byte *); -template int binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *); + template int + binlog_log_row<Update_rows_log_event>(TABLE *, const byte *, const byte *); +} #endif /* HAVE_ROW_BASED_REPLICATION */ @@ -3272,41 +3367,6 @@ int handler::ha_external_lock(THD *thd, int lock_type) int error; if (unlikely(error= external_lock(thd, lock_type))) DBUG_RETURN(error); -#ifdef HAVE_ROW_BASED_REPLICATION - if (table->file->is_injective()) - DBUG_RETURN(0); - - /* - There is a number of statements that are logged statement-based - but call external lock. For these, we do not need to generate a - table map. - - TODO: The need for this switch is an indication that the model for - locking combined with row-based replication needs to be looked - over. Ideally, no such special handling should be needed. - */ - switch (thd->lex->sql_command) { - case SQLCOM_TRUNCATE: - case SQLCOM_ALTER_TABLE: - case SQLCOM_OPTIMIZE: - case SQLCOM_REPAIR: - DBUG_RETURN(0); - default: - break; - } - - /* - If we are locking a table for writing, we generate a table map. - For all other kinds of locks, we don't do anything. - */ - if (lock_type == F_WRLCK && check_table_binlog_row_based(thd, table)) - { - int const has_trans= table->file->has_transactions(); - error= thd->binlog_write_table_map(table, has_trans); - if (unlikely(error)) - DBUG_RETURN(error); - } -#endif DBUG_RETURN(0); } diff --git a/sql/log.cc b/sql/log.cc index d05a8e4202f..11ee2dd58c9 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -71,11 +71,17 @@ sql_print_message_func sql_print_message_handlers[3] = struct binlog_trx_data { bool empty() const { +#ifdef HAVE_ROW_BASED_REPLICATION return pending == NULL && my_b_tell(&trans_log) == 0; +#else + return my_b_tell(&trans_log) == 0; +#endif } binlog_trx_data() {} IO_CACHE trans_log; // The transaction cache +#ifdef HAVE_ROW_BASED_REPLICATION Rows_log_event *pending; // The pending binrows event +#endif }; handlerton binlog_hton; @@ -1073,6 +1079,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev) #endif error= mysql_bin_log.write(thd, trans_log, end_ev); } +#ifdef HAVE_ROW_BASED_REPLICATION else { #ifdef HAVE_ROW_BASED_REPLICATION @@ -1092,6 +1099,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, Log_event *end_ev) transaction cache. */ mysql_bin_log.update_table_map_version(); +#endif statistic_increment(binlog_cache_use, &LOCK_status); if (trans_log->disk_writes != 0) @@ -2919,7 +2927,6 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) } -#ifdef HAVE_ROW_BASED_REPLICATION /* These functions are placed in this file since they need access to binlog_hton, which has internal linkage. @@ -2955,6 +2962,7 @@ int THD::binlog_setup_trx_data() engine has registered for the transaction. */ +#ifdef HAVE_ROW_BASED_REPLICATION int THD::binlog_write_table_map(TABLE *table, bool is_trans) { int error; @@ -3133,9 +3141,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) we are inside a stored function, we do not end the statement since this will close all tables on the slave. */ +#ifdef HAVE_ROW_BASED_REPLICATION bool const end_stmt= thd->prelocked_mode && thd->lex->requires_prelocking(); -#ifdef HAVE_ROW_BASED_REPLICATION thd->binlog_flush_pending_rows_event(end_stmt); #endif /*HAVE_ROW_BASED_REPLICATION*/ @@ -3186,7 +3194,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) (binlog_trx_data*) thd->ha_data[binlog_hton.slot]; IO_CACHE *trans_log= &trx_data->trans_log; bool trans_log_in_use= my_b_tell(trans_log) != 0; - if (event_info->get_cache_stmt() && !trans_log_in_use) trans_register_ha(thd, (thd->options & diff --git a/sql/log_event.cc b/sql/log_event.cc index 78ab54aeb79..bf876366879 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5595,6 +5595,7 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) bool Rows_log_event::write_data_header(IO_CACHE *file) { byte buf[ROWS_HEADER_LEN]; // No need to init the buffer + DBUG_ASSERT(m_table_id != ~0UL); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { int4store(buf + 0, m_table_id); @@ -5845,9 +5846,7 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* Open the table if it is not already open and add the table to table map. - If the table should not be replicated, we don't bother to do anything. - The table map will return NULL and the row-level event will effectively - be a no-op. + Note that for any table that should not be replicated, a filter is needed. */ uint count; /* @@ -5863,34 +5862,14 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) /* Error reporting borrowed from Query_log_event with many excessive simplifications (we don't honour --slave-skip-errors) - - BUG: There can be extreneous table maps in the binary log, - so in case we fail to open the table, we just generate a - warning and do not add the table to the list of tables to - open and lock. */ uint actual_error= thd->net.last_errno; - switch (actual_error) - { - case ER_NO_SUCH_TABLE: - slave_print_msg(WARNING_LEVEL, rli, actual_error, - thd->net.last_error ? - thd->net.last_error : - "<no message>"); - clear_all_errors(thd, rli); - rli->inc_event_relay_log_pos(); // Continue with next event - error= 0; - break; - - default: - slave_print_msg(ERROR_LEVEL, rli, actual_error, - "Error '%s' on opening table `%s`.`%s`", - (actual_error ? thd->net.last_error : - "unexpected success or fatal error"), - table_list->db, table_list->table_name); - thd->query_error= 1; - break; - } + slave_print_msg(ERROR_LEVEL, rli, actual_error, + "Error '%s' on opening table `%s`.`%s`", + (actual_error ? thd->net.last_error : + "unexpected success or fatal error"), + table_list->db, table_list->table_name); + thd->query_error= 1; } DBUG_RETURN(error); } diff --git a/sql/set_var.cc b/sql/set_var.cc index a44395c74ca..acd17a07f78 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1301,15 +1301,20 @@ bool sys_var_thd_binlog_format::is_readonly() const if global or not here. And this test will also prevent switching from RBR to RBR (a no-op which should not happen too often). + + If we don't have row-based replication compiled in, the variable + is always read-only. */ -#ifdef HAVE_ROW_BASED_REPLICATION +#ifndef HAVE_ROW_BASED_REPLICATION + my_error(ER_RBR_NOT_AVAILABLE, MYF(0)); + return 1; +#else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW) && thd->temporary_tables) { my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0)); return 1; } -#endif /*HAVE_ROW_BASED_REPLICATION*/ /* if in a stored function, it's too late to change mode */ @@ -1327,10 +1332,12 @@ bool sys_var_thd_binlog_format::is_readonly() const my_error(ER_NDB_CANT_SWITCH_BINLOG_FORMAT, MYF(0)); return 1; } -#endif +#endif /* HAVE_NDB_BINLOG */ +#endif /* HAVE_ROW_BASED_REPLICATION */ return sys_var_thd_enum::is_readonly(); } + void fix_binlog_format_after_update(THD *thd, enum_var_type type) { #ifdef HAVE_ROW_BASED_REPLICATION @@ -1338,6 +1345,7 @@ void fix_binlog_format_after_update(THD *thd, enum_var_type type) #endif /*HAVE_ROW_BASED_REPLICATION*/ } + static void fix_max_binlog_size(THD *thd, enum_var_type type) { DBUG_ENTER("fix_max_binlog_size"); diff --git a/sql/set_var.h b/sql/set_var.h index 1049b154d47..5e264c68337 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -881,15 +881,20 @@ public: byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); }; +#ifdef HAVE_ROW_BASED_REPLICATION extern void fix_binlog_format_after_update(THD *thd, enum_var_type type); +#endif class sys_var_thd_binlog_format :public sys_var_thd_enum { public: sys_var_thd_binlog_format(const char *name_arg, ulong SV::*offset_arg) :sys_var_thd_enum(name_arg, offset_arg, - &binlog_format_typelib, - fix_binlog_format_after_update) + &binlog_format_typelib +#ifdef HAVE_ROW_BASED_REPLICATION + , fix_binlog_format_after_update +#endif + ) {}; bool is_readonly() const; }; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 421da4d40b5..01fcfd1adf9 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5835,3 +5835,5 @@ ER_EVENT_SET_VAR_ERROR ER_PARTITION_MERGE_ERROR eng "%s handler cannot be used in partitioned tables" swe "%s kan inte användas i en partitionerad tabell" +ER_RBR_NOT_AVAILABLE + eng "The server was not built with row-based replication" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0a9e6472d9f..23603afc038 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2134,7 +2134,9 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) && !current_stmt_binlog_row_based) + { options&= ~OPTION_BIN_LOG; + } /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -2642,31 +2644,6 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end) error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); } - else if (stmt_end && binlog_table_maps > 0) - { /* there is no pending event at this point */ - /* - If pending is null and we are going to end the statement, we - have to write an extra, empty, binrow event so that the slave - knows to discard the tables it has received. Otherwise, the - table maps written this far will be included in the table maps - for the following statement. - - TODO: Remove the need for a dummy event altogether. It can be - fixed if we can write table maps to a memory buffer before - writing the first binrow event. We can then flush and clear the - memory buffer with table map events before writing the first - binrow event. In the event of a crash, nothing is lost since - the table maps are only needed if there are binrow events. - */ - - Rows_log_event *ev= - new Write_rows_log_event(this, 0, ~0UL, 0, FALSE); - ev->set_flags(Rows_log_event::STMT_END_F); - binlog_set_pending_rows_event(ev); - - error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0); - binlog_table_maps= 0; - } DBUG_RETURN(error); } @@ -2724,6 +2701,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, to how you treat this. */ case THD::ROW_QUERY_TYPE: +#ifdef HAVE_ROW_BASED_REPLICATION if (current_stmt_binlog_row_based) { /* @@ -2744,6 +2722,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, #endif /*HAVE_ROW_BASED_REPLICATION*/ DBUG_RETURN(0); } +#endif /* Otherwise, we fall through */ case THD::STMT_QUERY_TYPE: /* @@ -2752,7 +2731,9 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, */ { Query_log_event qinfo(this, query, query_len, is_trans, suppress_use); +#ifdef HAVE_ROW_BASED_REPLICATION qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F; +#endif /* Binlog table maps will be irrelevant after a Query_log_event (they are just removed on the slave side) so after the query diff --git a/sql/sql_class.h b/sql/sql_class.h index 723dad715bd..b6283b6d174 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -908,8 +908,10 @@ public: /* container for handler's private per-connection data */ void *ha_data[MAX_HA]; -#ifdef HAVE_ROW_BASED_REPLICATION #ifndef MYSQL_CLIENT + int binlog_setup_trx_data(); + +#ifdef HAVE_ROW_BASED_REPLICATION /* Public interface to write RBR events to the binlog @@ -939,7 +941,6 @@ public: RowsEventT* hint); Rows_log_event* binlog_get_pending_rows_event() const; void binlog_set_pending_rows_event(Rows_log_event* ev); - int binlog_setup_trx_data(); my_size_t max_row_length_blob(TABLE* table, const byte *data) const; my_size_t max_row_length(TABLE* table, const byte *data) const @@ -960,12 +961,15 @@ public: private: uint binlog_table_maps; // Number of table maps currently in the binlog - public: - -#endif + uint get_binlog_table_maps() const { + return binlog_table_maps; + } #endif /* HAVE_ROW_BASED_REPLICATION */ +#endif /* MYSQL_CLIENT */ + #ifndef MYSQL_CLIENT +public: enum enum_binlog_query_type { /* The query can be logged row-based or statement-based @@ -1403,18 +1407,26 @@ public: inline void set_current_stmt_binlog_row_based_if_mixed() { if (variables.binlog_format == BINLOG_FORMAT_MIXED) - current_stmt_binlog_row_based= 1; + current_stmt_binlog_row_based= TRUE; } inline void set_current_stmt_binlog_row_based() { - current_stmt_binlog_row_based= 1; + current_stmt_binlog_row_based= TRUE; + } + inline void clear_current_stmt_binlog_row_based() + { + current_stmt_binlog_row_based= FALSE; } +#endif inline void reset_current_stmt_binlog_row_based() { - current_stmt_binlog_row_based= test(variables.binlog_format == - BINLOG_FORMAT_ROW); +#ifdef HAVE_ROW_BASED_REPLICATION + current_stmt_binlog_row_based= + test(variables.binlog_format == BINLOG_FORMAT_ROW); +#else + current_stmt_binlog_row_based= FALSE; +#endif } -#endif /*HAVE_ROW_BASED_REPLICATION*/ }; @@ -1579,6 +1591,9 @@ class select_insert :public select_result_interceptor { bool send_eof(); /* not implemented: select_insert is never re-used in prepared statements */ void cleanup(); + +protected: + MYSQL_LOCK *lock; }; @@ -1588,7 +1603,6 @@ class select_create: public select_insert { List<create_field> *extra_fields; List<Key> *keys; HA_CREATE_INFO *create_info; - MYSQL_LOCK *lock; Field **field; public: select_create (TABLE_LIST *table, @@ -1598,10 +1612,10 @@ public: List<Item> &select_fields,enum_duplicates duplic, bool ignore) :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table), extra_fields(&fields_par),keys(&keys_par), - create_info(create_info_par), - lock(0) + create_info(create_info_par) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); + void binlog_show_create_table(TABLE **tables, uint count); void store_values(List<Item> &values); void send_error(uint errcode,const char *err); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index dd4748bc15c..73a0a9a69c2 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -42,8 +42,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ha_rows deleted= 0; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; - bool ha_delete_all_rows= 0; - ulonglong const saved_options= thd->options; DBUG_ENTER("mysql_delete"); if (open_and_lock_tables(thd, table_list)) @@ -75,20 +73,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, Test if the user wants to delete all rows and deletion doesn't have any side-effects (because of triggers), so we can use optimized handler::delete_all_rows() method. + + If row-based replication is used, we also delete the table row by + row. */ if (!using_limit && const_cond && (!conds || conds->val_int()) && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && - !(table->triggers && table->triggers->has_delete_triggers())) + !(table->triggers && table->triggers->has_delete_triggers()) && + !thd->current_stmt_binlog_row_based) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); ha_rows const maybe_deleted= table->file->stats.records; - /* - If all rows shall be deleted, we (almost) always log this - statement-based (see [binlog], below), so we set this flag and - test it below. - */ - ha_delete_all_rows= 1; + DBUG_PRINT("debug", ("Trying to use delete_all_rows()")); if (!(error=table->file->delete_all_rows())) { error= -1; // ok @@ -218,13 +215,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, thd->proc_info="updating"; will_batch= !table->file->start_bulk_delete(); - /* - We saved the thread options above before clearing the - OPTION_BIN_LOG, and will restore below, effectively disabling the - binary log (unless it was already disabled, of course). - */ - if (ha_delete_all_rows) - thd->options&= ~static_cast<ulonglong>(OPTION_BIN_LOG); table->mark_columns_needed_for_delete(); @@ -318,12 +308,6 @@ cleanup: delete select; transactional_table= table->file->has_transactions(); - /* - Restore the saved value of the OPTION_BIN_LOG bit in the thread - options before executing binlog_query() below. - */ - thd->options|= (saved_options & OPTION_BIN_LOG); - /* See similar binlogging code in sql_update.cc, for comments */ if ((error < 0) || (deleted && !transactional_table)) { @@ -338,11 +322,7 @@ cleanup: statement-based; otherwise, 'ha_delete_row()' was used to delete specific rows which we might log row-based. */ - THD::enum_binlog_query_type const - query_type(ha_delete_all_rows && !table->file->is_injective() ? - THD::STMT_QUERY_TYPE : - THD::ROW_QUERY_TYPE); - int log_result= thd->binlog_query(query_type, + int log_result= thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, transactional_table, FALSE); @@ -1004,6 +984,9 @@ trunc_by_del: thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); ha_enable_transaction(thd, FALSE); mysql_init_select(thd->lex); +#ifdef HAVE_ROW_BASED_REPLICATION + thd->clear_current_stmt_binlog_row_based(); +#endif error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, HA_POS_ERROR, LL(0), TRUE); ha_enable_transaction(thd, TRUE); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f1f97400283..aea9aba41ec 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2191,6 +2191,7 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par, bool ignore_check_option_errors) :table_list(table_list_par), table(table_par), fields(fields_par), last_insert_id(0), + lock(0), insert_into_view(table_list_par && table_list_par->view != 0) { bzero((char*) &info,sizeof(info)); @@ -2378,7 +2379,36 @@ bool select_insert::send_data(List<Item> &values) DBUG_RETURN(1); } } - if (!(error= write_record(thd, table, &info))) + + /* + The thd->lock lock contain the locks for the select part of the + statement and the 'lock' variable contain the write lock for the + currently locked table that is being created or inserted + into. However, the row-based replication will investigate the + thd->lock to decide what table maps are to be written, so this one + has to contain the tables locked for writing. To be able to write + table map for the table being created, we temporarily set + THD::lock to select_insert::lock while writing the record to the + storage engine. We cannot set this elsewhere, since the execution + of a stored function inside the select expression might cause the + lock structures to be NULL. + */ + + { + MYSQL_LOCK *saved_lock= NULL; + if (lock) + { + saved_lock= thd->lock; + thd->lock= lock; + } + + error= write_record(thd, table, &info); + + if (lock) + thd->lock= saved_lock; + } + + if (!error) { if (table->triggers || info.handle_duplicates == DUP_UPDATE) { @@ -2723,15 +2753,34 @@ private: }; -int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) +int +select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { - MY_HOOKS hooks(this); DBUG_ENTER("select_create::prepare"); + TABLEOP_HOOKS *hook_ptr= NULL; +#ifdef HAVE_ROW_BASED_REPLICATION + class MY_HOOKS : public TABLEOP_HOOKS { + public: + MY_HOOKS(select_create *x) : ptr(x) { } + virtual void do_prelock(TABLE **tables, uint count) + { + if (ptr->get_thd()->current_stmt_binlog_row_based) + ptr->binlog_show_create_table(tables, count); + } + + private: + select_create *ptr; + }; + + MY_HOOKS hooks(this); + hook_ptr= &hooks; +#endif + unit= u; if (!(table= create_table_from_items(thd, create_info, create_table, extra_fields, keys, &values, &lock, - &hooks))) + hook_ptr))) DBUG_RETURN(-1); // abort() deletes table if (table->s->fields < values.elements) @@ -2767,7 +2816,9 @@ int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } -void select_create::binlog_show_create_table(TABLE **tables, uint count) +#ifdef HAVE_ROW_BASED_REPLICATION +void +select_create::binlog_show_create_table(TABLE **tables, uint count) { /* Note 1: In RBR mode, we generate a CREATE TABLE statement for the @@ -2786,9 +2837,7 @@ void select_create::binlog_show_create_table(TABLE **tables, uint count) schema that will do a close_thread_tables(), destroying the statement transaction cache. */ -#ifdef HAVE_ROW_BASED_REPLICATION DBUG_ASSERT(thd->current_stmt_binlog_row_based); -#endif DBUG_ASSERT(tables && *tables && count > 0); char buf[2048]; @@ -2808,7 +2857,7 @@ void 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) { |