sqlx_sqlite/statement/
handle.rs

1use std::ffi::c_void;
2use std::ffi::CStr;
3
4use std::os::raw::{c_char, c_int};
5use std::ptr;
6use std::ptr::NonNull;
7use std::slice::from_raw_parts;
8use std::str::{from_utf8, from_utf8_unchecked};
9
10use libsqlite3_sys::{
11    sqlite3, sqlite3_bind_blob64, sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
12    sqlite3_bind_null, sqlite3_bind_parameter_count, sqlite3_bind_parameter_name,
13    sqlite3_bind_text64, sqlite3_changes, sqlite3_clear_bindings, sqlite3_column_blob,
14    sqlite3_column_bytes, sqlite3_column_count, sqlite3_column_database_name,
15    sqlite3_column_decltype, sqlite3_column_double, sqlite3_column_int, sqlite3_column_int64,
16    sqlite3_column_name, sqlite3_column_origin_name, sqlite3_column_table_name,
17    sqlite3_column_type, sqlite3_column_value, sqlite3_db_handle, sqlite3_finalize, sqlite3_reset,
18    sqlite3_sql, sqlite3_step, sqlite3_stmt, sqlite3_stmt_readonly, sqlite3_table_column_metadata,
19    sqlite3_value, SQLITE_DONE, SQLITE_LOCKED_SHAREDCACHE, SQLITE_MISUSE, SQLITE_OK, SQLITE_ROW,
20    SQLITE_TRANSIENT, SQLITE_UTF8,
21};
22
23use crate::error::{BoxDynError, Error};
24use crate::type_info::DataType;
25use crate::{SqliteError, SqliteTypeInfo};
26
27use super::unlock_notify;
28
29#[derive(Debug)]
30pub(crate) struct StatementHandle(NonNull<sqlite3_stmt>);
31
32// access to SQLite3 statement handles are safe to send and share between threads
33// as long as the `sqlite3_step` call is serialized.
34
35unsafe impl Send for StatementHandle {}
36
37macro_rules! expect_ret_valid {
38    ($fn_name:ident($($args:tt)*)) => {{
39        let val = $fn_name($($args)*);
40
41        TryFrom::try_from(val)
42            // This likely means UB in SQLite itself or our usage of it;
43            // signed integer overflow is UB in the C standard.
44            .unwrap_or_else(|_| panic!("{}() returned invalid value: {val:?}", stringify!($fn_name)))
45    }}
46}
47
48macro_rules! check_col_idx {
49    ($idx:ident) => {
50        c_int::try_from($idx).unwrap_or_else(|_| panic!("invalid column index: {}", $idx))
51    };
52}
53
54// might use some of this later
55#[allow(dead_code)]
56impl StatementHandle {
57    pub(super) fn new(ptr: NonNull<sqlite3_stmt>) -> Self {
58        Self(ptr)
59    }
60
61    #[inline]
62    pub(super) unsafe fn db_handle(&self) -> *mut sqlite3 {
63        // O(c) access to the connection handle for this statement handle
64        // https://sqlite.org/c3ref/db_handle.html
65        sqlite3_db_handle(self.0.as_ptr())
66    }
67
68    pub(crate) fn read_only(&self) -> bool {
69        // https://sqlite.org/c3ref/stmt_readonly.html
70        unsafe { sqlite3_stmt_readonly(self.0.as_ptr()) != 0 }
71    }
72
73    pub(crate) fn sql(&self) -> &str {
74        // https://sqlite.org/c3ref/expanded_sql.html
75        unsafe {
76            let raw = sqlite3_sql(self.0.as_ptr());
77            debug_assert!(!raw.is_null());
78
79            from_utf8_unchecked(CStr::from_ptr(raw).to_bytes())
80        }
81    }
82
83    #[inline]
84    pub(crate) fn last_error(&mut self) -> SqliteError {
85        unsafe { SqliteError::new(self.db_handle()) }
86    }
87
88    #[inline]
89    pub(crate) fn column_count(&self) -> usize {
90        // https://sqlite.org/c3ref/column_count.html
91        unsafe { expect_ret_valid!(sqlite3_column_count(self.0.as_ptr())) }
92    }
93
94    #[inline]
95    pub(crate) fn changes(&self) -> u64 {
96        // returns the number of changes of the *last* statement; not
97        // necessarily this statement.
98        // https://sqlite.org/c3ref/changes.html
99        unsafe { expect_ret_valid!(sqlite3_changes(self.db_handle())) }
100    }
101
102    #[inline]
103    pub(crate) fn column_name(&self, index: usize) -> &str {
104        // https://sqlite.org/c3ref/column_name.html
105        unsafe {
106            let name = sqlite3_column_name(self.0.as_ptr(), check_col_idx!(index));
107            debug_assert!(!name.is_null());
108
109            from_utf8_unchecked(CStr::from_ptr(name).to_bytes())
110        }
111    }
112
113    pub(crate) fn column_type_info(&self, index: usize) -> SqliteTypeInfo {
114        SqliteTypeInfo(DataType::from_code(self.column_type(index)))
115    }
116
117    pub(crate) fn column_type_info_opt(&self, index: usize) -> Option<SqliteTypeInfo> {
118        match DataType::from_code(self.column_type(index)) {
119            DataType::Null => None,
120            dt => Some(SqliteTypeInfo(dt)),
121        }
122    }
123
124    #[inline]
125    pub(crate) fn column_decltype(&self, index: usize) -> Option<SqliteTypeInfo> {
126        unsafe {
127            let decl = sqlite3_column_decltype(self.0.as_ptr(), check_col_idx!(index));
128            if decl.is_null() {
129                // If the Nth column of the result set is an expression or subquery,
130                // then a NULL pointer is returned.
131                return None;
132            }
133
134            let decl = from_utf8_unchecked(CStr::from_ptr(decl).to_bytes());
135            let ty: DataType = decl.parse().ok()?;
136
137            Some(SqliteTypeInfo(ty))
138        }
139    }
140
141    pub(crate) fn column_nullable(&self, index: usize) -> Result<Option<bool>, Error> {
142        unsafe {
143            let index = check_col_idx!(index);
144
145            // https://sqlite.org/c3ref/column_database_name.html
146            //
147            // ### Note
148            // The returned string is valid until the prepared statement is destroyed using
149            // sqlite3_finalize() or until the statement is automatically reprepared by the
150            // first call to sqlite3_step() for a particular run or until the same information
151            // is requested again in a different encoding.
152            let db_name = sqlite3_column_database_name(self.0.as_ptr(), index);
153            let table_name = sqlite3_column_table_name(self.0.as_ptr(), index);
154            let origin_name = sqlite3_column_origin_name(self.0.as_ptr(), index);
155
156            if db_name.is_null() || table_name.is_null() || origin_name.is_null() {
157                return Ok(None);
158            }
159
160            let mut not_null: c_int = 0;
161
162            // https://sqlite.org/c3ref/table_column_metadata.html
163            let status = sqlite3_table_column_metadata(
164                self.db_handle(),
165                db_name,
166                table_name,
167                origin_name,
168                // function docs state to provide NULL for return values you don't care about
169                ptr::null_mut(),
170                ptr::null_mut(),
171                &mut not_null,
172                ptr::null_mut(),
173                ptr::null_mut(),
174            );
175
176            if status != SQLITE_OK {
177                // implementation note: the docs for sqlite3_table_column_metadata() specify
178                // that an error can be returned if the column came from a view; however,
179                // experimentally we found that the above functions give us the true origin
180                // for columns in views that came from real tables and so we should never hit this
181                // error; for view columns that are expressions we are given NULL for their origins
182                // so we don't need special handling for that case either.
183                //
184                // this is confirmed in the `tests/sqlite-macros.rs` integration test
185                return Err(SqliteError::new(self.db_handle()).into());
186            }
187
188            Ok(Some(not_null == 0))
189        }
190    }
191
192    // Number Of SQL Parameters
193    #[inline]
194    pub(crate) fn bind_parameter_count(&self) -> usize {
195        // https://www.sqlite.org/c3ref/bind_parameter_count.html
196        unsafe { expect_ret_valid!(sqlite3_bind_parameter_count(self.0.as_ptr())) }
197    }
198
199    // Name Of A Host Parameter
200    // NOTE: The first host parameter has an index of 1, not 0.
201    #[inline]
202    pub(crate) fn bind_parameter_name(&self, index: usize) -> Option<&str> {
203        unsafe {
204            // https://www.sqlite.org/c3ref/bind_parameter_name.html
205            let name = sqlite3_bind_parameter_name(self.0.as_ptr(), check_col_idx!(index));
206            if name.is_null() {
207                return None;
208            }
209
210            Some(from_utf8_unchecked(CStr::from_ptr(name).to_bytes()))
211        }
212    }
213
214    // Binding Values To Prepared Statements
215    // https://www.sqlite.org/c3ref/bind_blob.html
216
217    #[inline]
218    pub(crate) fn bind_blob(&self, index: usize, v: &[u8]) -> c_int {
219        unsafe {
220            sqlite3_bind_blob64(
221                self.0.as_ptr(),
222                check_col_idx!(index),
223                v.as_ptr() as *const c_void,
224                v.len() as u64,
225                SQLITE_TRANSIENT(),
226            )
227        }
228    }
229
230    #[inline]
231    pub(crate) fn bind_text(&self, index: usize, v: &str) -> c_int {
232        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
233        let encoding = SQLITE_UTF8 as u8;
234
235        unsafe {
236            sqlite3_bind_text64(
237                self.0.as_ptr(),
238                check_col_idx!(index),
239                v.as_ptr() as *const c_char,
240                v.len() as u64,
241                SQLITE_TRANSIENT(),
242                encoding,
243            )
244        }
245    }
246
247    #[inline]
248    pub(crate) fn bind_int(&self, index: usize, v: i32) -> c_int {
249        unsafe { sqlite3_bind_int(self.0.as_ptr(), check_col_idx!(index), v as c_int) }
250    }
251
252    #[inline]
253    pub(crate) fn bind_int64(&self, index: usize, v: i64) -> c_int {
254        unsafe { sqlite3_bind_int64(self.0.as_ptr(), check_col_idx!(index), v) }
255    }
256
257    #[inline]
258    pub(crate) fn bind_double(&self, index: usize, v: f64) -> c_int {
259        unsafe { sqlite3_bind_double(self.0.as_ptr(), check_col_idx!(index), v) }
260    }
261
262    #[inline]
263    pub(crate) fn bind_null(&self, index: usize) -> c_int {
264        unsafe { sqlite3_bind_null(self.0.as_ptr(), check_col_idx!(index)) }
265    }
266
267    // result values from the query
268    // https://www.sqlite.org/c3ref/column_blob.html
269
270    #[inline]
271    pub(crate) fn column_type(&self, index: usize) -> c_int {
272        unsafe { sqlite3_column_type(self.0.as_ptr(), check_col_idx!(index)) }
273    }
274
275    #[inline]
276    pub(crate) fn column_int(&self, index: usize) -> i32 {
277        unsafe { sqlite3_column_int(self.0.as_ptr(), check_col_idx!(index)) as i32 }
278    }
279
280    #[inline]
281    pub(crate) fn column_int64(&self, index: usize) -> i64 {
282        unsafe { sqlite3_column_int64(self.0.as_ptr(), check_col_idx!(index)) as i64 }
283    }
284
285    #[inline]
286    pub(crate) fn column_double(&self, index: usize) -> f64 {
287        unsafe { sqlite3_column_double(self.0.as_ptr(), check_col_idx!(index)) }
288    }
289
290    #[inline]
291    pub(crate) fn column_value(&self, index: usize) -> *mut sqlite3_value {
292        unsafe { sqlite3_column_value(self.0.as_ptr(), check_col_idx!(index)) }
293    }
294
295    pub(crate) fn column_blob(&self, index: usize) -> &[u8] {
296        let len = unsafe {
297            expect_ret_valid!(sqlite3_column_bytes(self.0.as_ptr(), check_col_idx!(index)))
298        };
299
300        if len == 0 {
301            // empty blobs are NULL so just return an empty slice
302            return &[];
303        }
304
305        let ptr =
306            unsafe { sqlite3_column_blob(self.0.as_ptr(), check_col_idx!(index)) } as *const u8;
307        debug_assert!(!ptr.is_null());
308
309        unsafe { from_raw_parts(ptr, len) }
310    }
311
312    pub(crate) fn column_text(&self, index: usize) -> Result<&str, BoxDynError> {
313        Ok(from_utf8(self.column_blob(index))?)
314    }
315
316    pub(crate) fn clear_bindings(&self) {
317        unsafe { sqlite3_clear_bindings(self.0.as_ptr()) };
318    }
319
320    pub(crate) fn reset(&mut self) -> Result<(), SqliteError> {
321        // SAFETY: we have exclusive access to the handle
322        unsafe {
323            if sqlite3_reset(self.0.as_ptr()) != SQLITE_OK {
324                return Err(SqliteError::new(self.db_handle()));
325            }
326        }
327
328        Ok(())
329    }
330
331    pub(crate) fn step(&mut self) -> Result<bool, SqliteError> {
332        // SAFETY: we have exclusive access to the handle
333        unsafe {
334            loop {
335                match sqlite3_step(self.0.as_ptr()) {
336                    SQLITE_ROW => return Ok(true),
337                    SQLITE_DONE => return Ok(false),
338                    SQLITE_MISUSE => panic!("misuse!"),
339                    SQLITE_LOCKED_SHAREDCACHE => {
340                        // The shared cache is locked by another connection. Wait for unlock
341                        // notification and try again.
342                        unlock_notify::wait(self.db_handle())?;
343                        // Need to reset the handle after the unlock
344                        // (https://www.sqlite.org/unlock_notify.html)
345                        sqlite3_reset(self.0.as_ptr());
346                    }
347                    _ => return Err(SqliteError::new(self.db_handle())),
348                }
349            }
350        }
351    }
352}
353
354impl Drop for StatementHandle {
355    fn drop(&mut self) {
356        // SAFETY: we have exclusive access to the `StatementHandle` here
357        unsafe {
358            // https://sqlite.org/c3ref/finalize.html
359            let status = sqlite3_finalize(self.0.as_ptr());
360            if status == SQLITE_MISUSE {
361                // Panic in case of detected misuse of SQLite API.
362                //
363                // sqlite3_finalize returns it at least in the
364                // case of detected double free, i.e. calling
365                // sqlite3_finalize on already finalized
366                // statement.
367                panic!("Detected sqlite3_finalize misuse.");
368            }
369        }
370    }
371}