core: parse unary and write to disk

This commit is contained in:
Pere Diaz Bou
2024-06-18 19:51:47 +02:00
parent d795a7a3ba
commit 53c348402a
5 changed files with 146 additions and 18 deletions

View File

@@ -46,7 +46,11 @@ impl Database {
pub fn open(io: Rc<dyn crate::io::IO>, page_source: PageSource) -> Result<Database> {
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!(),

View File

@@ -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<SieveCache<usize, Rc<Page>>>,
buffer_pool: Rc<BufferPool>,
pub io: Rc<dyn crate::io::IO>,
}
impl Pager {
@@ -89,6 +90,7 @@ impl Pager {
pub fn finish_open(
db_header: Rc<RefCell<DatabaseHeader>>,
page_source: PageSource,
io: Rc<dyn crate::io::IO>,
) -> anyhow::Result<Self> {
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");
}
}

View File

@@ -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<Rc<RefCell
fn finish_read_database_header(buf: &Buffer, header: Rc<RefCell<DatabaseHeader>>) -> 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<RefCell<DatabaseHeader>>
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,

View File

@@ -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<dyn PageIO>,
}
impl Clone for PageSource {
fn clone(&self) -> Self {
Self {
io: self.io.clone(),
}
}
}
impl PageSource {
pub fn from_io(io: Rc<dyn PageIO>) -> Self {
Self { io }
@@ -23,10 +34,25 @@ impl PageSource {
pub fn get(&self, page_idx: usize, c: Rc<Completion>) -> Result<()> {
self.io.get(page_idx, c)
}
pub fn write(
&self,
page_idx: usize,
buffer: Rc<RefCell<Buffer>>,
c: Rc<WriteCompletion>,
) -> Result<()> {
self.io.write(page_idx, buffer, c)
}
}
pub trait PageIO {
fn get(&self, page_idx: usize, c: Rc<Completion>) -> Result<()>;
fn write(
&self,
page_idx: usize,
buffer: Rc<RefCell<Buffer>>,
c: Rc<WriteCompletion>,
) -> 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<RefCell<Buffer>>,
c: Rc<WriteCompletion>,
) -> 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")]

View File

@@ -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<Program> {
@@ -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::<i64>().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::<i64>().unwrap()
}
Expr::Unary(UnaryOperator::Negative, expr) => match *expr {
Expr::Literal(Literal::Numeric(numeric_value)) => {
-numeric_value.parse::<i64>().unwrap()
}
_ => 0,
},
_ => 0,
};
println!("{:?}", value_to_update);
(true, value_to_update)