@@ -24,13 +24,16 @@ use serde::{Deserialize, Serialize};
2424#[ cfg( feature = "visitor" ) ]
2525use sqlparser_derive:: { Visit , VisitMut } ;
2626
27- use crate :: display_utils:: { indented_list, Indent , SpaceOrNewline } ;
27+ use crate :: {
28+ ast:: display_separated,
29+ display_utils:: { indented_list, Indent , SpaceOrNewline } ,
30+ } ;
2831
2932use super :: {
3033 display_comma_separated, helpers:: attached_token:: AttachedToken , query:: InputFormatClause ,
3134 Assignment , Expr , FromTable , Ident , InsertAliases , MysqlInsertPriority , ObjectName , OnInsert ,
32- OrderByExpr , Query , SelectItem , Setting , SqliteOnConflict , TableObject , TableWithJoins ,
33- UpdateTableFromKind ,
35+ OrderByExpr , Query , SelectInto , SelectItem , Setting , SqliteOnConflict , TableFactor ,
36+ TableObject , TableWithJoins , UpdateTableFromKind , Values ,
3437} ;
3538
3639/// INSERT statement.
@@ -310,3 +313,340 @@ impl Display for Update {
310313 Ok ( ( ) )
311314 }
312315}
316+
317+ /// MERGE statement.
318+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
319+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
320+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
321+ pub struct Merge {
322+ /// The `MERGE` token that starts the statement.
323+ pub merge_token : AttachedToken ,
324+ /// optional INTO keyword
325+ pub into : bool ,
326+ /// Specifies the table to merge
327+ pub table : TableFactor ,
328+ /// Specifies the table or subquery to join with the target table
329+ pub source : TableFactor ,
330+ /// Specifies the expression on which to join the target table and source
331+ pub on : Box < Expr > ,
332+ /// Specifies the actions to perform when values match or do not match.
333+ pub clauses : Vec < MergeClause > ,
334+ // Specifies the output to save changes in MSSQL
335+ pub output : Option < OutputClause > ,
336+ }
337+
338+ impl Display for Merge {
339+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
340+ write ! (
341+ f,
342+ "MERGE{int} {table} USING {source} " ,
343+ int = if self . into { " INTO" } else { "" } ,
344+ table = self . table,
345+ source = self . source,
346+ ) ?;
347+ write ! ( f, "ON {on} " , on = self . on) ?;
348+ write ! ( f, "{}" , display_separated( & self . clauses, " " ) ) ?;
349+ if let Some ( ref output) = self . output {
350+ write ! ( f, " {output}" ) ?;
351+ }
352+ Ok ( ( ) )
353+ }
354+ }
355+
356+ /// A `WHEN` clause within a `MERGE` Statement
357+ ///
358+ /// Example:
359+ /// ```sql
360+ /// WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN DELETE
361+ /// ```
362+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
363+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
364+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
365+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
366+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
367+ pub struct MergeClause {
368+ /// The `WHEN` token that starts the sub-expression.
369+ pub when_token : AttachedToken ,
370+ pub clause_kind : MergeClauseKind ,
371+ pub predicate : Option < Expr > ,
372+ pub action : MergeAction ,
373+ }
374+
375+ impl Display for MergeClause {
376+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
377+ let MergeClause {
378+ when_token : _,
379+ clause_kind,
380+ predicate,
381+ action,
382+ } = self ;
383+
384+ write ! ( f, "WHEN {clause_kind}" ) ?;
385+ if let Some ( pred) = predicate {
386+ write ! ( f, " AND {pred}" ) ?;
387+ }
388+ write ! ( f, " THEN {action}" )
389+ }
390+ }
391+
392+ /// Variant of `WHEN` clause used within a `MERGE` Statement.
393+ ///
394+ /// Example:
395+ /// ```sql
396+ /// MERGE INTO T USING U ON FALSE WHEN MATCHED THEN DELETE
397+ /// ```
398+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
399+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
400+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
401+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
402+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
403+ pub enum MergeClauseKind {
404+ /// `WHEN MATCHED`
405+ Matched ,
406+ /// `WHEN NOT MATCHED`
407+ NotMatched ,
408+ /// `WHEN MATCHED BY TARGET`
409+ ///
410+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
411+ NotMatchedByTarget ,
412+ /// `WHEN MATCHED BY SOURCE`
413+ ///
414+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
415+ NotMatchedBySource ,
416+ }
417+
418+ impl Display for MergeClauseKind {
419+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
420+ match self {
421+ MergeClauseKind :: Matched => write ! ( f, "MATCHED" ) ,
422+ MergeClauseKind :: NotMatched => write ! ( f, "NOT MATCHED" ) ,
423+ MergeClauseKind :: NotMatchedByTarget => write ! ( f, "NOT MATCHED BY TARGET" ) ,
424+ MergeClauseKind :: NotMatchedBySource => write ! ( f, "NOT MATCHED BY SOURCE" ) ,
425+ }
426+ }
427+ }
428+
429+ /// Underlying statement of a `WHEN` clause within a `MERGE` Statement
430+ ///
431+ /// Example
432+ /// ```sql
433+ /// INSERT (product, quantity) VALUES(product, quantity)
434+ /// ```
435+ ///
436+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
437+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
438+ /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
439+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
440+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
441+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
442+ pub enum MergeAction {
443+ /// An `INSERT` clause
444+ ///
445+ /// Example:
446+ /// ```sql
447+ /// INSERT (product, quantity) VALUES(product, quantity)
448+ /// ```
449+ Insert ( MergeInsertExpr ) ,
450+ /// An `UPDATE` clause
451+ ///
452+ /// Example:
453+ /// ```sql
454+ /// UPDATE SET quantity = T.quantity + S.quantity
455+ /// ```
456+ Update ( MergeUpdateExpr ) ,
457+ /// A plain `DELETE` clause
458+ Delete {
459+ /// The `DELETE` token that starts the sub-expression.
460+ delete_token : AttachedToken ,
461+ } ,
462+ }
463+
464+ impl Display for MergeAction {
465+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
466+ match self {
467+ MergeAction :: Insert ( insert) => {
468+ write ! ( f, "INSERT {insert}" )
469+ }
470+ MergeAction :: Update ( update) => {
471+ write ! ( f, "UPDATE {update}" )
472+ }
473+ MergeAction :: Delete { .. } => {
474+ write ! ( f, "DELETE" )
475+ }
476+ }
477+ }
478+ }
479+
480+ /// The type of expression used to insert rows within a `MERGE` statement.
481+ ///
482+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
483+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
484+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
485+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
486+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
487+ pub enum MergeInsertKind {
488+ /// The insert expression is defined from an explicit `VALUES` clause
489+ ///
490+ /// Example:
491+ /// ```sql
492+ /// INSERT VALUES(product, quantity)
493+ /// ```
494+ Values ( Values ) ,
495+ /// The insert expression is defined using only the `ROW` keyword.
496+ ///
497+ /// Example:
498+ /// ```sql
499+ /// INSERT ROW
500+ /// ```
501+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
502+ Row ,
503+ }
504+
505+ impl Display for MergeInsertKind {
506+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
507+ match self {
508+ MergeInsertKind :: Values ( values) => {
509+ write ! ( f, "{values}" )
510+ }
511+ MergeInsertKind :: Row => {
512+ write ! ( f, "ROW" )
513+ }
514+ }
515+ }
516+ }
517+
518+ /// The expression used to insert rows within a `MERGE` statement.
519+ ///
520+ /// Examples
521+ /// ```sql
522+ /// INSERT (product, quantity) VALUES(product, quantity)
523+ /// INSERT ROW
524+ /// ```
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+ /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
529+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
530+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
531+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
532+ pub struct MergeInsertExpr {
533+ /// The `INSERT` token that starts the sub-expression.
534+ pub insert_token : AttachedToken ,
535+ /// Columns (if any) specified by the insert.
536+ ///
537+ /// Example:
538+ /// ```sql
539+ /// INSERT (product, quantity) VALUES(product, quantity)
540+ /// INSERT (product, quantity) ROW
541+ /// ```
542+ pub columns : Vec < Ident > ,
543+ /// The token, `[VALUES | ROW]` starting `kind`.
544+ pub kind_token : AttachedToken ,
545+ /// The insert type used by the statement.
546+ pub kind : MergeInsertKind ,
547+ /// An optional condition to restrict the insertion (Oracle specific)
548+ ///
549+ /// Enabled via [`Dialect::supports_merge_insert_predicate`](crate::dialect::Dialect::supports_merge_insert_predicate).
550+ pub insert_predicate : Option < Expr > ,
551+ }
552+
553+ impl Display for MergeInsertExpr {
554+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
555+ if !self . columns . is_empty ( ) {
556+ write ! ( f, "({}) " , display_comma_separated( self . columns. as_slice( ) ) ) ?;
557+ }
558+ write ! ( f, "{}" , self . kind) ?;
559+ if let Some ( predicate) = self . insert_predicate . as_ref ( ) {
560+ write ! ( f, " WHERE {}" , predicate) ?;
561+ }
562+ Ok ( ( ) )
563+ }
564+ }
565+
566+ /// The expression used to update rows within a `MERGE` statement.
567+ ///
568+ /// Examples
569+ /// ```sql
570+ /// UPDATE SET quantity = T.quantity + S.quantity
571+ /// ```
572+ ///
573+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
574+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
575+ /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
576+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
577+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
578+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
579+ pub struct MergeUpdateExpr {
580+ /// The `UPDATE` token that starts the sub-expression.
581+ pub update_token : AttachedToken ,
582+ /// The update assiment expressions
583+ pub assignments : Vec < Assignment > ,
584+ /// `where_clause` for the update (Oralce specific)
585+ ///
586+ /// Enabled via [`Dialect::supports_merge_update_predicate`](crate::dialect::Dialect::supports_merge_update_predicate).
587+ pub update_predicate : Option < Expr > ,
588+ /// `delete_clause` for the update "delete where" (Oracle specific)
589+ ///
590+ /// Enabled via [`Dialect::supports_merge_update_delete_predicate`](crate::dialect::Dialect::supports_merge_update_delete_predicate).
591+ pub delete_predicate : Option < Expr > ,
592+ }
593+
594+ impl Display for MergeUpdateExpr {
595+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
596+ write ! ( f, "SET {}" , display_comma_separated( & self . assignments) ) ?;
597+ if let Some ( predicate) = self . update_predicate . as_ref ( ) {
598+ write ! ( f, " WHERE {predicate}" ) ?;
599+ }
600+ if let Some ( predicate) = self . delete_predicate . as_ref ( ) {
601+ write ! ( f, " DELETE WHERE {predicate}" ) ?;
602+ }
603+ Ok ( ( ) )
604+ }
605+ }
606+
607+ /// A `OUTPUT` Clause in the end of a `MERGE` Statement
608+ ///
609+ /// Example:
610+ /// OUTPUT $action, deleted.* INTO dbo.temp_products;
611+ /// [mssql](https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
612+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
613+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
614+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
615+ pub enum OutputClause {
616+ Output {
617+ output_token : AttachedToken ,
618+ select_items : Vec < SelectItem > ,
619+ into_table : Option < SelectInto > ,
620+ } ,
621+ Returning {
622+ returning_token : AttachedToken ,
623+ select_items : Vec < SelectItem > ,
624+ } ,
625+ }
626+
627+ impl fmt:: Display for OutputClause {
628+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
629+ match self {
630+ OutputClause :: Output {
631+ output_token : _,
632+ select_items,
633+ into_table,
634+ } => {
635+ f. write_str ( "OUTPUT " ) ?;
636+ display_comma_separated ( select_items) . fmt ( f) ?;
637+ if let Some ( into_table) = into_table {
638+ f. write_str ( " " ) ?;
639+ into_table. fmt ( f) ?;
640+ }
641+ Ok ( ( ) )
642+ }
643+ OutputClause :: Returning {
644+ returning_token : _,
645+ select_items,
646+ } => {
647+ f. write_str ( "RETURNING " ) ?;
648+ display_comma_separated ( select_items) . fmt ( f)
649+ }
650+ }
651+ }
652+ }
0 commit comments