core/mvcc/cursor: CursorPosition::Loaded include if points to btree

This commit is contained in:
Pere Diaz Bou
2025-10-22 12:22:16 +02:00
parent a47ac6cb96
commit 38f6d20def

View File

@@ -16,8 +16,8 @@ use std::sync::Arc;
enum CursorPosition {
/// We haven't loaded any row yet.
BeforeFirst,
/// We have loaded a row.
Loaded(RowID),
/// We have loaded a row. This position points to a rowid in either MVCC index or in BTree.
Loaded { row_id: RowID, in_btree: bool },
/// We have reached the end of the table.
End,
}
@@ -29,7 +29,7 @@ pub struct MvccLazyCursor<Clock: LogicalClock> {
tx_id: u64,
/// Reusable immutable record, used to allow better allocation strategy.
reusable_immutable_record: RefCell<Option<ImmutableRecord>>,
_btree_cursor: Box<dyn CursorTrait>,
btree_cursor: Box<dyn CursorTrait>,
null_flag: bool,
record_cursor: RefCell<RecordCursor>,
next_rowid_lock: Arc<RwLock<()>>,
@@ -48,14 +48,13 @@ impl<Clock: LogicalClock + 'static> MvccLazyCursor<Clock> {
"BTreeCursor expected for mvcc cursor"
);
let table_id = db.get_table_id_from_root_page(root_page_or_table_id);
db.maybe_initialize_table(table_id, pager)?;
Ok(Self {
db,
tx_id,
current_pos: RefCell::new(CursorPosition::BeforeFirst),
table_id,
reusable_immutable_record: RefCell::new(None),
_btree_cursor: btree_cursor,
btree_cursor,
null_flag: false,
record_cursor: RefCell::new(RecordCursor::new()),
next_rowid_lock: Arc::new(RwLock::new(())),
@@ -64,14 +63,20 @@ impl<Clock: LogicalClock + 'static> MvccLazyCursor<Clock> {
pub fn current_row(&self) -> Result<Option<Row>> {
match *self.current_pos.borrow() {
CursorPosition::Loaded(id) => self.db.read(self.tx_id, id),
CursorPosition::Loaded {
row_id,
in_btree: _,
} => self.db.read(self.tx_id, row_id),
CursorPosition::BeforeFirst => {
// If we are before first, we need to try and find the first row.
let maybe_rowid =
self.db
.get_next_row_id_for_table(self.table_id, i64::MIN, self.tx_id);
if let Some(id) = maybe_rowid {
self.current_pos.replace(CursorPosition::Loaded(id));
self.current_pos.replace(CursorPosition::Loaded {
row_id: id,
in_btree: false,
});
self.db.read(self.tx_id, id)
} else {
Ok(None)
@@ -91,7 +96,10 @@ impl<Clock: LogicalClock + 'static> MvccLazyCursor<Clock> {
let _lock = lock.write();
let _ = self.last();
match *self.current_pos.borrow() {
CursorPosition::Loaded(id) => id.row_id + 1,
CursorPosition::Loaded {
row_id,
in_btree: _,
} => row_id.row_id + 1,
CursorPosition::BeforeFirst => 1,
CursorPosition::End => i64::MAX,
}
@@ -115,10 +123,13 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
fn last(&mut self) -> Result<IOResult<()>> {
let last_rowid = self.db.get_last_rowid(self.table_id);
if let Some(last_rowid) = last_rowid {
self.current_pos.replace(CursorPosition::Loaded(RowID {
table_id: self.table_id,
row_id: last_rowid,
}));
self.current_pos.replace(CursorPosition::Loaded {
row_id: RowID {
table_id: self.table_id,
row_id: last_rowid,
},
in_btree: false,
});
} else {
self.current_pos.replace(CursorPosition::BeforeFirst);
}
@@ -130,7 +141,10 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
fn next(&mut self) -> Result<IOResult<bool>> {
let before_first = matches!(self.get_current_pos(), CursorPosition::BeforeFirst);
let min_id = match *self.current_pos.borrow() {
CursorPosition::Loaded(id) => id.row_id + 1,
CursorPosition::Loaded {
row_id,
in_btree: _,
} => row_id.row_id + 1,
// TODO: do we need to forward twice?
CursorPosition::BeforeFirst => i64::MIN, // we need to find first row, so we look from the first id,
CursorPosition::End => {
@@ -139,12 +153,15 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
}
};
let new_position =
let new_position_in_mvcc =
match self
.db
.get_next_row_id_for_table(self.table_id, min_id, self.tx_id)
{
Some(id) => CursorPosition::Loaded(id),
Some(id) => CursorPosition::Loaded {
row_id: id,
in_btree: false,
},
None => {
if before_first {
// if it wasn't loaded and we didn't find anything, it means the table is empty.
@@ -155,12 +172,13 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
}
}
};
self.current_pos.replace(new_position);
// let new_position_in_btree = self.btree_cursor.next();
self.current_pos.replace(new_position_in_mvcc);
self.invalidate_record();
Ok(IOResult::Done(matches!(
self.get_current_pos(),
CursorPosition::Loaded(_)
CursorPosition::Loaded { .. }
)))
}
@@ -170,14 +188,20 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
fn rowid(&self) -> Result<IOResult<Option<i64>>> {
let rowid = match self.get_current_pos() {
CursorPosition::Loaded(id) => Some(id.row_id),
CursorPosition::Loaded {
row_id,
in_btree: _,
} => Some(row_id.row_id),
CursorPosition::BeforeFirst => {
// If we are before first, we need to try and find the first row.
let maybe_rowid =
self.db
.get_next_row_id_for_table(self.table_id, i64::MIN, self.tx_id);
if let Some(id) = maybe_rowid {
self.current_pos.replace(CursorPosition::Loaded(id));
self.current_pos.replace(CursorPosition::Loaded {
row_id: id,
in_btree: false,
});
Some(id.row_id)
} else {
self.current_pos.replace(CursorPosition::BeforeFirst);
@@ -232,7 +256,10 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
self.invalidate_record();
let rowid = self.db.seek_rowid(bound, lower_bound, self.tx_id);
if let Some(rowid) = rowid {
self.current_pos.replace(CursorPosition::Loaded(rowid));
self.current_pos.replace(CursorPosition::Loaded {
row_id: rowid,
in_btree: false,
});
if op.eq_only() {
if rowid.row_id == row_id {
Ok(IOResult::Done(SeekResult::Found))
@@ -267,7 +294,10 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
};
let row = crate::mvcc::database::Row::new(row_id, record_buf, num_columns);
self.current_pos.replace(CursorPosition::Loaded(row.id));
self.current_pos.replace(CursorPosition::Loaded {
row_id: row.id,
in_btree: false,
});
if self.db.read(self.tx_id, row.id)?.is_some() {
self.db.update(self.tx_id, row).inspect_err(|_| {
self.current_pos.replace(CursorPosition::BeforeFirst);
@@ -320,10 +350,13 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
false
};
if exists {
self.current_pos.replace(CursorPosition::Loaded(RowID {
table_id: self.table_id,
row_id: *int_key,
}));
self.current_pos.replace(CursorPosition::Loaded {
row_id: RowID {
table_id: self.table_id,
row_id: *int_key,
},
in_btree: false,
});
}
Ok(IOResult::Done(exists))
}
@@ -345,7 +378,7 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
// If we reached the end of the table, it means we traversed the whole table therefore there must be something in the table.
// If we have loaded a row, it means there is something in the table.
match self.get_current_pos() {
CursorPosition::Loaded(_) => false,
CursorPosition::Loaded { .. } => false,
CursorPosition::BeforeFirst => true,
CursorPosition::End => true,
}
@@ -392,7 +425,10 @@ impl<Clock: LogicalClock + 'static> CursorTrait for MvccLazyCursor<Clock> {
let rowid = self.db.seek_rowid(bound, lower_bound, self.tx_id);
if let Some(rowid) = rowid {
self.current_pos.replace(CursorPosition::Loaded(rowid));
self.current_pos.replace(CursorPosition::Loaded {
row_id: rowid,
in_btree: false,
});
} else {
self.current_pos.replace(CursorPosition::End);
}