• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::ffi;
2 use super::unlock_notify;
3 use super::StatementStatus;
4 #[cfg(feature = "modern_sqlite")]
5 use crate::util::SqliteMallocString;
6 use std::ffi::CStr;
7 use std::os::raw::c_int;
8 use std::ptr;
9 use std::sync::Arc;
10 
11 // Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
12 #[derive(Debug)]
13 pub struct RawStatement {
14     ptr: *mut ffi::sqlite3_stmt,
15     tail: usize,
16     // Cached indices of named parameters, computed on the fly.
17     cache: crate::util::ParamIndexCache,
18     // Cached SQL (trimmed) that we use as the key when we're in the statement
19     // cache. This is None for statements which didn't come from the statement
20     // cache.
21     //
22     // This is probably the same as `self.sql()` in most cases, but we don't
23     // care either way -- It's a better cache key as it is anyway since it's the
24     // actual source we got from rust.
25     //
26     // One example of a case where the result of `sqlite_sql` and the value in
27     // `statement_cache_key` might differ is if the statement has a `tail`.
28     statement_cache_key: Option<Arc<str>>,
29 }
30 
31 impl RawStatement {
new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement32     pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement {
33         RawStatement {
34             ptr: stmt,
35             tail,
36             cache: Default::default(),
37             statement_cache_key: None,
38         }
39     }
40 
is_null(&self) -> bool41     pub fn is_null(&self) -> bool {
42         self.ptr.is_null()
43     }
44 
set_statement_cache_key(&mut self, p: impl Into<Arc<str>>)45     pub(crate) fn set_statement_cache_key(&mut self, p: impl Into<Arc<str>>) {
46         self.statement_cache_key = Some(p.into());
47     }
48 
statement_cache_key(&self) -> Option<Arc<str>>49     pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> {
50         self.statement_cache_key.clone()
51     }
52 
ptr(&self) -> *mut ffi::sqlite3_stmt53     pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
54         self.ptr
55     }
56 
column_count(&self) -> usize57     pub fn column_count(&self) -> usize {
58         // Note: Can't cache this as it changes if the schema is altered.
59         unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
60     }
61 
column_type(&self, idx: usize) -> c_int62     pub fn column_type(&self, idx: usize) -> c_int {
63         unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
64     }
65 
66     #[cfg(feature = "column_decltype")]
column_decltype(&self, idx: usize) -> Option<&CStr>67     pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
68         unsafe {
69             let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
70             if decltype.is_null() {
71                 None
72             } else {
73                 Some(CStr::from_ptr(decltype))
74             }
75         }
76     }
77 
column_name(&self, idx: usize) -> Option<&CStr>78     pub fn column_name(&self, idx: usize) -> Option<&CStr> {
79         let idx = idx as c_int;
80         if idx < 0 || idx >= self.column_count() as c_int {
81             return None;
82         }
83         unsafe {
84             let ptr = ffi::sqlite3_column_name(self.ptr, idx);
85             // If ptr is null here, it's an OOM, so there's probably nothing
86             // meaningful we can do. Just assert instead of returning None.
87             assert!(
88                 !ptr.is_null(),
89                 "Null pointer from sqlite3_column_name: Out of memory?"
90             );
91             Some(CStr::from_ptr(ptr))
92         }
93     }
94 
step(&self) -> c_int95     pub fn step(&self) -> c_int {
96         if cfg!(feature = "unlock_notify") {
97             let db = unsafe { ffi::sqlite3_db_handle(self.ptr) };
98             let mut rc;
99             loop {
100                 rc = unsafe { ffi::sqlite3_step(self.ptr) };
101                 if unsafe { !unlock_notify::is_locked(db, rc) } {
102                     break;
103                 }
104                 rc = unsafe { unlock_notify::wait_for_unlock_notify(db) };
105                 if rc != ffi::SQLITE_OK {
106                     break;
107                 }
108                 self.reset();
109             }
110             rc
111         } else {
112             unsafe { ffi::sqlite3_step(self.ptr) }
113         }
114     }
115 
reset(&self) -> c_int116     pub fn reset(&self) -> c_int {
117         unsafe { ffi::sqlite3_reset(self.ptr) }
118     }
119 
bind_parameter_count(&self) -> usize120     pub fn bind_parameter_count(&self) -> usize {
121         unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
122     }
123 
bind_parameter_index(&self, name: &str) -> Option<usize>124     pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
125         self.cache.get_or_insert_with(name, |param_cstr| {
126             let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
127             match r {
128                 0 => None,
129                 i => Some(i as usize),
130             }
131         })
132     }
133 
clear_bindings(&self) -> c_int134     pub fn clear_bindings(&self) -> c_int {
135         unsafe { ffi::sqlite3_clear_bindings(self.ptr) }
136     }
137 
sql(&self) -> Option<&CStr>138     pub fn sql(&self) -> Option<&CStr> {
139         if self.ptr.is_null() {
140             None
141         } else {
142             Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
143         }
144     }
145 
finalize(mut self) -> c_int146     pub fn finalize(mut self) -> c_int {
147         self.finalize_()
148     }
149 
finalize_(&mut self) -> c_int150     fn finalize_(&mut self) -> c_int {
151         let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
152         self.ptr = ptr::null_mut();
153         r
154     }
155 
156     #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4
readonly(&self) -> bool157     pub fn readonly(&self) -> bool {
158         unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
159     }
160 
161     #[cfg(feature = "modern_sqlite")] // 3.14.0
expanded_sql(&self) -> Option<SqliteMallocString>162     pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
163         unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
164     }
165 
get_status(&self, status: StatementStatus, reset: bool) -> i32166     pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
167         assert!(!self.ptr.is_null());
168         unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
169     }
170 
171     #[cfg(feature = "extra_check")]
has_tail(&self) -> bool172     pub fn has_tail(&self) -> bool {
173         self.tail != 0
174     }
175 
tail(&self) -> usize176     pub fn tail(&self) -> usize {
177         self.tail
178     }
179 }
180 
181 impl Drop for RawStatement {
drop(&mut self)182     fn drop(&mut self) {
183         self.finalize_();
184     }
185 }
186