List:Commits« Previous MessageNext Message »
From:tomas Date:May 8 2006 11:44am
Subject:bk commit into 5.1 tree (tomas:1.2018) BUG#19069
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of tomas. When tomas 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
  1.2018 06/05/08 13:44:36 tomas@stripped +14 -0
  Bug #19069 RBR: does not allow replication to slave with different number of columns
  - partial solution, allowing more columns on the slave

  sql/rpl_utility.h
    1.1 06/05/08 13:44:30 tomas@stripped +60 -0
    New BitKeeper file ``sql/rpl_utility.h''

  sql/rpl_utility.cc
    1.1 06/05/08 13:44:30 tomas@stripped +156 -0
    New BitKeeper file ``sql/rpl_utility.cc''

  sql/rpl_utility.h
    1.0 06/05/08 13:44:30 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/sql/rpl_utility.h

  sql/rpl_utility.cc
    1.0 06/05/08 13:44:30 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/sql/rpl_utility.cc

  mysql-test/t/rpl_row_tabledefs.test
    1.1 06/05/08 13:44:29 tomas@stripped +8 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  mysql-test/t/rpl_ndb_tabledefs.test
    1.1 06/05/08 13:44:29 tomas@stripped +8 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  mysql-test/t/rpl_row_tabledefs.test
    1.0 06/05/08 13:44:29 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/mysql-test/t/rpl_row_tabledefs.test

  mysql-test/t/rpl_ndb_tabledefs.test
    1.0 06/05/08 13:44:29 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/mysql-test/t/rpl_ndb_tabledefs.test

  mysql-test/r/rpl_row_tabledefs.result
    1.1 06/05/08 13:44:28 tomas@stripped +381 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  mysql-test/r/rpl_ndb_tabledefs.result
    1.1 06/05/08 13:44:28 tomas@stripped +381 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
    1.1 06/05/08 13:44:28 tomas@stripped +252 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/share/errmsg.txt
    1.59 06/05/08 13:44:28 tomas@stripped +2 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  mysql-test/r/rpl_row_tabledefs.result
    1.0 06/05/08 13:44:28 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/mysql-test/r/rpl_row_tabledefs.result

  mysql-test/r/rpl_ndb_tabledefs.result
    1.0 06/05/08 13:44:28 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/mysql-test/r/rpl_ndb_tabledefs.result

  mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
    1.0 06/05/08 13:44:28 tomas@stripped +0 -0
    BitKeeper file /home/tomas/wl2325-alcatel/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test

  sql/mysql_priv.h
    1.339 06/05/08 13:44:27 tomas@stripped +5 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/log_event.h
    1.121 06/05/08 13:44:27 tomas@stripped +34 -16
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/log_event.cc
    1.205 06/05/08 13:44:27 tomas@stripped +255 -69
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/field.h
    1.170 06/05/08 13:44:27 tomas@stripped +25 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/field.cc
    1.287 06/05/08 13:44:27 tomas@stripped +8 -0
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

  sql/Makefile.am
    1.122 06/05/08 13:44:27 tomas@stripped +2 -2
    Bug #19069 RBR: does not allow replication to slave with different number of columns
    - partial solution, allowing more columns on the slave

# 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:	tomas
# Host:	poseidon.ndb.mysql.com
# Root:	/home/tomas/wl2325-alcatel

--- 1.121/sql/Makefile.am	2005-10-21 07:06:44 +02:00
+++ 1.122/sql/Makefile.am	2006-05-08 13:44:27 +02:00
@@ -55,7 +55,7 @@
 	                rpl_tblmap.h rpl_filter.h \
 			rpl_injector.h \
 			ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \
-			ha_ndbcluster.h opt_range.h protocol.h \
+			ha_ndbcluster.h opt_range.h protocol.h rpl_utility.h \
 			sql_select.h structs.h table.h sql_udf.h hash_filo.h\
 			lex.h lex_symbol.h sql_plugin.h sql_acl.h sql_crypt.h  \
 			log_event.h sql_repl.h slave.h \
@@ -88,7 +88,7 @@
 			discover.cc time.cc opt_range.cc opt_sum.cc \
 		   	records.cc filesort.cc handler.cc \
 	                rpl_tblmap.cc rpl_filter.cc \
-			rpl_injector.cc \
+			rpl_utility.cc rpl_injector.cc \
 		        ha_heap.cc ha_myisam.cc ha_myisammrg.cc \
 	                ha_berkeley.cc ha_innodb.cc \
 			ha_ndbcluster.cc sql_plugin.cc \

--- 1.286/sql/field.cc	2005-10-06 10:25:57 +02:00
+++ 1.287/sql/field.cc	2006-05-08 13:44:27 +02:00
@@ -8166,6 +8166,14 @@
 }
 
 
+void Field_bit::set_default()
+{
+  my_ptrdiff_t const offset= table->s->default_values - table->record[0];
+  uchar bits= get_rec_bits(bit_ptr + offset, bit_ofs, bit_len);
+  set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
+  Field::set_default();
+}
+
 /*
   Bit field support for non-MyISAM tables.
 */

--- 1.169/sql/field.h	2005-09-14 09:39:43 +02:00
+++ 1.170/sql/field.h	2006-05-08 13:44:27 +02:00
@@ -198,6 +198,16 @@
     { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; }
   inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
   inline bool real_maybe_null(void) { return null_ptr != 0; }
+
+  /*
+    Return a pointer to the last byte of the null bytes where the
+    field conceptually is placed.  In the case that the field does not
+    use any bits of the null bytes, a null pointer is returned.
+   */
+  virtual byte *last_null_byte() const {
+    return reinterpret_cast<byte*>(null_ptr);
+  }
+
   virtual void make_field(Send_field *);
   virtual void sort_string(char *buff,uint length)=0;
   virtual bool optimize_range(uint idx, uint part);
@@ -1348,6 +1358,21 @@
   void sql_type(String &str) const;
   char *pack(char *to, const char *from, uint max_length=~(uint) 0);
   const char *unpack(char* to, const char *from);
+  virtual void set_default();
+  virtual byte *last_null_byte() const {
+    /*
+      Code elsewhere is assuming that bytes are 8 bits, so I'm using
+      that value instead of the correct one: CHAR_BIT.
+
+      REFACTOR SUGGESTION (Matz): Change to use the correct number of
+      bits. On systems with CHAR_BIT > 8 (not very common), the
+      storage will lose the extra bits.
+    */
+    return reinterpret_cast<byte*>(bit_ofs + bit_len > 8 ?
+                                   bit_ptr + 1 : 
+                                   bit_ptr);
+  }
+
   Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
                        char *new_ptr, uchar *new_null_ptr,
                        uint new_null_bit);

--- 1.204/sql/log_event.cc	2005-10-14 11:55:25 +02:00
+++ 1.205/sql/log_event.cc	2006-05-08 13:44:27 +02:00
@@ -24,6 +24,7 @@
 #include "mysql_priv.h"
 #include "slave.h"
 #include "rpl_filter.h"
+#include "rpl_utility.h"
 #include <my_dir.h>
 #endif /* MYSQL_CLIENT */
 #include <my_bitmap.h>
@@ -5069,38 +5070,120 @@
 #ifndef MYSQL_CLIENT
 #ifdef HAVE_REPLICATION
 /*
-  Unpack a row into a record. The row is assumed to only consist of the fields
-  for which the bitset represented by 'arr' and 'bits'; the other parts of the
+  Unpack a row into a record.
+  
+  The row is assumed to only consist of the fields for which the
+  bitset represented by 'arr' and 'bits'; the other parts of the
   record are left alone.
+
+  At most 'colcnt' columns are read: if the table is larger than that,
+  the remaining fields are not filled in.
  */
-static char const *unpack_row(THD *thd, TABLE *table,
-                              char *record, char const *row,
-                              MY_BITMAP const *cols)
+static int
+unpack_row(RELAY_LOG_INFO *rli,
+           TABLE *table, uint column_count, byte *record,
+           char const *row, MY_BITMAP const *cols,
+           char const **row_end, ulong *master_reclength)
 {
   DBUG_ASSERT(record && row);
-
   MY_BITMAP *write_set= table->file->write_set;
-  my_size_t const n_null_bytes= table->s->null_bytes;
+
+  int error= 0;
+
   my_ptrdiff_t const offset= record - (byte*) table->record[0];
 
-  memcpy(record, row, n_null_bytes);
-  char const *ptr= row + n_null_bytes;
+  my_size_t master_null_bytes= table->s->null_bytes;
+  if (table->s->fields != column_count)
+  {
+    /*
+      We should copy the number of null bytes that were put in the row
+      by the master, not what is available on the slave.  To deduce the
+      number of bytes to copy, we look at the null_ptr for the field
+      after the last field on the master.
+    */
+    my_ptrdiff_t field_index= column_count;
+    byte *nb_ptr= NULL;
+    while (nb_ptr == NULL && field_index > 0)
+      nb_ptr= table->field[--field_index]->last_null_byte();
+
+    /*
+      If no preceeding field had a non-null last_null_byte(), the first
+      byte of record[0] is the correct one.
+    */
+    if (nb_ptr == NULL)
+      nb_ptr= table->record[0];
+    
+    master_null_bytes= 1 + nb_ptr - table->record[0];
+  }
+  memcpy(record, row, master_null_bytes);     // [1]
 
   bitmap_set_all(write_set);
+
   Field **const begin_ptr = table->field;
-  for (Field **field_ptr= begin_ptr ; *field_ptr ; ++field_ptr)
+  Field **field_ptr;
   {
-    Field *const f= *field_ptr;
-
-    if (bitmap_is_set(cols, field_ptr -  begin_ptr))
+    char const *ptr= row + master_null_bytes;
+    Field const **end_field_ptr= begin_ptr + column_count;
+    for (field_ptr= begin_ptr; field_ptr < end_field_ptr; ++field_ptr)
     {
+      Field *const f= *field_ptr;
+
       /* Field...::unpack() cannot return 0 */
-      ptr= f->unpack(f->ptr + offset, ptr);
+      if (bitmap_is_set(cols, field_ptr -  begin_ptr))
+        ptr= f->unpack(f->ptr + offset, ptr);
+      else
+        bitmap_clear_bit(write_set, (field_ptr - begin_ptr) + 1);
     }
-    else
+
+    *row_end = ptr;
+    if (master_reclength)
+    {
+      if (*field_ptr)
+        *master_reclength = (*field_ptr)->ptr - table->record[0];
+      else
+        *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 wedo that.
+   */
+  for ( ; *field_ptr ; ++field_ptr)
+  {
+    if (table->s->db_type == DB_TYPE_NDBCLUSTER)
+    {
+      if ((*field_ptr)->flags & NOT_NULL_FLAG)
+      {
+        slave_print_error(rli, ER_NO_DEFAULT_FOR_FIELD,
+                          "Field `%s` of table `%s`.`%s` "
+                          "cannot be NULL",
+                          (*field_ptr)->field_name, table->s->db, 
+                          table->s->table_name);
+        error = ER_NO_DEFAULT_FOR_FIELD;
+        return error;
+      }
       bitmap_clear_bit(write_set, (field_ptr - begin_ptr) + 1);
+      continue;
+    }
+    if ((*field_ptr)->flags & (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG))
+    {
+      slave_print_error(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, 
+                        table->s->table_name);
+      error = ER_NO_DEFAULT_FOR_FIELD;
+    }
+    else
+      (*field_ptr)->set_default();
   }
-  return ptr;
+
+  return error;
 }
 
 int Rows_log_event::exec_event(st_relay_log_info *rli)
@@ -5217,11 +5300,14 @@
 
     error= do_before_row_operations(table);
     while (error == 0 && row_start < m_rows_end) {
-      char const *row_end= do_prepare_row(thd, table, row_start);
+      char const *row_end= NULL;
+      if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
+        break; // We should to the after-row operation even in the
+               // case of error
       DBUG_ASSERT(row_end != NULL); // cannot happen
       DBUG_ASSERT(row_end <= m_rows_end);
 
-      error= do_exec_row(table, rli);
+      error= do_exec_row(table);
       switch (error)
       {
         /* Some recoverable errors */
@@ -5420,7 +5506,7 @@
 
 
 /**************************************************************************
-	Table_map_log_event member functions
+	Table_map_log_event member functions and support functions
 **************************************************************************/
 
 /*
@@ -5447,8 +5533,9 @@
   m_data_size+= 1 + m_colcnt;	// COLCNT and column types
 
   /* If malloc fails, catched in is_valid() */
-  if ((m_memory= m_coltype= my_malloc(m_colcnt, MYF(MY_WME))))
+  if ((m_memory= my_malloc(m_colcnt, MYF(MY_WME))))
   {
+    m_coltype= reinterpret_cast<unsigned char*>(m_memory);
     for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
       m_coltype[i]= m_table->field[i]->type();
   }
@@ -5645,6 +5732,13 @@
     */
     DBUG_ASSERT(m_table->in_use);
 
+    table_def const def(m_coltype, m_colcnt);
+    if (def.compatible_with(rli, m_table))
+    {
+      thd->query_error= 1;
+      DBUG_RETURN(ERR_BAD_TABLE_DEF);
+    }
+
     /*
       We record in the slave's information that the number m_table_id is
       mapped to the m_table object
@@ -5702,11 +5796,11 @@
   DBUG_ASSERT(cbuf_len <= sizeof(cbuf));
 
   return (my_b_safe_write(file, dbuf,      sizeof(dbuf)) ||
-          my_b_safe_write(file, m_dbnam,   m_dblen+1) ||
+          my_b_safe_write(file, (const byte*)m_dbnam,   m_dblen+1) ||
           my_b_safe_write(file, tbuf,      sizeof(tbuf)) ||
-          my_b_safe_write(file, m_tblnam,  m_tbllen+1) ||
-          my_b_safe_write(file, cbuf,      cbuf_len) ||
-          my_b_safe_write(file, m_coltype, m_colcnt));
+          my_b_safe_write(file, (const byte*)m_tblnam,  m_tbllen+1) ||
+          my_b_safe_write(file, reinterpret_cast<byte*>(cbuf), cbuf_len) ||
+          my_b_safe_write(file, reinterpret_cast<byte*>(m_coltype), m_colcnt));
 }
 #endif
 
@@ -5756,6 +5850,25 @@
   rli->m_table_map.clear_tables();
   close_thread_tables(thd_arg);
 }
+
+#ifndef DBUG_OFF
+static void
+print_column_values(char const *text, THD *thd, TABLE *table)
+{
+  THD *old_thd= table->in_use;
+  if (table->in_use == NULL)
+    table->in_use= thd;
+  for (Field **fptr= table->field ; *fptr ; ++fptr)
+  {
+    char buf[MAX_FIELD_WIDTH];
+    String str(buf, sizeof(buf), system_charset_info);
+    (*fptr)->val_str(&str);
+    DBUG_PRINT("info", ("%s for column %d is '%s'", 
+                        text, fptr - table->field, str.c_ptr()));
+  }
+  table->in_use= old_thd;
+}
+#endif
 #endif
 
 /**************************************************************************
@@ -5830,19 +5943,22 @@
   DBUG_RETURN(error);
 }
 
-char const *Write_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
-                                                 char const *row_start)
+int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+                                         TABLE *table,
+                                         char const *row_start,
+                                         char const **row_end)
 {
-  char const *ptr= row_start;
   DBUG_ASSERT(table != NULL);
-  /*
-    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(ptr);
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
-  return ptr;
+  DBUG_ASSERT(row_start && row_end);
+
+  int error;
+  error= unpack_row(rli,
+                    table, m_width, (byte*)table->record[0],
+                    row_start, &m_cols, row_end, &m_master_reclength);
+#ifndef DBUG_OFF
+  print_column_values("Unpacked value", thd, table);
+#endif
+  return error;
 }
 
 /*
@@ -5898,24 +6014,33 @@
 
   @param thd    Thread context for writing the record.
   @param table  Table to which record should be written.
-
+  @param master_reclength
+         Offset to first column that is not present on the master,
+         alternatively the length of the record on the master side.
   @return Error code on failure, 0 on success.
  */
 static int
-replace_record(THD *thd, TABLE *table)
+replace_record(THD *thd, TABLE *table,
+               ulong const master_reclength,
+               uint const master_fields)
 {
+  DBUG_ENTER("replace_record");
   DBUG_ASSERT(table != NULL && thd != NULL);
 
   int error;
   int keynum;
   auto_afree_ptr<char> key(NULL);
 
+#ifndef DBUG_OFF
+  print_column_values("Starting write value", thd, table);
+#endif
+
   while ((error= table->file->ha_write_row(table->record[0])))
   {
     if ((keynum= table->file->get_dup_key(error)) < 0)
     {
       /* We failed to retrieve the duplicate key */
-      return HA_ERR_FOUND_DUPP_KEY;
+      DBUG_RETURN(HA_ERR_FOUND_DUPP_KEY);
     }
 
     /*
@@ -5932,20 +6057,20 @@
     {
       error= table->file->rnd_pos(table->record[1], table->file->dupp_ref);
       if (error)
-        return error;
+        DBUG_RETURN(error);
     }
     else
     {
       if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
       {
-        return my_errno;
+        DBUG_RETURN(my_errno);
       }
 
       if (key.get() == NULL)
       {
         key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
         if (key.get() == NULL)
-          return ENOMEM;
+          DBUG_RETURN(ENOMEM);
       }
 
       key_copy(key.get(), table->record[0], table->key_info + keynum, 0);
@@ -5953,7 +6078,7 @@
                                          table->key_info[keynum].key_length,
                                          HA_READ_KEY_EXACT);
       if (error)
-        return error;
+        DBUG_RETURN(error);
     }
 
     /*
@@ -5961,6 +6086,59 @@
        will enable us to update it or, alternatively, delete it (so
        that we can insert the new row afterwards).
 
+       First we copy the columns into table->record[0] that are not
+       present on the master from table->record[1], if there are any.
+    */
+
+    DBUG_PRINT("info", ("Copying to %p from offset %u to %u", 
+                         table->record[0], 
+                         master_reclength, table->s->reclength));
+#ifndef DBUG_OFF
+    print_column_values("After copy value", thd, table);
+#endif
+    if (master_reclength < table->s->reclength)
+      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
+      not a very good solution: it should be refactored on
+      opportunity.
+
+      REFACTORING SUGGESTION (Matz).  Introduce a member function
+      similar to move_field_offset() called copy_field_offset() to
+      copy field values and implement it for all Field subclasses. Use
+      this function to copy data from the found record to the record
+      that are going to be inserted.
+
+      The copy_field_offset() function need to be a virtual function,
+      which in this case will prevent copying an entire range of
+      fields efficiently.
+     */
+    {
+      Field **field_ptr= table->field + master_fields;
+      for ( ; *field_ptr ; ++field_ptr)
+      {
+        switch ((*field_ptr)->real_type())
+        {
+        default:
+          /* Nothing to do */
+          break;
+
+        case FIELD_TYPE_BIT:
+          Field_bit *f= static_cast<Field_bit*>(*field_ptr);
+          my_ptrdiff_t const offset= table->record[1] - table->record[0];
+          uchar const bits=
+            get_rec_bits(f->bit_ptr + offset, f->bit_ofs, f->bit_len);
+          set_rec_bits(bits, f->bit_ptr, f->bit_ofs, f->bit_len);
+          break;
+        }
+      }
+    }
+    
+    /*
        REPLACE is defined as either INSERT or DELETE + INSERT.  If
        possible, we can replace it with an UPDATE, but that will not
        work on InnoDB if FOREIGN KEY checks are necessary.
@@ -5980,12 +6158,12 @@
     {
       error=table->file->ha_update_row(table->record[1],
                                        table->record[0]);
-      return error;
+      DBUG_RETURN(error);
     }
     else
     {
       if ((error= table->file->ha_delete_row(table->record[1])))
-        return error;
+        DBUG_RETURN(error);
 
       /*
          Matz: Do we need to set no_trans_update for non-transactional
@@ -5995,13 +6173,13 @@
       /* Will retry ha_write_row() with the offending row removed. */
     }
   }
-  return error;
+  DBUG_RETURN(error);
 }
 
-int Write_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Write_rows_log_event::do_exec_row(TABLE *table)
 {
   DBUG_ASSERT(table != NULL);
-  int error= replace_record(thd, table);
+  int error= replace_record(thd, table, m_master_reclength, m_width);
   return error;
 }
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
@@ -6239,20 +6417,22 @@
   return error;
 }
 
-char const *Delete_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
-                                                  char const *row_start)
+int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+                                          TABLE *table,
+                                          char const *row_start,
+                                          char const **row_end)
 {
-  char const *ptr= row_start;
-  DBUG_ASSERT(ptr);
+  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(ptr != NULL);
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
-
+  error= unpack_row(rli,
+                    table, m_width, table->record[0], 
+                    row_start, &m_cols, 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.
@@ -6269,14 +6449,14 @@
     key_copy(m_key, table->record[0], key_info, 0);
   }
 
-  return ptr;
+  return error;
 }
 
-int Delete_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Delete_rows_log_event::do_exec_row(TABLE *table)
 {
   DBUG_ENTER("Delete_rows_log_event::do_exec_row(TABLE*,...)");
-  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
-		       table, table->s->table_name, rli));
+  DBUG_PRINT("enter", ("table=%p (%s)",
+		       table, table->s->table_name));
   DBUG_ASSERT(table != NULL);
 
   int error= find_and_fetch_row(table, m_key, m_search_record);
@@ -6409,11 +6589,13 @@
   return error;
 }
 
-char const *Update_rows_log_event::do_prepare_row(THD *thd, TABLE *table,
-                                                  char const *row_start)
+int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
+                                          TABLE *table,
+                                          char const *row_start,
+                                          char const **row_end)
 {
-  char const *ptr= row_start;
-  DBUG_ASSERT(ptr);
+  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.
@@ -6421,10 +6603,14 @@
   DBUG_ASSERT(table->s->fields >= m_width);
 
   /* record[0] is the before image for the update */
-  ptr= unpack_row(thd, table, table->record[0], ptr, &m_cols);
-  DBUG_ASSERT(ptr != NULL);
+  error= unpack_row(rli,
+                    table, m_width, table->record[0],
+                    row_start, &m_cols, row_end, &m_master_reclength);
+  row_start = *row_end;
   /* record[1] is the after image for the update */
-  ptr= unpack_row(thd, table, table->record[1], ptr, &m_cols);
+  error= unpack_row(rli,
+                    table, m_width, table->record[1],
+                    row_start, &m_cols, row_end, &m_master_reclength);
 
   /*
     If we will access rows using the random access method, m_key will
@@ -6442,14 +6628,14 @@
     key_copy(m_key, table->record[0], key_info, 0);
   }
 
-  return ptr;
+  return error;
 }
 
-int Update_rows_log_event::do_exec_row(TABLE *table, st_relay_log_info *rli)
+int Update_rows_log_event::do_exec_row(TABLE *table)
 {
   DBUG_ENTER("Update_rows_log_event::do_exec_row(TABLE*,...)");
-  DBUG_PRINT("enter", ("table=%p (%s), rli=%p",
-		       table, table->s->table_name, rli));
+  DBUG_PRINT("enter", ("table=%p (%s)",
+		       table, table->s->table_name));
   DBUG_ASSERT(table != NULL);
 
   int error= find_and_fetch_row(table, m_key, m_search_record);

--- 1.120/sql/log_event.h	2005-10-07 14:56:30 +02:00
+++ 1.121/sql/log_event.h	2006-05-08 13:44:27 +02:00
@@ -1623,12 +1623,23 @@
     TYPE_CODE = TABLE_MAP_EVENT
   };
 
+  enum enum_error
+  {
+    ERR_OPEN_FAILURE = -1,                 /* Failure to open table */
+    ERR_OK = 0,                                  /* No error */
+    ERR_TABLE_LIMIT_EXCEEDED = 1,        /* No more room for tables */
+    ERR_OUT_OF_MEM = 2,                         /* Out of memory */
+    ERR_BAD_TABLE_DEF = 3,       /* Table definition does not match */
+    ERR_RBR_TO_SBR = 4    /* daisy-chanining RBR to SBR not allowed */
+  };
+
   enum enum_flag
   {
     /* 
        Nothing here right now, but the flags support is there in
        preparation for changes that are coming.
     */
+    ENUM_FLAG_COUNT
   };
 
   typedef uint16 flag_set;
@@ -1691,7 +1702,7 @@
   char const    *m_tblnam;
   my_size_t      m_tbllen;
   ulong          m_colcnt;
-  byte          *m_coltype;
+  unsigned char *m_coltype;
 
   gptr           m_memory;
   ulong          m_table_id;
@@ -1824,6 +1835,7 @@
   ulong       m_table_id;	/* Table ID */
   MY_BITMAP   m_cols;		/* Bitmap denoting columns available */
   ulong       m_width;          /* The width of the columns bitmap */
+  ulong       m_master_reclength; /* Length of record on master side */
 
   /* Bit buffer in the same memory as the class */
   uint32      m_bitbuf[128/(sizeof(uint32)*8)];
@@ -1877,12 +1889,15 @@
       since SQL thread specific data is not available: that data is made
       available for the do_exec function.
 
-    RETURN VALUE
       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. 
+
+    RETURN VALUE
+      Error code, if something went wrong, 0 otherwise.
    */
-  virtual char const *do_prepare_row(THD*, TABLE*, char const *row_start) = 0;
+  virtual int do_prepare_row(THD*, struct st_relay_log_info*, TABLE*,
+                             char const *row_start, char const **row_end) = 0;
 
   /*
     Primitive to do the actual execution necessary for a row.
@@ -1896,7 +1911,7 @@
       0 if execution succeeded, 1 if execution failed.
       
   */
-  virtual int do_exec_row(TABLE *table, st_relay_log_info *rli) = 0;
+  virtual int do_exec_row(TABLE *table) = 0;
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
 };
 
@@ -1955,10 +1970,11 @@
   gptr  m_memory;
   byte *m_search_record;
 
-  virtual int         do_before_row_operations(TABLE *table);
-  virtual int         do_after_row_operations(TABLE *table, int error);
-  virtual char const *do_prepare_row(THD*, TABLE*, char const *row_start);
-  virtual int         do_exec_row(TABLE *table, st_relay_log_info *rli);
+  virtual int do_before_row_operations(TABLE *table);
+  virtual int do_after_row_operations(TABLE *table, int error);
+  virtual int do_prepare_row(THD*, struct st_relay_log_info*, TABLE*,
+                             char const *row_start, char const **row_end);
+  virtual int do_exec_row(TABLE *table);
 #endif
 };
 
@@ -2019,10 +2035,11 @@
   byte *m_key;
   byte *m_search_record;
 
-  virtual int         do_before_row_operations(TABLE *table);
-  virtual int         do_after_row_operations(TABLE *table, int error);
-  virtual char const *do_prepare_row(THD*, TABLE*, char const *row_start);
-  virtual int         do_exec_row(TABLE *table, st_relay_log_info *rli);
+  virtual int do_before_row_operations(TABLE *table);
+  virtual int do_after_row_operations(TABLE *table, int error);
+  virtual int do_prepare_row(THD*, struct st_relay_log_info*, TABLE*,
+                             char const *row_start, char const **row_end);
+  virtual int do_exec_row(TABLE *table);
 #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
 };
 
@@ -2088,10 +2105,11 @@
   byte *m_key;
   byte *m_search_record;
 
-  virtual int         do_before_row_operations(TABLE *table);
-  virtual int         do_after_row_operations(TABLE *table, int error);
-  virtual char const *do_prepare_row(THD*, TABLE*, char const *row_start);
-  virtual int         do_exec_row(TABLE *table, st_relay_log_info *rli);
+  virtual int do_before_row_operations(TABLE *table);
+  virtual int do_after_row_operations(TABLE *table, int error);
+  virtual int do_prepare_row(THD*, struct st_relay_log_info*, TABLE*,
+                             char const *row_start, char const **row_end);
+  virtual int do_exec_row(TABLE *table);
 #endif
 };
 

--- 1.338/sql/mysql_priv.h	2006-03-29 16:08:09 +02:00
+++ 1.339/sql/mysql_priv.h	2006-05-08 13:44:27 +02:00
@@ -21,6 +21,9 @@
   except the part which must be in the server and in the client.
 */
 
+#ifndef MYSQL_PRIV_H
+#define MYSQL_PRIV_H
+
 #ifndef MYSQL_CLIENT
 
 #include <my_global.h>
@@ -1565,3 +1568,5 @@
 #endif
 
 #endif /* MYSQL_CLIENT */
+
+#endif /* MYSQL_PRIV_H */

--- 1.58/sql/share/errmsg.txt	2005-10-21 07:06:54 +02:00
+++ 1.59/sql/share/errmsg.txt	2006-05-08 13:44:28 +02:00
@@ -5422,6 +5422,8 @@
 	eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
 ER_BINLOG_ROW_LOGGING_FAILED
         eng "Writing one row to the row-based binary log failed"
+ER_BINLOG_ROW_WRONG_TABLE_DEF
+	eng "Table definition on master and slave does not match"
 ER_PARTITION_REQUIRES_VALUES_ERROR
         eng "%s PARTITIONING requires definition of VALUES %s for each partition"
--- New file ---
+++ mysql-test/extra/rpl_tests/rpl_row_tabledefs.test	06/05/08 13:44:28
# Test how replication of tables work when the definition on the
# master and slave differs.

# Consider making these part of the basic RBR tests.

connection master;
--disable_warnings
--disable_query_log
DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
--enable_query_log
--enable_warnings
sync_slave_with_master;
STOP SLAVE;
SET GLOBAL SQL_MODE='STRICT_ALL_TABLES';
START SLAVE;

connection master;
eval CREATE TABLE t1_int (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t1_bit (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t1_char (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t1_nodef (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
eval CREATE TABLE t4 (a INT PRIMARY KEY) ENGINE=$engine_type;
eval CREATE TABLE t5 (a INT PRIMARY KEY, b INT, c INT) ENGINE=$engine_type;
eval CREATE TABLE t6 (a INT PRIMARY KEY, b INT, c INT) ENGINE=$engine_type;

# Table used to detect that slave is running
eval CREATE TABLE t9 (a INT PRIMARY KEY) ENGINE=$engine_type;

sync_slave_with_master;

# On the slave, we add one INT column last in table 't1_int',
ALTER TABLE t1_int ADD x INT DEFAULT 42;
# ... and add one BIT column last in table 't1_bit',
ALTER TABLE t1_bit ADD x BIT(3) DEFAULT b'011';
# ... and add one CHAR column last in table 't1_char',
ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test';
# ... and add one non-nullable INT column last in table 't1_text'
#     with no default,
ALTER TABLE t1_nodef ADD x INT NOT NULL;
# ... and remove the last column in t2
ALTER TABLE t2 DROP b;
# ... change the type of the single column in table 't4'
ALTER TABLE t4 MODIFY a FLOAT;
# ... change the type of the middle column of table 't5'
ALTER TABLE t5 MODIFY b FLOAT;
# ... change the type of the last column of table 't6'
ALTER TABLE t6 MODIFY c FLOAT;

# Insert some values for tables on slave side. These should not be
# modified when the row from the master is applied.
INSERT INTO t1_int  VALUES (2,4,4711);
INSERT INTO t1_char VALUES (2,4,'Foo is a bar');
INSERT INTO t1_bit  VALUES (2,4,b'101');

--echo **** On Master ****
connection master;
INSERT INTO t1_int VALUES (1,2);
INSERT INTO t1_int VALUES (2,5);
INSERT INTO t1_bit VALUES (1,2);
INSERT INTO t1_bit VALUES (2,5);
INSERT INTO t1_char VALUES (1,2);
INSERT INTO t1_char VALUES (2,5);
SELECT * FROM t1_int ORDER BY a;
SELECT * FROM t1_bit ORDER BY a;
SELECT * FROM t1_char ORDER BY a;
--echo **** On Slave ****
sync_slave_with_master;
SELECT a,b,x FROM t1_int ORDER BY a;
SELECT a,b,HEX(x) FROM t1_bit ORDER BY a;
SELECT a,b,x FROM t1_char ORDER BY a;

# Each of these should generate an error and stop the slave

connection master;
INSERT INTO t9 VALUES (2);
sync_slave_with_master;
# Now slave is guaranteed to be running
connection master;
INSERT INTO t1_nodef VALUES (1,2);
connection slave;
wait_for_slave_to_stop;
--replace_result $MASTER_MYPORT MASTER_PORT
--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
--query_vertical SHOW SLAVE STATUS;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;

connection master;
INSERT INTO t9 VALUES (3);
sync_slave_with_master;
# Now slave is guaranteed to be running
connection master;
INSERT INTO t2 VALUES (2,4);
connection slave;
wait_for_slave_to_stop;
--replace_result $MASTER_MYPORT MASTER_PORT
--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
--query_vertical SHOW SLAVE STATUS;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;

connection master;
INSERT INTO t9 VALUES (4);
sync_slave_with_master;
# Now slave is guaranteed to be running
connection master;
INSERT INTO t4 VALUES (4);
connection slave;
wait_for_slave_to_stop;
--replace_result $MASTER_MYPORT MASTER_PORT
--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
--query_vertical SHOW SLAVE STATUS;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;

connection master;
INSERT INTO t9 VALUES (5);
sync_slave_with_master;
# Now slave is guaranteed to be running
connection master;
INSERT INTO t5 VALUES (5,10,25);
connection slave;
wait_for_slave_to_stop;
--replace_result $MASTER_MYPORT MASTER_PORT
--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
--query_vertical SHOW SLAVE STATUS;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;

connection master;
INSERT INTO t9 VALUES (6);
sync_slave_with_master;
# Now slave is guaranteed to be running
connection master;
INSERT INTO t6 VALUES (6,12,36);
connection slave;
wait_for_slave_to_stop;
--replace_result $MASTER_MYPORT MASTER_PORT
--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
--query_vertical SHOW SLAVE STATUS;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;

connection master;
--disable_warnings
DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
--enable_warnings
sync_slave_with_master;

# and some trickier ones

--echo **** On Master ****
--connection master
eval CREATE TABLE t1 (
		pk CHAR(12) NOT NULL,
		a BIT(8),
		f TINYINT,
		b BIT(4),
		c BIT(2),
		h BINARY(4),
		d BIT(4),
		e BIT(5),
		g BINARY(116),
		i BINARY(4),
		j BINARY(4),
             PRIMARY KEY (pk) ) ENGINE=$engine_type;

INSERT INTO t1 VALUES
 ("ab",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
 ("cd",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
 ("ef",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
 ("gh",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");

SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;
ALTER TABLE t1 ADD COLUMN k BIT(11);
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
UPDATE t1 set k = b'101';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;

--echo **** On Master ****
--connection master
INSERT INTO t1 VALUES
 ("ij",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
 ("kl",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
 ("mn",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
 ("op",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
UPDATE t1 SET k = b'1111110' where k is NULL;
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;

--echo **** On Master ****
--connection master
UPDATE t1 SET a = b'0';

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
UPDATE t1 set k = b'0';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;

--echo **** On Master ****
--connection master
UPDATE t1 SET j = NULL;

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;

--echo **** On Master ****
--connection master
drop TABLE t1;
--sync_slave_with_master


--echo **** On Master ****
--connection master
eval CREATE TABLE t1 ( a INT key,b INT,c INT,d INT,e INT,f INT,g INT,h INT) ENGINE=$engine_type;

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
ALTER TABLE t1 ADD COLUMN j BIT(8);
SELECT * FROM t1;

--echo **** On Master ****
--connection master
INSERT INTO t1 VALUES (1,1,1,1,1,1,1,1);

--echo **** On Slave ****
--sync_slave_with_master
--connection slave
SELECT * FROM t1;

--echo **** On Master ****
--connection master
drop TABLE t1;
--sync_slave_with_master

--- New file ---
+++ mysql-test/r/rpl_ndb_tabledefs.result	06/05/08 13:44:28
stop slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
STOP SLAVE;
SET GLOBAL SQL_MODE='STRICT_ALL_TABLES';
START SLAVE;
CREATE TABLE t1_int (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t1_bit (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t1_char (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t1_nodef (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='ndbcluster';
CREATE TABLE t4 (a INT PRIMARY KEY) ENGINE='ndbcluster';
CREATE TABLE t5 (a INT PRIMARY KEY, b INT, c INT) ENGINE='ndbcluster';
CREATE TABLE t6 (a INT PRIMARY KEY, b INT, c INT) ENGINE='ndbcluster';
CREATE TABLE t9 (a INT PRIMARY KEY) ENGINE='ndbcluster';
ALTER TABLE t1_int ADD x INT DEFAULT 42;
ALTER TABLE t1_bit ADD x BIT(3) DEFAULT b'011';
ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test';
ALTER TABLE t1_nodef ADD x INT NOT NULL;
ALTER TABLE t2 DROP b;
ALTER TABLE t4 MODIFY a FLOAT;
ALTER TABLE t5 MODIFY b FLOAT;
ALTER TABLE t6 MODIFY c FLOAT;
INSERT INTO t1_int  VALUES (2,4,4711);
INSERT INTO t1_char VALUES (2,4,'Foo is a bar');
INSERT INTO t1_bit  VALUES (2,4,b'101');
**** On Master ****
INSERT INTO t1_int VALUES (1,2);
INSERT INTO t1_int VALUES (2,5);
INSERT INTO t1_bit VALUES (1,2);
INSERT INTO t1_bit VALUES (2,5);
INSERT INTO t1_char VALUES (1,2);
INSERT INTO t1_char VALUES (2,5);
SELECT * FROM t1_int ORDER BY a;
a	b
1	2
2	5
SELECT * FROM t1_bit ORDER BY a;
a	b
1	2
2	5
SELECT * FROM t1_char ORDER BY a;
a	b
1	2
2	5
**** On Slave ****
SELECT a,b,x FROM t1_int ORDER BY a;
a	b	x
1	2	NULL
2	5	4711
SELECT a,b,HEX(x) FROM t1_bit ORDER BY a;
a	b	HEX(x)
1	2	NULL
2	5	5
SELECT a,b,x FROM t1_char ORDER BY a;
a	b	x
1	2	NULL
2	5	Foo is a bar
INSERT INTO t9 VALUES (2);
INSERT INTO t1_nodef VALUES (1,2);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
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	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (3);
INSERT INTO t2 VALUES (2,4);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Table width mismatch - received 2 columns, test.t2 has 1 columns
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (4);
INSERT INTO t4 VALUES (4);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 0 type mismatch - received type 3, test.t4 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (5);
INSERT INTO t5 VALUES (5,10,25);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 1 type mismatch - received type 3, test.t5 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (6);
INSERT INTO t6 VALUES (6,12,36);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 2 type mismatch - received type 3, test.t6 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
**** On Master ****
CREATE TABLE t1 (
pk CHAR(12) NOT NULL,
a BIT(8),
f TINYINT,
b BIT(4),
c BIT(2),
h BINARY(4),
d BIT(4),
e BIT(5),
g BINARY(116),
i BINARY(4),
j BINARY(4),
PRIMARY KEY (pk) ) ENGINE='ndbcluster';
INSERT INTO t1 VALUES
("ab",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
("cd",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("ef",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("gh",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g
ab	A3	2	5	2	b	6	C	c
cd	A3	2	5	2	NULL	6	C	c
ef	A3	NULL	5	2	NULL	6	C	c
gh	A3	NULL	5	2	b	6	C	c
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g
ab	A3	2	5	2	b	6	C	c
cd	A3	2	5	2	NULL	6	C	c
ef	A3	NULL	5	2	NULL	6	C	c
gh	A3	NULL	5	2	b	6	C	c
ALTER TABLE t1 ADD COLUMN k BIT(11);
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	NULL
cd	A3	2	5	2	NULL	6	C	c	NULL
ef	A3	NULL	5	2	NULL	6	C	c	NULL
gh	A3	NULL	5	2	b	6	C	c	NULL
UPDATE t1 set k = b'101';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
**** On Master ****
INSERT INTO t1 VALUES
("ij",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
("kl",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("mn",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("op",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
ij	A3	2	5	2	b	6	C	c	NULL
kl	A3	2	5	2	NULL	6	C	c	NULL
mn	A3	NULL	5	2	NULL	6	C	c	NULL
op	A3	NULL	5	2	b	6	C	c	NULL
UPDATE t1 SET k = b'1111110' where k is NULL;
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
ij	A3	2	5	2	b	6	C	c	7E
kl	A3	2	5	2	NULL	6	C	c	7E
mn	A3	NULL	5	2	NULL	6	C	c	7E
op	A3	NULL	5	2	b	6	C	c	7E
**** On Master ****
UPDATE t1 SET a = b'0';
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	5
cd	0	2	5	2	NULL	6	C	c	5
ef	0	NULL	5	2	NULL	6	C	c	5
gh	0	NULL	5	2	b	6	C	c	5
ij	0	2	5	2	b	6	C	c	7E
kl	0	2	5	2	NULL	6	C	c	7E
mn	0	NULL	5	2	NULL	6	C	c	7E
op	0	NULL	5	2	b	6	C	c	7E
UPDATE t1 set k = b'0';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	0
cd	0	2	5	2	NULL	6	C	c	0
ef	0	NULL	5	2	NULL	6	C	c	0
gh	0	NULL	5	2	b	6	C	c	0
ij	0	2	5	2	b	6	C	c	0
kl	0	2	5	2	NULL	6	C	c	0
mn	0	NULL	5	2	NULL	6	C	c	0
op	0	NULL	5	2	b	6	C	c	0
**** On Master ****
UPDATE t1 SET j = NULL;
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	0
cd	0	2	5	2	NULL	6	C	c	0
ef	0	NULL	5	2	NULL	6	C	c	0
gh	0	NULL	5	2	b	6	C	c	0
ij	0	2	5	2	b	6	C	c	0
kl	0	2	5	2	NULL	6	C	c	0
mn	0	NULL	5	2	NULL	6	C	c	0
op	0	NULL	5	2	b	6	C	c	0
**** On Master ****
drop TABLE t1;
**** On Master ****
CREATE TABLE t1 ( a INT key,b INT,c INT,d INT,e INT,f INT,g INT,h INT) ENGINE='ndbcluster';
**** On Slave ****
ALTER TABLE t1 ADD COLUMN j BIT(8);
SELECT * FROM t1;
a	b	c	d	e	f	g	h	j
**** On Master ****
INSERT INTO t1 VALUES (1,1,1,1,1,1,1,1);
**** On Slave ****
SELECT * FROM t1;
a	b	c	d	e	f	g	h	j
1	1	1	1	1	1	1	1	NULL
**** On Master ****
drop TABLE t1;

--- New file ---
+++ mysql-test/r/rpl_row_tabledefs.result	06/05/08 13:44:28
stop slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
STOP SLAVE;
SET GLOBAL SQL_MODE='STRICT_ALL_TABLES';
START SLAVE;
CREATE TABLE t1_int (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t1_bit (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t1_char (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t1_nodef (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t4 (a INT PRIMARY KEY) ENGINE='MyISAM';
CREATE TABLE t5 (a INT PRIMARY KEY, b INT, c INT) ENGINE='MyISAM';
CREATE TABLE t6 (a INT PRIMARY KEY, b INT, c INT) ENGINE='MyISAM';
CREATE TABLE t9 (a INT PRIMARY KEY) ENGINE='MyISAM';
ALTER TABLE t1_int ADD x INT DEFAULT 42;
ALTER TABLE t1_bit ADD x BIT(3) DEFAULT b'011';
ALTER TABLE t1_char ADD x CHAR(20) DEFAULT 'Just a test';
ALTER TABLE t1_nodef ADD x INT NOT NULL;
ALTER TABLE t2 DROP b;
ALTER TABLE t4 MODIFY a FLOAT;
ALTER TABLE t5 MODIFY b FLOAT;
ALTER TABLE t6 MODIFY c FLOAT;
INSERT INTO t1_int  VALUES (2,4,4711);
INSERT INTO t1_char VALUES (2,4,'Foo is a bar');
INSERT INTO t1_bit  VALUES (2,4,b'101');
**** On Master ****
INSERT INTO t1_int VALUES (1,2);
INSERT INTO t1_int VALUES (2,5);
INSERT INTO t1_bit VALUES (1,2);
INSERT INTO t1_bit VALUES (2,5);
INSERT INTO t1_char VALUES (1,2);
INSERT INTO t1_char VALUES (2,5);
SELECT * FROM t1_int ORDER BY a;
a	b
1	2
2	5
SELECT * FROM t1_bit ORDER BY a;
a	b
1	2
2	5
SELECT * FROM t1_char ORDER BY a;
a	b
1	2
2	5
**** On Slave ****
SELECT a,b,x FROM t1_int ORDER BY a;
a	b	x
1	2	42
2	5	4711
SELECT a,b,HEX(x) FROM t1_bit ORDER BY a;
a	b	HEX(x)
1	2	3
2	5	5
SELECT a,b,x FROM t1_char ORDER BY a;
a	b	x
1	2	Just a test
2	5	Foo is a bar
INSERT INTO t9 VALUES (2);
INSERT INTO t1_nodef VALUES (1,2);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
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	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (3);
INSERT INTO t2 VALUES (2,4);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Table width mismatch - received 2 columns, test.t2 has 1 columns
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (4);
INSERT INTO t4 VALUES (4);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 0 type mismatch - received type 3, test.t4 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (5);
INSERT INTO t5 VALUES (5,10,25);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 1 type mismatch - received type 3, test.t5 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
INSERT INTO t9 VALUES (6);
INSERT INTO t6 VALUES (6,12,36);
SHOW SLAVE STATUS;;
Slave_IO_State	#
Master_Host	127.0.0.1
Master_User	root
Master_Port	MASTER_PORT
Connect_Retry	1
Master_Log_File	master-bin.000001
Read_Master_Log_Pos	#
Relay_Log_File	#
Relay_Log_Pos	#
Relay_Master_Log_File	master-bin.000001
Slave_IO_Running	Yes
Slave_SQL_Running	No
Replicate_Do_DB	
Replicate_Ignore_DB	
Replicate_Do_Table	
Replicate_Ignore_Table	
Replicate_Wild_Do_Table	
Replicate_Wild_Ignore_Table	
Last_Errno	1454
Last_Error	Column 2 type mismatch - received type 3, test.t6 has type 4
Skip_Counter	0
Exec_Master_Log_Pos	#
Relay_Log_Space	#
Until_Condition	None
Until_Log_File	
Until_Log_Pos	0
Master_SSL_Allowed	No
Master_SSL_CA_File	
Master_SSL_CA_Path	
Master_SSL_Cert	
Master_SSL_Cipher	
Master_SSL_Key	
Seconds_Behind_Master	#
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=4;
START SLAVE;
DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
**** On Master ****
CREATE TABLE t1 (
pk CHAR(12) NOT NULL,
a BIT(8),
f TINYINT,
b BIT(4),
c BIT(2),
h BINARY(4),
d BIT(4),
e BIT(5),
g BINARY(116),
i BINARY(4),
j BINARY(4),
PRIMARY KEY (pk) ) ENGINE='MyISAM';
INSERT INTO t1 VALUES
("ab",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
("cd",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("ef",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("gh",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g
ab	A3	2	5	2	b	6	C	c
cd	A3	2	5	2	NULL	6	C	c
ef	A3	NULL	5	2	NULL	6	C	c
gh	A3	NULL	5	2	b	6	C	c
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g
ab	A3	2	5	2	b	6	C	c
cd	A3	2	5	2	NULL	6	C	c
ef	A3	NULL	5	2	NULL	6	C	c
gh	A3	NULL	5	2	b	6	C	c
ALTER TABLE t1 ADD COLUMN k BIT(11);
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	NULL
cd	A3	2	5	2	NULL	6	C	c	NULL
ef	A3	NULL	5	2	NULL	6	C	c	NULL
gh	A3	NULL	5	2	b	6	C	c	NULL
UPDATE t1 set k = b'101';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
**** On Master ****
INSERT INTO t1 VALUES
("ij",b'10100011',2,   b'0101',b'10',"b", b'0110',b'1100',"c","d","e"),
("kl",b'10100011',2,   b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("mn",b'10100011',NULL,b'0101',b'10',NULL,b'0110',b'1100',"c","d","e"),
("op",b'10100011',NULL,b'0101',b'10',"b", b'0110',b'1100',"c","d","e");
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
ij	A3	2	5	2	b	6	C	c	NULL
kl	A3	2	5	2	NULL	6	C	c	NULL
mn	A3	NULL	5	2	NULL	6	C	c	NULL
op	A3	NULL	5	2	b	6	C	c	NULL
UPDATE t1 SET k = b'1111110' where k is NULL;
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	A3	2	5	2	b	6	C	c	5
cd	A3	2	5	2	NULL	6	C	c	5
ef	A3	NULL	5	2	NULL	6	C	c	5
gh	A3	NULL	5	2	b	6	C	c	5
ij	A3	2	5	2	b	6	C	c	7E
kl	A3	2	5	2	NULL	6	C	c	7E
mn	A3	NULL	5	2	NULL	6	C	c	7E
op	A3	NULL	5	2	b	6	C	c	7E
**** On Master ****
UPDATE t1 SET a = b'0';
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	NULL
cd	0	2	5	2	NULL	6	C	c	NULL
ef	0	NULL	5	2	NULL	6	C	c	NULL
gh	0	NULL	5	2	b	6	C	c	NULL
ij	0	2	5	2	b	6	C	c	NULL
kl	0	2	5	2	NULL	6	C	c	NULL
mn	0	NULL	5	2	NULL	6	C	c	NULL
op	0	NULL	5	2	b	6	C	c	NULL
UPDATE t1 set k = b'0';
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	0
cd	0	2	5	2	NULL	6	C	c	0
ef	0	NULL	5	2	NULL	6	C	c	0
gh	0	NULL	5	2	b	6	C	c	0
ij	0	2	5	2	b	6	C	c	0
kl	0	2	5	2	NULL	6	C	c	0
mn	0	NULL	5	2	NULL	6	C	c	0
op	0	NULL	5	2	b	6	C	c	0
**** On Master ****
UPDATE t1 SET j = NULL;
**** On Slave ****
SELECT pk,HEX(a),f,HEX(b),HEX(c),h,HEX(d),HEX(e),g,HEX(k) FROM t1 ORDER BY pk;
pk	HEX(a)	f	HEX(b)	HEX(c)	h	HEX(d)	HEX(e)	g	HEX(k)
ab	0	2	5	2	b	6	C	c	NULL
cd	0	2	5	2	NULL	6	C	c	NULL
ef	0	NULL	5	2	NULL	6	C	c	NULL
gh	0	NULL	5	2	b	6	C	c	NULL
ij	0	2	5	2	b	6	C	c	NULL
kl	0	2	5	2	NULL	6	C	c	NULL
mn	0	NULL	5	2	NULL	6	C	c	NULL
op	0	NULL	5	2	b	6	C	c	NULL
**** On Master ****
drop TABLE t1;
**** On Master ****
CREATE TABLE t1 ( a INT key,b INT,c INT,d INT,e INT,f INT,g INT,h INT) ENGINE='MyISAM';
**** On Slave ****
ALTER TABLE t1 ADD COLUMN j BIT(8);
SELECT * FROM t1;
a	b	c	d	e	f	g	h	j
**** On Master ****
INSERT INTO t1 VALUES (1,1,1,1,1,1,1,1);
**** On Slave ****
SELECT * FROM t1;
a	b	c	d	e	f	g	h	j
1	1	1	1	1	1	1	1	NULL
**** On Master ****
drop TABLE t1;

--- New file ---
+++ mysql-test/t/rpl_ndb_tabledefs.test	06/05/08 13:44:29

-- source include/have_binlog_format_row.inc
-- source include/master-slave.inc

let $engine_type = 'ndbcluster';
-- source extra/rpl_tests/rpl_row_tabledefs.test



--- New file ---
+++ mysql-test/t/rpl_row_tabledefs.test	06/05/08 13:44:29

-- source include/have_binlog_format_row.inc
-- source include/master-slave.inc

let $engine_type = 'MyISAM';
-- source extra/rpl_tests/rpl_row_tabledefs.test



--- New file ---
+++ sql/rpl_utility.cc	06/05/08 13:44:30
/* Copyright 2006 MySQL AB. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "rpl_utility.h"

uint32
field_length_from_packed(enum_field_types const field_type, 
                         byte const *const data)
{
  uint32 length;

  switch (field_type) {
  case MYSQL_TYPE_DECIMAL:
  case MYSQL_TYPE_NEWDECIMAL:
    length= ~0UL;
    break;
  case MYSQL_TYPE_YEAR:
  case MYSQL_TYPE_TINY:
    length= 1;
    break;
  case MYSQL_TYPE_SHORT:
    length= 2;
    break;
  case MYSQL_TYPE_INT24:
    length= 3;
    break;
  case MYSQL_TYPE_LONG:
    length= 4;
    break;
#ifdef HAVE_LONG_LONG
  case MYSQL_TYPE_LONGLONG:
    length= 8;
    break;
#endif
  case MYSQL_TYPE_FLOAT:
    length= sizeof(float);
    break;
  case MYSQL_TYPE_DOUBLE:
    length= sizeof(double);
    break;
  case MYSQL_TYPE_NULL:
    length= 0;
    break;
  case MYSQL_TYPE_NEWDATE:
    length= 3;
    break;
  case MYSQL_TYPE_DATE:
    length= 4;
    break;
  case MYSQL_TYPE_TIME:
    length= 3;
    break;
  case MYSQL_TYPE_TIMESTAMP:
    length= 4;
    break;
  case MYSQL_TYPE_DATETIME:
    length= 8;
    break;
    break;
  case MYSQL_TYPE_BIT:
    length= ~0UL;
    break;
  default:
    /* This case should never be chosen */
    DBUG_ASSERT(0);
    /* If something goes awfully wrong, it's better to get a string than die */
  case MYSQL_TYPE_STRING:
    length= uint2korr(data);
    break;

  case MYSQL_TYPE_ENUM:
  case MYSQL_TYPE_SET:
  case MYSQL_TYPE_VAR_STRING:
  case MYSQL_TYPE_VARCHAR:
    length= ~0UL;                               // NYI
    break;

  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
  case MYSQL_TYPE_GEOMETRY:
    length= ~0UL;                               // NYI
    break;
  }
}

/*********************************************************************
 *                   table_def member definitions                    *
 *********************************************************************/

/*
  Is the definition compatible with a table?

  Compare the definition with a table to see if it is compatible with
  it.  A table definition is compatible with a table if
  - the columns types of the table definition is a (not necessarily
    proper) prefix of the column type of the table, or
  - the other way around
*/
int
table_def::compatible_with(RELAY_LOG_INFO *rli, TABLE *table)
  const
{
  /*
    We only check the initial columns for the tables.
  */
  uint const cols_to_check= min(table->s->fields, size());
  int error= 0;

  TABLE_SHARE const *const tsh= table->s;

  /*
    To get proper error reporting for all columns of the table, we
    both check the width and iterate over all columns.
  */
  if (tsh->fields < size())
  {
    DBUG_ASSERT(tsh->db && tsh->table_name);
    error= 1;
    slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
                      "Table width mismatch - "
                      "received %u columns, %s.%s has %u columns",
                      size(), tsh->db, tsh->table_name, tsh->fields);
  }

  for (uint col= 0 ; col < cols_to_check ; ++col)
  {
    if (table->field[col]->type() != type(col))
    {
      DBUG_ASSERT(col < size() && col < tsh->fields);
      DBUG_ASSERT(tsh->db && tsh->table_name);
      error= 1;
      slave_print_error(rli, ER_BINLOG_ROW_WRONG_TABLE_DEF,
                        "Column %d type mismatch - "
                        "received type %d, %s.%s has type %d",
                        col, type(col), tsh->db, tsh->table_name,
                        table->field[col]->type());
    }
  }

  return error;
}

--- New file ---
+++ sql/rpl_utility.h	06/05/08 13:44:30
/* Copyright 2006 MySQL AB. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef RPL_UTILITY_H
#define RPL_UTILITY_H

#ifndef __cplusplus
#error "Don't include this C++ header file from a non-C++ file!"
#endif

#include "mysql_priv.h"

uint32
field_length_from_packed(enum_field_types const field_type, 
                         byte const *const data);

/*
  A table definition from the master.

  RESPONSIBILITIES

  - Extract table definition data from the table map event
  - Check if table definition in table map is compatible with table
    definition on slave
 */

class table_def
{
public:
  typedef unsigned char field_type;

  table_def(field_type *t, my_size_t s)
    : m_type(t), m_size(s)
  {
  }

  my_size_t size() const { return m_size; }
  field_type type(my_ptrdiff_t i) const { return m_type[i]; }

  int compatible_with(struct st_relay_log_info *rli, TABLE *table) const;

private:
  my_size_t m_size;
  field_type *m_type;
};

#endif /* RPL_UTILITY_H */

Thread
bk commit into 5.1 tree (tomas:1.2018) BUG#19069tomas8 May