#At file:///Users/cbell/source/bzr/mysql-6.0-review/ based on revid:charles.bell@stripped
3836 Chuck Bell 2010-03-01
BUG#50357 : Innodb blob+text columns: empty strings become NULL after restore
If an InnoDB table with zero length blob fields is backed up and
restored, the fields are set to NULL instead of the empty string.
This patch corrects the behavior by recording the NULL status
during backup and applying it on restore.
original changeset: 3132 (mysql-backup-backport)
@ mysql-test/suite/backup/r/backup_blob.result
Corrected result file.
@ mysql-test/suite/backup/t/backup_blob.test
Added test case for checking NULL status after restore for zero
length blob fields.
@ sql/backup/be_default.cc
Added a check for saving NULL status during backup.
Added a check to setting NULL status during restore.
@ sql/backup/be_default.h
Added status indicators.
modified:
mysql-test/suite/backup/r/backup_blob.result
mysql-test/suite/backup/t/backup_blob.test
sql/backup/be_default.cc
sql/backup/be_default.h
=== modified file 'mysql-test/suite/backup/r/backup_blob.result'
Binary files a/mysql-test/suite/backup/r/backup_blob.result 2010-02-24 22:16:05 +0000 and b/mysql-test/suite/backup/r/backup_blob.result 2010-03-01 21:31:43 +0000 differ
=== modified file 'mysql-test/suite/backup/t/backup_blob.test'
--- a/mysql-test/suite/backup/t/backup_blob.test 2010-02-24 22:16:05 +0000
+++ b/mysql-test/suite/backup/t/backup_blob.test 2010-03-01 21:31:43 +0000
@@ -92,19 +92,32 @@ CREATE TABLE mydb.mytbl2 (
f10 longtext
) ENGINE INNODB;
+CREATE TABLE mydb.mytbl3 (
+ f1 char(10),
+ f2 varchar(10),
+ f3 binary(10),
+ f4 varbinary(10),
+ f5 blob,
+ f6 text
+) ENGINE INNODB;
+
+
--echo #
--echo # Insert some data.
--echo #
INSERT INTO mydb.mytbl1
- values (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ VALUES (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO mydb.mytbl2
- values (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ VALUES (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO mydb.mytbl3
+ VALUES ('','','','','','');
--echo #
---echo # Show valus from the tables.
+--echo # Show values from the tables before restore.
--echo #
SELECT * FROM mydb.mytbl1;
SELECT * FROM mydb.mytbl2;
+SELECT * FROM mydb.mytbl3;
--replace_column 1 #
BACKUP DATABASE mydb TO 'mydb.bak';
@@ -113,10 +126,11 @@ BACKUP DATABASE mydb TO 'mydb.bak';
RESTORE FROM 'mydb.bak' OVERWRITE;
--echo #
---echo # Show valus from the tables.
+--echo # Show values from the tables after restore.
--echo #
SELECT * FROM mydb.mytbl1;
SELECT * FROM mydb.mytbl2;
+SELECT * FROM mydb.mytbl3;
--echo
--echo Cleanup
=== modified file 'sql/backup/be_default.cc'
--- a/sql/backup/be_default.cc 2010-02-24 22:16:05 +0000
+++ b/sql/backup/be_default.cc 2010-03-01 21:31:43 +0000
@@ -549,13 +549,23 @@ result_t Backup::get_data(Buffer &buf)
case READ_BLOB:
{
uint32 size= ((Field_blob*) cur_table->field[*cur_blob])->get_length();
+
+ /*
+ Check to see if blob field is null first. If null, set bit to tell
+ restore to set the field to null.
+ */
+ if (((Field_blob*) cur_table->field[*cur_blob])->is_null())
+ *buf.data= BLOB_NULL;
+ else
+ *buf.data= 0;
+
/*
Check size of buffer to ensure data fits in the buffer. If it does
not fit, create new blob_buffer object.
*/
if ((size + META_SIZE) <= buf.size)
{
- *buf.data= BLOB_ONCE;
+ *buf.data|= BLOB_ONCE;
((Field_blob*) cur_table->field[*cur_blob])->get_ptr((uchar **)&ptr);
memcpy((byte *)buf.data + META_SIZE, ptr, size);
buf.size = size + META_SIZE;
@@ -568,7 +578,7 @@ result_t Backup::get_data(Buffer &buf)
((Field_blob*) cur_table->field[*cur_blob])->get_ptr((uchar **)&ptr);
blob_buffer.initialize((byte *)ptr, size);
- *buf.data= BLOB_FIRST; // First block.
+ *buf.data|= BLOB_FIRST; // First block.
uint32 field_size=
((Field_blob*) cur_table->field[*cur_blob])->get_length();
int4store(buf.data + META_SIZE, field_size); // Save max size.
@@ -797,7 +807,7 @@ result_t Restore::send_data(Buffer &buf)
hdl->ha_reset_auto_increment(auto_incr);
break;
}
-
+
// Buffer iterator not needed, just write the data.
case RCD_ONCE:
{
@@ -910,7 +920,26 @@ write_skip:
case WRITE_BLOB:
{
uint32 size= buf.size - META_SIZE;
+ bool set_null= (*buf.data & BLOB_NULL);
+ // Mask the null status indicator.
+ *buf.data&= BLOB_CTRL;
+
+ /*
+ BUG#50357 : Need to check for null fields set at backup time.
+ In this case, we check for null status and set the field accordingly.
+ If we do not set the field to null, check to see if the length
+ is 0. If it is, then we have a non-null field with a zero length
+ string (e.g. '').
+ */
+ if (*buf.data & (BLOB_ONCE | BLOB_FIRST))
+ {
+ if (set_null)
+ cur_table->field[*cur_blob]->set_null();
+ else if (size == 0)
+ cur_table->field[*cur_blob]->set_notnull();
+ }
+
block_type= *buf.data;
switch (block_type) {
=== modified file 'sql/backup/be_default.h'
--- a/sql/backup/be_default.h 2010-02-24 22:16:05 +0000
+++ b/sql/backup/be_default.h 2010-03-01 21:31:43 +0000
@@ -33,7 +33,9 @@ const byte BLOB_ONCE= 3U; // Singl
const byte BLOB_FIRST= (3U<<1); // First data block in buffer for blob buffer.
const byte BLOB_DATA= (3U<<2); // Intermediate data block for blob buffer.
const byte BLOB_LAST= (3U<<3); // Last data block in buffer for blob buffer.
+const byte BLOB_NULL= (3U<<5); // Stores blob field null status.
const byte AUTO_INCR= 5U; // Auto_increment value.
+const byte BLOB_CTRL= (BLOB_ONCE | BLOB_FIRST | BLOB_DATA | BLOB_LAST);
/**
Attachment: [text/bzr-bundle] bzr/charles.bell@sun.com-20100301213143-sxjn187w18si2qrf.bundle
Thread |
---|
• bzr commit into mysql-6.0-codebase-bugfixing branch(charles.bell:3836) Bug#50357 | Chuck Bell | 1 Mar |