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

versioned DELETE via row_end index leaves a row undeleted

    XMLWordPrintable

Details

    • Can result in unexpected behaviour
    • Q4/2026 Server Maintenance

    Description

      Reproduce

      create table t (
        id int primary key,
        row_start timestamp(6) generated always as row start,
        row_end   timestamp(6) generated always as row end,
        period for system_time(row_start, row_end),
        key row_end_idx (row_end)
      ) engine=myisam with system versioning;
       
      insert into t (id) values (1), (2), (3);
      delete from t;
       
      # Expected: no current rows remain. Actual: one row survives.
      select id from t;
       
      drop table t;
      

      Result

      Last row is not deleted.

      select id from t;
      id
      3
      

      Cause

      commit eff16d7593c
      Author: Sergei Golubchik <serg@mariadb.org>
      Date:   Fri Feb 9 23:50:26 2024 +0100
       
          Revert "MDEV-15458 Segfault in heap_scan() upon UPDATE after ADD SYSTEM VERSIONING"
       
          This partially reverts 43623f04a98
       
          Engines have to set ::position() after ::write_row(), otherwise
          the server won't be able to refer to the row just inserted.
          This is important for high-level indexes.
       
          heap part isn't reverted, so heap doesn't support high-level indexes.
          to fix this, it'll need info->lastpos in addition to info->current_ptr
      

      Caused by reappearance of HA_EXTRA_REMEMBER_POS / HA_EXTRA_RESTORE_POS in TABLE::delete_row().

      Reason

      A system-versioned DELETE on a MyISAM table indexed by row_end could leave the last current row undeleted. The delete scans the current rows via an equality search on row_end = MAX, which MyISAM drives with mi_rnext_same. That function keeps the search's reference key in info->lastkey2 and uses the HA_STATE_RNEXT_SAME flag to remember it has already stored it.

      Deleting a versioned row is an in-place update of row_end, and mi_update reuses info->lastkey2 as scratch space for the changed key, so it clears HA_STATE_RNEXT_SAME to tell mi_rnext_same to re-store its reference on the next call. However, TABLE::delete_row wrapped the update in HA_EXTRA_REMEMBER_POS/HA_EXTRA_RESTORE_POS, and RESTORE_POS restores the whole saved info->update word — resurrecting the HA_STATE_RNEXT_SAME bit that mi_update had just cleared.

      As a result mi_rnext_same skipped rebuilding its reference key and compared subsequent keys against the now-overwritten lastkey2, hitting a spurious end-of-file and terminating the scan one row early. Fixed by removing the REMEMBER_POS/RESTORE_POS wrapper so the scan state maintained by mi_update is preserved.

      #0  0x0000555557450e68 in mi_extra (info=0x7fffd4033538, function=HA_EXTRA_RESTORE_POS, extra_arg=0x0) at ../src/storage/myisam/mi_extra.c:203
      #1  0x000055555742de19 in ha_myisam::extra (this=0x7fffd4030af8, operation=HA_EXTRA_RESTORE_POS) at ../src/storage/myisam/ha_myisam.cc:2166
      #2  0x0000555556a1a068 in TABLE::delete_row<false> (this=0x7fffd40251b8, treat_versioned=true) at ../src/sql/sql_delete.cc:368
      #3  0x0000555556a1a4fd in TABLE::delete_row (this=0x7fffd40251b8) at ../src/sql/table.h:2007
      #4  0x0000555556a15570 in Sql_cmd_delete::delete_from_single_table (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_delete.cc:919
      #5  0x0000555556a1938b in Sql_cmd_delete::execute_inner (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_delete.cc:1917
      #6  0x0000555556b419d3 in Sql_cmd_dml::execute (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_select.cc:34624
      #7  0x0000555556a8133e in mysql_execute_command (thd=0x7fffd4000d68, is_called_from_prepared_stmt=false) at ../src/sql/sql_parse.cc:4467
      #8  0x0000555556a79b2d in mysql_parse (thd=0x7fffd4000d68, rawbuf=0x7fffd4016910 "delete from t", length=13, parser_state=0x7ffff07a91e8) at ../src/sql/sql_parse.cc:7958
       
      203           info->update=     info->save_update | HA_STATE_WRITTEN;
      

      #0  mi_rnext_same (info=0x7fffd4033538, buf=0x7fffd4031300 "\377\002") at ../src/storage/myisam/mi_rnext_same.c:64
      #1  0x000055555742dbed in ha_myisam::index_next_same (this=0x7fffd4030af8, buf=0x7fffd4031300 "\377\002", key=0x7fffd4913488 "\377\377\377\377\017B?\245\001", length=7) at ../src/storage/myisam/ha_myisam.cc:2044
      #2  0x000055555662d057 in handler::ha_index_next_same (this=0x7fffd4030af8, buf=0x7fffd4031300 "\377\002", key=0x7fffd4913488 "\377\377\377\377\017B?\245\001", keylen=7) at ../src/sql/handler.cc:4086
      #3  0x0000555556636836 in handler::read_range_next (this=0x7fffd4030af8) at ../src/sql/handler.cc:7436
      #4  0x0000555556d00dd5 in handler::multi_range_read_next (this=0x7fffd4030af8, range_info=0x7ffff07a69b8) at ../src/sql/multi_range_read.cc:570
      #5  0x0000555556d01049 in Mrr_simple_index_reader::get_next (this=0x7fffd40311b8, range_info=0x7ffff07a69b8) at ../src/sql/multi_range_read.cc:635
      #6  0x0000555556d03d21 in DsMrr_impl::dsmrr_next (this=0x7fffd4031068, range_info=0x7ffff07a69b8) at ../src/sql/multi_range_read.cc:1741
      #7  0x000055555742f154 in ha_myisam::multi_range_read_next (this=0x7fffd4030af8, range_info=0x7ffff07a69b8) at ../src/storage/myisam/ha_myisam.cc:2644
      #8  0x0000555556857c0d in QUICK_RANGE_SELECT::get_next (this=0x7fffd4913040) at ../src/sql/opt_range.cc:13319
      #9  0x0000555556889c7d in rr_quick (info=0x7ffff07a6ed0) at ../src/sql/records.cc:398
      #10 0x0000555556869a66 in READ_RECORD::read_record (this=0x7ffff07a6ed0) at ../src/sql/records.h:77
      #11 0x0000555556a15327 in Sql_cmd_delete::delete_from_single_table (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_delete.cc:885
      #12 0x0000555556a1938b in Sql_cmd_delete::execute_inner (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_delete.cc:1917
      #13 0x0000555556b419d3 in Sql_cmd_dml::execute (this=0x7fffd4017848, thd=0x7fffd4000d68) at ../src/sql/sql_select.cc:34624
      #14 0x0000555556a8133e in mysql_execute_command (thd=0x7fffd4000d68, is_called_from_prepared_stmt=false) at ../src/sql/sql_parse.cc:4467
      #15 0x0000555556a79b2d in mysql_parse (thd=0x7fffd4000d68, rawbuf=0x7fffd4016910 "delete from t", length=13, parser_state=0x7ffff07a91e8) at ../src/sql/sql_parse.cc:7958
       
      59            if (!(info->update & HA_STATE_RNEXT_SAME))
      60            {
      61              /* First rnext_same; Store old key */
      62              memcpy(info->lastkey2,info->lastkey,info->last_rkey_length);
      63            }
      

      Attachments

        Activity

          People

            midenok Aleksey Midenkov
            midenok Aleksey Midenkov
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Time Tracking

                Estimated:
                Original Estimate - 1h 28m Original Estimate - 1h 28m
                1h 28m
                Remaining:
                Time Spent - 3.25d Remaining Estimate - 1d
                1d
                Logged:
                Time Spent - 3.25d Remaining Estimate - 1d
                3.25d

                Git Integration

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