1use crate::alloc::{ConcurrentAllocator, TryReserveError, TursoAllocator};
2use crate::skiplist::{comparator::BasicComparator, map::Entry};
3use crate::turso_assert;
4
5use crate::mvcc::clock::LogicalClock;
6use crate::mvcc::database::{
7 create_seek_range, MVTableId, MvStore, Row, RowID, RowKey, RowVersions, SortableIndexKey,
8};
9#[cfg(any(clt_turso_tests, injected_yields))]
10use crate::mvcc::yield_hooks::{ProvidesYieldContext, YieldContext, YieldPointMarker};
11use crate::mvcc::yield_points::inject_io_yield;
12use crate::storage::btree::{BTreeCursor, BTreeKey, CursorTrait};
13use crate::sync::Arc;
14use crate::translate::plan::IterationDirection;
15use crate::types::{
16 compare_immutable, IOCompletions, IOResult, ImmutableRecord, IndexInfo, SeekKey, SeekOp,
17 SeekResult, Value,
18};
19use crate::vdbe::make_record;
20use crate::vdbe::Register;
21use crate::{return_if_io, Completion, Connection, LimboError, Pager, Result};
22use std::any::Any;
23use std::fmt::Debug;
24use std::ops::Bound;
25#[cfg(any(clt_turso_tests, injected_yields))]
26use strum::EnumCount;
27
28#[derive(Clone)]
29enum CursorPosition<A: ConcurrentAllocator = TursoAllocator> {
30 BeforeFirst,
32 Loaded {
34 row_id: RowID,
35 in_btree: bool,
37 versions: Option<RowVersions<A>>,
42 },
43 End,
45}
46
47impl<A: ConcurrentAllocator> Debug for CursorPosition<A> {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Self::BeforeFirst => f.write_str("BeforeFirst"),
51 Self::Loaded {
52 row_id, in_btree, ..
53 } => f
54 .debug_struct("Loaded")
55 .field("row_id", row_id)
56 .field("in_btree", in_btree)
57 .finish_non_exhaustive(),
58 Self::End => f.write_str("End"),
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy)]
64enum ExistsState {
65 ExistsBtree,
66}
67
68#[derive(Debug, Clone, Copy)]
69enum AdvanceBtreeState {
72 RewindCheckBtreeKey, NextBtree, NextCheckBtreeKey, }
76
77#[derive(Debug, Clone, Copy)]
78enum RewindState {
81 Advance,
82}
83
84#[derive(Debug, Clone, Copy)]
85enum NextState {
86 AdvanceUnitialized,
87 CheckNeedsAdvance,
88 Advance,
89}
90#[derive(Debug, Clone, Copy)]
91enum PrevState {
92 AdvanceUnitialized,
93 CheckNeedsAdvance,
94 Advance,
95}
96
97#[derive(Debug, Clone, Copy)]
98enum SeekBtreeState {
99 SeekBtree,
101 AdvanceBTree,
103 CheckRow,
105}
106
107#[derive(Debug, Clone, Copy)]
108enum SeekState {
109 SeekBtree(SeekBtreeState),
111 PickWinner,
113}
114
115#[derive(Debug, Clone, Copy)]
116enum CountState {
117 Rewind,
118 NextBtree { count: usize },
119 CheckBtreeKey { count: usize },
120}
121#[derive(Debug, Clone)]
122enum MvccLazyCursorState {
123 Next(NextState),
124 Prev(PrevState),
125 Rewind(RewindState),
126 Exists(ExistsState),
127 Seek(SeekState, IterationDirection),
128}
129
130#[cfg(any(clt_turso_tests, injected_yields))]
131#[derive(Debug, Clone, Copy, PartialEq, Eq, strum_macros::EnumCount)]
132#[repr(u8)]
133pub(crate) enum CursorYieldPoint {
134 NextStart,
135 NextBtreeAdvance,
136 PrevBtreeAdvance,
137 SeekStart,
138 SeekBtreeProgress,
139 ExistsBtreeFallback,
140 CountProgress,
141 AdvanceBtreeForwardProgress,
142 AdvanceBtreeBackwardProgress,
143}
144
145#[cfg(any(clt_turso_tests, injected_yields))]
146impl YieldPointMarker for CursorYieldPoint {
147 const POINT_COUNT: u8 = Self::COUNT as u8;
148
149 fn ordinal(self) -> u8 {
150 self as u8
151 }
152}
153
154#[cfg(any(clt_turso_tests, injected_yields))]
155impl<Clock: LogicalClock + 'static, A: ConcurrentAllocator> ProvidesYieldContext
156 for MvccLazyCursor<Clock, A>
157{
158 fn yield_context(&self) -> YieldContext {
159 YieldContext::new(
160 self.connection.yield_injector(),
161 self.connection.failure_injector(),
162 self.yield_instance_id,
163 cursor_yield_key(self.tx_id, self.table_id),
164 )
165 }
166}
167
168fn current_pos_matches_seek_key(
169 current_row_id: &RowKey,
170 seek_key: &SeekKey<'_>,
171 mv_cursor_type: &MvccCursorType,
172) -> Result<bool> {
173 Ok(match (current_row_id, seek_key) {
174 (RowKey::Int(current), SeekKey::TableRowId(target)) => *current == *target,
175 (RowKey::Record(current), SeekKey::IndexKey(target)) => {
176 let MvccCursorType::Index(index_info) = mv_cursor_type else {
177 return Ok(false);
178 };
179 let key_info: Vec<_> = index_info
180 .key_info
181 .iter()
182 .take(target.column_count())
183 .cloned()
184 .collect();
185 compare_immutable(target.get_values()?, current.key.get_values()?, &key_info).is_eq()
186 }
187 _ => false,
188 })
189}
190
191#[cfg(any(clt_turso_tests, injected_yields))]
192fn cursor_yield_key(tx_id: u64, table_id: MVTableId) -> u64 {
193 const CURSOR_SELECTION_TAG: u64 = 0x4355_5253_4F52_4352;
196 tx_id ^ (i64::from(table_id) as u64).rotate_left(17) ^ CURSOR_SELECTION_TAG
200}
201
202#[derive(Debug, Clone)]
208struct DualCursorPeek<A: ConcurrentAllocator = TursoAllocator> {
209 mvcc_peek: CursorPeek<A>,
211 btree_peek: CursorPeek<A>,
213}
214
215impl<A: ConcurrentAllocator> Default for DualCursorPeek<A> {
216 fn default() -> Self {
217 Self {
218 mvcc_peek: CursorPeek::default(),
219 btree_peek: CursorPeek::default(),
220 }
221 }
222}
223
224impl<A: ConcurrentAllocator> DualCursorPeek<A> {
225 fn get_next(&self, dir: IterationDirection) -> Option<(RowKey, bool, Option<RowVersions<A>>)> {
228 tracing::trace!(
229 "get_next: mvcc_key: {:?}, btree_key: {:?}",
230 self.mvcc_peek.get_row_key(),
231 self.btree_peek.get_row_key()
232 );
233 match (self.mvcc_peek.get_row_key(), self.btree_peek.get_row_key()) {
234 (Some(mvcc_key), Some(btree_key)) => {
235 if dir == IterationDirection::Forwards {
236 if mvcc_key <= btree_key {
238 Some((mvcc_key.clone(), false, self.mvcc_peek.get_versions()))
239 } else {
240 Some((btree_key.clone(), true, None))
241 }
242 } else if mvcc_key >= btree_key {
244 Some((mvcc_key.clone(), false, self.mvcc_peek.get_versions()))
245 } else {
246 Some((btree_key.clone(), true, None))
247 }
248 }
249 (Some(mvcc_key), None) => {
250 Some((mvcc_key.clone(), false, self.mvcc_peek.get_versions()))
251 }
252 (None, Some(btree_key)) => Some((btree_key.clone(), true, None)),
253 (None, None) => None,
254 }
255 }
256
257 pub fn cursor_position_from_next(
259 &self,
260 table_id: MVTableId,
261 dir: IterationDirection,
262 ) -> CursorPosition<A> {
263 match self.get_next(dir) {
264 Some((row_key, in_btree, versions)) => CursorPosition::Loaded {
265 row_id: RowID {
266 table_id,
267 row_id: row_key,
268 },
269 in_btree,
270 versions,
271 },
272 None => match dir {
273 IterationDirection::Forwards => CursorPosition::End,
274 IterationDirection::Backwards => CursorPosition::BeforeFirst,
275 },
276 }
277 }
278
279 pub fn both_uninitialized(&self) -> bool {
280 matches!(self.mvcc_peek, CursorPeek::Uninitialized)
281 && matches!(self.btree_peek, CursorPeek::Uninitialized)
282 }
283
284 pub fn btree_uninitialized(&self) -> bool {
285 matches!(self.btree_peek, CursorPeek::Uninitialized)
286 }
287
288 pub fn mvcc_exhausted(&self) -> bool {
289 matches!(self.mvcc_peek, CursorPeek::Exhausted)
290 }
291 pub fn btree_exhausted(&self) -> bool {
292 matches!(self.btree_peek, CursorPeek::Exhausted)
293 }
294}
295
296#[derive(Debug, Clone)]
297enum CursorPeek<A: ConcurrentAllocator = TursoAllocator> {
298 Uninitialized,
299 Row {
300 key: RowKey,
301 versions: Option<RowVersions<A>>,
304 },
305 Exhausted,
306}
307
308impl<A: ConcurrentAllocator> Default for CursorPeek<A> {
309 fn default() -> Self {
310 Self::Uninitialized
311 }
312}
313
314impl<A: ConcurrentAllocator> CursorPeek<A> {
315 pub fn get_row_key(&self) -> Option<&RowKey> {
316 match self {
317 CursorPeek::Row { key, .. } => Some(key),
318 _ => None,
319 }
320 }
321
322 pub fn get_versions(&self) -> Option<RowVersions<A>> {
323 match self {
324 CursorPeek::Row { versions, .. } => versions.clone(),
325 _ => None,
326 }
327 }
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
331pub enum MvccCursorType {
332 Table,
333 Index(Arc<IndexInfo>),
334}
335
336pub(crate) type MvccEntry<'l, T, A = TursoAllocator> =
337 Entry<'l, T, RowVersions<A>, BasicComparator, A>;
338
339pub(crate) type MvccIterator<'l, T, A = TursoAllocator> =
340 Box<dyn Iterator<Item = MvccEntry<'l, T, A>> + Send + Sync>;
341
342macro_rules! static_iterator_hack {
363 ($iter:expr, $key_type:ty) => {
364 static_iterator_hack!($iter, $key_type, crate::alloc::TursoAllocator)
365 };
366 ($iter:expr, $key_type:ty, $alloc:ty) => {
367 unsafe {
369 std::mem::transmute::<
370 Box<
371 dyn Iterator<Item = crate::mvcc::cursor::MvccEntry<'_, $key_type, $alloc>>
372 + Send
373 + Sync,
374 >,
375 Box<
376 dyn Iterator<Item = crate::mvcc::cursor::MvccEntry<'static, $key_type, $alloc>>
377 + Send
378 + Sync,
379 >,
380 >($iter)
381 }
382 };
383}
384
385pub(crate) use static_iterator_hack;
386
387#[derive(Default)]
393pub(crate) enum IndexShadowFinger<A: ConcurrentAllocator = TursoAllocator> {
394 #[default]
396 Uninitialized,
397 Peeked {
400 iter: MvccIterator<'static, Arc<SortableIndexKey>, A>,
401 key: Arc<SortableIndexKey>,
402 versions: RowVersions<A>,
403 },
404 Exhausted,
406}
407
408impl<A: ConcurrentAllocator> IndexShadowFinger<A> {
409 fn reset(&mut self) {
413 *self = Self::Uninitialized;
414 }
415
416 fn advance(mut iter: MvccIterator<'static, Arc<SortableIndexKey>, A>) -> Self {
420 match iter.next() {
421 Some(entry) => Self::Peeked {
422 key: entry.key().clone(),
423 versions: entry.value().clone(),
424 iter,
425 },
426 None => Self::Exhausted,
427 }
428 }
429
430 pub(crate) fn btree_row_is_valid<Clock: LogicalClock>(
434 &mut self,
435 db: &MvStore<Clock, A>,
436 table_id: MVTableId,
437 tx_id: u64,
438 key: &Arc<SortableIndexKey>,
439 ) -> bool {
440 if matches!(self, Self::Uninitialized) {
441 let iter = {
443 let index_rows = db.index_rows.get(&table_id);
445 let iter_box: Box<
449 dyn Iterator<Item = MvccEntry<'_, Arc<SortableIndexKey>, A>> + Send + Sync,
450 > = match index_rows {
451 Some(index_rows) => {
452 Box::new(index_rows.value().range::<SortableIndexKey, _>((
453 std::ops::Bound::Included(key.as_ref()),
454 std::ops::Bound::Unbounded,
455 )))
456 }
457 None => Box::new(std::iter::empty()),
458 };
459 static_iterator_hack!(iter_box, Arc<SortableIndexKey>, A)
460 };
461 *self = Self::advance(iter);
462 }
463 loop {
464 match self {
465 Self::Exhausted => return true,
467 Self::Uninitialized => unreachable!("created just above"),
468 Self::Peeked {
469 key: finger_key,
470 versions,
471 ..
472 } => match finger_key.as_ref().cmp(key.as_ref()) {
473 std::cmp::Ordering::Greater => return true,
475 std::cmp::Ordering::Equal => {
478 return !db.index_chain_invalidates_btree(versions, tx_id);
479 }
480 std::cmp::Ordering::Less => {}
482 },
483 }
484 let Self::Peeked { iter, .. } = std::mem::replace(self, Self::Uninitialized) else {
486 unreachable!("Less arm matched Peeked")
487 };
488 *self = Self::advance(iter);
489 }
490 }
491}
492
493pub struct MvccLazyCursor<Clock: LogicalClock + 'static, A: ConcurrentAllocator = TursoAllocator> {
494 pub db: Arc<MvStore<Clock, A>>,
495 #[cfg(any(clt_turso_tests, injected_yields))]
496 connection: Arc<Connection>,
497 #[cfg(any(clt_turso_tests, injected_yields))]
498 yield_instance_id: u64,
499 current_pos: CursorPosition<A>,
500 table_iterator: Option<MvccIterator<'static, RowID, A>>,
502 index_iterator: Option<MvccIterator<'static, Arc<SortableIndexKey>, A>>,
504 mv_cursor_type: MvccCursorType,
505 table_id: MVTableId,
506 tx_id: u64,
507 reusable_immutable_record: Option<ImmutableRecord>,
509 btree_cursor: Box<dyn CursorTrait>,
510 null_flag: bool,
511 creating_new_rowid: bool,
512 state: Option<MvccLazyCursorState>,
513 count_state: Option<CountState>,
515 btree_advance_state: Option<AdvanceBtreeState>,
516 dual_peek: DualCursorPeek<A>,
518 index_finger: IndexShadowFinger<A>,
520 index_finger_epoch: u64,
530}
531
532pub enum NextRowidResult {
533 Uninitialized,
535 Next {
537 new_rowid: i64,
538 prev_rowid: Option<i64>,
539 },
540 FindRandom,
542}
543
544impl<Clock: LogicalClock + 'static, A: ConcurrentAllocator> MvccLazyCursor<Clock, A> {
545 pub fn new(
546 db: Arc<MvStore<Clock, A>>,
547 connection: &Arc<Connection>,
548 tx_id: u64,
549 root_page_or_table_id: i64,
550 mv_cursor_type: MvccCursorType,
551 btree_cursor: Box<dyn CursorTrait>,
552 ) -> Result<MvccLazyCursor<Clock, A>> {
553 turso_assert!(
554 (&*btree_cursor as &dyn Any).is::<BTreeCursor>(),
555 "BTreeCursor expected for mvcc cursor"
556 );
557 let snapshot_ts = db.read_snapshot_ts(tx_id);
562 let table_id = if connection.experimental_mvcc_passive_checkpoint_enabled() {
563 db.try_get_table_id_from_root_page_at(root_page_or_table_id, snapshot_ts)
570 .ok_or(LimboError::SchemaUpdated)?
571 } else {
572 db.get_table_id_from_root_page_at(root_page_or_table_id, snapshot_ts)
573 };
574 Ok(Self {
575 db,
576 #[cfg(any(clt_turso_tests, injected_yields))]
577 yield_instance_id: connection.next_yield_instance_id(),
578 #[cfg(any(clt_turso_tests, injected_yields))]
579 connection: connection.clone(),
580 tx_id,
581 table_iterator: None,
582 index_iterator: None,
583 mv_cursor_type,
584 current_pos: CursorPosition::BeforeFirst,
585 table_id,
586 reusable_immutable_record: None,
587 btree_cursor,
588 null_flag: false,
589 creating_new_rowid: false,
590 state: None,
591 count_state: None,
592 btree_advance_state: None,
593 dual_peek: DualCursorPeek::default(),
594 index_finger: IndexShadowFinger::default(),
595 index_finger_epoch: 0,
596 })
597 }
598
599 fn btree_row_is_valid_forward(&mut self, key: &RowKey) -> bool {
602 let RowKey::Record(rec) = key else {
603 return self.query_btree_version_is_valid(key);
604 };
605 let epoch = self.db.index_rows_epoch();
609 if self.index_finger_epoch != epoch {
610 self.index_finger.reset();
611 self.index_finger_epoch = epoch;
612 }
613 let valid = self
614 .index_finger
615 .btree_row_is_valid(&self.db, self.table_id, self.tx_id, rec);
616 #[cfg(debug_assertions)]
619 debug_assert_eq!(
620 valid,
621 self.db.query_btree_version_is_valid(
622 self.table_id,
623 &RowKey::Record(rec.clone()),
624 self.tx_id
625 ),
626 "index finger diverged from query_btree_version_is_valid"
627 );
628 valid
629 }
630
631 pub fn current_row(&mut self) -> Result<IOResult<Option<&crate::types::ImmutableRecord>>> {
633 if self.get_null_flag() {
634 return Ok(IOResult::Done(None));
635 }
636 tracing::trace!("current_row({:?})", self.current_pos);
637 match &self.current_pos {
638 CursorPosition::Loaded { in_btree: true, .. } => self.btree_cursor.record(),
639 CursorPosition::Loaded {
640 in_btree: false, ..
641 } => {
642 let versions = match &self.current_pos {
645 CursorPosition::Loaded { versions, .. } => versions.clone(),
646 _ => unreachable!("matched Loaded above"),
647 };
648
649 let found = if let Some(versions) = &versions {
650 if self.reusable_immutable_record.is_none() {
654 self.reusable_immutable_record = Some(ImmutableRecord::new(1024)?);
655 }
656 let record = self.reusable_immutable_record.as_mut().unwrap();
657 self.db
658 .read_visible_into_record(self.tx_id, versions, record)?
659 } else {
660 let row_id = match &self.current_pos {
663 CursorPosition::Loaded { row_id, .. } => row_id.clone(),
664 _ => unreachable!("matched Loaded above"),
665 };
666 let maybe_index_id = match &self.mv_cursor_type {
667 MvccCursorType::Index(_) => Some(self.table_id),
668 MvccCursorType::Table => None,
669 };
670 match self
671 .db
672 .read_from_table_or_index(self.tx_id, &row_id, maybe_index_id)?
673 {
674 Some(row) => {
675 let record = self.get_immutable_record_or_create()?;
676 record.invalidate();
677 record.start_serialization(row.payload())?;
678 true
679 }
680 None => false,
681 }
682 };
683
684 if !found {
685 return Ok(IOResult::Done(None));
686 }
687 let record_ref = self.reusable_immutable_record.as_ref().ok_or_else(|| {
688 LimboError::InternalError("immutable record not initialized".to_string())
689 })?;
690 Ok(IOResult::Done(Some(record_ref)))
691 }
692 CursorPosition::BeforeFirst => {
693 Ok(IOResult::Done(None))
695 }
696 CursorPosition::End => Ok(IOResult::Done(None)),
697 }
698 }
699
700 pub fn read_mvcc_current_row(&self) -> Result<Option<Row>> {
701 let (row_id, versions) = match &self.current_pos {
702 CursorPosition::Loaded {
703 row_id,
704 in_btree,
705 versions,
706 } if !in_btree => (row_id, versions),
707 _ => panic!("invalid position to read current mvcc row"),
708 };
709 if let Some(versions) = versions {
712 return self.db.read_visible_from_versions(self.tx_id, versions);
713 }
714 let maybe_index_id = match &self.mv_cursor_type {
715 MvccCursorType::Index(_) => Some(self.table_id),
716 MvccCursorType::Table => None,
717 };
718 self.db
719 .read_from_table_or_index(self.tx_id, row_id, maybe_index_id)
720 }
721
722 pub fn close(self) -> Result<()> {
723 Ok(())
724 }
725
726 pub fn start_new_rowid(&mut self) -> Result<IOResult<NextRowidResult>> {
727 tracing::trace!("start_new_rowid");
728
729 let allocator = self.db.get_rowid_allocator(&self.table_id);
730 let locked = allocator.lock();
731 if !locked {
732 return Ok(IOResult::IO(IOCompletions::Single(Completion::new_yield())));
734 }
735
736 self.creating_new_rowid = true;
737 let res = if allocator.is_uninitialized() {
738 NextRowidResult::Uninitialized
739 } else if let Some((next_rowid, prev_max_rowid)) = allocator.get_next_rowid() {
740 NextRowidResult::Next {
741 new_rowid: next_rowid,
742 prev_rowid: prev_max_rowid,
743 }
744 } else {
745 NextRowidResult::FindRandom
746 };
747 Ok(IOResult::Done(res))
748 }
749
750 pub fn initialize_max_rowid(&mut self, max_rowid: Option<i64>) -> Result<()> {
751 let allocator = self.db.get_rowid_allocator(&self.table_id);
752 turso_assert!(
753 self.creating_new_rowid,
754 "cursor didn't start creating new rowid"
755 );
756 allocator.initialize(max_rowid);
757 Ok(())
758 }
759
760 pub fn allocate_next_rowid(&self) -> Option<(i64, Option<i64>)> {
763 let allocator = self.db.get_rowid_allocator(&self.table_id);
764 allocator.get_next_rowid()
765 }
766
767 pub fn end_new_rowid(&mut self) {
768 tracing::trace!(
769 "end_new_rowid creating_new_rowid={}",
770 self.creating_new_rowid
771 );
772 if self.creating_new_rowid {
776 let allocator = self.db.get_rowid_allocator(&self.table_id);
777 allocator.unlock();
778 self.creating_new_rowid = false;
779 }
780 }
781
782 fn get_immutable_record_or_create(&mut self) -> Result<&mut ImmutableRecord> {
783 if self.reusable_immutable_record.is_none() {
784 self.reusable_immutable_record = Some(ImmutableRecord::new(1024)?);
785 }
786 Ok(self.reusable_immutable_record.as_mut().unwrap())
787 }
788
789 fn get_current_pos(&self) -> CursorPosition<A> {
790 self.current_pos.clone()
791 }
792
793 fn is_btree_allocated(&self) -> bool {
794 let begin_ts = self.db.read_snapshot_ts(self.tx_id);
801 let read_mark = self.db.read_tx_mark(self.tx_id);
802 self.db
803 .is_btree_readable_at(&self.table_id, begin_ts, read_mark)
804 }
805
806 fn query_btree_version_is_valid(&self, key: &RowKey) -> bool {
807 self.db
808 .query_btree_version_is_valid(self.table_id, key, self.tx_id)
809 }
810
811 fn advance_mvcc_iterator(&mut self) {
813 let new_peek_state = match &self.mv_cursor_type {
814 MvccCursorType::Table => match self.db.advance_cursor_and_get_row_id_for_table(
815 self.table_id,
816 &mut self.table_iterator,
817 self.tx_id,
818 ) {
819 Some((row_id, versions)) => CursorPeek::Row {
820 key: row_id.row_id,
821 versions: Some(versions),
822 },
823 None => CursorPeek::Exhausted,
824 },
825 MvccCursorType::Index(_) => match self
826 .db
827 .advance_cursor_and_get_row_id_for_index(&mut self.index_iterator, self.tx_id)
828 {
829 Some(row_id) => CursorPeek::Row {
830 key: row_id.row_id,
831 versions: None,
832 },
833 None => CursorPeek::Exhausted,
834 },
835 };
836 self.dual_peek.mvcc_peek = new_peek_state;
837 }
838
839 fn advance_btree_forward(&mut self) -> Result<IOResult<()>> {
841 self._advance_btree_forward(true)
842 }
843
844 fn advance_btree_forward_from_current(&mut self) -> Result<IOResult<()>> {
846 self._advance_btree_forward(false)
847 }
848
849 fn _advance_btree_forward(&mut self, initialize: bool) -> Result<IOResult<()>> {
850 loop {
851 let state = self.btree_advance_state;
852 match state {
853 None => {
854 if !self.is_btree_allocated() {
855 self.dual_peek.btree_peek = CursorPeek::Exhausted;
856 self.btree_advance_state = None;
857 return Ok(IOResult::Done(()));
858 }
859 if initialize && self.dual_peek.btree_uninitialized() {
861 return_if_io!(self.btree_cursor.rewind());
862 self.btree_advance_state = Some(AdvanceBtreeState::RewindCheckBtreeKey);
863 } else {
864 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
865 }
866 inject_io_yield!(self, CursorYieldPoint::AdvanceBtreeForwardProgress);
867 }
868 Some(AdvanceBtreeState::RewindCheckBtreeKey) => {
869 let key = self.get_btree_current_key()?;
870 match key {
871 Some(k) if self.btree_row_is_valid_forward(&k) => {
872 self.dual_peek.btree_peek = CursorPeek::Row {
873 key: k,
874 versions: None,
875 };
876 self.btree_advance_state = None;
877 return Ok(IOResult::Done(()));
878 }
879 Some(_) => {
880 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
882 }
883 None => {
884 self.dual_peek.btree_peek = CursorPeek::Exhausted;
885 self.btree_advance_state = None;
886 return Ok(IOResult::Done(()));
887 }
888 }
889 }
890 Some(AdvanceBtreeState::NextBtree) => {
891 let peek = &mut self.dual_peek;
892 return_if_io!(self.btree_cursor.next());
893 let found = self.btree_cursor.has_record();
894 if !found {
895 peek.btree_peek = CursorPeek::Exhausted;
896 self.btree_advance_state = None;
897 return Ok(IOResult::Done(()));
898 }
899 self.btree_advance_state = Some(AdvanceBtreeState::NextCheckBtreeKey);
900 inject_io_yield!(self, CursorYieldPoint::AdvanceBtreeForwardProgress);
901 }
902 Some(AdvanceBtreeState::NextCheckBtreeKey) => {
903 let key = self.get_btree_current_key()?;
904 if let Some(key) = key {
905 if self.btree_row_is_valid_forward(&key) {
906 self.dual_peek.btree_peek = CursorPeek::Row {
907 key,
908 versions: None,
909 };
910 self.btree_advance_state = None;
911 return Ok(IOResult::Done(()));
912 }
913 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
916 } else {
917 self.dual_peek.btree_peek = CursorPeek::Exhausted;
918 self.btree_advance_state = None;
919 return Ok(IOResult::Done(()));
920 }
921 }
922 }
923 }
924 }
925
926 fn advance_btree_backward(&mut self) -> Result<IOResult<()>> {
928 self._advance_btree_backward(true)
929 }
930
931 fn advance_btree_backward_from_current(&mut self) -> Result<IOResult<()>> {
933 self._advance_btree_backward(false)
934 }
935
936 fn _advance_btree_backward(&mut self, initialize: bool) -> Result<IOResult<()>> {
937 loop {
938 let state = self.btree_advance_state;
939 match state {
940 None => {
941 if !self.is_btree_allocated() {
942 let peek = &mut self.dual_peek;
943 peek.btree_peek = CursorPeek::Exhausted;
944 self.btree_advance_state = None;
945 return Ok(IOResult::Done(()));
946 }
947 if initialize && self.dual_peek.btree_uninitialized() {
949 return_if_io!(self.btree_cursor.last());
950 self.btree_advance_state = Some(AdvanceBtreeState::RewindCheckBtreeKey);
951 } else {
952 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
953 }
954 inject_io_yield!(self, CursorYieldPoint::AdvanceBtreeBackwardProgress);
955 }
956 Some(AdvanceBtreeState::RewindCheckBtreeKey) => {
957 let key = self.get_btree_current_key()?;
958 match key {
959 Some(k) if self.query_btree_version_is_valid(&k) => {
960 self.dual_peek.btree_peek = CursorPeek::Row {
961 key: k,
962 versions: None,
963 };
964 self.btree_advance_state = None;
965 return Ok(IOResult::Done(()));
966 }
967 Some(_) => {
968 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
970 }
971 None => {
972 self.dual_peek.btree_peek = CursorPeek::Exhausted;
973 self.btree_advance_state = None;
974 return Ok(IOResult::Done(()));
975 }
976 }
977 }
978 Some(AdvanceBtreeState::NextBtree) => {
979 return_if_io!(self.btree_cursor.prev());
980 let peek = &mut self.dual_peek;
981 let found = self.btree_cursor.has_record();
982 if !found {
983 peek.btree_peek = CursorPeek::Exhausted;
984 self.btree_advance_state = None;
985 return Ok(IOResult::Done(()));
986 }
987 self.btree_advance_state = Some(AdvanceBtreeState::NextCheckBtreeKey);
988 inject_io_yield!(self, CursorYieldPoint::AdvanceBtreeBackwardProgress);
989 }
990 Some(AdvanceBtreeState::NextCheckBtreeKey) => {
991 let key = self.get_btree_current_key()?;
992 match key {
993 Some(k) if self.query_btree_version_is_valid(&k) => {
994 self.dual_peek.btree_peek = CursorPeek::Row {
995 key: k,
996 versions: None,
997 };
998 self.btree_advance_state = None;
999 return Ok(IOResult::Done(()));
1000 }
1001 Some(_) => {
1002 self.btree_advance_state = Some(AdvanceBtreeState::NextBtree);
1004 }
1005 None => {
1006 self.dual_peek.btree_peek = CursorPeek::Exhausted;
1007 self.btree_advance_state = None;
1008 return Ok(IOResult::Done(()));
1009 }
1010 }
1011 }
1012 }
1013 }
1014 }
1015
1016 fn get_btree_current_key(&mut self) -> Result<Option<RowKey>> {
1018 match &self.mv_cursor_type {
1019 MvccCursorType::Table => {
1020 let maybe_rowid = loop {
1021 match self.btree_cursor.rowid()? {
1022 IOResult::Done(maybe_rowid) => {
1023 break maybe_rowid.map(RowKey::Int);
1024 }
1025 IOResult::IO(c) => {
1026 c.wait(self.btree_cursor.get_pager().io.as_ref())?; }
1028 }
1029 };
1030 Ok(maybe_rowid)
1031 }
1032 MvccCursorType::Index(index_info) => {
1033 let maybe_record = loop {
1034 match self.btree_cursor.record()? {
1035 IOResult::Done(maybe_record) => {
1036 break maybe_record;
1037 }
1038 IOResult::IO(c) => {
1039 c.wait(self.btree_cursor.get_pager().io.as_ref())?; }
1041 }
1042 };
1043 Ok(maybe_record.map(|record| {
1044 RowKey::Record(Arc::new(SortableIndexKey {
1045 key: record.clone(),
1046 metadata: index_info.clone(),
1047 }))
1048 }))
1049 }
1050 }
1051 }
1052
1053 fn refresh_current_position(&mut self, dir: IterationDirection) {
1055 let new_position = self.dual_peek.cursor_position_from_next(self.table_id, dir);
1056 self.current_pos = new_position;
1057 }
1058
1059 fn reset_dual_peek(&mut self) {
1061 self.dual_peek = DualCursorPeek::default();
1062 self.index_finger.reset();
1064 }
1065
1066 fn seek_btree_and_set_peek(
1070 &mut self,
1071 seek_key: SeekKey<'_>,
1072 op: SeekOp,
1073 ) -> Result<IOResult<()>> {
1074 if !self.is_btree_allocated() {
1076 self.dual_peek.btree_peek = CursorPeek::Exhausted;
1077 self.state = None;
1078 return Ok(IOResult::Done(()));
1079 }
1080
1081 loop {
1082 let Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(btree_seek_state), direction)) =
1083 self.state.clone()
1084 else {
1085 panic!(
1086 "Invalid btree seek state in seek_btree_and_set_peek: {:?}",
1087 self.state
1088 );
1089 };
1090 match btree_seek_state {
1091 SeekBtreeState::SeekBtree => {
1092 let seek_result = return_if_io!(self.btree_cursor.seek(seek_key.clone(), op));
1093
1094 match seek_result {
1095 SeekResult::NotFound => {
1096 self.dual_peek.btree_peek = CursorPeek::Exhausted;
1097 return Ok(IOResult::Done(()));
1098 }
1099 SeekResult::TryAdvance => {
1100 self.state.replace(MvccLazyCursorState::Seek(
1102 SeekState::SeekBtree(SeekBtreeState::AdvanceBTree),
1103 direction,
1104 ));
1105 inject_io_yield!(self, CursorYieldPoint::SeekBtreeProgress);
1106 }
1107 SeekResult::Found => {
1108 self.state.replace(MvccLazyCursorState::Seek(
1109 SeekState::SeekBtree(SeekBtreeState::CheckRow),
1110 direction,
1111 ));
1112 inject_io_yield!(self, CursorYieldPoint::SeekBtreeProgress);
1113 }
1114 }
1115 }
1116 SeekBtreeState::AdvanceBTree => {
1117 return_if_io!(match direction {
1118 IterationDirection::Forwards => {
1119 self.advance_btree_forward_from_current()
1120 }
1121 IterationDirection::Backwards => {
1122 self.advance_btree_backward_from_current()
1123 }
1124 });
1125 self.state.replace(MvccLazyCursorState::Seek(
1126 SeekState::SeekBtree(SeekBtreeState::CheckRow),
1127 direction,
1128 ));
1129 inject_io_yield!(self, CursorYieldPoint::SeekBtreeProgress);
1130 }
1131 SeekBtreeState::CheckRow => {
1132 let key = self.get_btree_current_key()?;
1133 match key {
1134 Some(k) if self.query_btree_version_is_valid(&k) => {
1135 self.dual_peek.btree_peek = CursorPeek::Row {
1136 key: k,
1137 versions: None,
1138 };
1139 return Ok(IOResult::Done(()));
1140 }
1141 Some(_) => {
1142 self.state.replace(MvccLazyCursorState::Seek(
1144 SeekState::SeekBtree(SeekBtreeState::AdvanceBTree),
1145 direction,
1146 ));
1147 inject_io_yield!(self, CursorYieldPoint::SeekBtreeProgress);
1148 }
1149 None => {
1150 self.dual_peek.btree_peek = CursorPeek::Exhausted;
1151 return Ok(IOResult::Done(()));
1152 }
1153 }
1154 }
1155 }
1156 }
1157 }
1158
1159 fn init_mvcc_iterator_forward(&mut self) -> Result<(), TryReserveError> {
1161 if self.table_iterator.is_some() || self.index_iterator.is_some() {
1162 return Ok(()); }
1164 match &self.mv_cursor_type {
1165 MvccCursorType::Table => {
1166 let start_rowid = RowID {
1167 table_id: self.table_id,
1168 row_id: RowKey::Int(i64::MIN),
1169 };
1170 let range =
1171 create_seek_range(Bound::Included(start_rowid), IterationDirection::Forwards);
1172 let iter_box = Box::new(self.db.rows.range(range));
1173 self.table_iterator = Some(static_iterator_hack!(iter_box, RowID, A));
1174 }
1175 MvccCursorType::Index(_) => {
1176 let index_rows = self.db.get_or_create_index_rows(self.table_id)?;
1177 let index_rows = index_rows.value();
1178 let iter_box: Box<
1179 dyn Iterator<Item = MvccEntry<'_, Arc<SortableIndexKey>, A>> + Send + Sync,
1180 > = Box::new(index_rows.iter());
1181 self.index_iterator =
1182 Some(static_iterator_hack!(iter_box, Arc<SortableIndexKey>, A));
1183 }
1184 }
1185 Ok(())
1186 }
1187}
1188
1189impl<Clock: LogicalClock + 'static, A: ConcurrentAllocator> Drop for MvccLazyCursor<Clock, A> {
1190 fn drop(&mut self) {
1191 self.end_new_rowid();
1195 }
1196}
1197
1198impl<Clock: LogicalClock + 'static, A: ConcurrentAllocator> CursorTrait
1199 for MvccLazyCursor<Clock, A>
1200{
1201 fn last(&mut self) -> Result<IOResult<()>> {
1202 self.set_null_flag(false);
1205 let state = self.state.clone();
1206 if state.is_none() {
1207 let _ = self.table_iterator.take();
1208 let _ = self.index_iterator.take();
1209 self.reset_dual_peek();
1210 self.state
1211 .replace(MvccLazyCursorState::Rewind(RewindState::Advance));
1212 }
1213
1214 turso_assert!(
1215 matches!(
1216 self.state
1217 .as_ref()
1218 .expect("rewind state is not initialized"),
1219 MvccLazyCursorState::Rewind(RewindState::Advance)
1220 ),
1221 "invalid last state",
1222 { "state": format!("{:?}", self.state) }
1223 );
1224
1225 return_if_io!(self.advance_btree_backward());
1227
1228 self.invalidate_record();
1229 self.current_pos = CursorPosition::End;
1230
1231 match &self.mv_cursor_type {
1233 MvccCursorType::Table => match self.db.get_last_table_rowid(
1234 self.table_id,
1235 &mut self.table_iterator,
1236 self.tx_id,
1237 ) {
1238 Some(k) => {
1239 tracing::trace!("last: mvcc_key: {:?}", k);
1240 self.dual_peek.mvcc_peek = CursorPeek::Row {
1241 key: k,
1242 versions: None,
1243 };
1244 }
1245 None => {
1246 self.dual_peek.mvcc_peek = CursorPeek::Exhausted;
1247 }
1248 },
1249 MvccCursorType::Index(_) => match self.db.get_last_index_rowid(
1250 self.table_id,
1251 self.tx_id,
1252 &mut self.index_iterator,
1253 )? {
1254 Some(k) => {
1255 self.dual_peek.mvcc_peek = CursorPeek::Row {
1256 key: k,
1257 versions: None,
1258 };
1259 }
1260 None => {
1261 self.dual_peek.mvcc_peek = CursorPeek::Exhausted;
1262 }
1263 },
1264 };
1265
1266 self.refresh_current_position(IterationDirection::Backwards);
1267 self.invalidate_record();
1268 self.state = None;
1269
1270 Ok(IOResult::Done(()))
1271 }
1272
1273 fn next(&mut self) -> Result<IOResult<()>> {
1277 if self.state.is_none() {
1278 let current_pos = self.get_current_pos();
1280 if matches!(current_pos, CursorPosition::BeforeFirst) {
1281 let uninitialized = self.dual_peek.both_uninitialized();
1282 if uninitialized {
1283 self.init_mvcc_iterator_forward()?;
1285 self.advance_mvcc_iterator();
1286 self.state
1287 .replace(MvccLazyCursorState::Next(NextState::AdvanceUnitialized));
1288 } else {
1289 self.state
1290 .replace(MvccLazyCursorState::Next(NextState::CheckNeedsAdvance));
1291 }
1292 inject_io_yield!(self, CursorYieldPoint::NextStart);
1293 } else {
1294 self.state
1295 .replace(MvccLazyCursorState::Next(NextState::CheckNeedsAdvance));
1296 inject_io_yield!(self, CursorYieldPoint::NextStart);
1297 }
1298 }
1299 if matches!(
1301 self.state.as_ref().expect("next state is not initialized"),
1302 MvccLazyCursorState::Next(NextState::AdvanceUnitialized)
1303 ) {
1304 return_if_io!(self.advance_btree_forward());
1305 self.state
1306 .replace(MvccLazyCursorState::Next(NextState::CheckNeedsAdvance));
1307 }
1308
1309 if matches!(
1310 self.state.as_ref().expect("next state is not initialized"),
1311 MvccLazyCursorState::Next(NextState::CheckNeedsAdvance)
1312 ) {
1313 let current_pos = self.get_current_pos();
1315 let (need_advance_mvcc, need_advance_btree) = match ¤t_pos {
1316 CursorPosition::BeforeFirst => {
1317 (false, false)
1320 }
1321 CursorPosition::Loaded { in_btree, .. } => {
1322 if *in_btree {
1324 (false, true) } else {
1326 (true, false) }
1328 }
1329 CursorPosition::End => {
1330 self.state = None;
1331 return Ok(IOResult::Done(()));
1332 }
1333 };
1334
1335 if need_advance_mvcc && !self.dual_peek.mvcc_exhausted() {
1337 self.advance_mvcc_iterator();
1338 }
1339 if need_advance_btree && !self.dual_peek.btree_exhausted() {
1340 self.state
1341 .replace(MvccLazyCursorState::Next(NextState::Advance));
1342 inject_io_yield!(self, CursorYieldPoint::NextBtreeAdvance);
1343 }
1344 }
1345
1346 if matches!(
1347 self.state.as_ref().expect("next state is not initialized"),
1348 MvccLazyCursorState::Next(NextState::Advance)
1349 ) {
1350 return_if_io!(self.advance_btree_forward());
1351 }
1352
1353 self.refresh_current_position(IterationDirection::Forwards);
1354 self.invalidate_record();
1355 self.state = None;
1356
1357 Ok(IOResult::Done(()))
1358 }
1359
1360 fn prev(&mut self) -> Result<IOResult<()>> {
1364 if self.state.is_none() {
1365 let current_pos = self.get_current_pos();
1367 if matches!(current_pos, CursorPosition::End) {
1368 let uninitialized = self.dual_peek.both_uninitialized();
1369 if uninitialized {
1370 self.state
1371 .replace(MvccLazyCursorState::Prev(PrevState::AdvanceUnitialized));
1372 return_if_io!(self.last());
1373 } else {
1374 self.state
1375 .replace(MvccLazyCursorState::Prev(PrevState::CheckNeedsAdvance));
1376 }
1377 } else {
1378 self.state
1379 .replace(MvccLazyCursorState::Prev(PrevState::CheckNeedsAdvance));
1380 }
1381 }
1382
1383 if matches!(
1384 self.state.as_ref().expect("prev state is not initialized"),
1385 MvccLazyCursorState::Prev(PrevState::AdvanceUnitialized)
1386 ) {
1387 return_if_io!(self.last());
1388 self.state
1389 .replace(MvccLazyCursorState::Prev(PrevState::CheckNeedsAdvance));
1390 }
1391
1392 if matches!(
1393 self.state.as_ref().expect("prev state is not initialized"),
1394 MvccLazyCursorState::Prev(PrevState::CheckNeedsAdvance)
1395 ) {
1396 let current_pos = self.get_current_pos();
1398 let (need_advance_mvcc, need_advance_btree) = match ¤t_pos {
1399 CursorPosition::End => {
1400 (false, false)
1402 }
1403 CursorPosition::Loaded { in_btree, .. } => {
1404 if *in_btree {
1406 (false, true) } else {
1408 (true, false) }
1410 }
1411 CursorPosition::BeforeFirst => {
1412 self.state = None;
1413 return Ok(IOResult::Done(()));
1414 }
1415 };
1416
1417 if need_advance_mvcc && !self.dual_peek.mvcc_exhausted() {
1419 self.advance_mvcc_iterator();
1420 }
1421 if need_advance_btree && !self.dual_peek.btree_exhausted() {
1422 self.state
1423 .replace(MvccLazyCursorState::Prev(PrevState::Advance));
1424 inject_io_yield!(self, CursorYieldPoint::PrevBtreeAdvance);
1425 }
1426 }
1427
1428 if matches!(
1429 self.state.as_ref().expect("prev state is not initialized"),
1430 MvccLazyCursorState::Prev(PrevState::Advance)
1431 ) {
1432 return_if_io!(self.advance_btree_backward());
1433 }
1434 self.refresh_current_position(IterationDirection::Backwards);
1435 self.invalidate_record();
1436 self.state = None;
1437
1438 Ok(IOResult::Done(()))
1439 }
1440
1441 fn rowid(&mut self) -> Result<IOResult<Option<i64>>> {
1442 if self.get_null_flag() {
1443 return Ok(IOResult::Done(None));
1444 }
1445 let rowid = match self.get_current_pos() {
1446 CursorPosition::Loaded {
1447 row_id,
1448 in_btree: _,
1449 ..
1450 } => match &row_id.row_id {
1451 RowKey::Int(id) => Some(*id),
1452 RowKey::Record(sortable_key) => {
1453 let MvccCursorType::Index(index_info) = &self.mv_cursor_type else {
1455 panic!("RowKey::Record requires Index cursor type");
1456 };
1457 if index_info.has_rowid {
1458 match sortable_key.key.last_value() {
1459 Some(Ok(crate::types::ValueRef::Numeric(
1460 crate::numeric::Numeric::Integer(rowid),
1461 ))) => Some(rowid),
1462 _ => {
1463 crate::bail_parse_error!("Failed to parse rowid from index record")
1464 }
1465 }
1466 } else {
1467 crate::bail_parse_error!("Indexes without rowid are not supported in MVCC");
1468 }
1469 }
1470 },
1471 CursorPosition::BeforeFirst => None,
1472 CursorPosition::End => None,
1473 };
1474 Ok(IOResult::Done(rowid))
1475 }
1476
1477 fn record(&mut self) -> Result<IOResult<Option<&crate::types::ImmutableRecord>>> {
1478 self.current_row()
1479 }
1480
1481 fn seek_unpacked(
1482 &mut self,
1483 registers: &[Register],
1484 op: SeekOp,
1485 ) -> Result<IOResult<SeekResult>> {
1486 let record = make_record(registers, &0, ®isters.len())?;
1487 self.seek(SeekKey::IndexKey(&record), op)
1488 }
1489
1490 fn seek(&mut self, seek_key: SeekKey<'_>, op: SeekOp) -> Result<IOResult<SeekResult>> {
1491 if self.state.is_none() && op.eq_only() {
1524 if let CursorPosition::Loaded {
1525 row_id, in_btree, ..
1526 } = &self.current_pos
1527 {
1528 if current_pos_matches_seek_key(&row_id.row_id, &seek_key, &self.mv_cursor_type)? {
1529 let maybe_index_id = match &self.mv_cursor_type {
1530 MvccCursorType::Index(_) => Some(self.table_id),
1531 MvccCursorType::Table => None,
1532 };
1533 let visible = self
1541 .db
1542 .read_from_table_or_index(self.tx_id, row_id, maybe_index_id)?
1543 .is_some()
1544 || (*in_btree && self.query_btree_version_is_valid(&row_id.row_id));
1545 if visible {
1546 self.set_null_flag(false);
1550 return Ok(IOResult::Done(SeekResult::Found));
1551 }
1552 }
1553 }
1554 }
1555
1556 loop {
1557 let state = self.state.clone();
1558 match state {
1559 None => {
1560 let _ = self.table_iterator.take();
1562 let _ = self.index_iterator.take();
1563 self.reset_dual_peek();
1564 self.invalidate_record();
1565 self.set_null_flag(false);
1569
1570 let direction = op.iteration_direction();
1571 let inclusive = matches!(op, SeekOp::GE { .. } | SeekOp::LE { .. });
1572
1573 match &seek_key {
1574 SeekKey::TableRowId(row_id) => {
1575 let rowid = RowID {
1576 table_id: self.table_id,
1577 row_id: RowKey::Int(*row_id),
1578 };
1579
1580 let mvcc_rowid = self.db.seek_rowid(
1582 rowid.clone(),
1583 inclusive,
1584 op.eq_only(),
1585 direction,
1586 self.tx_id,
1587 &mut self.table_iterator,
1588 );
1589
1590 {
1592 self.dual_peek.mvcc_peek = match &mvcc_rowid {
1593 Some(rid) => CursorPeek::Row {
1594 key: rid.row_id.clone(),
1595 versions: None,
1596 },
1597 None => CursorPeek::Exhausted,
1598 };
1599 }
1600 }
1601 SeekKey::IndexKey(index_key) => {
1602 let index_info = {
1603 let MvccCursorType::Index(index_info) = &self.mv_cursor_type else {
1604 panic!("SeekKey::IndexKey requires Index cursor type");
1605 };
1606 Arc::new(IndexInfo::new_in(
1607 index_info.key_info.iter().cloned(),
1608 index_info.has_rowid,
1609 index_key.column_count(),
1610 index_info.is_unique,
1611 self.db.allocator(),
1612 )?)
1613 };
1614 let sortable_key =
1615 SortableIndexKey::new_from_record((*index_key).clone(), index_info);
1616
1617 let mvcc_rowid = self.db.seek_index(
1619 self.table_id,
1620 sortable_key.clone(),
1621 inclusive,
1622 op.eq_only(),
1623 direction,
1624 self.tx_id,
1625 &mut self.index_iterator,
1626 )?;
1627
1628 {
1630 self.dual_peek.mvcc_peek = match &mvcc_rowid {
1631 Some(rid) => CursorPeek::Row {
1632 key: rid.row_id.clone(),
1633 versions: None,
1634 },
1635 None => CursorPeek::Exhausted,
1636 };
1637 }
1638 }
1639 }
1640
1641 self.state.replace(MvccLazyCursorState::Seek(
1643 SeekState::SeekBtree(SeekBtreeState::SeekBtree),
1644 direction,
1645 ));
1646 inject_io_yield!(self, CursorYieldPoint::SeekStart);
1647 }
1648 Some(MvccLazyCursorState::Seek(SeekState::SeekBtree(_), direction)) => {
1649 return_if_io!(self.seek_btree_and_set_peek(seek_key.clone(), op));
1650 self.state
1651 .replace(MvccLazyCursorState::Seek(SeekState::PickWinner, direction));
1652 inject_io_yield!(self, CursorYieldPoint::SeekBtreeProgress);
1653 }
1654 Some(MvccLazyCursorState::Seek(SeekState::PickWinner, direction)) => {
1655 let winner = self.dual_peek.get_next(direction);
1658
1659 self.state = None;
1661
1662 if let Some((winner_key, in_btree, winner_versions)) = winner {
1663 self.current_pos = CursorPosition::Loaded {
1664 row_id: RowID {
1665 table_id: self.table_id,
1666 row_id: winner_key.clone(),
1667 },
1668 in_btree,
1669 versions: winner_versions,
1670 };
1671
1672 if op.eq_only() {
1673 let found = match &seek_key {
1675 SeekKey::TableRowId(row_id) => winner_key == RowKey::Int(*row_id),
1676 SeekKey::IndexKey(index_key) => {
1677 let RowKey::Record(found_key) = &winner_key else {
1678 panic!("Found rowid is not a record");
1679 };
1680 let MvccCursorType::Index(index_info) = &self.mv_cursor_type
1681 else {
1682 panic!("Index cursor expected");
1683 };
1684 let key_info: Vec<_> = index_info
1685 .key_info
1686 .iter()
1687 .take(index_key.column_count())
1688 .cloned()
1689 .collect();
1690 let cmp = compare_immutable(
1691 index_key.get_values()?,
1692 found_key.key.get_values()?,
1693 &key_info,
1694 );
1695 cmp.is_eq()
1696 }
1697 };
1698 if found {
1699 return Ok(IOResult::Done(SeekResult::Found));
1700 } else {
1701 return Ok(IOResult::Done(SeekResult::NotFound));
1702 }
1703 } else {
1704 return Ok(IOResult::Done(SeekResult::Found));
1705 }
1706 } else {
1707 let forwards = matches!(op, SeekOp::GE { .. } | SeekOp::GT);
1709 if forwards {
1710 self.current_pos = CursorPosition::End;
1711 } else {
1712 self.current_pos = CursorPosition::BeforeFirst;
1713 }
1714 return Ok(IOResult::Done(SeekResult::NotFound));
1715 }
1716 }
1717 _ => {
1718 panic!("Invalid state in seek: {:?}", self.state);
1719 }
1720 }
1721 }
1722 }
1723
1724 fn insert(&mut self, key: &BTreeKey) -> Result<IOResult<()>> {
1727 let row_id = match key {
1728 BTreeKey::TableRowId((rowid, _)) => RowID::new(self.table_id, RowKey::Int(*rowid)),
1729 BTreeKey::IndexKey(record) => {
1730 let MvccCursorType::Index(index_info) = &self.mv_cursor_type else {
1731 panic!("BTreeKey::IndexKey requires Index cursor type");
1732 };
1733 let sortable_key = Arc::new(SortableIndexKey::new_from_record(
1734 (*record).clone(),
1735 index_info.clone(),
1736 ));
1737 RowID::new(self.table_id, RowKey::Record(sortable_key))
1738 }
1739 };
1740 let row = match &self.mv_cursor_type {
1741 MvccCursorType::Table => {
1742 let BTreeKey::TableRowId((_, record)) = key else {
1743 return Err(LimboError::InternalError(
1744 "Table cursor requires a TableRowId key".to_string(),
1745 ));
1746 };
1747 let record = record.as_ref().ok_or_else(|| {
1748 LimboError::InternalError("TableRowId should have a record".to_string())
1749 })?;
1750 let num_columns = record.column_count();
1751 crate::with_mv_store_allocation_site!(
1752 RowPayload,
1753 Row::new_table_row_in(
1754 row_id,
1755 record.get_payload(),
1756 num_columns,
1757 self.db.allocator(),
1758 )
1759 )
1760 }
1761 MvccCursorType::Index(_) => {
1762 let BTreeKey::IndexKey(record) = key else {
1763 return Err(LimboError::InternalError(
1764 "Index cursor requires an IndexKey".to_string(),
1765 ));
1766 };
1767 Ok(Row::new_index_row(row_id, record.column_count()))
1768 }
1769 }?;
1770
1771 let was_btree_resident = match &self.current_pos {
1775 CursorPosition::Loaded {
1776 row_id: current_row_id,
1777 in_btree,
1778 ..
1779 } => *in_btree && *current_row_id == row.id,
1780 _ => false,
1781 };
1782
1783 self.current_pos = CursorPosition::Loaded {
1784 row_id: row.id.clone(),
1785 in_btree: was_btree_resident,
1786 versions: None,
1787 };
1788 let maybe_index_id = match &self.mv_cursor_type {
1789 MvccCursorType::Index(_) => Some(self.table_id),
1790 MvccCursorType::Table => None,
1791 };
1792 if self
1794 .db
1795 .read_from_table_or_index(self.tx_id, &row.id, maybe_index_id)?
1796 .is_some()
1797 {
1798 self.db
1799 .update_to_table_or_index(self.tx_id, row, maybe_index_id)
1800 .inspect_err(|_| {
1801 self.current_pos = CursorPosition::BeforeFirst;
1802 })?;
1803 } else if was_btree_resident {
1804 self.db
1807 .insert_btree_resident_to_table_or_index(self.tx_id, row, maybe_index_id)
1808 .inspect_err(|_| {
1809 self.current_pos = CursorPosition::BeforeFirst;
1810 })?;
1811 } else {
1812 self.db
1813 .insert_to_table_or_index(self.tx_id, row, maybe_index_id)
1814 .inspect_err(|_| {
1815 self.current_pos = CursorPosition::BeforeFirst;
1816 })?;
1817 }
1818 self.invalidate_record();
1819 Ok(IOResult::Done(()))
1820 }
1821
1822 fn delete(&mut self) -> Result<IOResult<()>> {
1823 let (rowid, in_btree) = match self.get_current_pos() {
1824 CursorPosition::Loaded {
1825 row_id, in_btree, ..
1826 } => (row_id, in_btree),
1827 _ => panic!("Cannot delete: no current row"),
1828 };
1829 if in_btree {
1830 turso_assert!(
1831 self.is_btree_allocated(),
1832 "MVCC cursor marked current row as B-tree resident without an allocated B-tree",
1833 { "row_id": &rowid }
1834 );
1835 }
1836 let maybe_index_id = match &self.mv_cursor_type {
1837 MvccCursorType::Index(_) => Some(self.table_id),
1838 MvccCursorType::Table => None,
1839 };
1840 if in_btree {
1847 return_if_io!(self.record());
1848 }
1849 let was_deleted =
1850 self.db
1851 .delete_from_table_or_index(self.tx_id, rowid.clone(), maybe_index_id)?;
1852 if !was_deleted {
1856 if !in_btree {
1859 self.invalidate_record();
1860 return Ok(IOResult::Done(()));
1861 }
1862 let IOResult::Done(Some(record)) = self.record()? else {
1865 crate::bail_corrupt_error!(
1866 "Btree cursor should have a record when deleting a row that only exists in the btree"
1867 );
1868 };
1869 let record = record.clone();
1871 let column_count = record.column_count();
1872 let row = match &self.mv_cursor_type {
1873 MvccCursorType::Table => crate::with_mv_store_allocation_site!(
1874 RowPayload,
1875 Row::new_table_row_in(
1876 rowid.clone(),
1877 record.get_payload(),
1878 column_count,
1879 self.db.allocator(),
1880 )
1881 ),
1882 MvccCursorType::Index(_) => Ok(Row::new_index_row(rowid.clone(), column_count)),
1883 }?;
1884 self.db
1885 .insert_tombstone_to_table_or_index(self.tx_id, rowid, row, maybe_index_id)?;
1886 }
1887 self.invalidate_record();
1888 Ok(IOResult::Done(()))
1889 }
1890
1891 fn set_null_flag(&mut self, flag: bool) {
1892 self.null_flag = flag;
1893 }
1894
1895 fn get_null_flag(&self) -> bool {
1896 self.null_flag
1897 }
1898
1899 fn exists(&mut self, key: &Value) -> Result<IOResult<bool>> {
1900 if self.state.is_none() {
1901 self.invalidate_record();
1902 let int_key = match key {
1903 Value::Numeric(crate::numeric::Numeric::Integer(i)) => i,
1904 _ => unreachable!("btree tables are indexed by integers!"),
1905 };
1906 let inclusive = true;
1907
1908 let rowid = self.db.seek_rowid(
1912 RowID {
1913 table_id: self.table_id,
1914 row_id: RowKey::Int(*int_key),
1915 },
1916 inclusive,
1917 true,
1918 IterationDirection::Forwards,
1919 self.tx_id,
1920 &mut self.table_iterator,
1921 );
1922
1923 let mvcc_exists = if let Some(rowid) = &rowid {
1924 let RowKey::Int(rowid) = rowid.row_id else {
1925 panic!("Rowid is not an integer in mvcc table cursor");
1926 };
1927 rowid == *int_key
1928 } else {
1929 false
1930 };
1931
1932 tracing::trace!(
1933 "MVCC exists check: mvcc_exists={mvcc_exists} find={int_key} got={rowid:?}"
1934 );
1935
1936 if mvcc_exists {
1938 self.dual_peek.mvcc_peek = CursorPeek::Row {
1939 key: RowKey::Int(*int_key),
1940 versions: None,
1941 };
1942 self.current_pos = CursorPosition::Loaded {
1943 row_id: RowID {
1944 table_id: self.table_id,
1945 row_id: RowKey::Int(*int_key),
1946 },
1947 in_btree: false,
1948 versions: None,
1949 };
1950 self.state = None;
1951 return Ok(IOResult::Done(true));
1952 }
1953
1954 if self.is_btree_allocated() {
1956 let btree_is_valid = self.query_btree_version_is_valid(&RowKey::Int(*int_key));
1958
1959 if !btree_is_valid {
1961 self.state = None;
1962 return Ok(IOResult::Done(false));
1963 }
1964 self.state
1965 .replace(MvccLazyCursorState::Exists(ExistsState::ExistsBtree));
1966 inject_io_yield!(self, CursorYieldPoint::ExistsBtreeFallback);
1967 } else {
1968 self.state = None;
1970 return Ok(IOResult::Done(false));
1971 }
1972 }
1973
1974 let Some(MvccLazyCursorState::Exists(ExistsState::ExistsBtree)) = self.state.clone() else {
1975 panic!("Invalid state {:?}", self.state);
1976 };
1977 turso_assert!(
1978 self.is_btree_allocated(),
1979 "BTree should be allocated when we are in ExistsBtree state"
1980 );
1981
1982 let found = return_if_io!(self.btree_cursor.exists(key));
1984
1985 if found {
1986 let int_key = match key {
1988 Value::Numeric(crate::numeric::Numeric::Integer(i)) => *i,
1989 _ => unreachable!("btree tables are indexed by integers!"),
1990 };
1991 let row_key = RowKey::Int(int_key);
1992
1993 let is_valid = self.query_btree_version_is_valid(&row_key);
1995
1996 if is_valid {
1997 self.dual_peek.btree_peek = CursorPeek::Row {
1999 key: row_key.clone(),
2000 versions: None,
2001 };
2002 self.current_pos = CursorPosition::Loaded {
2003 row_id: RowID {
2004 table_id: self.table_id,
2005 row_id: row_key,
2006 },
2007 in_btree: true,
2008 versions: None,
2009 };
2010 self.state = None;
2011 Ok(IOResult::Done(true))
2012 } else {
2013 tracing::trace!("B-tree row {int_key} is shadowed by MVCC");
2015 self.state = None;
2016 Ok(IOResult::Done(false))
2017 }
2018 } else {
2019 self.state = None;
2021 Ok(IOResult::Done(false))
2022 }
2023 }
2024
2025 fn clear_btree(&mut self) -> Result<IOResult<Option<usize>>> {
2026 todo!()
2027 }
2028
2029 fn btree_destroy(&mut self) -> Result<IOResult<Option<usize>>> {
2030 todo!()
2031 }
2032
2033 fn count(&mut self) -> Result<IOResult<usize>> {
2034 loop {
2035 let state = self.count_state;
2036 match state {
2037 None => {
2038 self.count_state.replace(CountState::Rewind);
2039 inject_io_yield!(self, CursorYieldPoint::CountProgress);
2040 }
2041 Some(CountState::Rewind) => {
2042 return_if_io!(self.rewind());
2043 self.count_state
2044 .replace(CountState::CheckBtreeKey { count: 0 });
2045 inject_io_yield!(self, CursorYieldPoint::CountProgress);
2046 }
2047 Some(CountState::CheckBtreeKey { count }) => {
2048 if let CursorPosition::Loaded {
2049 row_id: _,
2050 in_btree: _,
2051 ..
2052 } = self.get_current_pos()
2053 {
2054 self.count_state
2055 .replace(CountState::NextBtree { count: count + 1 });
2056 inject_io_yield!(self, CursorYieldPoint::CountProgress);
2057 } else {
2058 self.count_state = None;
2059 return Ok(IOResult::Done(count));
2060 }
2061 }
2062 Some(CountState::NextBtree { count }) => {
2063 return_if_io!(self.next());
2065 self.count_state
2066 .replace(CountState::CheckBtreeKey { count });
2067 inject_io_yield!(self, CursorYieldPoint::CountProgress);
2068 }
2069 }
2070 }
2071 }
2072
2073 fn is_empty(&self) -> bool {
2075 match self.get_current_pos() {
2078 CursorPosition::Loaded { .. } => false,
2079 CursorPosition::BeforeFirst => true,
2080 CursorPosition::End => true,
2081 }
2082 }
2083
2084 fn root_page(&self) -> i64 {
2085 self.table_id.into()
2086 }
2087
2088 fn rewind(&mut self) -> Result<IOResult<()>> {
2089 self.set_null_flag(false);
2092 let state = self.state.clone();
2093 if state.is_none() {
2094 let _ = self.table_iterator.take();
2095 let _ = self.index_iterator.take();
2096 self.reset_dual_peek();
2097 self.state
2098 .replace(MvccLazyCursorState::Rewind(RewindState::Advance));
2099 }
2100
2101 turso_assert!(
2102 matches!(
2103 self.state
2104 .as_ref()
2105 .expect("rewind state is not initialized"),
2106 MvccLazyCursorState::Rewind(RewindState::Advance)
2107 ),
2108 "invalid rewind state",
2109 { "state": format!("{:?}", self.state) }
2110 );
2111 return_if_io!(self.advance_btree_forward());
2113
2114 self.invalidate_record();
2115 self.current_pos = CursorPosition::BeforeFirst;
2116
2117 match &self.mv_cursor_type {
2121 MvccCursorType::Table => {
2122 let start_rowid = RowID {
2125 table_id: self.table_id,
2126 row_id: RowKey::Int(i64::MIN),
2127 };
2128 let range = (
2129 std::ops::Bound::Included(start_rowid),
2130 std::ops::Bound::Unbounded,
2131 );
2132 let iter_box = Box::new(self.db.rows.range(range));
2133 self.table_iterator = Some(static_iterator_hack!(iter_box, RowID, A));
2134 }
2135 MvccCursorType::Index(_) => {
2136 let index_rows = self.db.get_or_create_index_rows(self.table_id)?;
2138 let index_rows = index_rows.value();
2139 let iter_box: Box<
2140 dyn Iterator<Item = MvccEntry<'_, Arc<SortableIndexKey>, A>> + Send + Sync,
2141 > = Box::new(index_rows.iter());
2142 self.index_iterator =
2143 Some(static_iterator_hack!(iter_box, Arc<SortableIndexKey>, A));
2144 }
2145 }
2146
2147 self.advance_mvcc_iterator();
2149
2150 self.refresh_current_position(IterationDirection::Forwards);
2151
2152 self.invalidate_record();
2153 self.state = None;
2154 Ok(IOResult::Done(()))
2155 }
2156
2157 fn has_record(&self) -> bool {
2158 matches!(self.get_current_pos(), CursorPosition::Loaded { .. })
2159 }
2160
2161 fn set_has_record(&mut self, _has_record: bool) {
2162 todo!()
2163 }
2164
2165 fn get_index_info(&self) -> &Arc<crate::types::IndexInfo> {
2166 match &self.mv_cursor_type {
2167 MvccCursorType::Index(index_info) => index_info,
2168 MvccCursorType::Table => panic!("get_index_info called on table cursor"),
2169 }
2170 }
2171
2172 fn seek_end(&mut self) -> Result<IOResult<()>> {
2173 if self.is_btree_allocated() {
2174 self.btree_cursor.seek_end()
2176 } else {
2177 Ok(IOResult::Done(()))
2180 }
2181 }
2182
2183 fn seek_to_last(&mut self) -> Result<IOResult<()>> {
2184 match self.seek(SeekKey::TableRowId(i64::MAX), SeekOp::LE { eq_only: false })? {
2185 IOResult::Done(_) => Ok(IOResult::Done(())),
2186 IOResult::IO(iocompletions) => Ok(IOResult::IO(iocompletions)),
2187 }
2188 }
2189
2190 fn invalidate_record(&mut self) {
2191 if let Some(record) = self.reusable_immutable_record.as_mut() {
2192 record.invalidate();
2193 }
2194 }
2195
2196 fn has_rowid(&self) -> bool {
2197 match &self.mv_cursor_type {
2198 MvccCursorType::Index(index_info) => index_info.has_rowid,
2199 MvccCursorType::Table => true, }
2201 }
2202
2203 fn get_pager(&self) -> Arc<Pager> {
2204 self.btree_cursor.get_pager()
2205 }
2206
2207 fn get_skip_advance(&self) -> bool {
2208 todo!()
2209 }
2210
2211 fn is_mvcc(&self) -> bool {
2213 true
2214 }
2215}
2216
2217impl<Clock: LogicalClock, A: ConcurrentAllocator> Debug for MvccLazyCursor<Clock, A> {
2218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2219 f.debug_struct("MvccLazyCursor")
2220 .field("current_pos", &self.current_pos)
2221 .field("table_id", &self.table_id)
2222 .field("tx_id", &self.tx_id)
2223 .field("reusable_immutable_record", &self.reusable_immutable_record)
2224 .field("btree_cursor", &())
2225 .finish()
2226 }
2227}