@@ -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,334 @@ impl Display for Update {
310313 Ok ( ( ) )
311314 }
312315}
316+
317+ /// A `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 < ObjectName > ,
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+ pub insert_predicate : Option < Expr > ,
549+ }
550+
551+ impl Display for MergeInsertExpr {
552+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
553+ if !self . columns . is_empty ( ) {
554+ write ! ( f, "({}) " , display_comma_separated( self . columns. as_slice( ) ) ) ?;
555+ }
556+ write ! ( f, "{}" , self . kind) ?;
557+ if let Some ( predicate) = self . insert_predicate . as_ref ( ) {
558+ write ! ( f, " WHERE {}" , predicate) ?;
559+ }
560+ Ok ( ( ) )
561+ }
562+ }
563+
564+ /// The expression used to update rows within a `MERGE` statement.
565+ ///
566+ /// Examples
567+ /// ```sql
568+ /// UPDATE SET quantity = T.quantity + S.quantity
569+ /// ```
570+ ///
571+ /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
572+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
573+ /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
574+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
575+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
576+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
577+ pub struct MergeUpdateExpr {
578+ /// The `UPDATE` token that starts the sub-expression.
579+ pub update_token : AttachedToken ,
580+ /// The update assiment expressions
581+ pub assignments : Vec < Assignment > ,
582+ /// `where_clause` for the update (Oralce specific)
583+ pub update_predicate : Option < Expr > ,
584+ /// `delete_clause` for the update "delete where" (Oracle specific)
585+ pub delete_predicate : Option < Expr > ,
586+ }
587+
588+ impl Display for MergeUpdateExpr {
589+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
590+ write ! ( f, "SET {}" , display_comma_separated( & self . assignments) ) ?;
591+ if let Some ( predicate) = self . update_predicate . as_ref ( ) {
592+ write ! ( f, " WHERE {predicate}" ) ?;
593+ }
594+ if let Some ( predicate) = self . delete_predicate . as_ref ( ) {
595+ write ! ( f, " DELETE WHERE {predicate}" ) ?;
596+ }
597+ Ok ( ( ) )
598+ }
599+ }
600+
601+ /// A `OUTPUT` Clause in the end of a `MERGE` Statement
602+ ///
603+ /// Example:
604+ /// OUTPUT $action, deleted.* INTO dbo.temp_products;
605+ /// [mssql](https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
606+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
607+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
608+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
609+ pub enum OutputClause {
610+ Output {
611+ output_token : AttachedToken ,
612+ select_items : Vec < SelectItem > ,
613+ into_table : Option < SelectInto > ,
614+ } ,
615+ Returning {
616+ returning_token : AttachedToken ,
617+ select_items : Vec < SelectItem > ,
618+ } ,
619+ }
620+
621+ impl fmt:: Display for OutputClause {
622+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
623+ match self {
624+ OutputClause :: Output {
625+ output_token : _,
626+ select_items,
627+ into_table,
628+ } => {
629+ f. write_str ( "OUTPUT " ) ?;
630+ display_comma_separated ( select_items) . fmt ( f) ?;
631+ if let Some ( into_table) = into_table {
632+ f. write_str ( " " ) ?;
633+ into_table. fmt ( f) ?;
634+ }
635+ Ok ( ( ) )
636+ }
637+ OutputClause :: Returning {
638+ returning_token : _,
639+ select_items,
640+ } => {
641+ f. write_str ( "RETURNING " ) ?;
642+ display_comma_separated ( select_items) . fmt ( f)
643+ }
644+ }
645+ }
646+ }
0 commit comments