Bug #34300 Tinyblob & tinytext fields currupted after export/import and alter in 5.1
Submitted: 4 Feb 2008 23:24 Modified: 19 Jun 2010 18:05
Reporter: Omer Barnir (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:5.1.23 OS:Any
Assigned to: Vasil Dimov CPU Architecture:Any

[4 Feb 2008 23:24] Omer Barnir
Description:
A table including various data types, including tiny text and tiny blobs.
The table is exported and imported this results in the tiny blob fields being corrupled. If at this point the table is altered (and a column is added to it), the tiny text field gets corrupted as well.

In both cases the corruption results in the fieelds being filled with hex(0)

The bug is not observed (for the case of the tiny text if the table is not exported/imported.

The bug is not observed if the fields in the row have 'small' values (i.e. short strings in all text/blob fields
 
The bug is not observed in 5.0

How to repeat:
1) Download the attached tar.gz file and extract it in the mysql-test-directory

2) Load the data, run:
   perl ./mysql-test-run.pl --do-test=test04

3) Export the data:
   a. restart the server:
      perl ./mysql-test-run.pl --start-dirty --mysqld=--max-allowed-packet=256M
   b. Dump the database:
      ../bin/mysqldump --port=9306 --protocol=tcp --user=root --add-drop-table
                       --flush-privileges --force --all-databases 
                       --default-character-set=latin1  
                       --max_allowed_packet=256M > ./alldb.dump

4) Restart with a fresh database:
   perl ./mysql-test-run.pl --start-and-exit --mysqld=--max-allowed-packet=256M

5) Import the data: 
   ../bin/mysql --port=9306 --protocol=tcp --user=root 
                --max_allowed_packet=256M < ./alldb.dump

6) Login with the client:
   ../bin/mysql --port=9306 --protocol=tcp --user=root 

7) Run the following SQL:
   - use typesdb;
   - select f1, f2, left(f4,10), right(f4,10), hex(left(f4,10)), 
     hex(right(f4,10)), left(f5,10), right(f5,10), hex(left(f5,10)), 
     hex(right(f5,10)), left(f8,10), right(f8,10), hex(left(f8,10)), 
     hex(right(f8,10)), left(f9,10), right(f9,10), hex(left(f9,10)), 
     hex(right(f9,10)) from t1\G
*************************** 1. row ***************************
               f1: A
               f2: B
      left(f4,10): cababababa
     right(f4,10): abababwxyz
 hex(left(f4,10)): 63616261626162616261
hex(right(f4,10)): 6162616261627778797A
      left(f5,10): We the Peo
     right(f5,10): hird MySQL
 hex(left(f5,10)): 5765207468652050656F
hex(right(f5,10)): 68697264204D7953514C
      left(f8,10):                      <-- should be "cababababa"
     right(f8,10):                      <-- should be "abababwxyz"
 hex(left(f8,10)): 00000000000000000000 <-- should be "63616261626162616261"
hex(right(f8,10)): 00000000000000000000 <-- should be "6162616261627778797A"
      left(f9,10): We the Peo
     right(f9,10): hird MySQL
 hex(left(f9,10)): 5765207468652050656F
hex(right(f9,10)): 68697264204D7953514C
1 row in set (0.00 sec)

  - run: ALTER TABLE t1 ADD COLUMN (f59 VARCHAR(10));
  - run: SELECT f1, f2, left(f4,10), right(f4,10), hex(left(f4,10)), 
     hex(right(f4,10)), left(f5,10), right(f5,10), hex(left(f5,10)), 
     hex(right(f5,10)), left(f8,10), right(f8,10), hex(left(f8,10)), 
     hex(right(f8,10)), left(f9,10), right(f9,10), hex(left(f9,10)), 
     hex(right(f9,10)) from t1\G
*************************** 1. row ***************************
               f1: A
               f2: B
      left(f4,10):                      <-- should be "cababababa"
     right(f4,10):                      <-- should be "abababwxyz"
 hex(left(f4,10)): 00000000000000000000 <-- should be "63616261626162616261"
hex(right(f4,10)): 00000000000000000000 <-- should be "6162616261627778797A"
      left(f5,10): We the Peo
     right(f5,10): hird MySQL
 hex(left(f5,10)): 5765207468652050656F
hex(right(f5,10)): 68697264204D7953514C
      left(f8,10):                      <-- should be "cababababa"
     right(f8,10):                      <-- should be "abababwxyz"
 hex(left(f8,10)): 00000000000000000000 <-- should be "63616261626162616261"
hex(right(f8,10)): 00000000000000000000 <-- should be "6162616261627778797A"
      left(f9,10): We the Peo
     right(f9,10): hird MySQL
 hex(left(f9,10)): 5765207468652050656F
hex(right(f9,10)): 68697264204D7953514C
1 row in set (0.00 sec)

Note f4 is tinytext and f8 is tinyblob

   

Suggested fix:
No corruption should happen
[4 Feb 2008 23:25] Omer Barnir
test/result files

Attachment: bug_34300.tar.gz (application/x-gunzip, text), 30.44 KiB.

[1 Mar 2008 4:46] Davi Arnaut
mysql> select version();
+---------------+
| version()     |
+---------------+
| 5.1.23-rc-log | 
+---------------+
1 row in set (0.00 sec)

mysql> select f1, f2, left(f4,10), right(f4,10), hex(left(f4,10)), hex(right(f4,10)), left(f5,10), right(f5,10), hex(left(f5,10)), hex(right(f5,10)), left(f8,10), right(f8,10), hex(left(f8,10)), hex(right(f8,10)), left(f9,10), right(f9,10), hex(left(f9,10)), hex(right(f9,10)) from t1\G
*************************** 1. row ***************************
               f1: A
               f2: B
      left(f4,10): cababababa
     right(f4,10): abababwxyz
 hex(left(f4,10)): 63616261626162616261
hex(right(f4,10)): 6162616261627778797A
      left(f5,10): We the Peo
     right(f5,10): hird MySQL
 hex(left(f5,10)): 5765207468652050656F
hex(right(f5,10)): 68697264204D7953514C
      left(f8,10): cababababa
     right(f8,10): abababwxyz
 hex(left(f8,10)): 63616261626162616261
hex(right(f8,10)): 6162616261627778797A
      left(f9,10): We the Peo
     right(f9,10): hird MySQL
 hex(left(f9,10)): 5765207468652050656F
hex(right(f9,10)): 68697264204D7953514C
1 row in set (0.00 sec)

mysql> SELECT f1, f2, left(f4,10), right(f4,10), hex(left(f4,10)), hex(right(f4,10)), left(f5,10), right(f5,10), hex(left(f5,10)), hex(right(f5,10)), left(f8,10), right(f8,10), hex(left(f8,10)), hex(right(f8,10)), left(f9,10), right(f9,10), hex(left(f9,10)), hex(right(f9,10)) from t1\G
*************************** 1. row ***************************
               f1: A
               f2: B
      left(f4,10): cababababa
     right(f4,10): abababwxyz
 hex(left(f4,10)): 63616261626162616261
hex(right(f4,10)): 6162616261627778797A
      left(f5,10): We the Peo
     right(f5,10): hird MySQL
 hex(left(f5,10)): 5765207468652050656F
hex(right(f5,10)): 68697264204D7953514C
      left(f8,10): cababababa
     right(f8,10): abababwxyz
 hex(left(f8,10)): 63616261626162616261
hex(right(f8,10)): 6162616261627778797A
      left(f9,10): We the Peo
     right(f9,10): hird MySQL
 hex(left(f9,10)): 5765207468652050656F
hex(right(f9,10)): 68697264204D7953514C
1 row in set (0.00 sec)
[1 Mar 2008 6:04] Davi Arnaut
I was able to reproduce the problem on a third machine, it happens consistently after the alter table. 

mysql> SELECT f1, f2, left(f4,10), right(f4,10), hex(left(f4,10)),      hex(right(f4,10)), left(f5,10), right(f5,10), hex(left(f5,10)),      hex(right(f5,10)), left(f8,10), right(f8,10), hex(left(f8,10)),      hex(right(f8,10)), left(f9,10), right(f9,10), hex(left(f9,10)),      hex(right(f9,10)) from t1\G
*************************** 1. row ***************************
               f1: A
               f2: B
      left(f4,10): 
     right(f4,10): 
 hex(left(f4,10)): 00000000000000000000
hex(right(f4,10)): 00000000000000000000
      left(f5,10): We the Peo
     right(f5,10): hird MySQL
 hex(left(f5,10)): 5765207468652050656F
hex(right(f5,10)): 68697264204D7953514C
      left(f8,10): 
     right(f8,10): 
 hex(left(f8,10)): 00000000000000000000
hex(right(f8,10)): 00000000000000000000
      left(f9,10): We the Peo
     right(f9,10): hird MySQL
 hex(left(f9,10)): 5765207468652050656F
hex(right(f9,10)): 68697264204D7953514C
1 row in set (0.04 sec)
[4 Mar 2008 16:59] Heikki Tuuri
This does not look like an InnoDB bug.
[11 Mar 2008 12:42] Davi Arnaut
Reduced test case

Attachment: bug_34300_reduced.tar.gz (application/x-gzip, text), 27.56 KiB.

[12 Mar 2008 14:04] Davi Arnaut
I've reproduced the reduced test case on Linux under valgrind and on a Windows 2003 (VS 2005).

Debugging as the fields values are inserted shows that the fields values are correct right before innodb request and reads a page to write the records (btr_cur_search_to_nth_level, bug_read_page, bug_read_page_low), which happens after the fields are converted to the innodb format. After the page is requested and returned, it reads the page (or block) contents into the buffer that has the value of the blob fields (where the field data points to), overwriting the previous contents. Down the road in btr_cur_optimistic_insert, the value for the mediumtext field is properly restored but no the values of the two adjacent tinyblob fields. Another interesting fact is that the corruption doesn't show up if the mediumtext field is referenced (e.g: select f4, f9, length(f6) from t1)
[12 Mar 2008 17:03] Heikki Tuuri
Hmm... a possible reason might be that MySQL is the ALTER table fails to tell InnoDB to fetch ALL fields in the table. 

This is build_template() in ha_innodb.cc in 5.1:

        /* Note that in InnoDB, i is the column number. MySQL calls columns
        'fields'. */
        for (i = 0; i < n_fields; i++) {
                templ = prebuilt->mysql_template + n_requested_fields;
                field = table->field[i];

                if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
                        /* Decide which columns we should fetch
                        and which we can skip. */
                        register const ibool    index_contains_field =
                                dict_index_contains_col_or_prefix(index, i);

                        if (!index_contains_field && prebuilt->read_just_key) {
                                /* If this is a 'key read', we do not need
                                columns that are not in the key */

                                goto skip_field;
                        }

                        if (index_contains_field && fetch_all_in_key) {
                                /* This field is needed in the query */

                                goto include_field;
                        }

                        if (bitmap_is_set(table->read_set, i) ||
                            bitmap_is_set(table->write_set, i)) {
                                /* This field is needed in the query */

                                goto include_field;
                        }

                        if (fetch_primary_key_cols
                                && dict_table_col_in_clustered_key(
                                        index->table, i)) {
                                /* This field is needed in the query */

                                goto include_field;
                        }

                        /* This field is not needed in the query, skip it */

                        goto skip_field;
                }

Does MySQL set bitmap_is_set(table->read_set, i) for all columns in the table? Can you see InnoDB deciding in the above function to fetch all fields?
[12 Mar 2008 20:18] Davi Arnaut
Both mysql_alter_table and copy_data_between_tables call table:: use_all_columns, which I think sets the bitmap for all columns in the from and to tables. Furthermore, you piece of code you pasted does not kick in during the alter table because templ_type is ROW_MYSQL_WHOLE_ROW (so all fields are fetched).

What I find interesting and that I would like to know is why during a insert the requested page is read and the contents of this page are stored in the buffer which has the to be written blob?
[14 Mar 2008 10:42] Konstantin Osipov
Lars, reassigning to Heikki since the problem since to be on InnoDB side after all.
[20 Mar 2008 16:11] Vasil Dimov
Hmm, using bug_34300_reduced.tar.gz:

--- cut ---
hag:/tmp/mysql-5.1/mysql-test$ sh run.sh 
Logging: ./mtr test04
MySQL Version 5.1.24
Using dynamic switching of binlog format
Skipping ndbcluster, mysqld not compiled with ndbcluster
Skipping SSL, mysqld not compiled with SSL
Using MTR_BUILD_THREAD      = 0
Using MASTER_MYPORT         = 9306
Using MASTER_MYPORT1        = 9307
Using SLAVE_MYPORT          = 9308
Using SLAVE_MYPORT1         = 9309
Using SLAVE_MYPORT2         = 9310
Using IM_PORT               = 9313
Using IM_MYSQLD1_PORT       = 9314
Using IM_MYSQLD2_PORT       = 9315
Killing Possible Leftover Processes
Removing Stale Files
Creating Directories
Installing Master Database
=======================================================

TEST                           RESULT         TIME (ms)
-------------------------------------------------------

main.test04                    [ pass ]            942
-------------------------------------------------------
Stopping All Servers
All 1 tests were successful.
The servers were restarted 1 times
Spent 0.942 of 6 seconds executing testcases

Logging: ./mtr --start-dirty --mysqld=--max-allowed-packet=256M
MySQL Version 5.1.24
Using dynamic switching of binlog format
Skipping ndbcluster, mysqld not compiled with ndbcluster
Skipping SSL, mysqld not compiled with SSL
Using MTR_BUILD_THREAD      = 0
Using MASTER_MYPORT         = 9306
Using MASTER_MYPORT1        = 9307
Using SLAVE_MYPORT          = 9308
Using SLAVE_MYPORT1         = 9309
Using SLAVE_MYPORT2         = 9310
Using IM_PORT               = 9313
Using IM_MYSQLD1_PORT       = 9314
Using IM_MYSQLD2_PORT       = 9315
 - adding combinations
Killing Possible Leftover Processes
=======================================================

TEST                           RESULT         TIME (ms)
-------------------------------------------------------

Servers started, exiting
mysqldump: Got error: 2003: Can't connect to MySQL server on 'localhost' (61) when trying to connect
Logging: ./mtr --start-and-exit --valgrind-mysqld --mysqld=--max-allowed-packet=256M
MySQL Version 5.1.24
Using dynamic switching of binlog format
Turning on valgrind for mysqld(s) only
Running valgrind with options " --show-reachable=yes "
Skipping ndbcluster, mysqld not compiled with ndbcluster
Skipping SSL, mysqld not compiled with SSL
Using "../libtool" when running valgrind or debugger
Using MTR_BUILD_THREAD      = 0
Using MASTER_MYPORT         = 9306
Using MASTER_MYPORT1        = 9307
Using SLAVE_MYPORT          = 9308
Using SLAVE_MYPORT1         = 9309
Using SLAVE_MYPORT2         = 9310
Using IM_PORT               = 9313
Using IM_MYSQLD1_PORT       = 9314
Using IM_MYSQLD2_PORT       = 9315
 - adding combinations
Killing Possible Leftover Processes
Removing Stale Files
Creating Directories
Installing Master Database
Installing Slave1 Database
=======================================================

TEST                           RESULT         TIME (ms)
-------------------------------------------------------

mysql-test-run: WARNING: Process 71534 died
main.1st                       [ fail ]

ERROR: Failed to start master mysqld 0

Aborting: main.1st failed in default mode. 
To continue, re-run with '--force'.
Stopping All Servers
mysql-test-run: WARNING: caught exit of unknown child -1
mysql-test-run: WARNING: Forcing kill of process 71534
ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (61)
ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (61)
ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (61)
hag:/tmp/mysql-5.1/mysql-test$ 
--- cut --

So something is wrong, I tried performing the commands manually:

(installed mysql in /tmp/mysql-5.1-install/ and started it with
./libexec/mysqld --max_allowed_packet=256M)

mysql> CREATE DATABASE typesdb;
Query OK, 1 row affected (0.00 sec)

mysql> use typesdb;
Database changed
mysql> 
mysql> CREATE TABLE t1 (f4 tinytext, f6 mediumtext, f8 tinyblob) engine = innodb;
Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> INSERT INTO t1 (f4) VALUES ("xxx");
Query OK, 1 row affected (0.00 sec)

mysql> source /tmp/mysql-5.1/mysql-test/t/test04_64K.inc
Query OK, 0 rows affected (0.00 sec)

mysql> SET @@max_allowed_packet=16777215;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE t1 SET f6= REPEAT(@sample_text,128);
Query OK, 1 row affected (0.77 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> 
mysql> UPDATE t1 SET f8="zzz";
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0

hag:/tmp/mysql-5.1-install# ./bin/mysqldump --port=3306 --protocol=tcp --user=root --add-drop-table --flush-privileges --force --databases typesdb --default-character-set=latin1   --max_allowed_packet=256M > ./typesdb.dump

hag:/tmp/mysql-5.1-install# ./bin/mysql --port=3306 --protocol=tcp --user=root --max_allowed_packet=256M < ./typesdb.dump

hag:/tmp/mysql-5.1-install# ./bin/mysql --port=3306 --protocol=tcp --user=root --max_allowed_packet=256M < /tmp/bug_34300_reduced/corruption.sql 
f4	f8
xxx	zzz
f4	f8
xxx	zzz
hag:/tmp/mysql-5.1-install#

Strange... should I get the above result? It looks like correct.

I will try the first test case (non reduced)...
[21 Mar 2008 1:22] Davi Arnaut
The run.sh script didn't work because you don't have MTR_BUILD_THREAD set to auto, which binds the server to a different port. You can simply change the port number in the run.sh script or use the aforementioned environment variable.

As for the reproducibility of the test case, please read carefully my previous comments and you will note that I mention under which environments/systems the problem is repeatable.
[25 Mar 2008 7:19] Vasil Dimov
Simpler way to reproduce this:

1. start mysqld:

  ./libexec/mysqld --max_allowed_packet=256M

2. load typesdb-corruption.sql:

  ./mysql --port=3306 --protocol=tcp --user=root --max-allowed-packet=256M < typesdb-corruption.sql

Notice that if the sleep in typesdb-corruption.sql is enabled, then the corruption does not occur.
[25 Mar 2008 7:22] Vasil Dimov
Commands to reproduce the bug

Attachment: typesdb-corruption.sql.bz2 (application/octet-stream, text), 305 bytes.

[25 Mar 2008 8:25] Vasil Dimov
More manageable typesdb-corruption.sql:

--- cut ---
CREATE DATABASE `typesdb`;

USE `typesdb`;

DROP TABLE IF EXISTS `t1`;
CREATE TABLE `t1` (
  `f4` tinytext,
  `f6` mediumtext,
  `f8` tinytext
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `t1` VALUES ('xxx',repeat('a', 8459264),'zzz');

-- select sleep(20);

select f4, f8 from t1;

alter table t1 add column (f10 int);

select f4, f8 from t1;
--- cut ---
[25 Mar 2008 16:32] Heikki Tuuri
Both Vasil and Heikki debugging now...
[25 Mar 2008 18:42] Heikki Tuuri
The bug found.

5.0 row0sel.c contains this:

                if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets,
                                                templ->rec_field_no))) {

                        /* Copy an externally stored field to the temporary
                        heap */

                        ut_a(!prebuilt->trx->has_search_latch);

                        extern_field_heap = mem_heap_create(UNIV_PAGE_SIZE);

                        /* NOTE: if we are retrieving a big BLOB, we may
                        already run out of memory in the next call, which
                        causes an assert */

                        data = btr_rec_copy_externally_stored_field(rec,
                                        offsets, templ->rec_field_no, &len,
                                        extern_field_heap);

                        ut_a(len != UNIV_SQL_NULL);
                }

                if (len != UNIV_SQL_NULL) {
                        if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {

                                ut_a(prebuilt->templ_contains_blob);

                                /* A heuristic test that we can allocate the
                                memory for a big BLOB. We have a safety margin
                                of 1000000 bytes. Since the test takes some
                                CPU time, we do not use it for small BLOBs. */

                                if (UNIV_UNLIKELY(len > 2000000)
                                    && UNIV_UNLIKELY(!ut_test_malloc(
                                                        len + 1000000))) {

                                        ut_print_timestamp(stderr);
                                        fprintf(stderr,
"  InnoDB: Warning: could not allocate %lu + 1000000 bytes to retrieve\n"
"InnoDB: a big column. Table name ", (ulong) len);
                                        ut_print_name(stderr,
                                                prebuilt->trx,
                                                prebuilt->table->name);
                                        putc('\n', stderr);

                                        if (extern_field_heap) {
                                                mem_heap_free(
                                                        extern_field_heap);
                                        }
                                        return(FALSE);
                                }

                                /* Copy the BLOB data to the BLOB heap of
                                prebuilt */

                                if (prebuilt->blob_heap == NULL) {
                                        prebuilt->blob_heap =
                                                mem_heap_create(len);
                                }

                                data = memcpy(mem_heap_alloc(
                                                prebuilt->blob_heap, len),
                                                data, len);
                        }

BUT SOMEONE HAS REMOVED THE LATTER CODE FROM 5.1:

                if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets,
                                                      templ->rec_field_no))) {

                        /* Copy an externally stored field to the temporary
                        heap */

                        ut_a(!prebuilt->trx->has_search_latch);

                        if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
                                if (prebuilt->blob_heap == NULL) {
                                        prebuilt->blob_heap = mem_heap_create(
                                                UNIV_PAGE_SIZE);
                                }

                                heap = prebuilt->blob_heap;
                        } else {
                                extern_field_heap
                                        = mem_heap_create(UNIV_PAGE_SIZE);

                                heap = extern_field_heap;
                        }

                        /* NOTE: if we are retrieving a big BLOB, we may
                        already run out of memory in the next call, which
                        causes an assert */

                        data = btr_rec_copy_externally_stored_field(
                                rec, offsets, templ->rec_field_no,
                                &len, heap);

                        ut_a(len != UNIV_SQL_NULL);
                } else {
                        /* Field is stored in the row. */

                        data = rec_get_nth_field(rec, offsets,
                                                 templ->rec_field_no, &len);
                }

                if (len != UNIV_SQL_NULL) {
                        row_sel_field_store_in_mysql_format(
                                mysql_rec + templ->mysql_col_offset,
                                templ, data, len);

                        /* Cleanup */
                        if (extern_field_heap) {
                                mem_heap_free(extern_field_heap);
                                extern_field_heap = NULL;
                        }

...

In 5.1, InnoDB does NOT copy short BLOBs to the BLOB heap of prebuilt, but passes a pointer to teh record on the data page itself. If the page is removed or reorganized in the buffer pool => corruption of the BLOB data will occur.

Vasil, please return the 5.0 code logic to 5.1. That should fix the bug.
[25 Mar 2008 19:28] Davi Arnaut
It seems the code was removed in following changeset:

http://mysql.bkbits.net:8080/mysql-5.1/?PAGE=cset&REV=1.2154.44.1

It mentions:

"Optimize BLOB selects by using prebuilt->blob_heap directly instead of first reading BLOB data to a temporary heap and then copying it to prebuilt->blob_heap."

Thanks tracking this down.
[26 Mar 2008 13:21] Heikki Tuuri
The bug was made by Osku:

-----------------------------------------------------------------------
r587 | osku | 2006-05-23 15:35:58 +0300 (Tue, 23 May 2006) | 3 lines

Optimize BLOB selects by using prebuilt->blob_heap directly instead of first
reading BLOB data to a temporary heap and then copying it to
prebuilt->blob_heap.
------------------------------------------------------------------------

and I approved the patch :(.

--Heikki
[26 Mar 2008 13:25] Heikki Tuuri
A suggested pseudocode patch:

...
} else if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
           /* It is a BLOB field locally stored in the InnoDB record: we MUST
           copy its contents to prebuilt->blob_heap here because latter code
           assumes all BLOB values have been copied to a safe place. */

           memcpy the locally stored BLOB data to the prebuilt->blob_heap      (create the blob_heap if needed)}

} else (...
[26 Mar 2008 13:53] Heikki Tuuri
Running a stress test with ibtest3 and two ibtest3a now...
[26 Mar 2008 15:56] Heikki Tuuri
This patch seems to fix the problem:

--- row/row0sel.c	(revision 2382)
+++ row/row0sel.c	(working copy)
@@ -2640,12 +2640,31 @@ row_sel_store_mysql_rec(
 			ut_a(len != UNIV_SQL_NULL);
 		} else {
 			/* Field is stored in the row. */
 
 			data = rec_get_nth_field(rec, offsets,
 						 templ->rec_field_no, &len);
+
+			if (UNIV_UNLIKELY(templ->type == DATA_BLOB)
+			    && len != UNIV_SQL_NULL) {
+
+				/* It is a BLOB field locally stored in the
+				InnoDB record: we MUST copy its contents to
+				prebuilt->blob_heap here because later code
+				assumes all BLOB values have been copied to a
+				safe place. */
+
+				if (prebuilt->blob_heap == NULL) {
+					prebuilt->blob_heap = mem_heap_create(
+						UNIV_PAGE_SIZE);
+				}
+
+				data = memcpy(mem_heap_alloc(
+						prebuilt->blob_heap, len),
+						data, len);
+			}
 		}
 
 		if (len != UNIV_SQL_NULL) {
 			row_sel_field_store_in_mysql_format(
 				mysql_rec + templ->mysql_col_offset,
 				templ, data, len);
[31 Mar 2008 14:53] Bugs System
Pushed into 5.1.24-rc
[31 Mar 2008 19:02] Paul DuBois
Noted in 5.1.24 changelog.

For InnoDB, exporting and importing a table could corrupt TINYBLOB
columns, and a subsequent ALTER TABLE could corrupt TINYTEXT columns
as well.

Resetting report to Patch queued waiting for push into 6.0.x.
[3 Apr 2008 13:02] Bugs System
Pushed into 6.0.5-alpha
[7 Apr 2008 17:25] Paul DuBois
Noted in 6.0.5 changelog.
[5 May 2010 15:03] Bugs System
Pushed into 5.1.47 (revid:joro@sun.com-20100505145753-ivlt4hclbrjy8eye) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[6 May 2010 14:07] Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug.
Re-closing.
[28 May 2010 5:59] Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100524190136-egaq7e8zgkwb9aqi) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (pib:16)
[28 May 2010 6:28] Bugs System
Pushed into 6.0.14-alpha (revid:alik@sun.com-20100524190941-nuudpx60if25wsvx) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[28 May 2010 6:56] Bugs System
Pushed into 5.5.5-m3 (revid:alik@sun.com-20100524185725-c8k5q7v60i5nix3t) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[29 May 2010 22:48] Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug.
Re-closing.
[17 Jun 2010 12:02] Bugs System
Pushed into 5.1.47-ndb-7.0.16 (revid:martin.skold@mysql.com-20100617114014-bva0dy24yyd67697) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[17 Jun 2010 12:44] Bugs System
Pushed into 5.1.47-ndb-6.2.19 (revid:martin.skold@mysql.com-20100617115448-idrbic6gbki37h1c) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)
[17 Jun 2010 13:29] Bugs System
Pushed into 5.1.47-ndb-6.3.35 (revid:martin.skold@mysql.com-20100617114611-61aqbb52j752y116) (version source revid:vasil.dimov@oracle.com-20100331130613-8ja7n0vh36a80457) (merge vers: 5.1.46) (pib:16)