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