Skip to main content

quantydb_sqlite/
tree.rs

1//! Walking a table b-tree.
2//!
3//! Two things about this file are defences rather than features, and both
4//! exist because the tree comes out of a file we did not write.
5//!
6//! It does not recurse. A b-tree of sensible shape is a handful of levels
7//! deep, but nothing in the file stops the pointers from forming a chain
8//! thousands of levels long, and a recursive walker meets that with a stack
9//! overflow, which aborts the process. An explicit stack meets it by
10//! running.
11//!
12//! It refuses to visit a page twice. Pointers in the file can form a cycle,
13//! by corruption or by design, and a walker that follows them faithfully
14//! never returns. Since a well formed b-tree reaches every one of its pages
15//! exactly once, remembering what has been visited costs one bit per page
16//! and turns a hang into an error.
17//!
18//! Rows come out in rowid order, because that is the order the tree is in.
19//! Nothing sorts them.
20
21use std::collections::HashSet;
22
23use crate::cell::Cell;
24use crate::error::{Result, SqliteError};
25use crate::page::{BtreePage, PageKind};
26use crate::record::{self, SqliteValue};
27use crate::source::Source;
28use crate::Reader;
29
30/// One row from a scan, whichever kind of table it came from.
31///
32/// A rowid table gives every row a rowid; a without rowid table has none,
33/// because its rows are identified by the primary key that orders them.
34#[derive(Debug, Clone, PartialEq)]
35pub struct Row {
36    pub rowid: Option<i64>,
37    pub values: Vec<SqliteValue>,
38}
39
40/// One row of a table: its rowid and the values of its columns.
41///
42/// A column declared `integer primary key` is an alias for the rowid, and
43/// SQLite stores it as NULL in the record itself. The rowid on this struct
44/// is the real value.
45#[derive(Debug, Clone, PartialEq)]
46pub struct TableRow {
47    pub rowid: i64,
48    pub values: Vec<SqliteValue>,
49}
50
51/// Where the walk currently is on one level of the tree.
52struct Frame {
53    page: u32,
54    /// Next cell to visit. On an interior page, the index one past the last
55    /// cell means the right most child.
56    next: usize,
57}
58
59/// A depth first walk of a table b-tree, yielding rows in rowid order.
60///
61/// The iterator stops after its first error. Continuing past one would mean
62/// guessing which part of a broken file is still trustworthy, and a partial
63/// import that looks complete is exactly the outcome this crate exists to
64/// avoid.
65pub struct TableScan<'a, S: Source> {
66    reader: &'a Reader<S>,
67    stack: Vec<Frame>,
68    visited: HashSet<u32>,
69    /// The page the walk is currently reading. Depth first means the next
70    /// row almost always comes from the same page as the last one, so one
71    /// slot of cache turns a page read per row into a page read per page.
72    current: Option<BtreePage>,
73    /// The last rowid handed out. A table b-tree is ordered by rowid, so a
74    /// scan of one is strictly ascending; anything else means the tree
75    /// contradicts its own shape and the file is corrupt.
76    last_rowid: Option<i64>,
77    finished: bool,
78}
79
80impl<'a, S: Source> TableScan<'a, S> {
81    pub(crate) fn new(reader: &'a Reader<S>, root: u32) -> Result<TableScan<'a, S>> {
82        let page = reader.btree_page(root)?;
83        if !page.kind.is_table() {
84            return Err(SqliteError::malformed(
85                root,
86                format!(
87                    "page {root} is a {:?}, not a table b-tree; a without rowid table is \
88                     read with index_scan, or with rows, which picks for itself",
89                    page.kind
90                ),
91            ));
92        }
93        let mut visited = HashSet::new();
94        visited.insert(root);
95        Ok(TableScan {
96            reader,
97            stack: vec![Frame {
98                page: root,
99                next: 0,
100            }],
101            visited,
102            current: Some(page),
103            last_rowid: None,
104            finished: false,
105        })
106    }
107
108    /// Make sure `number` is the page in hand, reading it if it is not.
109    ///
110    /// This fills the cache rather than returning the page, so that reading
111    /// it afterwards borrows `self` immutably and leaves the reader
112    /// reachable in the same expression.
113    fn ensure_page(&mut self, number: u32) -> Result<()> {
114        if !matches!(&self.current, Some(page) if page.number == number) {
115            self.current = Some(self.reader.btree_page(number)?);
116        }
117        Ok(())
118    }
119
120    fn page_in_hand(&self) -> &BtreePage {
121        self.current
122            .as_ref()
123            .expect("ensure_page runs before every read")
124    }
125
126    fn descend(&mut self, child: u32) -> Result<()> {
127        if !self.visited.insert(child) {
128            return Err(SqliteError::malformed(
129                child,
130                "the b-tree visits this page twice, so its pointers form a cycle",
131            ));
132        }
133        let page = self.reader.btree_page(child)?;
134        if !page.kind.is_table() {
135            return Err(SqliteError::malformed(
136                child,
137                format!("a table b-tree points at a {:?}", page.kind),
138            ));
139        }
140        self.current = Some(page);
141        self.stack.push(Frame {
142            page: child,
143            next: 0,
144        });
145        Ok(())
146    }
147
148    /// One step of the walk: a row, a descent, or the end of a page.
149    fn step(&mut self) -> Result<Option<TableRow>> {
150        let Some(frame) = self.stack.last() else {
151            self.finished = true;
152            return Ok(None);
153        };
154        let (number, next) = (frame.page, frame.next);
155        self.ensure_page(number)?;
156        let page = self.page_in_hand();
157        let (kind, cell_count, right_most) = (page.kind, page.cell_count(), page.right_most);
158
159        if kind == PageKind::LeafTable {
160            if next >= cell_count {
161                self.stack.pop();
162                return Ok(None);
163            }
164            let cell = self.reader.cell(self.page_in_hand(), next)?;
165            self.bump();
166            return match cell {
167                Cell::TableLeaf { rowid, payload } => {
168                    // the tree is ordered by rowid, so a scan of it is
169                    // strictly ascending. a file where it is not has a
170                    // b-tree that is not one, and passing those rows on
171                    // would mean handing a caller an order it cannot trust.
172                    if let Some(previous) = self.last_rowid {
173                        if rowid <= previous {
174                            return Err(SqliteError::malformed(
175                                number,
176                                format!(
177                                    "rowid {rowid} follows {previous}, so the table b-tree \
178                                     is not in key order"
179                                ),
180                            ));
181                        }
182                    }
183                    self.last_rowid = Some(rowid);
184                    Ok(Some(TableRow {
185                        rowid,
186                        values: record::decode(&payload, self.reader.header().text_encoding)?,
187                    }))
188                }
189                other => Err(SqliteError::malformed(
190                    number,
191                    format!("a table leaf page holds {other:?}"),
192                )),
193            };
194        }
195
196        // interior page: visit every child, then the right most one
197        if next < cell_count {
198            let cell = self.reader.cell(self.page_in_hand(), next)?;
199            self.bump();
200            match cell {
201                Cell::TableInterior { child, .. } => self.descend(child)?,
202                other => {
203                    return Err(SqliteError::malformed(
204                        number,
205                        format!("a table interior page holds {other:?}"),
206                    ))
207                }
208            }
209        } else if next == cell_count {
210            self.bump();
211            let child = right_most.ok_or_else(|| {
212                SqliteError::malformed(number, "an interior page without a right most child")
213            })?;
214            self.descend(child)?;
215        } else {
216            self.stack.pop();
217        }
218        Ok(None)
219    }
220
221    fn bump(&mut self) {
222        if let Some(frame) = self.stack.last_mut() {
223            frame.next += 1;
224        }
225    }
226}
227
228impl<S: Source> Iterator for TableScan<'_, S> {
229    type Item = Result<TableRow>;
230
231    fn next(&mut self) -> Option<Result<TableRow>> {
232        while !self.finished {
233            if self.stack.is_empty() {
234                self.finished = true;
235                return None;
236            }
237            match self.step() {
238                Ok(Some(row)) => return Some(Ok(row)),
239                Ok(None) => continue,
240                Err(e) => {
241                    self.finished = true;
242                    return Some(Err(e));
243                }
244            }
245        }
246        None
247    }
248}
249
250/// Where the walk is on one level of an index b-tree.
251///
252/// An index b-tree keeps entries on its interior pages as well as its
253/// leaves, so a scan of one is an in-order walk: descend into the child
254/// left of a cell, emit that cell, then move right. `descended` is what
255/// tells those two halves of a visit apart.
256struct IndexFrame {
257    page: u32,
258    next: usize,
259    descended: bool,
260}
261
262/// A walk of an index b-tree, yielding its entries in key order.
263///
264/// This is how a without rowid table is read: such a table has no table
265/// b-tree at all, it *is* an index b-tree keyed by its primary key, and
266/// every entry holds the whole row.
267///
268/// The defences are the ones the table walk has, for the same reasons: an
269/// explicit stack so a deep tree cannot overflow ours, and a visited set so
270/// a cycle ends in an error rather than a hang.
271pub struct IndexScan<'a, S: Source> {
272    reader: &'a Reader<S>,
273    stack: Vec<IndexFrame>,
274    visited: HashSet<u32>,
275    current: Option<BtreePage>,
276    finished: bool,
277}
278
279impl<'a, S: Source> IndexScan<'a, S> {
280    pub(crate) fn new(reader: &'a Reader<S>, root: u32) -> Result<IndexScan<'a, S>> {
281        let page = reader.btree_page(root)?;
282        if page.kind.is_table() {
283            return Err(SqliteError::malformed(
284                root,
285                format!("page {root} is a {:?}, not an index b-tree", page.kind),
286            ));
287        }
288        let mut visited = HashSet::new();
289        visited.insert(root);
290        Ok(IndexScan {
291            reader,
292            stack: vec![IndexFrame {
293                page: root,
294                next: 0,
295                descended: false,
296            }],
297            visited,
298            current: Some(page),
299            finished: false,
300        })
301    }
302
303    /// Same split as the table walk: fill the cache here, borrow the page
304    /// immutably afterwards, so the reader stays reachable in the same
305    /// expression.
306    fn ensure_page(&mut self, number: u32) -> Result<()> {
307        if !matches!(&self.current, Some(page) if page.number == number) {
308            self.current = Some(self.reader.btree_page(number)?);
309        }
310        Ok(())
311    }
312
313    fn page_in_hand(&self) -> &BtreePage {
314        self.current
315            .as_ref()
316            .expect("ensure_page runs before every read")
317    }
318
319    fn descend(&mut self, child: u32) -> Result<()> {
320        if !self.visited.insert(child) {
321            return Err(SqliteError::malformed(
322                child,
323                "the index b-tree visits this page twice, so its pointers form a cycle",
324            ));
325        }
326        let page = self.reader.btree_page(child)?;
327        if page.kind.is_table() {
328            return Err(SqliteError::malformed(
329                child,
330                format!("an index b-tree points at a {:?}", page.kind),
331            ));
332        }
333        self.current = Some(page);
334        self.stack.push(IndexFrame {
335            page: child,
336            next: 0,
337            descended: false,
338        });
339        Ok(())
340    }
341
342    fn payload_of(&mut self, number: u32, index: usize) -> Result<Vec<u8>> {
343        self.ensure_page(number)?;
344        match self.reader.cell(self.page_in_hand(), index)? {
345            Cell::IndexLeaf { payload } | Cell::IndexInterior { payload, .. } => Ok(payload),
346            other => Err(SqliteError::malformed(
347                number,
348                format!("an index page holds {other:?}"),
349            )),
350        }
351    }
352
353    fn step(&mut self) -> Result<Option<Vec<SqliteValue>>> {
354        let Some(frame) = self.stack.last() else {
355            self.finished = true;
356            return Ok(None);
357        };
358        let (number, next, descended) = (frame.page, frame.next, frame.descended);
359        self.ensure_page(number)?;
360        let page = self.page_in_hand();
361        let (kind, cell_count, right_most) = (page.kind, page.cell_count(), page.right_most);
362
363        if kind == PageKind::LeafIndex {
364            if next >= cell_count {
365                self.stack.pop();
366                return Ok(None);
367            }
368            let payload = self.payload_of(number, next)?;
369            if let Some(frame) = self.stack.last_mut() {
370                frame.next += 1;
371            }
372            return Ok(Some(record::decode(
373                &payload,
374                self.reader.header().text_encoding,
375            )?));
376        }
377
378        // interior: left child, then the cell, then on to the right
379        if !descended {
380            if let Some(frame) = self.stack.last_mut() {
381                frame.descended = true;
382            }
383            let child = if next < cell_count {
384                self.ensure_page(number)?;
385                match self.reader.cell(self.page_in_hand(), next)? {
386                    Cell::IndexInterior { child, .. } => child,
387                    other => {
388                        return Err(SqliteError::malformed(
389                            number,
390                            format!("an index interior page holds {other:?}"),
391                        ))
392                    }
393                }
394            } else {
395                right_most.ok_or_else(|| {
396                    SqliteError::malformed(number, "an interior page without a right most child")
397                })?
398            };
399            self.descend(child)?;
400            return Ok(None);
401        }
402
403        if next < cell_count {
404            let payload = self.payload_of(number, next)?;
405            if let Some(frame) = self.stack.last_mut() {
406                frame.next += 1;
407                frame.descended = false;
408            }
409            return Ok(Some(record::decode(
410                &payload,
411                self.reader.header().text_encoding,
412            )?));
413        }
414
415        self.stack.pop();
416        Ok(None)
417    }
418}
419
420impl<S: Source> Iterator for IndexScan<'_, S> {
421    type Item = Result<Vec<SqliteValue>>;
422
423    fn next(&mut self) -> Option<Result<Vec<SqliteValue>>> {
424        while !self.finished {
425            if self.stack.is_empty() {
426                self.finished = true;
427                return None;
428            }
429            match self.step() {
430                Ok(Some(values)) => return Some(Ok(values)),
431                Ok(None) => continue,
432                Err(e) => {
433                    self.finished = true;
434                    return Some(Err(e));
435                }
436            }
437        }
438        None
439    }
440}
441
442/// A scan of either kind of table, yielding rows the same way.
443pub enum Rows<'a, S: Source> {
444    Rowid(TableScan<'a, S>),
445    Keyed(IndexScan<'a, S>),
446}
447
448impl<S: Source> Iterator for Rows<'_, S> {
449    type Item = Result<Row>;
450
451    fn next(&mut self) -> Option<Result<Row>> {
452        match self {
453            Rows::Rowid(scan) => scan.next().map(|row| {
454                row.map(|row| Row {
455                    rowid: Some(row.rowid),
456                    values: row.values,
457                })
458            }),
459            Rows::Keyed(scan) => scan.next().map(|values| {
460                values.map(|values| Row {
461                    rowid: None,
462                    values,
463                })
464            }),
465        }
466    }
467}