Skip to main content

rusqlite/
raw_statement.rs

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