core: introduce pseudo program with pragma

Introduced pragma statement parsing and update in memory of default page cache size.

There are some more "improvements" to the print insn procedure —  I couldn't decide what was the preferred way in rust to do printing on different int types so I went with the stupidest I could think of at the moment.
This commit is contained in:
Pere Diaz Bou
2024-06-15 17:01:48 +02:00
parent 17a22278c2
commit d795a7a3ba
4 changed files with 262 additions and 55 deletions

View File

@@ -18,8 +18,10 @@ use fallible_iterator::FallibleIterator;
use log::trace;
use pager::Pager;
use schema::Schema;
use sqlite3_ondisk::DatabaseHeader;
use sqlite3_parser::{ast::Cmd, lexer::sql::Parser};
use std::rc::Rc;
use std::{cell::RefCell, rc::Rc};
use vdbe::ProgramType;
#[cfg(feature = "fs")]
pub use io::PlatformIO;
@@ -30,6 +32,7 @@ pub use types::Value;
pub struct Database {
pager: Rc<Pager>,
schema: Rc<Schema>,
header: Rc<RefCell<DatabaseHeader>>,
}
impl Database {
@@ -43,11 +46,12 @@ 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, page_source)?);
let pager = Rc::new(Pager::finish_open(db_header.clone(), page_source)?);
let bootstrap_schema = Rc::new(Schema::new());
let conn = Connection {
pager: pager.clone(),
schema: bootstrap_schema.clone(),
header: db_header.clone(),
};
let mut schema = Schema::new();
let rows = conn.query("SELECT * FROM sqlite_schema")?;
@@ -74,13 +78,19 @@ impl Database {
}
}
let schema = Rc::new(schema);
Ok(Database { pager, schema })
let header = db_header;
Ok(Database {
pager,
schema,
header,
})
}
pub fn connect(&self) -> Connection {
Connection {
pager: self.pager.clone(),
schema: self.schema.clone(),
header: self.header.clone(),
}
}
}
@@ -88,6 +98,7 @@ impl Database {
pub struct Connection {
pager: Rc<Pager>,
schema: Rc<Schema>,
header: Rc<RefCell<DatabaseHeader>>,
}
impl Connection {
@@ -99,7 +110,11 @@ impl Connection {
if let Some(cmd) = cmd {
match cmd {
Cmd::Stmt(stmt) => {
let program = Rc::new(translate::translate(&self.schema, stmt)?);
let program = Rc::new(translate::translate(
&self.schema,
stmt,
&self.header.borrow(),
)?);
Ok(Statement::new(program, self.pager.clone()))
}
Cmd::Explain(_stmt) => todo!(),
@@ -110,6 +125,22 @@ 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 cache size
}
_ => todo!(),
}
}
pub fn query(&self, sql: impl Into<String>) -> Result<Option<Rows>> {
let sql = sql.into();
trace!("Querying: {}", sql);
@@ -118,12 +149,19 @@ impl Connection {
if let Some(cmd) = cmd {
match cmd {
Cmd::Stmt(stmt) => {
let program = Rc::new(translate::translate(&self.schema, stmt)?);
let program = Rc::new(translate::translate(
&self.schema,
stmt,
&self.header.borrow(),
)?);
if let ProgramType::PragmaChange(name, value) = &program.program_type {
self.update_pragma(name, *value);
}
let stmt = Statement::new(program, self.pager.clone());
Ok(Some(Rows { stmt }))
}
Cmd::Explain(stmt) => {
let program = translate::translate(&self.schema, stmt)?;
let program = translate::translate(&self.schema, stmt, &self.header.borrow())?;
program.explain();
Ok(None)
}
@@ -141,12 +179,12 @@ impl Connection {
if let Some(cmd) = cmd {
match cmd {
Cmd::Explain(stmt) => {
let program = translate::translate(&self.schema, stmt)?;
let program = translate::translate(&self.schema, stmt, &self.header.borrow())?;
program.explain();
}
Cmd::ExplainQueryPlan(_stmt) => todo!(),
Cmd::Stmt(stmt) => {
let program = translate::translate(&self.schema, stmt)?;
let program = translate::translate(&self.schema, stmt, &self.header.borrow())?;
let mut state = vdbe::ProgramState::new(program.max_registers);
program.step(&mut state, self.pager.clone())?;
}

View File

@@ -18,7 +18,7 @@
/// +-----------------+----------------+---------------------+----------------+
/// | | | | |
/// | Page header | Cell pointer | Unallocated | Cell content |
/// | (8 or 12 bytes) | array | space | area |
/// | (8 or 12 bytes) | array | space | area |
/// | | | | |
/// +-----------------+----------------+---------------------+----------------+
///
@@ -35,6 +35,7 @@ use std::rc::Rc;
/// The size of the database header in bytes.
pub const DATABASE_HEADER_SIZE: usize = 100;
const DEFAULT_CACHE_SIZE: i32 = -2000;
#[derive(Debug, Default)]
pub struct DatabaseHeader {
@@ -52,7 +53,7 @@ pub struct DatabaseHeader {
freelist_pages: u32,
schema_cookie: u32,
schema_format: u32,
default_cache_size: u32,
pub default_cache_size: i32,
vacuum: u32,
text_encoding: u32,
user_version: u32,
@@ -94,7 +95,10 @@ fn finish_read_database_header(buf: &Buffer, header: Rc<RefCell<DatabaseHeader>>
header.freelist_pages = u32::from_be_bytes([buf[36], buf[37], buf[38], buf[39]]);
header.schema_cookie = u32::from_be_bytes([buf[40], buf[41], buf[42], buf[43]]);
header.schema_format = u32::from_be_bytes([buf[44], buf[45], buf[46], buf[47]]);
header.default_cache_size = u32::from_be_bytes([buf[48], buf[49], buf[50], buf[51]]);
header.default_cache_size = i32::from_be_bytes([buf[48], buf[49], buf[50], buf[51]]);
if header.default_cache_size == 0 {
header.default_cache_size = DEFAULT_CACHE_SIZE;
}
header.vacuum = u32::from_be_bytes([buf[52], buf[53], buf[54], buf[55]]);
header.text_encoding = u32::from_be_bytes([buf[56], buf[57], buf[58], buf[59]]);
header.user_version = u32::from_be_bytes([buf[60], buf[61], buf[62], buf[63]]);

View File

@@ -1,12 +1,16 @@
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, Select, Stmt};
use sqlite3_parser::ast::{Expr, Literal, OneSelect, PragmaBody, QualifiedName, Select, Stmt};
/// Translate SQL statement into bytecode program.
pub fn translate(schema: &Schema, stmt: Stmt) -> Result<Program> {
pub fn translate(schema: &Schema, stmt: Stmt, database_header: &DatabaseHeader) -> Result<Program> {
match stmt {
Stmt::Select(select) => translate_select(schema, select),
Stmt::Pragma(name, body) => translate_pragma(&name, body, database_header),
_ => todo!(),
}
}
@@ -218,3 +222,58 @@ fn translate_expr(
Expr::Variable(_) => todo!(),
}
}
fn translate_pragma(
name: &QualifiedName,
body: Option<PragmaBody>,
database_header: &DatabaseHeader,
) -> Result<Program> {
let mut program = ProgramBuilder::new();
let init_offset = program.emit_placeholder();
let start_offset = program.offset();
let (change_pragma, new_value) = match body {
None => {
let pragma_result = program.alloc_register();
program.emit_insn(Insn::Integer {
value: database_header.default_cache_size.into(),
dest: pragma_result,
});
let pragma_result_end = program.next_free_register();
program.emit_insn(Insn::ResultRow {
register_start: pragma_result,
register_end: pragma_result_end,
});
(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
};
println!("{:?}", value_to_update);
(true, value_to_update)
}
Some(PragmaBody::Call(_)) => {
todo!()
}
};
program.emit_insn(Insn::Halt);
program.fixup_insn(
init_offset,
Insn::Init {
target_pc: program.offset(),
},
);
program.emit_insn(Insn::Transaction);
program.emit_insn(Insn::Goto {
target_pc: start_offset,
});
if change_pragma {
return Ok(program.build_pragma_change(name.name.to_string(), new_value));
}
Ok(program.build())
}

View File

@@ -3,6 +3,7 @@ use crate::pager::Pager;
use crate::types::{Cursor, CursorResult, OwnedValue, Record};
use anyhow::Result;
use core::fmt;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
@@ -154,6 +155,15 @@ impl ProgramBuilder {
Program {
max_registers: self.next_free_register,
insns: self.insns,
program_type: ProgramType::Default,
}
}
pub fn build_pragma_change(self, pragma_to_change: String, value: i64) -> Program {
Program {
max_registers: self.next_free_register,
insns: self.insns,
program_type: ProgramType::PragmaChange(pragma_to_change, value),
}
}
}
@@ -192,9 +202,15 @@ impl ProgramState {
}
}
pub enum ProgramType {
Default,
PragmaChange(String, i64),
}
pub struct Program {
pub max_registers: usize,
pub insns: Vec<Insn>,
pub program_type: ProgramType,
}
impl Program {
@@ -363,15 +379,37 @@ fn print_insn(addr: usize, insn: &Insn) {
println!("{}", s);
}
enum IntValue {
Int(i64),
Usize(usize),
}
impl fmt::Display for IntValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IntValue::Int(i) => f.pad(i.to_string().as_str()),
IntValue::Usize(i) => f.pad(i.to_string().as_str()),
}
}
}
fn insn_to_str(addr: usize, insn: &Insn) -> String {
let (opcode, p1, p2, p3, p4, p5, comment) = match insn {
let (opcode, p1, p2, p3, p4, p5, comment): (
&str,
IntValue,
IntValue,
IntValue,
&str,
IntValue,
String,
) = match insn {
Insn::Init { target_pc } => (
"Init",
0,
*target_pc,
0,
IntValue::Usize(0),
IntValue::Usize(*target_pc),
IntValue::Usize(0),
"",
0,
IntValue::Usize(0),
format!("Start at {}", target_pc),
),
Insn::OpenReadAsync {
@@ -379,25 +417,41 @@ fn insn_to_str(addr: usize, insn: &Insn) -> String {
root_page,
} => (
"OpenReadAsync",
*cursor_id,
*root_page,
0,
IntValue::Usize(*cursor_id),
IntValue::Usize(*root_page),
IntValue::Usize(0),
"",
0,
IntValue::Usize(0),
format!("root={}", root_page),
),
Insn::OpenReadAwait => ("OpenReadAwait", 0, 0, 0, "", 0, "".to_string()),
Insn::RewindAsync { cursor_id } => ("RewindAsync", *cursor_id, 0, 0, "", 0, "".to_string()),
Insn::OpenReadAwait => (
"OpenReadAwait",
IntValue::Usize(0),
IntValue::Usize(0),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::RewindAsync { cursor_id } => (
"RewindAsync",
IntValue::Usize(*cursor_id),
IntValue::Usize(0),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::RewindAwait {
cursor_id,
pc_if_empty,
} => (
"RewindAwait",
*cursor_id,
*pc_if_empty,
0,
IntValue::Usize(*cursor_id),
IntValue::Usize(*pc_if_empty),
IntValue::Usize(0),
"",
0,
IntValue::Usize(0),
"".to_string(),
),
Insn::Column {
@@ -406,11 +460,11 @@ fn insn_to_str(addr: usize, insn: &Insn) -> String {
dest,
} => (
"Column",
*cursor_id,
*column,
*dest,
IntValue::Usize(*cursor_id),
IntValue::Usize(*column),
IntValue::Usize(*dest),
"",
0,
IntValue::Usize(0),
format!("r[{}]= cursor {} column {}", dest, cursor_id, column),
),
Insn::ResultRow {
@@ -418,45 +472,97 @@ fn insn_to_str(addr: usize, insn: &Insn) -> String {
register_end,
} => (
"ResultRow",
*register_start,
*register_end,
0,
IntValue::Usize(*register_start),
IntValue::Usize(*register_end),
IntValue::Usize(0),
"",
0,
IntValue::Usize(0),
format!("output=r[{}..{}]", register_start, register_end),
),
Insn::NextAsync { cursor_id } => ("NextAsync", *cursor_id, 0, 0, "", 0, "".to_string()),
Insn::NextAsync { cursor_id } => (
"NextAsync",
IntValue::Usize(*cursor_id),
IntValue::Usize(0),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::NextAwait {
cursor_id,
pc_if_next,
} => (
"NextAwait",
*cursor_id,
*pc_if_next,
0,
IntValue::Usize(*cursor_id),
IntValue::Usize(*pc_if_next),
IntValue::Usize(0),
"",
0,
IntValue::Usize(0),
"".to_string(),
),
Insn::Halt => (
"Halt",
IntValue::Usize(0),
IntValue::Usize(0),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::Transaction => (
"Transaction",
IntValue::Usize(0),
IntValue::Usize(0),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::Goto { target_pc } => (
"Goto",
IntValue::Usize(0),
IntValue::Usize(*target_pc),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::Integer { value, dest } => (
"Integer",
IntValue::Usize(*dest),
IntValue::Int(*value),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::Halt => ("Halt", 0, 0, 0, "", 0, "".to_string()),
Insn::Transaction => ("Transaction", 0, 0, 0, "", 0, "".to_string()),
Insn::Goto { target_pc } => ("Goto", 0, *target_pc, 0, "", 0, "".to_string()),
Insn::Integer { value, dest } => {
("Integer", *dest, *value as usize, 0, "", 0, "".to_string())
}
Insn::String8 { value, dest } => (
"String8",
*dest,
0,
0,
IntValue::Usize(*dest),
IntValue::Usize(0),
IntValue::Usize(0),
value.as_str(),
0,
IntValue::Usize(0),
format!("r[{}]= '{}'", dest, value),
),
Insn::RowId { cursor_id, dest } => ("RowId", *cursor_id, *dest, 0, "", 0, "".to_string()),
Insn::DecrJumpZero { reg, target_pc } => {
("DecrJumpZero", *reg, *target_pc, 0, "", 0, "".to_string())
}
Insn::RowId { cursor_id, dest } => (
"RowId",
IntValue::Usize(*cursor_id),
IntValue::Usize(*dest),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
Insn::DecrJumpZero { reg, target_pc } => (
"DecrJumpZero",
IntValue::Usize(*reg),
IntValue::Usize(*target_pc),
IntValue::Usize(0),
"",
IntValue::Usize(0),
"".to_string(),
),
};
format!(
"{:<4} {:<13} {:<4} {:<4} {:<4} {:<13} {:<2} {}",