diff options
author | unknown <mats@mysql.com> | 2006-02-16 08:46:45 +0100 |
---|---|---|
committer | unknown <mats@mysql.com> | 2006-02-16 08:46:45 +0100 |
commit | 738a1ca08db810aa37069da149aa21590292caac (patch) | |
tree | 3f960f1eee8ec7454fbf52e22f645085d4b004dd /sql/log_event.cc | |
parent | 13adc06f53122cf890b8d58d21bc77a51132c80f (diff) | |
parent | 41f7d138539c01afbcb6efb2f772fae0e9c3cd05 (diff) | |
download | mariadb-git-738a1ca08db810aa37069da149aa21590292caac.tar.gz |
Merge mysql.com:/home/bkroot/mysql-5.1-new
into mysql.com:/home/bk/w3023-mysql-5.1-new
mysql-test/extra/binlog_tests/ctype_cp932.test:
Auto merged
mysql-test/r/binlog_row_blackhole.result:
Auto merged
mysql-test/r/binlog_stm_ctype_cp932.result:
Auto merged
sql/handler.cc:
Auto merged
sql/handler.h:
Auto merged
sql/lock.cc:
Auto merged
sql/log.cc:
Auto merged
sql/log.h:
Auto merged
sql/log_event.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/opt_range.cc:
Auto merged
sql/parse_file.cc:
Auto merged
sql/slave.cc:
Auto merged
sql/sql_acl.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_load.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql/table.h:
Auto merged
sql/log_event.cc:
Merge with mysql-5.1-new
Diffstat (limited to 'sql/log_event.cc')
-rw-r--r-- | sql/log_event.cc | 432 |
1 files changed, 193 insertions, 239 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc index cd16745df90..9fbb29bbc8c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1686,6 +1686,7 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, const char *query DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); clear_all_errors(thd, rli); + rli->clear_tables_to_lock(); /* Note: We do not need to execute reset_one_shot_variables() if this @@ -5064,17 +5065,19 @@ char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, MY_BITMAP const *cols, bool is_transactional) : Log_event(thd_arg, 0, is_transactional), + m_row_count(0), m_table(tbl_arg), m_table_id(tid), - m_width(tbl_arg->s->fields), - m_rows_buf((byte*)my_malloc(opt_binlog_rows_event_max_size * - sizeof(*m_rows_buf), MYF(MY_WME))), - m_rows_cur(m_rows_buf), - m_rows_end(m_rows_buf + opt_binlog_rows_event_max_size), + m_width(tbl_arg ? tbl_arg->s->fields : 1), + m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0) { - DBUG_ASSERT(m_table && m_table->s); - DBUG_ASSERT(m_table_id != ULONG_MAX); + /* + We allow a special form of dummy event when the table, and cols + are null and the table id is ULONG_MAX. + */ + DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ULONG_MAX || + !tbl_arg && !cols && tid == ULONG_MAX); if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS) set_flags(NO_FOREIGN_KEY_CHECKS_F); @@ -5085,7 +5088,11 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL, (m_width + 7) & ~7UL, false))) - memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols)); + { + /* Cols can be zero if this is a dummy binrows event */ + if (likely(cols != NULL)) + memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols)); + } else m_cols.bitmap= 0; // to not free it } @@ -5096,6 +5103,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event) : Log_event(buf, description_event), + m_row_count(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0) { DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)"); @@ -5121,8 +5129,6 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, post_start+= RW_FLAGS_OFFSET; } - DBUG_ASSERT(m_table_id != ULONG_MAX); - m_flags= uint2korr(post_start); byte const *const var_start= (const byte *)buf + common_header_len + @@ -5178,7 +5184,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DBUG_DUMP("row_data", (const char*)row_data, min(length, 32)); DBUG_ASSERT(m_rows_buf <= m_rows_cur); - DBUG_ASSERT(m_rows_buf < m_rows_end); + DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end); DBUG_ASSERT(m_rows_cur <= m_rows_end); /* The cast will always work since m_rows_cur <= m_rows_end */ @@ -5186,12 +5192,12 @@ int Rows_log_event::do_add_row_data(byte *const row_data, { my_size_t const block_size= 1024; my_ptrdiff_t const old_alloc= m_rows_end - m_rows_buf; - my_ptrdiff_t const new_alloc= - old_alloc + block_size * (length / block_size + block_size - 1); my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf; + my_ptrdiff_t const new_alloc= + block_size * ((cur_size + length) / block_size + block_size - 1); - byte* const new_buf= - (byte*)my_realloc((gptr)m_rows_buf, new_alloc, MYF(MY_WME)); + byte* const new_buf= (byte*)my_realloc((gptr)m_rows_buf, new_alloc, + MYF(MY_ALLOW_ZERO_PTR|MY_WME)); if (unlikely(!new_buf)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -5212,6 +5218,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, DBUG_ASSERT(m_rows_cur + length < m_rows_end); memcpy(m_rows_cur, row_data, length); m_rows_cur+= length; + m_row_count++; DBUG_RETURN(0); } #endif @@ -5255,10 +5262,28 @@ static char const *unpack_row(TABLE *table, int Rows_log_event::exec_event(st_relay_log_info *rli) { DBUG_ENTER("Rows_log_event::exec_event(st_relay_log_info*)"); - DBUG_ASSERT(m_table_id != ULONG_MAX); int error= 0; char const *row_start= (char const *)m_rows_buf; - TABLE* table= rli->m_table_map.get_table(m_table_id); + + /* + If m_table_id == ULONG_MAX, then we have a dummy event that does + not contain any data. In that case, we just remove all tables in + the tables_to_lock list, step the relay log position, and return + with success. + */ + if (m_table_id == ULONG_MAX) + { + /* + This one is supposed to be set: just an extra check so that + nothing strange has happened. + */ + DBUG_ASSERT(get_flags(STMT_END_F)); + + rli->clear_tables_to_lock(); + thd->clear_error(); + rli->inc_event_relay_log_pos(); + DBUG_RETURN(0); + } /* 'thd' has been set by exec_relay_log_event(), just before calling @@ -5267,85 +5292,51 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) DBUG_ASSERT(rli->sql_thd == thd); /* - lock_tables() reads the contents of thd->lex, so they must be - initialized, so we should call lex_start(); to be even safer, we call - mysql_init_query() which does a more complete set of inits. + If there is no locks taken, this is the first binrow event seen + after the table map events. We should then lock all the tables + used in the transaction and proceed with execution of the actual + event. */ - mysql_init_query(thd, NULL, 0); - - if (table) + if (!thd->lock) { + bool need_reopen= 1; /* To execute the first lap of the loop below */ + /* - table == NULL means that this table should not be - replicated (this was set up by Table_map_log_event::exec_event() which - tested replicate-* rules). + lock_tables() reads the contents of thd->lex, so they must be + initialized, so we should call lex_start(); to be even safer, we + call mysql_init_query() which does a more complete set of inits. */ - TABLE_LIST table_list; - bool need_reopen; - uint count= 1; - bzero(&table_list, sizeof(table_list)); - table_list.lock_type= TL_WRITE; - table_list.next_global= table_list.next_local= 0; - table_list.table= table; - - for ( ; ; ) - { - table_list.db= const_cast<char*>(table->s->db.str); - table_list.alias= table_list.table_name= - const_cast<char*>(table->s->table_name.str); + mysql_init_query(thd, NULL, 0); - if ((error= lock_tables(thd, &table_list, count, &need_reopen)) == 0) - break; + while ((error= lock_tables(thd, rli->tables_to_lock, + rli->tables_to_lock_count, &need_reopen))) + { if (!need_reopen) { slave_print_msg(ERROR_LEVEL, rli, error, - "Error in %s event: error during table %s.%s lock", - get_type_str(), table->s->db.str, - table->s->table_name.str); + "Error in %s event: when locking tables", + get_type_str()); DBUG_RETURN(error); } + /* - we need to store a local copy of the table names since the table object - will become invalid after close_tables_for_reopen - */ - char *db= my_strdup(table->s->db.str, MYF(MY_WME)); - char *table_name= my_strdup(table->s->table_name.str, MYF(MY_WME)); + So we need to reopen the tables. - if (db == 0 || table_name == 0) - { - /* - Since the lock_tables() failed, the table is not locked, so - we don't need to unlock them. - */ - DBUG_RETURN(HA_ERR_OUT_OF_MEM); - } + We need to flush the pending RBR event, since it keeps a + pointer to an open table. - /* - We also needs to flush the pending RBR event, since it keeps a - pointer to an open table. + ALTERNATIVE SOLUTION (not implemented): Extract a pointer to + the pending RBR event and reset the table pointer after the + tables has been reopened. - ALTERNATIVE SOLUTION: Extract a pointer to the pending RBR - event and reset the table pointer after the tables has been - reopened. + NOTE: For this new scheme there should be no pending event: + need to add code to assert that is the case. */ thd->binlog_flush_pending_rows_event(false); + close_tables_for_reopen(thd, rli->tables_to_lock); - close_tables_for_reopen(thd, &table_list); - - /* open the table again, same as in Table_map_event::exec_event */ - table_list.db= const_cast<char*>(db); - table_list.alias= table_list.table_name= const_cast<char*>(table_name); - table_list.updating= 1; - TABLE_LIST *tables= &table_list; - if ((error= open_tables(thd, &tables, &count, 0)) == 0) - { - /* reset some variables for the table list*/ - table_list.updating= 0; - /* retrieve the new table reference and update the table map */ - table= table_list.table; - error= rli->m_table_map.set_table(m_table_id, table); - } - else /* error in open_tables */ + if ((error= open_tables(thd, &rli->tables_to_lock, + &rli->tables_to_lock_count, 0))) { if (thd->query_error || thd->is_fatal_error) { @@ -5355,19 +5346,41 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) */ uint actual_error= thd->net.last_errno; slave_print_msg(ERROR_LEVEL, rli, actual_error, - "Error '%s' on reopening table `%s`.`%s`", + "Error '%s' on reopening tables", (actual_error ? thd->net.last_error : - "unexpected success or fatal error"), - db, table_name); + "unexpected success or fatal error")); thd->query_error= 1; } - } - my_free((char*) db, MYF(MY_ALLOW_ZERO_PTR)); - my_free((char*) table_name, MYF(MY_ALLOW_ZERO_PTR)); - - if (error) DBUG_RETURN(error); + } } + /* + When the open and locking succeeded, we add all the tables to + the table map and remove them from tables to lock. + */ + + TABLE_LIST *ptr= rli->tables_to_lock; + while (ptr) + { + rli->m_table_map.set_table(ptr->table_id, ptr->table); + rli->touching_table(ptr->db, ptr->table_name, ptr->table_id); + char *to_free= reinterpret_cast<char*>(ptr); + ptr= ptr->next_global; + my_free(to_free, MYF(MY_WME)); + } + rli->tables_to_lock= 0; + rli->tables_to_lock_count= 0; + } + + TABLE* table= rli->m_table_map.get_table(m_table_id); + + if (table) + { + /* + table == NULL means that this table should not be replicated + (this was set up by Table_map_log_event::exec_event() which + tested replicate-* rules). + */ /* It's not needed to set_time() but @@ -5520,52 +5533,25 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) DBUG_RETURN(error); } - if (table) + if (table && (table->s->primary_key == MAX_KEY) && !cache_stmt) { /* - As "table" is not NULL, we did a successful lock_tables(), without any - prior LOCK TABLES and are not in prelocked mode, so this assertion should - be true. - */ - DBUG_ASSERT(thd->lock); - /* - If we are here, there are more events to come which may use our mappings - and our table. So don't clear mappings or close tables, just unlock - tables. - Why don't we lock the table once for all in - Table_map_log_event::exec_event() ? Because we could have in binlog: - BEGIN; - Table_map t1 -> 1 - Write_rows to id 1 - Table_map t2 -> 2 - Write_rows to id 2 - Xid_log_event - So we cannot lock t1 when executing the first Table_map, because at that - moment we don't know we'll also have to lock t2, and all tables must be - locked at once in MySQL. + ------------ Temporary fix until WL#2975 is implemented --------- + + This event is not the last one (no STMT_END_F). If we stop now + (in case of terminate_slave_thread()), how will we restart? We + have to restart from Table_map_log_event, but as this table is + not transactional, the rows already inserted will still be + present, and idempotency is not guaranteed (no PK) so we risk + that repeating leads to double insert. So we desperately try to + continue, hope we'll eventually leave this buggy situation (by + executing the final Rows_log_event). If we are in a hopeless + wait (reached end of last relay log and nothing gets appended + there), we timeout after one minute, and notify DBA about the + problem. When WL#2975 is implemented, just remove the member + st_relay_log_info::unsafe_to_stop_at and all its occurences. */ - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; - if ((table->s->primary_key == MAX_KEY) && - !cache_stmt) - { - /* - ------------ Temporary fix until WL#2975 is implemented --------- - This event is not the last one (no STMT_END_F). If we stop now (in - case of terminate_slave_thread()), how will we restart? We have to - restart from Table_map_log_event, but as this table is not - transactional, the rows already inserted will still be present, and - idempotency is not guaranteed (no PK) so we risk that repeating leads - to double insert. So we desperately try to continue, hope we'll - eventually leave this buggy situation (by executing the final - Rows_log_event). If we are in a hopeless wait (reached end of last - relay log and nothing gets appended there), we timeout after one - minute, and notify DBA about the problem. - When WL#2975 is implemented, just remove the member - st_relay_log_info::unsafe_to_stop_at and all its occurences. - */ - rli->unsafe_to_stop_at= time(0); - } + rli->unsafe_to_stop_at= time(0); } DBUG_ASSERT(error == 0); @@ -5579,7 +5565,6 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) #ifndef MYSQL_CLIENT bool Rows_log_event::write_data_header(IO_CACHE *file) { - DBUG_ASSERT(m_table_id != ULONG_MAX); byte buf[ROWS_HEADER_LEN]; // No need to init the buffer DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { @@ -5611,16 +5596,17 @@ bool Rows_log_event::write_data_body(IO_CACHE*file) } #endif -#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) && defined(DBUG_RBR) +#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Rows_log_event::pack_info(Protocol *protocol) { - char buf[256]; - char const *const flagstr= get_flags(STMT_END_F) ? "STMT_END_F" : ""; - char const *const dbnam= m_table->s->db.str; - char const *const tblnam= m_table->s->table_name.str; - my_size_t bytes= snprintf(buf, sizeof(buf), - "%s.%s - %s", dbnam, tblnam, flagstr); - protocol->store(buf, bytes, &my_charset_bin); +#ifdef DBUG_RBR + char buf[256]; + char const *const flagstr= + get_flags(STMT_END_F) ? " flags: STMT_END_F" : ""; + my_size_t bytes= snprintf(buf, sizeof(buf), + "table_id: %lu%s", m_table_id, flagstr); + protocol->store(buf, bytes, &my_charset_bin); +#endif } #endif @@ -5763,59 +5749,6 @@ Table_map_log_event::~Table_map_log_event() } /* - Find a table based on database name and table name. - - DESCRIPTION - - Currently, only the first table of the 'table_list' is located. If the - table is found in the list of open tables for the thread, the 'table' - field of 'table_list' is filled in. - - PARAMETERS - - thd Thread structure - table_list List of tables to locate in the thd->open_tables list. - count Pointer to a variable that will be set to the number of - tables found. If the pointer is NULL, nothing will be stored. - - RETURN VALUE - - The number of tables found. - - TO DO - - Replace the list of table searches with a hash based on the combined - database and table name. The handler_tables_hash is inappropriate since - it hashes on the table alias. At the same time, the function can be - extended to handle a full list of table names, in the same spirit as - open_tables() and lock_tables(). -*/ -#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) -static uint find_tables(THD *thd, TABLE_LIST *table_list, uint *count) -{ - uint result= 0; - - /* we verify that the caller knows our limitation */ - DBUG_ASSERT(table_list->next_global == 0); - for (TABLE *table= thd->open_tables; table ; table= table->next) - { - if (strcmp(table->s->db.str, table_list->db) == 0 - && strcmp(table->s->table_name.str, table_list->table_name) == 0) - { - /* Copy the table pointer into the table list. */ - table_list->table= table; - result= 1; - break; - } - } - - if (count) - *count= result; - return result; -} -#endif - -/* Return value is an error code, one of: -1 Failure to open table [from open_tables()] @@ -5838,20 +5771,37 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) thd->query_id= next_query_id(); pthread_mutex_unlock(&LOCK_thread_count); - TABLE_LIST table_list; + TABLE_LIST *table_list; + char *db_mem, *tname_mem; + void *const memory= + my_multi_malloc(MYF(MY_WME), + &table_list, sizeof(TABLE_LIST), + &db_mem, NAME_LEN + 1, + &tname_mem, NAME_LEN + 1, + NULL); + + /* + If memory is allocated, it the pointer to it should be stored in + table_list. If this is not true, the memory will not be correctly + free:ed later. + */ + DBUG_ASSERT(memory == NULL || memory == table_list); + uint32 dummy_len; - bzero(&table_list, sizeof(table_list)); - table_list.db= const_cast<char *> - (rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); - table_list.alias= table_list.table_name= const_cast<char*>(m_tblnam); - table_list.lock_type= TL_WRITE; - table_list.next_global= table_list.next_local= 0; - table_list.updating= 1; + bzero(table_list, sizeof(*table_list)); + table_list->db = db_mem; + table_list->alias= table_list->table_name = tname_mem; + table_list->lock_type= TL_WRITE; + table_list->next_global= table_list->next_local= 0; + table_list->table_id= m_table_id; + table_list->updating= 1; + strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); + strmov(table_list->table_name, m_tblnam); int error= 0; - if (rpl_filter->db_ok(table_list.db) && - (!rpl_filter->is_on() || rpl_filter->tables_ok("", &table_list))) + if (rpl_filter->db_ok(table_list->db) && + (!rpl_filter->is_on() || rpl_filter->tables_ok("", table_list))) { /* Check if the slave is set to use SBR. If so, the slave should @@ -5877,36 +5827,32 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) be a no-op. */ uint count; - if (find_tables(thd, &table_list, &count) == 0) + /* + open_tables() reads the contents of thd->lex, so they must be + initialized, so we should call lex_start(); to be even safer, we + call mysql_init_query() which does a more complete set of inits. + */ + mysql_init_query(thd, NULL, 0); + if ((error= open_tables(thd, &table_list, &count, 0))) { - /* - open_tables() reads the contents of thd->lex, so they must be - initialized, so we should call lex_start(); to be even safer, we call - mysql_init_query() which does a more complete set of inits. - */ - mysql_init_query(thd, NULL, 0); - TABLE_LIST *tables= &table_list; - if ((error= open_tables(thd, &tables, &count, 0))) + if (thd->query_error || thd->is_fatal_error) { - if (thd->query_error || thd->is_fatal_error) - { - /* - Error reporting borrowed from Query_log_event with many excessive - simplifications (we don't honour --slave-skip-errors) - */ - uint actual_error= thd->net.last_errno; - 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); + /* + Error reporting borrowed from Query_log_event with many excessive + simplifications (we don't honour --slave-skip-errors) + */ + uint actual_error= thd->net.last_errno; + 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); } - m_table= table_list.table; + m_table= table_list->table; /* This will fail later otherwise, the 'in_use' field should be @@ -5984,19 +5930,16 @@ int Table_map_log_event::exec_event(st_relay_log_info *rli) } /* - We record in the slave's information that the number m_table_id is - mapped to the m_table object - */ - if (!error) - error= rli->m_table_map.set_table(m_table_id, m_table); - - /* - Tell the RLI that we are touching a table. - - TODO: Maybe we can combine this with the previous operation? + We record in the slave's information that the table should be + locked by linking the table into the list of tables to lock, and + tell the RLI that we are touching a table. */ if (!error) - rli->touching_table(m_dbnam, m_tblnam, m_table_id); + { + table_list->next_global= table_list->next_local= rli->tables_to_lock; + rli->tables_to_lock= table_list; + rli->tables_to_lock_count++; + } } /* @@ -6063,12 +6006,23 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file) field. */ +#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Table_map_log_event::pack_info(Protocol *protocol) { +#ifdef DBUG_RBR + char buf[256]; + my_size_t bytes= snprintf(buf, sizeof(buf), + "table_id: %lu (%s.%s)", + m_table_id, m_dbnam, m_tblnam); + protocol->store(buf, bytes, &my_charset_bin); +#else char buf[256]; - my_size_t bytes= my_snprintf(buf, sizeof(buf), "%s.%s", m_dbnam, m_tblnam); + my_size_t bytes= snprintf(buf, sizeof(buf), "%s.%s", m_dbnam, m_tblnam); protocol->store(buf, bytes, &my_charset_bin); +#endif } +#endif + #endif |