Merge 'sqlite3: Implement sqlite3_db_filename()' from Pekka Enberg

Closes #2771
This commit is contained in:
Pekka Enberg
2025-08-25 11:23:13 +03:00
committed by GitHub
3 changed files with 74 additions and 6 deletions

View File

@@ -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*),

View File

@@ -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<dyn turso_core::IO>,
db: Arc<turso_core::Database>,
conn: Arc<turso_core::Connection>,
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<dyn turso_core::IO> = match filename {
let io: Arc<dyn turso_core::IO> = 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,

View File

@@ -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);
}
}
}