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