Commit 3936ce19 authored by unknown's avatar unknown

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.
parent d4d01d59
...@@ -25,6 +25,8 @@ eval CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=$engine_type; ...@@ -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 t4 (a INT) ENGINE=$engine_type;
eval CREATE TABLE t5 (a INT, b INT, c 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 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 # Table used to detect that slave is running
eval CREATE TABLE t9 (a INT) ENGINE=$engine_type; eval CREATE TABLE t9 (a INT) ENGINE=$engine_type;
...@@ -53,6 +55,17 @@ ALTER TABLE t5 MODIFY b FLOAT; ...@@ -53,6 +55,17 @@ ALTER TABLE t5 MODIFY b FLOAT;
# ... change the type of the last column of table 't6' # ... change the type of the last column of table 't6'
ALTER TABLE t6 MODIFY c FLOAT; 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 # Insert some values for tables on slave side. These should not be
# modified when the row from the master is applied. # modified when the row from the master is applied.
INSERT INTO t1_int VALUES (2, 4, 4711); INSERT INTO t1_int VALUES (2, 4, 4711);
...@@ -90,7 +103,7 @@ SELECT a,b,x FROM t1_int; ...@@ -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,HEX(x),HEX(y),HEX(z) FROM t1_bit;
SELECT a,b,x FROM t1_char; 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; connection master;
INSERT INTO t9 VALUES (2); INSERT INTO t9 VALUES (2);
...@@ -162,9 +175,60 @@ wait_for_slave_to_stop; ...@@ -162,9 +175,60 @@ wait_for_slave_to_stop;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
START SLAVE; 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; connection master;
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t1_int,t1_bit,t1_char,t1_nodef; 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 --enable_warnings
sync_slave_with_master; sync_slave_with_master;
...@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='MyISAM'; ...@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='MyISAM';
CREATE TABLE t4 (a INT) ENGINE='MyISAM'; CREATE TABLE t4 (a INT) ENGINE='MyISAM';
CREATE TABLE t5 (a INT, b INT, c 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 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'; CREATE TABLE t9 (a INT) ENGINE='MyISAM';
ALTER TABLE t1_int ADD x INT DEFAULT 42; ALTER TABLE t1_int ADD x INT DEFAULT 42;
ALTER TABLE t1_bit ALTER TABLE t1_bit
...@@ -28,6 +30,12 @@ ALTER TABLE t2 DROP b; ...@@ -28,6 +30,12 @@ ALTER TABLE t2 DROP b;
ALTER TABLE t4 MODIFY a FLOAT; ALTER TABLE t4 MODIFY a FLOAT;
ALTER TABLE t5 MODIFY b FLOAT; ALTER TABLE t5 MODIFY b FLOAT;
ALTER TABLE t6 MODIFY c 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_int VALUES (2, 4, 4711);
INSERT INTO t1_char VALUES (2, 4, 'Foo is a bar'); INSERT INTO t1_char VALUES (2, 4, 'Foo is a bar');
INSERT INTO t1_bit VALUES (2, 4, b'101', b'11100', b'01'); INSERT INTO t1_bit VALUES (2, 4, b'101', b'11100', b'01');
...@@ -151,7 +159,7 @@ Replicate_Do_Table ...@@ -151,7 +159,7 @@ Replicate_Do_Table
Replicate_Ignore_Table Replicate_Ignore_Table
Replicate_Wild_Do_Table Replicate_Wild_Do_Table
Replicate_Wild_Ignore_Table Replicate_Wild_Ignore_Table
Last_Errno 1514 Last_Errno 1522
Last_Error Table width mismatch - received 2 columns, test.t2 has 1 columns Last_Error Table width mismatch - received 2 columns, test.t2 has 1 columns
Skip_Counter 0 Skip_Counter 0
Exec_Master_Log_Pos # Exec_Master_Log_Pos #
...@@ -189,7 +197,7 @@ Replicate_Do_Table ...@@ -189,7 +197,7 @@ Replicate_Do_Table
Replicate_Ignore_Table Replicate_Ignore_Table
Replicate_Wild_Do_Table Replicate_Wild_Do_Table
Replicate_Wild_Ignore_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 Last_Error Column 0 type mismatch - received type 3, test.t4 has type 4
Skip_Counter 0 Skip_Counter 0
Exec_Master_Log_Pos # Exec_Master_Log_Pos #
...@@ -227,7 +235,7 @@ Replicate_Do_Table ...@@ -227,7 +235,7 @@ Replicate_Do_Table
Replicate_Ignore_Table Replicate_Ignore_Table
Replicate_Wild_Do_Table Replicate_Wild_Do_Table
Replicate_Wild_Ignore_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 Last_Error Column 1 type mismatch - received type 3, test.t5 has type 4
Skip_Counter 0 Skip_Counter 0
Exec_Master_Log_Pos # Exec_Master_Log_Pos #
...@@ -265,7 +273,7 @@ Replicate_Do_Table ...@@ -265,7 +273,7 @@ Replicate_Do_Table
Replicate_Ignore_Table Replicate_Ignore_Table
Replicate_Wild_Do_Table Replicate_Wild_Do_Table
Replicate_Wild_Ignore_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 Last_Error Column 2 type mismatch - received type 3, test.t6 has type 4
Skip_Counter 0 Skip_Counter 0
Exec_Master_Log_Pos # Exec_Master_Log_Pos #
...@@ -282,5 +290,92 @@ Master_SSL_Key ...@@ -282,5 +290,92 @@ Master_SSL_Key
Seconds_Behind_Master # Seconds_Behind_Master #
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
START SLAVE; 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 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;
...@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='InnoDB'; ...@@ -16,6 +16,8 @@ CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE='InnoDB';
CREATE TABLE t4 (a INT) ENGINE='InnoDB'; CREATE TABLE t4 (a INT) ENGINE='InnoDB';
CREATE TABLE t5 (a INT, b INT, c 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 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'; CREATE TABLE t9 (a INT) ENGINE='InnoDB';
ALTER TABLE t1_int ADD x INT DEFAULT 42; ALTER TABLE t1_int ADD x INT DEFAULT 42;
ALTER TABLE t1_bit ALTER TABLE t1_bit
...@@ -28,6 +30,12 @@ ALTER TABLE t2 DROP b; ...@@ -28,6 +30,12 @@ ALTER TABLE t2 DROP b;
ALTER TABLE t4 MODIFY a FLOAT; ALTER TABLE t4 MODIFY a FLOAT;
ALTER TABLE t5 MODIFY b FLOAT; ALTER TABLE t5 MODIFY b FLOAT;
ALTER TABLE t6 MODIFY c 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_int VALUES (2, 4, 4711);
INSERT INTO t1_char VALUES (2, 4, 'Foo is a bar'); INSERT INTO t1_char VALUES (2, 4, 'Foo is a bar');
INSERT INTO t1_bit VALUES (2, 4, b'101', b'11100', b'01'); INSERT INTO t1_bit VALUES (2, 4, b'101', b'11100', b'01');
...@@ -282,5 +290,92 @@ Master_SSL_Key ...@@ -282,5 +290,92 @@ Master_SSL_Key
Seconds_Behind_Master # Seconds_Behind_Master #
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2;
START SLAVE; 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 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;
...@@ -36,7 +36,6 @@ rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly ...@@ -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_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_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 : 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 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 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 mysqldump : BUG#18078 2006-03-10 lars
......
...@@ -1261,7 +1261,10 @@ my_size_t ...@@ -1261,7 +1261,10 @@ my_size_t
Field::do_last_null_byte() const Field::do_last_null_byte() const
{ {
DBUG_ASSERT(null_ptr == NULL || (byte*) null_ptr >= table->record[0]); 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 ...@@ -8196,7 +8199,10 @@ Field_bit::do_last_null_byte() const
else else
result= bit_ptr; 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, Field *Field_bit::new_key_field(MEM_ROOT *root,
......
...@@ -208,10 +208,24 @@ class Field ...@@ -208,10 +208,24 @@ class Field
inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; } inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
inline bool real_maybe_null(void) { return null_ptr != 0; } 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 Find the position of the last null byte for the field.
field conceptually is placed. In the case that the field does not
use any bits of the null bytes, a null pointer is returned. 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 last_null_byte() const {
my_size_t bytes= do_last_null_byte(); my_size_t bytes= do_last_null_byte();
...@@ -384,6 +398,17 @@ class Field ...@@ -384,6 +398,17 @@ class Field
friend class Item_func_group_concat; friend class Item_func_group_concat;
private: 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; virtual my_size_t do_last_null_byte() const;
}; };
......
...@@ -5304,7 +5304,7 @@ int Rows_log_event::do_add_row_data(byte *const row_data, ...@@ -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 row_end Pointer to variable that will hold the value of the
one-after-end position for the row one-after-end position for the row
master_reclength 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 record on the master side
rw_set Pointer to bitmap that holds either the read_set or the rw_set Pointer to bitmap that holds either the read_set or the
write_set of the table write_set of the table
...@@ -5317,13 +5317,22 @@ int Rows_log_event::do_add_row_data(byte *const row_data, ...@@ -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 At most 'colcnt' columns are read: if the table is larger than
that, the remaining fields are not filled in. 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 static int
unpack_row(RELAY_LOG_INFO *rli, unpack_row(RELAY_LOG_INFO *rli,
TABLE *table, uint const colcnt, byte *record, TABLE *table, uint const colcnt, byte *record,
char const *row, MY_BITMAP const *cols, char const *row, MY_BITMAP const *cols,
char const **row_end, ulong *master_reclength, 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); DBUG_ASSERT(record && row);
my_ptrdiff_t const offset= record - (byte*) table->record[0]; my_ptrdiff_t const offset= record - (byte*) table->record[0];
...@@ -5334,10 +5343,20 @@ unpack_row(RELAY_LOG_INFO *rli, ...@@ -5334,10 +5343,20 @@ unpack_row(RELAY_LOG_INFO *rli,
Field **fptr= &table->field[colcnt-1]; Field **fptr= &table->field[colcnt-1];
do do
master_null_bytes= (*fptr)->last_null_byte(); 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) OBSERVE! There might still be nullable columns following the
master_null_bytes= table->s->null_bytes; 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); DBUG_ASSERT(master_null_bytes <= table->s->null_bytes);
...@@ -5348,31 +5367,29 @@ unpack_row(RELAY_LOG_INFO *rli, ...@@ -5348,31 +5367,29 @@ unpack_row(RELAY_LOG_INFO *rli,
Field **const begin_ptr = table->field; Field **const begin_ptr = table->field;
Field **field_ptr; 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 f= *field_ptr;
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);
}
*row_end = ptr; if (bitmap_is_set(cols, field_ptr - begin_ptr))
if (master_reclength)
{ {
if (*field_ptr) ptr= f->unpack(f->ptr + offset, ptr);
*master_reclength = (*field_ptr)->ptr - (char*) table->record[0]; /* Field...::unpack() cannot return 0 */
else DBUG_ASSERT(ptr != NULL);
*master_reclength = table->s->reclength;
} }
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, ...@@ -5381,16 +5398,26 @@ unpack_row(RELAY_LOG_INFO *rli,
it was not there already. We iterate over all remaining columns, it was not there already. We iterate over all remaining columns,
even if there were an error, to get as many error messages as 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, 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) 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` " "Field `%s` of table `%s`.`%s` "
"has no default value and cannot be NULL", "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); table->s->table_name.str);
error = ER_NO_DEFAULT_FOR_FIELD; error = ER_NO_DEFAULT_FOR_FIELD;
} }
...@@ -5562,8 +5589,8 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) ...@@ -5562,8 +5589,8 @@ int Rows_log_event::exec_event(st_relay_log_info *rli)
{ {
char const *row_end= NULL; char const *row_end= NULL;
if ((error= do_prepare_row(thd, rli, table, row_start, &row_end))) if ((error= do_prepare_row(thd, rli, table, row_start, &row_end)))
break; // We should to the after-row operation even in the break; // We should perform the after-row operation even in
// case of error // the case of error
DBUG_ASSERT(row_end != NULL); // cannot happen DBUG_ASSERT(row_end != NULL); // cannot happen
DBUG_ASSERT(row_end <= (const char*)m_rows_end); 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, ...@@ -6224,7 +6251,7 @@ int Write_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
error= unpack_row(rli, error= unpack_row(rli,
table, m_width, table->record[0], table, m_width, table->record[0],
row_start, &m_cols, row_end, &m_master_reclength, 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); bitmap_copy(table->read_set, table->write_set);
return error; return error;
} }
...@@ -6285,10 +6312,17 @@ copy_extra_record_fields(TABLE *table, ...@@ -6285,10 +6312,17 @@ copy_extra_record_fields(TABLE *table,
my_size_t master_reclength, my_size_t master_reclength,
my_ptrdiff_t master_fields) my_ptrdiff_t master_fields)
{ {
DBUG_PRINT("info", ("Copying to %p from field %d at offset %u to field %d at offset %u", DBUG_PRINT("info", ("Copying to %p "
table->record[0], "from field %d at offset %u "
"to field %d at offset %u",
table->record[0],
master_fields, master_reclength, master_fields, master_reclength,
table->s->fields, table->s->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) if (master_reclength < table->s->reclength)
bmove_align(table->record[0] + master_reclength, bmove_align(table->record[0] + master_reclength,
table->record[1] + 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, ...@@ -6771,7 +6805,7 @@ int Delete_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
error= unpack_row(rli, error= unpack_row(rli,
table, m_width, table->record[0], table, m_width, table->record[0],
row_start, &m_cols, row_end, &m_master_reclength, 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 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. 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, ...@@ -6914,13 +6948,13 @@ int Update_rows_log_event::do_prepare_row(THD *thd, RELAY_LOG_INFO *rli,
error= unpack_row(rli, error= unpack_row(rli,
table, m_width, table->record[0], table, m_width, table->record[0],
row_start, &m_cols, row_end, &m_master_reclength, row_start, &m_cols, row_end, &m_master_reclength,
table->read_set); table->read_set, UPDATE_ROWS_EVENT);
row_start = *row_end; row_start = *row_end;
/* m_after_image is the after image for the update */ /* m_after_image is the after image for the update */
error= unpack_row(rli, error= unpack_row(rli,
table, m_width, m_after_image, table, m_width, m_after_image,
row_start, &m_cols, row_end, &m_master_reclength, 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 If we will access rows using the random access method, m_key will
......
...@@ -107,11 +107,6 @@ field_length_from_packed(enum_field_types const field_type, ...@@ -107,11 +107,6 @@ field_length_from_packed(enum_field_types const field_type,
/* /*
Is the definition compatible with a table? 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 int
table_def::compatible_with(RELAY_LOG_INFO *rli, TABLE *table) table_def::compatible_with(RELAY_LOG_INFO *rli, TABLE *table)
......
...@@ -32,29 +32,95 @@ field_length_from_packed(enum_field_types const field_type, ...@@ -32,29 +32,95 @@ field_length_from_packed(enum_field_types const field_type,
RESPONSIBILITIES 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 - Check if table definition in table map is compatible with table
definition on slave 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 class table_def
{ {
public: public:
/*
Convenience declaration of the type of the field type data in a
table map event.
*/
typedef unsigned char field_type; 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; } 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]; } 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; int compatible_with(RELAY_LOG_INFO *rli, TABLE *table) const;
private: private:
my_size_t m_size; my_size_t m_size; // Number of elements in the types array
field_type *m_type; field_type *m_type; // Array of type descriptors
}; };
#endif /* RPL_UTILITY_H */ #endif /* RPL_UTILITY_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment