1#[cfg(not(feature = "std"))]
8use crate::prelude::*;
9use crate::traits::SQLiteTable;
10use crate::values::SQLiteValue;
11use drizzle_core::{
12 SQL, SQLChunk, Token, helpers as core_helpers,
13 traits::{SQLModel, ToSQL},
14};
15
16pub(crate) use core_helpers::{
18 delete, except, from, group_by_expr, having, insert, intersect, limit, offset, order_by,
19 select, select_distinct, set, union, union_all, update, r#where,
20};
21
22pub use drizzle_core::Join;
23
24#[doc(hidden)]
27pub trait JoinSource<'a>: join_source_private::Sealed {
28 type JoinedTable;
29
30 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>>;
31}
32
33mod join_source_private {
34 pub trait Sealed {}
35}
36
37impl<'a, Table> join_source_private::Sealed for Table where Table: SQLiteTable<'a> {}
38
39impl<'a, Name, Projection, Query> join_source_private::Sealed
40 for drizzle_core::Derived<'a, SQLiteValue<'a>, Name, Projection, Query>
41where
42 Name: drizzle_core::Tag,
43 Projection: drizzle_core::DerivedProjection<Name>,
44 Query: ToSQL<'a, SQLiteValue<'a>>,
45{
46}
47
48impl<'a, Table> JoinSource<'a> for Table
49where
50 Table: SQLiteTable<'a>,
51{
52 type JoinedTable = Table;
53
54 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>> {
55 self.into_sql()
56 }
57}
58
59impl<'a, Name, Projection, Query> JoinSource<'a>
60 for drizzle_core::Derived<'a, SQLiteValue<'a>, Name, Projection, Query>
61where
62 Name: drizzle_core::Tag,
63 Projection: drizzle_core::DerivedProjection<Name>,
64 Query: ToSQL<'a, SQLiteValue<'a>>,
65{
66 type JoinedTable = Self;
67
68 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>> {
69 self.into_sql()
70 }
71}
72
73#[doc(hidden)]
79pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
80 type JoinedTable;
81 type OnSources;
83
84 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>>;
85}
86
87mod cross_join_arg_private {
88 pub trait Sealed {}
89
90 impl<'a, Source> Sealed for Source where Source: super::JoinSource<'a> {}
91
92 impl<'a, Source, Condition> Sealed for (Source, Condition)
93 where
94 Source: super::JoinSource<'a>,
95 Condition: drizzle_core::ToSQL<'a, crate::values::SQLiteValue<'a>>,
96 {
97 }
98}
99
100impl<'a, Source, FromTable> CrossJoinArg<'a, FromTable> for Source
101where
102 Source: JoinSource<'a>,
103{
104 type JoinedTable = Source::JoinedTable;
105 type OnSources = ();
106
107 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>> {
108 Join::new()
109 .cross()
110 .into_sql()
111 .append(self.into_join_source_sql())
112 }
113}
114
115impl<'a, Source, Condition, FromTable> CrossJoinArg<'a, FromTable> for (Source, Condition)
116where
117 Source: JoinSource<'a>,
118 Condition: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::expr::ExprSources,
119{
120 type JoinedTable = Source::JoinedTable;
121 type OnSources = Condition::Sources;
122
123 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>> {
124 let (source, condition) = self;
125 Join::new()
126 .inner()
127 .into_sql()
128 .append(source.into_join_source_sql())
129 .push(Token::ON)
130 .append(condition.into_sql())
131 }
132}
133
134drizzle_core::impl_join_arg_trait!(
135 table_trait: SQLiteTable<'a>,
136 table_info_trait: drizzle_core::SQLTableInfo,
137 condition_trait: ToSQL<'a, SQLiteValue<'a>>,
138 join_source_trait: JoinSource<'a>,
139 value_type: SQLiteValue<'a>,
140);
141
142drizzle_core::impl_join_helpers!(
144 table_trait: SQLiteTable<'a>,
145 condition_trait: ToSQL<'a, SQLiteValue<'a>>,
146 sql_type: SQL<'a, SQLiteValue<'a>>,
147);
148
149pub(crate) fn values<'a, Table, T>(
155 rows: impl IntoIterator<Item = Table::Insert<T>>,
156) -> SQL<'a, SQLiteValue<'a>>
157where
158 Table: SQLiteTable<'a> + Default,
159{
160 let rows: Vec<Table::Insert<T>> = rows.into_iter().collect();
161
162 if rows.is_empty() {
163 return SQL::from(Token::VALUES);
164 }
165
166 let columns_info = rows[0].columns();
169 let columns_slice = columns_info.as_ref();
170
171 if columns_slice.is_empty() {
177 if rows.len() == 1 {
178 return SQL::from_iter([Token::DEFAULT, Token::VALUES]);
179 }
180 let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
181 for index in 0..rows.len() {
182 if index > 0 {
183 values_sql.push_mut(Token::COMMA);
184 }
185 values_sql.append_mut(SQL::from(Token::NULL).parens());
186 }
187 return SQL::raw("rowid")
188 .parens()
189 .push(Token::VALUES)
190 .append(values_sql);
191 }
192
193 let columns_sql = SQL::columns(columns_slice);
194 let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
195 for (idx, row) in rows.iter().enumerate() {
196 if idx > 0 {
197 values_sql.push_mut(Token::COMMA);
198 }
199 values_sql.push_mut(Token::LPAREN);
200 values_sql.append_mut(row.values());
201 values_sql.push_mut(Token::RPAREN);
202 }
203
204 columns_sql.parens().push(Token::VALUES).append(values_sql)
205}
206
207#[track_caller]
212pub(crate) fn standalone_offset<'a, P>(offset: P) -> SQL<'a, SQLiteValue<'a>>
213where
214 P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
215{
216 SQL::from(Token::LIMIT)
217 .append(SQL::raw("-1"))
218 .append(core_helpers::offset(offset))
219}
220
221pub(crate) fn before_upsert<'a>(sql: SQL<'a, SQLiteValue<'a>>) -> SQL<'a, SQLiteValue<'a>> {
230 let mut depth = 0usize;
231 let mut ends_in_from = false;
232 for chunk in &sql.chunks {
233 match chunk {
234 SQLChunk::Token(Token::LPAREN) => depth += 1,
235 SQLChunk::Token(Token::RPAREN) => depth = depth.saturating_sub(1),
236 SQLChunk::Token(Token::SELECT) if depth == 0 => ends_in_from = false,
237 SQLChunk::Token(Token::FROM) if depth == 0 => ends_in_from = true,
238 SQLChunk::Token(
239 Token::WHERE
240 | Token::GROUP
241 | Token::HAVING
242 | Token::WINDOW
243 | Token::ORDER
244 | Token::LIMIT,
245 ) if depth == 0 => ends_in_from = false,
246 _ => {}
247 }
248 }
249 if ends_in_from {
250 sql.push(Token::WHERE).append(SQL::raw("true"))
251 } else {
252 sql
253 }
254}
255
256pub(crate) fn returning<'a, 'b, I>(columns: I) -> SQL<'a, SQLiteValue<'a>>
258where
259 I: ToSQL<'a, SQLiteValue<'a>>,
260{
261 let columns = columns.into_sql();
262 let columns = if columns.chunks.is_empty() {
263 SQL::from(Token::STAR)
264 } else {
265 columns
266 };
267 SQL::from(Token::RETURNING).append(columns)
268}