sqlite_ll/
readable.rs

1use core::ffi::c_int;
2use core::ptr;
3
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use sqlite3_sys as ffi;
8
9use crate::{Error, FixedBytes, Null, Result, Statement, Type, Value, Writable};
10
11mod sealed {
12    use alloc::string::String;
13    use alloc::vec::Vec;
14
15    use crate::{FixedBytes, Null, Value};
16
17    pub trait Sealed {}
18    impl Sealed for i64 {}
19    impl Sealed for f64 {}
20    impl Sealed for Null {}
21    impl Sealed for String {}
22    impl Sealed for Vec<u8> {}
23    impl<T> Sealed for Option<T> where T: Sealed {}
24    impl<const N: usize> Sealed for FixedBytes<N> {}
25    impl Sealed for Value {}
26}
27
28/// A type suitable for reading from a prepared statement.
29///
30/// Use with [`Statement::read`].
31pub trait Readable
32where
33    Self: self::sealed::Sealed + Sized,
34{
35    #[doc(hidden)]
36    fn read(stmt: &Statement, index: c_int) -> Result<Self>;
37}
38
39impl Readable for Value {
40    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
41        let value = match stmt.column_type(index) {
42            Type::BLOB => Value::blob(<_>::read(stmt, index)?),
43            Type::TEXT => Value::text(<_>::read(stmt, index)?),
44            Type::FLOAT => Value::float(<_>::read(stmt, index)?),
45            Type::INTEGER => Value::integer(<_>::read(stmt, index)?),
46            Type::NULL => Value::null(),
47            _ => return Err(Error::new(ffi::SQLITE_MISMATCH)),
48        };
49
50        Ok(value)
51    }
52}
53
54/// [`Readable`] implementation for `f64`.
55///
56/// # Examples
57///
58/// ```
59/// use sqlite_ll::{Connection, State};
60///
61/// let c = Connection::open_memory()?;
62///
63/// c.execute(r##"
64/// CREATE TABLE numbers (value REAL);
65/// INSERT INTO numbers (value) VALUES (3.14), (2.71);
66/// "##)?;
67///
68/// let mut stmt = c.prepare("SELECT value FROM numbers")?;
69///
70/// while let State::Row = stmt.step()? {
71///     let value = stmt.read::<f64>(0)?;
72///     assert!(matches!(value, 3.14 | 2.71));
73/// }
74/// # Ok::<_, sqlite_ll::Error>(())
75/// ```
76impl Readable for f64 {
77    #[inline]
78    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
79        Ok(unsafe { ffi::sqlite3_column_double(stmt.as_ptr(), index) })
80    }
81}
82
83/// [`Readable`] implementation for `i64`.
84///
85/// # Examples
86///
87/// ```
88/// use sqlite_ll::{Connection, State};
89///
90/// let c = Connection::open_memory()?;
91///
92/// c.execute(r##"
93/// CREATE TABLE numbers (value INTEGER);
94/// INSERT INTO numbers (value) VALUES (3), (2);
95/// "##)?;
96///
97/// let mut stmt = c.prepare("SELECT value FROM numbers")?;
98///
99/// while let State::Row = stmt.step()? {
100///     let value = stmt.read::<i64>(0)?;
101///     assert!(matches!(value, 3 | 2));
102/// }
103/// # Ok::<_, sqlite_ll::Error>(())
104/// ```
105impl Readable for i64 {
106    #[inline]
107    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
108        Ok(unsafe { ffi::sqlite3_column_int64(stmt.as_ptr(), index) })
109    }
110}
111
112/// [`Readable`] implementation which returns a newly allocated [`String`].
113///
114/// For a more memory-efficient way of reading bytes, consider using its
115/// [`Writable`] implementation instead.
116///
117/// # Examples
118///
119/// ```
120/// use sqlite_ll::{Connection, State};
121///
122/// let c = Connection::open_memory()?;
123///
124/// c.execute(r##"
125/// CREATE TABLE users (name TEXT);
126/// INSERT INTO users (name) VALUES ('Alice'), ('Bob');
127/// "##)?;
128///
129/// let mut stmt = c.prepare("SELECT name FROM users")?;
130///
131/// while let State::Row = stmt.step()? {
132///     let name = stmt.read::<String>(0)?;
133///     assert!(matches!(name.as_str(), "Alice" | "Bob"));
134/// }
135/// # Ok::<_, sqlite_ll::Error>(())
136/// ```
137///
138/// Automatic conversion:
139///
140/// ```
141/// use sqlite_ll::{Connection, State};
142///
143/// let c = Connection::open_memory()?;
144///
145/// c.execute(r##"
146/// CREATE TABLE users (id INTEGER);
147/// INSERT INTO users (id) VALUES (1), (2);
148/// "##)?;
149///
150/// let mut stmt = c.prepare("SELECT id FROM users")?;
151///
152/// while let State::Row = stmt.step()? {
153///     let name = stmt.read::<String>(0)?;
154///     assert!(matches!(name.as_str(), "1" | "2"));
155/// }
156/// # Ok::<_, sqlite_ll::Error>(())
157/// ```
158impl Readable for String {
159    #[inline]
160    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
161        let mut s = String::new();
162        s.write(stmt, index)?;
163        Ok(s)
164    }
165}
166
167/// [`Readable`] implementation which returns a newly allocated [`Vec`].
168///
169/// For a more memory-efficient way of reading bytes, consider using its
170/// [`Writable`] implementation instead.
171///
172/// # Examples
173///
174/// ```
175/// use sqlite_ll::{Connection, State};
176///
177/// let c = Connection::open_memory()?;
178///
179/// c.execute(r##"
180/// CREATE TABLE users (name TEXT);
181/// INSERT INTO users (name) VALUES ('Alice'), ('Bob');
182/// "##)?;
183///
184/// let mut stmt = c.prepare("SELECT name FROM users")?;
185///
186/// while let State::Row = stmt.step()? {
187///     let name = stmt.read::<Vec<u8>>(0)?;
188///     assert!(matches!(name.as_slice(), b"Alice" | b"Bob"));
189/// }
190/// # Ok::<_, sqlite_ll::Error>(())
191/// ```
192///
193/// Automatic conversion:
194///
195/// ```
196/// use sqlite_ll::{Connection, State};
197///
198/// let c = Connection::open_memory()?;
199///
200/// c.execute(r##"
201/// CREATE TABLE users (id INTEGER);
202/// INSERT INTO users (id) VALUES (1), (2);
203/// "##)?;
204///
205/// let mut stmt = c.prepare("SELECT id FROM users")?;
206///
207/// while let State::Row = stmt.step()? {
208///     let name = stmt.read::<Vec::<u8>>(0)?;
209///     assert!(matches!(name.as_slice(), b"1" | b"2"));
210/// }
211/// # Ok::<_, sqlite_ll::Error>(())
212/// ```
213impl Readable for Vec<u8> {
214    #[inline]
215    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
216        let mut buf = Vec::new();
217        buf.write(stmt, index)?;
218        Ok(buf)
219    }
220}
221
222/// [`Readable`] implementation for [`FixedBytes`] which reads at most `N`
223/// bytes.
224///
225/// If the column contains more than `N` bytes, a [`Code::MISMATCH`] error is
226/// returned.
227///
228/// # Examples
229///
230/// ```
231/// use sqlite_ll::{Connection, State, FixedBytes, Code};
232///
233/// let c = Connection::open_memory()?;
234/// c.execute(r##"
235/// CREATE TABLE users (id BLOB);
236/// INSERT INTO users (id) VALUES (X'01020304'), (X'0506070809');
237/// "##)?;
238///
239/// let mut stmt = c.prepare("SELECT id FROM users")?;
240///
241/// assert_eq!(stmt.step()?, State::Row);
242/// let bytes = stmt.read::<FixedBytes<4>>(0)?;
243/// assert_eq!(bytes.as_bytes(), &[1, 2, 3, 4]);
244///
245/// assert_eq!(stmt.step()?, State::Row);
246/// let e = stmt.read::<FixedBytes<4>>(0).unwrap_err();
247/// assert_eq!(e.code(), Code::MISMATCH);
248///
249/// let bytes = stmt.read::<FixedBytes<5>>(0)?;
250/// assert_eq!(bytes.as_bytes(), &[5, 6, 7, 8, 9]);
251/// # Ok::<_, sqlite_ll::Error>(())
252/// ```
253impl<const N: usize> Readable for FixedBytes<N> {
254    #[inline]
255    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
256        let mut bytes = FixedBytes::new();
257
258        unsafe {
259            let ptr = ffi::sqlite3_column_blob(stmt.as_ptr(), index);
260
261            if ptr.is_null() {
262                return Ok(bytes);
263            }
264
265            let Ok(len) = usize::try_from(ffi::sqlite3_column_bytes(stmt.as_ptr(), index)) else {
266                return Err(Error::new(ffi::SQLITE_MISMATCH));
267            };
268
269            if len > N {
270                return Err(Error::new(ffi::SQLITE_MISMATCH));
271            }
272
273            ptr::copy_nonoverlapping(ptr.cast::<u8>(), bytes.as_mut_ptr(), len);
274
275            bytes.set_len(len);
276            Ok(bytes)
277        }
278    }
279}
280
281/// [`Readable`] implementation for [`Null`].
282///
283/// # Examples
284///
285/// ```
286/// use sqlite_ll::{Connection, Null, State};
287///
288/// let c = Connection::open_memory()?;
289/// c.execute(r##"
290/// CREATE TABLE users (name TEXT, age INTEGER);
291/// INSERT INTO users (name, age) VALUES ('Alice', NULL), ('Bob', 30);
292/// "##)?;
293///
294/// let mut stmt = c.prepare("SELECT age FROM users WHERE name = ?")?;
295/// stmt.bind(1, "Alice")?;
296///
297/// let mut names = Vec::new();
298///
299/// while let State::Row = stmt.step()? {
300///     names.push(stmt.read::<Null>(0)?);
301/// }
302///
303/// assert_eq!(names, vec![Null]);
304/// # Ok::<_, sqlite_ll::Error>(())
305/// ```
306impl Readable for Null {
307    #[inline]
308    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
309        if stmt.column_type(index) == Type::NULL {
310            Ok(Null)
311        } else {
312            Err(Error::new(ffi::SQLITE_MISMATCH))
313        }
314    }
315}
316
317/// [`Readable`] implementation for [`Option`].
318///
319/// # Examples
320///
321/// ```
322/// use sqlite_ll::{Connection, State};
323///
324/// let c = Connection::open_memory()?;
325/// c.execute(r##"
326/// CREATE TABLE users (name TEXT, age INTEGER);
327/// "##)?;
328///
329/// let mut stmt = c.prepare("INSERT INTO users (name, age) VALUES (?, ?)")?;
330///
331/// stmt.reset()?;
332/// stmt.bind(1, "Alice")?;
333/// stmt.bind(2, None::<i64>)?;
334/// assert_eq!(stmt.step()?, State::Done);
335///
336/// stmt.reset()?;
337/// stmt.bind(1, "Bob")?;
338/// stmt.bind(2, Some(30i64))?;
339/// assert_eq!(stmt.step()?, State::Done);
340///
341/// let mut stmt = c.prepare("SELECT name, age FROM users")?;
342///
343/// let mut names_and_ages = Vec::new();
344///
345/// while let State::Row = stmt.step()? {
346///     let name: String = stmt.read(0)?;
347///     let age: Option<i64> = stmt.read(1)?;
348///     names_and_ages.push((name, age));
349/// }
350///
351/// names_and_ages.sort();
352/// assert_eq!(names_and_ages, vec![(String::from("Alice"), None), (String::from("Bob"), Some(30))]);
353/// # Ok::<_, sqlite_ll::Error>(())
354/// ```
355impl<T> Readable for Option<T>
356where
357    T: Readable,
358{
359    #[inline]
360    fn read(stmt: &Statement, index: c_int) -> Result<Self> {
361        if stmt.column_type(index) == Type::NULL {
362            Ok(None)
363        } else {
364            T::read(stmt, index).map(Some)
365        }
366    }
367}