List:Commits« Previous MessageNext Message »
From:Mats Kindahl Date:September 13 2006 5:25pm
Subject:bk commit into 5.1 tree (mats:1.2258)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of mats. When mats 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, 2006-09-13 19:25:12+02:00, mats@romeo.(none) +9 -0
  WL#3259 (RBR with more columns on slave than master):
  Incorporating changes from review.
  Fixing one bug that surfaced.

  mysql-test/extra/rpl_tests/rpl_row_tabledefs.test@stripped, 2006-09-13 19:25:08+02:00, mats@romeo.(none) +66 -2
    Adding tests that UPDATE and DELETE does not generate an error.

  mysql-test/r/rpl_row_tabledefs_2myisam.result@stripped, 2006-09-13 19:25:08+02:00, mats@romeo.(none) +100 -5
    Result change.

  mysql-test/r/rpl_row_tabledefs_3innodb.result@stripped, 2006-09-13 19:25:08+02:00, mats@romeo.(none) +96 -1
    Result change.

  mysql-test/t/disabled.def@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +0 -1
    Enabling rpl_sp_effects (even though it gives a result mismatch currently).

  sql/field.cc@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +8 -2
    Using constant to denote undefined last null byte.

  sql/field.h@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +28 -3
    Using constant to denote undefined last null byte.
    Adding documentation.

  sql/log_event.cc@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +72 -38
    Not generating error for non-NULL no-DEFAULT columns when updating or deleting row.
    Better documentation and comments.

  sql/rpl_utility.cc@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +0 -5
    Moving documentation to header file.

  sql/rpl_utility.h@stripped, 2006-09-13 19:25:09+02:00, mats@romeo.(none) +71 -5
    Documenting class and members.

# 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:	mats
# Host:	romeo.(none)
# Root:	/home/bk/w3259-mysql-5.1-new-rpl

--- 1.329/sql/field.cc	2006-09-13 19:25:19 +02:00
+++ 1.330/sql/field.cc	2006-09-13 19:25:19 +02:00
@@ -1261,7 +1261,10 @@
 Field::do_last_null_byte() const
 {
   DBUG_ASSERT(null_ptr == NULL || (byte*) null_ptr >= table->record[0]);
-  return null_ptr ? (byte*) null_ptr - table->record[0] + 1 : 0;
+  if (null_ptr)
+    return (byte*) null_ptr - table->record[0] + 1;
+  else
+    return LAST_NULL_BYTE_UNDEF;
 }
 
 
@@ -8196,7 +8199,10 @@
   else
     result= bit_ptr;
 
-  return result ? (byte*) result - table->record[0] + 1 : 0;
+  if (result)
+    return (byte*) result - table->record[0] + 1;
+  else
+    return LAST_NULL_BYTE_UNDEF;
 }
 
 Field *Field_bit::new_key_field(MEM_ROOT *root,

--- 1.186/sql/field.h	2006-09-13 19:25:19 +02:00
+++ 1.187/sql/field.h	2006-09-13 19:25:19 +02:00
@@ -208,10 +208,24 @@
   inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
   inline bool real_maybe_null(void) { return null_ptr != 0; }
 
+  enum {
+    LAST_NULL_BYTE_UNDEF= 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.
+    Find the position of the last null byte for the field.
+
+    SYNOPSIS
+      last_null_byte()
+
+    DESCRIPTION
+      Return a pointer to the last byte of the null bytes where the
+      field conceptually is placed.
+
+    RETURN VALUE
+      The position of the last null byte relative to the beginning of
+      the record. If the field does not use any bits of the null
+      bytes, the value 0 (LAST_NULL_BYTE_UNDEF) is returned.
    */
   my_size_t last_null_byte() const {
     my_size_t bytes= do_last_null_byte();
@@ -384,6 +398,17 @@
   friend class Item_func_group_concat;
 
 private:
+  /*
+    Primitive for implementing last_null_byte().
+
+    SYNOPSIS
+      do_last_null_byte()
+
+    DESCRIPTION
+      Primitive for the implementation of the last_null_byte()
+      function. This represents the inheritance interface and can be
+      overridden by subclasses.
+   */
   virtual my_size_t do_last_null_byte() const;
 };
 

--- 1.241/sql/log_event.cc	2006-09-13 19:25:19 +02:00
+++ 1.242/sql/log_event.cc	2006-09-13 19:25:19 +02:00
@@ -5304,7 +5304,7 @@
     row_end Pointer to variable that will hold the value of the
             one-after-end position for the row
     master_reclength
-            Pointer to variable that will hold the length of the
+            Pointer to variable that will be set to the length of the
             record on the master side
     rw_set  Pointer to bitmap that holds either the read_set or the
             write_set of the table
@@ -5317,13 +5317,22 @@
 
       At most 'colcnt' columns are read: if the table is larger than
       that, the remaining fields are not filled in.
+
+  RETURN VALUE
+
+      Error code, or zero if no error. The following error codes can
+      be returned:
+
+      ER_NO_DEFAULT_FOR_FIELD
+        Returned if one of the fields existing on the slave but not on
+        the master does not have a default value (and isn't nullable)
  */
 static int
 unpack_row(RELAY_LOG_INFO *rli,
            TABLE *table, uint const colcnt, byte *record,
            char const *row, MY_BITMAP const *cols,
            char const **row_end, ulong *master_reclength,
-           MY_BITMAP* const rw_set)
+           MY_BITMAP* const rw_set, Log_event_type const event_type)
 {
   DBUG_ASSERT(record && row);
   my_ptrdiff_t const offset= record - (byte*) table->record[0];
@@ -5334,10 +5343,20 @@
     Field **fptr= &table->field[colcnt-1];
     do
       master_null_bytes= (*fptr)->last_null_byte();
-    while (master_null_bytes == 0 && fptr-- > table->field);
+    while (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF &&
+           fptr-- > table->field);
+
+    /*
+      If master_null_bytes is LAST_NULL_BYTE_UNDEF (0) at this time,
+      there were no nullable fields nor BIT fields at all in the
+      columns that are common to the master and the slave. In that
+      case, there is only one null byte holding the X bit.
 
-    if (master_null_bytes == 0)
-      master_null_bytes= table->s->null_bytes;
+      OBSERVE! There might still be nullable columns following the
+      common columns, so table->s->null_bytes might be greater than 1.
+     */
+    if (master_null_bytes == Field::LAST_NULL_BYTE_UNDEF)
+      master_null_bytes= 1;
   }
 
   DBUG_ASSERT(master_null_bytes <= table->s->null_bytes);
@@ -5348,31 +5367,29 @@
 
   Field **const begin_ptr = table->field;
   Field **field_ptr;
+  char const *ptr= row + master_null_bytes;
+  Field **const end_ptr= begin_ptr + colcnt;
+  for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr)
   {
-    char const *ptr= row + master_null_bytes;
-    Field **const end_ptr= begin_ptr + colcnt;
-    for (field_ptr= begin_ptr ; field_ptr < end_ptr ; ++field_ptr)
-    {
-      Field *const f= *field_ptr;
-
-      if (bitmap_is_set(cols, field_ptr -  begin_ptr))
-      {
-        ptr= f->unpack(f->ptr + offset, ptr);
-        /* Field...::unpack() cannot return 0 */
-        DBUG_ASSERT(ptr != NULL);
-      }
-      else
-        bitmap_clear_bit(rw_set, field_ptr - begin_ptr);
-    }
+    Field *const f= *field_ptr;
 
-    *row_end = ptr;
-    if (master_reclength)
+    if (bitmap_is_set(cols, field_ptr -  begin_ptr))
     {
-      if (*field_ptr)
-        *master_reclength = (*field_ptr)->ptr - (char*) table->record[0];
-      else
-        *master_reclength = table->s->reclength;
+      ptr= f->unpack(f->ptr + offset, ptr);
+      /* Field...::unpack() cannot return 0 */
+      DBUG_ASSERT(ptr != NULL);
     }
+    else
+      bitmap_clear_bit(rw_set, field_ptr - begin_ptr);
+  }
+
+  *row_end = ptr;
+  if (master_reclength)
+  {
+    if (*field_ptr)
+      *master_reclength = (*field_ptr)->ptr - (char*) table->record[0];
+    else
+      *master_reclength = table->s->reclength;
   }
 
   /*
@@ -5381,16 +5398,26 @@
     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.
+    so redo that.
+
+    This generation of error messages is only relevant when inserting
+    new rows.
    */
   for ( ; *field_ptr ; ++field_ptr)
   {
-    if ((*field_ptr)->flags & (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG))
+    uint32 const mask= NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG;
+
+    DBUG_PRINT("debug", ("flags = 0x%x, mask = 0x%x, flags & mask = 0x%x",
+                         (*field_ptr)->flags, mask,
+                         (*field_ptr)->flags & mask));
+
+    if (event_type == WRITE_ROWS_EVENT &&
+        ((*field_ptr)->flags & mask) == mask)
     {
-      slave_print_msg(ERROR_LEVEL, rli, ER_NO_DEFAULT_FOR_FIELD, 
+      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, 
+                      (*field_ptr)->field_name, table->s->db.str,
                       table->s->table_name.str);
       error = ER_NO_DEFAULT_FOR_FIELD;
     }
@@ -5562,8 +5589,8 @@
     {
       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
+        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);
@@ -6224,7 +6251,7 @@
   error= unpack_row(rli,
                     table, m_width, table->record[0],
                     row_start, &m_cols, row_end, &m_master_reclength,
-                    table->write_set);
+                    table->write_set, WRITE_ROWS_EVENT);
   bitmap_copy(table->read_set, table->write_set);
   return error;
 }
@@ -6285,10 +6312,17 @@
                          my_size_t master_reclength,
                          my_ptrdiff_t master_fields)
 {
-  DBUG_PRINT("info", ("Copying to %p from field %d at offset %u to field %d at offset %u", 
-                      table->record[0], 
+  DBUG_PRINT("info", ("Copying to %p "
+                      "from field %d at offset %u "
+                      "to field %d at offset %u",
+                      table->record[0],
                       master_fields, 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).
+  */
+  DBUG_ASSERT(master_reclength <= table->s->reclength);
   if (master_reclength < table->s->reclength)
     bmove_align(table->record[0] + master_reclength,
                 table->record[1] + master_reclength,
@@ -6771,7 +6805,7 @@
   error= unpack_row(rli,
                     table, m_width, table->record[0], 
                     row_start, &m_cols, row_end, &m_master_reclength,
-                    table->read_set);
+                    table->read_set, DELETE_ROWS_EVENT);
   /*
     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.
@@ -6914,13 +6948,13 @@
   error= unpack_row(rli,
                     table, m_width, table->record[0],
                     row_start, &m_cols, row_end, &m_master_reclength,
-                    table->read_set);
+                    table->read_set, UPDATE_ROWS_EVENT);
   row_start = *row_end;
   /* m_after_image is the after image for the update */
   error= unpack_row(rli,
                     table, m_width, m_after_image,
                     row_start, &m_cols, row_end, &m_master_reclength,
-                    table->write_set);
+                    table->write_set, UPDATE_ROWS_EVENT);
 
   /*
     If we will access rows using the random access method, m_key will

--- 1.184/mysql-test/t/disabled.def	2006-09-13 19:25:19 +02:00
+++ 1.185/mysql-test/t/disabled.def	2006-09-13 19:25:19 +02:00
@@ -36,7 +36,6 @@
 rpl_row_func003		 : BUG#19074 2006-13-04 andrei  test failed
 rpl_row_inexist_tbl      : BUG#18948 2006-03-09 mats    Disabled since patch makes this test wait forever
 rpl_sp                   : BUG#16456 2006-02-16 jmiller
-rpl_sp_effects           : BUG#19862 2006-08-22 mats    Bug appear to be fixed
 rpl_until                : BUG#15886 2006-02-16 jmiller Unstable test case
 sp-goto                  : BUG#18949 2006-02-16 jmiller GOTO is currently is disabled - will be fixed in the future
 mysqldump                : BUG#18078 2006-03-10 lars

--- 1.3/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test	2006-09-13 19:25:19 +02:00
+++ 1.4/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test	2006-09-13 19:25:19 +02:00
@@ -25,6 +25,8 @@
 eval CREATE TABLE t4 (a INT) ENGINE=$engine_type;
 eval CREATE TABLE t5 (a INT, b INT, c INT) ENGINE=$engine_type;
 eval CREATE TABLE t6 (a INT, b INT, c INT) ENGINE=$engine_type;
+eval CREATE TABLE t7 (a INT NOT NULL) ENGINE=$engine_type;
+eval CREATE TABLE t8 (a INT NOT NULL) ENGINE=$engine_type;
 
 # Table used to detect that slave is running
 eval CREATE TABLE t9 (a INT) ENGINE=$engine_type;
@@ -53,6 +55,17 @@
 # ... change the type of the last column of table 't6'
 ALTER TABLE t6 MODIFY c FLOAT;
 
+# ... add one byte worth of null bytes to the table on the slave
+ALTER TABLE t7 ADD e1 INT, ADD e2 INT, ADD e3 INT, ADD e4 INT,
+               ADD e5 INT, ADD e6 INT, ADD e7 INT, ADD e8 INT;
+
+# ... add 8 columns that are nullable: t8 will not be entirely
+#     nullable and have no null bits (just an X bit)
+ALTER TABLE t8 ADD e1 INT NOT NULL DEFAULT 0, ADD e2 INT NOT NULL DEFAULT 0,
+               ADD e3 INT NOT NULL DEFAULT 0, ADD e4 INT NOT NULL DEFAULT 0,
+               ADD e5 INT NOT NULL DEFAULT 0, ADD e6 INT NOT NULL DEFAULT 0,
+               ADD e7 INT NOT NULL DEFAULT 0, ADD e8 INT NOT NULL DEFAULT 0;
+        
 # 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);
@@ -90,7 +103,7 @@
 SELECT a,b,HEX(x),HEX(y),HEX(z) FROM t1_bit;
 SELECT a,b,x FROM t1_char;
 
-# Each of these should generate an error and stop the slave
+# Each of these inserts should generate an error and stop the slave
 
 connection master;
 INSERT INTO t9 VALUES (2);
@@ -163,8 +176,59 @@
 START SLAVE;
 
 connection master;
+INSERT INTO t9 VALUES (6);
+sync_slave_with_master;
+--replace_result $MASTER_MYPORT MASTER_PORT
+--replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 #
+--query_vertical SHOW SLAVE STATUS
+
+# Testing some tables extra field that can be null and cannot be null
+# (but have default values)
+
+connection master;
+INSERT INTO t7 VALUES (1),(2),(3);
+INSERT INTO t8 VALUES (1),(2),(3);
+SELECT * FROM t7;
+SELECT * FROM t8;
+sync_slave_with_master;
+SELECT * FROM t7;
+SELECT * FROM t8;
+
+# We will now try to update and then delete a row on the master where
+# the extra field on the slave does not have a default value. This
+# update should not generate an error even though there is no default
+# for the extra column. 
+
+--echo **** On Master ****
+connection master;
+TRUNCATE t1_nodef;
+SET SQL_LOG_BIN=0;
+INSERT INTO t1_nodef VALUES (1,2);
+INSERT INTO t1_nodef VALUES (2,4);
+SET SQL_LOG_BIN=1;
+--echo **** On Slave ****
+connection slave;
+INSERT INTO t1_nodef VALUES (1,2,3);
+INSERT INTO t1_nodef VALUES (2,4,6);
+--echo **** On Master ****
+connection master;
+UPDATE t1_nodef SET b=2*b WHERE a=1;
+SELECT * FROM t1_nodef;
+--echo **** On Slave ****
+sync_slave_with_master;
+SELECT * FROM t1_nodef;
+--echo **** On Master ****
+connection master;
+DELETE FROM t1_nodef WHERE a=2;
+SELECT * FROM t1_nodef;
+--echo **** On Slave ****
+sync_slave_with_master;
+SELECT * FROM t1_nodef;
+
+--echo **** Cleanup ****
+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;
+DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t7,t8,t9;
 --enable_warnings
 sync_slave_with_master;

--- 1.4/mysql-test/r/rpl_row_tabledefs_2myisam.result	2006-09-13 19:25:19 +02:00
+++ 1.5/mysql-test/r/rpl_row_tabledefs_2myisam.result	2006-09-13 19:25:19 +02:00
@@ -16,6 +16,8 @@
 CREATE TABLE t4 (a INT) ENGINE='MyISAM';
 CREATE TABLE t5 (a INT, b INT, c INT) ENGINE='MyISAM';
 CREATE TABLE t6 (a INT, b INT, c INT) ENGINE='MyISAM';
+CREATE TABLE t7 (a INT NOT NULL) ENGINE='MyISAM';
+CREATE TABLE t8 (a INT NOT NULL) ENGINE='MyISAM';
 CREATE TABLE t9 (a INT) ENGINE='MyISAM';
 ALTER TABLE t1_int ADD x INT DEFAULT 42;
 ALTER TABLE t1_bit
@@ -28,6 +30,12 @@
 ALTER TABLE t4 MODIFY a FLOAT;
 ALTER TABLE t5 MODIFY b FLOAT;
 ALTER TABLE t6 MODIFY c FLOAT;
+ALTER TABLE t7 ADD e1 INT, ADD e2 INT, ADD e3 INT, ADD e4 INT,
+ADD e5 INT, ADD e6 INT, ADD e7 INT, ADD e8 INT;
+ALTER TABLE t8 ADD e1 INT NOT NULL DEFAULT 0, ADD e2 INT NOT NULL DEFAULT 0,
+ADD e3 INT NOT NULL DEFAULT 0, ADD e4 INT NOT NULL DEFAULT 0,
+ADD e5 INT NOT NULL DEFAULT 0, ADD e6 INT NOT NULL DEFAULT 0,
+ADD e7 INT NOT NULL DEFAULT 0, ADD e8 INT NOT NULL DEFAULT 0;
 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', b'11100', b'01');
@@ -151,7 +159,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1514
+Last_Errno	1522
 Last_Error	Table width mismatch - received 2 columns, test.t2 has 1 columns
 Skip_Counter	0
 Exec_Master_Log_Pos	#
@@ -189,7 +197,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1514
+Last_Errno	1522
 Last_Error	Column 0 type mismatch - received type 3, test.t4 has type 4
 Skip_Counter	0
 Exec_Master_Log_Pos	#
@@ -227,7 +235,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1514
+Last_Errno	1522
 Last_Error	Column 1 type mismatch - received type 3, test.t5 has type 4
 Skip_Counter	0
 Exec_Master_Log_Pos	#
@@ -265,7 +273,7 @@
 Replicate_Ignore_Table	
 Replicate_Wild_Do_Table	
 Replicate_Wild_Ignore_Table	
-Last_Errno	1514
+Last_Errno	1522
 Last_Error	Column 2 type mismatch - received type 3, test.t6 has type 4
 Skip_Counter	0
 Exec_Master_Log_Pos	#
@@ -282,5 +290,92 @@
 Seconds_Behind_Master	#
 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
 START SLAVE;
+INSERT INTO t9 VALUES (6);
+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	Yes
+Replicate_Do_DB	
+Replicate_Ignore_DB	
+Replicate_Do_Table	
+Replicate_Ignore_Table	
+Replicate_Wild_Do_Table	
+Replicate_Wild_Ignore_Table	
+Last_Errno	0
+Last_Error	
+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	#
+INSERT INTO t7 VALUES (1),(2),(3);
+INSERT INTO t8 VALUES (1),(2),(3);
+SELECT * FROM t7;
+a
+1
+2
+3
+SELECT * FROM t8;
+a
+1
+2
+3
+SELECT * FROM t7;
+a	e1	e2	e3	e4	e5	e6	e7	e8
+1	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+2	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+3	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+SELECT * FROM t8;
+a	e1	e2	e3	e4	e5	e6	e7	e8
+1	0	0	0	0	0	0	0	0
+2	0	0	0	0	0	0	0	0
+3	0	0	0	0	0	0	0	0
+**** On Master ****
+TRUNCATE t1_nodef;
+SET SQL_LOG_BIN=0;
+INSERT INTO t1_nodef VALUES (1,2);
+INSERT INTO t1_nodef VALUES (2,4);
+SET SQL_LOG_BIN=1;
+**** On Slave ****
+INSERT INTO t1_nodef VALUES (1,2,3);
+INSERT INTO t1_nodef VALUES (2,4,6);
+**** On Master ****
+UPDATE t1_nodef SET b=2*b WHERE a=1;
+SELECT * FROM t1_nodef;
+a	b
+1	4
+2	4
+**** On Slave ****
+SELECT * FROM t1_nodef;
+a	b	x
+1	4	3
+2	4	6
+**** On Master ****
+DELETE FROM t1_nodef WHERE a=2;
+SELECT * FROM t1_nodef;
+a	b
+1	4
+**** On Slave ****
+SELECT * FROM t1_nodef;
+a	b	x
+1	4	3
+**** Cleanup ****
 DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
-DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
+DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t7,t8,t9;

--- 1.2/mysql-test/r/rpl_row_tabledefs_3innodb.result	2006-09-13 19:25:19 +02:00
+++ 1.3/mysql-test/r/rpl_row_tabledefs_3innodb.result	2006-09-13 19:25:19 +02:00
@@ -16,6 +16,8 @@
 CREATE TABLE t4 (a INT) ENGINE='InnoDB';
 CREATE TABLE t5 (a INT, b INT, c INT) ENGINE='InnoDB';
 CREATE TABLE t6 (a INT, b INT, c INT) ENGINE='InnoDB';
+CREATE TABLE t7 (a INT NOT NULL) ENGINE='InnoDB';
+CREATE TABLE t8 (a INT NOT NULL) ENGINE='InnoDB';
 CREATE TABLE t9 (a INT) ENGINE='InnoDB';
 ALTER TABLE t1_int ADD x INT DEFAULT 42;
 ALTER TABLE t1_bit
@@ -28,6 +30,12 @@
 ALTER TABLE t4 MODIFY a FLOAT;
 ALTER TABLE t5 MODIFY b FLOAT;
 ALTER TABLE t6 MODIFY c FLOAT;
+ALTER TABLE t7 ADD e1 INT, ADD e2 INT, ADD e3 INT, ADD e4 INT,
+ADD e5 INT, ADD e6 INT, ADD e7 INT, ADD e8 INT;
+ALTER TABLE t8 ADD e1 INT NOT NULL DEFAULT 0, ADD e2 INT NOT NULL DEFAULT 0,
+ADD e3 INT NOT NULL DEFAULT 0, ADD e4 INT NOT NULL DEFAULT 0,
+ADD e5 INT NOT NULL DEFAULT 0, ADD e6 INT NOT NULL DEFAULT 0,
+ADD e7 INT NOT NULL DEFAULT 0, ADD e8 INT NOT NULL DEFAULT 0;
 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', b'11100', b'01');
@@ -282,5 +290,92 @@
 Seconds_Behind_Master	#
 SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
 START SLAVE;
+INSERT INTO t9 VALUES (6);
+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	Yes
+Replicate_Do_DB	
+Replicate_Ignore_DB	
+Replicate_Do_Table	
+Replicate_Ignore_Table	
+Replicate_Wild_Do_Table	
+Replicate_Wild_Ignore_Table	
+Last_Errno	0
+Last_Error	
+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	#
+INSERT INTO t7 VALUES (1),(2),(3);
+INSERT INTO t8 VALUES (1),(2),(3);
+SELECT * FROM t7;
+a
+1
+2
+3
+SELECT * FROM t8;
+a
+1
+2
+3
+SELECT * FROM t7;
+a	e1	e2	e3	e4	e5	e6	e7	e8
+1	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+2	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+3	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL
+SELECT * FROM t8;
+a	e1	e2	e3	e4	e5	e6	e7	e8
+1	0	0	0	0	0	0	0	0
+2	0	0	0	0	0	0	0	0
+3	0	0	0	0	0	0	0	0
+**** On Master ****
+TRUNCATE t1_nodef;
+SET SQL_LOG_BIN=0;
+INSERT INTO t1_nodef VALUES (1,2);
+INSERT INTO t1_nodef VALUES (2,4);
+SET SQL_LOG_BIN=1;
+**** On Slave ****
+INSERT INTO t1_nodef VALUES (1,2,3);
+INSERT INTO t1_nodef VALUES (2,4,6);
+**** On Master ****
+UPDATE t1_nodef SET b=2*b WHERE a=1;
+SELECT * FROM t1_nodef;
+a	b
+1	4
+2	4
+**** On Slave ****
+SELECT * FROM t1_nodef;
+a	b	x
+1	4	3
+2	4	6
+**** On Master ****
+DELETE FROM t1_nodef WHERE a=2;
+SELECT * FROM t1_nodef;
+a	b
+1	4
+**** On Slave ****
+SELECT * FROM t1_nodef;
+a	b	x
+1	4	3
+**** Cleanup ****
 DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef;
-DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t9;
+DROP TABLE IF EXISTS t2,t3,t4,t5,t6,t7,t8,t9;

--- 1.2/sql/rpl_utility.cc	2006-09-13 19:25:19 +02:00
+++ 1.3/sql/rpl_utility.cc	2006-09-13 19:25:19 +02:00
@@ -107,11 +107,6 @@
 /*
   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)

--- 1.1/sql/rpl_utility.h	2006-09-13 19:25:19 +02:00
+++ 1.2/sql/rpl_utility.h	2006-09-13 19:25:19 +02:00
@@ -32,29 +32,95 @@
 
   RESPONSIBILITIES
 
-  - Extract table definition data from the table map event
+  - Extract and decode table definition data from the table map event
   - Check if table definition in table map is compatible with table
     definition on slave
+
+  DESCRIPTION
+
+    Currently, the only field type data available is an array of the
+    type operators that are present in the table map event.
+
+  TODO
+
+    Add type operands to this structure to allow detection of
+    difference between, e.g., BIT(5) and BIT(10).
  */
 
 class table_def
 {
 public:
+  /*
+    Convenience declaration of the type of the field type data in a
+    table map event.
+  */
   typedef unsigned char field_type;
 
-  table_def(field_type *t, my_size_t s)
-    : m_type(t), m_size(s)
+  /*
+    Constructor.
+
+    SYNOPSIS
+      table_def()
+      types Array of types
+      size  Number of elements in array 'types'
+   */
+  table_def(field_type *types, my_size_t size)
+    : m_type(types), m_size(size)
   {
   }
 
+  /*
+    Return the number of fields there is type data for.
+
+    SYNOPSIS
+      size()
+
+    RETURN VALUE
+      The number of fields that there is type data for.
+   */
   my_size_t size() const { return m_size; }
+
+  /*
+    Return a representation of the type data for one field.
+
+    SYNOPSIS
+      type()
+      i   Field index to return data for
+
+    RETURN VALUE
+
+      Will return a representation of the type data for field
+      'i'. Currently, only the type identifier is returned.
+   */
   field_type type(my_ptrdiff_t i) const { return m_type[i]; }
 
+  /*
+    Decide if the table definition is compatible with a table.
+
+    SYNOPSIS
+      compatible_with()
+      rli   Pointer to relay log info
+      table Pointer to table to compare with.
+
+    DESCRIPTION
+
+      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
+
+    RETURN VALUE
+      1  if the table definition is not compatible with 'table'
+      0  if the table definition is compatible with 'table'
+  */
   int compatible_with(RELAY_LOG_INFO *rli, TABLE *table) const;
 
 private:
-  my_size_t m_size;
-  field_type *m_type;
+  my_size_t m_size;           // Number of elements in the types array
+  field_type *m_type;                     // Array of type descriptors
 };
 
 #endif /* RPL_UTILITY_H */
Thread
bk commit into 5.1 tree (mats:1.2258)Mats Kindahl13 Sep