From 74baede69245ad9ec9f344dd611b46d83e148271 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Sun, 30 Oct 2022 11:38:23 +0100 Subject: [PATCH] writes the database file, needs bugfixing --- README.md | 22 ++++++++++++++++++- src/builder.rs | 14 ++++++++---- src/database.rs | 57 +++++++++++++++++++++++++++++++++++++++++++------ src/page.rs | 20 +++++++++++------ 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f6793a8..0907e5d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,24 @@ # Sqlighte.Rs * rust version of https://gitlab.com/sander-hautvast/sqlighter -* still very much work in progress \ No newline at end of file +* still work in progress + +Creating a database will be as simple as: +```rust +fn test_build() -> Result<(), Error> { + let mut builder = Builder::new(); + builder.schema( + "foo", + "create table foo(bar varchar(10))", + ); + let mut record = Record::new(1); + record.add_value(values::string("helloworld")); + builder.add_record(record); + + let database: Database = builder.into(); + let file = File::create("foo.db")?; + let writer = BufWriter::new(file); + write(database, writer)?; + Ok(()) + } +``` \ No newline at end of file diff --git a/src/builder.rs b/src/builder.rs index bc09d60..f407dfe 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -10,11 +10,16 @@ pub struct Builder { schema: Option, } +fn new_page() -> Page { + let mut page = Page::new_leaf(); + page.fw_position = 8; + page +} impl Builder { pub fn new() -> Self { Self { - current_page: Page::new_leaf(), + current_page: new_page(), n_records_on_current_page: 0, leaf_pages: Vec::new(), schema: None, @@ -24,7 +29,7 @@ impl Builder { pub fn add_record(&mut self, record: Record) { if self.current_page_is_full(&record) { self.finish_current_page(); - self.leaf_pages.push(mem::replace(&mut self.current_page, Page::new_leaf())); + self.leaf_pages.push(mem::replace(&mut self.current_page, new_page())); self.n_records_on_current_page = 0; } @@ -61,6 +66,7 @@ impl Into for Builder { self.current_page.put_u16(self.current_page.bw_position - 1); } + self.leaf_pages.push(self.current_page); Database::new(self.schema.unwrap(), self.leaf_pages) //panics is schema is not set } } @@ -74,7 +80,7 @@ mod tests { use super::*; #[test] - fn test_build() -> Result<(), Error>{ + fn test_build() -> Result<(), Error> { let mut builder = Builder::new(); builder.schema( "foo", @@ -85,7 +91,7 @@ mod tests { builder.add_record(record); let database: Database = builder.into(); - let file = File::create("foo.txt")?; + let file = File::create("foo.db")?; let writer = BufWriter::new(file); write(database, writer)?; Ok(()) diff --git a/src/database.rs b/src/database.rs index 8d12da1..10eb124 100644 --- a/src/database.rs +++ b/src/database.rs @@ -2,7 +2,7 @@ use std::io::{BufWriter, Error, Write}; use std::mem; use crate::varint; use crate::page; -use crate::page::Page; +use crate::page::{Page, PageType}; use crate::record::Record; use crate::values; @@ -53,17 +53,60 @@ impl Into for SchemaRecord { pub fn write(database: Database, mut writer: BufWriter) -> Result<(), Error> { let mut current_top_layer = database.leaf_pages; let mut n_pages = current_top_layer.len(); - while current_top_layer.len() > 1 { // interior page needed? + while current_top_layer.len() > 1 { // db needs interior pages? current_top_layer = create_interior_pages(current_top_layer); n_pages += current_top_layer.len(); } - let table_root_page = current_top_layer.get(0); // + let table_root_page = current_top_layer.get_mut(0).unwrap(); writer.write_all(&create_header_page(n_pages + 1, database.schema).data)?; // 1 for header page - // - // recursiveAssignPagenumbers(table_root_page); // 3 extra passes... :( - // recursiveSetPageReferences(table_root_page); // don't think combining is possible - // recursiveWritePages(channel, table_root_page); + + assign_pagenumbers(table_root_page, 2); + set_page_references(table_root_page); + write_pages(&mut writer, table_root_page)?; + + Ok(()) +} + +fn assign_pagenumbers(page: &mut Page, page_counter: u32) { + page.number = page_counter; + let mut counter = page_counter; + for child in page.children.iter_mut() { + counter += 1; + assign_pagenumbers(child, counter); + } +} + +fn set_page_references(page: &mut Page) { + if let PageType::Interior = page.page_type { + page.fw_position = page::POSITION_CELL_COUNT; + page.put_u16((page.children.len() - 1) as u16); + + page.fw_position = page::POSITION_RIGHTMOST_POINTER_LEAFPAGES; + page.put_u32(page.get_page_nr_last_child()); + + let index = 0; + let before_last = page.children.len() - 1; + let child_numbers: Vec = page.children.iter().map(|child| child.number).collect(); + + for child_page_number in child_numbers { + if index < before_last { + page.fw_position = page::START_OF_INTERIOR_PAGE + (index as u16) * 2; + page.fw_position = page.get_u16(); + page.put_u32(child_page_number); + } + } + } + for child in page.children.iter_mut() { + set_page_references(child); + } +} + +fn write_pages(writer: &mut BufWriter, page: &Page) -> Result<(), Error> { + writer.write(&page.data)?; + for child in page.children.iter() { + write_pages(writer, child)?; + } Ok(()) } diff --git a/src/page.rs b/src/page.rs index d3626c8..a7b991e 100644 --- a/src/page.rs +++ b/src/page.rs @@ -4,12 +4,13 @@ use crate::database; pub const POSITION_CELL_COUNT: u16 = 3; pub const START_OF_CONTENT_AREA: u16 = 5; pub const START_OF_INTERIOR_PAGE: u16 = 12; +pub const POSITION_RIGHTMOST_POINTER_LEAFPAGES: u16 = 8; pub enum PageType { Leaf, Interior, Root, - Other + Other, } /// Represents an SQLite page @@ -18,9 +19,9 @@ pub struct Page { pub fw_position: u16, pub bw_position: u16, pub key: u64, - children: Vec, - number: u32, - page_type: PageType, + pub children: Vec, + pub number: u32, + pub page_type: PageType, } impl Page { @@ -36,7 +37,7 @@ impl Page { } } - fn default(size: usize) -> Self{ + fn default(size: usize) -> Self { Self { data: vec![0; size], fw_position: 0, @@ -81,6 +82,7 @@ impl Page { self.data[self.bw_position as usize] = *v; self.bw_position += 1; } + self.bw_position -= bytes.len() as u16; } pub fn put_u8(&mut self, value: u8) { @@ -108,9 +110,15 @@ impl Page { } // may panic - pub fn get_page_nr_last_child(self) -> u32 { + pub fn get_page_nr_last_child(&self) -> u32 { self.children[self.children.len() - 1].number } + + pub fn get_u16(&self) -> u16 { + let position = self.fw_position as usize; + (self.data[position] as u16) << 8 + self.data[position + 1] + // does not increase the fw pointerr + } } fn u16_to_bytes(value: u16) -> [u8; 2] {