1
+ use either:: Either ;
1
2
use std:: ffi:: c_void;
2
3
use std:: ffi:: CStr ;
4
+ use std:: hint;
3
5
use std:: os:: raw:: { c_char, c_int} ;
4
6
use std:: ptr;
5
7
use std:: ptr:: NonNull ;
6
8
use std:: slice:: from_raw_parts;
7
9
use std:: str:: { from_utf8, from_utf8_unchecked} ;
10
+ use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
8
11
9
12
use libsqlite3_sys:: {
10
13
sqlite3, sqlite3_bind_blob64, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
11
14
sqlite3_bind_null, sqlite3_bind_parameter_count, sqlite3_bind_parameter_name,
12
- sqlite3_bind_text64, sqlite3_changes, sqlite3_column_blob, sqlite3_column_bytes,
13
- sqlite3_column_count, sqlite3_column_database_name, sqlite3_column_decltype,
14
- sqlite3_column_double, sqlite3_column_int, sqlite3_column_int64, sqlite3_column_name,
15
- sqlite3_column_origin_name, sqlite3_column_table_name, sqlite3_column_type,
16
- sqlite3_column_value, sqlite3_db_handle, sqlite3_finalize, sqlite3_reset, sqlite3_sql,
17
- sqlite3_stmt, sqlite3_stmt_readonly, sqlite3_table_column_metadata, sqlite3_value,
18
- SQLITE_MISUSE , SQLITE_OK , SQLITE_TRANSIENT , SQLITE_UTF8 ,
15
+ sqlite3_bind_text64, sqlite3_changes, sqlite3_clear_bindings, sqlite3_column_blob,
16
+ sqlite3_column_bytes, sqlite3_column_count, sqlite3_column_database_name,
17
+ sqlite3_column_decltype, sqlite3_column_double, sqlite3_column_int, sqlite3_column_int64,
18
+ sqlite3_column_name, sqlite3_column_origin_name, sqlite3_column_table_name,
19
+ sqlite3_column_type, sqlite3_column_value, sqlite3_db_handle, sqlite3_finalize, sqlite3_reset,
20
+ sqlite3_sql, sqlite3_step, sqlite3_stmt, sqlite3_stmt_readonly, sqlite3_table_column_metadata,
21
+ sqlite3_value, SQLITE_DONE , SQLITE_MISUSE , SQLITE_OK , SQLITE_ROW , SQLITE_TRANSIENT ,
22
+ SQLITE_UTF8 ,
19
23
} ;
20
24
21
25
use crate :: error:: { BoxDynError , Error } ;
22
26
use crate :: sqlite:: type_info:: DataType ;
23
27
use crate :: sqlite:: { SqliteError , SqliteTypeInfo } ;
24
28
25
29
#[ derive( Debug ) ]
26
- pub ( crate ) struct StatementHandle ( pub ( super ) NonNull < sqlite3_stmt > ) ;
30
+ pub ( crate ) struct StatementHandle ( NonNull < sqlite3_stmt > , Lock ) ;
27
31
28
32
// access to SQLite3 statement handles are safe to send and share between threads
29
33
// as long as the `sqlite3_step` call is serialized.
@@ -32,6 +36,10 @@ unsafe impl Send for StatementHandle {}
32
36
unsafe impl Sync for StatementHandle { }
33
37
34
38
impl StatementHandle {
39
+ pub ( super ) fn new ( ptr : NonNull < sqlite3_stmt > ) -> Self {
40
+ Self ( ptr, Lock :: new ( ) )
41
+ }
42
+
35
43
#[ inline]
36
44
pub ( super ) unsafe fn db_handle ( & self ) -> * mut sqlite3 {
37
45
// O(c) access to the connection handle for this statement handle
@@ -280,8 +288,37 @@ impl StatementHandle {
280
288
Ok ( from_utf8 ( self . column_blob ( index) ) ?)
281
289
}
282
290
291
+ pub ( crate ) fn step ( & self ) -> Result < Either < u64 , ( ) > , Error > {
292
+ self . 1 . enter_step ( ) ;
293
+
294
+ let status = unsafe { sqlite3_step ( self . 0 . as_ptr ( ) ) } ;
295
+ let result = match status {
296
+ SQLITE_ROW => Ok ( Either :: Right ( ( ) ) ) ,
297
+ SQLITE_DONE => Ok ( Either :: Left ( self . changes ( ) ) ) ,
298
+ _ => Err ( self . last_error ( ) . into ( ) ) ,
299
+ } ;
300
+
301
+ if self . 1 . exit_step ( ) {
302
+ unsafe { sqlite3_reset ( self . 0 . as_ptr ( ) ) } ;
303
+ self . 1 . exit_reset ( ) ;
304
+ }
305
+
306
+ result
307
+ }
308
+
283
309
pub ( crate ) fn reset ( & self ) {
310
+ if !self . 1 . enter_reset ( ) {
311
+ // reset or step already in progress
312
+ return ;
313
+ }
314
+
284
315
unsafe { sqlite3_reset ( self . 0 . as_ptr ( ) ) } ;
316
+
317
+ self . 1 . exit_reset ( ) ;
318
+ }
319
+
320
+ pub ( crate ) fn clear_bindings ( & self ) {
321
+ unsafe { sqlite3_clear_bindings ( self . 0 . as_ptr ( ) ) } ;
285
322
}
286
323
}
287
324
impl Drop for StatementHandle {
@@ -301,3 +338,44 @@ impl Drop for StatementHandle {
301
338
}
302
339
}
303
340
}
341
+
342
+ const RESET : u8 = 0b0000_0001 ;
343
+ const STEP : u8 = 0b0000_0010 ;
344
+
345
+ // Lock to synchronize calls to `step` and `reset`.
346
+ #[ derive( Debug ) ]
347
+ struct Lock ( AtomicU8 ) ;
348
+
349
+ impl Lock {
350
+ fn new ( ) -> Self {
351
+ Self ( AtomicU8 :: new ( 0 ) )
352
+ }
353
+
354
+ // If this returns `true` reset can be performed, otherwise reset must be delayed until the
355
+ // current step finishes and `exit_step` is called.
356
+ fn enter_reset ( & self ) -> bool {
357
+ self . 0 . fetch_or ( RESET , Ordering :: Acquire ) == 0
358
+ }
359
+
360
+ fn exit_reset ( & self ) {
361
+ self . 0 . fetch_and ( !RESET , Ordering :: Release ) ;
362
+ }
363
+
364
+ fn enter_step ( & self ) {
365
+ // NOTE: spin loop should be fine here as we are only waiting for a `reset` to finish which
366
+ // should be quick.
367
+ while self
368
+ . 0
369
+ . compare_exchange ( 0 , STEP , Ordering :: Acquire , Ordering :: Relaxed )
370
+ . is_err ( )
371
+ {
372
+ hint:: spin_loop ( ) ;
373
+ }
374
+ }
375
+
376
+ // If this returns `true` it means a previous attempt to reset was delayed and must be
377
+ // performed now (followed by `exit_reset`).
378
+ fn exit_step ( & self ) -> bool {
379
+ self . 0 . fetch_and ( !STEP , Ordering :: Release ) & RESET != 0
380
+ }
381
+ }
0 commit comments