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    #[cfg(not(feature = "unlock_notify"))]
146    pub fn step(&self) -> c_int {
147        unsafe { ffi::sqlite3_step(self.ptr) }
148    }
149
150    #[cfg(feature = "unlock_notify")]
151    pub fn step(&self) -> c_int {
152        use crate::unlock_notify;
153        let mut db = ptr::null_mut::<ffi::sqlite3>();
154        loop {
155            unsafe {
156                let mut rc = ffi::sqlite3_step(self.ptr);
157                // Bail out early for success and errors unrelated to locking. We
158                // still need check `is_locked` after this, but checking now lets us
159                // avoid one or two (admittedly cheap) calls into SQLite that we
160                // don't need to make.
161                if (rc & 0xff) != ffi::SQLITE_LOCKED {
162                    break rc;
163                }
164                if db.is_null() {
165                    db = ffi::sqlite3_db_handle(self.ptr);
166                }
167                if !unlock_notify::is_locked(db, rc) {
168                    break rc;
169                }
170                rc = unlock_notify::wait_for_unlock_notify(db);
171                if rc != ffi::SQLITE_OK {
172                    break rc;
173                }
174                self.reset();
175            }
176        }
177    }
178
179    #[inline]
180    pub fn reset(&self) -> c_int {
181        unsafe { ffi::sqlite3_reset(self.ptr) }
182    }
183
184    #[inline]
185    pub fn bind_parameter_count(&self) -> usize {
186        unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
187    }
188
189    #[inline]
190    pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
191        self.cache.get_or_insert_with(name, |param_cstr| {
192            let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
193            match r {
194                0 => None,
195                i => Some(i as usize),
196            }
197        })
198    }
199
200    #[inline]
201    pub fn bind_parameter_name(&self, index: i32) -> Option<&CStr> {
202        unsafe {
203            let name = ffi::sqlite3_bind_parameter_name(self.ptr, index);
204            if name.is_null() {
205                None
206            } else {
207                Some(CStr::from_ptr(name))
208            }
209        }
210    }
211
212    #[inline]
213    pub fn clear_bindings(&mut self) {
214        unsafe {
215            ffi::sqlite3_clear_bindings(self.ptr);
216        } // rc is always SQLITE_OK
217    }
218
219    #[inline]
220    pub fn sql(&self) -> Option<&CStr> {
221        if self.ptr.is_null() {
222            None
223        } else {
224            Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
225        }
226    }
227
228    #[inline]
229    pub fn finalize(mut self) -> c_int {
230        self.finalize_()
231    }
232
233    #[inline]
234    fn finalize_(&mut self) -> c_int {
235        let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
236        self.ptr = ptr::null_mut();
237        r
238    }
239
240    // does not work for PRAGMA
241    #[inline]
242    pub fn readonly(&self) -> bool {
243        unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
244    }
245
246    #[inline]
247    pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
248        unsafe { expanded_sql(self.ptr) }
249    }
250
251    #[inline]
252    pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
253        unsafe { stmt_status(self.ptr, status, reset) }
254    }
255
256    #[inline]
257    pub fn is_explain(&self) -> i32 {
258        unsafe { ffi::sqlite3_stmt_isexplain(self.ptr) }
259    }
260
261    // TODO sqlite3_normalized_sql (https://sqlite.org/c3ref/expanded_sql.html) // 3.27.0 + SQLITE_ENABLE_NORMALIZE
262}
263
264#[inline]
265pub(crate) unsafe fn expanded_sql(ptr: *mut ffi::sqlite3_stmt) -> Option<SqliteMallocString> {
266    SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(ptr))
267}
268#[inline]
269pub(crate) unsafe fn stmt_status(
270    ptr: *mut ffi::sqlite3_stmt,
271    status: StatementStatus,
272    reset: bool,
273) -> i32 {
274    assert!(!ptr.is_null());
275    ffi::sqlite3_stmt_status(ptr, status as i32, reset as i32)
276}
277
278impl Drop for RawStatement {
279    fn drop(&mut self) {
280        self.finalize_();
281    }
282}