Skip to main content

rust_ef/query/
execute_update.rs

1//! `ExecuteUpdateBuilder<T>` — bulk update operation builder.
2//!
3//! Produces `UPDATE <table> SET col = ?, ... WHERE <expr>` SQL with
4//! parameters ordered as: SET values first, then WHERE parameters.
5
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9use crate::entity::IEntityType;
10use crate::error::EFResult;
11use crate::provider::{DbValue, IDatabaseProvider};
12
13use super::compile::{
14    build_where_clause_with_offset, compile_bool_expr, PortablePlaceholderGenerator,
15};
16use super::state::QueryState;
17
18/// Builder for bulk update operations.
19#[derive(Clone)]
20pub struct ExecuteUpdateBuilder<T: IEntityType> {
21    pub(crate) state: QueryState,
22    pub(crate) updates: Vec<(String, DbValue)>,
23    pub(crate) provider: Option<Arc<dyn IDatabaseProvider>>,
24    pub(crate) _phantom: PhantomData<T>,
25}
26
27impl<T: IEntityType> ExecuteUpdateBuilder<T> {
28    /// Sets a named column to a DbValue.
29    ///
30    /// `#[doc(hidden)]` — called by `linq!(set b.views, 10; execute_update)`
31    /// expansion.
32    #[doc(hidden)]
33    pub fn set_column_internal(mut self, column: &'static str, value: impl Into<DbValue>) -> Self {
34        self.updates.push((column.to_string(), value.into()));
35        self
36    }
37
38    /// Returns the generated SQL.
39    pub fn to_sql(&self) -> String {
40        let gen: &dyn crate::provider::ISqlGenerator = self
41            .provider
42            .as_ref()
43            .map(|p| p.sql_generator())
44            .unwrap_or(&PortablePlaceholderGenerator);
45        let mut param_idx = 1usize;
46        let sets: Vec<String> = self
47            .updates
48            .iter()
49            .map(|(col, _)| {
50                let ph = gen.parameter_placeholder(param_idx);
51                param_idx += 1;
52                format!("{} = {}", col, ph)
53            })
54            .collect();
55        let where_clause = if let Some(ref expr) = self.state.where_expr {
56            let mut param_idx = param_idx;
57            compile_bool_expr(expr, gen, &mut param_idx)
58        } else {
59            build_where_clause_with_offset(&self.state.filters, gen, param_idx)
60        };
61        if where_clause.is_empty() {
62            format!("UPDATE {} SET {}", self.state.from, sets.join(", "))
63        } else {
64            format!(
65                "UPDATE {} SET {} WHERE {}",
66                self.state.from,
67                sets.join(", "),
68                where_clause
69            )
70        }
71    }
72
73    /// Returns params for this bulk update.
74    pub fn params(&self) -> Vec<DbValue> {
75        let mut params: Vec<DbValue> = self.updates.iter().map(|(_, v)| v.clone()).collect();
76        params.extend_from_slice(&self.state.parameters);
77        params
78    }
79
80    /// Executes the bulk update.
81    pub async fn execute(self) -> EFResult<u64> {
82        let sql = self.to_sql();
83        let params = self.params();
84        let provider = self.provider.as_ref().ok_or_else(|| {
85            crate::error::EFError::configuration(
86                "No provider attached to ExecuteUpdateBuilder.".to_string(),
87            )
88        })?;
89        let mut conn = provider.get_connection().await?;
90        conn.execute(&sql, &params).await
91    }
92}