diff --git a/src/builder.rs b/src/builder.rs index 6cb626c..bc09d60 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,7 +3,7 @@ use crate::database::{Database, SchemaRecord}; use crate::page::{self, Page}; use crate::record::Record; -struct Builder { +pub struct Builder { current_page: Page, n_records_on_current_page: u16, leaf_pages: Vec, @@ -21,7 +21,7 @@ impl Builder { } } - pub fn add_record(&mut self, mut record: Record) { + 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())); @@ -35,21 +35,8 @@ impl Builder { self.n_records_on_current_page += 1; } - pub fn schema(&mut self, schema: SchemaRecord) { - self.schema = Some(schema); - } - - pub fn build(mut self) -> Database { - self.current_page.fw_position = page::POSITION_CELL_COUNT; - self.current_page.put_u16(self.n_records_on_current_page); - - if self.n_records_on_current_page > 0 { - self.current_page.put_u16(self.current_page.bw_position); - } else { - self.current_page.put_u16(self.current_page.bw_position - 1); - } - - Database::new(self.schema.unwrap(), self.leaf_pages) //panics is schema is not set + pub fn schema(&mut self, table_name: &str, sql: &str) { + self.schema = Some(SchemaRecord::new(1, table_name, 2, sql)); } fn current_page_is_full(&self, record: &Record) -> bool { @@ -63,3 +50,44 @@ impl Builder { } } +impl Into for Builder { + fn into(mut self) -> Database { + self.current_page.fw_position = page::POSITION_CELL_COUNT; + self.current_page.put_u16(self.n_records_on_current_page); + + if self.n_records_on_current_page > 0 { + self.current_page.put_u16(self.current_page.bw_position); + } else { + self.current_page.put_u16(self.current_page.bw_position - 1); + } + + Database::new(self.schema.unwrap(), self.leaf_pages) //panics is schema is not set + } +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::io::{BufWriter, Error}; + use crate::database::write; + use crate::values; + use super::*; + + #[test] + 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.txt")?; + let writer = BufWriter::new(file); + write(database, writer)?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/database.rs b/src/database.rs index aae0e0a..8d12da1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,8 +1,10 @@ -use std::io::{BufWriter, Write}; +use std::io::{BufWriter, Error, Write}; use std::mem; use crate::varint; use crate::page; use crate::page::Page; +use crate::record::Record; +use crate::values; pub struct Database { schema: SchemaRecord, @@ -18,9 +20,37 @@ impl Database { } } -pub struct SchemaRecord {} +pub struct SchemaRecord { + rowid: u64, + table_name: String, + root_page: u32, + sql: String, +} -pub fn write(database: Database, writer: BufWriter) { +impl SchemaRecord { + pub fn new(rowid: u64, table_name: &str, root_page: u32, sql: &str) -> Self { + Self { + rowid, + table_name: table_name.to_owned(), + root_page, + sql: sql.to_owned(), + } + } +} + +impl Into for SchemaRecord { + fn into(self) -> Record { + let mut record = Record::new(self.rowid); + record.add_value(values::string("table")); + record.add_value(values::string(&self.table_name.to_ascii_lowercase())); + record.add_value(values::string(&self.table_name.to_ascii_lowercase())); + record.add_value(values::integer(self.root_page as i64)); + record.add_value(values::string(&self.sql)); + record + } +} + +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? @@ -29,11 +59,33 @@ pub fn write(database: Database, writer: BufWriter) { } let table_root_page = current_top_layer.get(0); // - // writeFromStart(writer, createHeaderPage(n_pages + 1)); // 1 for header page + 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); + Ok(()) +} + +fn create_header_page(n_pages: usize, schema: SchemaRecord) -> Page { + let mut header_page = Page::new_root(); + write_header(&mut header_page, n_pages as u32); + + let payload_location_write_location = header_page.fw_position; // mark current position + + let payload_location = write_schema(&mut header_page, schema); //write schema payload from the end + header_page.fw_position = payload_location_write_location; // go back to marked position + header_page.put_u16(payload_location); //payload start + header_page.put_u8(0); // the number of fragmented free bytes within the cell content area + header_page.put_u16(payload_location); // first cell + return header_page; +} + +fn write_schema(root_page: &mut Page, schema_record: SchemaRecord) -> u16 { + let record: Record = schema_record.into(); + let bytes: Vec = record.into(); + root_page.put_bytes_bw(&bytes); + root_page.bw_position } fn create_interior_pages(mut child_pages: Vec) -> Vec { @@ -61,6 +113,7 @@ fn create_interior_pages(mut child_pages: Vec) -> Vec { } else { last_leaf = leaf_page; } + child_count += 1; } interior_page.fw_position = page::START_OF_CONTENT_AREA; @@ -78,7 +131,7 @@ fn create_cell(page: &mut Page) { page.put_u16(page.bw_position); } -fn write_header(mut rootpage: Page, n_pages: u32) { +fn write_header(rootpage: &mut Page, n_pages: u32) { rootpage.put_bytes(&MAGIC_HEADER); rootpage.put_u16(DEFAULT_PAGE_SIZE); rootpage.put_u8(FILE_FORMAT_WRITE_VERSION); @@ -133,4 +186,6 @@ const NO_FREE_BLOCKS: u16 = 0; pub const TABLE_LEAF_PAGE: u8 = 0x0d; pub const TABLE_INTERIOR_PAGE: u8 = 0x05; const INDEX_LEAF_PAGE: u8 = 0x0a; -const INDEX_INTERIOR_PAGE: u8 = 0x02; \ No newline at end of file +const INDEX_INTERIOR_PAGE: u8 = 0x02; + + diff --git a/src/page.rs b/src/page.rs index b94cc4e..d3626c8 100644 --- a/src/page.rs +++ b/src/page.rs @@ -48,6 +48,10 @@ impl Page { } } + pub fn new_root() -> Self { + Page::with_capacity(database::DEFAULT_PAGE_SIZE, PageType::Leaf) + } + pub fn new_leaf() -> Self { let mut page = Page::with_capacity(database::DEFAULT_PAGE_SIZE, PageType::Leaf); page.put_u8(database::TABLE_LEAF_PAGE); diff --git a/src/record.rs b/src/record.rs index ce47b05..d324de9 100644 --- a/src/record.rs +++ b/src/record.rs @@ -8,14 +8,14 @@ pub struct Record { } impl Record { - fn new(rowid: u64) -> Self { + pub fn new(rowid: u64) -> Self { Self { rowid, values: vec![], } } - fn add_value(&mut self, value: Value) { + pub fn add_value(&mut self, value: Value) { self.values.push(value); }