summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <mats@romeo.(none)>2006-09-13 19:25:12 +0200
committerunknown <mats@romeo.(none)>2006-09-13 19:25:12 +0200
commit3936ce19d20e085cb5317d2fc024ee6818e4bbf4 (patch)
treea66b9bd59add75c92a1ec41646e6f0d3aa5baeb1
parentd4d01d5906d09ac23ae6f46e71377ea2786505fc (diff)
downloadmariadb-git-3936ce19d20e085cb5317d2fc024ee6818e4bbf4.tar.gz
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: Adding tests that UPDATE and DELETE does not generate an error. mysql-test/r/rpl_row_tabledefs_2myisam.result: Result change. mysql-test/r/rpl_row_tabledefs_3innodb.result: Result change. mysql-test/t/disabled.def: Enabling rpl_sp_effects (even though it gives a result mismatch currently). sql/field.cc: Using constant to denote undefined last null byte. sql/field.h: Using constant to denote undefined last null byte. Adding documentation. sql/log_event.cc: Not generating error for non-NULL no-DEFAULT columns when updating or deleting row. Better documentation and comments. sql/rpl_utility.cc: Moving documentation to header file. sql/rpl_utility.h: Documenting class and members.
-rw-r--r--mysql-test/extra/rpl_tests/rpl_row_tabledefs.test68
-rw-r--r--mysql-test/r/rpl_row_tabledefs_2myisam.result105
-rw-r--r--mysql-test/r/rpl_row_tabledefs_3innodb.result97
-rw-r--r--mysql-test/t/disabled.def1
-rw-r--r--sql/field.cc10
-rw-r--r--sql/field.h31
-rw-r--r--sql/log_event.cc110
-rw-r--r--sql/rpl_utility.cc5
-rw-r--r--sql/rpl_utility.h76
9 files changed, 441 insertions, 62 deletions
diff --git a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
index b17103d8396..1d39a2c3efd 100644
--- a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
+++ b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
@@ -25,6 +25,8 @@ eval CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=$engine_type;
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 @@ ALTER TABLE t5 MODIFY b FLOAT;
# ... 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,x FROM t1_int;
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 @@ SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
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;
diff --git a/mysql-test/r/rpl_row_tabledefs_2myisam.result b/mysql-test/r/rpl_row_tabledefs_2myisam.result
index 37559b0412a..ae792a5dc2a 100644
--- a/mysql-test/r/rpl_row_tabledefs_2myisam.result
+++ b/mysql-test/r/rpl_row_tabledefs_2myisam.result
@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
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 t2 DROP b;
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_Do_Table
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_Do_Table
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_Do_Table
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_Do_Table
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 @@ Master_SSL_Key
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;
diff --git a/mysql-test/r/rpl_row_tabledefs_3innodb.result b/mysql-test/r/rpl_row_tabledefs_3innodb.result
index 0c1b25ec7fc..b7f0b7b15e2 100644
--- a/mysql-test/r/rpl_row_tabledefs_3innodb.result
+++ b/mysql-test/r/rpl_row_tabledefs_3innodb.result
@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='InnoDB';
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 t2 DROP b;
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 @@ Master_SSL_Key
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;
diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
index f1322fcb699..a3bedbaa22d 100644
--- a/mysql-test/t/disabled.def
+++ b/mysql-test/t/disabled.def
@@ -36,7 +36,6 @@ rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly
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
diff --git a/sql/field.cc b/sql/field.cc
index 340f33f1e01..479a562aaac 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1261,7 +1261,10 @@ my_size_t
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 @@ Field_bit::do_last_null_byte() const
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,
diff --git a/sql/field.h b/sql/field.h
index 51b624b799f..26a9cd0b465 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -208,10 +208,24 @@ public:
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 @@ public:
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;
};
diff --git a/sql/log_event.cc b/sql/log_event.cc
index ceacc1eade7..ebfaa4bfff3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -5304,7 +5304,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data,
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 @@ int Rows_log_event::do_add_row_data(byte *const row_data,
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 @@ unpack_row(RELAY_LOG_INFO *rli,
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 @@ unpack_row(RELAY_LOG_INFO *rli,
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 @@ unpack_row(RELAY_LOG_INFO *rli,
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 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
{
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 @@ int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
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 @@ copy_extra_record_fields(TABLE *table,
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 @@ int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
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 @@ int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
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
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 5405d022223..c80b6dc3f69 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -107,11 +107,6 @@ field_length_from_packed(enum_field_types const field_type,
/*
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)
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 0ac3c10eec6..df0b0cd2ee1 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -32,29 +32,95 @@ field_length_from_packed(enum_field_types const field_type,
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 */