diff --git a/core/lib.rs b/core/lib.rs index da9337e90..49a6447d2 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -240,6 +240,7 @@ impl Database { syms: RefCell::new(SymbolTable::new()), total_changes: Cell::new(0), _shared_cache: false, + cache_size: Cell::new(self.header.lock().default_page_cache_size), }); if let Err(e) = conn.register_builtins() { return Err(LimboError::ExtensionError(e)); @@ -339,6 +340,7 @@ pub struct Connection { total_changes: Cell, syms: RefCell, _shared_cache: bool, + cache_size: Cell, } impl Connection { @@ -595,6 +597,13 @@ impl Connection { self.total_changes.get() } + pub fn get_cache_size(&self) -> i32 { + self.cache_size.get() + } + pub fn set_cache_size(&self, size: i32) { + self.cache_size.set(size); + } + #[cfg(feature = "fs")] pub fn open_new(&self, path: &str, vfs: &str) -> Result<(Arc, Arc)> { Database::open_with_vfs(&self._db, path, vfs) diff --git a/core/storage/sqlite3_ondisk.rs b/core/storage/sqlite3_ondisk.rs index dd7e9cf2f..97b008ee6 100644 --- a/core/storage/sqlite3_ondisk.rs +++ b/core/storage/sqlite3_ondisk.rs @@ -251,7 +251,7 @@ impl Default for DatabaseHeader { freelist_pages: 0, schema_cookie: 0, schema_format: 4, // latest format, new sqlite3 databases use this format - default_page_cache_size: 500, // pages + default_page_cache_size: DEFAULT_CACHE_SIZE, vacuum_mode_largest_root_page: 0, text_encoding: 1, // utf-8 user_version: 0, diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 37f222922..c796ec8ba 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -88,6 +88,7 @@ pub fn translate( body.map(|b| *b), database_header.clone(), pager, + connection.clone(), program, )?, stmt => translate_inner(schema, stmt, syms, query_mode, program)?, diff --git a/core/translate/pragma.rs b/core/translate/pragma.rs index 5c36d5474..e1b2c13c0 100644 --- a/core/translate/pragma.rs +++ b/core/translate/pragma.rs @@ -3,7 +3,7 @@ use limbo_sqlite3_parser::ast; use limbo_sqlite3_parser::ast::PragmaName; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::sync::Arc; use crate::fast_lock::SpinLock; @@ -33,6 +33,7 @@ pub fn translate_pragma( body: Option, database_header: Arc>, pager: Rc, + connection: Weak, mut program: ProgramBuilder, ) -> crate::Result { let opts = ProgramBuilderOpts { @@ -56,7 +57,14 @@ pub fn translate_pragma( match body { None => { - query_pragma(pragma, schema, None, database_header.clone(), &mut program)?; + query_pragma( + pragma, + schema, + None, + database_header.clone(), + connection, + &mut program, + )?; } Some(ast::PragmaBody::Equals(value)) => match pragma { PragmaName::TableInfo => { @@ -65,6 +73,7 @@ pub fn translate_pragma( schema, Some(value), database_header.clone(), + connection, &mut program, )?; } @@ -76,6 +85,7 @@ pub fn translate_pragma( value, database_header.clone(), pager, + connection, &mut program, )?; } @@ -87,6 +97,7 @@ pub fn translate_pragma( schema, Some(value), database_header.clone(), + connection, &mut program, )?; } @@ -109,36 +120,51 @@ fn update_pragma( value: ast::Expr, header: Arc>, pager: Rc, + connection: Weak, program: &mut ProgramBuilder, ) -> crate::Result<()> { match pragma { PragmaName::CacheSize => { - let cache_size = match value { - ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => { - numeric_value.parse::()? - } - ast::Expr::Unary(ast::UnaryOperator::Negative, expr) => match *expr { - ast::Expr::Literal(ast::Literal::Numeric(numeric_value)) => { - -numeric_value.parse::()? - } - _ => bail_parse_error!("Not a valid value"), - }, - _ => bail_parse_error!("Not a valid value"), + let cache_size = match parse_signed_number(&value)? { + Value::Integer(size) => size, + Value::Float(size) => size as i64, + _ => bail_parse_error!("Invalid value for cache size pragma"), }; - update_cache_size(cache_size, header, pager)?; + update_cache_size(cache_size, header, pager, connection)?; Ok(()) } PragmaName::JournalMode => { - query_pragma(PragmaName::JournalMode, schema, None, header, program)?; + query_pragma( + PragmaName::JournalMode, + schema, + None, + header, + connection, + program, + )?; Ok(()) } PragmaName::LegacyFileFormat => Ok(()), PragmaName::WalCheckpoint => { - query_pragma(PragmaName::WalCheckpoint, schema, None, header, program)?; + query_pragma( + PragmaName::WalCheckpoint, + schema, + None, + header, + connection, + program, + )?; Ok(()) } PragmaName::PageCount => { - query_pragma(PragmaName::PageCount, schema, None, header, program)?; + query_pragma( + PragmaName::PageCount, + schema, + None, + header, + connection, + program, + )?; Ok(()) } PragmaName::UserVersion => { @@ -178,13 +204,14 @@ fn query_pragma( schema: &Schema, value: Option, database_header: Arc>, + connection: Weak, program: &mut ProgramBuilder, ) -> crate::Result<()> { let register = program.alloc_register(); match pragma { PragmaName::CacheSize => { program.emit_int( - database_header.lock().default_page_cache_size.into(), + connection.upgrade().unwrap().get_cache_size() as i64, register, ); program.emit_result_row(register, 1); @@ -287,30 +314,25 @@ fn update_cache_size( value: i64, header: Arc>, pager: Rc, + connection: Weak, ) -> crate::Result<()> { let mut cache_size_unformatted: i64 = 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 + let page_size = header.lock().get_page_size(); + kb / page_size as i64 } 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; } - - let mut header_guard = header.lock(); - - // update in-memory header - header_guard.default_page_cache_size = cache_size_unformatted - .try_into() - .unwrap_or_else(|_| panic!("invalid value, too big for a i32 {}", value)); - - // update in disk - pager.write_database_header(&header_guard)?; + connection + .upgrade() + .unwrap() + .set_cache_size(cache_size_unformatted as i32); // update cache size pager diff --git a/testing/pragma.test b/testing/pragma.test index c4a478bb3..dbe7b00eb 100755 --- a/testing/pragma.test +++ b/testing/pragma.test @@ -3,6 +3,16 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +do_execsql_test_on_specific_db ":memory:" pragma-cache-size-default { + PRAGMA cache_size +} {-2000} + +do_execsql_test pragma-set-cache-size { + PRAGMA cache_size = 100; + PRAGMA cache_size +} {100} + +# Even though the cache size was set to 100 in previous test, a new connection defaults back to -2000. do_execsql_test pragma-cache-size { PRAGMA cache_size } {-2000}