Bug #10465 DECIMAL, crash on DELETE
Submitted: 9 May 2005 9:43 Modified: 24 May 2005 19:41
Reporter: Matthias Leich Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:5.0 OS:
Assigned to: Jani Tolonen CPU Architecture:Any

[9 May 2005 9:43] Matthias Leich
Description:
SET SESSION STORAGE_ENGINE=INNODB;
CREATE TABLE t1 (F1 DECIMAL(4) NOT NULL, UNIQUE (F1));
CREATE TABLE t2 (F1 DECIMAL(4), FOREIGN KEY (F1) REFERENCES t1(F1));

# Here comes the server crash
DELETE FROM t2 WHERE F1 = 5;

My environment:
   - Intel PC with Linux(SuSE 9.1)
   - MySQL compiled from source
        Version 5.0 ChangeSet@1.1934.2.2, 2005-05-07

Some observations:
- I am nearly sure that this bug was introduced by a push between
  the 29'th of April and the 7'th of May. 
- If I use the data type BIGINT instead of DECIMAL, the crash disappears.
- The master.err file shows that it is most probably an InnoDB problem.  

How to repeat:
Please use my attached test case 
nist_test.test , copy it to mysql-test/t

  touch r/nist_test.result     # Produce a dummy file with expected
                                      # results.
  ./mysql-test-run nist_test
[9 May 2005 9:45] Matthias Leich
test case

Attachment: nist_test.test (application/test, text), 3.14 KiB.

[9 May 2005 9:46] Matthias Leich
master.err

Attachment: master.err (application/octet-stream, text), 4.72 KiB.

[9 May 2005 9:53] Jan Lindström
Addditional information:

050509 11:56:53InnoDB: Assertion failure in thread 147466 in file row0sel.c line 2082
InnoDB: Failing assertion: dict_col_get_type(field->col)->mtype == dfield_get_type(dfield)->mtype
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/mysql/en/Forcing_recovery.html
InnoDB: about forcing recovery.

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 147466 (LWP 15439)]
0x0830ab2a in row_sel_convert_mysql_key_to_innobase (tuple=0x40ad8468,
    buf=0x8c7741a '¥' <repeats 62 times>, "h4z\025é\a", buf_len=57,
    index=0x40ae4468, key_ptr=0x65d0143b "¥p'a\bž Å\b\n", key_len=4,
    trx=0x40ad3468) at row0sel.c:2082
2082                         == dfield_get_type(dfield)->mtype);
Current language:  auto; currently c
(gdb) p dict_col_get_type(field->col)
$1 = (dtype_t *) 0x40ae30b8
(gdb) p *dict_col_get_type(field->col)
$2 = {mtype = 8, prtype = 256, len = 6, prec = 0, mbminlen = 0, mbmaxlen = 0}
(gdb) p *dfield_get_type(dfield)
$3 = {mtype = 0, prtype = 0, len = 0, prec = 0, mbminlen = 0, mbmaxlen = 0}
(gdb) where
#0  0x0830ab2a in row_sel_convert_mysql_key_to_innobase (tuple=0x40ad8468,
    buf=0x8c7741a '¥' <repeats 62 times>, "h4z\025é\a", buf_len=57,
    index=0x40ae4468, key_ptr=0x65d0143b "¥p'a\bž Å\b\n", key_len=4,
    trx=0x40ad3468) at row0sel.c:2082
#1  0x0826da23 in ha_innobase::records_in_range(unsigned, st_key_range*, st_key_range*) (this=0x8c76f70, keynr=0, min_key=0x65d00fec, max_key=0x65d00fdc)
    at ha_innodb.cc:5015
#2  0x0824b286 in check_quick_keys (param=0x65d011fc, idx=0,
    key_tree=0x8c775e0, min_key=0x65d01438 "", min_key_flag=0,
    max_key=0x65d01b36 "", max_key_flag=0) at opt_range.cc:5171
#3  0x0824abee in check_quick_select (param=0x65d011fc, idx=0, tree=0x8c775e0)
    at opt_range.cc:4987
#4  0x08246329 in get_key_scans_params (param=0x65d011fc, tree=0x8c774a8,
    index_read_must_be_used=false, read_time=3.2999999999999998)
    at opt_range.cc:3140
#5  0x082434c5 in SQL_SELECT::test_quick_select(THD*, Bitmap<64>, unsigned long long, unsigned long long, bool) (this=0x8c78cb0, thd=0x8c520b8, keys_to_use=
      {map = 1}, prev_tables=0, limit=18446744073709551615,
    force_quick_range=false) at opt_range.cc:1786
#6  0x08253946 in SQL_SELECT::check_quick(THD*, bool, unsigned long long) (
    this=0x8c78cb0, thd=0x8c520b8, force_quick_range=false,
    limit=18446744073709551615) at opt_range.h:694
#7  0x08213881 in mysql_delete(THD*, st_table_list*, Item*, st_sql_list*, unsig---Type <return> to continue, or q <return> to quit---
ned long long, unsigned long) (thd=0x8c520b8, table_list=0x8c789a0,
    conds=0x8c78bc8, order=0x8c523b8, limit=18446744073709551615, options=0)
    at sql_delete.cc:102
#8  0x081b38df in mysql_execute_command(THD*) (thd=0x8c520b8)
    at sql_parse.cc:3225
#9  0x081b8d02 in mysql_parse(THD*, char*, unsigned) (thd=0x8c520b8,
    inBuf=0x8c78930 "DELETE FROM t2 WHERE F1 = 5", length=27)
    at sql_parse.cc:5243
#10 0x081afbfc in dispatch_command(enum_server_command, THD*, char*, unsigned)
    (command=COM_QUERY, thd=0x8c520b8,
    packet=0x8c708d1 "DELETE FROM t2 WHERE F1 = 5", packet_length=28)
    at sql_parse.cc:1651
#11 0x081af51a in do_command(THD*) (thd=0x8c520b8) at sql_parse.cc:1457
#12 0x081ae781 in handle_one_connection (arg=0x8c520b8) at sql_parse.cc:1114
#13 0x40062f60 in pthread_start_thread () from /lib/i686/libpthread.so.0
#14 0x400630fe in pthread_start_thread_event () from /lib/i686/libpthread.so.0
#15 0x401f5327 in clone () from /lib/i686/libc.so.6
(gdb)
[9 May 2005 10:22] Jan Lindström
Based on below gdb output, why key_len = 4 when field length is 2 ? Because of this
row_sel_convert_mysql_key_to_innobase tryes to convert more than one key
to InnoDB.

Breakpoint 1, row_sel_convert_mysql_key_to_innobase (tuple=0x40ad9468,
    buf=0x8c49d80 '¥' <repeats 64 times>, "h4z\025\221", buf_len=57,
    index=0x40ad8468, key_ptr=0x637a7438 "", key_len=4, trx=0x40acbc68)
    at row0sel.c:2039
2039            byte*           original_buf    = buf;
Current language:  auto; currently c
(gdb) next
2040            byte*           original_key_ptr = key_ptr;
(gdb)
2048            ulint           n_fields = 0;
(gdb)
2054            key_end = key_ptr + key_len;
(gdb)
2058            dtuple_set_n_fields(tuple, ULINT_MAX);
(gdb)
2060            dfield = dtuple_get_nth_field(tuple, 0);
(gdb)
2061            field = dict_index_get_nth_field(index, 0);
(gdb)
2063            if (dfield_get_type(dfield)->mtype == DATA_SYS) {
(gdb) p *dfield
$1 = {data = 0x0, len = 0, type = {mtype = 4, prtype = 4130038, len = 2,
    prec = 0, mbminlen = 1, mbmaxlen = 1}}
(gdb) p *field
$2 = {col = 0x40ad7870, name = 0x40ad79a0 "F1", order = 0, prefix_len = 0,
  fixed_len = 0, fixed_offs = 0}
(gdb) p *(field->col)
$3 = {hash = 0x0, ind = 0, clust_pos = 3, ord_part = 1,
  name = 0x40ad79a0 "F1", type = {mtype = 4, prtype = 4130038, len = 2,
    prec = 0, mbminlen = 1, mbmaxlen = 1}, table = 0x40ad6e68, aux = 0}
(gdb) next
2079            while (key_ptr < key_end) {
(gdb) p key_len
$4 = 4
(gdb) p key_ptr
$5 = (unsigned char *) 0x637a7438 ""
(gdb) p key_end
$6 = (unsigned char *) 0x637a743c ""
(gdb) next
2082                         == dfield_get_type(dfield)->mtype);
(gdb)
2084                    data_offset = 0;
(gdb)
2085                    is_null = FALSE;
(gdb)
2087                    if (!(dfield_get_type(dfield)->prtype & DATA_NOT_NULL)) {
(gdb) p dfield_get_type(dfield)->prtype
$7 = 4130038
(gdb) next
2091                            data_offset = 1;
(gdb)
2093                            if (*key_ptr != 0) {
(gdb) p *key_ptr
$8 = 0 '\0'
(gdb) next
2100                    type = dfield_get_type(dfield)->mtype;
(gdb)
2104                    if (type == DATA_BLOB) {
(gdb) p *type
Cannot access memory at address 0x4
(gdb) next
2128                    } else if (field->prefix_len > 0) {
(gdb) p *dfield
$9 = {data = 0x0, len = 0, type = {mtype = 4, prtype = 4130038, len = 2,
    prec = 0, mbminlen = 1, mbmaxlen = 1}}
(gdb) next
2143                            data_len = dfield_get_type(dfield)->len;
(gdb)
2144                            data_field_len = data_offset + data_len;
(gdb) p data_len
$10 = 2
(gdb) next
2147                    if (dtype_get_mysql_type(dfield_get_type(dfield))
(gdb) p data_field_len
$11 = 3
(gdb) next
2165                    if (!is_null) {
(gdb)
2166                            row_mysql_store_col_in_innobase_format(
(gdb)
2173                            buf += data_len;
(gdb)
2176                    key_ptr += data_field_len;
(gdb)
2178                    if (key_ptr > key_end) {
(gdb)
2208                    n_fields++;
(gdb)
2209                    field++;
(gdb)
2210                    dfield++;
(gdb)
2079            while (key_ptr < key_end) {
(gdb) p key_ptr
$12 = (unsigned char *) 0x637a743b "¥"
(gdb) p key_end
$13 = (unsigned char *) 0x637a743c ""
(gdb) next
2082                         == dfield_get_type(dfield)->mtype);
(gdb) list 2081
2076                    return;
2077            }
2078
2079            while (key_ptr < key_end) {
2080
2081                    ut_a(dict_col_get_type(field->col)->mtype
2082                         == dfield_get_type(dfield)->mtype);
2083
2084                    data_offset = 0;
2085                    is_null = FALSE;
(gdb) p *field->col
$14 = {hash = 0x0, ind = 1, clust_pos = 0, ord_part = 2,
  name = 0x40ad79a8 "DB_ROW_ID", type = {mtype = 8, prtype = 256, len = 6,
    prec = 0, mbminlen = 0, mbmaxlen = 0}, table = 0x40ad6e68,
  aux = 4294967295}
(gdb) p dfield
$15 = (dfield_t *) 0x40ad94a4
(gdb) p *dfield
$16 = {data = 0x0, len = 0, type = {mtype = 0, prtype = 0, len = 0, prec = 0,
    mbminlen = 0, mbmaxlen = 0}}
[9 May 2005 12:26] Jan Lindström
Changing category to MySQL Server because this error is most probable in MYSQL code. DECIMAL(4) can be store to 2 bytes. But ha_innobase::records_in_range() gets as a parameter min_key and max_key length 4:

#1  0x0826da23 in ha_innobase::records_in_range(unsigned, st_key_range*, st_key_range*) (this=0x8c50080, keynr=0, min_key=0x637a6fec, max_key=0x637a6fdc)
    at ha_innodb.cc:5015
5015            row_sel_convert_mysql_key_to_innobase(
Current language:  auto; currently c++
(gdb) p *min_key
$1 = {key = 0x637a7438 "", length = 4, flag = HA_READ_KEY_EXACT}
(gdb) p *max_key
$2 = {key = 0x637a7b36 "", length = 4, flag = HA_READ_AFTER_KEY}
(gdb) p/x *max_key.key@4
$5 = {0x0, 0x80, 0x5, 0xa5}
(gdb) p/x *min_key.key@4
$6 = {0x0, 0x80, 0x5, 0xa5}

Regards,
    JanL
[9 May 2005 12:52] Heikki Tuuri
Changed the synopsis, since the FOREIGN KEY had nothing to do with this.
--Heikki
[9 May 2005 16:34] Matthias Leich
I guess I found an easier test case to replay the problem:
SET SESSION STORAGE_ENGINE=INNODB;
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (GRADE DECIMAL(4) NOT NULL,
PRIMARY KEY (GRADE));
INSERT INTO t1 (GRADE) VALUES (151);
SELECT GRADE  FROM t1;
GRADE
151
SELECT GRADE  FROM t1 WHERE GRADE= 151;
GRADE
         <---- Where is the record
The combination PRIMARY KEY/InnoDB/DECIMAL is important.
[12 May 2005 14:37] Jani Tolonen
Thank you for your bug report. This issue has been committed to our
source repository of that product and will be incorporated into the
next release.

If necessary, you can access the source repository and build the latest
available version, including the bugfix, yourself. More information 
about accessing the source trees is available at
    http://www.mysql.com/doc/en/Installing_source_tree.html
[12 May 2005 14:38] Jani Tolonen
Thank you for your bug report. This issue has been committed to our
source repository of that product and will be incorporated into the
next release.

If necessary, you can access the source repository and build the latest
available version, including the bugfix, yourself. More information 
about accessing the source trees is available at
    http://www.mysql.com/doc/en/Installing_source_tree.html
[21 May 2005 15:35] Heikki Tuuri
Jani,

if I understand correctly this is an incompatible change, and must be documented prominently.

Regards,

Heikki
[24 May 2005 19:41] Paul Dubois
Noted in 5.0 upgrading instructions and
5.0.6 changelog.