From b162f89b73c781d321712b03e0595caea1ff98a7 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 25 Aug 2025 07:36:12 +0300 Subject: [PATCH] sqlite3: Implement sqlite3_db_filename() --- sqlite3/include/sqlite3.h | 2 ++ sqlite3/src/lib.rs | 38 +++++++++++++++++++++++++++++------ sqlite3/tests/compat/mod.rs | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/sqlite3/include/sqlite3.h b/sqlite3/include/sqlite3.h index 68a8944e5..f56f38b8c 100644 --- a/sqlite3/include/sqlite3.h +++ b/sqlite3/include/sqlite3.h @@ -76,6 +76,8 @@ int sqlite3_close(sqlite3 *db); int sqlite3_close_v2(sqlite3 *db); +const char *sqlite3_db_filename(sqlite3 *db, const char *db_name); + int sqlite3_trace_v2(sqlite3 *_db, unsigned int _mask, void (*_callback)(unsigned int, void*, void*, void*), diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index b7bd6529a..46d6d64b3 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -56,6 +56,7 @@ struct sqlite3Inner { pub(crate) malloc_failed: bool, pub(crate) e_open_state: u8, pub(crate) p_err: *mut ffi::c_void, + pub(crate) filename: CString, } impl sqlite3 { @@ -63,6 +64,7 @@ impl sqlite3 { io: Arc, db: Arc, conn: Arc, + filename: CString, ) -> Self { let inner = sqlite3Inner { _io: io, @@ -73,6 +75,7 @@ impl sqlite3 { malloc_failed: false, e_open_state: SQLITE_STATE_OPEN, p_err: std::ptr::null_mut(), + filename, }; #[allow(clippy::arc_with_non_send_sync)] let inner = Arc::new(Mutex::new(inner)); @@ -132,26 +135,30 @@ pub unsafe extern "C" fn sqlite3_open( if db_out.is_null() { return SQLITE_MISUSE; } - let filename = CStr::from_ptr(filename); - let filename = match filename.to_str() { + let filename_cstr = CStr::from_ptr(filename); + let filename_str = match filename_cstr.to_str() { Ok(s) => s, Err(_) => return SQLITE_MISUSE, }; - let io: Arc = match filename { + let io: Arc = match filename_str { ":memory:" => Arc::new(turso_core::MemoryIO::new()), _ => match turso_core::PlatformIO::new() { Ok(io) => Arc::new(io), Err(_) => return SQLITE_CANTOPEN, }, }; - match turso_core::Database::open_file(io.clone(), filename, false, false) { + match turso_core::Database::open_file(io.clone(), filename_str, false, false) { Ok(db) => { let conn = db.connect().unwrap(); - *db_out = Box::leak(Box::new(sqlite3::new(io, db, conn))); + let filename = match filename_str { + ":memory:" => CString::new("".to_string()).unwrap(), + _ => CString::from(filename_cstr), + }; + *db_out = Box::leak(Box::new(sqlite3::new(io, db, conn, filename))); SQLITE_OK } Err(e) => { - trace!("error opening database {}: {:?}", filename, e); + trace!("error opening database {}: {:?}", filename_str, e); SQLITE_CANTOPEN } } @@ -184,6 +191,25 @@ pub unsafe extern "C" fn sqlite3_close_v2(db: *mut sqlite3) -> ffi::c_int { sqlite3_close(db) } +#[no_mangle] +pub unsafe extern "C" fn sqlite3_db_filename( + db: *mut sqlite3, + db_name: *const ffi::c_char, +) -> *const ffi::c_char { + if db.is_null() { + return std::ptr::null(); + } + if !db_name.is_null() { + let name = CStr::from_ptr(db_name); + if name.to_bytes() != b"main" { + return std::ptr::null(); + } + } + let db = &*db; + let inner = db.inner.lock().unwrap(); + inner.filename.as_ptr() +} + #[no_mangle] pub unsafe extern "C" fn sqlite3_trace_v2( _db: *mut sqlite3, diff --git a/sqlite3/tests/compat/mod.rs b/sqlite3/tests/compat/mod.rs index a40ae7538..94361cf11 100644 --- a/sqlite3/tests/compat/mod.rs +++ b/sqlite3/tests/compat/mod.rs @@ -19,6 +19,7 @@ extern "C" { fn sqlite3_libversion_number() -> i32; fn sqlite3_close(db: *mut sqlite3) -> i32; fn sqlite3_open(filename: *const libc::c_char, db: *mut *mut sqlite3) -> i32; + fn sqlite3_db_filename(db: *mut sqlite3, db_name: *const libc::c_char) -> *const libc::c_char; fn sqlite3_prepare_v2( db: *mut sqlite3, sql: *const libc::c_char, @@ -1279,4 +1280,43 @@ mod tests { assert_eq!(sqlite3_finalize(stmt), SQLITE_OK); } } + + #[test] + fn test_sqlite3_db_filename() { + const SQLITE_OK: i32 = 0; + + unsafe { + // Test with in-memory database + let mut db: *mut sqlite3 = ptr::null_mut(); + assert_eq!(sqlite3_open(c":memory:".as_ptr(), &mut db), SQLITE_OK); + let filename = sqlite3_db_filename(db, c"main".as_ptr()); + assert!(!filename.is_null()); + let filename_str = std::ffi::CStr::from_ptr(filename).to_str().unwrap(); + assert_eq!(filename_str, ""); + assert_eq!(sqlite3_close(db), SQLITE_OK); + + // Open a file-backed database + let temp_file = tempfile::NamedTempFile::with_suffix(".db").unwrap(); + let path = std::ffi::CString::new(temp_file.path().to_str().unwrap()).unwrap(); + let mut db = ptr::null_mut(); + assert_eq!(sqlite3_open(path.as_ptr(), &mut db), SQLITE_OK); + + // Test with "main" database name + let filename = sqlite3_db_filename(db, c"main".as_ptr()); + assert!(!filename.is_null()); + let filename_str = std::ffi::CStr::from_ptr(filename).to_str().unwrap(); + assert_eq!(filename_str, temp_file.path().to_str().unwrap()); + + // Test with NULL database name (defaults to main) + let filename_default = sqlite3_db_filename(db, ptr::null()); + assert!(!filename_default.is_null()); + assert_eq!(filename, filename_default); + + // Test with non-existent database name + let filename = sqlite3_db_filename(db, c"temp".as_ptr()); + assert!(filename.is_null()); + + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } }