Skip to main content

drizzle_postgres/
helpers.rs

1//! SQL fragment builders used by the `PostgreSQL` query builders.
2//!
3//! Most items are internal. The public ones render join fragments for
4//! hand-built SQL: `join`, `left_join`, `natural_join` and the other
5//! `*_join` functions (`JOIN table ON condition`), and the `PostgreSQL`-only
6//! `*_join_using` functions (`JOIN table USING (columns)`). [`JoinArg`] is the
7//! argument type of the select builder's `.join(...)` methods.
8
9#[cfg(not(feature = "std"))]
10use crate::prelude::*;
11use crate::traits::PostgresTable;
12use crate::values::PostgresValue;
13use drizzle_core::{SQL, SQLTableInfo, ToSQL, Token, helpers, traits::SQLModel};
14
15// Re-export core helpers with PostgresValue type for convenience
16pub(crate) use helpers::{
17    delete, except, except_all, from, group_by_expr, having, intersect, intersect_all, limit,
18    offset, order_by, select, select_distinct, set, union, union_all, update, r#where,
19};
20
21// Re-export Join from core
22pub use drizzle_core::Join;
23
24/// A table or derived table that can be joined.
25#[doc(hidden)]
26pub trait JoinSource<'a>: join_source_private::Sealed {
27    type JoinedTable;
28
29    fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>>;
30}
31
32mod join_source_private {
33    pub trait Sealed {}
34}
35
36impl<'a, Table> join_source_private::Sealed for Table where Table: PostgresTable<'a> {}
37
38impl<'a, Name, Projection, Query> join_source_private::Sealed
39    for drizzle_core::Derived<'a, PostgresValue<'a>, Name, Projection, Query>
40where
41    Name: drizzle_core::Tag,
42    Projection: drizzle_core::DerivedProjection<Name>,
43    Query: ToSQL<'a, PostgresValue<'a>>,
44{
45}
46
47impl<'a, Table> JoinSource<'a> for Table
48where
49    Table: PostgresTable<'a>,
50{
51    type JoinedTable = Table;
52
53    fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>> {
54        self.into_sql()
55    }
56}
57
58impl<'a, Name, Projection, Query> JoinSource<'a>
59    for drizzle_core::Derived<'a, PostgresValue<'a>, Name, Projection, Query>
60where
61    Name: drizzle_core::Tag,
62    Projection: drizzle_core::DerivedProjection<Name>,
63    Query: ToSQL<'a, PostgresValue<'a>>,
64{
65    type JoinedTable = Self;
66
67    fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>> {
68        self.into_sql()
69    }
70}
71
72/// A source or legacy tuple accepted by [`crate::builder::SelectBuilder::cross_join`].
73///
74/// A bare source renders `CROSS JOIN`. The legacy `(source, predicate)`
75/// form renders the equivalent portable `INNER JOIN ... ON ...`, because
76/// PostgreSQL does not allow an `ON` clause after `CROSS JOIN`.
77#[doc(hidden)]
78pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
79    type JoinedTable;
80    /// Sources read by the legacy `ON` predicate (see [`drizzle_core::scope`]).
81    type OnSources;
82
83    fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>>;
84}
85
86mod cross_join_arg_private {
87    pub trait Sealed {}
88
89    impl<'a, Source> Sealed for Source where Source: super::JoinSource<'a> {}
90
91    impl<'a, Source, Condition> Sealed for (Source, Condition)
92    where
93        Source: super::JoinSource<'a>,
94        Condition: drizzle_core::ToSQL<'a, crate::values::PostgresValue<'a>>,
95    {
96    }
97}
98
99impl<'a, Source, FromTable> CrossJoinArg<'a, FromTable> for Source
100where
101    Source: JoinSource<'a>,
102{
103    type JoinedTable = Source::JoinedTable;
104    type OnSources = ();
105
106    fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>> {
107        Join::new()
108            .cross()
109            .into_sql()
110            .append(self.into_join_source_sql())
111    }
112}
113
114impl<'a, Source, Condition, FromTable> CrossJoinArg<'a, FromTable> for (Source, Condition)
115where
116    Source: JoinSource<'a>,
117    Condition: ToSQL<'a, PostgresValue<'a>> + drizzle_core::expr::ExprSources,
118{
119    type JoinedTable = Source::JoinedTable;
120    type OnSources = Condition::Sources;
121
122    fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>> {
123        let (source, condition) = self;
124        Join::new()
125            .inner()
126            .into_sql()
127            .append(source.into_join_source_sql())
128            .push(Token::ON)
129            .append(condition.into_sql())
130    }
131}
132
133drizzle_core::impl_join_arg_trait!(
134    table_trait: PostgresTable<'a>,
135    table_info_trait: SQLTableInfo,
136    condition_trait: ToSQL<'a, PostgresValue<'a>>,
137    join_source_trait: JoinSource<'a>,
138    value_type: PostgresValue<'a>,
139);
140
141// Generate all join helper functions using the shared macro
142drizzle_core::impl_join_helpers!(
143    table_trait: PostgresTable<'a>,
144    condition_trait: ToSQL<'a, PostgresValue<'a>>,
145    sql_type: SQL<'a, PostgresValue<'a>>,
146);
147
148/// Renders `SELECT DISTINCT ON (on) columns`.
149pub(crate) fn select_distinct_on<'a, On, Columns>(
150    on: On,
151    columns: Columns,
152) -> SQL<'a, PostgresValue<'a>>
153where
154    On: ToSQL<'a, PostgresValue<'a>>,
155    Columns: ToSQL<'a, PostgresValue<'a>>,
156{
157    SQL::from_iter([Token::SELECT, Token::DISTINCT, Token::ON, Token::LPAREN])
158        .append(on.into_sql())
159        .push(Token::RPAREN)
160        .append(columns.into_sql())
161}
162
163//------------------------------------------------------------------------------
164// USING clause internal helper (PostgreSQL-specific)
165//------------------------------------------------------------------------------
166
167fn join_using_internal<'a, Table>(
168    table: Table,
169    join: Join,
170    columns: impl ToSQL<'a, PostgresValue<'a>>,
171) -> SQL<'a, PostgresValue<'a>>
172where
173    Table: PostgresTable<'a>,
174{
175    join.into_sql()
176        .append(table.into_sql())
177        .push(Token::USING)
178        .push(Token::LPAREN)
179        .append(columns.into_sql())
180        .push(Token::RPAREN)
181}
182
183//------------------------------------------------------------------------------
184// USING clause versions of JOIN functions (PostgreSQL-specific)
185//------------------------------------------------------------------------------
186
187/// Renders `JOIN table USING (columns)`, which joins rows whose same-named
188/// columns are equal.
189///
190/// `columns` is rendered as given. `PostgreSQL` requires bare column names in
191/// `USING`, so pass identifiers such as `SQL::ident("id")`; a table column
192/// renders qualified (`"posts"."id"`), which the server rejects.
193///
194/// # Examples
195///
196/// ```rust
197/// # extern crate self as drizzle;
198/// # mod _drizzle {
199/// #     pub mod core { pub use drizzle_core::*; }
200/// #     pub mod error { pub use drizzle_core::error::*; }
201/// #     pub mod types { pub use drizzle_types::*; }
202/// #     pub mod migrations { pub use drizzle_migrations::*; }
203/// #     pub use drizzle_types::Dialect;
204/// #     pub use drizzle_types as ddl;
205/// #     pub mod postgres {
206/// #         pub mod values { pub use drizzle_postgres::values::*; }
207/// #         pub mod traits { pub use drizzle_postgres::traits::*; }
208/// #         pub mod common { pub use drizzle_postgres::common::*; }
209/// #         pub mod attrs { pub use drizzle_postgres::attrs::*; }
210/// #         pub mod builder { pub use drizzle_postgres::builder::*; }
211/// #         pub mod helpers { pub use drizzle_postgres::helpers::*; }
212/// #         pub mod expr { pub use drizzle_postgres::expr::*; }
213/// #         pub mod types { pub use drizzle_postgres::types::*; }
214/// #         #[cfg(feature = "aws-data-api")]
215/// #         pub mod aws_data_api { pub use drizzle_postgres::aws_data_api::*; }
216/// #         pub struct Row;
217/// #         impl Row {
218/// #             pub fn get<'a, I, T>(&'a self, _: I) -> T { unimplemented!() }
219/// #             pub fn try_get<'a, I, T>(&'a self, _: I) -> Result<T, Box<dyn std::error::Error + Sync + Send>> { unimplemented!() }
220/// #         }
221/// #         pub mod prelude {
222/// #             pub use drizzle_macros::{PostgresTable, PostgresSchema, PostgresIndex};
223/// #             pub use drizzle_postgres::attrs::*;
224/// #             pub use drizzle_postgres::common::PostgresSchemaType;
225/// #             pub use drizzle_postgres::traits::{PostgresColumn, PostgresTable};
226/// #             pub use drizzle_postgres::values::{PostgresInsertValue, PostgresUpdateValue, PostgresValue};
227/// #             pub use drizzle_core::*;
228/// #         }
229/// #     }
230/// # }
231/// # pub use _drizzle::*;
232/// # pub use const_format;
233/// # fn main() {
234/// # use drizzle::postgres::prelude::*;
235/// # use drizzle::postgres::builder::QueryBuilder;
236/// # #[PostgresTable(name = "users")]
237/// # struct User {
238/// #     #[column(serial, primary)]
239/// #     id: i32,
240/// #     name: String,
241/// #     email: Option<String>,
242/// # }
243/// # #[PostgresTable(name = "posts")]
244/// # struct Post {
245/// #     #[column(serial, primary)]
246/// #     id: i32,
247/// #     #[column(references = User::id)]
248/// #     author_id: i32,
249/// #     title: String,
250/// # }
251/// # #[derive(PostgresSchema)]
252/// # struct Schema {
253/// #     user: User,
254/// #     post: Post,
255/// # }
256/// # let db = QueryBuilder::new::<Schema>();
257/// # let Schema { user, post } = Schema::new();
258/// use drizzle::core::SQL;
259/// use drizzle::postgres::values::PostgresValue;
260///
261/// let join = drizzle::postgres::helpers::join_using(post, SQL::<PostgresValue>::ident("id"));
262/// assert_eq!(join.sql(), r#"JOIN "posts" USING ("id")"#);
263/// # }
264/// ```
265pub fn join_using<'a, Table>(
266    table: Table,
267    columns: impl ToSQL<'a, PostgresValue<'a>>,
268) -> SQL<'a, PostgresValue<'a>>
269where
270    Table: PostgresTable<'a>,
271{
272    join_using_internal(table, Join::new(), columns)
273}
274
275/// Renders `INNER JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
276pub fn inner_join_using<'a, Table>(
277    table: Table,
278    columns: impl ToSQL<'a, PostgresValue<'a>>,
279) -> SQL<'a, PostgresValue<'a>>
280where
281    Table: PostgresTable<'a>,
282{
283    join_using_internal(table, Join::new().inner(), columns)
284}
285
286/// Renders `LEFT JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
287pub fn left_join_using<'a, Table>(
288    table: Table,
289    columns: impl ToSQL<'a, PostgresValue<'a>>,
290) -> SQL<'a, PostgresValue<'a>>
291where
292    Table: PostgresTable<'a>,
293{
294    join_using_internal(table, Join::new().left(), columns)
295}
296
297/// Renders `LEFT OUTER JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
298pub fn left_outer_join_using<'a, Table>(
299    table: Table,
300    columns: impl ToSQL<'a, PostgresValue<'a>>,
301) -> SQL<'a, PostgresValue<'a>>
302where
303    Table: PostgresTable<'a>,
304{
305    join_using_internal(table, Join::new().left().outer(), columns)
306}
307
308/// Renders `RIGHT JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
309pub fn right_join_using<'a, Table>(
310    table: Table,
311    columns: impl ToSQL<'a, PostgresValue<'a>>,
312) -> SQL<'a, PostgresValue<'a>>
313where
314    Table: PostgresTable<'a>,
315{
316    join_using_internal(table, Join::new().right(), columns)
317}
318
319/// Renders `RIGHT OUTER JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
320pub fn right_outer_join_using<'a, Table>(
321    table: Table,
322    columns: impl ToSQL<'a, PostgresValue<'a>>,
323) -> SQL<'a, PostgresValue<'a>>
324where
325    Table: PostgresTable<'a>,
326{
327    join_using_internal(table, Join::new().right().outer(), columns)
328}
329
330/// Renders `FULL JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
331pub fn full_join_using<'a, Table>(
332    table: Table,
333    columns: impl ToSQL<'a, PostgresValue<'a>>,
334) -> SQL<'a, PostgresValue<'a>>
335where
336    Table: PostgresTable<'a>,
337{
338    join_using_internal(table, Join::new().full(), columns)
339}
340
341/// Renders `FULL OUTER JOIN table USING (columns)`. See [`join_using`] for how to pass `columns`.
342pub fn full_outer_join_using<'a, Table>(
343    table: Table,
344    columns: impl ToSQL<'a, PostgresValue<'a>>,
345) -> SQL<'a, PostgresValue<'a>>
346where
347    Table: PostgresTable<'a>,
348{
349    join_using_internal(table, Join::new().full().outer(), columns)
350}
351
352// Note: NATURAL JOINs don't use USING clause as they automatically match column names
353// CROSS JOIN also doesn't use USING clause as it produces Cartesian product
354
355/// Renders `INSERT INTO table`.
356pub(crate) fn insert<'a, Table>(table: &Table) -> SQL<'a, PostgresValue<'a>>
357where
358    Table: PostgresTable<'a>,
359{
360    SQL::from_iter([Token::INSERT, Token::INTO]).append(table)
361}
362
363/// Creates the rows of an INSERT statement.
364///
365/// Rows usually set the same columns. A `None` passed to a `with_*` setter
366/// leaves that column to its default without changing the row's type, so
367/// rows can differ; then every row lists the union of the columns, with
368/// `DEFAULT` where it sets none.
369pub(crate) fn values<'a, Table, T>(
370    rows: impl IntoIterator<Item = Table::Insert<T>>,
371) -> SQL<'a, PostgresValue<'a>>
372where
373    Table: PostgresTable<'a>,
374{
375    let rows: Vec<_> = rows.into_iter().collect();
376
377    if rows.is_empty() {
378        return SQL::from(Token::VALUES);
379    }
380
381    let columns_info = rows[0].columns();
382    let columns_slice = columns_info.as_ref();
383    if rows[1..]
384        .iter()
385        .any(|row| row.columns().as_ref() != columns_slice)
386    {
387        let rows_sql = drizzle_core::helpers::insert_values_with_defaults(
388            rows.iter()
389                .map(|row| (row.columns(), row.values()))
390                .collect(),
391        );
392        if let Some(rows_sql) = rows_sql {
393            return rows_sql;
394        }
395    }
396
397    if columns_slice.is_empty() {
398        // `DEFAULT VALUES` inserts one row. A query without columns inserts
399        // one all-default row per result row.
400        // Raw text, not SELECT/FROM tokens: the renderer expands a bare
401        // `SELECT` token followed by `FROM` into a projection.
402        return if rows.len() == 1 {
403            SQL::from_iter([Token::DEFAULT, Token::VALUES])
404        } else {
405            SQL::raw("SELECT FROM").append(SQL::func(
406                "generate_series",
407                SQL::number(1)
408                    .push(Token::COMMA)
409                    .append(SQL::number(rows.len())),
410            ))
411        };
412    }
413
414    let columns_sql = SQL::columns(columns_slice);
415    let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
416    for (idx, row) in rows.iter().enumerate() {
417        if idx > 0 {
418            values_sql.push_mut(Token::COMMA);
419        }
420        values_sql.push_mut(Token::LPAREN);
421        values_sql.append_mut(row.values());
422        values_sql.push_mut(Token::RPAREN);
423    }
424
425    columns_sql.parens().push(Token::VALUES).append(values_sql)
426}
427
428/// Renders `RETURNING columns`, or `RETURNING *` for an empty list.
429pub(crate) fn returning<'a, 'b, I>(columns: I) -> SQL<'a, PostgresValue<'a>>
430where
431    I: ToSQL<'a, PostgresValue<'a>>,
432{
433    let columns = columns.into_sql();
434    let columns = if columns.chunks.is_empty() {
435        SQL::from(Token::STAR)
436    } else {
437        columns
438    };
439    SQL::from(Token::RETURNING).append(columns)
440}
441
442//------------------------------------------------------------------------------
443// FOR UPDATE/SHARE row locking (PostgreSQL-specific)
444//------------------------------------------------------------------------------
445
446/// Renders `FOR UPDATE`.
447pub(crate) fn for_update<'a>() -> SQL<'a, PostgresValue<'a>> {
448    SQL::from_iter([Token::FOR, Token::UPDATE])
449}
450
451/// Renders `FOR SHARE`.
452pub(crate) fn for_share<'a>() -> SQL<'a, PostgresValue<'a>> {
453    SQL::from_iter([Token::FOR, Token::SHARE])
454}
455
456/// Renders `FOR NO KEY UPDATE`.
457pub(crate) fn for_no_key_update<'a>() -> SQL<'a, PostgresValue<'a>> {
458    SQL::from_iter([Token::FOR, Token::NO, Token::KEY, Token::UPDATE])
459}
460
461/// Renders `FOR KEY SHARE`.
462pub(crate) fn for_key_share<'a>() -> SQL<'a, PostgresValue<'a>> {
463    SQL::from_iter([Token::FOR, Token::KEY, Token::SHARE])
464}
465
466/// Renders `FOR UPDATE OF table`, with the bare table name `PostgreSQL` requires.
467pub(crate) fn for_update_of<'a>(table_name: &str) -> SQL<'a, PostgresValue<'a>> {
468    SQL::from_iter([Token::FOR, Token::UPDATE, Token::OF])
469        .append(SQL::ident(String::from(table_name)))
470}
471
472/// Renders `FOR SHARE OF table`, with the bare table name `PostgreSQL` requires.
473pub(crate) fn for_share_of<'a>(table_name: &str) -> SQL<'a, PostgresValue<'a>> {
474    SQL::from_iter([Token::FOR, Token::SHARE, Token::OF])
475        .append(SQL::ident(String::from(table_name)))
476}
477
478/// Renders `NOWAIT`.
479pub(crate) fn nowait<'a>() -> SQL<'a, PostgresValue<'a>> {
480    SQL::from(Token::NOWAIT)
481}
482
483/// Renders `SKIP LOCKED`.
484pub(crate) fn skip_locked<'a>() -> SQL<'a, PostgresValue<'a>> {
485    SQL::from_iter([Token::SKIP, Token::LOCKED])
486}