Skip to main content

toolu_orm_query/
update.rs

1//! UPDATE query builder with SET clause and filter support.
2
3use toolu_orm_core::dialect::Dialect;
4use toolu_orm_core::expr::Expr;
5use toolu_orm_core::query_column::Column;
6use toolu_orm_core::value::Value;
7
8use crate::where_clause::{append_where_for, cfg_single_backend, impl_filter};
9
10cfg_single_backend! {
11  use crate::exec_helpers::impl_execute;
12}
13
14// ── SetClause ─────────────────────────────────────────────────────────────────
15
16enum SetClause {
17  Value { column: String, value: Value },
18  Expr { column: String, sql: String },
19}
20
21// ── UpdateBuilder ─────────────────────────────────────────────────────────────
22
23pub struct UpdateBuilder {
24  table: String,
25  sets: Vec<SetClause>,
26  filters: Vec<Expr>,
27}
28
29impl_filter!(UpdateBuilder);
30
31impl UpdateBuilder {
32  pub fn new(table: &str) -> Self {
33    Self {
34      table: table.to_owned(),
35      sets: Vec::new(),
36      filters: Vec::new(),
37    }
38  }
39
40  pub fn set<T>(mut self, col: &Column<T>, val: impl Into<Value>) -> Self {
41    self.sets.push(SetClause::Value {
42      column: col.name.to_owned(),
43      value: val.into(),
44    });
45    self
46  }
47
48  pub fn set_expr<T>(mut self, col: &Column<T>, expr: &str) -> Self {
49    self.sets.push(SetClause::Expr {
50      column: col.name.to_owned(),
51      sql: expr.to_owned(),
52    });
53    self
54  }
55
56  pub fn to_sql_for(&self, dialect: Dialect) -> (String, Vec<Value>) {
57    let mut sql = String::new();
58    let mut params: Vec<Value> = Vec::new();
59
60    let mut set_params: Vec<Value> = Vec::new();
61    for clause in &self.sets {
62      if let SetClause::Value { value, .. } = clause {
63        set_params.push(value.clone());
64      }
65    }
66
67    sql.push_str(&format!(r#"UPDATE "{}""#, self.table));
68    sql.push_str(" SET ");
69
70    let mut set_idx = 1usize;
71    let set_parts: Vec<String> = self
72      .sets
73      .iter()
74      .map(|clause| match clause {
75        SetClause::Value { column, .. } => {
76          let part = format!(r#""{column}" = {}"#, dialect.param(set_idx));
77          set_idx += 1;
78          part
79        },
80        SetClause::Expr {
81          column,
82          sql: expr_sql,
83        } => {
84          format!(r#""{column}" = {expr_sql}"#)
85        },
86      })
87      .collect();
88
89    sql.push_str(&set_parts.join(", "));
90    params.extend(set_params);
91
92    append_where_for(&self.filters, &mut sql, &mut params, dialect);
93
94    (sql, params)
95  }
96
97  pub fn to_sql(&self) -> (String, Vec<Value>) {
98    self.to_sql_for(Dialect::CURRENT)
99  }
100}
101
102cfg_single_backend! {
103  impl_execute!(UpdateBuilder, "UPDATE");
104}