Below is the list of changes that have just been committed into a local
5.1 repository of rafal. When rafal does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-05-05 13:35:44+02:00, rafal@quant.(none) +10 -0
BUG#21842 (Cluster fails to replicate to innodb or myisam with err
134 using TPC-B):
This is a preliminary patch in preparation for the bug fix. The main
change is to make unpack_row() function non-destructive. That is, if
a column is not present in the row it will be left as it is in
the record to which we unpack (table->record[0]). If a caller of
unpack_row() wants the missing columns to be initialized with default
values, it must do it itself. Function prepare_record() is added for
that purpose.
Other changes in this changeset:
- Change signature of unpack_row(): don't report errors and don't
setup table's rw_set here.
- In Rows_log_event and derived classes, don't pass arguments to
the execution primitives (do_...() member functions) but use class
members instead.
- Factor-out code used for opening tables in a Rows event to
a separate method open_and_lock_tables().
- Change the way errors are reported when filling fields with default
values. Now user can see correct error number in SHOW SLAVE STATUS
output.
- The changes seems to fix rpl_ndb_extraCol test. Before it produced
results different than the same test run on other storage engines.
Now the results are identical. The result file is updated
accordingly.
mysql-test/r/rpl_ndb_extraCol.result@stripped, 2007-05-05 13:35:39+02:00, rafal@quant.(none)
+27 -27
Correct results. Now the results of the NDB test are the same as for
other engines.
mysql-test/r/rpl_row_tabledefs_2myisam.result@stripped, 2007-05-05 13:35:39+02:00,
rafal@quant.(none) +1 -1
Correct reported error code.
mysql-test/r/rpl_row_tabledefs_3innodb.result@stripped, 2007-05-05 13:35:39+02:00,
rafal@quant.(none) +1 -1
Correct reported error code.
sql/field_conv.cc@stripped, 2007-05-05 13:35:39+02:00, rafal@quant.(none) +3 -3
Fixing indenation
sql/log_event.cc@stripped, 2007-05-05 13:35:39+02:00, rafal@quant.(none) +331 -253
- Spacing fixes.
- Initialization of new Rows_log_event members.
- Move table opening and locking code from
Rows_log_event::do_apply_event() to a new open_and_lock_tables()
method.
- Move initialization of tables write/read sets outside the rows
processing loop (and out of unpack_row() function).
- Changes in documentation.
sql/log_event.h@stripped, 2007-05-05 13:35:40+02:00, rafal@quant.(none) +45 -29
- Add Rows_log_event members describing the row being processed.
- Add open_and_lock_tables() method.
- change signatures of do_...() methods (replace method arguments by
class members).
sql/rpl_record.cc@stripped, 2007-05-05 13:35:40+02:00, rafal@quant.(none) +45 -31
- Changed signature of unpack_row.
- Make unpack_row non-destructive for columns not present in the row.
- Move initialization of a record with default values to a separate
function prepare_record().
- Don't fill read/write set here as this can be done outside.
- Change the way errors are reported when filling default values
(no need for rli structure).
sql/rpl_record.h@stripped, 2007-05-05 13:35:40+02:00, rafal@quant.(none) +5 -5
- Change signature of unpack_row().
- Declare prepare_record().
sql/rpl_utility.cc@stripped, 2007-05-05 13:35:40+02:00, rafal@quant.(none) +0 -0
Cancel cset 1.3.2.1 which added function name to error messages - it
should not be there.
sql/slave.cc@stripped, 2007-05-05 13:35:40+02:00, rafal@quant.(none) +6 -1
Added comment.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: rafal
# Host: quant.(none)
# Root: /ext/mysql/bk/mysql-5.1-bug21842
--- 1.70/sql/field_conv.cc 2007-05-05 13:35:51 +02:00
+++ 1.71/sql/field_conv.cc 2007-05-05 13:35:51 +02:00
@@ -557,11 +557,11 @@
to_null_ptr= to->null_ptr;
to_bit= to->null_bit;
if (from_null_ptr)
- do_copy= do_copy_null;
+ do_copy= do_copy_null;
else
{
- null_row= &from->table->null_row;
- do_copy= do_outer_field_null;
+ null_row= &from->table->null_row;
+ do_copy= do_outer_field_null;
}
}
else
--- 1.277/sql/log_event.cc 2007-05-05 13:35:51 +02:00
+++ 1.278/sql/log_event.cc 2007-05-05 13:35:51 +02:00
@@ -598,7 +598,7 @@
const char *event_type;
if (p)
log_name = p + 1;
-
+
protocol->prepare_for_resend();
protocol->store(log_name, &my_charset_bin);
protocol->store((ulonglong) pos);
@@ -1427,7 +1427,7 @@
start+= 4;
}
*/
-
+
/* Store length of status variables */
status_vars_len= (uint) (start-start_of_status);
DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
@@ -1450,7 +1450,7 @@
/*
Query_log_event::Query_log_event()
-
+
The simplest constructor that could possibly work. This is used for
creating static objects that have a special meaning and are invisible
to the log.
@@ -1494,7 +1494,7 @@
db_len = (db) ? (uint32) strlen(db) : 0;
if (thd_arg->variables.collation_database != thd_arg->db_charset)
charset_database_number= thd_arg->variables.collation_database->number;
-
+
/*
If we don't use flags2 for anything else than options contained in
thd->options, it would be more efficient to flags2=thd_arg->options
@@ -1579,7 +1579,7 @@
post_header_len= description_event->post_header_len[event_type-1];
DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d",
event_len, common_header_len, post_header_len));
-
+
/*
We test if the event's length is sensible, and if so we compute data_len.
We cannot rely on QUERY_HEADER_LEN here as it would not be format-tolerant.
@@ -1589,7 +1589,7 @@
DBUG_VOID_RETURN;
data_len = event_len - (common_header_len + post_header_len);
buf+= common_header_len;
-
+
slave_proxy_id= thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET);
exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
db_len = (uint)buf[Q_DB_LEN_OFFSET]; // TODO: add a check of all *_len vars
@@ -1616,7 +1616,7 @@
*/
/* variable-part: the status vars; only in MySQL 5.0 */
-
+
start= (Log_event::Byte*) (buf+post_header_len);
end= (const Log_event::Byte*) (start+status_vars_len);
for (const Log_event::Byte* pos= start; pos < end;)
@@ -1681,7 +1681,7 @@
pos= (const uchar*) end; // Break loop
}
}
-
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
time_zone_len + 1 +
@@ -2051,7 +2051,7 @@
}
else
thd->variables.collation_database= thd->db_charset;
-
+
/* Execute the query (note that we bypass dispatch_command()) */
mysql_parse(thd, thd->query, thd->query_length);
@@ -2920,7 +2920,7 @@
sql_ex.escaped_len = (uint8) ex->escaped->length();
sql_ex.opt_flags = 0;
sql_ex.cached_new_format = -1;
-
+
if (ex->dumpfile)
sql_ex.opt_flags|= DUMPFILE_FLAG;
if (ex->opt_enclosed)
@@ -2949,7 +2949,7 @@
sql_ex.empty_flags |= LINE_START_EMPTY;
if (!ex->escaped->length())
sql_ex.empty_flags |= ESCAPED_EMPTY;
-
+
skip_lines = ex->skip_lines;
List_iterator<Item> li(fields_arg);
@@ -3032,7 +3032,7 @@
buf_end,
buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
DBUG_RETURN(1);
-
+
data_len = event_len - body_offset;
if (num_fields > data_len) // simple sanity check against corruption
DBUG_RETURN(1);
@@ -3087,7 +3087,7 @@
!commented)
memcpy(print_event_info->db, db, db_len + 1);
}
-
+
if (db && db[0] && different_db)
my_b_printf(&cache, "%suse %s%s\n",
commented ? "# " : "",
@@ -3107,7 +3107,7 @@
my_b_printf(&cache," REPLACE ");
else if (sql_ex.opt_flags & IGNORE_FLAG)
my_b_printf(&cache," IGNORE ");
-
+
my_b_printf(&cache, "INTO TABLE `%s`", table_name);
my_b_printf(&cache, " FIELDS TERMINATED BY ");
pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len);
@@ -3116,10 +3116,10 @@
my_b_printf(&cache," OPTIONALLY ");
my_b_printf(&cache, " ENCLOSED BY ");
pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len);
-
+
my_b_printf(&cache, " ESCAPED BY ");
pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
-
+
my_b_printf(&cache," LINES TERMINATED BY ");
pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len);
@@ -3237,7 +3237,7 @@
const_cast<RELAY_LOG_INFO*>(rli)->future_group_master_log_pos= log_pos;
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
}
-
+
/*
We test replicate_*_db rules. Note that we have already prepared
the file to load, even if we are going to ignore and delete it
@@ -4118,7 +4118,7 @@
ulong event_length;
int4store(buf, name_len);
-
+
if ((buf1[0]= is_null))
{
buf1_length= 1;
@@ -4805,7 +4805,7 @@
"could not open file '%s'", fname_buf);
goto err;
}
-
+
// a trick to avoid allocating another buffer
fname= fname_buf;
fname_len= (uint) (strmov(ext, ".data") - fname);
@@ -4819,7 +4819,7 @@
}
end_io_cache(&file);
my_close(fd, MYF(0));
-
+
// fname_buf now already has .data, not .info, because we did our trick
my_delete(fname_buf, MYF(0)); // old copy may exist already
if ((fd= my_create(fname_buf, CREATE_MODE,
@@ -5114,7 +5114,7 @@
{
}
#endif
-
+
/*
Execute_load_log_event ctor
@@ -5586,6 +5586,7 @@
m_table_id(tid),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
+ m_curr_row(NULL), m_curr_row_end(NULL),
m_flags(0)
{
/*
@@ -5625,7 +5626,8 @@
*description_event)
: Log_event(buf, description_event),
m_row_count(0),
- m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
+ m_curr_row(NULL), m_curr_row_end(NULL)
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
uint8 const common_header_len= description_event->common_header_len;
@@ -5711,6 +5713,7 @@
m_rows_buf= (byte*)my_malloc(data_size, MYF(MY_WME));
if (likely((bool)m_rows_buf))
{
+ m_curr_row= m_rows_buf;
m_rows_end= m_rows_buf + data_size;
m_rows_cur= m_rows_end;
memcpy(m_rows_buf, ptr_rows_data, data_size);
@@ -5815,7 +5818,6 @@
{
DBUG_ENTER("Rows_log_event::do_apply_event(st_relay_log_info*)");
int error= 0;
- char const *row_start= (char const *)m_rows_buf;
/*
If m_table_id == ~0UL, then we have a dummy event that does not
@@ -5851,134 +5853,14 @@
event.
*/
if (!thd->lock)
- {
- bool need_reopen= 1; /* To execute the first lap of the loop below */
+ error= open_and_lock_tables(const_cast<RELAY_LOG_INFO*>(rli));
- /*
- lock_tables() reads the contents of thd->lex, so they must be
- initialized. Contrary to in
- Table_map_log_event::do_apply_event() we don't call
- mysql_init_query() as that may reset the binlog format.
- */
- lex_start(thd, NULL, 0);
-
- while ((error= lock_tables(thd, rli->tables_to_lock,
- rli->tables_to_lock_count, &need_reopen)))
- {
- if (!need_reopen)
- {
- 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' in %s event: when locking tables",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"),
- get_type_str());
- thd->is_fatal_error= 1;
- }
- else
- {
- slave_print_msg(ERROR_LEVEL, rli, error,
- "Error in %s event: when locking tables",
- get_type_str());
- }
- const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
- DBUG_RETURN(error);
- }
-
- /*
- So we need to reopen the tables.
-
- We need 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.
-
- 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);
- TABLE_LIST *tables= rli->tables_to_lock;
- close_tables_for_reopen(thd, &tables);
-
- uint tables_count= rli->tables_to_lock_count;
- if ((error= open_tables(thd, &tables, &tables_count, 0)))
- {
- 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 reopening tables",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"));
- thd->query_error= 1;
- }
- const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
- DBUG_RETURN(error);
- }
- }
-
- /*
- When the open and locking succeeded, we check all tables to
- ensure that they still have the correct type.
-
- We can use a down cast here since we know that every table added
- to the tables_to_lock is a RPL_TABLE_LIST.
- */
-
- {
- RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
- {
- if (ptr->m_tabledef.compatible_with(rli, ptr->table))
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
- thd->query_error= 1;
- const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
- DBUG_RETURN(ERR_BAD_TABLE_DEF);
- }
- }
- }
-
- /*
- ... and then we add all the tables to the table map and remove
- them from tables to lock.
-
- We also invalidate the query cache for all the tables, since
- they will now be changed.
-
- TODO [/Matz]: Maybe the query cache should not be invalidated
- here? It might be that a table is not changed, even though it
- was locked for the statement. We do know that each
- Rows_log_event contain at least one row, so after processing one
- Rows_log_event, we can invalidate the query cache for the
- associated table.
- */
- for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
- {
- const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id,
ptr->table);
- }
-#ifdef HAVE_QUERY_CACHE
- query_cache.invalidate_locked_for_write(rli->tables_to_lock);
-#endif
- const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
- }
+ if (error)
+ DBUG_RETURN(error);
DBUG_ASSERT(rli->tables_to_lock == NULL && rli->tables_to_lock_count ==
0);
- TABLE* table=
const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(m_table_id);
+ TABLE* table= m_table=
const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.get_table(m_table_id);
if (table)
{
@@ -6025,23 +5907,51 @@
*/
const_cast<RELAY_LOG_INFO*>(rli)->set_flag(RELAY_LOG_INFO::IN_STMT);
- error= do_before_row_operations(table);
- while (error == 0 && row_start < (const char*) m_rows_end)
+ /*
+ Initialize table's write and read sets. Extra columns are included
+ as they will be filled with default values.
+
+ bit pos: 0 1 2 3 4 ... N-1 | N N+1 ... M |
+ bit val: X X X X X .... X | 1 1 ... 1 |
+
+ N = number of columns on master (m_witdth)
+ M = number of columns on slave
+ X = bits from the row's column set (m_cols)
+ */
+
+ // TODO: consider doing this inside Table_map event
+
+ bitmap_set_all(table->write_set);
+
+ // Note: I (Rafal) couldn't find any bitmap function which would
+ // copy bits from m_cols to a prefix of write_set.
+
+ for (uint bit=0 ; bit < m_width ; ++bit)
+ if (!bitmap_is_set(&m_cols,bit))
+ bitmap_clear_bit(table->write_set,bit);
+
+ bitmap_copy(table->read_set, table->write_set);
+
+ error= do_before_row_operations(thd);
+
+ // row processing loop
+
+ while (error == 0 && m_curr_row < m_rows_end)
{
- char const *row_end= NULL;
- if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
+ if ((error= do_prepare_row(thd)))
break; // We should perform the after-row operation even in
// the case of error
- DBUG_ASSERT(row_end != NULL); // cannot happen
- DBUG_ASSERT(row_end <= (const char*)m_rows_end);
-
/* in_use can have been set to NULL in close_tables_for_reopen */
THD* old_thd= table->in_use;
+
if (!table->in_use)
table->in_use= thd;
- error= do_exec_row(table);
+
+ error= do_exec_row(thd);
+
table->in_use = old_thd;
+
switch (error)
{
/* Some recoverable errors */
@@ -6053,24 +5963,34 @@
break;
default:
- slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
- "Error in %s event: row application failed",
- get_type_str());
- thd->query_error= 1;
- break;
+ slave_print_msg(ERROR_LEVEL, rli, thd->net.last_errno,
+ "Error in %s event: row application failed",
+ get_type_str());
+ thd->query_error= 1;
+ break;
}
- row_start= row_end;
- }
+ // at this moment m_curr_row_end should be set
+ DBUG_ASSERT(m_curr_row_end != NULL);
+ DBUG_ASSERT(m_curr_row < m_curr_row_end);
+ DBUG_ASSERT(m_curr_row_end <= (const char*)m_rows_end);
+
+ m_curr_row= m_curr_row_end;
+
+ } // row processing loop
+
DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
const_cast<RELAY_LOG_INFO*>(rli)->abort_slave= 1;);
- error= do_after_row_operations(table, error);
+
+ error= do_after_row_operations(thd,error);
+
if (!cache_stmt)
{
DBUG_PRINT("info", ("Marked that we need to keep log"));
thd->options|= OPTION_KEEP_LOG;
}
- }
+
+ } // if (table)
if (error)
{ /* error has occured during the transaction */
@@ -6127,6 +6047,147 @@
DBUG_RETURN(0);
}
+/**
+ Open tables needed for Row events.
+
+ This function is called after a sequence of Table_map events in the first
+ following Rows event. It opens and locks tables collected in the rli
+ structure while processing Table_map events.
+
+ @returns Non-zero error code in case of an error.
+ */
+int Rows_log_event::open_and_lock_tables(RELAY_LOG_INFO *const rli)
+{
+ DBUG_ENTER("Rows_log_event::open_and_lock_tables");
+
+ int error;
+ bool need_reopen= 1; /* To execute the first lap of the loop below */
+
+ /*
+ lock_tables() reads the contents of thd->lex, so they must be
+ initialized. Contrary to in
+ Table_map_log_event::do_apply_event() we don't call
+ mysql_init_query() as that may reset the binlog format.
+ */
+ lex_start(thd, NULL, 0);
+
+ while ((error= lock_tables(thd, rli->tables_to_lock,
+ rli->tables_to_lock_count, &need_reopen)))
+ {
+ if (!need_reopen)
+ {
+ 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' in %s event: when locking tables",
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"),
+ get_type_str());
+ thd->is_fatal_error= 1;
+ }
+ else
+ {
+ slave_print_msg(ERROR_LEVEL, rli, error,
+ "Error in %s event: when locking tables",
+ get_type_str());
+ }
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(error);
+ }
+
+ /*
+ So we need to reopen the tables.
+
+ We need 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.
+
+ 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);
+ TABLE_LIST *tables= rli->tables_to_lock;
+ close_tables_for_reopen(thd, &tables);
+
+ uint tables_count= rli->tables_to_lock_count;
+ if ((error= open_tables(thd, &tables, &tables_count, 0)))
+ {
+ 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 reopening tables",
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"));
+ thd->query_error= 1;
+ }
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST.
+ */
+
+ {
+ RPL_TABLE_LIST *ptr= rli->tables_to_lock;
+ for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ {
+ if (ptr->m_tabledef.compatible_with(rli, ptr->table))
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ thd->query_error= 1;
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+ DBUG_RETURN(ERR_BAD_TABLE_DEF);
+ }
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and remove
+ them from tables to lock.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Rows_log_event contain at least one row, so after processing one
+ Rows_log_event, we can invalidate the query cache for the
+ associated table.
+ */
+ for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
+ {
+ const_cast<RELAY_LOG_INFO*>(rli)->m_table_map.set_table(ptr->table_id,
ptr->table);
+ }
+#ifdef HAVE_QUERY_CACHE
+ query_cache.invalidate_locked_for_write(rli->tables_to_lock);
+#endif
+ const_cast<RELAY_LOG_INFO*>(rli)->clear_tables_to_lock();
+
+ DBUG_RETURN(0);
+}
+
+
int
Rows_log_event::do_update_pos(RELAY_LOG_INFO *rli)
{
@@ -6714,7 +6775,7 @@
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Write_rows_log_event::do_before_row_operations(TABLE *table)
+int Write_rows_log_event::do_before_row_operations(THD *thd)
{
int error= 0;
@@ -6734,15 +6795,15 @@
*/
thd->lex->sql_command= SQLCOM_REPLACE;
- table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); // Needed for ndbcluster
- table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); // Needed for ndbcluster
- table->file->extra(HA_EXTRA_IGNORE_NO_KEY); // Needed for ndbcluster
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); // Needed for ndbcluster
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); // Needed for ndbcluster
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY); // Needed for ndbcluster
/*
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.
*/
- table->file->ha_start_bulk_insert(0);
+ m_table->file->ha_start_bulk_insert(0);
/*
We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
any TIMESTAMP column with data from the row but instead will use
@@ -6758,30 +6819,27 @@
some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
analyze if explicit data is provided for slave's TIMESTAMP columns).
*/
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
return error;
}
-int Write_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int Write_rows_log_event::do_after_row_operations(THD *thd, int error)
{
if (error == 0)
- error= table->file->ha_end_bulk_insert();
+ error= m_table->file->ha_end_bulk_insert();
return error;
}
-int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
- TABLE *table,
- char const *const row_start,
- char const **const row_end)
+int Write_rows_log_event::do_prepare_row(THD *thd)
{
- DBUG_ASSERT(table != NULL);
- DBUG_ASSERT(row_start && row_end);
+ DBUG_ASSERT(m_table != NULL);
- int error;
- error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->write_set, WRITE_ROWS_EVENT);
- bitmap_copy(table->read_set, table->write_set);
- return error;
+ int error1, error2;
+ error1= prepare_record(m_table,m_width,TRUE);
+ error2= unpack_row(m_table, m_width, m_curr_row, &m_cols,
+ (const char**)&m_curr_row_end, &m_master_reclength);
+
+ return error2 ? error2 : error1;
}
/*
@@ -6826,14 +6884,27 @@
}
-
/*
Copy "extra" columns from record[1] to record[0].
- Copy the extra fields that are not present on the master but are
- present on the slave from record[1] to record[0]. This is used
- after fetching a record that are to be updated, either inside
- replace_record() or as part of executing an update_row().
+ Both records belong to @c table which is slave's copy of a master table.
+ This table can contain more columns than the master's counterpart. It is
+ assumed that record[0] contains data sent from master and thus
+ the extra fields are not filled. Record[1] should contain a record
+ from slave's table (corresponding to master's record[0]) and the function
+ copies the extra columns from here to record[0].
+
+ Note: this function assumes that all master's columns are filled in
+ record[0] - it should not contain any "holes".
+
+ SYNOPIS
+ copy_extra_record_fields()
+ table The table defining record columns.
+ table->record[0] Target (master) record.
+ table->record[1] Source (slave) record.
+ master_reclength Length of master record.
+ master_fields Number of fields in master record (i.e. number of columns
+ on master)
*/
static int
copy_extra_record_fields(TABLE *table,
@@ -6846,6 +6917,7 @@
(long) table->record[0],
(ulong) master_fields, (ulong) master_reclength,
table->s->fields, table->s->reclength));
+
/*
Copying the extra fields of the slave that does not exist on
master into record[0] (which are basically the default values).
@@ -6855,7 +6927,7 @@
bmove_align(table->record[0] + master_reclength,
table->record[1] + master_reclength,
table->s->reclength - master_reclength);
-
+
/*
Bit columns are special. We iterate over all the remaining
columns and copy the "extra" bits to the new record. This is
@@ -6947,16 +7019,20 @@
/*
- Replace the provided record in the database.
+ Write a record to a table, replacing old record if it already exists there
+ (i.e. a record with the same primary key value is replaced by the new record).
SYNOPSIS
replace_record()
thd Thread context for writing the record.
table Table to which record should be written.
+ table->record[0]
+ The record to be written.
+ table->write_set
+ Set of columns in record[0] to be updated on replace.
master_reclength
Offset to first column that is not present on the master,
- alternatively the length of the record on the master
- side.
+ alternatively the length of the record on master.
RETURN VALUE
Error code on failure, 0 on success.
@@ -7052,6 +7128,12 @@
*/
copy_extra_record_fields(table, master_reclength, master_fields);
+#ifndef DBUG_OFF
+ DBUG_PRINT("debug",("preparing for update: before and after image"));
+ DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
+ DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
+#endif
+
/*
REPLACE is defined as either INSERT or DELETE + INSERT. If
possible, we can replace it with an UPDATE, but that will not
@@ -7090,10 +7172,10 @@
DBUG_RETURN(error);
}
-int Write_rows_log_event::do_exec_row(TABLE *table)
+int Write_rows_log_event::do_exec_row(THD *thd)
{
- DBUG_ASSERT(table != NULL);
- int error= replace_record(thd, table, m_master_reclength, m_width);
+ DBUG_ASSERT(m_table != NULL);
+ int error= replace_record(thd, m_table, m_master_reclength, m_width);
return error;
}
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
@@ -7436,12 +7518,12 @@
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Delete_rows_log_event::do_before_row_operations(TABLE *table)
+int Delete_rows_log_event::do_before_row_operations(THD *thd)
{
DBUG_ASSERT(m_memory == NULL);
- if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION)
&&
- table->s->primary_key < MAX_KEY)
+ if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION)
&&
+ m_table->s->primary_key < MAX_KEY)
{
/*
We don't need to allocate any memory for m_after_image and
@@ -7452,17 +7534,17 @@
int error= 0;
- if (table->s->keys > 0)
+ if (m_table->s->keys > 0)
{
m_memory=
my_multi_malloc(MYF(MY_WME),
- &m_after_image, table->s->reclength,
- &m_key, table->key_info->key_length,
+ &m_after_image, m_table->s->reclength,
+ &m_key, m_table->key_info->key_length,
NULL);
}
else
{
- m_after_image= (byte*)my_malloc(table->s->reclength, MYF(MY_WME));
+ m_after_image= (byte*)my_malloc(m_table->s->reclength, MYF(MY_WME));
m_memory= (gptr)m_after_image;
m_key= NULL;
}
@@ -7472,10 +7554,10 @@
return error;
}
-int Delete_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int Delete_rows_log_event::do_after_row_operations(THD *thd, int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
- table->file->ha_index_or_rnd_end();
+ m_table->file->ha_index_or_rnd_end();
my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
m_memory= NULL;
m_after_image= NULL;
@@ -7484,48 +7566,45 @@
return error;
}
-int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
- TABLE *table,
- char const *const row_start,
- char const **const row_end)
+int Delete_rows_log_event::do_prepare_row(THD *thd)
{
int error;
- DBUG_ASSERT(row_start && row_end);
/*
This assertion actually checks that there is at least as many
columns on the slave as on the master.
*/
- DBUG_ASSERT(table->s->fields >= m_width);
+ DBUG_ASSERT(m_table->s->fields >= m_width);
- error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->read_set, DELETE_ROWS_EVENT);
+ prepare_record(m_table,m_width);
+ error= unpack_row(m_table, m_width, m_curr_row, &m_cols,
+ (const char**)&m_curr_row_end, &m_master_reclength);
/*
If we will access rows using the random access method, m_key will
be set to NULL, so we do not need to make a key copy in that case.
*/
if (m_key)
{
- KEY *const key_info= table->key_info;
+ KEY *const key_info= m_table->key_info;
- key_copy(m_key, table->record[0], key_info, 0);
+ key_copy(m_key, m_table->record[0], key_info, 0);
}
return error;
}
-int Delete_rows_log_event::do_exec_row(TABLE *table)
+int Delete_rows_log_event::do_exec_row(THD *thd)
{
int error;
- DBUG_ASSERT(table != NULL);
+ DBUG_ASSERT(m_table != NULL);
- if (!(error= find_and_fetch_row(table, m_key)))
+ if (!(error= find_and_fetch_row(m_table, m_key)))
{
/*
Now we should have the right row to delete. We are using
record[0] since it is guaranteed to point to a record with the
correct value.
*/
- error= table->file->ha_delete_row(table->record[0]);
+ error= m_table->file->ha_delete_row(m_table->record[0]);
}
return error;
}
@@ -7618,38 +7697,38 @@
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Update_rows_log_event::do_before_row_operations(TABLE *table)
+int Update_rows_log_event::do_before_row_operations(THD *thd)
{
DBUG_ASSERT(m_memory == NULL);
int error= 0;
- if (table->s->keys > 0)
+ if (m_table->s->keys > 0)
{
m_memory=
my_multi_malloc(MYF(MY_WME),
- &m_after_image, table->s->reclength,
- &m_key, table->key_info->key_length,
+ &m_after_image, m_table->s->reclength,
+ &m_key, m_table->key_info->key_length,
NULL);
}
else
{
- m_after_image= (byte*)my_malloc(table->s->reclength, MYF(MY_WME));
+ m_after_image= (byte*)my_malloc(m_table->s->reclength, MYF(MY_WME));
m_memory= (gptr)m_after_image;
m_key= NULL;
}
if (!m_memory)
return HA_ERR_OUT_OF_MEM;
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
return error;
}
-int Update_rows_log_event::do_after_row_operations(TABLE *table, int error)
+int Update_rows_log_event::do_after_row_operations(THD *thd,int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd,
returning error?*/
- table->file->ha_index_or_rnd_end();
+ m_table->file->ha_index_or_rnd_end();
my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
m_memory= NULL;
m_after_image= NULL;
@@ -7658,43 +7737,44 @@
return error;
}
-int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO const *rli,
- TABLE *table,
- char const *const row_start,
- char const **const row_end)
+int Update_rows_log_event::do_prepare_row(THD *thd)
{
int error;
- DBUG_ASSERT(row_start && row_end);
/*
This assertion actually checks that there is at least as many
columns on the slave as on the master.
*/
- DBUG_ASSERT(table->s->fields >= m_width);
+ DBUG_ASSERT(m_table->s->fields >= m_width);
/*
We need to perform some juggling below since unpack_row() always
- unpacks into table->record[0]. For more information, see the
+ unpacks into m_table->record[0]. For more information, see the
comments for unpack_row().
*/
/* record[0] is the before image for the update */
- error= unpack_row(rli, table, m_width, row_start, &m_cols, row_end,
- &m_master_reclength, table->read_set, UPDATE_ROWS_EVENT);
- store_record(table, record[1]);
- char const *next_start = *row_end;
+ prepare_record(m_table,m_width);
+ error= unpack_row(m_table, m_width, m_curr_row, &m_cols,
+ (const char**)&m_curr_row_end, &m_master_reclength);
+ store_record(m_table, record[1]);
+
+ char const *next_start = m_curr_row_end;
+
+ prepare_record(m_table,m_width);
+ error= unpack_row(m_table, m_width, next_start, &m_cols_ai,
+ (const char**)&m_curr_row_end, &m_master_reclength);
+
/* m_after_image is the after image for the update */
- error= unpack_row(rli, table, m_width, next_start, &m_cols_ai, row_end,
- &m_master_reclength, table->write_set, UPDATE_ROWS_EVENT);
- bmove_align(m_after_image, table->record[0], table->s->reclength);
- restore_record(table, record[1]);
+ bmove_align(m_after_image, m_table->record[0], m_table->s->reclength);
+ restore_record(m_table, record[1]);
/*
Don't print debug messages when running valgrind since they can
trigger false warnings.
*/
#ifndef HAVE_purify
- DBUG_DUMP("record[0]", (const char *)table->record[0], table->s->reclength);
- DBUG_DUMP("m_after_image", (const char *)m_after_image, table->s->reclength);
+ DBUG_DUMP("record[0]", (const char *)m_table->record[0],
m_table->s->reclength);
+ DBUG_DUMP("m_after_image", (const char *)m_after_image, m_table->s->reclength);
#endif
/*
@@ -7703,19 +7783,19 @@
*/
if (m_key)
{
- KEY *const key_info= table->key_info;
+ KEY *const key_info= m_table->key_info;
- key_copy(m_key, table->record[0], key_info, 0);
+ key_copy(m_key, m_table->record[0], key_info, 0);
}
return error;
}
-int Update_rows_log_event::do_exec_row(TABLE *table)
+int Update_rows_log_event::do_exec_row(THD *thd)
{
- DBUG_ASSERT(table != NULL);
+ DBUG_ASSERT(m_table != NULL);
- int error= find_and_fetch_row(table, m_key);
+ int error= find_and_fetch_row(m_table, m_key);
if (error)
return error;
@@ -7732,8 +7812,8 @@
overwriting the default values that where put there by the
unpack_row() function.
*/
- bmove_align(table->record[0], m_after_image, table->s->reclength);
- copy_extra_record_fields(table, m_master_reclength, m_width);
+ bmove_align(m_table->record[0], m_after_image, m_table->s->reclength);
+ copy_extra_record_fields(m_table, m_master_reclength, m_width);
/*
Now we have the right row to update. The old row (the one we're
@@ -7741,7 +7821,7 @@
We also have copied the original values already in the slave's
database into the after image delivered from the master.
*/
- error= table->file->ha_update_row(table->record[1], table->record[0]);
+ error= m_table->file->ha_update_row(m_table->record[1],
m_table->record[0]);
return error;
}
@@ -7863,5 +7943,3 @@
DBUG_ENTER("Incident_log_event::write_data_body");
DBUG_RETURN(write_str(file, m_message.str, m_message.length));
}
-
-
--- 1.144/sql/log_event.h 2007-05-05 13:35:51 +02:00
+++ 1.145/sql/log_event.h 2007-05-05 13:35:51 +02:00
@@ -2237,11 +2237,21 @@
byte *m_rows_cur; /* One-after the end of the data */
byte *m_rows_end; /* One-after the end of the allocated space */
+ byte *m_curr_row; /* Start of the row being processed */
+ byte *m_curr_row_end; /* One-after the end of the current row */
+
flag_set m_flags; /* Flags for row-level events */
private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+
+ /*
+ Used in a first event which comes after Table_map event to open
+ and lock tables needed for row operations.
+ */
+ int open_and_lock_tables(RELAY_LOG_INFO *const);
+
virtual int do_apply_event(RELAY_LOG_INFO const *rli);
virtual int do_update_pos(RELAY_LOG_INFO *rli);
@@ -2261,7 +2271,7 @@
The member function will return 0 if all went OK, or a non-zero
error code otherwise.
*/
- virtual int do_before_row_operations(TABLE *table) = 0;
+ virtual int do_before_row_operations(THD*) = 0;
/*
Primitive to clean up after a sequence of row executions.
@@ -2271,8 +2281,14 @@
After doing a sequence of do_prepare_row() and do_exec_row(),
this member function should be called to clean up and release
any allocated buffers.
+
+ The error argument, if non-zero, indicates error which happened
+ while processing rows (or in the do_before_row_operations() function)
+ before calling this function. In that case this function should return
+ non-zero error code (the same error which was passed as an argument
+ if no errors were encountered here).
*/
- virtual int do_after_row_operations(TABLE *table, int error) = 0;
+ virtual int do_after_row_operations(THD*,int error) = 0;
/*
Primitive to prepare for handling one row in a row-level event.
@@ -2280,32 +2296,35 @@
DESCRIPTION
The member function prepares for execution of operations needed for one
- row in a row-level event by reading up data from the buffer containing
- the row. No specific interpretation of the data is normally done here,
- since SQL thread specific data is not available: that data is made
- available for the do_exec function.
-
- A pointer to the start of the next row, or NULL if the preparation
- failed. Currently, preparation cannot fail, but don't rely on this
- behavior.
+ row in a row-level event. The row is located at m_curr_row. The end of
+ the row might be unknown (in which case m_curr_row_end == m_curr_row) as
+ it is discovered only when the row data is unpacked and that can happen
+ later in do_exec_row().
RETURN VALUE
Error code, if something went wrong, 0 otherwise.
*/
- virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*,
- char const *row_start, char const **row_end) = 0;
+
+ // TODO: consider not passing the THD struct to discourage doing parts of
+ // row execution here
+
+ virtual int do_prepare_row(THD*) =0;
/*
Primitive to do the actual execution necessary for a row.
DESCRIPTION
The member function will do the actual execution needed to handle a row.
-
+ The row is located at m_curr_row. When the function returns,
+ m_curr_row_end should point at the next row (one byte after the end
+ of the current row).
+
RETURN VALUE
0 if execution succeeded, 1 if execution failed.
*/
- virtual int do_exec_row(TABLE *table) = 0;
+ virtual int do_exec_row(THD*) = 0;
+
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
};
@@ -2361,11 +2380,10 @@
gptr m_memory;
byte *m_after_image;
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*,
- char const *row_start, char const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(THD*);
+ virtual int do_after_row_operations(THD*,int error);
+ virtual int do_prepare_row(THD*);
+ virtual int do_exec_row(THD*);
#endif
};
@@ -2441,11 +2459,10 @@
byte *m_key;
byte *m_after_image;
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*,
- char const *row_start, char const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(THD*);
+ virtual int do_after_row_operations(THD*,int error);
+ virtual int do_prepare_row(THD*);
+ virtual int do_exec_row(THD*);
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
};
@@ -2512,11 +2529,10 @@
byte *m_key;
byte *m_after_image;
- virtual int do_before_row_operations(TABLE *table);
- virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, RELAY_LOG_INFO const*, TABLE*,
- char const *row_start, char const **row_end);
- virtual int do_exec_row(TABLE *table);
+ virtual int do_before_row_operations(THD*);
+ virtual int do_after_row_operations(THD*,int error);
+ virtual int do_prepare_row(THD*);
+ virtual int do_exec_row(THD*);
#endif
};
--- 1.303/sql/slave.cc 2007-05-05 13:35:51 +02:00
+++ 1.304/sql/slave.cc 2007-05-05 13:35:51 +02:00
@@ -576,7 +576,9 @@
case ERROR_LEVEL:
/*
This my_error call only has effect in client threads.
- Slave threads do nothing in my_error().
+ Slave threads do nothing in my_error().
+ (I think this is not true and the my_error call below is a source of
+ spurious and confusing entries in slave's error log /Rafal)
*/
my_error(ER_UNKNOWN_ERROR, MYF(0), msg);
/*
@@ -1782,9 +1784,12 @@
int reason= ev->shall_skip(rli);
if (reason == Log_event::EVENT_SKIP_COUNT)
--rli->slave_skip_counter;
+
pthread_mutex_unlock(&rli->data_lock);
+
if (reason == Log_event::EVENT_SKIP_NOT)
exec_res= ev->apply_event(rli);
+
#ifndef DBUG_OFF
/*
This only prints information to the debug trace.
--- 1.4/mysql-test/r/rpl_ndb_extraCol.result 2007-05-05 13:35:51 +02:00
+++ 1.5/mysql-test/r/rpl_ndb_extraCol.result 2007-05-05 13:35:51 +02:00
@@ -28,9 +28,9 @@
*** Select from slave ***
SELECT * FROM t1 ORDER BY a;
a b c d e
-1 2 TEXAS NULL NULL
-2 1 AUSTIN NULL NULL
-3 4 QA NULL NULL
+1 2 TEXAS 2 TEST
+2 1 AUSTIN 2 TEST
+3 4 QA 2 TEST
*** Drop t1 ***
DROP TABLE t1;
*** Create t3 on slave ***
@@ -293,9 +293,9 @@
*** Select from slave ***
SELECT * FROM t7 ORDER BY a;
a b c d e
-1 b1b1 Kyle NULL NULL
-2 b1b1 JOE NULL NULL
-3 b1b1 QA NULL NULL
+1 b1b1 Kyle 0000-00-00 00:00:00 Extra Column Testing
+2 b1b1 JOE 0000-00-00 00:00:00 Extra Column Testing
+3 b1b1 QA 0000-00-00 00:00:00 Extra Column Testing
*** Drop t7 ***
DROP TABLE t7;
*** Create t8 on slave ***
@@ -453,9 +453,9 @@
*** Select on Slave ***
SELECT * FROM t12 ORDER BY a;
a b f c e
-1 b1b1b1b1b1b1b1b1 Kyle NULL NULL
-2 b1b1b1b1b1b1b1b1 JOE NULL NULL
-3 b1b1b1b1b1b1b1b1 QA NULL NULL
+1 b1b1b1b1b1b1b1b1 Kyle test 1
+2 b1b1b1b1b1b1b1b1 JOE test 1
+3 b1b1b1b1b1b1b1b1 QA test 1
*** Drop t12 ***
DROP TABLE t12;
**** Extra Colums End ****
@@ -485,9 +485,9 @@
*** Select on Slave ****
SELECT * FROM t13 ORDER BY a;
a b c d e
-1 b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP
-2 b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP
-3 b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP
+1 b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP
+2 b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP
+3 b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP
*** Drop t13 ***
DROP TABLE t13;
*** 22117 END ***
@@ -521,9 +521,9 @@
*** Select on Slave ****
SELECT * FROM t14 ORDER BY c1;
c1 c2 c3 c4 c5 c6 c7
-1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP
-2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP
-3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP
+1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP
+2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP
+3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP
*** connect to master and drop columns ***
ALTER TABLE t14 DROP COLUMN c2;
ALTER TABLE t14 DROP COLUMN c4;
@@ -536,9 +536,9 @@
*** Select from Slave ***
SELECT * FROM t14 ORDER BY c1;
c1 c3 c5 c6 c7
-1 Replication Testing Extra Col Kyle NULL CURRENT_TIMESTAMP
-2 This Test Should work JOE NULL CURRENT_TIMESTAMP
-3 If is does not, I will open a bug QA NULL CURRENT_TIMESTAMP
+1 Replication Testing Extra Col Kyle 1 CURRENT_TIMESTAMP
+2 This Test Should work JOE 1 CURRENT_TIMESTAMP
+3 If is does not, I will open a bug QA 1 CURRENT_TIMESTAMP
*** Drop t14 ***
DROP TABLE t14;
*** Create t15 on slave ***
@@ -569,9 +569,9 @@
*** Select on Slave ****
SELECT * FROM t15 ORDER BY c1;
c1 c2 c3 c4 c5 c6 c7
-1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP
-2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP
-3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP
+1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP
+2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP
+3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP
*** Add column on master that is a Extra on Slave ***
ALTER TABLE t15 ADD COLUMN c6 INT AFTER c5;
********************************************
@@ -625,9 +625,9 @@
*** Try to select from slave ****
SELECT * FROM t15 ORDER BY c1;
c1 c2 c3 c4 c5 c6 c7
-1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP
-2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP
-3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP
+1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP
+2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP
+3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP
5 2.00 Replication Testing b1b1b1b1b1b1b1b1 Buda 2 CURRENT_TIMESTAMP
*** DROP TABLE t15 ***
DROP TABLE t15;
@@ -659,9 +659,9 @@
*** Select on Slave ****
SELECT * FROM t16 ORDER BY c1;
c1 c2 c3 c4 c5 c6 c7
-1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle NULL CURRENT_TIMESTAMP
-2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE NULL CURRENT_TIMESTAMP
-3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA NULL CURRENT_TIMESTAMP
+1 1.00 Replication Testing Extra Col b1b1b1b1b1b1b1b1 Kyle 1 CURRENT_TIMESTAMP
+2 2.00 This Test Should work b1b1b1b1b1b1b1b1 JOE 1 CURRENT_TIMESTAMP
+3 3.00 If is does not, I will open a bug b1b1b1b1b1b1b1b1 QA 1 CURRENT_TIMESTAMP
*** Add Partition on master ***
ALTER TABLE t16 PARTITION BY KEY(c1) PARTITIONS 4;
INSERT INTO t16 () VALUES(4,1.00,'Replication Rocks',@b1,'Omer');
--- 1.11/mysql-test/r/rpl_row_tabledefs_2myisam.result 2007-05-05 13:35:51 +02:00
+++ 1.12/mysql-test/r/rpl_row_tabledefs_2myisam.result 2007-05-05 13:35:51 +02:00
@@ -122,7 +122,7 @@
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
-Last_Errno 1105
+Last_Errno 1364
Last_Error Error in Write_rows event: error during transaction execution on table
test.t1_nodef
Skip_Counter 0
Exec_Master_Log_Pos #
--- 1.8/mysql-test/r/rpl_row_tabledefs_3innodb.result 2007-05-05 13:35:51 +02:00
+++ 1.9/mysql-test/r/rpl_row_tabledefs_3innodb.result 2007-05-05 13:35:51 +02:00
@@ -122,7 +122,7 @@
Replicate_Ignore_Table
Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table
-Last_Errno 1105
+Last_Errno 1364
Last_Error Error in Write_rows event: error during transaction execution on table
test.t1_nodef
Skip_Counter 0
Exec_Master_Log_Pos #
--- 1.6/sql/rpl_utility.cc 2007-05-05 13:35:51 +02:00
+++ 1.7/sql/rpl_utility.cc 2007-05-05 13:35:51 +02:00
@@ -129,7 +129,7 @@
DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
error= 1;
slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
- "table_def::compatible_with: Table width mismatch - "
+ "Table width mismatch - "
"received %u columns, %s.%s has %u columns",
(uint) size(), tsh->db.str, tsh->table_name.str,
tsh->fields);
@@ -143,7 +143,7 @@
DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
error= 1;
slave_print_msg(ERROR_LEVEL, rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
- "table_def::compatible_with: Column %d type mismatch - "
+ "Column %d type mismatch - "
"received type %d, %s.%s has type %d",
col, type(col), tsh->db.str, tsh->table_name.str,
table->field[col]->type());
--- 1.2/sql/rpl_record.cc 2007-05-05 13:35:51 +02:00
+++ 1.3/sql/rpl_record.cc 2007-05-05 13:35:51 +02:00
@@ -16,6 +16,7 @@
#include "mysql_priv.h"
#include "rpl_record.h"
#include "slave.h" // Need to pull in slave_print_msg
+#include "rpl_rli.h" // for MAX_SLAVE_ERRMSG
/**
Pack a record of data for a table into a format suitable for
@@ -139,7 +140,7 @@
At most @c colcnt columns are read: if the table is larger than
that, the remaining fields are not filled in.
- @param rli Relay log info
+ @param rli Relay log info (only for reporting errors)
@param table Table to unpack into
@param colcnt Number of columns to read from record
@param row Packed row data
@@ -151,7 +152,9 @@
record on the master side
@param rw_set Pointer to bitmap that holds either the read_set or the
write_set of the table
-
+ @param event_type
+ Decides if errors should be reported if a missing field
+ is not null and has no default value.
@retval 0 No error
@@ -162,11 +165,9 @@
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
-unpack_row(RELAY_LOG_INFO const *rli,
- TABLE *table, uint const colcnt,
+unpack_row(TABLE *table, uint const colcnt,
char const *const row_data, MY_BITMAP const *cols,
- char const **const row_end, ulong *const master_reclength,
- MY_BITMAP* const rw_set, Log_event_type const event_type)
+ char const **const row_end, ulong *const master_reclength)
{
DBUG_ENTER("unpack_row");
DBUG_ASSERT(row_data);
@@ -176,10 +177,6 @@
char const *null_ptr= row_data;
char const *pack_ptr= row_data + master_null_byte_count;
- bitmap_clear_all(rw_set);
-
- empty_record(table);
-
Field **const begin_ptr = table->field;
Field **field_ptr;
Field **const end_ptr= begin_ptr + colcnt;
@@ -224,7 +221,6 @@
pack_ptr= f->unpack(f->ptr, pack_ptr);
}
- bitmap_set_bit(rw_set, f->field_index);
null_mask <<= 1;
}
}
@@ -244,37 +240,55 @@
*master_reclength = table->s->reclength;
}
- /*
- Set properties for remaining columns, if there are any. We let the
- corresponding bit in the write_set be set, to write the value if
- it was not there already. We iterate over all remaining columns,
- even if there were an error, to get as many error messages as
- possible. We are still able to return a pointer to the next row,
- so redo that.
+ DBUG_RETURN(error);
+}
- This generation of error messages is only relevant when inserting
- new rows.
- */
- for ( ; *field_ptr ; ++field_ptr)
+#endif // HAVE_REPLICATION
+
+/**
+ Fills table->record[0] with default values.
+
+ For some unknown (to Rafal) reason, using empty_record() alone is not
+ enough if the record is later written to the table. Because of that,
+ we also iterate over fields and call f->set_default().
+
+ For optimization reasons, the explicit initialization can be skipped for
+ first @c skip fields. This is useful if later we are going to fill these
+ fields from other source (e.g. from a Rows replication event).
+
+ If @c check is true, fields are explicitely initialized only if they have
+ default value or can be NULL. Otherwise error is reported.
+ */
+int prepare_record(TABLE *table, const uint skip, const bool check)
+{
+ DBUG_ENTER("prepare_record");
+
+ int error= 0;
+ empty_record(table);
+
+ /* Explicit initialization of fields */
+
+ for (Field **field_ptr= table->field+skip ; *field_ptr ; ++field_ptr)
{
uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG;
Field *const f= *field_ptr;
- if (event_type == WRITE_ROWS_EVENT &&
- ((*field_ptr)->flags & mask) == mask)
+ if (check && ((f->flags & mask) == mask))
{
- slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD,
- "Field `%s` of table `%s`.`%s` "
- "has no default value and cannot be NULL",
- (*field_ptr)->field_name, table->s->db.str,
- table->s->table_name.str);
+ char buff[MAX_SLAVE_ERRMSG];
+
+ my_snprintf(buff,sizeof(buff),"%s.%s(%s)",
+ table->s->db.str,
+ table->s->table_name.str,
+ f->field_name);
+ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), buff);
+
error = ER_NO_DEFAULT_FOR_FIELD;
}
else
f->set_default();
}
- DBUG_RETURN(error);
+ DBUG_RETURN(error);
}
-#endif // HAVE_REPLICATION
--- 1.1/sql/rpl_record.h 2007-05-05 13:35:51 +02:00
+++ 1.2/sql/rpl_record.h 2007-05-05 13:35:51 +02:00
@@ -22,12 +22,12 @@
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int unpack_row(RELAY_LOG_INFO const *rli,
- TABLE *table, uint const colcnt,
+int unpack_row(TABLE *table, uint const colcnt,
char const *const row_data, MY_BITMAP const *cols,
- char const **const row_end, ulong *const master_reclength,
- MY_BITMAP* const rw_set,
- Log_event_type const event_type);
+ char const **const row_end, ulong *const master_reclength);
#endif
+
+// Fill table's record[0] with default values.
+int prepare_record(TABLE*, const uint =0, const bool =FALSE);
#endif
| Thread |
|---|
| • bk commit into 5.1 tree (rafal:1.2575) BUG#21842 | rsomla | 5 May |