Bug #28138 | indexing column prefixes produces corruption in InnoDB | ||
---|---|---|---|
Submitted: | 27 Apr 2007 16:42 | Modified: | 18 Jun 2010 22:30 |
Reporter: | Matthew Lord | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S1 (Critical) |
Version: | 5.0.30, 5.0.38, 5.0.42BK, 5.1.18BK | OS: | Any |
Assigned to: | Marko Mäkelä | CPU Architecture: | Any |
Tags: | bfsm_2007_05_03, corruption, crashing, innodb, Prefix Indexing |
[27 Apr 2007 16:42]
Matthew Lord
[2 May 2007 10:08]
Mads Martin Joergensen
Heikki, you agree it's an innodb bug?
[3 May 2007 3:12]
MySQL Verification Team
slightly randomized testcase to cause corruptions. Run for ~1 minute and observe errors logs of server...
Attachment: bug28138.c (text/plain), 6.06 KiB.
[3 May 2007 3:14]
MySQL Verification Team
my complete error log from the above testcase.
Attachment: www.zip (application/zip, text), 209.68 KiB.
[3 May 2007 12:29]
James Day
Regression bug. Reproduced with 5.0.40 mysqld-nt which crashed but not with 5.0.27.
[3 May 2007 14:51]
James Day
5.0.27 and 5.0.28 work fine. 5.0.30, 5.0.36, 5.0.40 crash with corruption found by check table, all tests with mysqld-nt.
[4 May 2007 13:09]
Heikki Tuuri
Assign to Vasil.
[15 May 2007 19:55]
James Day
I've updated the synopsis from "indexing column prefixes reports as false corruption" to "indexing column prefixes produces corruption in InnoDB".
[22 May 2007 13:06]
Heikki Tuuri
Could this be the same bug: http://bugs.mysql.com/bug.php?id=28125
[23 May 2007 17:14]
Heikki Tuuri
The bug probably found by Vasil and me: rem0rec.c in 5.0: /************************************************************* Builds a new-style physical record out of a data tuple and stores it beginning from the start of the given buffer. */ static rec_t* rec_convert_dtuple_to_rec_new( /*==========================*/ /* out: pointer to the origin of physical record */ byte* buf, /* in: start address of the physical record */ dict_index_t* index, /* in: record descriptor */ dtuple_t* dtuple) /* in: data tuple */ { ... for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(dtuple, i); type = dfield_get_type(field); ... if (fixed_len) { ut_ad(len == fixed_len); } else { ut_ad(len <= dtype_get_len(type) || dtype_get_mtype(type) == DATA_BLOB); if (len < 128 || (dtype_get_len(type) < 256 && dtype_get_mtype(type) != DATA_BLOB)) { *lens-- = (byte) len; printf( "Storing len to 1 byte: %lu\n", (ulint) len); } else { /* the extern bits will be set later */ ut_ad(len < 16384); *lens-- = (byte) (len >> 8) | 0x80; *lens-- = (byte) len; printf( "Storing len to 2 bytes: %lu\n", (ulint) len); } } ############ ABOVE WE LOOK AT the type of the dtuple field ############ BUT BELOW: /********************************************************** The following function determines the offsets to each field in the record. The offsets are written to a previously allocated array of ulint, where rec_offs_n_fields(offsets) has been initialized to the number of fields in the record. The rest of the array will be initialized by this function. rec_offs_base(offsets)[0] will be set to the extra size (if REC_OFFS_COMPACT is set, the record is in the new format), and rec_offs_base(offsets)[1..n_fields] will be set to offsets past the end of fields 0..n_fields, or to the beginning of fields 1..n_fields+1. When the high-order bit of the offset at [i+1] is set (REC_OFFS_SQL_NULL), the field i is NULL. When the second high-order bit of the offset at [i+1] is set (REC_OFFS_EXTERNAL), the field i is being stored externally. */ static void rec_init_offsets( /*=============*/ /* out: the offsets */ rec_t* rec, /* in: physical record */ dict_index_t* index, /* in: record descriptor */ ulint* offsets)/* in/out: array of offsets; in: n=rec_offs_n_fields(offsets) */ { ... if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ dtype_t* type = dict_col_get_type( dict_field_get_col(field)); len = *lens--; if (UNIV_UNLIKELY(dtype_get_len(type) > 255) || UNIV_UNLIKELY(dtype_get_mtype(type) == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ len <<= 8; len |= *lens--; offs += len & 0x3fff; if (UNIV_UNLIKELY(len & 0x4000)) { len = offs | REC_OFFS_EXTERNAL; } else { len = offs; } goto resolved; } } len = offs += len; ... ########### WE LOOK AT THE TYPE OF THE COLUMN! The 'length' of the type for the dtuple field may be different from the 'length' of the column type for column prefix indexes. Are there other places in the code where we have the same bug?
[23 May 2007 17:32]
Heikki Tuuri
I cannot repeat the bug with 5.0: heikki@ws35:~/mysql-5.0/client$ ./mysql test Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.0.38-debug-log Source distribution Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> insert into pt values ("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV "> VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"); Query OK, 1 row affected (0.00 sec) mysql> check table pt; +---------+-------+----------+----------+ | Table | Op | Msg_type | Msg_text | +---------+-------+----------+----------+ | test.pt | check | status | OK | +---------+-------+----------+----------+ 1 row in set (0.00 sec) mysql> heikki@ws35:~/mysql-5.0/sql$ ./mysqld 070523 20:31:06 [Warning] Changed limits: max_open_files: 1024 max_connections: 886 table_cache: 64 070523 20:31:06 InnoDB: Started; log sequence number 0 48188 070523 20:31:06 [Note] /home/heikki/mysql-5.0/sql/.libs/lt-mysqld: ready for connections. Version: '5.0.38-debug-log' socket: '/home/heikki/bugsocket' port: 3307 Source distribution dfield type len 6 dfield type len 6 dfield type len 7 dfield type len 256 dfield type len 256 dfield type len 6 Storing len to 2 bytes: 129 Storing len to 2 bytes: 128
[23 May 2007 17:37]
Heikki Tuuri
Also: mysql> CREATE TABLE `prefix_test` ( `lt` varbinary(4000) NOT NULL, KEY `lt_index` -> (`lt`(255))) ENGINE=InnoDB; Query OK, 0 rows affected (0.01 sec) mysql> insert into prefix_test (lt) values -> ("alsdf;lkasdpfusssssssssssssssssffffffffffffffffffffffffffffffffkkkkkkkkkkkkkkkkkkkkkk2222222222222222222222222222225555555555555555555555555555555555999999999999999999999999999999999999999999999912123123123;1j23;lkj123;l1k2j31;l2kj31;2lk3j12;3lkj12;3lkj12;3lkj123;lkjapdoiufapsoiudfpaoisdufapsodiufaspdoifuaspdoifuaspdoifuasdpofiu098370987340198134123pi;klajdf;lakjad;fkasdf/mnasd/fnas.dmfnasdf/nasdf/masdf;lkajvzx;ckjvpiuyqweopuryqepoiruqwpeiruqwpeiruqwerha;dkfjasdfn/mzncv/mn/m,n/mn/:Cvjhoiuqepriuqpweiurqwepioruqwperiu07a;jasdkfasdnbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb3333333333333jjjjjjjjkkkkkkkkaaaaaaaa;kljad;fkljasd;fkjasd;lkfjasd;lkfjas;dlkfjas;dlkfjpiuqerpoiquwepriuqweriuqyweruytqweurytqwiuerygqwehrbqwlebrqclwebn kcvzxcmvz/xcmvzx/cvzx/cmvzxc kljasd;kfjpoiu'qkjq'wekrjq'kerqwe[prqweiuyaua9840173407861234a;kjad;fjasdfasdfasl;dkfjas dflkjasdf as;dkfj as;dfjasdtyfaisudyf asdfiy asdif yasidufy asdif "> yasduf asdf;asd fasdf adf a asdfalsdfj asdfasdkfjpqieuyrqpiuweyr'adfj;adsfasdklfj asdfkjasdfa asdfasdf asdfj asdfjasldkfjasdf qqerjqwker qwer jqwer;kqwjerpqweur03847501234786510345b asdf"); Query OK, 1 row affected (0.01 sec) mysql> check table prefix_test; +------------------+-------+----------+----------+ | Table | Op | Msg_type | Msg_text | +------------------+-------+----------+----------+ | test.prefix_test | check | status | OK | +------------------+-------+----------+----------+ 1 row in set (0.01 sec) mysql>
[23 May 2007 17:59]
Heikki Tuuri
This patch introduced the bug to 5.1: Author: marko Date: 2006-09-06 13:23:47 +0300 (Wed, 06 Sep 2006) New Revision: 799 Log: On data tuples being updated or inserted (but not searched for), set type->len to the prefix_len of the index column, if it is a prefix index. This should prevent bugs similar to Bug #21638 from occurring. dict_index_copy_types(): Set type->len to prefix_len if prefix_len != 0. row_build_index_entry(): Set type->len to prefix_len if prefix_len != 0, also when the column in the tuple is SQL NULL, because the type information may be used for interpreting other records during btr_page_reorganize(). ---------------------- Modified: trunk/dict/dict0dict.c trunk/row/row0row.c Modified: trunk/dict/dict0dict.c =================================================================== --- trunk/dict/dict0dict.c 2006-09-05 20:41:05 UTC (rev 798) +++ trunk/dict/dict0dict.c 2006-09-06 10:23:47 UTC (rev 799) @@ -1723,8 +1723,6 @@ dict_index_t* index, /* in: index */ ulint n_fields) /* in: number of field types to copy */ { - dtype_t* dfield_type; - dtype_t* type; ulint i; if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { @@ -1734,10 +1732,17 @@ } for (i = 0; i < n_fields; i++) { + dict_field_t* ifield; + dtype_t* dfield_type; + dtype_t* type; + + ifield = dict_index_get_nth_field(index, i); dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); - type = dict_col_get_type(dict_field_get_col - (dict_index_get_nth_field(index, i))); + type = dict_col_get_type(dict_field_get_col(ifield)); *dfield_type = *type; + if (UNIV_UNLIKELY(ifield->prefix_len)) { + dfield_type->len = ifield->prefix_len; + } } } Modified: trunk/row/row0row.c =================================================================== --- trunk/row/row0row.c 2006-09-05 20:41:05 UTC (rev 798) +++ trunk/row/row0row.c 2006-09-06 10:23:47 UTC (rev 799) @@ -143,17 +143,22 @@ dfield_copy(dfield, dfield2); /* If a column prefix index, take only the prefix */ - if (ind_field->prefix_len > 0 - && dfield_get_len(dfield2) != UNIV_SQL_NULL) { + if (ind_field->prefix_len) { + if (dfield_get_len(dfield2) != UNIV_SQL_NULL) { - cur_type = dict_col_get_type - (dict_field_get_col(ind_field)); + cur_type = dict_col_get_type + (dict_field_get_col(ind_field)); - storage_len = dtype_get_at_most_n_mbchars - (cur_type, ind_field->prefix_len, - dfield_get_len(dfield2), dfield2->data); + storage_len = dtype_get_at_most_n_mbchars + (cur_type, + ind_field->prefix_len, + dfield_get_len(dfield2), + dfield2->data); - dfield_set_len(dfield, storage_len); + dfield_set_len(dfield, storage_len); + } + + dfield_get_type(dfield)->len = ind_field->prefix_len; } }
[23 May 2007 18:03]
Heikki Tuuri
Assigning this to Marko. Are there also other places where we assume dfield type len == column type len? That is not true for column prefix indexes.
[24 May 2007 11:42]
Marko Mäkelä
Using the attached bug28138.c, I can repeat this on 5.1 even after reverting r799. So I believe that it can be repeated with this test program on 5.0 as well.
[24 May 2007 11:49]
MySQL Verification Team
Marko, yes 5.0 is affected too. I uploaded my 5.0.42 error logs in www.zip
[24 May 2007 12:42]
Marko Mäkelä
We could reproduce the crash with 5.0 too. It looks like this bug was caused by the fix of Bug #21638. When we revert r791,799 from our 5.1 tree or r792 from our 5.0 tree, the test case bug28138.c will not trigger corruption or crash.
[24 May 2007 12:59]
Marko Mäkelä
Revert Bug #21638 fix from 5.0
Attachment: 5.0-r792.patch (text/x-diff), 3.88 KiB.
[24 May 2007 13:02]
Marko Mäkelä
Revert Bug #21638 fix from 5.1
Attachment: 5.1-r791-799.patch (text/x-diff), 5.36 KiB.
[12 Jul 2007 17:28]
Timothy Smith
Queued to 5.0- and 5.1-maint team trees
[19 Jul 2007 15:47]
Bugs System
Pushed into 5.1.21-beta
[19 Jul 2007 15:49]
Bugs System
Pushed into 5.0.48
[22 Jul 2007 18:17]
Paul DuBois
Noted in 5.0.48, 5.1.21 changelogs. Indexing column prefixes in InnoDB tables could cause table corruption.
[12 Sep 2007 23:55]
James Day
A symptom of this bug can also be error log entries like this: InnoDB: error in sec index entry update in InnoDB: index `PrefixIndex` of table `dbname/Content` InnoDB: tuple DATA TUPLE: 3 fields; InnoDB crashing is also possible. Since this causes tablespace corruption you should remove the affected index or replace with a full length version as soon as possible, or upgrade to a version with the bug fix. If you do see corruption, drop the index.
[5 May 2010 15:19]
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 2:58]
Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug. Re-closing.
[28 May 2010 5:54]
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:23]
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:51]
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 2:51]
Paul DuBois
Push resulted from incorporation of InnoDB tree. No changes pertinent to this bug. Re-closing.
[17 Jun 2010 11:55]
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:33]
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:21]
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)