use std::collections::HashSet;
use crate::cell::Cell;
use crate::error::{Result, SqliteError};
use crate::page::{BtreePage, PageKind};
use crate::record::{self, SqliteValue};
use crate::source::Source;
use crate::Reader;
#[derive(Debug, Clone, PartialEq)]
pub struct Row {
pub rowid: Option<i64>,
pub values: Vec<SqliteValue>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TableRow {
pub rowid: i64,
pub values: Vec<SqliteValue>,
}
struct Frame {
page: u32,
next: usize,
}
pub struct TableScan<'a, S: Source> {
reader: &'a Reader<S>,
stack: Vec<Frame>,
visited: HashSet<u32>,
current: Option<BtreePage>,
last_rowid: Option<i64>,
finished: bool,
}
impl<'a, S: Source> TableScan<'a, S> {
pub(crate) fn new(reader: &'a Reader<S>, root: u32) -> Result<TableScan<'a, S>> {
let page = reader.btree_page(root)?;
if !page.kind.is_table() {
return Err(SqliteError::malformed(
root,
format!(
"page {root} is a {:?}, not a table b-tree; a without rowid table is \
read with index_scan, or with rows, which picks for itself",
page.kind
),
));
}
let mut visited = HashSet::new();
visited.insert(root);
Ok(TableScan {
reader,
stack: vec![Frame {
page: root,
next: 0,
}],
visited,
current: Some(page),
last_rowid: None,
finished: false,
})
}
fn ensure_page(&mut self, number: u32) -> Result<()> {
if !matches!(&self.current, Some(page) if page.number == number) {
self.current = Some(self.reader.btree_page(number)?);
}
Ok(())
}
fn page_in_hand(&self) -> &BtreePage {
self.current
.as_ref()
.expect("ensure_page runs before every read")
}
fn descend(&mut self, child: u32) -> Result<()> {
if !self.visited.insert(child) {
return Err(SqliteError::malformed(
child,
"the b-tree visits this page twice, so its pointers form a cycle",
));
}
let page = self.reader.btree_page(child)?;
if !page.kind.is_table() {
return Err(SqliteError::malformed(
child,
format!("a table b-tree points at a {:?}", page.kind),
));
}
self.current = Some(page);
self.stack.push(Frame {
page: child,
next: 0,
});
Ok(())
}
fn step(&mut self) -> Result<Option<TableRow>> {
let Some(frame) = self.stack.last() else {
self.finished = true;
return Ok(None);
};
let (number, next) = (frame.page, frame.next);
self.ensure_page(number)?;
let page = self.page_in_hand();
let (kind, cell_count, right_most) = (page.kind, page.cell_count(), page.right_most);
if kind == PageKind::LeafTable {
if next >= cell_count {
self.stack.pop();
return Ok(None);
}
let cell = self.reader.cell(self.page_in_hand(), next)?;
self.bump();
return match cell {
Cell::TableLeaf { rowid, payload } => {
if let Some(previous) = self.last_rowid {
if rowid <= previous {
return Err(SqliteError::malformed(
number,
format!(
"rowid {rowid} follows {previous}, so the table b-tree \
is not in key order"
),
));
}
}
self.last_rowid = Some(rowid);
Ok(Some(TableRow {
rowid,
values: record::decode(&payload, self.reader.header().text_encoding)?,
}))
}
other => Err(SqliteError::malformed(
number,
format!("a table leaf page holds {other:?}"),
)),
};
}
if next < cell_count {
let cell = self.reader.cell(self.page_in_hand(), next)?;
self.bump();
match cell {
Cell::TableInterior { child, .. } => self.descend(child)?,
other => {
return Err(SqliteError::malformed(
number,
format!("a table interior page holds {other:?}"),
))
}
}
} else if next == cell_count {
self.bump();
let child = right_most.ok_or_else(|| {
SqliteError::malformed(number, "an interior page without a right most child")
})?;
self.descend(child)?;
} else {
self.stack.pop();
}
Ok(None)
}
fn bump(&mut self) {
if let Some(frame) = self.stack.last_mut() {
frame.next += 1;
}
}
}
impl<S: Source> Iterator for TableScan<'_, S> {
type Item = Result<TableRow>;
fn next(&mut self) -> Option<Result<TableRow>> {
while !self.finished {
if self.stack.is_empty() {
self.finished = true;
return None;
}
match self.step() {
Ok(Some(row)) => return Some(Ok(row)),
Ok(None) => continue,
Err(e) => {
self.finished = true;
return Some(Err(e));
}
}
}
None
}
}
struct IndexFrame {
page: u32,
next: usize,
descended: bool,
}
pub struct IndexScan<'a, S: Source> {
reader: &'a Reader<S>,
stack: Vec<IndexFrame>,
visited: HashSet<u32>,
current: Option<BtreePage>,
finished: bool,
}
impl<'a, S: Source> IndexScan<'a, S> {
pub(crate) fn new(reader: &'a Reader<S>, root: u32) -> Result<IndexScan<'a, S>> {
let page = reader.btree_page(root)?;
if page.kind.is_table() {
return Err(SqliteError::malformed(
root,
format!("page {root} is a {:?}, not an index b-tree", page.kind),
));
}
let mut visited = HashSet::new();
visited.insert(root);
Ok(IndexScan {
reader,
stack: vec![IndexFrame {
page: root,
next: 0,
descended: false,
}],
visited,
current: Some(page),
finished: false,
})
}
fn ensure_page(&mut self, number: u32) -> Result<()> {
if !matches!(&self.current, Some(page) if page.number == number) {
self.current = Some(self.reader.btree_page(number)?);
}
Ok(())
}
fn page_in_hand(&self) -> &BtreePage {
self.current
.as_ref()
.expect("ensure_page runs before every read")
}
fn descend(&mut self, child: u32) -> Result<()> {
if !self.visited.insert(child) {
return Err(SqliteError::malformed(
child,
"the index b-tree visits this page twice, so its pointers form a cycle",
));
}
let page = self.reader.btree_page(child)?;
if page.kind.is_table() {
return Err(SqliteError::malformed(
child,
format!("an index b-tree points at a {:?}", page.kind),
));
}
self.current = Some(page);
self.stack.push(IndexFrame {
page: child,
next: 0,
descended: false,
});
Ok(())
}
fn payload_of(&mut self, number: u32, index: usize) -> Result<Vec<u8>> {
self.ensure_page(number)?;
match self.reader.cell(self.page_in_hand(), index)? {
Cell::IndexLeaf { payload } | Cell::IndexInterior { payload, .. } => Ok(payload),
other => Err(SqliteError::malformed(
number,
format!("an index page holds {other:?}"),
)),
}
}
fn step(&mut self) -> Result<Option<Vec<SqliteValue>>> {
let Some(frame) = self.stack.last() else {
self.finished = true;
return Ok(None);
};
let (number, next, descended) = (frame.page, frame.next, frame.descended);
self.ensure_page(number)?;
let page = self.page_in_hand();
let (kind, cell_count, right_most) = (page.kind, page.cell_count(), page.right_most);
if kind == PageKind::LeafIndex {
if next >= cell_count {
self.stack.pop();
return Ok(None);
}
let payload = self.payload_of(number, next)?;
if let Some(frame) = self.stack.last_mut() {
frame.next += 1;
}
return Ok(Some(record::decode(
&payload,
self.reader.header().text_encoding,
)?));
}
if !descended {
if let Some(frame) = self.stack.last_mut() {
frame.descended = true;
}
let child = if next < cell_count {
self.ensure_page(number)?;
match self.reader.cell(self.page_in_hand(), next)? {
Cell::IndexInterior { child, .. } => child,
other => {
return Err(SqliteError::malformed(
number,
format!("an index interior page holds {other:?}"),
))
}
}
} else {
right_most.ok_or_else(|| {
SqliteError::malformed(number, "an interior page without a right most child")
})?
};
self.descend(child)?;
return Ok(None);
}
if next < cell_count {
let payload = self.payload_of(number, next)?;
if let Some(frame) = self.stack.last_mut() {
frame.next += 1;
frame.descended = false;
}
return Ok(Some(record::decode(
&payload,
self.reader.header().text_encoding,
)?));
}
self.stack.pop();
Ok(None)
}
}
impl<S: Source> Iterator for IndexScan<'_, S> {
type Item = Result<Vec<SqliteValue>>;
fn next(&mut self) -> Option<Result<Vec<SqliteValue>>> {
while !self.finished {
if self.stack.is_empty() {
self.finished = true;
return None;
}
match self.step() {
Ok(Some(values)) => return Some(Ok(values)),
Ok(None) => continue,
Err(e) => {
self.finished = true;
return Some(Err(e));
}
}
}
None
}
}
pub enum Rows<'a, S: Source> {
Rowid(TableScan<'a, S>),
Keyed(IndexScan<'a, S>),
}
impl<S: Source> Iterator for Rows<'_, S> {
type Item = Result<Row>;
fn next(&mut self) -> Option<Result<Row>> {
match self {
Rows::Rowid(scan) => scan.next().map(|row| {
row.map(|row| Row {
rowid: Some(row.rowid),
values: row.values,
})
}),
Rows::Keyed(scan) => scan.next().map(|values| {
values.map(|values| Row {
rowid: None,
values,
})
}),
}
}
}