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: usize, 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: usize) -> RawStatement32 pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> 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 66 #[cfg(feature = "column_decltype")] column_decltype(&self, idx: usize) -> Option<&CStr>67 pub fn column_decltype(&self, idx: usize) -> Option<&CStr> { 68 unsafe { 69 let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int); 70 if decltype.is_null() { 71 None 72 } else { 73 Some(CStr::from_ptr(decltype)) 74 } 75 } 76 } 77 column_name(&self, idx: usize) -> Option<&CStr>78 pub fn column_name(&self, idx: usize) -> Option<&CStr> { 79 let idx = idx as c_int; 80 if idx < 0 || idx >= self.column_count() as c_int { 81 return None; 82 } 83 unsafe { 84 let ptr = ffi::sqlite3_column_name(self.ptr, idx); 85 // If ptr is null here, it's an OOM, so there's probably nothing 86 // meaningful we can do. Just assert instead of returning None. 87 assert!( 88 !ptr.is_null(), 89 "Null pointer from sqlite3_column_name: Out of memory?" 90 ); 91 Some(CStr::from_ptr(ptr)) 92 } 93 } 94 step(&self) -> c_int95 pub fn step(&self) -> c_int { 96 if cfg!(feature = "unlock_notify") { 97 let db = unsafe { ffi::sqlite3_db_handle(self.ptr) }; 98 let mut rc; 99 loop { 100 rc = unsafe { ffi::sqlite3_step(self.ptr) }; 101 if unsafe { !unlock_notify::is_locked(db, rc) } { 102 break; 103 } 104 rc = unsafe { unlock_notify::wait_for_unlock_notify(db) }; 105 if rc != ffi::SQLITE_OK { 106 break; 107 } 108 self.reset(); 109 } 110 rc 111 } else { 112 unsafe { ffi::sqlite3_step(self.ptr) } 113 } 114 } 115 reset(&self) -> c_int116 pub fn reset(&self) -> c_int { 117 unsafe { ffi::sqlite3_reset(self.ptr) } 118 } 119 bind_parameter_count(&self) -> usize120 pub fn bind_parameter_count(&self) -> usize { 121 unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize } 122 } 123 bind_parameter_index(&self, name: &str) -> Option<usize>124 pub fn bind_parameter_index(&self, name: &str) -> Option<usize> { 125 self.cache.get_or_insert_with(name, |param_cstr| { 126 let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) }; 127 match r { 128 0 => None, 129 i => Some(i as usize), 130 } 131 }) 132 } 133 clear_bindings(&self) -> c_int134 pub fn clear_bindings(&self) -> c_int { 135 unsafe { ffi::sqlite3_clear_bindings(self.ptr) } 136 } 137 sql(&self) -> Option<&CStr>138 pub fn sql(&self) -> Option<&CStr> { 139 if self.ptr.is_null() { 140 None 141 } else { 142 Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) }) 143 } 144 } 145 finalize(mut self) -> c_int146 pub fn finalize(mut self) -> c_int { 147 self.finalize_() 148 } 149 finalize_(&mut self) -> c_int150 fn finalize_(&mut self) -> c_int { 151 let r = unsafe { ffi::sqlite3_finalize(self.ptr) }; 152 self.ptr = ptr::null_mut(); 153 r 154 } 155 156 #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4 readonly(&self) -> bool157 pub fn readonly(&self) -> bool { 158 unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 } 159 } 160 161 #[cfg(feature = "modern_sqlite")] // 3.14.0 expanded_sql(&self) -> Option<SqliteMallocString>162 pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> { 163 unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) } 164 } 165 get_status(&self, status: StatementStatus, reset: bool) -> i32166 pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 { 167 assert!(!self.ptr.is_null()); 168 unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) } 169 } 170 171 #[cfg(feature = "extra_check")] has_tail(&self) -> bool172 pub fn has_tail(&self) -> bool { 173 self.tail != 0 174 } 175 tail(&self) -> usize176 pub fn tail(&self) -> usize { 177 self.tail 178 } 179 } 180 181 impl Drop for RawStatement { drop(&mut self)182 fn drop(&mut self) { 183 self.finalize_(); 184 } 185 } 186