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`).
This commit is contained in:
gwenn 2024-11-10 16:56:03 +01:00
parent 0af8bfc603
commit 3d85c79891
6 changed files with 28 additions and 19 deletions

View File

@ -207,8 +207,8 @@ impl Backup<'_, '_> {
to: &'b mut Connection, to: &'b mut Connection,
to_name: DatabaseName<'_>, to_name: DatabaseName<'_>,
) -> Result<Backup<'a, 'b>> { ) -> Result<Backup<'a, 'b>> {
let to_name = to_name.as_cstring()?; let to_name = to_name.as_cstr()?;
let from_name = from_name.as_cstring()?; let from_name = from_name.as_cstr()?;
let to_db = to.db.borrow_mut().db; let to_db = to.db.borrow_mut().db;

View File

@ -346,7 +346,7 @@ impl InnerConnection {
fn remove_preupdate_hook(&mut self) {} fn remove_preupdate_hook(&mut self) {}
pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> { pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> {
let name = db_name.as_cstring()?; let name = db_name.as_cstr()?;
let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) }; let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) };
match r { match r {
0 => Ok(false), 0 => Ok(false),
@ -368,7 +368,7 @@ impl InnerConnection {
db_name: Option<super::DatabaseName<'_>>, db_name: Option<super::DatabaseName<'_>>,
) -> Result<super::transaction::TransactionState> { ) -> Result<super::transaction::TransactionState> {
let r = if let Some(ref name) = db_name { 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()) } unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) }
} else { } else {
unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) } unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) }

View File

@ -347,12 +347,12 @@ fn path_to_cstring(p: &Path) -> Result<CString> {
pub enum DatabaseName<'a> { pub enum DatabaseName<'a> {
/// The main database. /// The main database.
Main, Main,
/// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). /// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables).
Temp, Temp,
/// A database that has been attached via "ATTACH DATABASE ...". /// A database that has been attached via "ATTACH DATABASE ...".
Attached(&'a str), Attached(&'a str),
/// Optim
C(&'a CStr),
} }
/// Shorthand for [`DatabaseName::Main`]. /// Shorthand for [`DatabaseName::Main`].
@ -361,16 +361,24 @@ pub const MAIN_DB: DatabaseName<'static> = DatabaseName::Main;
/// Shorthand for [`DatabaseName::Temp`]. /// Shorthand for [`DatabaseName::Temp`].
pub const TEMP_DB: DatabaseName<'static> = 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<'_> { impl DatabaseName<'_> {
#[inline] #[inline]
fn as_cstring(&self) -> Result<SmallCString> { fn as_cstr(&self) -> Result<std::borrow::Cow<'_, CStr>> {
use self::DatabaseName::{Attached, Main, Temp}; Ok(match *self {
match *self { DatabaseName::Main => std::borrow::Cow::Borrowed(c"main"),
Main => str_to_cstring("main"), // TODO C-string literals DatabaseName::Temp => std::borrow::Cow::Borrowed(c"temp"),
Temp => str_to_cstring("temp"), DatabaseName::Attached(s) => std::borrow::Cow::Owned(CString::new(s)?),
Attached(s) => str_to_cstring(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")] #[cfg(feature = "hooks")]
@ -647,7 +655,7 @@ impl Connection {
pub fn path(&self) -> Option<&str> { pub fn path(&self) -> Option<&str> {
unsafe { unsafe {
let db = self.handle(); 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()); let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
if db_filename.is_null() { if db_filename.is_null() {
None None

View File

@ -47,6 +47,7 @@ impl Sql {
DatabaseName::Main => self.buf.push_str("main"), DatabaseName::Main => self.buf.push_str("main"),
DatabaseName::Temp => self.buf.push_str("temp"), DatabaseName::Temp => self.buf.push_str("temp"),
DatabaseName::Attached(s) => self.push_identifier(s), DatabaseName::Attached(s) => self.push_identifier(s),
DatabaseName::C(s) => self.push_identifier(s.to_str().expect("invalid database name")),
}; };
} }

View File

@ -66,7 +66,7 @@ impl Deref for Data<'_> {
impl Connection { impl Connection {
/// Serialize a database. /// Serialize a database.
pub fn serialize(&self, schema: DatabaseName) -> Result<Data> { pub fn serialize(&self, schema: DatabaseName) -> Result<Data> {
let schema = schema.as_cstring()?; let schema = schema.as_cstr()?;
let mut sz = 0; let mut sz = 0;
let mut ptr: *mut u8 = unsafe { let mut ptr: *mut u8 = unsafe {
ffi::sqlite3_serialize( ffi::sqlite3_serialize(
@ -102,7 +102,7 @@ impl Connection {
data: OwnedData, data: OwnedData,
read_only: bool, read_only: bool,
) -> Result<()> { ) -> Result<()> {
let schema = schema.as_cstring()?; let schema = schema.as_cstr()?;
let (data, sz) = data.into_raw(); let (data, sz) = data.into_raw();
let sz = sz.try_into().unwrap(); let sz = sz.try_into().unwrap();
let flags = if read_only { let flags = if read_only {

View File

@ -42,7 +42,7 @@ impl Session<'_> {
db: &'conn Connection, db: &'conn Connection,
name: DatabaseName<'_>, name: DatabaseName<'_>,
) -> Result<Session<'conn>> { ) -> Result<Session<'conn>> {
let name = name.as_cstring()?; let name = name.as_cstr()?;
let db = db.db.borrow_mut().db; let db = db.db.borrow_mut().db;
@ -154,7 +154,7 @@ impl Session<'_> {
/// Load the difference between tables. /// Load the difference between tables.
pub fn diff(&mut self, from: DatabaseName<'_>, table: &str) -> Result<()> { 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 = str_to_cstring(table)?;
let table = table.as_ptr(); let table = table.as_ptr();
unsafe { unsafe {