1use crate::sqlite3_connector as ffi;
2use crate::{Cursor, Result, Type, Value};
3
4use std::marker::PhantomData;
5
6pub struct Statement {
8 raw: (ffi::Sqlite3StmtHandle, ffi::Sqlite3DbHandle),
9 phantom: PhantomData<(ffi::Sqlite3StmtHandle, ffi::Sqlite3DbHandle)>,
10}
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum State {
15 Row,
17 Done,
19}
20
21pub trait Bindable {
23 fn bind(self, _: &mut Statement, _: usize) -> Result<()>;
27}
28
29pub trait Readable: Sized {
31 fn read(_: &Statement, _: usize) -> Result<Self>;
35}
36
37impl Statement {
38 #[inline]
42 pub fn bind<T: Bindable>(&mut self, i: usize, value: T) -> Result<()> {
43 value.bind(self, i)
44 }
45
46 #[inline]
48 pub fn count(&self) -> usize {
49 unsafe { ffi::sqlite3_column_count(self.raw.0) as usize }
50 }
51
52 pub fn kind(&self, i: usize) -> Type {
56 debug_assert!(i < self.count(), "the index is out of range");
57 match unsafe { ffi::sqlite3_column_type(self.raw.0, i as _) } {
58 ffi::SQLITE_BLOB => Type::Binary,
59 ffi::SQLITE_FLOAT => Type::Float,
60 ffi::SQLITE_INTEGER => Type::Integer,
61 ffi::SQLITE_TEXT => Type::String,
62 ffi::SQLITE_NULL => Type::Null,
63 _ => unreachable!(),
64 }
65 }
66
67 #[inline]
69 pub fn name(&self, i: usize) -> String {
70 debug_assert!(i < self.count(), "the index is out of range");
71 unsafe { ffi::sqlite3_column_name(self.raw.0, i as _) }
72 }
73
74 #[inline]
76 pub fn names(&self) -> Vec<String> {
77 (0..self.count()).map(|i| self.name(i)).collect()
78 }
79
80 pub fn next(&mut self) -> Result<State> {
85 Ok(match unsafe { ffi::sqlite3_step(self.raw.0) } {
86 ffi::SQLITE_ROW => State::Row,
87 ffi::SQLITE_DONE => State::Done,
88 code => error!(self.raw.1, code),
89 })
90 }
91
92 #[inline]
96 pub fn read<T: Readable>(&self, i: usize) -> Result<T> {
97 debug_assert!(i < self.count(), "the index is out of range");
98 Readable::read(self, i)
99 }
100
101 #[inline]
103 pub fn reset(&mut self) -> Result<()> {
104 unsafe { ok_raw!(self.raw.1, ffi::sqlite3_reset(self.raw.0)) };
105 Ok(())
106 }
107
108 #[inline]
110 pub fn cursor(self) -> Cursor {
111 crate::cursor::new(self)
112 }
113
114 #[inline]
116 pub fn as_raw(&self) -> ffi::Sqlite3StmtHandle {
117 self.raw.0
118 }
119}
120
121impl<'l> Drop for Statement {
122 #[inline]
123 fn drop(&mut self) {
124 unsafe { ffi::sqlite3_finalize(self.raw.0) };
125 }
126}
127
128impl Bindable for &Value {
129 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
130 match self {
131 Value::Binary(value) => value.bind(statement, i),
132 &Value::Float(value) => value.bind(statement, i),
133 &Value::Integer(value) => value.bind(statement, i),
134 Value::String(value) => value.as_str().bind(statement, i),
135 Value::Null => ().bind(statement, i),
136 }
137 }
138}
139
140impl Bindable for &Vec<u8> {
141 #[inline]
142 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
143 debug_assert!(i > 0, "the indexing starts from 1");
144 unsafe {
145 ok_raw!(
146 statement.raw.1,
147 ffi::sqlite3_bind_blob(statement.raw.0, i as _, self.into(), 0)
148 );
149 }
150 Ok(())
151 }
152}
153
154impl Bindable for f64 {
155 #[inline]
156 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
157 debug_assert!(i > 0, "the indexing starts from 1");
158 unsafe {
159 ok_raw!(
160 statement.raw.1,
161 ffi::sqlite3_bind_double(statement.raw.0, i as i32, self)
162 );
163 }
164 Ok(())
165 }
166}
167
168impl Bindable for i64 {
169 #[inline]
170 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
171 debug_assert!(i > 0, "the indexing starts from 1");
172 unsafe {
173 ok_raw!(
174 statement.raw.1,
175 ffi::sqlite3_bind_int64(statement.raw.0, i as i32, self as _)
176 );
177 }
178 Ok(())
179 }
180}
181
182impl<'l> Bindable for &'l str {
183 #[inline]
184 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
185 debug_assert!(i > 0, "the indexing starts from 1");
186 unsafe {
187 ok_raw!(
188 statement.raw.1,
189 ffi::sqlite3_bind_text(statement.raw.0, i as i32, self.into(), 0)
190 );
191 }
192 Ok(())
193 }
194}
195
196impl Bindable for () {
197 #[inline]
198 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
199 debug_assert!(i > 0, "the indexing starts from 1");
200 unsafe {
201 ok_raw!(
202 statement.raw.1,
203 ffi::sqlite3_bind_null(statement.raw.0, i as i32)
204 );
205 }
206 Ok(())
207 }
208}
209
210impl<T: Bindable> Bindable for Option<T> {
211 #[inline]
212 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
213 debug_assert!(i > 0, "the indexing starts from 1");
214 match self {
215 Some(inner) => inner.bind(statement, i),
216 None => ().bind(statement, i),
217 }
218 }
219}
220
221impl Readable for Value {
222 fn read(statement: &Statement, i: usize) -> Result<Self> {
223 Ok(match statement.kind(i) {
224 Type::Binary => Value::Binary(Readable::read(statement, i)?),
225 Type::Float => Value::Float(Readable::read(statement, i)?),
226 Type::Integer => Value::Integer(Readable::read(statement, i)?),
227 Type::String => Value::String(Readable::read(statement, i)?),
228 Type::Null => Value::Null,
229 })
230 }
231}
232
233impl Readable for f64 {
234 #[inline]
235 fn read(statement: &Statement, i: usize) -> Result<Self> {
236 Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as _) })
237 }
238}
239
240impl Readable for i64 {
241 #[inline]
242 fn read(statement: &Statement, i: usize) -> Result<Self> {
243 Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as _) })
244 }
245}
246
247impl Readable for String {
248 #[inline]
249 fn read(statement: &Statement, i: usize) -> Result<Self> {
250 unsafe { Ok(ffi::sqlite3_column_text(statement.raw.0, i as _)) }
251 }
252}
253
254impl Readable for Vec<u8> {
255 #[inline]
256 fn read(statement: &Statement, i: usize) -> Result<Self> {
257 unsafe { Ok(ffi::sqlite3_column_blob(statement.raw.0, i as i32)) }
258 }
259}
260
261impl<T: Readable> Readable for Option<T> {
262 #[inline]
263 fn read(statement: &Statement, i: usize) -> Result<Self> {
264 if statement.kind(i) == Type::Null {
265 Ok(None)
266 } else {
267 T::read(statement, i).map(Some)
268 }
269 }
270}
271
272#[inline]
273pub fn new<T: AsRef<str>>(raw1: ffi::Sqlite3DbHandle, statement: T) -> Result<Statement> {
274 let result = unsafe { ffi::sqlite3_prepare_v2(raw1, statement.as_ref().into()) };
275 if result.ret_code != ffi::SQLITE_OK {
276 error!(raw1, result.ret_code)
277 }
278
279 Ok(Statement {
280 raw: (result.stmt_handle, raw1),
281 phantom: PhantomData,
282 })
283}