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
32unsafe 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 .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#[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 sqlite3_db_handle(self.0.as_ptr())
66 }
67
68 pub(crate) fn read_only(&self) -> bool {
69 unsafe { sqlite3_stmt_readonly(self.0.as_ptr()) != 0 }
71 }
72
73 pub(crate) fn sql(&self) -> &str {
74 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 unsafe { expect_ret_valid!(sqlite3_column_count(self.0.as_ptr())) }
92 }
93
94 #[inline]
95 pub(crate) fn changes(&self) -> u64 {
96 unsafe { expect_ret_valid!(sqlite3_changes(self.db_handle())) }
100 }
101
102 #[inline]
103 pub(crate) fn column_name(&self, index: usize) -> &str {
104 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 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 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 let status = sqlite3_table_column_metadata(
164 self.db_handle(),
165 db_name,
166 table_name,
167 origin_name,
168 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 return Err(SqliteError::new(self.db_handle()).into());
186 }
187
188 Ok(Some(not_null == 0))
189 }
190 }
191
192 #[inline]
194 pub(crate) fn bind_parameter_count(&self) -> usize {
195 unsafe { expect_ret_valid!(sqlite3_bind_parameter_count(self.0.as_ptr())) }
197 }
198
199 #[inline]
202 pub(crate) fn bind_parameter_name(&self, index: usize) -> Option<&str> {
203 unsafe {
204 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 #[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 #[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 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 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 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 unlock_notify::wait(self.db_handle())?;
343 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 unsafe {
358 let status = sqlite3_finalize(self.0.as_ptr());
360 if status == SQLITE_MISUSE {
361 panic!("Detected sqlite3_finalize misuse.");
368 }
369 }
370 }
371}