From 9d2f26bb045065f25b8a93f9fa822f1600c4e36f Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sun, 24 Aug 2025 16:58:06 +0300 Subject: [PATCH] sqlite3: Implement sqlite3_clear_bindings() --- core/lib.rs | 4 +++ core/vdbe/mod.rs | 4 +++ sqlite3/src/lib.rs | 12 +++++++ sqlite3/tests/compat/mod.rs | 65 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/core/lib.rs b/core/lib.rs index e8842527c..843c40bc3 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -2170,6 +2170,10 @@ impl Statement { self.state.bind_at(index, value); } + pub fn clear_bindings(&mut self) { + self.state.clear_bindings(); + } + pub fn reset(&mut self) { self.state.reset(); } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index b99c17cd5..5a9cbe646 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -335,6 +335,10 @@ impl ProgramState { self.parameters.insert(index, value); } + pub fn clear_bindings(&mut self) { + self.parameters.clear(); + } + pub fn get_parameter(&self, index: NonZero) -> Value { self.parameters.get(&index).cloned().unwrap_or(Value::Null) } diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index 32a2976e3..dc7d3491e 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -702,6 +702,18 @@ pub unsafe extern "C" fn sqlite3_bind_blob( SQLITE_OK } +#[no_mangle] +pub unsafe extern "C" fn sqlite3_clear_bindings(stmt: *mut sqlite3_stmt) -> ffi::c_int { + if stmt.is_null() { + return SQLITE_MISUSE; + } + + let stmt_ref = &mut *stmt; + stmt_ref.stmt.clear_bindings(); + + SQLITE_OK +} + #[no_mangle] pub unsafe extern "C" fn sqlite3_column_type( stmt: *mut sqlite3_stmt, diff --git a/sqlite3/tests/compat/mod.rs b/sqlite3/tests/compat/mod.rs index 2192a89f9..96f12a991 100644 --- a/sqlite3/tests/compat/mod.rs +++ b/sqlite3/tests/compat/mod.rs @@ -27,6 +27,7 @@ extern "C" { tail: *mut *const libc::c_char, ) -> i32; fn sqlite3_step(stmt: *mut sqlite3_stmt) -> i32; + fn sqlite3_reset(stmt: *mut sqlite3_stmt) -> i32; fn sqlite3_finalize(stmt: *mut sqlite3_stmt) -> i32; fn sqlite3_wal_checkpoint(db: *mut sqlite3, db_name: *const libc::c_char) -> i32; fn sqlite3_wal_checkpoint_v2( @@ -49,6 +50,7 @@ extern "C" { 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; + fn sqlite3_clear_bindings(stmt: *mut sqlite3_stmt) -> i32; fn sqlite3_column_name(stmt: *mut sqlite3_stmt, idx: i32) -> *const libc::c_char; fn sqlite3_last_insert_rowid(db: *mut sqlite3) -> i32; fn sqlite3_column_count(stmt: *mut sqlite3_stmt) -> i32; @@ -1095,4 +1097,67 @@ mod tests { } } } + + #[test] + fn test_sqlite3_clear_bindings() { + unsafe { + let mut db: *mut sqlite3 = ptr::null_mut(); + let mut stmt: *mut sqlite3_stmt = ptr::null_mut(); + + assert_eq!(sqlite3_open(c":memory:".as_ptr(), &mut db), SQLITE_OK); + + assert_eq!( + sqlite3_prepare_v2( + db, + c"CREATE TABLE person (id INTEGER, name TEXT, age INTEGER)".as_ptr(), + -1, + &mut stmt, + ptr::null_mut() + ), + SQLITE_OK + ); + assert_eq!(sqlite3_step(stmt), SQLITE_DONE); + assert_eq!(sqlite3_finalize(stmt), SQLITE_OK); + + assert_eq!( + sqlite3_prepare_v2( + db, + c"INSERT INTO person (id, name, age) VALUES (1, 'John', 25), (2, 'Jane', 30)" + .as_ptr(), + -1, + &mut stmt, + ptr::null_mut() + ), + SQLITE_OK + ); + assert_eq!(sqlite3_step(stmt), SQLITE_DONE); + assert_eq!(sqlite3_finalize(stmt), SQLITE_OK); + + assert_eq!( + sqlite3_prepare_v2( + db, + c"SELECT * FROM person WHERE id = ? AND age > ?".as_ptr(), + -1, + &mut stmt, + ptr::null_mut() + ), + SQLITE_OK + ); + + // Bind parameters - should find John (id=1, age=25 > 20) + assert_eq!(sqlite3_bind_int(stmt, 1, 1), SQLITE_OK); + assert_eq!(sqlite3_bind_int(stmt, 2, 20), SQLITE_OK); + assert_eq!(sqlite3_step(stmt), SQLITE_ROW); + assert_eq!(sqlite3_column_int(stmt, 0), 1); + assert_eq!(sqlite3_column_int(stmt, 2), 25); + + // Reset and clear bindings, query should return no rows + assert_eq!(sqlite3_reset(stmt), SQLITE_OK); + assert_eq!(sqlite3_clear_bindings(stmt), SQLITE_OK); + assert_eq!(sqlite3_step(stmt), SQLITE_DONE); + + assert_eq!(sqlite3_finalize(stmt), SQLITE_OK); + assert_eq!(sqlite3_close(db), SQLITE_OK); + } + } }