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