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