From 3d85c798914376936cc233adfec349c7f070b9e0 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 10 Nov 2024 16:56:03 +0100 Subject: [PATCH] Use C string literal for database name No alloc for "main" and "temp". No alloc possible when the attached database name is known at compile time / a C string literal can be usd. No alloc when the database name is given by SQLite (`sqlite3_wal_hook`). --- src/backup.rs | 4 ++-- src/inner_connection.rs | 4 ++-- src/lib.rs | 30 +++++++++++++++++++----------- src/pragma.rs | 1 + src/serialize.rs | 4 ++-- src/session.rs | 4 ++-- 6 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/backup.rs b/src/backup.rs index bba0555..a268ded 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -207,8 +207,8 @@ impl Backup<'_, '_> { to: &'b mut Connection, to_name: DatabaseName<'_>, ) -> Result> { - let to_name = to_name.as_cstring()?; - let from_name = from_name.as_cstring()?; + let to_name = to_name.as_cstr()?; + let from_name = from_name.as_cstr()?; let to_db = to.db.borrow_mut().db; diff --git a/src/inner_connection.rs b/src/inner_connection.rs index 5615807..1c08899 100644 --- a/src/inner_connection.rs +++ b/src/inner_connection.rs @@ -346,7 +346,7 @@ impl InnerConnection { fn remove_preupdate_hook(&mut self) {} pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result { - let name = db_name.as_cstring()?; + let name = db_name.as_cstr()?; let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) }; match r { 0 => Ok(false), @@ -368,7 +368,7 @@ impl InnerConnection { db_name: Option>, ) -> Result { let r = if let Some(ref name) = db_name { - let name = name.as_cstring()?; + let name = name.as_cstr()?; unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) } } else { unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) } diff --git a/src/lib.rs b/src/lib.rs index 62f61fd..dbb643f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -347,12 +347,12 @@ fn path_to_cstring(p: &Path) -> Result { pub enum DatabaseName<'a> { /// The main database. Main, - /// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). Temp, - /// A database that has been attached via "ATTACH DATABASE ...". Attached(&'a str), + /// Optim + C(&'a CStr), } /// Shorthand for [`DatabaseName::Main`]. @@ -361,16 +361,24 @@ pub const MAIN_DB: DatabaseName<'static> = DatabaseName::Main; /// Shorthand for [`DatabaseName::Temp`]. pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp; -// Currently DatabaseName is only used by the backup and blob mods, so hide -// this (private) impl to avoid dead code warnings. impl DatabaseName<'_> { #[inline] - fn as_cstring(&self) -> Result { - use self::DatabaseName::{Attached, Main, Temp}; - match *self { - Main => str_to_cstring("main"), // TODO C-string literals - Temp => str_to_cstring("temp"), - Attached(s) => str_to_cstring(s), + fn as_cstr(&self) -> Result> { + Ok(match *self { + DatabaseName::Main => std::borrow::Cow::Borrowed(c"main"), + DatabaseName::Temp => std::borrow::Cow::Borrowed(c"temp"), + DatabaseName::Attached(s) => std::borrow::Cow::Owned(CString::new(s)?), + DatabaseName::C(s) => std::borrow::Cow::Borrowed(s), + }) + } + #[cfg(feature = "hooks")] + pub(crate) fn from_cstr(cs: &std::ffi::CStr) -> DatabaseName<'_> { + if cs == c"main" { + DatabaseName::Main + } else if cs == c"temp" { + DatabaseName::Temp + } else { + DatabaseName::C(cs) } } #[cfg(feature = "hooks")] @@ -647,7 +655,7 @@ impl Connection { pub fn path(&self) -> Option<&str> { unsafe { let db = self.handle(); - let db_name = DatabaseName::Main.as_cstring().unwrap(); + let db_name = DatabaseName::Main.as_cstr().unwrap(); let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr()); if db_filename.is_null() { None diff --git a/src/pragma.rs b/src/pragma.rs index 8f6c8f5..ff9b398 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -47,6 +47,7 @@ impl Sql { DatabaseName::Main => self.buf.push_str("main"), DatabaseName::Temp => self.buf.push_str("temp"), DatabaseName::Attached(s) => self.push_identifier(s), + DatabaseName::C(s) => self.push_identifier(s.to_str().expect("invalid database name")), }; } diff --git a/src/serialize.rs b/src/serialize.rs index fedc260..2e94f7b 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -66,7 +66,7 @@ impl Deref for Data<'_> { impl Connection { /// Serialize a database. pub fn serialize(&self, schema: DatabaseName) -> Result { - let schema = schema.as_cstring()?; + let schema = schema.as_cstr()?; let mut sz = 0; let mut ptr: *mut u8 = unsafe { ffi::sqlite3_serialize( @@ -102,7 +102,7 @@ impl Connection { data: OwnedData, read_only: bool, ) -> Result<()> { - let schema = schema.as_cstring()?; + let schema = schema.as_cstr()?; let (data, sz) = data.into_raw(); let sz = sz.try_into().unwrap(); let flags = if read_only { diff --git a/src/session.rs b/src/session.rs index 180f588..6311936 100644 --- a/src/session.rs +++ b/src/session.rs @@ -42,7 +42,7 @@ impl Session<'_> { db: &'conn Connection, name: DatabaseName<'_>, ) -> Result> { - let name = name.as_cstring()?; + let name = name.as_cstr()?; let db = db.db.borrow_mut().db; @@ -154,7 +154,7 @@ impl Session<'_> { /// Load the difference between tables. pub fn diff(&mut self, from: DatabaseName<'_>, table: &str) -> Result<()> { - let from = from.as_cstring()?; + let from = from.as_cstr()?; let table = str_to_cstring(table)?; let table = table.as_ptr(); unsafe {