drizzle_sqlite/
helpers.rs

1use crate::{common::Join, values::SQLiteValue};
2use drizzle_core::{
3    SQL, SQLTable, ToSQL, helpers as core_helpers,
4    traits::{SQLColumnInfo, SQLModel, SQLParam},
5};
6
7// Re-export core helpers with SQLiteValue type for convenience
8pub(crate) use core_helpers::{
9    delete, from, group_by, having, limit, offset, order_by, select, set, update, r#where,
10};
11
12/// Helper to convert column info to SQL for joining (column names only for INSERT)
13fn columns_info_to_sql<'a>(columns: &[&'static dyn SQLColumnInfo]) -> SQL<'a, SQLiteValue<'a>> {
14    // For INSERT statements, we need just column names, not fully qualified names
15    let joined_names = columns
16        .iter()
17        .map(|col| col.name())
18        .collect::<Vec<_>>()
19        .join(", ");
20    SQL::raw(joined_names)
21}
22
23fn join_internal<'a, T, Value>(table: T, join: Join, condition: SQL<'a, Value>) -> SQL<'a, Value>
24where
25    T: SQLTable<'a, Value>,
26    Value: SQLParam + 'a,
27{
28    let sql = join.to_sql();
29    let sql = sql.append_raw(" ");
30    let sql = sql.append(table.to_sql());
31    let sql = sql.append_raw(" ON ");
32    sql.append(condition)
33}
34
35/// Helper function to create a JOIN clause using table generic
36pub fn natural_join<'a, T, Value>(table: T, condition: SQL<'a, Value>) -> SQL<'a, Value>
37where
38    T: SQLTable<'a, Value>,
39    Value: SQLParam + 'a,
40{
41    join_internal(table, Join::default().natural(), condition)
42}
43
44/// Helper function to create a JOIN clause using table generic
45pub fn join<'a, T, Value>(table: T, condition: SQL<'a, Value>) -> SQL<'a, Value>
46where
47    T: SQLTable<'a, Value>,
48    Value: SQLParam + 'a,
49{
50    join_internal(table, Join::default(), condition)
51}
52
53pub fn natural_left_join<'a, T, Value>(table: T, condition: SQL<'a, Value>) -> SQL<'a, Value>
54where
55    T: SQLTable<'a, Value>,
56    Value: SQLParam + 'a,
57{
58    join_internal(table, Join::new().natural().left(), condition)
59}
60
61/// Helper function to create a LEFT JOIN clause using table generic
62pub fn left_join<'a, T, TableValue>(table: T, condition: SQL<'a, TableValue>) -> SQL<'a, TableValue>
63where
64    T: SQLTable<'a, TableValue>,
65    TableValue: SQLParam + 'a,
66{
67    join_internal(table, Join::new().left(), condition)
68}
69
70pub fn left_outer_join<'a, T, TableValue>(
71    table: T,
72    condition: SQL<'a, TableValue>,
73) -> SQL<'a, TableValue>
74where
75    T: SQLTable<'a, TableValue>,
76    TableValue: SQLParam + 'a,
77{
78    join_internal(table, Join::new().left().outer(), condition)
79}
80
81pub fn natural_left_outer_join<'a, T, TableValue>(
82    table: T,
83    condition: SQL<'a, TableValue>,
84) -> SQL<'a, TableValue>
85where
86    T: SQLTable<'a, TableValue>,
87    TableValue: SQLParam + 'a,
88{
89    join_internal(table, Join::new().natural().left().outer(), condition)
90}
91
92pub fn natural_right_join<'a, T, TableValue>(
93    table: T,
94    condition: SQL<'a, TableValue>,
95) -> SQL<'a, TableValue>
96where
97    T: SQLTable<'a, TableValue>,
98    TableValue: SQLParam + 'a,
99{
100    join_internal(table, Join::new().natural().right(), condition)
101}
102
103/// Helper function to create a RIGHT JOIN clause using table generic
104pub fn right_join<'a, T, TableValue>(
105    table: T,
106    condition: SQL<'a, TableValue>,
107) -> SQL<'a, TableValue>
108where
109    T: SQLTable<'a, TableValue>,
110    TableValue: SQLParam + 'a,
111{
112    join_internal(table, Join::new().right(), condition)
113}
114
115pub fn right_outer_join<'a, T, TableValue>(
116    table: T,
117    condition: SQL<'a, TableValue>,
118) -> SQL<'a, TableValue>
119where
120    T: SQLTable<'a, TableValue>,
121    TableValue: SQLParam + 'a,
122{
123    join_internal(table, Join::new().right().outer(), condition)
124}
125
126pub fn natural_right_outer_join<'a, T, TableValue>(
127    table: T,
128    condition: SQL<'a, TableValue>,
129) -> SQL<'a, TableValue>
130where
131    T: SQLTable<'a, TableValue>,
132    TableValue: SQLParam + 'a,
133{
134    join_internal(table, Join::new().natural().right().outer(), condition)
135}
136
137pub fn natural_full_join<'a, T, TableValue>(
138    table: T,
139    condition: SQL<'a, TableValue>,
140) -> SQL<'a, TableValue>
141where
142    T: SQLTable<'a, TableValue>,
143    TableValue: SQLParam + 'a,
144{
145    join_internal(table, Join::new().natural().full(), condition)
146}
147
148/// Helper function to create a FULL JOIN clause using table generic
149pub fn full_join<'a, T, TableValue>(table: T, condition: SQL<'a, TableValue>) -> SQL<'a, TableValue>
150where
151    T: SQLTable<'a, TableValue>,
152    TableValue: SQLParam + 'a,
153{
154    join_internal(table, Join::new().full(), condition)
155}
156
157pub fn full_outer_join<'a, T, TableValue>(
158    table: T,
159    condition: SQL<'a, TableValue>,
160) -> SQL<'a, TableValue>
161where
162    T: SQLTable<'a, TableValue>,
163    TableValue: SQLParam + 'a,
164{
165    join_internal(table, Join::new().full().outer(), condition)
166}
167
168pub fn natural_full_outer_join<'a, T, TableValue>(
169    table: T,
170    condition: SQL<'a, TableValue>,
171) -> SQL<'a, TableValue>
172where
173    T: SQLTable<'a, TableValue>,
174    TableValue: SQLParam + 'a,
175{
176    join_internal(table, Join::new().natural().full().outer(), condition)
177}
178
179pub fn natural_inner_join<'a, T, TableValue>(
180    table: T,
181    condition: SQL<'a, TableValue>,
182) -> SQL<'a, TableValue>
183where
184    T: SQLTable<'a, TableValue>,
185    TableValue: SQLParam + 'a,
186{
187    join_internal(table, Join::new().natural().inner(), condition)
188}
189
190/// Helper function to create an INNER JOIN clause using table generic
191pub fn inner_join<'a, T, TableValue>(
192    table: T,
193    condition: SQL<'a, TableValue>,
194) -> SQL<'a, TableValue>
195where
196    T: SQLTable<'a, TableValue>,
197    TableValue: SQLParam + 'a,
198{
199    join_internal(table, Join::new().inner(), condition)
200}
201
202/// Helper function to create a CROSS JOIN clause using table generic
203pub fn cross_join<'a, T, TableValue>(
204    table: T,
205    condition: SQL<'a, TableValue>,
206) -> SQL<'a, TableValue>
207where
208    T: SQLTable<'a, TableValue>,
209    TableValue: SQLParam + 'a,
210{
211    join_internal(table, Join::new().cross(), condition)
212}
213
214/// Creates an INSERT INTO statement with the specified table - SQLite specific
215pub(crate) fn insert<'a, T>(table: T) -> SQL<'a, SQLiteValue<'a>>
216where
217    T: SQLTable<'a, SQLiteValue<'a>>,
218{
219    SQL::text("INSERT INTO").append(&table)
220}
221
222/// Helper function to create VALUES clause for INSERT with pattern validation
223/// All rows must have the same column pattern (enforced by type parameter)
224pub(crate) fn values<'a, Table, T>(
225    rows: impl IntoIterator<Item = <Table as SQLTable<'a, SQLiteValue<'a>>>::Insert<T>>,
226) -> SQL<'a, SQLiteValue<'a>>
227where
228    Table: SQLTable<'a, SQLiteValue<'a>> + Default,
229    Table::Insert<T>: SQLModel<'a, SQLiteValue<'a>>,
230{
231    let rows: Vec<_> = rows.into_iter().collect();
232
233    if rows.is_empty() {
234        return SQL::raw("VALUES");
235    }
236
237    // Since all rows have the same PATTERN, they all have the same columns
238    // Get column info from the first row (all rows will have the same columns)
239    let columns_info = rows[0].columns();
240
241    // Check if this is a DEFAULT VALUES case (no columns)
242    if columns_info.is_empty() {
243        return SQL::raw("DEFAULT VALUES");
244    }
245
246    let columns_sql = columns_info_to_sql(&columns_info);
247    let value_clauses: Vec<_> = rows.iter().map(|row| row.values().subquery()).collect();
248
249    columns_sql
250        .subquery()
251        .append_raw("VALUES")
252        .append(SQL::join(value_clauses, ", "))
253}
254
255/// Helper function to create a RETURNING clause - SQLite specific
256pub(crate) fn returning<'a, 'b, I>(columns: I) -> SQL<'a, SQLiteValue<'a>>
257where
258    I: ToSQL<'a, SQLiteValue<'a>>,
259{
260    SQL::raw("RETURNING").append(columns.to_sql())
261}