From 53c348402a23807e4feda669b1698f618ffc56a0 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 18 Jun 2024 19:51:47 +0200 Subject: [PATCH] core: parse unary and write to disk --- core/lib.rs | 11 ++++-- core/pager.rs | 9 ++++- core/sqlite3_ondisk.rs | 78 +++++++++++++++++++++++++++++++++++++++--- core/storage.rs | 44 ++++++++++++++++++++++-- core/translate.rs | 22 +++++++----- 5 files changed, 146 insertions(+), 18 deletions(-) diff --git a/core/lib.rs b/core/lib.rs index ceda27f3a..d7f6f7d59 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -46,7 +46,11 @@ impl Database { pub fn open(io: Rc, page_source: PageSource) -> Result { let db_header = Pager::begin_open(&page_source)?; io.run_once()?; - let pager = Rc::new(Pager::finish_open(db_header.clone(), page_source)?); + let pager = Rc::new(Pager::finish_open( + db_header.clone(), + page_source, + io.clone(), + )?); let bootstrap_schema = Rc::new(Schema::new()); let conn = Connection { pager: pager.clone(), @@ -128,13 +132,14 @@ impl Connection { pub fn update_pragma(&self, name: &String, value: i64) { match name.as_str() { "cache_size" => { - // update in disk - // update in-memory header self.header.borrow_mut().default_cache_size = value .try_into() .expect(&format!("invalid value, too big for a i32 {}", value)); + // update in disk + self.pager.write_database_header(&self.header.borrow()); + // update cache size } _ => todo!(), diff --git a/core/pager.rs b/core/pager.rs index 3665eba67..a30206ec5 100644 --- a/core/pager.rs +++ b/core/pager.rs @@ -76,9 +76,10 @@ impl Page { /// to pages of the database file, including caching, concurrency control, and /// transaction management. pub struct Pager { - page_source: PageSource, + pub page_source: PageSource, page_cache: RefCell>>, buffer_pool: Rc, + pub io: Rc, } impl Pager { @@ -89,6 +90,7 @@ impl Pager { pub fn finish_open( db_header: Rc>, page_source: PageSource, + io: Rc, ) -> anyhow::Result { let db_header = db_header.borrow(); let page_size = db_header.page_size as usize; @@ -98,6 +100,7 @@ impl Pager { page_source, buffer_pool, page_cache, + io, }) } @@ -119,4 +122,8 @@ impl Pager { page_cache.insert(page_idx, page.clone()); Ok(page) } + + pub fn write_database_header(&self, header: &DatabaseHeader) { + sqlite3_ondisk::begin_write_database_header(header, self).expect("failed to write header"); + } } diff --git a/core/sqlite3_ondisk.rs b/core/sqlite3_ondisk.rs index 7950d36c2..8dff25bea 100644 --- a/core/sqlite3_ondisk.rs +++ b/core/sqlite3_ondisk.rs @@ -24,8 +24,8 @@ /// /// For more information, see: https://www.sqlite.org/fileformat.html use crate::buffer_pool::BufferPool; -use crate::io::{Buffer, Completion}; -use crate::pager::Page; +use crate::io::{Buffer, Completion, WriteCompletion}; +use crate::pager::{Page, Pager}; use crate::types::{OwnedRecord, OwnedValue}; use crate::PageSource; use anyhow::{anyhow, Result}; @@ -37,7 +37,7 @@ use std::rc::Rc; pub const DATABASE_HEADER_SIZE: usize = 100; const DEFAULT_CACHE_SIZE: i32 = -2000; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct DatabaseHeader { magic: [u8; 16], pub page_size: u16, @@ -80,7 +80,7 @@ pub fn begin_read_database_header(page_source: &PageSource) -> Result>) -> Result<()> { let buf = buf.as_slice(); - let mut header = header.borrow_mut(); + let mut header = std::cell::RefCell::borrow_mut(&header); header.magic.copy_from_slice(&buf[0..16]); header.page_size = u16::from_be_bytes([buf[16], buf[17]]); header.write_version = buf[18]; @@ -110,6 +110,76 @@ fn finish_read_database_header(buf: &Buffer, header: Rc> Ok(()) } +pub fn begin_write_database_header(header: &DatabaseHeader, pager: &Pager) -> Result<()> { + let header = Rc::new(header.clone()); + let page_source = Rc::new(pager.page_source.clone()); + + let drop_fn = Rc::new(|_buf| {}); + let buffer_to_copy = Rc::new(RefCell::new(Buffer::allocate(512, drop_fn))); + let buffer_to_copy_in_cb = buffer_to_copy.clone(); + + let header_cb = header.clone(); + let complete = Box::new(move |buffer: &Buffer| { + let header = header_cb.clone(); + let buffer: Buffer = buffer.clone(); + let buffer = Rc::new(RefCell::new(buffer)); + { + let mut buf_mut = std::cell::RefCell::borrow_mut(&buffer); + let buf = buf_mut.as_mut_slice(); + buf[0..16].copy_from_slice(&header.magic); + buf[16..18].copy_from_slice(&header.page_size.to_be_bytes()); + buf[18] = header.write_version; + buf[19] = header.read_version; + buf[20] = header.unused_space; + buf[21] = header.max_embed_frac; + buf[22] = header.min_embed_frac; + buf[23] = header.min_leaf_frac; + buf[24..28].copy_from_slice(&header.change_counter.to_be_bytes()); + buf[28..32].copy_from_slice(&header.database_size.to_be_bytes()); + buf[32..36].copy_from_slice(&header.freelist_trunk_page.to_be_bytes()); + buf[36..40].copy_from_slice(&header.freelist_pages.to_be_bytes()); + buf[40..44].copy_from_slice(&header.schema_cookie.to_be_bytes()); + buf[44..48].copy_from_slice(&header.schema_format.to_be_bytes()); + buf[48..52].copy_from_slice(&header.default_cache_size.to_be_bytes()); + + buf[52..56].copy_from_slice(&header.vacuum.to_be_bytes()); + buf[56..60].copy_from_slice(&header.text_encoding.to_be_bytes()); + buf[60..64].copy_from_slice(&header.user_version.to_be_bytes()); + buf[64..68].copy_from_slice(&header.incremental_vacuum.to_be_bytes()); + + buf[68..72].copy_from_slice(&header.application_id.to_be_bytes()); + buf[72..92].copy_from_slice(&header.reserved); + buf[92..96].copy_from_slice(&header.version_valid_for.to_be_bytes()); + buf[96..100].copy_from_slice(&header.version_number.to_be_bytes()); + let mut buffer_to_copy = std::cell::RefCell::borrow_mut(&buffer_to_copy_in_cb); + let buffer_to_copy_slice = buffer_to_copy.as_mut_slice(); + + buffer_to_copy_slice.copy_from_slice(buf); + } + }); + + let drop_fn = Rc::new(|_buf| {}); + let buf = Buffer::allocate(512, drop_fn); + let c = Rc::new(Completion::new(buf.clone(), complete)); + page_source.get(1, c.clone())?; + // run get header block + pager.io.run_once()?; + + let buffer_in_cb = buffer_to_copy.clone(); + let write_complete = Box::new(move |bytes_written: usize| { + let buf = buffer_in_cb.clone(); + let buf_len = std::cell::RefCell::borrow(&buf).len(); + if bytes_written < buf_len { + log::error!("wrote({bytes_written}) less than expected({buf_len})"); + } + // finish_read_database_header(buf, header).unwrap(); + }); + let c = Rc::new(WriteCompletion::new(write_complete)); + page_source.write(0, buffer_to_copy.clone(), c).unwrap(); + + Ok(()) +} + #[derive(Debug)] pub struct BTreePageHeader { page_type: PageType, diff --git a/core/storage.rs b/core/storage.rs index 95a1b3257..a9d2fc995 100644 --- a/core/storage.rs +++ b/core/storage.rs @@ -1,13 +1,24 @@ -use crate::io::Completion; #[cfg(feature = "fs")] use crate::io::File; +use crate::{ + io::{Completion, WriteCompletion}, + Buffer, +}; use anyhow::Result; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; pub struct PageSource { io: Rc, } +impl Clone for PageSource { + fn clone(&self) -> Self { + Self { + io: self.io.clone(), + } + } +} + impl PageSource { pub fn from_io(io: Rc) -> Self { Self { io } @@ -23,10 +34,25 @@ impl PageSource { pub fn get(&self, page_idx: usize, c: Rc) -> Result<()> { self.io.get(page_idx, c) } + + pub fn write( + &self, + page_idx: usize, + buffer: Rc>, + c: Rc, + ) -> Result<()> { + self.io.write(page_idx, buffer, c) + } } pub trait PageIO { fn get(&self, page_idx: usize, c: Rc) -> Result<()>; + fn write( + &self, + page_idx: usize, + buffer: Rc>, + c: Rc, + ) -> Result<()>; } #[cfg(feature = "fs")] @@ -46,6 +72,20 @@ impl PageIO for FileStorage { self.file.pread(pos, c)?; Ok(()) } + + fn write( + &self, + page_idx: usize, + buffer: Rc>, + c: Rc, + ) -> Result<()> { + let buffer_size = buffer.borrow().len(); + assert!(buffer_size >= 512); + assert!(buffer_size <= 65536); + assert!((buffer_size & (buffer_size - 1)) == 0); + self.file.pwrite(page_idx, buffer, c)?; + Ok(()) + } } #[cfg(feature = "fs")] diff --git a/core/translate.rs b/core/translate.rs index 5f4aab6c4..7bcfa76de 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -1,10 +1,10 @@ -use std::borrow::Borrow; - use crate::schema::Schema; use crate::sqlite3_ondisk::DatabaseHeader; use crate::vdbe::{Insn, Program, ProgramBuilder}; use anyhow::Result; -use sqlite3_parser::ast::{Expr, Literal, OneSelect, PragmaBody, QualifiedName, Select, Stmt}; +use sqlite3_parser::ast::{ + Expr, Literal, OneSelect, PragmaBody, QualifiedName, Select, Stmt, UnaryOperator, +}; /// Translate SQL statement into bytecode program. pub fn translate(schema: &Schema, stmt: Stmt, database_header: &DatabaseHeader) -> Result { @@ -248,11 +248,17 @@ fn translate_pragma( (false, 0) } Some(PragmaBody::Equals(value)) => { - let value_to_update = if let Expr::Literal(Literal::Numeric(numeric_value)) = value { - numeric_value.parse::().unwrap() - } else { - // If you put gibberish into a pragma update it turns it into 0 I think - 0 + let value_to_update = match value { + Expr::Literal(Literal::Numeric(numeric_value)) => { + numeric_value.parse::().unwrap() + } + Expr::Unary(UnaryOperator::Negative, expr) => match *expr { + Expr::Literal(Literal::Numeric(numeric_value)) => { + -numeric_value.parse::().unwrap() + } + _ => 0, + }, + _ => 0, }; println!("{:?}", value_to_update); (true, value_to_update)