drizzle_sqlite/
builder.rs

1use drizzle_core::Token;
2// Re-export common enums and traits from core
3pub use drizzle_core::{
4    OrderBy, SQL, ToSQL,
5    traits::{SQLSchema, SQLTable},
6};
7
8// Local imports
9use crate::{
10    common::SQLiteSchemaType,
11    traits::{SQLiteSQL, SQLiteTable},
12    values::SQLiteValue,
13};
14use std::{fmt::Debug, marker::PhantomData};
15
16// Import modules - these provide specific builder types
17pub mod cte;
18pub mod delete;
19pub mod insert;
20pub mod prepared;
21pub mod select;
22pub mod update;
23
24// Re-export CTE types
25pub use cte::{CTEDefinition, CTEView};
26
27// Export state markers for easier use
28pub use delete::{DeleteInitial, DeleteReturningSet, DeleteWhereSet};
29pub use insert::{
30    Conflict, InsertInitial, InsertOnConflictSet, InsertReturningSet, InsertValuesSet,
31};
32pub use select::{
33    SelectFromSet, SelectGroupSet, SelectInitial, SelectJoinSet, SelectLimitSet, SelectOffsetSet,
34    SelectOrderSet, SelectWhereSet,
35};
36pub use update::{UpdateInitial, UpdateReturningSet, UpdateSetClauseSet, UpdateWhereSet};
37
38//------------------------------------------------------------------------------
39// Common SQL Components
40//------------------------------------------------------------------------------
41
42/// Represents an ORDER BY clause in a query
43#[derive(Debug, Clone)]
44pub struct OrderByClause<'a> {
45    /// The expression to order by
46    pub expr: SQL<'a, SQLiteValue<'a>>,
47    /// The direction to sort (ASC or DESC)
48    pub direction: OrderBy,
49}
50
51impl<'a> OrderByClause<'a> {
52    /// Creates a new ORDER BY clause
53    pub const fn new(expr: SQL<'a, SQLiteValue<'a>>, direction: OrderBy) -> Self {
54        Self { expr, direction }
55    }
56}
57
58pub trait BuilderState {}
59
60#[derive(Debug, Clone)]
61pub struct BuilderInit;
62
63#[derive(Debug, Clone)]
64pub struct CTEInit;
65
66impl BuilderState for BuilderInit {}
67impl ExecutableState for BuilderInit {}
68
69impl ExecutableState for CTEInit {}
70
71/// Main query builder for SQLite operations.
72///
73/// `QueryBuilder` provides a type-safe, fluent API for building SQL queries. It uses compile-time
74/// type checking to ensure queries are valid and properly structured.
75///
76/// ## Type Parameters
77///
78/// - `Schema`: The database schema type, ensuring queries only reference valid tables
79/// - `State`: The current builder state, enforcing proper query construction order
80/// - `Table`: The table type being operated on (for single-table operations)
81///
82/// ## Basic Usage
83///
84/// ```rust
85/// use drizzle_sqlite::builder::QueryBuilder;
86/// use drizzle_macros::{SQLiteTable, SQLiteSchema};
87/// use drizzle_core::ToSQL;
88///
89/// #[SQLiteTable(name = "users")]
90/// struct User {
91///     #[integer(primary)]
92///     id: i32,
93///     #[text]
94///     name: String,
95/// }
96///
97/// #[derive(SQLiteSchema)]
98/// struct Schema {
99///     user: User,
100/// }
101///
102/// // Create a query builder for your schema
103/// let builder = QueryBuilder::new::<Schema>();
104/// let Schema { user } = Schema::new();
105///
106/// // Build queries using the fluent API
107/// let query = builder
108///     .select(user.name)
109///     .from(user);
110/// assert_eq!(query.to_sql().sql(), r#"SELECT "users"."name" FROM "users""#);
111/// ```
112///
113/// ## Query Types
114///
115/// The builder supports all major SQL operations:
116///
117/// ### SELECT Queries
118/// ```rust
119/// # use drizzle_sqlite::builder::QueryBuilder;
120/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
121/// # use drizzle_core::{ToSQL, expressions::conditions::gt};
122/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
123/// # #[derive(SQLiteSchema)] struct Schema { user: User }
124/// # let builder = QueryBuilder::new::<Schema>();
125/// # let Schema { user } = Schema::new();
126/// let query = builder.select(user.name).from(user);
127/// let query = builder.select((user.id, user.name)).from(user).r#where(gt(user.id, 10));
128/// ```
129///
130/// ### INSERT Queries
131/// ```rust
132/// # use drizzle_sqlite::builder::QueryBuilder;
133/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
134/// # use drizzle_core::ToSQL;
135/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
136/// # #[derive(SQLiteSchema)] struct Schema { user: User }
137/// # let builder = QueryBuilder::new::<Schema>();
138/// # let Schema { user } = Schema::new();
139/// let query = builder
140///     .insert(user)
141///     .values([InsertUser::new("Alice")]);
142/// ```
143///
144/// ### UPDATE Queries
145/// ```rust
146/// # use drizzle_sqlite::builder::QueryBuilder;
147/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
148/// # use drizzle_core::{ToSQL, expressions::conditions::eq};
149/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
150/// # #[derive(SQLiteSchema)] struct Schema { user: User }
151/// # let builder = QueryBuilder::new::<Schema>();
152/// # let Schema { user } = Schema::new();
153/// let query = builder
154///     .update(user)
155///     .set(UpdateUser::default().with_name("Bob"))
156///     .r#where(eq(user.id, 1));
157/// ```
158///
159/// ### DELETE Queries  
160/// ```rust
161/// # use drizzle_sqlite::builder::QueryBuilder;
162/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
163/// # use drizzle_core::{ToSQL, expressions::conditions::lt};
164/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
165/// # #[derive(SQLiteSchema)] struct Schema { user: User }
166/// # let builder = QueryBuilder::new::<Schema>();
167/// # let Schema { user } = Schema::new();
168/// let query = builder
169///     .delete(user)
170///     .r#where(lt(user.id, 10));
171/// ```
172///
173/// ## Common Table Expressions (CTEs)
174///
175/// The builder supports WITH clauses for complex queries with typed field access:
176///
177/// ```rust
178/// # use drizzle_sqlite::builder::QueryBuilder;
179/// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
180/// # use drizzle_core::ToSQL;
181/// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
182/// # #[derive(SQLiteSchema)] struct Schema { user: User }
183/// # let builder = QueryBuilder::new::<Schema>();
184/// # let Schema { user } = Schema::new();
185/// // Create a CTE with typed field access using .as_cte()
186/// let active_users = builder
187///     .select((user.id, user.name))
188///     .from(user)
189///     .as_cte("active_users");
190///
191/// // Use the CTE with typed column access via Deref
192/// let query = builder
193///     .with(&active_users)
194///     .select(active_users.name)  // Typed field access!
195///     .from(&active_users);
196/// assert_eq!(
197///     query.to_sql().sql(),
198///     r#"WITH active_users AS (SELECT "users"."id", "users"."name" FROM "users") SELECT "active_users"."name" FROM "active_users""#
199/// );
200/// ```
201#[derive(Debug, Clone, Default)]
202pub struct QueryBuilder<'a, Schema = (), State = (), Table = ()> {
203    pub sql: SQL<'a, SQLiteValue<'a>>,
204    schema: PhantomData<Schema>,
205    state: PhantomData<State>,
206    table: PhantomData<Table>,
207}
208
209//------------------------------------------------------------------------------
210// QueryBuilder Implementation
211//------------------------------------------------------------------------------
212
213impl<'a, Schema, State, Table> ToSQL<'a, SQLiteValue<'a>>
214    for QueryBuilder<'a, Schema, State, Table>
215{
216    fn to_sql(&self) -> SQLiteSQL<'a> {
217        self.sql.clone()
218    }
219}
220
221impl<'a> QueryBuilder<'a> {
222    /// Creates a new query builder for the given schema type.
223    ///
224    /// This is the entry point for building SQL queries. The schema type parameter
225    /// ensures that only valid tables from your schema can be used in queries.
226    ///
227    /// # Examples
228    ///
229    /// ```rust
230    /// use drizzle_sqlite::builder::QueryBuilder;
231    /// use drizzle_macros::{SQLiteTable, SQLiteSchema};
232    ///
233    /// #[SQLiteTable(name = "users")]
234    /// struct User {
235    ///     #[integer(primary)]
236    ///     id: i32,
237    ///     #[text]
238    ///     name: String,
239    /// }
240    ///
241    /// #[derive(SQLiteSchema)]
242    /// struct MySchema {
243    ///     user: User,
244    /// }
245    ///
246    /// let builder = QueryBuilder::new::<MySchema>();
247    /// ```
248    pub const fn new<S>() -> QueryBuilder<'a, S, BuilderInit> {
249        QueryBuilder {
250            sql: SQL::empty(),
251            schema: PhantomData,
252            state: PhantomData,
253            table: PhantomData,
254        }
255    }
256}
257
258impl<'a, Schema, State> QueryBuilder<'a, Schema, State>
259where
260    State: BuilderState,
261{
262    /// Begins a SELECT query with the specified columns.
263    ///
264    /// This method starts building a SELECT statement. You can select individual columns,
265    /// multiple columns as a tuple, or use `()` to select all columns.
266    ///
267    /// # Examples
268    ///
269    /// ```rust
270    /// # use drizzle_sqlite::builder::QueryBuilder;
271    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
272    /// # use drizzle_core::ToSQL;
273    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
274    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
275    /// # let builder = QueryBuilder::new::<Schema>();
276    /// # let Schema { user } = Schema::new();
277    /// // Select a single column
278    /// let query = builder.select(user.name).from(user);
279    /// assert_eq!(query.to_sql().sql(), r#"SELECT "users"."name" FROM "users""#);
280    ///
281    /// // Select multiple columns
282    /// let query = builder.select((user.id, user.name)).from(user);
283    /// assert_eq!(query.to_sql().sql(), r#"SELECT "users"."id", "users"."name" FROM "users""#);
284    /// ```
285    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
286    where
287        T: ToSQL<'a, SQLiteValue<'a>>,
288    {
289        let sql = crate::helpers::select(columns);
290        select::SelectBuilder {
291            sql,
292            schema: PhantomData,
293            state: PhantomData,
294            table: PhantomData,
295        }
296    }
297}
298
299impl<'a, Schema> QueryBuilder<'a, Schema, CTEInit> {
300    pub fn select<T>(&self, columns: T) -> select::SelectBuilder<'a, Schema, select::SelectInitial>
301    where
302        T: ToSQL<'a, SQLiteValue<'a>>,
303    {
304        let sql = self.sql.clone().append(crate::helpers::select(columns));
305        select::SelectBuilder {
306            sql,
307            schema: PhantomData,
308            state: PhantomData,
309            table: PhantomData,
310        }
311    }
312
313    pub fn with<C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
314    where
315        C: CTEDefinition<'a>,
316    {
317        let sql = self
318            .sql
319            .clone()
320            .push(Token::COMMA)
321            .append(cte.cte_definition());
322        QueryBuilder {
323            sql,
324            schema: PhantomData,
325            state: PhantomData,
326            table: PhantomData,
327        }
328    }
329}
330
331impl<'a, Schema, State> QueryBuilder<'a, Schema, State>
332where
333    State: BuilderState,
334{
335    /// Begins an INSERT query for the specified table.
336    ///
337    /// This method starts building an INSERT statement. The table must be part of the schema
338    /// and will be type-checked at compile time.
339    ///
340    /// # Examples
341    ///
342    /// ```rust
343    /// # use drizzle_sqlite::builder::QueryBuilder;
344    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
345    /// # use drizzle_core::ToSQL;
346    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
347    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
348    /// # let builder = QueryBuilder::new::<Schema>();
349    /// # let Schema { user } = Schema::new();
350    /// let query = builder
351    ///     .insert(user)
352    ///     .values([InsertUser::new("Alice")]);
353    /// assert_eq!(query.to_sql().sql(), r#"INSERT INTO "users" (name) VALUES (?)"#);
354    /// ```
355    pub fn insert<Table>(
356        &self,
357        table: Table,
358    ) -> insert::InsertBuilder<'a, Schema, insert::InsertInitial, Table>
359    where
360        Table: SQLiteTable<'a>,
361    {
362        let sql = crate::helpers::insert(table);
363
364        insert::InsertBuilder {
365            sql,
366            schema: PhantomData,
367            state: PhantomData,
368            table: PhantomData,
369        }
370    }
371
372    /// Begins an UPDATE query for the specified table.
373    ///
374    /// This method starts building an UPDATE statement. The table must be part of the schema
375    /// and will be type-checked at compile time.
376    ///
377    /// # Examples
378    ///
379    /// ```rust
380    /// # use drizzle_sqlite::builder::QueryBuilder;
381    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
382    /// # use drizzle_core::{ToSQL, expressions::conditions::eq};
383    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
384    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
385    /// # let builder = QueryBuilder::new::<Schema>();
386    /// # let Schema { user } = Schema::new();
387    /// let query = builder
388    ///     .update(user)
389    ///     .set(UpdateUser::default().with_name("Bob"))
390    ///     .r#where(eq(user.id, 1));
391    /// assert_eq!(query.to_sql().sql(), r#"UPDATE "users" SET "name" = ? WHERE "users"."id" = ?"#);
392    /// ```
393    pub fn update<Table>(
394        &self,
395        table: Table,
396    ) -> update::UpdateBuilder<'a, Schema, update::UpdateInitial, Table>
397    where
398        Table: SQLiteTable<'a>,
399    {
400        let sql = crate::helpers::update::<'a, Table, SQLiteSchemaType, SQLiteValue<'a>>(table);
401
402        update::UpdateBuilder {
403            sql,
404            schema: PhantomData,
405            state: PhantomData,
406            table: PhantomData,
407        }
408    }
409
410    /// Begins a DELETE query for the specified table.
411    ///
412    /// This method starts building a DELETE statement. The table must be part of the schema
413    /// and will be type-checked at compile time.
414    ///
415    /// # Examples
416    ///
417    /// ```rust
418    /// # use drizzle_sqlite::builder::QueryBuilder;
419    /// # use drizzle_macros::{SQLiteTable, SQLiteSchema};
420    /// # use drizzle_core::{ToSQL, expressions::conditions::lt};
421    /// # #[SQLiteTable(name = "users")] struct User { #[integer(primary)] id: i32, #[text] name: String }
422    /// # #[derive(SQLiteSchema)] struct Schema { user: User }
423    /// # let builder = QueryBuilder::new::<Schema>();
424    /// # let Schema { user } = Schema::new();
425    /// let query = builder
426    ///     .delete(user)
427    ///     .r#where(lt(user.id, 10));
428    /// assert_eq!(query.to_sql().sql(), r#"DELETE FROM "users" WHERE "users"."id" < ?"#);
429    /// ```
430    pub fn delete<Table>(
431        &self,
432        table: Table,
433    ) -> delete::DeleteBuilder<'a, Schema, delete::DeleteInitial, Table>
434    where
435        Table: SQLiteTable<'a>,
436    {
437        let sql = crate::helpers::delete::<'a, Table, SQLiteSchemaType, SQLiteValue<'a>>(table);
438
439        delete::DeleteBuilder {
440            sql,
441            schema: PhantomData,
442            state: PhantomData,
443            table: PhantomData,
444        }
445    }
446
447    pub fn with<C>(&self, cte: C) -> QueryBuilder<'a, Schema, CTEInit>
448    where
449        C: CTEDefinition<'a>,
450    {
451        let sql = SQL::from(Token::WITH).append(cte.cte_definition());
452        QueryBuilder {
453            sql,
454            schema: PhantomData,
455            state: PhantomData,
456            table: PhantomData,
457        }
458    }
459}
460
461// Marker trait to indicate a query builder state is executable
462pub trait ExecutableState {}
463
464#[cfg(test)]
465mod tests {
466    use super::*;
467
468    #[test]
469    fn test_query_builder_new() {
470        let qb = QueryBuilder::new::<()>();
471        let sql = qb.to_sql();
472        assert_eq!(sql.sql(), "");
473        assert_eq!(sql.params().len(), 0);
474    }
475
476    #[test]
477    fn test_builder_state_trait() {
478        // Test that different states implement BuilderState
479        fn assert_builder_state<T: BuilderState>() {}
480
481        assert_builder_state::<BuilderInit>();
482        // assert_builder_state::<SelectInitial>();
483        // assert_builder_state::<InsertInitial>();
484        // assert_builder_state::<UpdateInitial>();
485        // assert_builder_state::<DeleteInitial>();
486    }
487}