rusqlite/
raw_statement.rs

1use super::ffi;
2use super::unlock_notify;
3#[cfg(not(any(
4    feature = "loadable_extension",
5    feature = "loadable_extension_embedded"
6)))]
7use super::StatementStatus;
8#[cfg(feature = "modern_sqlite")]
9use crate::util::SqliteMallocString;
10use std::ffi::CStr;
11use std::os::raw::c_int;
12use std::ptr;
13use std::sync::Arc;
14
15// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
16#[derive(Debug)]
17pub struct RawStatement {
18    ptr: *mut ffi::sqlite3_stmt,
19    tail: usize,
20    // Cached indices of named parameters, computed on the fly.
21    cache: crate::util::ParamIndexCache,
22    // Cached SQL (trimmed) that we use as the key when we're in the statement
23    // cache. This is None for statements which didn't come from the statement
24    // cache.
25    //
26    // This is probably the same as `self.sql()` in most cases, but we don't
27    // care either way -- It's a better cache key as it is anyway since it's the
28    // actual source we got from rust.
29    //
30    // One example of a case where the result of `sqlite_sql` and the value in
31    // `statement_cache_key` might differ is if the statement has a `tail`.
32    statement_cache_key: Option<Arc<str>>,
33}
34
35impl RawStatement {
36    #[inline]
37    pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement {
38        RawStatement {
39            ptr: stmt,
40            tail,
41            cache: Default::default(),
42            statement_cache_key: None,
43        }
44    }
45
46    #[inline]
47    pub fn is_null(&self) -> bool {
48        self.ptr.is_null()
49    }
50
51    #[inline]
52    pub(crate) fn set_statement_cache_key(&mut self, p: impl Into<Arc<str>>) {
53        self.statement_cache_key = Some(p.into());
54    }
55
56    #[inline]
57    pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> {
58        self.statement_cache_key.clone()
59    }
60
61    #[inline]
62    pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt {
63        self.ptr
64    }
65
66    #[inline]
67    pub fn column_count(&self) -> usize {
68        // Note: Can't cache this as it changes if the schema is altered.
69        unsafe { ffi::sqlite3_column_count(self.ptr) as usize }
70    }
71
72    #[inline]
73    pub fn column_type(&self, idx: usize) -> c_int {
74        unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
75    }
76
77    #[inline]
78    #[cfg(feature = "column_decltype")]
79    pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
80        unsafe {
81            let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
82            if decltype.is_null() {
83                None
84            } else {
85                Some(CStr::from_ptr(decltype))
86            }
87        }
88    }
89
90    #[inline]
91    pub fn column_name(&self, idx: usize) -> Option<&CStr> {
92        let idx = idx as c_int;
93        if idx < 0 || idx >= self.column_count() as c_int {
94            return None;
95        }
96        unsafe {
97            let ptr = ffi::sqlite3_column_name(self.ptr, idx);
98            // If ptr is null here, it's an OOM, so there's probably nothing
99            // meaningful we can do. Just assert instead of returning None.
100            assert!(
101                !ptr.is_null(),
102                "Null pointer from sqlite3_column_name: Out of memory?"
103            );
104            Some(CStr::from_ptr(ptr))
105        }
106    }
107
108    #[cfg_attr(not(feature = "unlock_notify"), inline)]
109    pub fn step(&self) -> c_int {
110        if cfg!(feature = "unlock_notify") {
111            let db = unsafe { ffi::sqlite3_db_handle(self.ptr) };
112            let mut rc;
113            loop {
114                rc = unsafe { ffi::sqlite3_step(self.ptr) };
115                if unsafe { !unlock_notify::is_locked(db, rc) } {
116                    break;
117                }
118                rc = unsafe { unlock_notify::wait_for_unlock_notify(db) };
119                if rc != ffi::SQLITE_OK {
120                    break;
121                }
122                self.reset();
123            }
124            rc
125        } else {
126            unsafe { ffi::sqlite3_step(self.ptr) }
127        }
128    }
129
130    #[inline]
131    pub fn reset(&self) -> c_int {
132        unsafe { ffi::sqlite3_reset(self.ptr) }
133    }
134
135    #[inline]
136    pub fn bind_parameter_count(&self) -> usize {
137        unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize }
138    }
139
140    #[inline]
141    pub fn bind_parameter_index(&self, name: &str) -> Option<usize> {
142        self.cache.get_or_insert_with(name, |param_cstr| {
143            let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) };
144            match r {
145                0 => None,
146                i => Some(i as usize),
147            }
148        })
149    }
150
151    #[inline]
152    pub fn clear_bindings(&self) -> c_int {
153        unsafe { ffi::sqlite3_clear_bindings(self.ptr) }
154    }
155
156    #[inline]
157    pub fn sql(&self) -> Option<&CStr> {
158        if self.ptr.is_null() {
159            None
160        } else {
161            Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) })
162        }
163    }
164
165    #[inline]
166    pub fn finalize(mut self) -> c_int {
167        self.finalize_()
168    }
169
170    #[inline]
171    fn finalize_(&mut self) -> c_int {
172        let r = unsafe { ffi::sqlite3_finalize(self.ptr) };
173        self.ptr = ptr::null_mut();
174        r
175    }
176
177    #[inline]
178    #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4
179    pub fn readonly(&self) -> bool {
180        unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
181    }
182
183    #[inline]
184    #[cfg(feature = "modern_sqlite")] // 3.14.0
185    pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> {
186        unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) }
187    }
188
189    #[cfg(not(any(
190        feature = "loadable_extension",
191        feature = "loadable_extension_embedded"
192    )))]
193    #[inline]
194    pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
195        assert!(!self.ptr.is_null());
196        unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
197    }
198
199    #[inline]
200    #[cfg(feature = "extra_check")]
201    pub fn has_tail(&self) -> bool {
202        self.tail != 0
203    }
204
205    #[inline]
206    pub fn tail(&self) -> usize {
207        self.tail
208    }
209}
210
211impl Drop for RawStatement {
212    fn drop(&mut self) {
213        self.finalize_();
214    }
215}