Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-16065

Assertion `!cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, index)' failed in btr_pcur_restore_position_func on UPDATE

Details

    Description

      --source include/have_innodb.inc
       
      CREATE TABLE t1 (f1 INT, f2 INT, f3 INT, f4 VARCHAR(8)) ENGINE=InnoDB;
      INSERT INTO t1 VALUES (1,2,3,'foo');
      ALTER TABLE t1 ADD PRIMARY KEY (f2);
      ALTER TABLE t1 DROP PRIMARY KEY;
      ALTER TABLE t1 ADD PRIMARY KEY (f1);
      ALTER TABLE t1 DROP PRIMARY KEY;
      ALTER TABLE t1 ADD PRIMARY KEY (f4,f3);
      ALTER TABLE t1 ADD COLUMN f5 INT;
      UPDATE t1 SET f5 = f3;
      UPDATE t1 SET f5 = f1;
       
      # Cleanup
      DROP TABLE t1;
      

      10.3

      mysqld: /data/src/10.3/storage/innobase/btr/btr0pcur.cc:313: ulint btr_pcur_restore_position_func(ulint, btr_pcur_t*, const char*, unsigned int, mtr_t*): Assertion `!cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, index)' failed.
      180501  2:06:46 [ERROR] mysqld got signal 6 ;
       
      #7  0x00007f1ee626cee2 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6
      #8  0x000055a998b0c68b in btr_pcur_restore_position_func (latch_mode=2, cursor=0x7f1e8400dbc0, file=0x55a999069138 "/data/src/10.3/storage/innobase/row/row0upd.cc", line=3094, mtr=0x7f1edb59c790) at /data/src/10.3/storage/innobase/btr/btr0pcur.cc:311
      #9  0x000055a998a52ace in row_upd_clust_step (node=0x7f1e84048238, thr=0x7f1e840485d0) at /data/src/10.3/storage/innobase/row/row0upd.cc:3094
      #10 0x000055a998a53392 in row_upd (node=0x7f1e84048238, thr=0x7f1e840485d0) at /data/src/10.3/storage/innobase/row/row0upd.cc:3280
      #11 0x000055a998a53881 in row_upd_step (thr=0x7f1e840485d0) at /data/src/10.3/storage/innobase/row/row0upd.cc:3424
      #12 0x000055a9989f03b6 in row_update_for_mysql (prebuilt=0x7f1e840479d8) at /data/src/10.3/storage/innobase/row/row0mysql.cc:1882
      #13 0x000055a99889c53a in ha_innobase::update_row (this=0x7f1e84178728, old_row=0x7f1e84045188 "\376\001", new_row=0x7f1e84045168 "\376\001") at /data/src/10.3/storage/innobase/handler/ha_innodb.cc:8894
      #14 0x000055a998589936 in handler::ha_update_row (this=0x7f1e84178728, old_data=0x7f1e84045188 "\376\001", new_data=0x7f1e84045168 "\376\001") at /data/src/10.3/sql/handler.cc:6216
      #15 0x000055a998390907 in mysql_update (thd=0x7f1e84000b00, table_list=0x7f1e84014e40, fields=..., values=..., conds=0x0, order_num=0, order=0x0, limit=18446744073709551615, handle_duplicates=DUP_ERROR, ignore=false, found_return=0x7f1edb59d6d0, updated_return=0x7f1edb59d790) at /data/src/10.3/sql/sql_update.cc:943
      #16 0x000055a99829bca6 in mysql_execute_command (thd=0x7f1e84000b00) at /data/src/10.3/sql/sql_parse.cc:4578
      #17 0x000055a9982a6a46 in mysql_parse (thd=0x7f1e84000b00, rawbuf=0x7f1e84014d68 "UPDATE t1 SET f5 = f1", length=21, parser_state=0x7f1edb59e5d0, is_com_multi=false, is_next_command=false) at /data/src/10.3/sql/sql_parse.cc:8001
      #18 0x000055a998294229 in dispatch_command (command=COM_QUERY, thd=0x7f1e84000b00, packet=0x7f1e84125f41 "UPDATE t1 SET f5 = f1", packet_length=21, is_com_multi=false, is_next_command=false) at /data/src/10.3/sql/sql_parse.cc:1846
      #19 0x000055a998292c68 in do_command (thd=0x7f1e84000b00) at /data/src/10.3/sql/sql_parse.cc:1391
      #20 0x000055a9983f5aef in do_handle_one_connection (connect=0x55a99a9b8600) at /data/src/10.3/sql/sql_connect.cc:1402
      #21 0x000055a9983f587c in handle_one_connection (arg=0x55a99a9b8600) at /data/src/10.3/sql/sql_connect.cc:1308
      #22 0x000055a998879a8d in pfs_spawn_thread (arg=0x55a99aa598c0) at /data/src/10.3/storage/perfschema/pfs.cc:1862
      #23 0x00007f1ee7f43494 in start_thread (arg=0x7f1edb59f700) at pthread_create.c:333
      #24 0x00007f1ee632993f in clone () from /lib/x86_64-linux-gnu/libc.so.6
      

      It appears to be a regression from this commit

      commit cb16bc95ff9a7b4ce0835acf8fa6d6a06ad10c3c
      Author: Marko Mäkelä <marko.makela@mariadb.com>
      Date:   Mon Apr 30 11:25:36 2018 +0300
       
          MDEV-14906 Assertion index->is_instant() failed on DELETE
      

      Attachments

        Issue Links

          Activity

            I can repeat this. All the ALTER TABLE only serve to add NOT NULL constraints to the columns. Here is a simpler version of the test:

            --source include/have_innodb.inc
            CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, f3 INT, f4 VARCHAR(8), PRIMARY KEY(f4,f3)) ENGINE=InnoDB;
            INSERT INTO t1 VALUES (1,2,3,'foo');
            ALTER TABLE t1 ADD COLUMN f5 INT;
            UPDATE t1 SET f5 = 3;
            UPDATE t1 SET f5 = 1;
            DROP TABLE t1;
            

            The INSERT can be moved right before the ADD COLUMN, and the bug will still reproduce. The table contents is as follows:
            The PAGE_FREE list contains 1 record (PAGE_GARBAGE=34 bytes) at 0x7e: purged record (f4='foo',f3=3,DB_TRX_ID,DB_ROLL_PTR,f1=1,f2=2).

            The successor of the page infimum is a default row record starting at 0xa2, containing dummy values for the hard-coded columns: (f4='',f3=0,DB_TRX_ID,DB_ROLL_PTR,f1=0,f2=0). Its successor is the only user-visible record, whose successor is the page supremum.

            The user-visible record starts at 0xc3 and has the following contents:
            (f4='foo',f3=3,DB_TRX_ID,DB_ROLL_PTR,f1=1,f2=2,f3=3). It is the rec in the assertion failure:

            !cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, index)

            The offsets1, which is built for the copied prefix cursor->old_rec, shows the wrong field end offsets (0 and 4) instead of the correct ones (3 and 7), which are in offsets2. Both cursor->old_rec and rec contain the 7 bytes 'foo',0x80000003 corresponding to the key values ('foo',3).

            The offsets1 are obviously wrong. When computing the offsets, btr_pcur_restore_position() is looking for the length bytes at the wrong place:

            (gdb) p/x cursor->old_rec[-7]@7
            $10 = {0x3, 0x0, 0x0, 0x0, 0x20, 0xff, 0xad}
            (gdb) p/x rec[-8]@8
            $13 = {0x3, 0x0, 0x0, 0x0, 0x0, 0x24, 0xff, 0xad}
            

            The status bits at rec[-3] & 7 are as they should be for these records:
            REC_STATUS_ORDINARY = 0 for cursor->old_rec, indicating that there is no field to encode instantly_added_columns+1,
            REC_STATUS_COLUMNS_ADDED = 4 for rec, indicating the extra field for encoding instantly_added_columns=1.

            The bug is that in cursor->old_rec, the MDEV-14906 fix is incorrectly reserving one byte for the null flags, even though all columns were NOT NULL before the nullable column was instantly added. The record header length should be only 6 bytes, not 7 or 8 bytes. That is why the length of f4='foo' is being read as 0 instead of 3.

            marko Marko Mäkelä added a comment - I can repeat this. All the ALTER TABLE only serve to add NOT NULL constraints to the columns. Here is a simpler version of the test: --source include/have_innodb.inc CREATE TABLE t1 (f1 INT NOT NULL , f2 INT NOT NULL , f3 INT , f4 VARCHAR (8), PRIMARY KEY (f4,f3)) ENGINE=InnoDB; INSERT INTO t1 VALUES (1,2,3, 'foo' ); ALTER TABLE t1 ADD COLUMN f5 INT ; UPDATE t1 SET f5 = 3; UPDATE t1 SET f5 = 1; DROP TABLE t1; The INSERT can be moved right before the ADD COLUMN , and the bug will still reproduce. The table contents is as follows: The PAGE_FREE list contains 1 record (PAGE_GARBAGE=34 bytes) at 0x7e: purged record (f4='foo',f3=3,DB_TRX_ID,DB_ROLL_PTR,f1=1,f2=2). The successor of the page infimum is a default row record starting at 0xa2, containing dummy values for the hard-coded columns: (f4='',f3=0,DB_TRX_ID,DB_ROLL_PTR,f1=0,f2=0). Its successor is the only user-visible record, whose successor is the page supremum. The user-visible record starts at 0xc3 and has the following contents: (f4='foo',f3=3,DB_TRX_ID,DB_ROLL_PTR,f1=1,f2=2,f3=3). It is the rec in the assertion failure: !cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, index) The offsets1 , which is built for the copied prefix cursor->old_rec , shows the wrong field end offsets (0 and 4) instead of the correct ones (3 and 7), which are in offsets2 . Both cursor->old_rec and rec contain the 7 bytes 'foo',0x80000003 corresponding to the key values ('foo',3). The offsets1 are obviously wrong. When computing the offsets, btr_pcur_restore_position() is looking for the length bytes at the wrong place: (gdb) p/x cursor->old_rec[-7]@7 $10 = {0x3, 0x0, 0x0, 0x0, 0x20, 0xff, 0xad} (gdb) p/x rec[-8]@8 $13 = {0x3, 0x0, 0x0, 0x0, 0x0, 0x24, 0xff, 0xad} The status bits at rec[-3] & 7 are as they should be for these records: REC_STATUS_ORDINARY = 0 for cursor->old_rec , indicating that there is no field to encode instantly_added_columns+1, REC_STATUS_COLUMNS_ADDED = 4 for rec , indicating the extra field for encoding instantly_added_columns=1. The bug is that in cursor->old_rec , the MDEV-14906 fix is incorrectly reserving one byte for the null flags, even though all columns were NOT NULL before the nullable column was instantly added. The record header length should be only 6 bytes, not 7 or 8 bytes. That is why the length of f4='foo' is being read as 0 instead of 3.

            For the record: 1+1 columns should be enough to reproduce (I used 2+1 in the test):

            diff --git a/mysql-test/suite/innodb/t/instant_alter.test b/mysql-test/suite/inn
            odb/t/instant_alter.test
            index d95f412fb37..c414b92f713 100644
            --- a/mysql-test/suite/innodb/t/instant_alter.test
            +++ b/mysql-test/suite/innodb/t/instant_alter.test
            @@ -311,6 +311,14 @@ INSERT INTO t1 SET a='a';
             SELECT * FROM t1;
             DROP TABLE t1;
             
            +# MDEV-16065 Assertion failed in btr_pcur_restore_position_func on UPDATE
            +eval CREATE TABLE t1 (a INT, b VARCHAR(8), PRIMARY KEY(b,a)) $engine;
            +INSERT INTO t1 VALUES (1,'foo');
            +ALTER TABLE t1 ADD COLUMN c INT;
            +UPDATE t1 SET c = 1;
            +UPDATE t1 SET c = 2;
            +DROP TABLE t1;
            +
             dec $format;
             }
             disconnect analyze;
            

            marko Marko Mäkelä added a comment - For the record: 1+1 columns should be enough to reproduce (I used 2+1 in the test): diff --git a/mysql-test/suite/innodb/t/instant_alter.test b/mysql-test/suite/inn odb/t/instant_alter.test index d95f412fb37..c414b92f713 100644 --- a/mysql-test/suite/innodb/t/instant_alter.test +++ b/mysql-test/suite/innodb/t/instant_alter.test @@ -311,6 +311,14 @@ INSERT INTO t1 SET a= 'a' ; SELECT * FROM t1; DROP TABLE t1; +# MDEV-16065 Assertion failed in btr_pcur_restore_position_func on UPDATE +eval CREATE TABLE t1 (a INT , b VARCHAR (8), PRIMARY KEY (b,a)) $engine; + INSERT INTO t1 VALUES (1, 'foo' ); + ALTER TABLE t1 ADD COLUMN c INT ; + UPDATE t1 SET c = 1; + UPDATE t1 SET c = 2; + DROP TABLE t1; + dec $format; } disconnect analyze;

            People

              marko Marko Mäkelä
              elenst Elena Stepanova
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.