elif_orm/query/
upsert.rs

1//! Query Builder UPSERT operations
2
3use super::builder::QueryBuilder;
4use super::types::*;
5use serde_json::Value;
6
7/// Builder for UPSERT operations (INSERT ... ON CONFLICT UPDATE)
8#[derive(Debug)]
9pub struct UpsertBuilder<M = ()> {
10    pub(crate) query_builder: QueryBuilder<M>,
11    pub(crate) conflict_columns: Vec<String>,
12    pub(crate) update_clauses: Vec<SetClause>,
13}
14
15impl<M> UpsertBuilder<M> {
16    /// Add an update clause for conflict resolution
17    pub fn update_set<T: Into<Value>>(mut self, column: &str, value: T) -> Self {
18        self.update_clauses.push(SetClause {
19            column: column.to_string(),
20            value: Some(value.into()),
21        });
22        self
23    }
24
25    /// Add a NULL update clause for conflict resolution
26    pub fn update_set_null(mut self, column: &str) -> Self {
27        self.update_clauses.push(SetClause {
28            column: column.to_string(),
29            value: None,
30        });
31        self
32    }
33
34    /// Finish building the upsert query
35    pub fn build(self) -> QueryBuilder<M> {
36        // For now, we'll return the underlying query builder
37        // In a full implementation, this would store the upsert information
38        self.query_builder
39    }
40
41    /// Generate SQL for the upsert operation
42    pub fn to_sql_with_params(&self) -> (String, Vec<String>) {
43        let mut sql = String::new();
44        let mut params = Vec::new();
45        let mut param_counter = 1;
46
47        // Start with INSERT
48        if let Some(table) = &self.query_builder.insert_table {
49            sql.push_str(&format!("INSERT INTO {}", table));
50
51            if !self.query_builder.set_clauses.is_empty() {
52                sql.push_str(" (");
53                let columns: Vec<String> = self
54                    .query_builder
55                    .set_clauses
56                    .iter()
57                    .map(|clause| clause.column.clone())
58                    .collect();
59                sql.push_str(&columns.join(", "));
60                sql.push_str(") VALUES (");
61
62                for (i, clause) in self.query_builder.set_clauses.iter().enumerate() {
63                    if i > 0 {
64                        sql.push_str(", ");
65                    }
66                    if let Some(ref value) = clause.value {
67                        sql.push_str(&format!("${}", param_counter));
68                        params.push(value.to_string());
69                        param_counter += 1;
70                    } else {
71                        sql.push_str("NULL");
72                    }
73                }
74                sql.push(')');
75            }
76        }
77
78        // Add ON CONFLICT clause
79        if !self.conflict_columns.is_empty() {
80            sql.push_str(&format!(
81                " ON CONFLICT ({}) DO UPDATE SET ",
82                self.conflict_columns.join(", ")
83            ));
84
85            for (i, clause) in self.update_clauses.iter().enumerate() {
86                if i > 0 {
87                    sql.push_str(", ");
88                }
89                sql.push_str(&format!("{} = ", clause.column));
90                if let Some(ref value) = clause.value {
91                    sql.push_str(&format!("${}", param_counter));
92                    params.push(value.to_string());
93                    param_counter += 1;
94                } else {
95                    sql.push_str("NULL");
96                }
97            }
98        }
99
100        (sql, params)
101    }
102}