mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-01 15:34:19 +01:00
Merge 'sqlite3: Implement sqlite3_next_stmt()' from Pekka Enberg
Closes #2780
This commit is contained in:
@@ -107,6 +107,8 @@ int sqlite3_stmt_readonly(sqlite3_stmt *_stmt);
|
||||
|
||||
int sqlite3_stmt_busy(sqlite3_stmt *_stmt);
|
||||
|
||||
sqlite3_stmt *sqlite3_next_stmt(sqlite3 *db, sqlite3_stmt *stmt);
|
||||
|
||||
int sqlite3_serialize(sqlite3 *_db, const char *_schema, void **_out, int *_out_bytes, unsigned int _flags);
|
||||
|
||||
int sqlite3_deserialize(sqlite3 *_db, const char *_schema, const void *_in_, int _in_bytes, unsigned int _flags);
|
||||
|
||||
@@ -57,6 +57,7 @@ struct sqlite3Inner {
|
||||
pub(crate) e_open_state: u8,
|
||||
pub(crate) p_err: *mut ffi::c_void,
|
||||
pub(crate) filename: CString,
|
||||
pub(crate) stmt_list: *mut sqlite3_stmt,
|
||||
}
|
||||
|
||||
impl sqlite3 {
|
||||
@@ -76,6 +77,7 @@ impl sqlite3 {
|
||||
e_open_state: SQLITE_STATE_OPEN,
|
||||
p_err: std::ptr::null_mut(),
|
||||
filename,
|
||||
stmt_list: std::ptr::null_mut(),
|
||||
};
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let inner = Arc::new(Mutex::new(inner));
|
||||
@@ -91,6 +93,7 @@ pub struct sqlite3_stmt {
|
||||
Option<unsafe extern "C" fn(*mut ffi::c_void)>,
|
||||
*mut ffi::c_void,
|
||||
)>,
|
||||
pub(crate) next: *mut sqlite3_stmt,
|
||||
}
|
||||
|
||||
impl sqlite3_stmt {
|
||||
@@ -99,6 +102,7 @@ impl sqlite3_stmt {
|
||||
db,
|
||||
stmt,
|
||||
destructors: Vec::new(),
|
||||
next: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,7 +283,12 @@ pub unsafe extern "C" fn sqlite3_prepare_v2(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
};
|
||||
*out_stmt = Box::leak(Box::new(sqlite3_stmt::new(raw_db, stmt)));
|
||||
let new_stmt = Box::leak(Box::new(sqlite3_stmt::new(raw_db, stmt)));
|
||||
|
||||
new_stmt.next = db.stmt_list;
|
||||
db.stmt_list = new_stmt;
|
||||
|
||||
*out_stmt = new_stmt;
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
@@ -290,6 +299,25 @@ pub unsafe extern "C" fn sqlite3_finalize(stmt: *mut sqlite3_stmt) -> ffi::c_int
|
||||
}
|
||||
let stmt_ref = &mut *stmt;
|
||||
|
||||
if !stmt_ref.db.is_null() {
|
||||
let db = &mut *stmt_ref.db;
|
||||
let mut db_inner = db.inner.lock().unwrap();
|
||||
|
||||
if db_inner.stmt_list == stmt {
|
||||
db_inner.stmt_list = stmt_ref.next;
|
||||
} else {
|
||||
let mut current = db_inner.stmt_list;
|
||||
while !current.is_null() {
|
||||
let current_ref = &mut *current;
|
||||
if current_ref.next == stmt {
|
||||
current_ref.next = stmt_ref.next;
|
||||
break;
|
||||
}
|
||||
current = current_ref.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (_idx, destructor_opt, ptr) in stmt_ref.destructors.drain(..) {
|
||||
if let Some(destructor_fn) = destructor_opt {
|
||||
destructor_fn(ptr);
|
||||
@@ -381,6 +409,25 @@ pub unsafe extern "C" fn sqlite3_stmt_busy(_stmt: *mut sqlite3_stmt) -> ffi::c_i
|
||||
stub!();
|
||||
}
|
||||
|
||||
/// Iterate over all prepared statements in the database.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_next_stmt(
|
||||
db: *mut sqlite3,
|
||||
stmt: *mut sqlite3_stmt,
|
||||
) -> *mut sqlite3_stmt {
|
||||
if db.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
if stmt.is_null() {
|
||||
let db = &*db;
|
||||
let db = db.inner.lock().unwrap();
|
||||
db.stmt_list
|
||||
} else {
|
||||
let stmt = &mut *stmt;
|
||||
stmt.next
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_serialize(
|
||||
_db: *mut sqlite3,
|
||||
|
||||
@@ -48,6 +48,7 @@ extern "C" {
|
||||
) -> i32;
|
||||
fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> i32;
|
||||
fn sqlite3_column_int(stmt: *mut sqlite3_stmt, idx: i32) -> i64;
|
||||
fn sqlite3_next_stmt(db: *mut sqlite3, stmt: *mut sqlite3_stmt) -> *mut sqlite3_stmt;
|
||||
fn sqlite3_bind_int(stmt: *mut sqlite3_stmt, idx: i32, val: i64) -> i32;
|
||||
fn sqlite3_bind_parameter_count(stmt: *mut sqlite3_stmt) -> i32;
|
||||
fn sqlite3_bind_parameter_name(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char;
|
||||
@@ -1319,4 +1320,81 @@ mod tests {
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqlite3_next_stmt() {
|
||||
const SQLITE_OK: i32 = 0;
|
||||
|
||||
unsafe {
|
||||
let mut db: *mut sqlite3 = ptr::null_mut();
|
||||
assert_eq!(sqlite3_open(c":memory:".as_ptr(), &mut db), SQLITE_OK);
|
||||
|
||||
// Initially, there should be no prepared statements
|
||||
let iter = sqlite3_next_stmt(db, ptr::null_mut());
|
||||
assert!(iter.is_null());
|
||||
|
||||
// Prepare first statement
|
||||
let mut stmt1: *mut sqlite3_stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(db, c"SELECT 1;".as_ptr(), -1, &mut stmt1, ptr::null_mut()),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert!(!stmt1.is_null());
|
||||
|
||||
// Now there should be one statement
|
||||
let iter = sqlite3_next_stmt(db, ptr::null_mut());
|
||||
assert_eq!(iter, stmt1);
|
||||
|
||||
// And no more after that
|
||||
let iter = sqlite3_next_stmt(db, stmt1);
|
||||
assert!(iter.is_null());
|
||||
|
||||
// Prepare second statement
|
||||
let mut stmt2: *mut sqlite3_stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(db, c"SELECT 2;".as_ptr(), -1, &mut stmt2, ptr::null_mut()),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert!(!stmt2.is_null());
|
||||
|
||||
// Prepare third statement
|
||||
let mut stmt3: *mut sqlite3_stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(db, c"SELECT 3;".as_ptr(), -1, &mut stmt3, ptr::null_mut()),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert!(!stmt3.is_null());
|
||||
|
||||
// Count all statements
|
||||
let mut count = 0;
|
||||
let mut iter = sqlite3_next_stmt(db, ptr::null_mut());
|
||||
while !iter.is_null() {
|
||||
count += 1;
|
||||
iter = sqlite3_next_stmt(db, iter);
|
||||
}
|
||||
assert_eq!(count, 3);
|
||||
|
||||
// Finalize the middle statement
|
||||
assert_eq!(sqlite3_finalize(stmt2), SQLITE_OK);
|
||||
|
||||
// Count should now be 2
|
||||
count = 0;
|
||||
iter = sqlite3_next_stmt(db, ptr::null_mut());
|
||||
while !iter.is_null() {
|
||||
count += 1;
|
||||
iter = sqlite3_next_stmt(db, iter);
|
||||
}
|
||||
assert_eq!(count, 2);
|
||||
|
||||
// Finalize remaining statements
|
||||
assert_eq!(sqlite3_finalize(stmt1), SQLITE_OK);
|
||||
assert_eq!(sqlite3_finalize(stmt3), SQLITE_OK);
|
||||
|
||||
// Should be no statements left
|
||||
let iter = sqlite3_next_stmt(db, ptr::null_mut());
|
||||
assert!(iter.is_null());
|
||||
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ void test_sqlite3_bind_text2();
|
||||
void test_sqlite3_bind_blob();
|
||||
void test_sqlite3_column_type();
|
||||
void test_sqlite3_column_decltype();
|
||||
void test_sqlite3_next_stmt();
|
||||
|
||||
int allocated = 0;
|
||||
|
||||
@@ -35,6 +36,7 @@ int main(void)
|
||||
test_sqlite3_bind_blob();
|
||||
test_sqlite3_column_type();
|
||||
test_sqlite3_column_decltype();
|
||||
test_sqlite3_next_stmt();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user