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)]
27#[diagnostic::on_unimplemented(
28 message = "`{Self}` cannot follow JOIN",
29 label = "join a table, a view, or an aliased subquery"
30)]
31pub trait JoinSource<'a>: join_source_private::Sealed {
32 type JoinedTable;
33
34 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>>;
35}
36
37mod join_source_private {
38 pub trait Sealed {}
39}
40
41impl<'a, Table> join_source_private::Sealed for Table where Table: SQLiteTable<'a> {}
42
43impl<'a, Name, Projection, Query> join_source_private::Sealed
44 for drizzle_core::Derived<'a, SQLiteValue<'a>, Name, Projection, Query>
45where
46 Name: drizzle_core::Tag,
47 Projection: drizzle_core::DerivedProjection<Name>,
48 Query: ToSQL<'a, SQLiteValue<'a>>,
49{
50}
51
52impl<'a, Table> JoinSource<'a> for Table
53where
54 Table: SQLiteTable<'a>,
55{
56 type JoinedTable = Table;
57
58 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>> {
59 self.into_sql()
60 }
61}
62
63impl<'a, Name, Projection, Query> JoinSource<'a>
64 for drizzle_core::Derived<'a, SQLiteValue<'a>, Name, Projection, Query>
65where
66 Name: drizzle_core::Tag,
67 Projection: drizzle_core::DerivedProjection<Name>,
68 Query: ToSQL<'a, SQLiteValue<'a>>,
69{
70 type JoinedTable = Self;
71
72 fn into_join_source_sql(self) -> SQL<'a, SQLiteValue<'a>> {
73 self.into_sql()
74 }
75}
76
77#[doc(hidden)]
83pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
84 type JoinedTable;
85 type OnSources;
87
88 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>>;
89}
90
91mod cross_join_arg_private {
92 pub trait Sealed {}
93
94 impl<'a, Source> Sealed for Source where Source: super::JoinSource<'a> {}
95
96 impl<'a, Source, Condition> Sealed for (Source, Condition)
97 where
98 Source: super::JoinSource<'a>,
99 Condition: drizzle_core::ToSQL<'a, crate::values::SQLiteValue<'a>>,
100 {
101 }
102}
103
104impl<'a, Source, FromTable> CrossJoinArg<'a, FromTable> for Source
105where
106 Source: JoinSource<'a>,
107{
108 type JoinedTable = Source::JoinedTable;
109 type OnSources = ();
110
111 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>> {
112 Join::new()
113 .cross()
114 .into_sql()
115 .append(self.into_join_source_sql())
116 }
117}
118
119impl<'a, Source, Condition, FromTable> CrossJoinArg<'a, FromTable> for (Source, Condition)
120where
121 Source: JoinSource<'a>,
122 Condition: ToSQL<'a, SQLiteValue<'a>> + drizzle_core::expr::ExprSources,
123{
124 type JoinedTable = Source::JoinedTable;
125 type OnSources = Condition::Sources;
126
127 fn into_cross_join_sql(self) -> SQL<'a, SQLiteValue<'a>> {
128 let (source, condition) = self;
129 Join::new()
130 .inner()
131 .into_sql()
132 .append(source.into_join_source_sql())
133 .push(Token::ON)
134 .append(condition.into_sql())
135 }
136}
137
138drizzle_core::impl_join_arg_trait!(
139 table_trait: SQLiteTable<'a>,
140 table_info_trait: drizzle_core::SQLTableInfo,
141 condition_trait: ToSQL<'a, SQLiteValue<'a>>,
142 join_source_trait: JoinSource<'a>,
143 value_type: SQLiteValue<'a>,
144);
145
146drizzle_core::impl_join_helpers!(
148 table_trait: SQLiteTable<'a>,
149 condition_trait: ToSQL<'a, SQLiteValue<'a>>,
150 sql_type: SQL<'a, SQLiteValue<'a>>,
151);
152
153pub(crate) fn values<'a, Table, T>(
159 rows: impl IntoIterator<Item = Table::Insert<T>>,
160) -> SQL<'a, SQLiteValue<'a>>
161where
162 Table: SQLiteTable<'a> + Default,
163{
164 let rows: Vec<Table::Insert<T>> = rows.into_iter().collect();
165
166 if rows.is_empty() {
167 return match <Table as drizzle_core::SQLSchema<
168 'a,
169 crate::common::SQLiteSchemaType,
170 SQLiteValue<'a>,
171 >>::TYPE
172 {
173 crate::common::SQLiteSchemaType::Table(table) => {
174 core_helpers::insert_no_rows(table, "SELECT NULL WHERE 1 = 0")
175 }
176 _ => SQL::from(Token::VALUES),
177 };
178 }
179
180 let columns_info = rows[0].columns();
183 let columns_slice = columns_info.as_ref();
184
185 if columns_slice.is_empty() {
191 if rows.len() == 1 {
192 return SQL::from_iter([Token::DEFAULT, Token::VALUES]);
193 }
194 let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
195 for index in 0..rows.len() {
196 if index > 0 {
197 values_sql.push_mut(Token::COMMA);
198 }
199 values_sql.append_mut(SQL::from(Token::NULL).parens());
200 }
201 return SQL::raw("rowid")
202 .parens()
203 .push(Token::VALUES)
204 .append(values_sql);
205 }
206
207 let columns_sql = SQL::columns(columns_slice);
208 let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
209 for (idx, row) in rows.iter().enumerate() {
210 if idx > 0 {
211 values_sql.push_mut(Token::COMMA);
212 }
213 values_sql.push_mut(Token::LPAREN);
214 values_sql.append_mut(row.values());
215 values_sql.push_mut(Token::RPAREN);
216 }
217
218 columns_sql.parens().push(Token::VALUES).append(values_sql)
219}
220
221#[track_caller]
226pub(crate) fn standalone_offset<'a, P>(offset: P) -> SQL<'a, SQLiteValue<'a>>
227where
228 P: drizzle_core::PaginationArg<'a, SQLiteValue<'a>>,
229{
230 SQL::from(Token::LIMIT)
231 .append(SQL::raw("-1"))
232 .append(core_helpers::offset(offset))
233}
234
235pub(crate) fn before_upsert<'a>(sql: SQL<'a, SQLiteValue<'a>>) -> SQL<'a, SQLiteValue<'a>> {
244 let mut depth = 0usize;
245 let mut ends_in_from = false;
246 for chunk in &sql.chunks {
247 match chunk {
248 SQLChunk::Token(Token::LPAREN) => depth += 1,
249 SQLChunk::Token(Token::RPAREN) => depth = depth.saturating_sub(1),
250 SQLChunk::Token(Token::SELECT) if depth == 0 => ends_in_from = false,
251 SQLChunk::Token(Token::FROM) if depth == 0 => ends_in_from = true,
252 SQLChunk::Token(
253 Token::WHERE
254 | Token::GROUP
255 | Token::HAVING
256 | Token::WINDOW
257 | Token::ORDER
258 | Token::LIMIT,
259 ) if depth == 0 => ends_in_from = false,
260 _ => {}
261 }
262 }
263 if ends_in_from {
264 sql.push(Token::WHERE).append(SQL::raw("true"))
265 } else {
266 sql
267 }
268}
269
270pub(crate) fn returning<'a, 'b, I>(columns: I) -> SQL<'a, SQLiteValue<'a>>
272where
273 I: ToSQL<'a, SQLiteValue<'a>>,
274{
275 let columns = columns.into_sql();
276 let columns = if columns.chunks.is_empty() {
277 SQL::from(Token::STAR)
278 } else {
279 columns
280 };
281 SQL::from(Token::RETURNING).append(columns)
282}