Skip to main content

diesel_libsql/
query_builder.rs

1//! QueryFragment implementations and HasSqlType for the LibSql backend.
2//!
3//! SQLite-specific `QueryFragment<Sqlite>` impls do NOT apply to `LibSql`
4//! because it's a different backend type. We must provide matching impls here.
5
6use diesel::query_builder::{
7    AstPass, BoxedLimitOffsetClause, IntoBoxedClause, LimitClause, LimitOffsetClause,
8    NoLimitClause, NoOffsetClause, OffsetClause, QueryFragment, ReturningClause,
9};
10use diesel::sql_types::*;
11use diesel::sqlite::SqliteType;
12use diesel::QueryResult;
13
14use crate::backend::{LibSql, LibSqlReturningClause};
15
16// ============================================================
17// HasSqlType implementations
18// ============================================================
19
20impl HasSqlType<SmallInt> for LibSql {
21    fn metadata(_: &mut ()) -> SqliteType {
22        SqliteType::SmallInt
23    }
24}
25
26impl HasSqlType<Integer> for LibSql {
27    fn metadata(_: &mut ()) -> SqliteType {
28        SqliteType::Integer
29    }
30}
31
32impl HasSqlType<BigInt> for LibSql {
33    fn metadata(_: &mut ()) -> SqliteType {
34        SqliteType::Long
35    }
36}
37
38impl HasSqlType<Float> for LibSql {
39    fn metadata(_: &mut ()) -> SqliteType {
40        SqliteType::Float
41    }
42}
43
44impl HasSqlType<Double> for LibSql {
45    fn metadata(_: &mut ()) -> SqliteType {
46        SqliteType::Double
47    }
48}
49
50impl HasSqlType<Text> for LibSql {
51    fn metadata(_: &mut ()) -> SqliteType {
52        SqliteType::Text
53    }
54}
55
56impl HasSqlType<Binary> for LibSql {
57    fn metadata(_: &mut ()) -> SqliteType {
58        SqliteType::Binary
59    }
60}
61
62impl HasSqlType<Bool> for LibSql {
63    fn metadata(_: &mut ()) -> SqliteType {
64        SqliteType::Integer
65    }
66}
67
68impl HasSqlType<Date> for LibSql {
69    fn metadata(_: &mut ()) -> SqliteType {
70        SqliteType::Text
71    }
72}
73
74impl HasSqlType<Time> for LibSql {
75    fn metadata(_: &mut ()) -> SqliteType {
76        SqliteType::Text
77    }
78}
79
80impl HasSqlType<Timestamp> for LibSql {
81    fn metadata(_: &mut ()) -> SqliteType {
82        SqliteType::Text
83    }
84}
85
86impl HasSqlType<Numeric> for LibSql {
87    fn metadata(_: &mut ()) -> SqliteType {
88        SqliteType::Double
89    }
90}
91
92impl HasSqlType<TinyInt> for LibSql {
93    fn metadata(_: &mut ()) -> SqliteType {
94        SqliteType::SmallInt
95    }
96}
97
98// ============================================================
99// LimitOffset QueryFragment impls (mirrors sqlite)
100// ============================================================
101
102impl QueryFragment<LibSql> for LimitOffsetClause<NoLimitClause, NoOffsetClause> {
103    fn walk_ast<'b>(&'b self, _out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
104        Ok(())
105    }
106}
107
108impl<L> QueryFragment<LibSql> for LimitOffsetClause<LimitClause<L>, NoOffsetClause>
109where
110    LimitClause<L>: QueryFragment<LibSql>,
111{
112    fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
113        self.limit_clause.walk_ast(out)?;
114        Ok(())
115    }
116}
117
118impl<O> QueryFragment<LibSql> for LimitOffsetClause<NoLimitClause, OffsetClause<O>>
119where
120    OffsetClause<O>: QueryFragment<LibSql>,
121{
122    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
123        // SQLite requires a LIMIT clause before any OFFSET clause
124        // Using LIMIT -1 is the same as no limit
125        out.push_sql(" LIMIT -1 ");
126        self.offset_clause.walk_ast(out)?;
127        Ok(())
128    }
129}
130
131impl<L, O> QueryFragment<LibSql> for LimitOffsetClause<LimitClause<L>, OffsetClause<O>>
132where
133    LimitClause<L>: QueryFragment<LibSql>,
134    OffsetClause<O>: QueryFragment<LibSql>,
135{
136    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
137        self.limit_clause.walk_ast(out.reborrow())?;
138        self.offset_clause.walk_ast(out.reborrow())?;
139        Ok(())
140    }
141}
142
143impl QueryFragment<LibSql> for BoxedLimitOffsetClause<'_, LibSql> {
144    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
145        match (self.limit.as_ref(), self.offset.as_ref()) {
146            (Some(limit), Some(offset)) => {
147                QueryFragment::<LibSql>::walk_ast(limit.as_ref(), out.reborrow())?;
148                QueryFragment::<LibSql>::walk_ast(offset.as_ref(), out.reborrow())?;
149            }
150            (Some(limit), None) => {
151                QueryFragment::<LibSql>::walk_ast(limit.as_ref(), out.reborrow())?;
152            }
153            (None, Some(offset)) => {
154                out.push_sql(" LIMIT -1 ");
155                QueryFragment::<LibSql>::walk_ast(offset.as_ref(), out.reborrow())?;
156            }
157            (None, None) => {}
158        }
159        Ok(())
160    }
161}
162
163// ============================================================
164// IntoBoxedClause impls for LimitOffset
165// ============================================================
166
167impl<'a> IntoBoxedClause<'a, LibSql> for LimitOffsetClause<NoLimitClause, NoOffsetClause> {
168    type BoxedClause = BoxedLimitOffsetClause<'a, LibSql>;
169
170    fn into_boxed(self) -> Self::BoxedClause {
171        BoxedLimitOffsetClause {
172            limit: None,
173            offset: None,
174        }
175    }
176}
177
178impl<'a, L> IntoBoxedClause<'a, LibSql> for LimitOffsetClause<LimitClause<L>, NoOffsetClause>
179where
180    L: QueryFragment<LibSql> + Send + 'a,
181{
182    type BoxedClause = BoxedLimitOffsetClause<'a, LibSql>;
183
184    fn into_boxed(self) -> Self::BoxedClause {
185        BoxedLimitOffsetClause {
186            limit: Some(Box::new(self.limit_clause)),
187            offset: None,
188        }
189    }
190}
191
192impl<'a, O> IntoBoxedClause<'a, LibSql> for LimitOffsetClause<NoLimitClause, OffsetClause<O>>
193where
194    O: QueryFragment<LibSql> + Send + 'a,
195{
196    type BoxedClause = BoxedLimitOffsetClause<'a, LibSql>;
197
198    fn into_boxed(self) -> Self::BoxedClause {
199        BoxedLimitOffsetClause {
200            limit: None,
201            offset: Some(Box::new(self.offset_clause)),
202        }
203    }
204}
205
206impl<'a, L, O> IntoBoxedClause<'a, LibSql> for LimitOffsetClause<LimitClause<L>, OffsetClause<O>>
207where
208    L: QueryFragment<LibSql> + Send + 'a,
209    O: QueryFragment<LibSql> + Send + 'a,
210{
211    type BoxedClause = BoxedLimitOffsetClause<'a, LibSql>;
212
213    fn into_boxed(self) -> Self::BoxedClause {
214        BoxedLimitOffsetClause {
215            limit: Some(Box::new(self.limit_clause)),
216            offset: Some(Box::new(self.offset_clause)),
217        }
218    }
219}
220
221// ============================================================
222// Returning clause QueryFragment impl
223// ============================================================
224
225impl<Expr> QueryFragment<LibSql, LibSqlReturningClause> for ReturningClause<Expr>
226where
227    Expr: QueryFragment<LibSql>,
228{
229    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, LibSql>) -> QueryResult<()> {
230        out.skip_from(true);
231        out.push_sql(" RETURNING ");
232        self.0.walk_ast(out.reborrow())?;
233        Ok(())
234    }
235}