Skip to main content

diesel/mariadb/returning/
old_impl.rs

1//! `UPDATE ... RETURNING OLD_VALUE(col)` support for MariaDB 13.0 and later.
2
3use crate::deserialize::SqlTypeLikeMarker;
4use crate::expression::subselect::SubselectGroupBy;
5use crate::expression::{
6    AppearsOnTable, Expression, QueryMetadata, SelectableExpression, TypedExpressionType,
7    ValidGrouping, is_aggregate,
8};
9use crate::mariadb::Mariadb;
10use crate::query_builder::returning::{OldIdent, ReturningQuerySource, UpdateStmt};
11use crate::query_builder::{AstPass, QueryFragment, QueryId};
12use crate::query_dsl::load_dsl::CompatibleType;
13use crate::query_source::{AppearsInFromClause, Column};
14use crate::result::QueryResult;
15use crate::sql_types::{IntoNotNullable, IntoNullable, SingleValue};
16use crate::util::TupleSize;
17use core::marker::PhantomData;
18
19/// Wraps a column to refer to its pre-modification value in the `RETURNING`
20/// clause of a Mariadb `UPDATE` statement.
21///
22/// This is the type returned by [`old_value()`](old_value()).
23#[derive(#[automatically_derived]
impl<C: ::core::fmt::Debug> ::core::fmt::Debug for OldValue<C> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "OldValue",
            "_column", &&self._column)
    }
}Debug, #[automatically_derived]
impl<C: ::core::clone::Clone> ::core::clone::Clone for OldValue<C> {
    #[inline]
    fn clone(&self) -> Self {
        Self { _column: ::core::clone::Clone::clone(&self._column) }
    }
}Clone, #[automatically_derived]
impl<C: ::core::marker::Copy> ::core::marker::Copy for OldValue<C> { }Copy, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<C: diesel::query_builder::QueryId> diesel::query_builder::QueryId
            for OldValue<C> {
            type QueryId =
                OldValue<<C as diesel::query_builder::QueryId>::QueryId>;
            const HAS_STATIC_QUERY_ID: bool =
                <C as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID &&
                    true;
            const IS_WINDOW_FUNCTION: bool =
                <C as diesel::query_builder::QueryId>::IS_WINDOW_FUNCTION ||
                    false;
        }
    };QueryId)]
24pub struct OldValue<C> {
25    _column: C,
26}
27
28impl<C> OldValue<C> {
29    pub(crate) fn new(c: C) -> Self {
30        OldValue { _column: c }
31    }
32}
33
34/// Refer to the pre-modification value of `col` in a Mariadb `RETURNING`
35/// clause.
36///
37/// This corresponds to the SQL `RETURNING OLD_VALUE(col)` syntax introduced in
38/// Mariadb 13.0.
39///
40/// # Requires Mariadb 13.0 or newer
41///
42/// Diesel emits `OLD_VALUE(col)` in the SQL it sends to the database. Earlier
43/// versions of Mariadb will reject the query at execution time.
44///
45/// # Statement compatibility
46///
47/// `old_value(col)` is valid inside the `RETURNING` clause of:
48///
49/// * an `UPDATE` statement, as a whole `RETURNING` item that loads into the
50///   same Rust types as `col` (since every returned row necessarily came from a
51///   pre-existing row).
52///
53/// Use of `old_value(col)` in `INSERT` or `DELETE` `RETURNING`
54/// is rejected at compile time, because it is invalid
55/// there. (Note that `ON CONFLICT DO NOTHING` never returns untouched rows.)
56///
57/// On MariaDB 13.0, `old_value` of a view column that is an expression or a
58/// constant crashes the server
59/// ([MDEV-40125](https://jira.mariadb.org/browse/MDEV-40125)). MariaDB 13.1.1
60/// returns the right value.
61///
62/// # Example
63///
64/// ```rust
65/// # include!("../../doctest_setup.rs");
66/// #
67/// # #[cfg(feature = "mariadb")]
68/// # fn main() {
69/// #     use schema::users::dsl::*;
70/// #     use diesel::mariadb::returning::old_value;
71/// #     let connection = &mut establish_connection();
72/// #     // `RETURNING OLD_VALUE(col)` requires Mariadb 13.0+
73/// #     if !mariadb_server_supports_update_returning(connection) { return; }
74/// let was_and_now = diesel::update(users.find(1))
75///     .set(name.eq("Updated"))
76///     .returning((old_value(name), name))
77///     .get_result::<(String, String)>(connection);
78/// assert_eq!(Ok(("Sean".to_string(), "Updated".to_string())), was_and_now);
79/// # }
80/// # #[cfg(not(feature = "mariadb"))]
81/// # fn main() {}
82/// ```
83pub fn old_value<C: Column>(col: C) -> old_value<C> {
84    OldValue::new(col)
85}
86
87impl<C> Expression for OldValue<C>
88where
89    C: Column + Expression,
90    C::SqlType: SingleValue,
91{
92    type SqlType = OldValueOf<C::SqlType>;
93}
94
95/// SQL type of [`old_value(col)`](old_value()). It loads into the same Rust
96/// types as `ST` through `CompatibleType`, and since it implements neither
97/// `SqlType` nor `SingleValue`, no operator, function or comparison accepts it.
98/// Those are the positions where
99/// [MDEV-40126](https://jira.mariadb.org/browse/MDEV-40126) breaks `OLD_VALUE`.
100#[derive(#[automatically_derived]
impl<ST: ::core::fmt::Debug> ::core::fmt::Debug for OldValueOf<ST> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "OldValueOf",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<ST: ::core::clone::Clone> ::core::clone::Clone for OldValueOf<ST> {
    #[inline]
    fn clone(&self) -> Self { Self(::core::clone::Clone::clone(&self.0)) }
}Clone, #[automatically_derived]
impl<ST: ::core::marker::Copy> ::core::marker::Copy for OldValueOf<ST> { }Copy, #[automatically_derived]
impl<ST: ::core::default::Default> ::core::default::Default for OldValueOf<ST>
    {
    #[inline]
    fn default() -> Self { Self(::core::default::Default::default()) }
}Default, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<ST: diesel::query_builder::QueryId>
            diesel::query_builder::QueryId for OldValueOf<ST> {
            type QueryId =
                OldValueOf<<ST as diesel::query_builder::QueryId>::QueryId>;
            const HAS_STATIC_QUERY_ID: bool =
                <ST as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID &&
                    true;
            const IS_WINDOW_FUNCTION: bool =
                <ST as diesel::query_builder::QueryId>::IS_WINDOW_FUNCTION ||
                    false;
        }
    };QueryId)]
101pub struct OldValueOf<ST>(PhantomData<ST>);
102
103#[diagnostic::do_not_recommend]
104impl<U, ST> CompatibleType<U, Mariadb> for OldValueOf<ST>
105where
106    ST: CompatibleType<U, Mariadb>,
107{
108    type SqlType = ST;
109}
110
111impl<ST> TypedExpressionType for OldValueOf<ST> {}
112
113impl<ST> SqlTypeLikeMarker for OldValueOf<ST> {
114    type SqlType = ST;
115}
116
117impl<ST: TupleSize> TupleSize for OldValueOf<ST> {
118    const SIZE: usize = ST::SIZE;
119}
120
121impl<C> ValidGrouping<()> for OldValue<C>
122where
123    C: Column,
124{
125    type IsAggregate = is_aggregate::No;
126}
127
128// `OLD_VALUE` always names the row of the outer statement
129impl<C, GB, From> ValidGrouping<SubselectGroupBy<GB, From>> for OldValue<C>
130where
131    C: Column,
132    Self: ValidGrouping<GB>,
133{
134    type IsAggregate = <Self as ValidGrouping<GB>>::IsAggregate;
135}
136
137impl<ST: IntoNullable> IntoNullable for OldValueOf<ST> {
138    type Nullable = OldValueOf<ST::Nullable>;
139}
140
141impl<ST: IntoNotNullable> IntoNotNullable for OldValueOf<ST> {
142    type NotNullable = OldValueOf<ST::NotNullable>;
143}
144
145impl<ST> QueryMetadata<OldValueOf<ST>> for Mariadb
146where
147    Self: QueryMetadata<ST>,
148{
149    fn row_metadata(lookup: &mut Self::MetadataLookup, out: &mut Vec<Option<Self::TypeMetadata>>) {
150        <Self as QueryMetadata<ST>>::row_metadata(lookup, out);
151    }
152}
153
154// `OldValue<C>` is selectable on a `RETURNING` clause whose statement-kind marker
155// is `UpdateStmt`. Since `OLD_VALUE` is only valid in `UPDATE ... RETURNING`
156//
157// It's not selectable on any subqueries in the returning clause
158impl<C, QS> AppearsOnTable<ReturningQuerySource<UpdateStmt, QS>> for OldValue<C>
159where
160    C: Column,
161    Self: Expression,
162    // Check that we have exactly one `old` identifier in the `RETURNING` clause.
163    ReturningQuerySource<UpdateStmt, QS>:
164        AppearsInFromClause<OldIdent, Count = crate::query_source::Once>,
165    // Check that the `old` identifier relates the table of that column.
166    ReturningQuerySource<UpdateStmt, QS>: AppearsInFromClause<
167            ReturningQuerySource<OldIdent, C::Table>,
168            Count = crate::query_source::Once,
169        >,
170{
171}
172
173// `old_value(col)` is only valid in `UPDATE ... RETURNING` for Mariadb,
174// so we don't need to implement `SelectableExpression` for any other statement kinds.
175impl<C> SelectableExpression<ReturningQuerySource<UpdateStmt, C::Table>> for OldValue<C>
176where
177    C: Column,
178    Self: AppearsOnTable<ReturningQuerySource<UpdateStmt, C::Table>>,
179{
180}
181
182impl<C> QueryFragment<Mariadb> for OldValue<C>
183where
184    C: Column,
185{
186    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mariadb>) -> QueryResult<()> {
187        out.push_sql("OLD_VALUE(");
188        out.push_identifier(C::NAME)?;
189        out.push_sql(")");
190        Ok(())
191    }
192}
193
194pub use return_type_helpers_reexported::*;
195
196pub(crate) mod return_type_helpers_reexported {
197    use super::OldValue;
198
199    /// The return type of [`old_value(col)`](super::old_value()).
200    #[allow(non_camel_case_types)]
201    pub type old_value<C> = OldValue<C>;
202}