Skip to main content

diesel/query_builder/update_statement/
batch_update.rs

1#[cfg(doc)]
2use crate::backend::SqlDialect;
3use crate::backend::{Backend, sql_dialect};
4use crate::query_builder::{AstPass, QueryFragment};
5use crate::{QueryResult, query_builder::*};
6use core::marker::PhantomData;
7
8#[cfg(any(
9    feature = "__sqlite-shared",
10    feature = "postgres_backend",
11    feature = "mysql_backend",
12    feature = "mariadb_backend"
13))]
14pub(crate) const BATCH_UPDATE_ALIAS: &str = "__diesel_internal_temp_values";
15
16#[cfg(any(
17    feature = "__sqlite-shared",
18    feature = "postgres_backend",
19    feature = "mysql_backend",
20    feature = "mariadb_backend"
21))]
22pub trait BatchValueHelper<DB: Backend> {
23    fn assign<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
24
25    fn column_name<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
26
27    fn bind_value<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
28}
29
30#[cfg(any(
31    feature = "__sqlite-shared",
32    feature = "postgres_backend",
33    feature = "mysql_backend",
34    feature = "mariadb_backend"
35))]
36pub trait BatchAssignHelper<DB: Backend> {
37    fn batch_assign_identifier<'a>(&'a self, out: AstPass<'_, 'a, DB>) -> QueryResult<()>;
38}
39
40#[cfg(any(
41    feature = "__sqlite-shared",
42    feature = "postgres_backend",
43    feature = "mysql_backend",
44    feature = "mariadb_backend"
45))]
46pub trait BatchKeyHelper<PK, DB: Backend> {
47    fn bind_value<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
48
49    fn column_name<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
50
51    fn assign<'b>(pk: &'b PK, out: AstPass<'_, 'b, DB>) -> QueryResult<()>;
52}
53
54#[cfg(any(
55    feature = "__sqlite-shared",
56    feature = "postgres_backend",
57    feature = "mysql_backend",
58    feature = "mariadb_backend"
59))]
60impl<PK, DB, C> BatchKeyHelper<PK, DB> for C
61where
62    DB: Backend + crate::sql_types::HasSqlType<PK::SqlType>,
63    C: crate::serialize::ToSql<PK::SqlType, DB>,
64    PK: crate::expression::Expression + crate::Column + QueryFragment<DB>,
65    PK::SqlType: crate::sql_types::SingleValue,
66{
67    fn bind_value<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
68        out.push_bind_param(self)
69    }
70
71    fn column_name<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
72        out.push_identifier(PK::NAME)
73    }
74
75    fn assign<'b>(pk: &'b PK, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
76        pk.walk_ast(out.reborrow())?;
77        out.push_sql(" = ");
78        out.push_identifier(BATCH_UPDATE_ALIAS)?;
79        out.push_sql(".");
80        out.push_identifier(PK::NAME)?;
81        Ok(())
82    }
83}
84
85/// This type represents a batch update clause, which allows
86/// to update multiple rows at once.
87///
88/// Custom backends can specialize the [`QueryFragment`]
89/// implementation via [`SqlDialect::BatchUpdateSupport`]
90/// or provide fully custom [`ExecuteDsl`](crate::query_dsl::methods::ExecuteDsl)
91/// and [`LoadQuery`](crate::query_dsl::methods::LoadQuery) implementations
92#[cfg_attr(
93    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
94    cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
95)]
96// warn(dead_code) is a false positive for the fields 'values' and 'primary_key' as
97// specialized implementations for the backends actually use them.
98#[allow(dead_code)]
99#[derive(#[automatically_derived]
#[allow(dead_code)]
impl<I: ::core::fmt::Debug, C: ::core::fmt::Debug, PK: ::core::fmt::Debug,
    Tab: ::core::fmt::Debug> ::core::fmt::Debug for BatchUpdate<I, C, PK, Tab>
    {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "BatchUpdate",
            "values", &self.values, "primary_key", &self.primary_key,
            "_marker", &&self._marker)
    }
}Debug)]
100pub struct BatchUpdate<I, C, PK, Tab> {
101    // values.0 -> I: Identifier from Identifiable::Id
102    // values.1 -> V: Changeset from AsChangeset::Changeset
103    pub(crate) values: Vec<(I, C)>,
104    // PK: PrimaryKey will have same SqlType as I
105    pub(crate) primary_key: PK,
106    _marker: PhantomData<Tab>,
107}
108
109impl<I, C, PK, Tab> BatchUpdate<I, C, PK, Tab> {
110    /// Docs
111    pub fn new(values: Vec<(I, C)>, primary_key: PK) -> Self {
112        Self {
113            values,
114            primary_key,
115            _marker: PhantomData,
116        }
117    }
118}
119
120impl<I, C, PK, Tab, DB> QueryFragment<DB> for BatchUpdate<I, C, PK, Tab>
121where
122    DB: Backend,
123    DB::BatchUpdateSupport: sql_dialect::batch_update_support::SupportsBatchUpdate,
124    Self: QueryFragment<DB, DB::BatchUpdateSupport>,
125{
126    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
127        <Self as QueryFragment<DB, DB::BatchUpdateSupport>>::walk_ast(self, pass)
128    }
129}