matdb 0.1.0

An experimental embedded SQL-like DBMS
Documentation
use std::os::unix::fs::MetadataExt;

use crate::{value::Value, Db, PAGE_SIZE};

mod cache;
mod cell;
mod rangeiter;
use anyhow::{bail, Context, Result};
use encoding::{decode_page, Page};
pub use rangeiter::*;
use write_op::{write_op, BTreeWriteOp};
mod append;
mod encoding;
mod free;
mod tx;
mod write_op;

impl Db {
    pub(super) fn recover(&mut self) -> Result<()> {
        self.cached_pages.clear();
        self.write_pages.clear();
        self.free_list.clear();

        (
            self.main_tree_root,
            self.this_tx_id,
            self.next_freelist_ptr,
            self.previous_main_root,
            self.page_count,
        ) = if self.file.is_some()
            && self.file.as_ref().unwrap().metadata()?.size() / PAGE_SIZE as u64 > 0
        {
            let wpc = self.file.as_ref().unwrap().metadata()?.size() / PAGE_SIZE as u64;
            let mut range = (0..wpc).rev();
            loop {
                let page_idx = range.next().unwrap();

                match decode_page(&self.get_page(page_idx)?)? {
                    Page::Footer(root, tx_id, next_freelist, free_pages) => {
                        self.free_list.extend(free_pages);
                        self.file
                            .as_mut()
                            .unwrap()
                            .set_len((page_idx + 1) * PAGE_SIZE as u64)
                            .context("failed setting database file size after recovering")?;
                        break (root, tx_id + 1, next_freelist, Some(root), wpc);
                    }
                    _ => {}
                };

                if page_idx == 0 {
                    bail!("failed to find a valid footer page in database file")
                }
            }
        } else {
            self.add_header()?;
            (
                self.append_leaf(Vec::new())?,
                1,
                None,
                None,
                self.page_count,
            )
        };

        self.has_recovered = true;

        Ok(())
    }

    pub(super) fn tree_root(&self) -> u64 {
        self.main_tree_root
    }

    pub(super) fn new_tree(&mut self) -> Result<u64> {
        self.append_leaf(Vec::new())
    }

    pub(super) fn delete_tree(&mut self, tree: u64) -> Result<()> {
        write_op(self, tree, BTreeWriteOp::DeleteTree)?;
        Ok(())
    }

    pub(super) fn set_value(
        &mut self,
        tree: u64,
        key: Vec<Value>,
        value: Vec<Value>,
    ) -> Result<Option<u64>> {
        write_op(self, tree, BTreeWriteOp::Set(key, value))
    }

    pub(super) fn set_value_root(&mut self, key: Vec<Value>, value: Vec<Value>) -> Result<()> {
        if let Some(new_root) = write_op(self, self.main_tree_root, BTreeWriteOp::Set(key, value))?
        {
            self.main_tree_root = new_root;
        }

        Ok(())
    }

    pub(super) fn delete_pair(&mut self, tree: u64, key: Vec<Value>) -> Result<Option<u64>> {
        write_op(self, tree, BTreeWriteOp::Delete(key))
    }

    pub(super) fn delete_pair_root(&mut self, key: Vec<Value>) -> Result<()> {
        if let Some(new_root) = write_op(self, self.main_tree_root, BTreeWriteOp::Delete(key))? {
            self.main_tree_root = new_root;
        }

        Ok(())
    }
}