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