mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-06 01:34:21 +01:00
Merge pull request #51 from pereman2/pragma_cache_size
core: pragma cache_size support
This commit is contained in:
@@ -39,4 +39,13 @@ impl limbo_core::PageIO for PageIO {
|
||||
fn get(&self, _page_idx: usize, _c: Rc<limbo_core::Completion>) -> Result<()> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_page_idx: usize,
|
||||
_buffer: Rc<std::cell::RefCell<limbo_core::Buffer>>,
|
||||
_c: Rc<limbo_core::WriteCompletion>,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
57
core/lib.rs
57
core/lib.rs
@@ -18,18 +18,20 @@ 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};
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use io::PlatformIO;
|
||||
pub use io::{Buffer, Completion, File, IO};
|
||||
pub use io::{Buffer, Completion, File, WriteCompletion, IO};
|
||||
pub use storage::{PageIO, PageSource};
|
||||
pub use types::Value;
|
||||
|
||||
pub struct Database {
|
||||
pager: Rc<Pager>,
|
||||
schema: Rc<Schema>,
|
||||
header: Rc<RefCell<DatabaseHeader>>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
@@ -43,11 +45,16 @@ 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,
|
||||
io.clone(),
|
||||
)?);
|
||||
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 +81,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 +101,7 @@ impl Database {
|
||||
pub struct Connection {
|
||||
pager: Rc<Pager>,
|
||||
schema: Rc<Schema>,
|
||||
header: Rc<RefCell<DatabaseHeader>>,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
@@ -99,7 +113,12 @@ 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.clone(),
|
||||
self.pager.clone(),
|
||||
)?);
|
||||
Ok(Statement::new(program, self.pager.clone()))
|
||||
}
|
||||
Cmd::Explain(_stmt) => todo!(),
|
||||
@@ -118,12 +137,22 @@ 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.clone(),
|
||||
self.pager.clone(),
|
||||
)?);
|
||||
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.clone(),
|
||||
self.pager.clone(),
|
||||
)?;
|
||||
program.explain();
|
||||
Ok(None)
|
||||
}
|
||||
@@ -141,12 +170,22 @@ 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.clone(),
|
||||
self.pager.clone(),
|
||||
)?;
|
||||
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.clone(),
|
||||
self.pager.clone(),
|
||||
)?;
|
||||
let mut state = vdbe::ProgramState::new(program.max_registers);
|
||||
program.step(&mut state, self.pager.clone())?;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::PageSource;
|
||||
use log::trace;
|
||||
use sieve_cache::SieveCache;
|
||||
use std::cell::RefCell;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::RwLock;
|
||||
@@ -72,13 +73,36 @@ impl Page {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageCache<K: Eq + Hash + Clone, V> {
|
||||
cache: SieveCache<K, V>,
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash + Clone, V> PageCache<K, V> {
|
||||
pub fn new(cache: SieveCache<K, V>) -> Self {
|
||||
Self { cache }
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: K, value: V) {
|
||||
self.cache.insert(key, value);
|
||||
}
|
||||
|
||||
pub fn get(&mut self, key: &K) -> Option<&V> {
|
||||
self.cache.get(key)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, capacity: usize) {
|
||||
self.cache = SieveCache::new(capacity).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// The pager interface implements the persistence layer by providing access
|
||||
/// to pages of the database file, including caching, concurrency control, and
|
||||
/// transaction management.
|
||||
pub struct Pager {
|
||||
page_source: PageSource,
|
||||
page_cache: RefCell<SieveCache<usize, Rc<Page>>>,
|
||||
pub page_source: PageSource,
|
||||
page_cache: RefCell<PageCache<usize, Rc<Page>>>,
|
||||
buffer_pool: Rc<BufferPool>,
|
||||
pub io: Rc<dyn crate::io::IO>,
|
||||
}
|
||||
|
||||
impl Pager {
|
||||
@@ -89,15 +113,17 @@ 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;
|
||||
let buffer_pool = Rc::new(BufferPool::new(page_size));
|
||||
let page_cache = RefCell::new(SieveCache::new(10).unwrap());
|
||||
let page_cache = RefCell::new(PageCache::new(SieveCache::new(10).unwrap()));
|
||||
Ok(Self {
|
||||
page_source,
|
||||
buffer_pool,
|
||||
page_cache,
|
||||
io,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,4 +145,12 @@ 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");
|
||||
}
|
||||
|
||||
pub fn change_page_cache_size(&self, capacity: usize) {
|
||||
self.page_cache.borrow_mut().resize(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
/// +-----------------+----------------+---------------------+----------------+
|
||||
/// | | | | |
|
||||
/// | Page header | Cell pointer | Unallocated | Cell content |
|
||||
/// | (8 or 12 bytes) | array | space | area |
|
||||
/// | (8 or 12 bytes) | array | space | area |
|
||||
/// | | | | |
|
||||
/// +-----------------+----------------+---------------------+----------------+
|
||||
///
|
||||
/// 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};
|
||||
@@ -35,8 +35,13 @@ use std::rc::Rc;
|
||||
|
||||
/// The size of the database header in bytes.
|
||||
pub const DATABASE_HEADER_SIZE: usize = 100;
|
||||
// DEFAULT_CACHE_SIZE negative values mean that we store the amount of pages a XKiB of memory can hold.
|
||||
// We can calculate "real" cache size by diving by page size.
|
||||
const DEFAULT_CACHE_SIZE: i32 = -2000;
|
||||
// Minimun number of pages that cache can hold.
|
||||
pub const MIN_PAGE_CACHE_SIZE: usize = 10;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct DatabaseHeader {
|
||||
magic: [u8; 16],
|
||||
pub page_size: u16,
|
||||
@@ -52,7 +57,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,
|
||||
@@ -79,7 +84,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];
|
||||
@@ -94,7 +99,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]]);
|
||||
@@ -106,6 +114,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,
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -1,16 +1,65 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::pager::Pager;
|
||||
use crate::schema::Schema;
|
||||
use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE};
|
||||
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, UnaryOperator,
|
||||
};
|
||||
|
||||
/// Translate SQL statement into bytecode program.
|
||||
pub fn translate(schema: &Schema, stmt: Stmt) -> Result<Program> {
|
||||
pub fn translate(
|
||||
schema: &Schema,
|
||||
stmt: Stmt,
|
||||
database_header: Rc<RefCell<DatabaseHeader>>,
|
||||
pager: Rc<Pager>,
|
||||
) -> Result<Program> {
|
||||
match stmt {
|
||||
Stmt::Select(select) => translate_select(schema, select),
|
||||
Stmt::Pragma(name, body) => translate_pragma(&name, body, database_header, pager),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_pragma(
|
||||
name: &String,
|
||||
value: i64,
|
||||
header: Rc<RefCell<DatabaseHeader>>,
|
||||
pager: Rc<Pager>,
|
||||
) {
|
||||
match name.as_str() {
|
||||
"cache_size" => {
|
||||
let mut cache_size_unformatted = value;
|
||||
let mut cache_size = if cache_size_unformatted < 0 {
|
||||
let kb = cache_size_unformatted.abs() * 1024;
|
||||
kb / 512 // assume 512 page size for now
|
||||
} else {
|
||||
value
|
||||
} as usize;
|
||||
if cache_size < MIN_PAGE_CACHE_SIZE {
|
||||
// update both in memory and stored disk value
|
||||
cache_size = MIN_PAGE_CACHE_SIZE;
|
||||
cache_size_unformatted = MIN_PAGE_CACHE_SIZE as i64;
|
||||
}
|
||||
|
||||
// update in-memory header
|
||||
header.borrow_mut().default_cache_size = cache_size_unformatted
|
||||
.try_into()
|
||||
.expect(&format!("invalid value, too big for a i32 {}", value));
|
||||
|
||||
// update in disk
|
||||
let header_copy = header.borrow().clone();
|
||||
pager.write_database_header(&header_copy);
|
||||
|
||||
// update cache size
|
||||
pager.change_page_cache_size(cache_size);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
fn translate_select(schema: &Schema, select: Select) -> Result<Program> {
|
||||
let mut program = ProgramBuilder::new();
|
||||
let init_offset = program.emit_placeholder();
|
||||
@@ -218,3 +267,60 @@ fn translate_expr(
|
||||
Expr::Variable(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_pragma(
|
||||
name: &QualifiedName,
|
||||
body: Option<PragmaBody>,
|
||||
database_header: Rc<RefCell<DatabaseHeader>>,
|
||||
pager: Rc<Pager>,
|
||||
) -> Result<Program> {
|
||||
let mut program = ProgramBuilder::new();
|
||||
let init_offset = program.emit_placeholder();
|
||||
let start_offset = program.offset();
|
||||
match body {
|
||||
None => {
|
||||
let pragma_result = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::Integer {
|
||||
value: database_header.borrow().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,
|
||||
});
|
||||
}
|
||||
Some(PragmaBody::Equals(value)) => {
|
||||
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,
|
||||
};
|
||||
update_pragma(&name.name.0, value_to_update, database_header, pager);
|
||||
}
|
||||
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,
|
||||
});
|
||||
Ok(program.build())
|
||||
}
|
||||
|
||||
175
core/vdbe.rs
175
core/vdbe.rs
@@ -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;
|
||||
@@ -363,15 +364,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 +402,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 +445,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 +457,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} {}",
|
||||
|
||||
Reference in New Issue
Block a user