Skip to main content

diesel_libsql/
bind_collector.rs

1//! Bind collector for the LibSql backend.
2
3use diesel::query_builder::{BindCollector, MoveableBindCollector};
4use diesel::serialize::{IsNull, Output};
5use diesel::sql_types::HasSqlType;
6use diesel::sqlite::SqliteType;
7use diesel::QueryResult;
8
9use crate::backend::LibSql;
10
11/// A bind value for LibSql statements.
12///
13/// This type wraps values that can be converted to `libsql::Value`.
14#[derive(Debug)]
15pub struct LibSqlBindValue<'a> {
16    pub(crate) inner: InternalBindValue<'a>,
17}
18
19#[derive(Debug)]
20pub(crate) enum InternalBindValue<'a> {
21    Null,
22    I32(i32),
23    I64(i64),
24    F64(f64),
25    BorrowedString(&'a str),
26    String(Box<str>),
27    BorrowedBinary(&'a [u8]),
28    Binary(Box<[u8]>),
29}
30
31impl std::fmt::Display for InternalBindValue<'_> {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        let name = match self {
34            InternalBindValue::Null => "Null",
35            InternalBindValue::I32(_) | InternalBindValue::I64(_) => "Integer",
36            InternalBindValue::F64(_) => "Float",
37            InternalBindValue::BorrowedString(_) | InternalBindValue::String(_) => "Text",
38            InternalBindValue::BorrowedBinary(_) | InternalBindValue::Binary(_) => "Binary",
39        };
40        f.write_str(name)
41    }
42}
43
44impl InternalBindValue<'_> {
45    /// Convert to an owned `libsql::Value`.
46    pub(crate) fn to_libsql_value(&self) -> libsql::Value {
47        match self {
48            InternalBindValue::Null => libsql::Value::Null,
49            InternalBindValue::I32(i) => libsql::Value::Integer(*i as i64),
50            InternalBindValue::I64(i) => libsql::Value::Integer(*i),
51            InternalBindValue::F64(f) => libsql::Value::Real(*f),
52            InternalBindValue::BorrowedString(s) => libsql::Value::Text((*s).to_string()),
53            InternalBindValue::String(s) => libsql::Value::Text(s.to_string()),
54            InternalBindValue::BorrowedBinary(b) => libsql::Value::Blob(b.to_vec()),
55            InternalBindValue::Binary(b) => libsql::Value::Blob(b.to_vec()),
56        }
57    }
58}
59
60// From impls for LibSqlBindValue (mirrors SqliteBindValue)
61impl From<i32> for LibSqlBindValue<'_> {
62    fn from(i: i32) -> Self {
63        Self {
64            inner: InternalBindValue::I32(i),
65        }
66    }
67}
68
69impl From<i64> for LibSqlBindValue<'_> {
70    fn from(i: i64) -> Self {
71        Self {
72            inner: InternalBindValue::I64(i),
73        }
74    }
75}
76
77impl From<f64> for LibSqlBindValue<'_> {
78    fn from(f: f64) -> Self {
79        Self {
80            inner: InternalBindValue::F64(f),
81        }
82    }
83}
84
85impl<'a> From<&'a str> for LibSqlBindValue<'a> {
86    fn from(s: &'a str) -> Self {
87        Self {
88            inner: InternalBindValue::BorrowedString(s),
89        }
90    }
91}
92
93impl From<String> for LibSqlBindValue<'_> {
94    fn from(s: String) -> Self {
95        Self {
96            inner: InternalBindValue::String(s.into_boxed_str()),
97        }
98    }
99}
100
101impl<'a> From<&'a [u8]> for LibSqlBindValue<'a> {
102    fn from(b: &'a [u8]) -> Self {
103        Self {
104            inner: InternalBindValue::BorrowedBinary(b),
105        }
106    }
107}
108
109impl From<Vec<u8>> for LibSqlBindValue<'_> {
110    fn from(b: Vec<u8>) -> Self {
111        Self {
112            inner: InternalBindValue::Binary(b.into_boxed_slice()),
113        }
114    }
115}
116
117impl<'a, T> From<Option<T>> for LibSqlBindValue<'a>
118where
119    T: Into<LibSqlBindValue<'a>>,
120{
121    fn from(o: Option<T>) -> Self {
122        match o {
123            Some(v) => v.into(),
124            None => Self {
125                inner: InternalBindValue::Null,
126            },
127        }
128    }
129}
130
131/// Bind parameter collector for LibSql queries.
132#[derive(Debug, Default)]
133pub struct LibSqlBindCollector<'a> {
134    pub(crate) binds: Vec<(InternalBindValue<'a>, SqliteType)>,
135}
136
137impl<'a> BindCollector<'a, LibSql> for LibSqlBindCollector<'a> {
138    type Buffer = LibSqlBindValue<'a>;
139
140    fn push_bound_value<T, U>(&mut self, bind: &'a U, metadata_lookup: &mut ()) -> QueryResult<()>
141    where
142        LibSql: HasSqlType<T>,
143        U: diesel::serialize::ToSql<T, LibSql> + ?Sized,
144    {
145        let value = LibSqlBindValue {
146            inner: InternalBindValue::Null,
147        };
148        let mut to_sql_output = Output::new(value, metadata_lookup);
149        let is_null = bind
150            .to_sql(&mut to_sql_output)
151            .map_err(diesel::result::Error::SerializationError)?;
152        let bind = to_sql_output.into_inner();
153        let metadata = <LibSql as HasSqlType<T>>::metadata(metadata_lookup);
154        self.binds.push((
155            match is_null {
156                IsNull::No => bind.inner,
157                IsNull::Yes => InternalBindValue::Null,
158            },
159            metadata,
160        ));
161        Ok(())
162    }
163
164    fn push_null_value(&mut self, metadata: SqliteType) -> QueryResult<()> {
165        self.binds.push((InternalBindValue::Null, metadata));
166        Ok(())
167    }
168}
169
170// Owned version for MoveableBindCollector
171#[derive(Debug, Clone)]
172enum OwnedBindValue {
173    Null,
174    I32(i32),
175    I64(i64),
176    F64(f64),
177    String(Box<str>),
178    Binary(Box<[u8]>),
179}
180
181impl From<&InternalBindValue<'_>> for OwnedBindValue {
182    fn from(value: &InternalBindValue<'_>) -> Self {
183        match value {
184            InternalBindValue::Null => OwnedBindValue::Null,
185            InternalBindValue::I32(v) => OwnedBindValue::I32(*v),
186            InternalBindValue::I64(v) => OwnedBindValue::I64(*v),
187            InternalBindValue::F64(v) => OwnedBindValue::F64(*v),
188            InternalBindValue::BorrowedString(s) => {
189                OwnedBindValue::String(String::from(*s).into_boxed_str())
190            }
191            InternalBindValue::String(s) => OwnedBindValue::String(s.clone()),
192            InternalBindValue::BorrowedBinary(b) => {
193                OwnedBindValue::Binary(Vec::from(*b).into_boxed_slice())
194            }
195            InternalBindValue::Binary(b) => OwnedBindValue::Binary(b.clone()),
196        }
197    }
198}
199
200impl From<&OwnedBindValue> for InternalBindValue<'_> {
201    fn from(value: &OwnedBindValue) -> Self {
202        match value {
203            OwnedBindValue::Null => InternalBindValue::Null,
204            OwnedBindValue::I32(v) => InternalBindValue::I32(*v),
205            OwnedBindValue::I64(v) => InternalBindValue::I64(*v),
206            OwnedBindValue::F64(v) => InternalBindValue::F64(*v),
207            OwnedBindValue::String(s) => InternalBindValue::String(s.clone()),
208            OwnedBindValue::Binary(b) => InternalBindValue::Binary(b.clone()),
209        }
210    }
211}
212
213/// Owned bind data for [`MoveableBindCollector`] support.
214///
215/// Stores a snapshot of bind parameters that can be sent across threads.
216#[derive(Debug)]
217pub struct LibSqlBindCollectorData {
218    binds: Vec<(OwnedBindValue, SqliteType)>,
219}
220
221impl MoveableBindCollector<LibSql> for LibSqlBindCollector<'_> {
222    type BindData = LibSqlBindCollectorData;
223
224    fn moveable(&self) -> Self::BindData {
225        LibSqlBindCollectorData {
226            binds: self
227                .binds
228                .iter()
229                .map(|(bind, tpe)| (OwnedBindValue::from(bind), *tpe))
230                .collect(),
231        }
232    }
233
234    fn append_bind_data(&mut self, from: &Self::BindData) {
235        self.binds.reserve_exact(from.binds.len());
236        self.binds.extend(
237            from.binds
238                .iter()
239                .map(|(bind, tpe)| (InternalBindValue::from(bind), *tpe)),
240        );
241    }
242
243    fn push_debug_binds<'a, 'b>(
244        bind_data: &Self::BindData,
245        f: &'a mut Vec<Box<dyn std::fmt::Debug + 'b>>,
246    ) {
247        f.extend(
248            bind_data
249                .binds
250                .iter()
251                .map(|(b, _)| Box::new(b.clone()) as Box<dyn std::fmt::Debug>),
252        );
253    }
254}