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]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "BatchInsert",
            "values", &self.values, "_marker", &&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<V, Tab, QId, STABLE_QUERY_ID>
39{
40    type QueryId = QId;
41
42    const HAS_STATIC_QUERY_ID: bool = STABLE_QUERY_ID;
43}
44
45impl<T, Table, QId, DB, const HAS_STATIC_QUERY_ID: bool> CanInsertInSingleQuery<DB>
46    for BatchInsert<T, Table, QId, HAS_STATIC_QUERY_ID>
47where
48    T: CanInsertInSingleQuery<DB>,
49DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
50{
51    fn rows_to_insert(&self) -> Option<usize> {
52        self.values.rows_to_insert()
53    }
54}
55
56impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for [T; N]
57where
58DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
59{
60    fn rows_to_insert(&self) -> Option<usize> {
61        Some(N)
62    }
63}
64
65impl<T, DB, const N: usize> CanInsertInSingleQuery<DB> for Box<[T; N]>
66where
67    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
68{
69    fn rows_to_insert(&self) -> Option<usize> {
70        Some(N)
71    }
72}
73
74impl<T, DB> CanInsertInSingleQuery<DB> for [T]
75where
76    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>
77{
78    fn rows_to_insert(&self) -> Option<usize> {
79        Some(self.len())
80    }
81}
82
83impl<T, DB> CanInsertInSingleQuery<DB> for Vec<T>
84where
85    DB: Backend+ SqlDialect<InsertWithDefaultKeyword = sql_dialect::default_keyword_for_insert::IsoSqlDefaultKeyword>,
86{
87    fn rows_to_insert(&self) -> Option<usize> {
88        Some(self.len())
89    }
90}
91
92impl<Tab, DB, V, QId, const HAS_STATIC_QUERY_ID: bool> QueryFragment<DB>
93    for BatchInsert<V, Tab, QId, HAS_STATIC_QUERY_ID>
94where
95    DB: Backend,
96    Self: QueryFragment<DB, DB::BatchInsertSupport>,
97{
98    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
99        <Self as QueryFragment<DB, DB::BatchInsertSupport>>::walk_ast(self, pass)
100    }
101}
102
103impl<Tab, DB, V, QId, const HAS_STATIC_QUERY_ID: bool>
104    QueryFragment<DB, sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport>
105    for BatchInsert<Vec<ValuesClause<V, Tab>>, Tab, QId, HAS_STATIC_QUERY_ID>
106where
107    DB: Backend
108        + SqlDialect<
109            BatchInsertSupport = sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport,
110        >,
111    DB::InsertWithDefaultKeyword: sql_dialect::default_keyword_for_insert::SupportsDefaultKeyword,
112    ValuesClause<V, Tab>: QueryFragment<DB>,
113    V: QueryFragment<DB>,
114{
115    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
116        if !HAS_STATIC_QUERY_ID {
117            out.unsafe_to_cache_prepared();
118        }
119
120        let mut values = self.values.iter();
121        if let Some(value) = values.next() {
122            value.walk_ast(out.reborrow())?;
123        }
124        for value in values {
125            out.push_sql(", (");
126            value.values.walk_ast(out.reborrow())?;
127            out.push_sql(")");
128        }
129        Ok(())
130    }
131}