sqlparser/ast/
dml.rs1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[cfg(not(feature = "std"))]
19use alloc::{boxed::Box, format, string::ToString, vec::Vec};
20
21use core::fmt::{self, Display};
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "visitor")]
25use sqlparser_derive::{Visit, VisitMut};
26
27use crate::{
28 ast::display_separated,
29 display_utils::{indented_list, Indent, SpaceOrNewline},
30};
31
32use super::{
33 display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
34 Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
35 OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict,
36 TableFactor, TableObject, TableWithJoins, UpdateTableFromKind, Values,
37};
38
39/// INSERT statement.
40#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
43pub struct Insert {
44 /// Token for the `INSERT` keyword (or its substitutes)
45 pub insert_token: AttachedToken,
46 /// A query optimizer hint
47 ///
48 /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
49 /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
50 pub optimizer_hint: Option<OptimizerHint>,
51 /// Only for Sqlite
52 pub or: Option<SqliteOnConflict>,
53 /// Only for mysql
54 pub ignore: bool,
55 /// INTO - optional keyword
56 pub into: bool,
57 /// TABLE
58 pub table: TableObject,
59 /// table_name as foo (for PostgreSQL)
60 pub table_alias: Option<Ident>,
61 /// COLUMNS
62 pub columns: Vec<Ident>,
63 /// Overwrite (Hive)
64 pub overwrite: bool,
65 /// A SQL query that specifies what to insert
66 pub source: Option<Box<Query>>,
67 /// MySQL `INSERT INTO ... SET`
68 /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
69 pub assignments: Vec<Assignment>,
70 /// partitioned insert (Hive)
71 pub partitioned: Option<Vec<Expr>>,
72 /// Columns defined after PARTITION
73 pub after_columns: Vec<Ident>,
74 /// whether the insert has the table keyword (Hive)
75 pub has_table_keyword: bool,
76 /// ON INSERT
77 pub on: Option<OnInsert>,
78 /// RETURNING
79 pub returning: Option<Vec<SelectItem>>,
80 /// Only for mysql
81 pub replace_into: bool,
82 /// Only for mysql
83 pub priority: Option<MysqlInsertPriority>,
84 /// Only for mysql
85 pub insert_alias: Option<InsertAliases>,
86 /// Settings used for ClickHouse.
87 ///
88 /// ClickHouse syntax: `INSERT INTO tbl SETTINGS format_template_resultset = '/some/path/resultset.format'`
89 ///
90 /// [ClickHouse `INSERT INTO`](https://clickhouse.com/docs/en/sql-reference/statements/insert-into)
91 pub settings: Option<Vec<Setting>>,
92 /// Format for `INSERT` statement when not using standard SQL format. Can be e.g. `CSV`,
93 /// `JSON`, `JSONAsString`, `LineAsString` and more.
94 ///
95 /// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
96 ///
97 /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
98 pub format_clause: Option<InputFormatClause>,
99}
100
101impl Display for Insert {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 let table_name = if let Some(alias) = &self.table_alias {
104 format!("{0} AS {alias}", self.table)
105 } else {
106 self.table.to_string()
107 };
108
109 if let Some(on_conflict) = self.or {
110 f.write_str("INSERT")?;
111 if let Some(hint) = self.optimizer_hint.as_ref() {
112 write!(f, " {hint}")?;
113 }
114 write!(f, " {on_conflict} INTO {table_name} ")?;
115 } else {
116 write!(
117 f,
118 "{start}",
119 start = if self.replace_into {
120 "REPLACE"
121 } else {
122 "INSERT"
123 }
124 )?;
125 if let Some(hint) = self.optimizer_hint.as_ref() {
126 write!(f, " {hint}")?;
127 }
128 if let Some(priority) = self.priority {
129 write!(f, " {priority}",)?;
130 }
131
132 write!(
133 f,
134 "{ignore}{over}{int}{tbl} {table_name} ",
135 table_name = table_name,
136 ignore = if self.ignore { " IGNORE" } else { "" },
137 over = if self.overwrite { " OVERWRITE" } else { "" },
138 int = if self.into { " INTO" } else { "" },
139 tbl = if self.has_table_keyword { " TABLE" } else { "" },
140 )?;
141 }
142 if !self.columns.is_empty() {
143 write!(f, "({})", display_comma_separated(&self.columns))?;
144 SpaceOrNewline.fmt(f)?;
145 }
146 if let Some(ref parts) = self.partitioned {
147 if !parts.is_empty() {
148 write!(f, "PARTITION ({})", display_comma_separated(parts))?;
149 SpaceOrNewline.fmt(f)?;
150 }
151 }
152 if !self.after_columns.is_empty() {
153 write!(f, "({})", display_comma_separated(&self.after_columns))?;
154 SpaceOrNewline.fmt(f)?;
155 }
156
157 if let Some(settings) = &self.settings {
158 write!(f, "SETTINGS {}", display_comma_separated(settings))?;
159 SpaceOrNewline.fmt(f)?;
160 }
161
162 if let Some(source) = &self.source {
163 source.fmt(f)?;
164 } else if !self.assignments.is_empty() {
165 write!(f, "SET")?;
166 indented_list(f, &self.assignments)?;
167 } else if let Some(format_clause) = &self.format_clause {
168 format_clause.fmt(f)?;
169 } else if self.columns.is_empty() {
170 write!(f, "DEFAULT VALUES")?;
171 }
172
173 if let Some(insert_alias) = &self.insert_alias {
174 write!(f, " AS {0}", insert_alias.row_alias)?;
175
176 if let Some(col_aliases) = &insert_alias.col_aliases {
177 if !col_aliases.is_empty() {
178 write!(f, " ({})", display_comma_separated(col_aliases))?;
179 }
180 }
181 }
182
183 if let Some(on) = &self.on {
184 write!(f, "{on}")?;
185 }
186
187 if let Some(returning) = &self.returning {
188 SpaceOrNewline.fmt(f)?;
189 f.write_str("RETURNING")?;
190 indented_list(f, returning)?;
191 }
192 Ok(())
193 }
194}
195
196/// DELETE statement.
197#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
198#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
199#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
200pub struct Delete {
201 /// Token for the `DELETE` keyword
202 pub delete_token: AttachedToken,
203 /// A query optimizer hint
204 ///
205 /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
206 /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
207 pub optimizer_hint: Option<OptimizerHint>,
208 /// Multi tables delete are supported in mysql
209 pub tables: Vec<ObjectName>,
210 /// FROM
211 pub from: FromTable,
212 /// USING (Snowflake, Postgres, MySQL)
213 pub using: Option<Vec<TableWithJoins>>,
214 /// WHERE
215 pub selection: Option<Expr>,
216 /// RETURNING
217 pub returning: Option<Vec<SelectItem>>,
218 /// ORDER BY (MySQL)
219 pub order_by: Vec<OrderByExpr>,
220 /// LIMIT (MySQL)
221 pub limit: Option<Expr>,
222}
223
224impl Display for Delete {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 f.write_str("DELETE")?;
227 if let Some(hint) = self.optimizer_hint.as_ref() {
228 f.write_str(" ")?;
229 hint.fmt(f)?;
230 }
231 if !self.tables.is_empty() {
232 indented_list(f, &self.tables)?;
233 }
234 match &self.from {
235 FromTable::WithFromKeyword(from) => {
236 f.write_str(" FROM")?;
237 indented_list(f, from)?;
238 }
239 FromTable::WithoutKeyword(from) => {
240 indented_list(f, from)?;
241 }
242 }
243 if let Some(using) = &self.using {
244 SpaceOrNewline.fmt(f)?;
245 f.write_str("USING")?;
246 indented_list(f, using)?;
247 }
248 if let Some(selection) = &self.selection {
249 SpaceOrNewline.fmt(f)?;
250 f.write_str("WHERE")?;
251 SpaceOrNewline.fmt(f)?;
252 Indent(selection).fmt(f)?;
253 }
254 if let Some(returning) = &self.returning {
255 SpaceOrNewline.fmt(f)?;
256 f.write_str("RETURNING")?;
257 indented_list(f, returning)?;
258 }
259 if !self.order_by.is_empty() {
260 SpaceOrNewline.fmt(f)?;
261 f.write_str("ORDER BY")?;
262 indented_list(f, &self.order_by)?;
263 }
264 if let Some(limit) = &self.limit {
265 SpaceOrNewline.fmt(f)?;
266 f.write_str("LIMIT")?;
267 SpaceOrNewline.fmt(f)?;
268 Indent(limit).fmt(f)?;
269 }
270 Ok(())
271 }
272}
273
274/// UPDATE statement.
275#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
278pub struct Update {
279 /// Token for the `UPDATE` keyword
280 pub update_token: AttachedToken,
281 /// A query optimizer hint
282 ///
283 /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
284 /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
285 pub optimizer_hint: Option<OptimizerHint>,
286 /// TABLE
287 pub table: TableWithJoins,
288 /// Column assignments
289 pub assignments: Vec<Assignment>,
290 /// Table which provide value to be set
291 pub from: Option<UpdateTableFromKind>,
292 /// WHERE
293 pub selection: Option<Expr>,
294 /// RETURNING
295 pub returning: Option<Vec<SelectItem>>,
296 /// SQLite-specific conflict resolution clause
297 pub or: Option<SqliteOnConflict>,
298 /// LIMIT
299 pub limit: Option<Expr>,
300}
301
302impl Display for Update {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 f.write_str("UPDATE ")?;
305 if let Some(hint) = self.optimizer_hint.as_ref() {
306 hint.fmt(f)?;
307 f.write_str(" ")?;
308 }
309 if let Some(or) = &self.or {
310 or.fmt(f)?;
311 f.write_str(" ")?;
312 }
313 self.table.fmt(f)?;
314 if let Some(UpdateTableFromKind::BeforeSet(from)) = &self.from {
315 SpaceOrNewline.fmt(f)?;
316 f.write_str("FROM")?;
317 indented_list(f, from)?;
318 }
319 if !self.assignments.is_empty() {
320 SpaceOrNewline.fmt(f)?;
321 f.write_str("SET")?;
322 indented_list(f, &self.assignments)?;
323 }
324 if let Some(UpdateTableFromKind::AfterSet(from)) = &self.from {
325 SpaceOrNewline.fmt(f)?;
326 f.write_str("FROM")?;
327 indented_list(f, from)?;
328 }
329 if let Some(selection) = &self.selection {
330 SpaceOrNewline.fmt(f)?;
331 f.write_str("WHERE")?;
332 SpaceOrNewline.fmt(f)?;
333 Indent(selection).fmt(f)?;
334 }
335 if let Some(returning) = &self.returning {
336 SpaceOrNewline.fmt(f)?;
337 f.write_str("RETURNING")?;
338 indented_list(f, returning)?;
339 }
340 if let Some(limit) = &self.limit {
341 SpaceOrNewline.fmt(f)?;
342 write!(f, "LIMIT {limit}")?;
343 }
344 Ok(())
345 }
346}
347
348/// A `MERGE` statement.
349#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
350#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
351#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
352pub struct Merge {
353 /// The `MERGE` token that starts the statement.
354 pub merge_token: AttachedToken,
355 /// A query optimizer hint
356 ///
357 /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
358 pub optimizer_hint: Option<OptimizerHint>,
359 /// optional INTO keyword
360 pub into: bool,
361 /// Specifies the table to merge
362 pub table: TableFactor,
363 /// Specifies the table or subquery to join with the target table
364 pub source: TableFactor,
365 /// Specifies the expression on which to join the target table and source
366 pub on: Box<Expr>,
367 /// Specifies the actions to perform when values match or do not match.
368 pub clauses: Vec<MergeClause>,
369 /// Specifies the output to save changes in MSSQL
370 pub output: Option<OutputClause>,
371}
372
373impl Display for Merge {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 f.write_str("MERGE")?;
376 if let Some(hint) = self.optimizer_hint.as_ref() {
377 write!(f, " {hint}")?;
378 }
379 if self.into {
380 write!(f, " INTO")?;
381 }
382 write!(
383 f,
384 " {table} USING {source} ",
385 table = self.table,
386 source = self.source
387 )?;
388 write!(f, "ON {on} ", on = self.on)?;
389 write!(f, "{}", display_separated(&self.clauses, " "))?;
390 if let Some(ref output) = self.output {
391 write!(f, " {output}")?;
392 }
393 Ok(())
394 }
395}
396
397/// A `WHEN` clause within a `MERGE` Statement
398///
399/// Example:
400/// ```sql
401/// WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN DELETE
402/// ```
403/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
404/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
405#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
406#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
407#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
408pub struct MergeClause {
409 /// The `WHEN` token that starts the sub-expression.
410 pub when_token: AttachedToken,
411 /// The type of `WHEN` clause.
412 pub clause_kind: MergeClauseKind,
413 /// An optional predicate to further restrict the clause.
414 pub predicate: Option<Expr>,
415 /// The action to perform when the clause is matched.
416 pub action: MergeAction,
417}
418
419impl Display for MergeClause {
420 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421 let MergeClause {
422 when_token: _,
423 clause_kind,
424 predicate,
425 action,
426 } = self;
427
428 write!(f, "WHEN {clause_kind}")?;
429 if let Some(pred) = predicate {
430 write!(f, " AND {pred}")?;
431 }
432 write!(f, " THEN {action}")
433 }
434}
435
436/// Variant of `WHEN` clause used within a `MERGE` Statement.
437///
438/// Example:
439/// ```sql
440/// MERGE INTO T USING U ON FALSE WHEN MATCHED THEN DELETE
441/// ```
442/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
443/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
444#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
445#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
446#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
447pub enum MergeClauseKind {
448 /// `WHEN MATCHED`
449 Matched,
450 /// `WHEN NOT MATCHED`
451 NotMatched,
452 /// `WHEN MATCHED BY TARGET`
453 ///
454 /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
455 NotMatchedByTarget,
456 /// `WHEN MATCHED BY SOURCE`
457 ///
458 /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
459 NotMatchedBySource,
460}
461
462impl Display for MergeClauseKind {
463 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464 match self {
465 MergeClauseKind::Matched => write!(f, "MATCHED"),
466 MergeClauseKind::NotMatched => write!(f, "NOT MATCHED"),
467 MergeClauseKind::NotMatchedByTarget => write!(f, "NOT MATCHED BY TARGET"),
468 MergeClauseKind::NotMatchedBySource => write!(f, "NOT MATCHED BY SOURCE"),
469 }
470 }
471}
472
473/// Underlying statement of a `WHEN` clause within a `MERGE` Statement
474///
475/// Example
476/// ```sql
477/// INSERT (product, quantity) VALUES(product, quantity)
478/// ```
479///
480/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
481/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
482/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
483#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
484#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
485#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
486pub enum MergeAction {
487 /// An `INSERT` clause
488 ///
489 /// Example:
490 /// ```sql
491 /// INSERT (product, quantity) VALUES(product, quantity)
492 /// ```
493 Insert(MergeInsertExpr),
494 /// An `UPDATE` clause
495 ///
496 /// Example:
497 /// ```sql
498 /// UPDATE SET quantity = T.quantity + S.quantity
499 /// ```
500 Update(MergeUpdateExpr),
501 /// A plain `DELETE` clause
502 Delete {
503 /// The `DELETE` token that starts the sub-expression.
504 delete_token: AttachedToken,
505 },
506}
507
508impl Display for MergeAction {
509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510 match self {
511 MergeAction::Insert(insert) => {
512 write!(f, "INSERT {insert}")
513 }
514 MergeAction::Update(update) => {
515 write!(f, "UPDATE {update}")
516 }
517 MergeAction::Delete { .. } => {
518 write!(f, "DELETE")
519 }
520 }
521 }
522}
523
524/// The type of expression used to insert rows within a `MERGE` statement.
525///
526/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
527/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
528#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
529#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
530#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
531pub enum MergeInsertKind {
532 /// The insert expression is defined from an explicit `VALUES` clause
533 ///
534 /// Example:
535 /// ```sql
536 /// INSERT VALUES(product, quantity)
537 /// ```
538 Values(Values),
539 /// The insert expression is defined using only the `ROW` keyword.
540 ///
541 /// Example:
542 /// ```sql
543 /// INSERT ROW
544 /// ```
545 /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
546 Row,
547}
548
549impl Display for MergeInsertKind {
550 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
551 match self {
552 MergeInsertKind::Values(values) => {
553 write!(f, "{values}")
554 }
555 MergeInsertKind::Row => {
556 write!(f, "ROW")
557 }
558 }
559 }
560}
561
562/// The expression used to insert rows within a `MERGE` statement.
563///
564/// Examples
565/// ```sql
566/// INSERT (product, quantity) VALUES(product, quantity)
567/// INSERT ROW
568/// ```
569///
570/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
571/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
572/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
573#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
574#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
575#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
576pub struct MergeInsertExpr {
577 /// The `INSERT` token that starts the sub-expression.
578 pub insert_token: AttachedToken,
579 /// Columns (if any) specified by the insert.
580 ///
581 /// Example:
582 /// ```sql
583 /// INSERT (product, quantity) VALUES(product, quantity)
584 /// INSERT (product, quantity) ROW
585 /// ```
586 pub columns: Vec<ObjectName>,
587 /// The token, `[VALUES | ROW]` starting `kind`.
588 pub kind_token: AttachedToken,
589 /// The insert type used by the statement.
590 pub kind: MergeInsertKind,
591 /// An optional condition to restrict the insertion (Oracle specific)
592 pub insert_predicate: Option<Expr>,
593}
594
595impl Display for MergeInsertExpr {
596 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597 if !self.columns.is_empty() {
598 write!(f, "({}) ", display_comma_separated(self.columns.as_slice()))?;
599 }
600 write!(f, "{}", self.kind)?;
601 if let Some(predicate) = self.insert_predicate.as_ref() {
602 write!(f, " WHERE {}", predicate)?;
603 }
604 Ok(())
605 }
606}
607
608/// The expression used to update rows within a `MERGE` statement.
609///
610/// Examples
611/// ```sql
612/// UPDATE SET quantity = T.quantity + S.quantity
613/// ```
614///
615/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
616/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
617/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
618#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
619#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
620#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
621pub struct MergeUpdateExpr {
622 /// The `UPDATE` token that starts the sub-expression.
623 pub update_token: AttachedToken,
624 /// The update assiment expressions
625 pub assignments: Vec<Assignment>,
626 /// `where_clause` for the update (Oralce specific)
627 pub update_predicate: Option<Expr>,
628 /// `delete_clause` for the update "delete where" (Oracle specific)
629 pub delete_predicate: Option<Expr>,
630}
631
632impl Display for MergeUpdateExpr {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 write!(f, "SET {}", display_comma_separated(&self.assignments))?;
635 if let Some(predicate) = self.update_predicate.as_ref() {
636 write!(f, " WHERE {predicate}")?;
637 }
638 if let Some(predicate) = self.delete_predicate.as_ref() {
639 write!(f, " DELETE WHERE {predicate}")?;
640 }
641 Ok(())
642 }
643}
644
645/// A `OUTPUT` Clause in the end of a `MERGE` Statement
646///
647/// Example:
648/// OUTPUT $action, deleted.* INTO dbo.temp_products;
649/// [mssql](https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
650#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
651#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
652#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
653pub enum OutputClause {
654 /// `OUTPUT` clause
655 Output {
656 /// The `OUTPUT` token that starts the sub-expression.
657 output_token: AttachedToken,
658 /// The select items to output
659 select_items: Vec<SelectItem>,
660 /// Optional `INTO` table to direct the output
661 into_table: Option<SelectInto>,
662 },
663 /// `RETURNING` clause
664 Returning {
665 /// The `RETURNING` token that starts the sub-expression.
666 returning_token: AttachedToken,
667 /// The select items to return
668 select_items: Vec<SelectItem>,
669 },
670}
671
672impl fmt::Display for OutputClause {
673 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
674 match self {
675 OutputClause::Output {
676 output_token: _,
677 select_items,
678 into_table,
679 } => {
680 f.write_str("OUTPUT ")?;
681 display_comma_separated(select_items).fmt(f)?;
682 if let Some(into_table) = into_table {
683 f.write_str(" ")?;
684 into_table.fmt(f)?;
685 }
686 Ok(())
687 }
688 OutputClause::Returning {
689 returning_token: _,
690 select_items,
691 } => {
692 f.write_str("RETURNING ")?;
693 display_comma_separated(select_items).fmt(f)
694 }
695 }
696 }
697}