1use libsqlite3_sys::{self as ffi};
2use std::ptr;
3
4use super::{
5 DataType, DatabaseError, StepResult,
6 database::ffi_db,
7 error::{BindError, DecodeError, PrepareError, ResetError, StepError},
8};
9use crate::common::SqliteStr;
10
11macro_rules! ffi_stmt {
12 (@ $method:ident($db:expr, $stmt:expr $(, $($args:expr),*)?), $into:ty $(, $ret:expr)?) => {
13 match {
14 let db = $db;
15 let result = unsafe { libsqlite3_sys::$method($stmt $(, $($args),*)?) };
16 (db,result)
17 } {
18 (_, libsqlite3_sys::SQLITE_OK) => Ok({ $($ret)? }),
19 (db, result) => Err(<$into>::from(super::DatabaseError::from_code(result, db))),
20 }
21 };
22 ($method:ident($db:expr, $stmt:expr $(, $($args:expr),*)?) as _ $(, $ret:expr)?) => {
23 super::statement::ffi_stmt!(@ $method($db, $stmt $(, $($args),*)?), super::DatabaseError $(, $ret)?)
24 };
25 ($method:ident($db:expr, $stmt:expr $(, $($args:expr),*)?) $(, $ret:expr)?) => {
26 super::statement::ffi_stmt!(@ $method($db, $stmt $(, $($args),*)?), _ $(, $ret)?)
27 };
28}
29
30pub(super) use ffi_stmt;
31
32pub fn prepare_v2<S: SqliteStr>(db: *mut ffi::sqlite3, sql: S) -> Result<*mut ffi::sqlite3_stmt, PrepareError> {
46 let mut stmt = ptr::null_mut();
47 let (ptr, len, _) = sql.as_nulstr();
48 match ffi_db!(sqlite3_prepare_v2(db, ptr, len, &mut stmt, ptr::null_mut())) {
49 Ok(()) => {
50 #[cfg(feature = "log")]
51 log::debug!("prepared {sql:?}");
52 Ok(stmt)
53 },
54 Err(err) => Err(err),
55 }
56}
57
58pub trait Statement {
64 fn as_stmt_ptr(&self) -> *mut ffi::sqlite3_stmt;
65}
66
67impl<S> Statement for &S where S: Statement {
68 fn as_stmt_ptr(&self) -> *mut ffi::sqlite3_stmt {
69 S::as_stmt_ptr(self)
70 }
71}
72
73impl Statement for *mut ffi::sqlite3_stmt {
74 fn as_stmt_ptr(&self) -> *mut ffi::sqlite3_stmt {
75 *self
76 }
77}
78
79impl<T> StatementExt for T where T: Statement { }
80
81pub trait StatementExt: Statement {
83 fn as_db_ptr(&self) -> *mut ffi::sqlite3 {
85 unsafe { ffi::sqlite3_db_handle(self.as_stmt_ptr()) }
86 }
87
88 fn step(&self) -> Result<StepResult, StepError> {
89 match unsafe { ffi::sqlite3_step(self.as_stmt_ptr()) } {
90 ffi::SQLITE_ROW => Ok(StepResult::Row),
91 ffi::SQLITE_DONE => Ok(StepResult::Done),
92 result => Err(DatabaseError::from_code(result, self.as_db_ptr()).into()),
93 }
94 }
95
96 fn reset(&self) -> Result<(), ResetError> {
97 ffi_stmt!(sqlite3_reset(self.as_db_ptr(), self.as_stmt_ptr()))
98 }
99
100 fn clear_bindings(&self) -> Result<(), ResetError> {
101 ffi_stmt!(sqlite3_clear_bindings(self.as_db_ptr(), self.as_stmt_ptr()))
102 }
103
104 fn bind_int(&self, idx: i32, value: i32) -> Result<(), BindError> {
110 ffi_stmt!(sqlite3_bind_int(self.as_db_ptr(), self.as_stmt_ptr(), idx, value))
111 }
112
113 fn bind_double(&self, idx: i32, value: f64) -> Result<(), BindError> {
117 ffi_stmt!(sqlite3_bind_double(self.as_db_ptr(), self.as_stmt_ptr(), idx, value))
118 }
119
120 fn bind_null(&self, idx: i32) -> Result<(), BindError> {
124 ffi_stmt!(sqlite3_bind_null(self.as_db_ptr(), self.as_stmt_ptr(), idx))
125 }
126
127 fn bind_text<S: SqliteStr>(&self, idx: i32, text: S) -> Result<(), BindError> {
133 let (ptr, len, dtor) = text.as_sqlite_str()?;
134 ffi_stmt!(sqlite3_bind_text(self.as_db_ptr(), self.as_stmt_ptr(), idx, ptr, len, dtor))
135 }
136
137 fn bind_blob(&self, idx: i32, data: &[u8]) -> Result<(), BindError> {
141 ffi_stmt!(sqlite3_bind_blob(
142 self.as_db_ptr(),
143 self.as_stmt_ptr(),
144 idx,
145 data.as_ptr().cast(),
146 i32::try_from(data.len()).unwrap_or(i32::MAX),
147 ffi::SQLITE_TRANSIENT()
148 ))
149 }
150
151 fn column_count(&self) -> i32 {
157 unsafe { ffi::sqlite3_column_count(self.as_stmt_ptr()) }
158 }
159
160 fn data_count(&self) -> i32 {
164 unsafe { ffi::sqlite3_data_count(self.as_stmt_ptr()) }
165 }
166
167 fn column_type(&self, idx: i32) -> DataType {
168 let code = unsafe { ffi::sqlite3_column_type(self.as_stmt_ptr(), idx) };
169 DataType::from_code(code).expect("sqlite return non datatype from `sqlite3_column_type`")
170 }
171
172 fn column_int(&self, idx: i32) -> i32 {
173 unsafe { ffi::sqlite3_column_int(self.as_stmt_ptr(), idx) }
174 }
175
176 fn column_double(&self, idx: i32) -> f64 {
177 unsafe { ffi::sqlite3_column_double(self.as_stmt_ptr(), idx) }
178 }
179
180 fn column_text(&self, idx: i32) -> Result<&str, DecodeError> {
181 let text = unsafe {
182 let text = ffi::sqlite3_column_text(self.as_stmt_ptr(), idx);
183 std::ffi::CStr::from_ptr(text.cast())
184 };
185 text.to_str().map_err(DecodeError::Utf8)
186 }
187
188 fn column_blob(&self, idx: i32) -> &[u8] {
189 unsafe {
190 let len = self.column_bytes(idx) as usize;
191 let data = ffi::sqlite3_column_blob(self.as_stmt_ptr(), idx).cast();
192 std::slice::from_raw_parts(data, len)
193 }
194 }
195
196 fn column_bytes(&self, idx: i32) -> i32 {
197 unsafe { ffi::sqlite3_column_bytes(self.as_stmt_ptr(), idx) }
198 }
199
200 fn finalize(&self) -> Result<(), DatabaseError> {
204 ffi_stmt!(sqlite3_finalize(self.as_db_ptr(), self.as_stmt_ptr()) as _)
205 }
206}
207