drizzle_sqlite/
builder.rs

1use drizzle_core::prepared::prepare_render;
2// Re-export common enums and traits from core
3pub use drizzle_core::{
4    OrderBy, SQL, ToSQL,
5    traits::{IsInSchema, SQLSchema, SQLTable},
6};
7
8// Local imports
9use crate::values::SQLiteValue;
10use std::{fmt::Debug, marker::PhantomData};
11
12// Import modules - these provide specific builder types
13pub mod delete;
14pub mod insert;
15pub mod prepared;
16pub mod select;
17pub mod update;
18
19// Driver implementations
20#[cfg(feature = "rusqlite")]
21pub mod rusqlite;
22
23#[cfg(feature = "turso")]
24pub mod turso;
25
26#[cfg(feature = "libsql")]
27pub mod libsql;
28
29// Export state markers for easier use
30pub use delete::{DeleteInitial, DeleteReturningSet, DeleteWhereSet};
31pub use insert::{
32    Conflict, InsertInitial, InsertOnConflictSet, InsertReturningSet, InsertValuesSet,
33};
34pub use select::{
35    SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet, SelectOffsetSet,
36    SelectOrderSet, SelectWhereSet,
37};
38pub use update::{UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet};
39
40//------------------------------------------------------------------------------
41// Common SQL Components
42//------------------------------------------------------------------------------
43
44/// Represents an ORDER BY clause in a query
45#[derive(Debug, Clone)]
46pub struct OrderByClause<'a> {
47    /// The expression to order by
48    pub expr: SQL<'a, SQLiteValue<'a>>,
49    /// The direction to sort (ASC or DESC)
50    pub direction: OrderBy,
51}
52
53impl<'a> OrderByClause<'a> {
54    /// Creates a new ORDER BY clause
55    pub const fn new(expr: SQL<'a, SQLiteValue<'a>>, direction: OrderBy) -> Self {
56        Self { expr, direction }
57    }
58}
59
60pub trait BuilderState {}
61
62#[derive(Debug, Clone)]
63pub struct BuilderInit;
64
65#[derive(Debug, Clone)]
66pub struct CTEInit;
67
68impl BuilderState for BuilderInit {}
69impl ExecutableState for BuilderInit {}
70
71impl ExecutableState for CTEInit {}
72
73/// Main query builder for SQLite
74///
75/// The `S` type parameter represents the schema type, which is used
76/// to ensure type safety when building queries.
77#[derive(Debug, Clone, Default)]
78pub struct QueryBuilder<'a, Schema = (), State = (), Table = ()> {
79    pub sql: SQL<'a, SQLiteValue<'a>>,
80    schema: PhantomData<Schema>,
81    state: PhantomData<State>,
82    table: PhantomData<Table>,
83}
84
85//------------------------------------------------------------------------------
86// QueryBuilder Implementation
87//------------------------------------------------------------------------------
88
89impl<'a, Schema, State, Table> ToSQL<'a, SQLiteValue<'a>>
90    for QueryBuilder<'a, Schema, State, Table>
91{
92    fn to_sql(&self) -> SQL<'a, SQLiteValue<'a>> {
93        self.sql.clone()
94    }
95}
96
97impl<'a> QueryBuilder<'a> {
98    /// Creates a new query builder for the given schema
99    pub const fn new<S>() -> QueryBuilder<'a, S, BuilderInit> {
100        QueryBuilder {
101            sql: SQL::empty(),
102            schema: PhantomData,
103            state: PhantomData,
104            table: PhantomData,
105        }
106    }
107}
108
109impl<'a, Schema, State> QueryBuilder<'a, Schema, State>
110where
111    State: BuilderState,
112{
113    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
114    where
115        T: ToSQL<'a, SQLiteValue<'a>>,
116    {
117        let sql = crate::helpers::select(columns);
118        select::SelectBuilder {
119            sql,
120            schema: PhantomData,
121            state: PhantomData,
122            table: PhantomData,
123        }
124    }
125}
126
127impl<'a, Schema> QueryBuilder<'a, Schema, CTEInit> {
128    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
129    where
130        T: ToSQL<'a, SQLiteValue<'a>>,
131    {
132        let sql = self.sql.clone().append(crate::helpers::select(columns));
133        select::SelectBuilder {
134            sql,
135            schema: PhantomData,
136            state: PhantomData,
137            table: PhantomData,
138        }
139    }
140
141    pub fn with<Q, C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
142    where
143        Q: ToSQL<'a, SQLiteValue<'a>>,
144        C: AsRef<drizzle_core::expressions::DefinedCTE<'a, SQLiteValue<'a>, Q>>,
145    {
146        let sql = self
147            .sql
148            .clone()
149            .append_raw(", ")
150            .append(cte.as_ref().definition());
151        QueryBuilder {
152            sql,
153            schema: PhantomData,
154            state: PhantomData,
155            table: PhantomData,
156        }
157    }
158}
159
160impl<'a, Schema, State> QueryBuilder<'a, Schema, State>
161where
162    State: BuilderState,
163{
164    pub fn insert<Table>(
165        &self,
166        table: Table,
167    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
168    where
169        Table: IsInSchema<Schema> + SQLTable<'a, SQLiteValue<'a>>,
170    {
171        let sql = crate::helpers::insert(table);
172
173        insert::InsertBuilder {
174            sql,
175            schema: PhantomData,
176            state: PhantomData,
177            table: PhantomData,
178        }
179    }
180
181    pub fn update<Table>(
182        &self,
183        table: Table,
184    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
185    where
186        Table: IsInSchema<Schema> + SQLTable<'a, SQLiteValue<'a>>,
187    {
188        let sql = crate::helpers::update::<'a, Table, SQLiteValue<'a>>(table);
189
190        update::UpdateBuilder {
191            sql,
192            schema: PhantomData,
193            state: PhantomData,
194            table: PhantomData,
195        }
196    }
197
198    pub fn delete<T>(&self, table: T) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, T>
199    where
200        T: IsInSchema<Schema> + SQLTable<'a, SQLiteValue<'a>>,
201    {
202        let sql = crate::helpers::delete::<'a, T, SQLiteValue<'a>>(table);
203
204        delete::DeleteBuilder {
205            sql,
206            schema: PhantomData,
207            state: PhantomData,
208            table: PhantomData,
209        }
210    }
211
212    pub fn with<Q, C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
213    where
214        Q: ToSQL<'a, SQLiteValue<'a>>,
215        C: AsRef<drizzle_core::expressions::DefinedCTE<'a, SQLiteValue<'a>, Q>>,
216    {
217        let sql = SQL::raw("WITH").append(cte.as_ref().definition());
218        QueryBuilder {
219            sql,
220            schema: PhantomData,
221            state: PhantomData,
222            table: PhantomData,
223        }
224    }
225}
226
227// Marker trait to indicate a query builder state is executable
228pub trait ExecutableState {}
229
230impl<'a, Schema, State, Table> QueryBuilder<'a, Schema, State, Table>
231where
232    State: ExecutableState,
233{
234    /// Creates a prepared statement that can be executed multiple times
235    pub fn prepare(&self) -> prepared::PreparedStatement<'a> {
236        let inner = prepare_render(self.sql.clone());
237
238        prepared::PreparedStatement { inner }
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    #[test]
247    fn test_query_builder_new() {
248        let qb = QueryBuilder::new::<()>();
249        let sql = qb.to_sql();
250        assert_eq!(sql.sql(), "");
251        assert_eq!(sql.params().len(), 0);
252    }
253
254    #[test]
255    fn test_builder_state_trait() {
256        // Test that different states implement BuilderState
257        fn assert_builder_state<T: BuilderState>() {}
258
259        assert_builder_state::<BuilderInit>();
260        // assert_builder_state::<SelectInitial>();
261        // assert_builder_state::<InsertInitial>();
262        // assert_builder_state::<UpdateInitial>();
263        // assert_builder_state::<DeleteInitial>();
264    }
265}