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}