diff --git a/mysql-test/suite/innodb/r/reverse_scan_miss_rec.result b/mysql-test/suite/innodb/r/reverse_scan_miss_rec.result new file mode 100644 index 00000000000..50f93892dbe --- /dev/null +++ b/mysql-test/suite/innodb/r/reverse_scan_miss_rec.result @@ -0,0 +1,42 @@ +create table c(id int primary key, val varchar(8000)); +insert into c values (1,repeat('a',3000)); +insert into c values (2,repeat('a',3000)); +insert into c values (3,repeat('a',3000)); +insert into c values (4,repeat('a',3000)); +insert into c values (5,repeat('a',3000)); +insert into c values (-1,repeat('a',2000)); +insert into c values (-2,repeat('a',2000)); +insert into c values (-3,repeat('a',2000)); +insert into c values (-4,repeat('a',2000)); +insert into c values (-5,repeat('a',2000)); +select id,length(val) from c order by id desc; +id length(val) +5 3000 +4 3000 +3 3000 +2 3000 +1 3000 +-1 2000 +-2 2000 +-3 2000 +-4 2000 +-5 2000 +set DEBUG="+d,desc_scan_debug"; +set DEBUG_SYNC="desc_scan_before_restore_position SIGNAL desc_scan_before_restore_position_done WAIT_FOR continue"; +select id,length(val) from c order by id desc; +set DEBUG_SYNC="now WAIT_FOR desc_scan_before_restore_position_done"; +update c set val=repeat('b',6000) where id=2; +set DEBUG_SYNC="now SIGNAL continue"; +id length(val) +5 3000 +4 3000 +3 3000 +2 3000 +1 3000 +-1 2000 +-2 2000 +-3 2000 +-4 2000 +-5 2000 +set DEBUG="-d,desc_scan_debug"; +drop table c; diff --git a/mysql-test/suite/innodb/t/reverse_scan_miss_rec.test b/mysql-test/suite/innodb/t/reverse_scan_miss_rec.test new file mode 100644 index 00000000000..1d16ca31c31 --- /dev/null +++ b/mysql-test/suite/innodb/t/reverse_scan_miss_rec.test @@ -0,0 +1,31 @@ +--source include/have_debug.inc + +connect (con1, localhost, root,,); +--connection default +create table c(id int primary key, val varchar(8000)); +insert into c values (1,repeat('a',3000)); +insert into c values (2,repeat('a',3000)); +insert into c values (3,repeat('a',3000)); +insert into c values (4,repeat('a',3000)); +insert into c values (5,repeat('a',3000)); + +insert into c values (-1,repeat('a',2000)); +insert into c values (-2,repeat('a',2000)); +insert into c values (-3,repeat('a',2000)); +insert into c values (-4,repeat('a',2000)); +insert into c values (-5,repeat('a',2000)); + +--connection con1 +select id,length(val) from c order by id desc; +set DEBUG="+d,desc_scan_debug"; +set DEBUG_SYNC="desc_scan_before_restore_position SIGNAL desc_scan_before_restore_position_done WAIT_FOR continue"; +send select id,length(val) from c order by id desc; +--connection default +set DEBUG_SYNC="now WAIT_FOR desc_scan_before_restore_position_done"; +update c set val=repeat('b',6000) where id=2; +set DEBUG_SYNC="now SIGNAL continue"; +--connection con1 +reap; +set DEBUG="-d,desc_scan_debug"; +--connection default +drop table c; \ No newline at end of file diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index e82d611c99d..b08a40aa840 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3786,6 +3786,7 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor, dberr_t optim_err; roll_ptr_t roll_ptr; bool was_first; + bool was_before_last = false; ulint n_reserved = 0; ulint max_ins_size = 0; trx_t *const trx = (thr == nullptr) ? nullptr : thr_get_trx(thr); @@ -4092,6 +4093,10 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor, record on its page? */ was_first = page_cur_is_before_first(page_cursor); + was_before_last = + !page_rec_is_supremum(btr_cur_get_rec(cursor)) && + page_rec_is_supremum(page_rec_get_next(btr_cur_get_rec(cursor))); + /* Lock checks and undo logging were already performed by btr_cur_upd_lock_and_undo(). We do not try btr_cur_optimistic_insert() because @@ -4106,6 +4111,15 @@ dberr_t btr_cur_pessimistic_update(ulint flags, btr_cur_t *cursor, ut_ad(rec_offs_validate(rec, cursor->index, *offsets)); page_cursor->rec = rec; + if (was_before_last) { + /** We may moved the record from the current page to the next page. + * Therefore, we need to disable optimistic access to the next page to + * ensure that the record will not be mistakenly skipped during reverse + * scanning on cursor. + */ + buf_block_modify_clock_inc(btr_cur_get_block(cursor)); + } + /* Multiple transactions cannot simultaneously operate on the same temp-table in parallel. max_trx_id is ignored for temp tables because it not required diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index 8745ead0124..7a733348001 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -375,6 +375,12 @@ void btr_pcur_t::move_backward_from_page(mtr_t *mtr) { mtr_commit(mtr); + DBUG_EXECUTE_IF("desc_scan_debug", { + if (strcmp(index()->table_name, "test/c") == 0) { + DEBUG_SYNC_C("desc_scan_before_restore_position"); + } + }); + mtr_start(mtr); restore_position(latch_mode2, mtr, UT_LOCATION_HERE);