1 use super::ffi;
2 use super::StatementStatus;
3 use crate::util::ParamIndexCache;
4 use crate::util::SqliteMallocString;
5 use std::ffi::CStr;
6 use std::os::raw::c_int;
7 use std::ptr;
8 use std::sync::Arc;
9 
10 // Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
11 #[derive(Debug)]
12 pub struct RawStatement {
13     ptr: *mut ffi::sqlite3_stmt,
14     tail: usize,
15     // Cached indices of named parameters, computed on the fly.
16     cache: ParamIndexCache,
17     // Cached SQL (trimmed) that we use as the key when we're in the statement
18     // cache. This is None for statements which didn't come from the statement
19     // cache.
20     //
21     // This is probably the same as `self.sql()` in most cases, but we don't
22     // care either way -- It's a better cache key as it is anyway since it's the
23     // actual source we got from rust.
24     //
25     // One example of a case where the result of `sqlite_sql` and the value in
26     // `statement_cache_key` might differ is if the statement has a `tail`.
27     statement_cache_key: Option<Arc<str>>,
28 }
29 
30 impl RawStatement {
31     #[inline]
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: ParamIndexCache::default(),
37             statement_cache_key: None,
38         }
39     }
40 
41     #[inline]
is_null(&self) -> bool42     pub fn is_null(&self) -> bool {
43         self.ptr.is_null()
44     }
45 
46     #[inline]
set_statement_cache_key(&mut self, p: impl Into<Arc<str>>)47     pub(crate) fn set_statement_cache_key(&mut self, p: impl Into<Arc<str>>) {
48         self.statement_cache_key = Some(p.into());
49     }
50 
51     #[inline]
statement_cache_key(&self) -> Option<Arc<str>>52     pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> {
53         self.statement_cache_key.clone()
54     }
55 
56     #[inline]
ptr(&self) -> *mut ffi::sqlite3_stmt57     pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
58         self.ptr
59     }
60 
61     #[inline]
column_count(&self) -> usize62     pub fn column_count(&self) -> usize {
63         // Note: Can't cache this as it changes if the schema is altered.
64         unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
65     }
66 
67     #[inline]
column_type(&self, idx: usize) -> c_int68     pub fn column_type(&self, idx: usize) -> c_int {
69         unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
70     }
71 
72     #[inline]
73     #[cfg(feature = "column_decltype")]
column_decltype(&self, idx: usize) -> Option<&CStr>74     pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
75         unsafe {
76             let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
77             if decltype.is_null() {
78                 None
79             } else {
80                 Some(CStr::from_ptr(decltype))
81             }
82         }
83     }
84 
85     #[inline]
column_name(&self, idx: usize) -> Option<&CStr>86     pub fn column_name(&self, idx: usize) -> Option<&CStr> {
87         let idx = idx as c_int;
88         if idx < 0 || idx >= self.column_count() as c_int {
89             return None;
90         }
91         unsafe {
92             let ptr = ffi::sqlite3_column_name(self.ptr, idx);
93             // If ptr is null here, it's an OOM, so there's probably nothing
94             // meaningful we can do. Just assert instead of returning None.
95             assert!(
96                 !ptr.is_null(),
97                 "Null pointer from sqlite3_column_name: Out of memory?"
98             );
99             Some(CStr::from_ptr(ptr))
100         }
101     }
102 
103     #[inline]
104     #[cfg(not(feature = "unlock_notify"))]
step(&self) -> c_int105     pub fn step(&self) -> c_int {
106         unsafe { ffi::sqlite3_step(self.ptr) }
107     }
108 
109     #[cfg(feature = "unlock_notify")]
step(&self) -> c_int110     pub fn step(&self) -> c_int {
111         use crate::unlock_notify;
112         let mut db = ptr::null_mut::<ffi::sqlite3>();
113         loop {
114             unsafe {
115                 let mut rc = ffi::sqlite3_step(self.ptr);
116                 // Bail out early for success and errors unrelated to locking. We
117                 // still need check `is_locked` after this, but checking now lets us
118                 // avoid one or two (admittedly cheap) calls into SQLite that we
119                 // don't need to make.
120                 if (rc & 0xff) != ffi::SQLITE_LOCKED {
121                     break rc;
122                 }
123                 if db.is_null() {
124                     db = ffi::sqlite3_db_handle(self.ptr);
125                 }
126                 if !unlock_notify::is_locked(db, rc) {
127                     break rc;
128                 }
129                 rc = unlock_notify::wait_for_unlock_notify(db);
130                 if rc != ffi::SQLITE_OK {
131                     break rc;
132                 }
133                 self.reset();
134             }
135         }
136     }
137 
138     #[inline]
reset(&self) -> c_int139     pub fn reset(&self) -> c_int {
140         unsafe { ffi::sqlite3_reset(self.ptr) }
141     }
142 
143     #[inline]
bind_parameter_count(&self) -> usize144     pub fn bind_parameter_count(&self) -> usize {
145         unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
146     }
147 
148     #[inline]
bind_parameter_index(&self, name: &str) -> Option<usize>149     pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
150         self.cache.get_or_insert_with(name, |param_cstr| {
151             let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
152             match r {
153                 0 => None,
154                 i => Some(i as usize),
155             }
156         })
157     }
158 
159     #[inline]
bind_parameter_name(&self, index: i32) -> Option<&CStr>160     pub fn bind_parameter_name(&self, index: i32) -> Option<&CStr> {
161         unsafe {
162             let name = ffi::sqlite3_bind_parameter_name(self.ptr, index);
163             if name.is_null() {
164                 None
165             } else {
166                 Some(CStr::from_ptr(name))
167             }
168         }
169     }
170 
171     #[inline]
clear_bindings(&self)172     pub fn clear_bindings(&self) {
173         unsafe {
174             ffi::sqlite3_clear_bindings(self.ptr);
175         } // rc is always SQLITE_OK
176     }
177 
178     #[inline]
sql(&self) -> Option<&CStr>179     pub fn sql(&self) -> Option<&CStr> {
180         if self.ptr.is_null() {
181             None
182         } else {
183             Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
184         }
185     }
186 
187     #[inline]
finalize(mut self) -> c_int188     pub fn finalize(mut self) -> c_int {
189         self.finalize_()
190     }
191 
192     #[inline]
finalize_(&mut self) -> c_int193     fn finalize_(&mut self) -> c_int {
194         let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
195         self.ptr = ptr::null_mut();
196         r
197     }
198 
199     // does not work for PRAGMA
200     #[inline]
readonly(&self) -> bool201     pub fn readonly(&self) -> bool {
202         unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
203     }
204 
205     #[inline]
expanded_sql(&self) -> Option<SqliteMallocString>206     pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
207         unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
208     }
209 
210     #[inline]
get_status(&self, status: StatementStatus, reset: bool) -> i32211     pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
212         assert!(!self.ptr.is_null());
213         unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
214     }
215 
216     #[inline]
217     #[cfg(feature = "extra_check")]
has_tail(&self) -> bool218     pub fn has_tail(&self) -> bool {
219         self.tail != 0
220     }
221 
222     #[inline]
tail(&self) -> usize223     pub fn tail(&self) -> usize {
224         self.tail
225     }
226 
227     #[inline]
228     #[cfg(feature = "modern_sqlite")] // 3.28.0
is_explain(&self) -> i32229     pub fn is_explain(&self) -> i32 {
230         unsafe { ffi::sqlite3_stmt_isexplain(self.ptr) }
231     }
232 
233     // TODO sqlite3_normalized_sql (https://sqlite.org/c3ref/expanded_sql.html) // 3.27.0 + SQLITE_ENABLE_NORMALIZE
234 }
235 
236 impl Drop for RawStatement {
drop(&mut self)237     fn drop(&mut self) {
238         self.finalize_();
239     }
240 }
241