• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::ffi;
2 use super::StatementStatus;
3 use crate::util::ParamIndexCache;
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: 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 {
32     #[inline]
new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement33     pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement {
34         RawStatement {
35             ptr: stmt,
36             tail,
37             cache: ParamIndexCache::default(),
38             statement_cache_key: None,
39         }
40     }
41 
42     #[inline]
is_null(&self) -> bool43     pub fn is_null(&self) -> bool {
44         self.ptr.is_null()
45     }
46 
47     #[inline]
set_statement_cache_key(&mut self, p: impl Into<Arc<str>>)48     pub(crate) fn set_statement_cache_key(&mut self, p: impl Into<Arc<str>>) {
49         self.statement_cache_key = Some(p.into());
50     }
51 
52     #[inline]
statement_cache_key(&self) -> Option<Arc<str>>53     pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> {
54         self.statement_cache_key.clone()
55     }
56 
57     #[inline]
ptr(&self) -> *mut ffi::sqlite3_stmt58     pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
59         self.ptr
60     }
61 
62     #[inline]
column_count(&self) -> usize63     pub fn column_count(&self) -> usize {
64         // Note: Can't cache this as it changes if the schema is altered.
65         unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
66     }
67 
68     #[inline]
column_type(&self, idx: usize) -> c_int69     pub fn column_type(&self, idx: usize) -> c_int {
70         unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
71     }
72 
73     #[inline]
74     #[cfg(feature = "column_decltype")]
column_decltype(&self, idx: usize) -> Option<&CStr>75     pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
76         unsafe {
77             let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
78             if decltype.is_null() {
79                 None
80             } else {
81                 Some(CStr::from_ptr(decltype))
82             }
83         }
84     }
85 
86     #[inline]
column_name(&self, idx: usize) -> Option<&CStr>87     pub fn column_name(&self, idx: usize) -> Option<&CStr> {
88         let idx = idx as c_int;
89         if idx < 0 || idx >= self.column_count() as c_int {
90             return None;
91         }
92         unsafe {
93             let ptr = ffi::sqlite3_column_name(self.ptr, idx);
94             // If ptr is null here, it's an OOM, so there's probably nothing
95             // meaningful we can do. Just assert instead of returning None.
96             assert!(
97                 !ptr.is_null(),
98                 "Null pointer from sqlite3_column_name: Out of memory?"
99             );
100             Some(CStr::from_ptr(ptr))
101         }
102     }
103 
104     #[inline]
105     #[cfg(not(feature = "unlock_notify"))]
step(&self) -> c_int106     pub fn step(&self) -> c_int {
107         unsafe { ffi::sqlite3_step(self.ptr) }
108     }
109 
110     #[cfg(feature = "unlock_notify")]
step(&self) -> c_int111     pub fn step(&self) -> c_int {
112         use crate::unlock_notify;
113         let mut db = core::ptr::null_mut::<ffi::sqlite3>();
114         loop {
115             unsafe {
116                 let mut rc = ffi::sqlite3_step(self.ptr);
117                 // Bail out early for success and errors unrelated to locking. We
118                 // still need check `is_locked` after this, but checking now lets us
119                 // avoid one or two (admittedly cheap) calls into SQLite that we
120                 // don't need to make.
121                 if (rc & 0xff) != ffi::SQLITE_LOCKED {
122                     break rc;
123                 }
124                 if db.is_null() {
125                     db = ffi::sqlite3_db_handle(self.ptr);
126                 }
127                 if !unlock_notify::is_locked(db, rc) {
128                     break rc;
129                 }
130                 rc = unlock_notify::wait_for_unlock_notify(db);
131                 if rc != ffi::SQLITE_OK {
132                     break rc;
133                 }
134                 self.reset();
135             }
136         }
137     }
138 
139     #[inline]
reset(&self) -> c_int140     pub fn reset(&self) -> c_int {
141         unsafe { ffi::sqlite3_reset(self.ptr) }
142     }
143 
144     #[inline]
bind_parameter_count(&self) -> usize145     pub fn bind_parameter_count(&self) -> usize {
146         unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
147     }
148 
149     #[inline]
bind_parameter_index(&self, name: &str) -> Option<usize>150     pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
151         self.cache.get_or_insert_with(name, |param_cstr| {
152             let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
153             match r {
154                 0 => None,
155                 i => Some(i as usize),
156             }
157         })
158     }
159 
160     #[inline]
bind_parameter_name(&self, index: i32) -> Option<&CStr>161     pub fn bind_parameter_name(&self, index: i32) -> Option<&CStr> {
162         unsafe {
163             let name = ffi::sqlite3_bind_parameter_name(self.ptr, index);
164             if name.is_null() {
165                 None
166             } else {
167                 Some(CStr::from_ptr(name))
168             }
169         }
170     }
171 
172     #[inline]
clear_bindings(&self) -> c_int173     pub fn clear_bindings(&self) -> c_int {
174         unsafe { ffi::sqlite3_clear_bindings(self.ptr) }
175     }
176 
177     #[inline]
sql(&self) -> Option<&CStr>178     pub fn sql(&self) -> Option<&CStr> {
179         if self.ptr.is_null() {
180             None
181         } else {
182             Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
183         }
184     }
185 
186     #[inline]
finalize(mut self) -> c_int187     pub fn finalize(mut self) -> c_int {
188         self.finalize_()
189     }
190 
191     #[inline]
finalize_(&mut self) -> c_int192     fn finalize_(&mut self) -> c_int {
193         let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
194         self.ptr = ptr::null_mut();
195         r
196     }
197 
198     // does not work for PRAGMA
199     #[inline]
200     #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4
readonly(&self) -> bool201     pub fn readonly(&self) -> bool {
202         unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
203     }
204 
205     #[inline]
206     #[cfg(feature = "modern_sqlite")] // 3.14.0
expanded_sql(&self) -> Option<SqliteMallocString>207     pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
208         unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
209     }
210 
211     #[inline]
get_status(&self, status: StatementStatus, reset: bool) -> i32212     pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
213         assert!(!self.ptr.is_null());
214         unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
215     }
216 
217     #[inline]
218     #[cfg(feature = "extra_check")]
has_tail(&self) -> bool219     pub fn has_tail(&self) -> bool {
220         self.tail != 0
221     }
222 
223     #[inline]
tail(&self) -> usize224     pub fn tail(&self) -> usize {
225         self.tail
226     }
227 }
228 
229 impl Drop for RawStatement {
drop(&mut self)230     fn drop(&mut self) {
231         self.finalize_();
232     }
233 }
234