Skip to main content

radixdb_sql/ast/
dml.rs

1// Copyright 2026 RadixDB Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17/// INSERT statement
18#[derive(Debug, Clone, PartialEq)]
19pub struct InsertStatement {
20    pub token: Token,
21    pub table_name: Identifier,
22    pub columns: Vec<Identifier>,
23    /// VALUES clause rows (None if using SELECT)
24    pub values: Vec<Vec<Expression>>,
25    /// SELECT statement for INSERT INTO ... SELECT (None if using VALUES)
26    pub select: Option<Box<SelectStatement>>,
27    /// ON DUPLICATE KEY UPDATE (MySQL-style) or ON CONFLICT DO UPDATE (PostgreSQL-style)
28    pub on_duplicate: bool,
29    pub update_columns: Vec<Identifier>,
30    pub update_expressions: Vec<Expression>,
31    /// ON CONFLICT DO NOTHING (skip duplicates silently)
32    pub do_nothing: bool,
33    /// Conflict target columns for ON CONFLICT (col1, col2, ...)
34    pub conflict_target: Vec<Identifier>,
35    /// RETURNING clause expressions
36    pub returning: Vec<Expression>,
37}
38
39impl fmt::Display for InsertStatement {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let mut result = format!("INSERT INTO {}", self.table_name);
42        if !self.columns.is_empty() {
43            let cols: Vec<String> = self.columns.iter().map(|c| c.to_string()).collect();
44            result.push_str(&format!(" ({})", cols.join(", ")));
45        }
46        if let Some(ref select) = self.select {
47            // INSERT INTO ... SELECT
48            result.push_str(&format!(" {}", select));
49        } else {
50            // INSERT INTO ... VALUES
51            result.push_str(" VALUES ");
52            let rows: Vec<String> = self
53                .values
54                .iter()
55                .map(|row| {
56                    let vals: Vec<String> = row.iter().map(|v| v.to_string()).collect();
57                    format!("({})", vals.join(", "))
58                })
59                .collect();
60            result.push_str(&rows.join(", "));
61        }
62        if self.do_nothing {
63            result.push_str(" ON CONFLICT");
64            if !self.conflict_target.is_empty() {
65                let cols: Vec<String> =
66                    self.conflict_target.iter().map(|c| c.to_string()).collect();
67                result.push_str(&format!(" ({})", cols.join(", ")));
68            }
69            result.push_str(" DO NOTHING");
70        } else if self.on_duplicate {
71            if !self.conflict_target.is_empty() {
72                let cols: Vec<String> =
73                    self.conflict_target.iter().map(|c| c.to_string()).collect();
74                result.push_str(&format!(
75                    " ON CONFLICT ({}) DO UPDATE SET ",
76                    cols.join(", ")
77                ));
78            } else {
79                result.push_str(" ON DUPLICATE KEY UPDATE ");
80            }
81            let updates: Vec<String> = self
82                .update_columns
83                .iter()
84                .zip(&self.update_expressions)
85                .map(|(col, expr)| format!("{} = {}", col, expr))
86                .collect();
87            result.push_str(&updates.join(", "));
88        }
89        if !self.returning.is_empty() {
90            let returning: Vec<String> = self.returning.iter().map(|e| e.to_string()).collect();
91            result.push_str(&format!(" RETURNING {}", returning.join(", ")));
92        }
93        write!(f, "{}", result)
94    }
95}
96
97/// UPDATE statement
98#[derive(Debug, Clone, PartialEq)]
99pub struct UpdateStatement {
100    pub token: Token,
101    pub table_name: Identifier,
102    pub updates: FxHashMap<SmartString, Expression>,
103    pub where_clause: Option<Box<Expression>>,
104    /// RETURNING clause expressions
105    pub returning: Vec<Expression>,
106}
107
108impl fmt::Display for UpdateStatement {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        let mut result = format!("UPDATE {} SET ", self.table_name);
111        let updates: Vec<String> = self
112            .updates
113            .iter()
114            .map(|(col, val)| format!("{} = {}", col, val))
115            .collect();
116        result.push_str(&updates.join(", "));
117        if let Some(ref where_clause) = self.where_clause {
118            result.push_str(&format!(" WHERE {}", where_clause));
119        }
120        if !self.returning.is_empty() {
121            let returning: Vec<String> = self.returning.iter().map(|e| e.to_string()).collect();
122            result.push_str(&format!(" RETURNING {}", returning.join(", ")));
123        }
124        write!(f, "{}", result)
125    }
126}
127
128/// DELETE statement
129#[derive(Debug, Clone, PartialEq)]
130pub struct DeleteStatement {
131    pub token: Token,
132    pub table_name: Identifier,
133    /// Optional table alias (e.g., DELETE FROM users AS u)
134    pub alias: Option<Identifier>,
135    pub where_clause: Option<Box<Expression>>,
136    /// RETURNING clause expressions
137    pub returning: Vec<Expression>,
138}
139
140impl fmt::Display for DeleteStatement {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        let mut result = format!("DELETE FROM {}", self.table_name);
143        if let Some(ref alias) = self.alias {
144            result.push_str(&format!(" AS {}", alias));
145        }
146        if let Some(ref where_clause) = self.where_clause {
147            result.push_str(&format!(" WHERE {}", where_clause));
148        }
149        if !self.returning.is_empty() {
150            let returning: Vec<String> = self.returning.iter().map(|e| e.to_string()).collect();
151            result.push_str(&format!(" RETURNING {}", returning.join(", ")));
152        }
153        write!(f, "{}", result)
154    }
155}