Skip to main content

diesel/query_builder/insert_statement/
batch_insert.rs

1use super::ValuesClause;
2use crate::QueryResult;
3use crate::backend::{Backend, SqlDialect, sql_dialect};
4use crate::insertable::CanInsertInSingleQuery;
5use crate::query_builder::{AstPass, QueryFragment, QueryId};
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8use core::marker::PhantomData;
9
10/// This type represents a batch insert clause, which allows
11/// to insert multiple rows at once.
12///
13/// Custom backends can specialize the [`QueryFragment`]
14/// implementation via [`SqlDialect::BatchInsertSupport`]
15/// or provide fully custom [`ExecuteDsl`](crate::query_dsl::methods::ExecuteDsl)
16/// and [`LoadQuery`](crate::query_dsl::methods::LoadQuery) implementations
17#[cfg_attr(
18    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
19    cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")
20)]
21#[derive(#[automatically_derived]impl<V: ::core::fmt::Debug, Tab: ::core::fmt::Debug,
    QId: ::core::fmt::Debug, const STABLE_QUERY_ID : bool> ::core::fmt::Debug
    for BatchInsert<V, Tab, QId, STABLE_QUERY_ID> {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "BatchInsert values _marker", &self.values, &&self._marker)
    }
}Debug)]
22pub struct BatchInsert<V, Tab, QId, const STABLE_QUERY_ID: bool> {
23    /// List of values that should be inserted
24    pub values: V,
25    _marker: PhantomData<(QId, Tab)>,
26}
27
28impl<V, Tab, QId, const STABLE_QUERY_ID: bool> BatchInsert<V, Tab, QId, STABLE_QUERY_ID> {
29    pub(crate) fn new(values: V) -> Self {
30        Self {
31            values,
32            _marker: PhantomData,
33        }
34    }
35}
36
37impl<V, QId: 'static, Tab: 'static, const STABLE_QUERY_ID: bool> QueryId
38    for BatchInsert<Vec<V>, Tab, QId, STABLE_QUERY_ID>
39where
40    V: QueryId,
41{
42    type QueryId = QId;
43
44    const HAS_STATIC_QUERY_ID: bool = STABLE_QUERY_ID && V::HAS_STATIC_QUERY_ID;
45}
46
47impl<T, Table, QId, DB, const HAS_STATIC_QUERY_ID: bool> CanInsertInSingleQuery<DB>
48    for BatchInsert<T, Table, QId, HAS_STATIC_QUERY_ID>
49where
50    T: CanInsertInSingleQuery<DB>,
51    DB: Backend,
52    DB: SqlDialect<
53        InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword,
54    >,
55{
56    fn rows_to_insert(&self) -> Option<usize> {
57        self.values.rows_to_insert()
58    }
59}
60
61impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for [T; N]
62where
63DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
64{
65    fn rows_to_insert(&self) -> Option<usize> {
66        Some(N)
67    }
68}
69
70impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for Box<[T; N]>
71where
72    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
73{
74    fn rows_to_insert(&self) -> Option<usize> {
75        Some(N)
76    }
77}
78
79impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for alloc::rc::Rc<[T; N]>
80where
81    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
82{
83    fn rows_to_insert(&self) -> Option<usize> {
84        Some(N)
85    }
86}
87
88impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for alloc::sync::Arc<[T; N]>
89where
90    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
91{
92    fn rows_to_insert(&self) -> Option<usize> {
93        Some(N)
94    }
95}
96
97impl<T, DB> CanInsertInSingleQuery<DB> for [T]
98where
99    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
100{
101    fn rows_to_insert(&self) -> Option<usize> {
102        Some(self.len())
103    }
104}
105
106impl<T, DB> CanInsertInSingleQuery<DB> for Vec<T>
107where
108    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>,
109{
110    fn rows_to_insert(&self) -> Option<usize> {
111        Some(self.len())
112    }
113}
114
115impl<Tab, DB, V, QId, const HAS_STATIC_QUERY_ID: bool> QueryFragment<DB>
116    for BatchInsert<V, Tab, QId, HAS_STATIC_QUERY_ID>
117where
118    DB: Backend,
119    Self: QueryFragment<DB, DB::BatchInsertSupport>,
120{
121    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
122        <Self as QueryFragment<DB, DB::BatchInsertSupport>>::walk_ast(self, pass)
123    }
124}
125
126impl<Tab, DB, V, QId, const HAS_STATIC_QUERY_ID: bool>
127    QueryFragment<DB, sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport>
128    for BatchInsert<Vec<ValuesClause<V, Tab>>, Tab, QId, HAS_STATIC_QUERY_ID>
129where
130    DB: Backend
131        + SqlDialect<
132            BatchInsertSupport = sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport,
133        >,
134    DB::InsertWithDefaultKeyword: sql_dialect::default_keyword_for_insert::SupportsDefaultKeyword,
135    ValuesClause<V, Tab>: QueryFragment<DB>,
136    V: QueryFragment<DB>,
137{
138    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
139        if !HAS_STATIC_QUERY_ID {
140            out.unsafe_to_cache_prepared();
141        }
142
143        let mut values = self.values.iter();
144        if let Some(value) = values.next() {
145            value.walk_ast(out.reborrow())?;
146        }
147        for value in values {
148            out.push_sql(", (");
149            value.values.walk_ast(out.reborrow())?;
150            out.push_sql(")");
151        }
152        Ok(())
153    }
154}