From 2965ec6bcb1c9032ebdad69b5500182a716ec855 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Wed, 4 May 2022 15:08:47 +0200 Subject: [PATCH 1/2] Parse source of MERGE as table_factor Some MERGE queries need a table as a soruce, added proper test showing it --- src/ast/mod.rs | 8 +--- src/parser.rs | 6 +-- tests/sqlparser_common.rs | 94 ++++++++++++++++++++++----------------- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a052cd976..0f2ca44c5 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1040,9 +1040,7 @@ pub enum Statement { // Specifies the table to merge table: TableFactor, // Specifies the table or subquery to join with the target table - source: Box, - // Specifies alias to the table that is joined with target table - alias: Option, + source: TableFactor, // Specifies the expression on which to join the target table and source on: Box, // Specifies the actions to perform when values match or do not match. @@ -1751,14 +1749,10 @@ impl fmt::Display for Statement { Statement::Merge { table, source, - alias, on, clauses, } => { write!(f, "MERGE INTO {} USING {} ", table, source)?; - if let Some(a) = alias { - write!(f, "as {} ", a)?; - }; write!(f, "ON {} ", on)?; write!(f, "{}", display_separated(clauses, " ")) } diff --git a/src/parser.rs b/src/parser.rs index 203b7e3f8..e0fb8fe7f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4252,16 +4252,14 @@ impl<'a> Parser<'a> { let table = self.parse_table_factor()?; self.expect_keyword(Keyword::USING)?; - let source = self.parse_query_body(0)?; - let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; + let source = self.parse_table_factor()?; self.expect_keyword(Keyword::ON)?; let on = self.parse_expr()?; let clauses = self.parse_merge_clauses()?; Ok(Statement::Merge { table, - source: Box::new(source), - alias, + source: source, on: Box::new(on), clauses, }) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 43db2f769..bc0c491e1 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4330,12 +4330,11 @@ fn test_revoke() { #[test] fn parse_merge() { - let sql = "MERGE INTO s.bar AS dest USING (SELECT * FROM s.foo) as stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE"; + let sql = "MERGE INTO s.bar AS dest USING (SELECT * FROM s.foo) AS stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE"; match verified_stmt(sql) { Statement::Merge { table, source, - alias, on, clauses, } => { @@ -4353,44 +4352,47 @@ fn parse_merge() { ); assert_eq!( source, - Box::new(SetExpr::Query(Box::new(Query { - with: None, - body: SetExpr::Select(Box::new(Select { - distinct: false, - top: None, - projection: vec![SelectItem::Wildcard], - into: None, - from: vec![TableWithJoins { - relation: TableFactor::Table { - name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), - alias: None, - args: vec![], - with_hints: vec![], - }, - joins: vec![] - }], - lateral_views: vec![], - selection: None, - group_by: vec![], - cluster_by: vec![], - distribute_by: vec![], - sort_by: vec![], - having: None, - qualify: None - })), - order_by: vec![], - limit: None, - offset: None, - fetch: None, - lock: None - }))) - ); - assert_eq!( - alias, - Some(TableAlias { - name: Ident::new("stg"), - columns: vec![] - }) + TableFactor::Derived { + lateral: false, + subquery: Box::new(Query { + with: None, + body: SetExpr::Select(Box::new(Select { + distinct: false, + top: None, + projection: vec![SelectItem::Wildcard], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]), + alias: None, + args: vec![], + with_hints: vec![] + }, + joins: vec![] + }], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + qualify: None, + })), + order_by: vec![], + limit: None, + offset: None, + fetch: None, + lock: None + }), + alias: Some(TableAlias { + name: Ident { + value: "stg".to_string(), + quote_style: None + }, + columns: vec![] + }) + } ); assert_eq!( on, @@ -4468,6 +4470,18 @@ fn parse_merge() { } } +#[test] +fn test_merge_into_using_table() { + let sql = "MERGE INTO target_table USING source_table \ + ON target_table.id = source_table.oooid \ + WHEN MATCHED THEN \ + UPDATE SET target_table.description = source_table.description \ + WHEN NOT MATCHED THEN \ + INSERT (ID, description) VALUES (source_table.id, source_table.description)"; + + verified_stmt(sql); +} + #[test] fn test_lock() { let sql = "SELECT * FROM student WHERE id = '1' FOR UPDATE"; From 17127cffc6b7c553b07d0a9d66e12c3edbf72033 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Wed, 4 May 2022 15:24:52 +0200 Subject: [PATCH 2/2] Clippy fix --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index e0fb8fe7f..1c360658d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4259,7 +4259,7 @@ impl<'a> Parser<'a> { Ok(Statement::Merge { table, - source: source, + source, on: Box::new(on), clauses, })