Skip to main content

diesel/pg/returning/
old_impl.rs

1//! `RETURNING old.col` support for PostgreSQL 18 and later.
2
3use crate::backend::{Backend, sql_dialect};
4use crate::expression::{
5    AppearsOnTable, Expression, SelectableExpression, ValidGrouping, is_aggregate,
6};
7use crate::query_builder::returning::{
8    InsertStmtWithOnConflictDoUpdate, ReturningQuerySource, UpdateStmt,
9};
10use crate::query_builder::{AstPass, QueryFragment, QueryId};
11use crate::query_source::{AppearsInFromClause, Column, Never, Once, QueryRelation};
12use crate::result::QueryResult;
13
14/// Wraps a column to refer to its pre-modification value in the `RETURNING`
15/// clause of a PostgreSQL `UPDATE` or `INSERT ... ON CONFLICT ... DO UPDATE`
16/// statement.
17///
18/// This is the type returned by [`old()`](old()).
19#[derive(#[automatically_derived]
impl<C: ::core::fmt::Debug> ::core::fmt::Debug for Old<C> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "Old",
            "_column", &&self._column)
    }
}Debug, #[automatically_derived]
impl<C: ::core::clone::Clone> ::core::clone::Clone for Old<C> {
    #[inline]
    fn clone(&self) -> Old<C> {
        Old { _column: ::core::clone::Clone::clone(&self._column) }
    }
}Clone, #[automatically_derived]
impl<C: ::core::marker::Copy> ::core::marker::Copy for Old<C> { }Copy)]
20pub struct Old<C> {
21    _column: C,
22}
23
24impl<C> Old<C> {
25    pub(crate) fn new(c: C) -> Self {
26        Old { _column: c }
27    }
28}
29
30/// Refer to the pre-modification value of `col` in a PostgreSQL `RETURNING`
31/// clause.
32///
33/// This corresponds to the SQL `RETURNING old.col` syntax introduced in
34/// PostgreSQL 18.
35///
36/// # Requires PostgreSQL 18 or newer
37///
38/// Diesel emits `old.col` in the SQL it sends to the database. Earlier
39/// versions of PostgreSQL will reject the query at execution time.
40///
41/// # Statement compatibility
42///
43/// `old(col)` is valid inside the `RETURNING` clause of:
44///
45/// * an `UPDATE` statement, where it has the same Rust SQL type as `col`
46///   (since every returned row necessarily came from a pre-existing row).
47/// * an `INSERT ... ON CONFLICT ... DO UPDATE` statement, **but only when
48///   wrapped in [`.nullable()`]**: rows that were freshly inserted (rather
49///   than updated) have no `old` row, and `old.col` is `NULL` for them, so
50///   for type-safe deserialization you must opt into a nullable Rust SQL
51///   type. Writing `old(col)` directly (without `.nullable()`) in this
52///   context is rejected at compile time.
53///
54/// Use of `old(col)` in plain `INSERT` (without `ON CONFLICT ... DO UPDATE`)
55/// or `DELETE` `RETURNING` is rejected at compile time, because it is not useful
56/// there. (Note that `ON CONFLICT DO NOTHING` never returns untouched rows.)
57///
58/// [`.nullable()`]: crate::NullableExpressionMethods::nullable
59///
60/// # Example
61///
62/// ```rust
63/// # include!("../../doctest_setup.rs");
64/// #
65/// # #[cfg(feature = "postgres")]
66/// # fn main() {
67/// #     use schema::users::dsl::*;
68/// #     use diesel::pg::returning::old;
69/// #     let connection = &mut establish_connection();
70/// #     // `RETURNING old.col` requires PostgreSQL 18+
71/// #     let pg_version: i32 = diesel::dsl::sql::<diesel::sql_types::Integer>(
72/// #         "SELECT current_setting('server_version_num')::int",
73/// #     ).get_result(connection).unwrap();
74/// #     if pg_version < 180000 { return; }
75/// let was_and_now = diesel::update(users.find(1))
76///     .set(name.eq("Updated"))
77///     .returning((old(name), name))
78///     .get_result::<(String, String)>(connection);
79/// assert_eq!(Ok(("Sean".to_string(), "Updated".to_string())), was_and_now);
80/// # }
81/// # #[cfg(not(feature = "postgres"))]
82/// # fn main() {}
83/// ```
84pub fn old<C: Column>(col: C) -> Old<C> {
85    Old::new(col)
86}
87
88impl<C> QueryId for Old<C> {
89    type QueryId = ();
90
91    const HAS_STATIC_QUERY_ID: bool = false;
92}
93
94impl<C> Expression for Old<C>
95where
96    C: Column + Expression,
97{
98    type SqlType = <C as Expression>::SqlType;
99}
100
101impl<C> ValidGrouping<()> for Old<C>
102where
103    C: Column,
104{
105    type IsAggregate = is_aggregate::No;
106}
107
108// `Old<C>` is selectable on a `RETURNING` clause whose statement-kind marker
109// is `UpdateStmt`. It is deliberately *not* selectable on
110// `ReturningQuerySource<InsertStmtWithOnConflictDoUpdate, _>` directly — only
111// `Nullable<Old<C>>` is, via the existing `Nullable` machinery and the
112// `ToInnerJoin` mapping in `returning_query_source` (which makes
113// `InsertStmtWithOnConflictDoUpdate`'s inner-join "fall back" to `UpdateStmt`).
114// That's how we force users to write `old(col).nullable()` in an
115// `INSERT ... ON CONFLICT ... DO UPDATE RETURNING` and reject `old(col)`
116// alone at compile time.
117impl<C, QS> AppearsOnTable<QS> for Old<C>
118where
119    C: Column,
120    Self: Expression,
121    // Check that we have exactly one `old` identifier in the `RETURNING` clause.
122    QS: AppearsInFromClause<OldIdent, Count = crate::query_source::Once>,
123    // Check that the `old` identifier relates the table of that column.
124    QS: AppearsInFromClause<
125            ReturningQuerySource<OldIdent, C::Table>,
126            Count = crate::query_source::Once,
127        >,
128{
129}
130
131/// Represents the identifier `old` in the `RETURNING` clause.
132/// It is independent of the table of the column, and used as QS in marker in AppearsInFromClause.
133///
134/// We use this to typecheck that there is only one `old` identifier when we use `old`, so that
135/// there is no ambiguity, and also as a generic
136/// "any valid OLD statement-kind marker for ReturningQuerySource".
137#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OldIdent {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "OldIdent")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OldIdent {
    #[inline]
    fn clone(&self) -> OldIdent { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OldIdent { }Copy)]
138pub struct OldIdent;
139
140/// There is an `old.` in ReturningQuerySource<UpdateStmt, T>
141///
142/// Useful to check non-ambiguity of `old`
143impl<StmtKind, T> AppearsInFromClause<OldIdent> for ReturningQuerySource<StmtKind, T> {
144    type Count = Once;
145}
146/// There isn't one directly on tables
147/// (this is useful for typechecking `old` in subqueries in returning)
148impl<T> AppearsInFromClause<OldIdent> for T
149where
150    T: QueryRelation,
151{
152    type Count = Never;
153}
154/// There is an `old.` for T in ReturningQuerySource<UpdateStmt, T>
155impl<T> AppearsInFromClause<ReturningQuerySource<OldIdent, T>>
156    for ReturningQuerySource<UpdateStmt, T>
157{
158    type Count = Once;
159}
160/// There is an `old.` for T in ReturningQuerySource<InsertStmtWithOnConflictDoUpdate, T>
161impl<T> AppearsInFromClause<ReturningQuerySource<OldIdent, T>>
162    for ReturningQuerySource<InsertStmtWithOnConflictDoUpdate, T>
163{
164    type Count = Once;
165}
166
167// We intentionally did not add implementations for use of `old(col)` in plain `INSERT`
168// (without `ON CONFLICT ... DO UPDATE`) or `DELETE` `RETURNING`, because it is not useful
169// there as one can just use `RETURNING column_name`. (`ON CONFLICT DO NOTHING` never returns
170// untouched rows, so allowing `ON CONFLICT DO NOTHING ... RETURNING` might be misleading to
171// users on that regard - I, the author, have seen bugs caused by not being aware of this
172// PG behavior.)
173
174impl<C> SelectableExpression<ReturningQuerySource<UpdateStmt, C::Table>> for Old<C>
175where
176    C: Column,
177    Self: AppearsOnTable<ReturningQuerySource<UpdateStmt, C::Table>>,
178{
179}
180
181impl<C, DB> QueryFragment<DB> for Old<C>
182where
183    DB: Backend,
184    Self: QueryFragment<DB, DB::ReturningClause>,
185{
186    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
187        <Self as QueryFragment<DB, DB::ReturningClause>>::walk_ast(self, pass)
188    }
189}
190
191impl<C, DB> QueryFragment<DB, sql_dialect::returning_clause::PgLikeReturningClause> for Old<C>
192where
193    DB: Backend<ReturningClause = sql_dialect::returning_clause::PgLikeReturningClause>,
194    C: Column,
195{
196    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
197        out.push_sql("old.");
198        out.push_identifier(C::NAME)?;
199        Ok(())
200    }
201}
202
203pub use return_type_helpers_reexported::*;
204
205pub(crate) mod return_type_helpers_reexported {
206    use super::Old;
207
208    /// The return type of [`old(col)`](super::old()).
209    #[allow(non_camel_case_types)]
210    pub type old<C> = Old<C>;
211}