Skip to main content

sql_peas/
columns.rs

1use std::ffi::{c_double, c_int};
2
3/// Bindable and readable types
4use crate::{ColumnIndex, ParameterIndex, Result, Statement, Type, Value};
5
6// https://sqlite.org/c3ref/c_static.html
7macro_rules! transient(
8    () => (
9        std::mem::transmute::<
10        *const std::ffi::c_void,
11        std::option::Option<unsafe extern "C" fn(*mut std::ffi::c_void)>
12        >(!0 as *const core::ffi::c_void)
13    );
14);
15
16/// A type suitable for binding to a prepared statement.
17pub trait Bindable {
18    /// Bind to a parameter.
19    fn bind(self, _: &Statement) -> Result<()>;
20}
21
22/// A type suitable for binding to a prepared statement given a parameter index.
23pub trait BindableWithIndex {
24    /// Bind to a parameter.
25    ///
26    /// In case of integer indices, the first parameter has index 1.
27    fn bind<T: ParameterIndex>(self, _: &Statement, _: T) -> Result<()>;
28}
29
30/// A type suitable for reading from a prepared statement given a column index.
31pub trait ReadableWithIndex: Sized {
32    /// Read from a column.
33    ///
34    /// In case of integer indices, the first column has index 0.
35    fn read<T: ColumnIndex>(_: &Statement, _: T) -> Result<Self>;
36}
37
38impl<T, U> Bindable for (T, U)
39where
40    T: ParameterIndex,
41    U: BindableWithIndex,
42{
43    #[inline]
44    fn bind(self, statement: &Statement) -> Result<()> {
45        self.1.bind(statement, self.0)
46    }
47}
48
49impl<T> Bindable for &[T]
50where
51    T: BindableWithIndex + Clone,
52{
53    fn bind(self, statement: &Statement) -> Result<()> {
54        for (index, value) in self.iter().enumerate() {
55            value.clone().bind(statement, index + 1)?;
56        }
57        Ok(())
58    }
59}
60
61impl<T, U> Bindable for &[(T, U)]
62where
63    T: ParameterIndex,
64    U: BindableWithIndex + Clone,
65{
66    fn bind(self, statement: &Statement) -> Result<()> {
67        for (index, value) in self.iter() {
68            value.clone().bind(statement, *index)?;
69        }
70        Ok(())
71    }
72}
73
74impl BindableWithIndex for &[u8] {
75    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
76        unsafe {
77            ok!(
78                statement.raw.1,
79                ffi::sqlite3_bind_blob(
80                    statement.raw.0,
81                    index.index(statement)? as c_int,
82                    self.as_ptr() as *const _,
83                    self.len() as c_int,
84                    transient!(),
85                )
86            );
87        }
88        Ok(())
89    }
90}
91
92impl BindableWithIndex for f64 {
93    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
94        unsafe {
95            ok!(
96                statement.raw.1,
97                ffi::sqlite3_bind_double(
98                    statement.raw.0,
99                    index.index(statement)? as c_int,
100                    self as c_double
101                )
102            );
103        }
104        Ok(())
105    }
106}
107
108impl BindableWithIndex for i64 {
109    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
110        unsafe {
111            ok!(
112                statement.raw.1,
113                ffi::sqlite3_bind_int64(
114                    statement.raw.0,
115                    index.index(statement)? as c_int,
116                    self as ffi::sqlite3_int64
117                )
118            );
119        }
120        Ok(())
121    }
122}
123
124impl BindableWithIndex for &str {
125    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
126        unsafe {
127            ok!(
128                statement.raw.1,
129                ffi::sqlite3_bind_text(
130                    statement.raw.0,
131                    index.index(statement)? as c_int,
132                    self.as_ptr() as *const _,
133                    self.len() as c_int,
134                    transient!(),
135                )
136            );
137        }
138        Ok(())
139    }
140}
141
142impl BindableWithIndex for () {
143    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
144        unsafe {
145            ok!(
146                statement.raw.1,
147                ffi::sqlite3_bind_null(statement.raw.0, index.index(statement)? as c_int)
148            );
149        }
150        Ok(())
151    }
152}
153
154impl BindableWithIndex for Value {
155    #[inline]
156    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
157        (index, &self).bind(statement)
158    }
159}
160
161impl BindableWithIndex for &Value {
162    fn bind<T: ParameterIndex>(self, statement: &Statement, index: T) -> Result<()> {
163        match self {
164            Value::Binary(ref value) => (value as &[u8]).bind(statement, index),
165            Value::Float(value) => value.bind(statement, index),
166            Value::Integer(value) => value.bind(statement, index),
167            Value::String(ref value) => (value as &str).bind(statement, index),
168            Value::Null => ().bind(statement, index),
169        }
170    }
171}
172
173impl<T> BindableWithIndex for Option<T>
174where
175    T: BindableWithIndex,
176{
177    #[inline]
178    fn bind<U: ParameterIndex>(self, statement: &Statement, index: U) -> Result<()> {
179        match self {
180            Some(value) => value.bind(statement, index),
181            None => ().bind(statement, index),
182        }
183    }
184}
185
186impl<T> BindableWithIndex for &Option<T>
187where
188    T: BindableWithIndex + Clone,
189{
190    #[inline]
191    fn bind<U: ParameterIndex>(self, statement: &Statement, index: U) -> Result<()> {
192        match self {
193            Some(value) => value.clone().bind(statement, index),
194            None => ().bind(statement, index),
195        }
196    }
197}
198
199impl ReadableWithIndex for Vec<u8> {
200    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
201        use std::ptr::copy_nonoverlapping as copy;
202        unsafe {
203            let pointer =
204                ffi::sqlite3_column_blob(statement.raw.0, index.index(statement)? as c_int);
205            if pointer.is_null() {
206                return Ok(vec![]);
207            }
208            let count = ffi::sqlite3_column_bytes(statement.raw.0, index.index(statement)? as c_int)
209                as usize;
210            let mut buffer = Vec::with_capacity(count);
211            copy(pointer as *const u8, buffer.as_mut_ptr(), count);
212            buffer.set_len(count);
213            Ok(buffer)
214        }
215    }
216}
217
218impl ReadableWithIndex for f64 {
219    #[allow(clippy::unnecessary_cast)]
220    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
221        Ok(unsafe {
222            ffi::sqlite3_column_double(statement.raw.0, index.index(statement)? as c_int) as f64
223        })
224    }
225}
226
227impl ReadableWithIndex for i64 {
228    #[allow(clippy::unnecessary_cast)]
229    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
230        Ok(unsafe {
231            ffi::sqlite3_column_int64(statement.raw.0, index.index(statement)? as c_int) as i64
232        })
233    }
234}
235
236impl ReadableWithIndex for String {
237    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
238        unsafe {
239            let pointer =
240                ffi::sqlite3_column_text(statement.raw.0, index.index(statement)? as c_int);
241            if pointer.is_null() {
242                raise!("cannot read a text column");
243            }
244            Ok(c_str_to_string!(pointer))
245        }
246    }
247}
248
249impl ReadableWithIndex for Value {
250    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
251        Ok(match statement.column_type(index)? {
252            Type::Binary => Value::Binary(ReadableWithIndex::read(statement, index)?),
253            Type::Float => Value::Float(ReadableWithIndex::read(statement, index)?),
254            Type::Integer => Value::Integer(ReadableWithIndex::read(statement, index)?),
255            Type::String => Value::String(ReadableWithIndex::read(statement, index)?),
256            Type::Null => Value::Null,
257        })
258    }
259}
260
261impl<T: ReadableWithIndex> ReadableWithIndex for Option<T> {
262    fn read<U: ColumnIndex>(statement: &Statement, index: U) -> Result<Self> {
263        if statement.column_type(index)? == Type::Null {
264            Ok(None)
265        } else {
266            T::read(statement, index).map(Some)
267        }
268    }
269}