mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-28 13:34:24 +01:00
next rowid was being tracked globally for all tables and restarted to 0 every time database was opened
155 lines
5.3 KiB
Rust
155 lines
5.3 KiB
Rust
use crate::mvcc::clock::LogicalClock;
|
|
use crate::mvcc::database::{MvStore, Row, RowID};
|
|
use crate::Pager;
|
|
use crate::Result;
|
|
use std::fmt::Debug;
|
|
use std::rc::Rc;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
enum CursorPosition {
|
|
/// We haven't loaded any row yet.
|
|
BeforeFirst,
|
|
/// We have loaded a row.
|
|
Loaded(RowID),
|
|
/// We have reached the end of the table.
|
|
End,
|
|
}
|
|
#[derive(Debug)]
|
|
pub struct MvccLazyCursor<Clock: LogicalClock> {
|
|
pub db: Arc<MvStore<Clock>>,
|
|
current_pos: CursorPosition,
|
|
table_id: u64,
|
|
tx_id: u64,
|
|
}
|
|
|
|
impl<Clock: LogicalClock> MvccLazyCursor<Clock> {
|
|
pub fn new(
|
|
db: Arc<MvStore<Clock>>,
|
|
tx_id: u64,
|
|
table_id: u64,
|
|
pager: Rc<Pager>,
|
|
) -> Result<MvccLazyCursor<Clock>> {
|
|
db.maybe_initialize_table(table_id, pager)?;
|
|
let cursor = Self {
|
|
db,
|
|
tx_id,
|
|
current_pos: CursorPosition::BeforeFirst,
|
|
table_id,
|
|
};
|
|
Ok(cursor)
|
|
}
|
|
|
|
/// Insert a row into the table.
|
|
/// Sets the cursor to the inserted row.
|
|
pub fn insert(&mut self, row: Row) -> Result<()> {
|
|
self.current_pos = CursorPosition::Loaded(row.id);
|
|
self.db.insert(self.tx_id, row).inspect_err(|_| {
|
|
self.current_pos = CursorPosition::BeforeFirst;
|
|
})?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn current_row_id(&mut self) -> Option<RowID> {
|
|
match self.current_pos {
|
|
CursorPosition::Loaded(id) => Some(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);
|
|
if let Some(id) = maybe_rowid {
|
|
self.current_pos = CursorPosition::Loaded(id);
|
|
Some(id)
|
|
} else {
|
|
self.current_pos = CursorPosition::BeforeFirst;
|
|
None
|
|
}
|
|
}
|
|
CursorPosition::End => None,
|
|
}
|
|
}
|
|
|
|
pub fn current_row(&mut self) -> Result<Option<Row>> {
|
|
match self.current_pos {
|
|
CursorPosition::Loaded(id) => self.db.read(self.tx_id, 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);
|
|
if let Some(id) = maybe_rowid {
|
|
self.current_pos = CursorPosition::Loaded(id);
|
|
self.db.read(self.tx_id, id)
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
}
|
|
CursorPosition::End => Ok(None),
|
|
}
|
|
}
|
|
|
|
pub fn close(self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
/// Move the cursor to the next row. Returns true if the cursor moved to the next row, false if the cursor is at the end of the table.
|
|
pub fn forward(&mut self) -> bool {
|
|
let before_first = matches!(self.current_pos, CursorPosition::BeforeFirst);
|
|
let min_id = match self.current_pos {
|
|
CursorPosition::Loaded(id) => 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 => {
|
|
// let's keep same state, we reached the end so no point in moving forward.
|
|
return false;
|
|
}
|
|
};
|
|
self.current_pos = match self.db.get_next_row_id_for_table(self.table_id, min_id) {
|
|
Some(id) => CursorPosition::Loaded(id),
|
|
None => {
|
|
if before_first {
|
|
// if it wasn't loaded and we didn't find anything, it means the table is empty.
|
|
CursorPosition::BeforeFirst
|
|
} else {
|
|
// if we had something loaded, and we didn't find next key then it means we are at the end.
|
|
CursorPosition::End
|
|
}
|
|
}
|
|
};
|
|
matches!(self.current_pos, CursorPosition::Loaded(_))
|
|
}
|
|
|
|
/// Returns true if the is not pointing to any row.
|
|
pub fn is_empty(&self) -> bool {
|
|
// 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.current_pos {
|
|
CursorPosition::Loaded(_) => false,
|
|
CursorPosition::BeforeFirst => true,
|
|
CursorPosition::End => true,
|
|
}
|
|
}
|
|
|
|
pub fn rewind(&mut self) {
|
|
self.current_pos = CursorPosition::BeforeFirst;
|
|
}
|
|
|
|
pub fn last(&mut self) {
|
|
let last_rowid = self.db.get_last_rowid(self.table_id);
|
|
if let Some(last_rowid) = last_rowid {
|
|
self.current_pos = CursorPosition::Loaded(RowID {
|
|
table_id: self.table_id,
|
|
row_id: last_rowid,
|
|
});
|
|
} else {
|
|
self.current_pos = CursorPosition::BeforeFirst;
|
|
}
|
|
}
|
|
|
|
pub fn get_next_rowid(&mut self) -> i64 {
|
|
self.last();
|
|
match self.current_pos {
|
|
CursorPosition::Loaded(id) => id.row_id + 1,
|
|
CursorPosition::BeforeFirst => i64::MIN,
|
|
CursorPosition::End => i64::MAX,
|
|
}
|
|
}
|
|
}
|