Skip to main content

drizzle_sqlite/
helpers.rs

1//! Free functions that render single `SQLite` clauses as [`SQL`].
2//!
3//! The builders in [`crate::builder`] call these internally. The public ones
4//! are the JOIN helpers (`join`, `left_join`, `natural_join`, ...), which
5//! render `<kind> JOIN table ON condition` fragments for hand-written SQL.
6
7#[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
16// Core clause helpers, used by the builders.
17pub(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/// A source that can follow `JOIN`: a `SQLite` table or a derived table
25/// (subquery with an alias).
26#[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/// A source or legacy tuple accepted by [`crate::builder::SelectBuilder::cross_join`].
74///
75/// A bare source renders `CROSS JOIN`. The legacy `(source, predicate)`
76/// form renders the equivalent portable `INNER JOIN ... ON ...`, because
77/// PostgreSQL does not allow an `ON` clause after `CROSS JOIN`.
78#[doc(hidden)]
79pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
80    type JoinedTable;
81    /// Sources read by the legacy `ON` predicate (see [`drizzle_core::scope`]).
82    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
142// Generate all join helper functions using the shared macro
143drizzle_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
149/// Renders `(columns) VALUES (...), (...)` for an INSERT.
150///
151/// Takes the column list from the first row, so all rows must set the same
152/// columns. With no rows it renders a bare `VALUES`; with no columns it
153/// renders `DEFAULT VALUES` (one row) or `(rowid) VALUES (NULL), ...`.
154pub(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    // Since all rows have the same PATTERN, they all have the same columns
167    // Get column info from the first row (all rows will have the same columns)
168    let columns_info = rows[0].columns();
169    let columns_slice = columns_info.as_ref();
170
171    // Every column takes its default. `DEFAULT VALUES` inserts one row, and
172    // SQLite has no `DEFAULT` keyword inside VALUES, so several such rows
173    // insert NULL into `rowid`, which assigns the next rowid and leaves every
174    // declared column to its default. (A WITHOUT ROWID table rejects this
175    // with "no column named rowid" instead of inserting a single row.)
176    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/// An `OFFSET` for a query without a `LIMIT`.
208///
209/// `SQLite` only accepts `OFFSET` as part of a `LIMIT` clause; a negative
210/// limit means "no limit".
211#[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
221/// Ends an `INSERT ... SELECT` so an upsert clause can follow it.
222///
223/// When the final `SELECT` ends in its `FROM` clause, SQLite parses the `ON`
224/// of `ON CONFLICT` as a join constraint and rejects the statement. A
225/// trailing `WHERE true` closes the `SELECT`, as SQLite's documentation
226/// recommends. Inserts from VALUES, and `SELECT`s that already end in a
227/// `WHERE`, `GROUP BY`, `HAVING`, `WINDOW`, `ORDER BY` or `LIMIT`, are
228/// returned unchanged.
229pub(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
256/// Renders `RETURNING columns`, or `RETURNING *` when `columns` is empty.
257pub(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}