From 50653258cf6b74546fc4c223db714a3bd418693e Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Wed, 17 Sep 2025 10:49:01 +0300 Subject: [PATCH] core: Wrap symbol table with RwLock Make it Send. --- core/ext/mod.rs | 4 ++-- core/lib.rs | 28 ++++++++++++++-------------- core/vdbe/execute.rs | 13 +++++-------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/core/ext/mod.rs b/core/ext/mod.rs index dde4bcea7..0092c1a55 100644 --- a/core/ext/mod.rs +++ b/core/ext/mod.rs @@ -163,7 +163,7 @@ impl Database { /// Register any built-in extensions that can be stored on the Database so we do not have /// to register these once-per-connection, and the connection can just extend its symbol table pub fn register_global_builtin_extensions(&self) -> Result<(), String> { - let syms = self.builtin_syms.as_ptr(); + let syms = self.builtin_syms.data_ptr(); // Pass the mutex pointer and the appropriate handler let schema_mutex_ptr = &self.schema as *const Mutex> as *mut Mutex>; let ctx = Box::into_raw(Box::new(ExtensionCtx { @@ -222,7 +222,7 @@ impl Connection { let schema_mutex_ptr = &self._db.schema as *const Mutex> as *mut Mutex>; let ctx = ExtensionCtx { - syms: self.syms.as_ptr(), + syms: self.syms.data_ptr(), schema: schema_mutex_ptr as *mut c_void, }; let ctx = Box::into_raw(Box::new(ctx)) as *mut c_void; diff --git a/core/lib.rs b/core/lib.rs index 07d989990..81ab52b9d 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -203,7 +203,7 @@ pub struct Database { db_state: Arc, init_lock: Arc>, open_flags: OpenFlags, - builtin_syms: RefCell, + builtin_syms: RwLock, opts: DatabaseOpts, n_connections: AtomicUsize, } @@ -445,7 +445,7 @@ impl Database { // parse schema let conn = db.connect()?; - let syms = conn.syms.borrow(); + let syms = conn.syms.read(); let pager = conn.pager.borrow().clone(); if let Some(encryption_opts) = encryption_opts { @@ -502,7 +502,7 @@ impl Database { last_insert_rowid: Cell::new(0), last_change: Cell::new(0), total_changes: Cell::new(0), - syms: RefCell::new(SymbolTable::new()), + syms: RwLock::new(SymbolTable::new()), _shared_cache: false, cache_size: Cell::new(default_cache_size), page_size: Cell::new(page_size), @@ -523,9 +523,9 @@ impl Database { }); self.n_connections .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - let builtin_syms = self.builtin_syms.borrow(); + let builtin_syms = self.builtin_syms.read(); // add built-in extensions symbols to the connection to prevent having to load each time - conn.syms.borrow_mut().extend(&builtin_syms); + conn.syms.write().extend(&builtin_syms); Ok(conn) } @@ -983,7 +983,7 @@ pub struct Connection { last_insert_rowid: Cell, last_change: Cell, total_changes: Cell, - syms: RefCell, + syms: RwLock, _shared_cache: bool, cache_size: Cell, /// page size used for an uninitialized database or the next vacuum command. @@ -1042,7 +1042,7 @@ impl Connection { tracing::trace!("Preparing: {}", sql); let mut parser = Parser::new(sql.as_bytes()); let cmd = parser.next_cmd()?; - let syms = self.syms.borrow(); + let syms = self.syms.read(); let cmd = cmd.expect("Successful parse on nonempty input string should produce a command"); let byte_offset_end = parser.offset(); let input = str::from_utf8(&sql.as_bytes()[..byte_offset_end]) @@ -1166,7 +1166,7 @@ impl Connection { let stmt = self.prepare("SELECT * FROM sqlite_schema")?; // TODO: This function below is synchronous, make it async - parse_schema_rows(stmt, &mut fresh, &self.syms.borrow(), None, existing_views)?; + parse_schema_rows(stmt, &mut fresh, &self.syms.read(), None, existing_views)?; tracing::debug!( "reparse_schema: schema_version={}, tables={:?}", @@ -1194,7 +1194,7 @@ impl Connection { tracing::trace!("Preparing and executing batch: {}", sql); let mut parser = Parser::new(sql.as_bytes()); while let Some(cmd) = parser.next_cmd()? { - let syms = self.syms.borrow(); + let syms = self.syms.read(); let pager = self.pager.borrow().clone(); let byte_offset_end = parser.offset(); let input = str::from_utf8(&sql.as_bytes()[..byte_offset_end]) @@ -1246,7 +1246,7 @@ impl Connection { if self.closed.get() { return Err(LimboError::InternalError("Connection closed".to_string())); } - let syms = self.syms.borrow(); + let syms = self.syms.read(); let pager = self.pager.borrow().clone(); let mode = QueryMode::new(&cmd); let (Cmd::Stmt(stmt) | Cmd::Explain(stmt) | Cmd::ExplainQueryPlan(stmt)) = cmd; @@ -1278,7 +1278,7 @@ impl Connection { self.maybe_update_schema()?; let mut parser = Parser::new(sql.as_bytes()); while let Some(cmd) = parser.next_cmd()? { - let syms = self.syms.borrow(); + let syms = self.syms.read(); let pager = self.pager.borrow().clone(); let byte_offset_end = parser.offset(); let input = str::from_utf8(&sql.as_bytes()[..byte_offset_end]) @@ -1783,7 +1783,7 @@ impl Connection { let rows = self .query("SELECT * FROM sqlite_schema")? .expect("query must be parsed to statement"); - let syms = self.syms.borrow(); + let syms = self.syms.read(); self.with_schema_mut(|schema| { let existing_views = schema.incremental_views.clone(); if let Err(LimboError::ExtensionError(e)) = @@ -2122,7 +2122,7 @@ impl Connection { /// Creates a HashSet of modules that have been loaded pub fn get_syms_vtab_mods(&self) -> std::collections::HashSet { - self.syms.borrow().vtab_modules.keys().cloned().collect() + self.syms.read().vtab_modules.keys().cloned().collect() } pub fn set_encryption_key(&self, key: EncryptionKey) -> Result<()> { @@ -2405,7 +2405,7 @@ impl Statement { let cmd = parser.next_cmd()?; let cmd = cmd.expect("Same SQL string should be able to be parsed"); - let syms = conn.syms.borrow(); + let syms = conn.syms.read(); let mode = self.query_mode; debug_assert_eq!(QueryMode::new(&cmd), mode,); let (Cmd::Stmt(stmt) | Cmd::Explain(stmt) | Cmd::ExplainQueryPlan(stmt)) = cmd; diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 58476ead1..ee6167306 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -1078,12 +1078,9 @@ pub fn op_vcreate( }; let conn = program.connection.clone(); let table = - crate::VirtualTable::table(Some(&table_name), &module_name, args, &conn.syms.borrow())?; + crate::VirtualTable::table(Some(&table_name), &module_name, args, &conn.syms.read())?; { - conn.syms - .borrow_mut() - .vtabs - .insert(table_name, table.clone()); + conn.syms.write().vtabs.insert(table_name, table.clone()); } state.pc += 1; Ok(InsnFunctionStepResult::Step) @@ -1261,7 +1258,7 @@ pub fn op_vdestroy( load_insn!(VDestroy { db, table_name }, insn); let conn = program.connection.clone(); { - let Some(vtab) = conn.syms.borrow_mut().vtabs.remove(table_name) else { + let Some(vtab) = conn.syms.write().vtabs.remove(table_name) else { return Err(crate::LimboError::InternalError( "Could not find Virtual Table to Destroy".to_string(), )); @@ -6693,7 +6690,7 @@ pub fn op_parse_schema( parse_schema_rows( stmt, schema, - &conn.syms.borrow(), + &conn.syms.read(), program.connection.mv_tx.get(), existing_views, ) @@ -6708,7 +6705,7 @@ pub fn op_parse_schema( parse_schema_rows( stmt, schema, - &conn.syms.borrow(), + &conn.syms.read(), program.connection.mv_tx.get(), existing_views, )