databend_common_ast/ast/statements/
merge_into.rs1use std::fmt::Display;
16use std::fmt::Formatter;
17
18use derive_visitor::Drive;
19use derive_visitor::DriveMut;
20
21use crate::ast::Expr;
22use crate::ast::Hint;
23use crate::ast::Identifier;
24use crate::ast::Query;
25use crate::ast::TableAlias;
26use crate::ast::TableRef;
27use crate::ast::TableReference;
28use crate::ast::WithOptions;
29use crate::ast::write_comma_separated_list;
30use crate::ast::write_dot_separated_list;
31
32#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
33pub struct MutationUpdateExpr {
34 pub table: Option<Identifier>,
35 pub name: Identifier,
36 pub expr: Expr,
37}
38
39impl Display for MutationUpdateExpr {
40 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
41 if self.table.is_some() {
42 write!(f, "{}.", self.table.clone().unwrap())?;
43 }
44
45 write!(f, "{} = {}", self.name, self.expr)
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
50pub enum MatchOperation {
51 Update {
52 update_list: Vec<MutationUpdateExpr>,
53 is_star: bool,
54 },
55 Delete,
56}
57
58#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
59pub struct MatchedClause {
60 pub selection: Option<Expr>,
61 pub operation: MatchOperation,
62}
63
64#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
65pub struct InsertOperation {
66 pub columns: Option<Vec<Identifier>>,
67 pub values: Vec<Expr>,
68 pub is_star: bool,
69}
70
71#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
72pub struct UnmatchedClause {
73 pub selection: Option<Expr>,
74 pub insert_operation: InsertOperation,
75}
76
77#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
78pub enum MergeOption {
79 Match(MatchedClause),
80 Unmatch(UnmatchedClause),
81}
82
83#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
84pub struct MergeIntoStmt {
85 pub hints: Option<Hint>,
86 pub catalog: Option<Identifier>,
87 pub database: Option<Identifier>,
88 pub table_ident: Identifier,
89 pub source: MutationSource,
90 pub target_alias: Option<TableAlias>,
92 pub join_expr: Expr,
93 pub merge_options: Vec<MergeOption>,
94}
95
96impl Display for MergeIntoStmt {
97 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
98 write!(f, "MERGE INTO ")?;
99 write_dot_separated_list(
100 f,
101 self.catalog
102 .iter()
103 .chain(&self.database)
104 .chain(Some(&self.table_ident)),
105 )?;
106 if let Some(alias) = &self.target_alias {
107 write!(f, " AS {}", alias.name)?;
108 }
109 write!(f, " USING {} ON {}", self.source, self.join_expr)?;
110
111 for clause in &self.merge_options {
112 match clause {
113 MergeOption::Match(match_clause) => {
114 write!(f, " WHEN MATCHED ")?;
115 if let Some(e) = &match_clause.selection {
116 write!(f, "AND {} ", e)?;
117 }
118 write!(f, "THEN ")?;
119
120 match &match_clause.operation {
121 MatchOperation::Update {
122 update_list,
123 is_star,
124 } => {
125 if *is_star {
126 write!(f, "UPDATE *")?;
127 } else {
128 write!(f, "UPDATE SET ")?;
129 write_comma_separated_list(f, update_list)?;
130 }
131 }
132 MatchOperation::Delete => {
133 write!(f, "DELETE")?;
134 }
135 }
136 }
137 MergeOption::Unmatch(unmatch_clause) => {
138 write!(f, " WHEN NOT MATCHED ")?;
139 if let Some(e) = &unmatch_clause.selection {
140 write!(f, "AND {} ", e)?;
141 }
142 write!(f, "THEN INSERT")?;
143
144 if let Some(columns) = &unmatch_clause.insert_operation.columns
145 && !columns.is_empty()
146 {
147 write!(f, " (")?;
148 write_comma_separated_list(f, columns)?;
149 write!(f, ")")?;
150 }
151
152 if unmatch_clause.insert_operation.is_star {
153 write!(f, " *")?;
154 } else {
155 write!(f, " VALUES(")?;
156 write_comma_separated_list(
157 f,
158 unmatch_clause.insert_operation.values.clone(),
159 )?;
160 write!(f, ")")?;
161 }
162 }
163 }
164 }
165 Ok(())
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Drive, DriveMut)]
170pub enum MutationSource {
171 Select {
172 query: Box<Query>,
173 source_alias: TableAlias,
174 },
175 Table {
176 catalog: Option<Identifier>,
177 database: Option<Identifier>,
178 table: Identifier,
179 alias: Option<TableAlias>,
180 with_options: Option<WithOptions>,
181 },
182}
183
184impl MutationSource {
185 pub fn transform_table_reference(&self) -> TableReference {
186 match self {
187 Self::Select {
188 query,
189 source_alias,
190 } => TableReference::Subquery {
191 span: None,
192 lateral: false,
193 subquery: query.clone(),
194 alias: Some(source_alias.clone()),
195 pivot: None,
196 unpivot: None,
197 },
198 Self::Table {
199 catalog,
200 database,
201 table,
202 with_options,
203 alias,
204 } => TableReference::Table {
205 span: None,
206 table: TableRef {
207 catalog: catalog.clone(),
208 database: database.clone(),
209 table: table.clone(),
210 branch: None,
211 },
212 alias: alias.clone(),
213 temporal: None,
214 with_options: with_options.clone(),
215 pivot: None,
216 unpivot: None,
217 sample: None,
218 },
219 }
220 }
221}
222
223impl Display for MutationSource {
224 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
225 match self {
226 MutationSource::Select {
227 query,
228 source_alias,
229 } => write!(f, "({query}) AS {source_alias}"),
230
231 MutationSource::Table {
232 catalog,
233 database,
234 table,
235 with_options,
236 alias,
237 } => {
238 write_dot_separated_list(
239 f,
240 catalog.iter().chain(database.iter()).chain(Some(table)),
241 )?;
242 if let Some(with_options) = with_options {
243 write!(f, " {with_options}")?;
244 }
245 if alias.is_some() {
246 write!(f, " AS {}", alias.as_ref().unwrap())?;
247 }
248 Ok(())
249 }
250 }
251 }
252}