1 use super::ffi; 2 use super::StatementStatus; 3 use crate::util::ParamIndexCache; 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: usize, 16 // Cached indices of named parameters, computed on the fly. 17 cache: 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 { 32 #[inline] new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement33 pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement { 34 RawStatement { 35 ptr: stmt, 36 tail, 37 cache: ParamIndexCache::default(), 38 statement_cache_key: None, 39 } 40 } 41 42 #[inline] is_null(&self) -> bool43 pub fn is_null(&self) -> bool { 44 self.ptr.is_null() 45 } 46 47 #[inline] set_statement_cache_key(&mut self, p: impl Into<Arc<str>>)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] statement_cache_key(&self) -> Option<Arc<str>>53 pub(crate) fn statement_cache_key(&self) -> Option<Arc<str>> { 54 self.statement_cache_key.clone() 55 } 56 57 #[inline] ptr(&self) -> *mut ffi::sqlite3_stmt58 pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { 59 self.ptr 60 } 61 62 #[inline] column_count(&self) -> usize63 pub fn column_count(&self) -> usize { 64 // Note: Can't cache this as it changes if the schema is altered. 65 unsafe { ffi::sqlite3_column_count(self.ptr) as usize } 66 } 67 68 #[inline] column_type(&self, idx: usize) -> c_int69 pub fn column_type(&self, idx: usize) -> c_int { 70 unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) } 71 } 72 73 #[inline] 74 #[cfg(feature = "column_decltype")] column_decltype(&self, idx: usize) -> Option<&CStr>75 pub fn column_decltype(&self, idx: usize) -> Option<&CStr> { 76 unsafe { 77 let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int); 78 if decltype.is_null() { 79 None 80 } else { 81 Some(CStr::from_ptr(decltype)) 82 } 83 } 84 } 85 86 #[inline] column_name(&self, idx: usize) -> Option<&CStr>87 pub fn column_name(&self, idx: usize) -> Option<&CStr> { 88 let idx = idx as c_int; 89 if idx < 0 || idx >= self.column_count() as c_int { 90 return None; 91 } 92 unsafe { 93 let ptr = ffi::sqlite3_column_name(self.ptr, idx); 94 // If ptr is null here, it's an OOM, so there's probably nothing 95 // meaningful we can do. Just assert instead of returning None. 96 assert!( 97 !ptr.is_null(), 98 "Null pointer from sqlite3_column_name: Out of memory?" 99 ); 100 Some(CStr::from_ptr(ptr)) 101 } 102 } 103 104 #[inline] 105 #[cfg(not(feature = "unlock_notify"))] step(&self) -> c_int106 pub fn step(&self) -> c_int { 107 unsafe { ffi::sqlite3_step(self.ptr) } 108 } 109 110 #[cfg(feature = "unlock_notify")] step(&self) -> c_int111 pub fn step(&self) -> c_int { 112 use crate::unlock_notify; 113 let mut db = core::ptr::null_mut::<ffi::sqlite3>(); 114 loop { 115 unsafe { 116 let mut rc = ffi::sqlite3_step(self.ptr); 117 // Bail out early for success and errors unrelated to locking. We 118 // still need check `is_locked` after this, but checking now lets us 119 // avoid one or two (admittedly cheap) calls into SQLite that we 120 // don't need to make. 121 if (rc & 0xff) != ffi::SQLITE_LOCKED { 122 break rc; 123 } 124 if db.is_null() { 125 db = ffi::sqlite3_db_handle(self.ptr); 126 } 127 if !unlock_notify::is_locked(db, rc) { 128 break rc; 129 } 130 rc = unlock_notify::wait_for_unlock_notify(db); 131 if rc != ffi::SQLITE_OK { 132 break rc; 133 } 134 self.reset(); 135 } 136 } 137 } 138 139 #[inline] reset(&self) -> c_int140 pub fn reset(&self) -> c_int { 141 unsafe { ffi::sqlite3_reset(self.ptr) } 142 } 143 144 #[inline] bind_parameter_count(&self) -> usize145 pub fn bind_parameter_count(&self) -> usize { 146 unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize } 147 } 148 149 #[inline] bind_parameter_index(&self, name: &str) -> Option<usize>150 pub fn bind_parameter_index(&self, name: &str) -> Option<usize> { 151 self.cache.get_or_insert_with(name, |param_cstr| { 152 let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) }; 153 match r { 154 0 => None, 155 i => Some(i as usize), 156 } 157 }) 158 } 159 160 #[inline] bind_parameter_name(&self, index: i32) -> Option<&CStr>161 pub fn bind_parameter_name(&self, index: i32) -> Option<&CStr> { 162 unsafe { 163 let name = ffi::sqlite3_bind_parameter_name(self.ptr, index); 164 if name.is_null() { 165 None 166 } else { 167 Some(CStr::from_ptr(name)) 168 } 169 } 170 } 171 172 #[inline] clear_bindings(&self) -> c_int173 pub fn clear_bindings(&self) -> c_int { 174 unsafe { ffi::sqlite3_clear_bindings(self.ptr) } 175 } 176 177 #[inline] sql(&self) -> Option<&CStr>178 pub fn sql(&self) -> Option<&CStr> { 179 if self.ptr.is_null() { 180 None 181 } else { 182 Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) }) 183 } 184 } 185 186 #[inline] finalize(mut self) -> c_int187 pub fn finalize(mut self) -> c_int { 188 self.finalize_() 189 } 190 191 #[inline] finalize_(&mut self) -> c_int192 fn finalize_(&mut self) -> c_int { 193 let r = unsafe { ffi::sqlite3_finalize(self.ptr) }; 194 self.ptr = ptr::null_mut(); 195 r 196 } 197 198 // does not work for PRAGMA 199 #[inline] 200 #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4 readonly(&self) -> bool201 pub fn readonly(&self) -> bool { 202 unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 } 203 } 204 205 #[inline] 206 #[cfg(feature = "modern_sqlite")] // 3.14.0 expanded_sql(&self) -> Option<SqliteMallocString>207 pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> { 208 unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) } 209 } 210 211 #[inline] get_status(&self, status: StatementStatus, reset: bool) -> i32212 pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 { 213 assert!(!self.ptr.is_null()); 214 unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) } 215 } 216 217 #[inline] 218 #[cfg(feature = "extra_check")] has_tail(&self) -> bool219 pub fn has_tail(&self) -> bool { 220 self.tail != 0 221 } 222 223 #[inline] tail(&self) -> usize224 pub fn tail(&self) -> usize { 225 self.tail 226 } 227 } 228 229 impl Drop for RawStatement { drop(&mut self)230 fn drop(&mut self) { 231 self.finalize_(); 232 } 233 } 234