into keyword is optional (#473)

Signed-off-by: Maciej Obuchowski <obuchowski.maciej@gmail.com>
This commit is contained in:
Maciej Obuchowski 2022-05-09 19:08:24 +02:00 committed by GitHub
parent 8ef5fc8624
commit 835bb2f9ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 25 deletions

View file

@ -770,6 +770,8 @@ pub enum Statement {
Insert {
/// Only for Sqlite
or: Option<SqliteOnConflict>,
/// INTO - optional keyword
into: bool,
/// TABLE
table_name: ObjectName,
/// COLUMNS
@ -1043,6 +1045,8 @@ pub enum Statement {
Savepoint { name: Ident },
// MERGE INTO statement, based on Snowflake. See <https://docs.snowflake.com/en/sql-reference/sql/merge.html>
Merge {
// optional INTO keyword
into: bool,
// Specifies the table to merge
table: TableFactor,
// Specifies the table or subquery to join with the target table
@ -1188,6 +1192,7 @@ impl fmt::Display for Statement {
}
Statement::Insert {
or,
into,
table_name,
overwrite,
partitioned,
@ -1202,9 +1207,10 @@ impl fmt::Display for Statement {
} else {
write!(
f,
"INSERT {act}{tbl} {table_name} ",
"INSERT{over}{int}{tbl} {table_name} ",
table_name = table_name,
act = if *overwrite { "OVERWRITE" } else { "INTO" },
over = if *overwrite { " OVERWRITE" } else { "" },
int = if *into { " INTO" } else { "" },
tbl = if *table { " TABLE" } else { "" }
)?;
}
@ -1755,13 +1761,18 @@ impl fmt::Display for Statement {
write!(f, "{}", name)
}
Statement::Merge {
into,
table,
source,
alias,
on,
clauses,
} => {
write!(f, "MERGE INTO {} USING {} ", table, source)?;
write!(
f,
"MERGE{int} {table} USING {source} ",
int = if *into { " INTO" } else { "" }
)?;
if let Some(a) = alias {
write!(f, "as {} ", a)?;
};

View file

@ -3802,8 +3802,11 @@ impl<'a> Parser<'a> {
} else {
None
};
let action = self.expect_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE])?;
let overwrite = action == Keyword::OVERWRITE;
let action = self.parse_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE]);
let into = action == Some(Keyword::INTO);
let overwrite = action == Some(Keyword::OVERWRITE);
let local = self.parse_keyword(Keyword::LOCAL);
if self.parse_keyword(Keyword::DIRECTORY) {
@ -3854,6 +3857,7 @@ impl<'a> Parser<'a> {
Ok(Statement::Insert {
or,
table_name,
into,
overwrite,
partitioned,
columns,
@ -4266,7 +4270,7 @@ impl<'a> Parser<'a> {
}
pub fn parse_merge(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword(Keyword::INTO)?;
let into = self.parse_keyword(Keyword::INTO);
let table = self.parse_table_factor()?;
@ -4278,6 +4282,7 @@ impl<'a> Parser<'a> {
let clauses = self.parse_merge_clauses()?;
Ok(Statement::Merge {
into,
table,
source: Box::new(source),
alias,

View file

@ -41,6 +41,9 @@ fn parse_insert_values() {
let rows1 = vec![row.clone()];
let rows2 = vec![row.clone(), row];
let sql = "INSERT customer VALUES (1, 2, 3)";
check_one(sql, "customer", &[], &rows1);
let sql = "INSERT INTO customer VALUES (1, 2, 3)";
check_one(sql, "customer", &[], &rows1);
@ -91,16 +94,6 @@ fn parse_insert_values() {
verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)");
}
#[test]
fn parse_insert_invalid() {
let sql = "INSERT public.customer (id, name, active) VALUES (1, 2, 3)";
let res = parse_sql_statements(sql);
assert_eq!(
ParserError::ParserError("Expected one of INTO or OVERWRITE, found: public".to_string()),
res.unwrap_err()
);
}
#[test]
fn parse_insert_sqlite() {
let dialect = SQLiteDialect {};
@ -4357,14 +4350,29 @@ 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";
match verified_stmt(sql) {
let sql_no_into = "MERGE 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), verified_stmt(sql_no_into)) {
(
Statement::Merge {
into,
table,
source,
alias,
on,
clauses,
} => {
},
Statement::Merge {
into: no_into,
table: table_no_into,
source: source_no_into,
alias: alias_no_into,
on: on_no_into,
clauses: clauses_no_into,
},
) => {
assert!(into);
assert!(!no_into);
assert_eq!(
table,
TableFactor::Table {
@ -4377,6 +4385,8 @@ fn parse_merge() {
with_hints: vec![]
}
);
assert_eq!(table, table_no_into);
assert_eq!(
source,
Box::new(SetExpr::Query(Box::new(Query {
@ -4411,6 +4421,8 @@ fn parse_merge() {
lock: None
})))
);
assert_eq!(source, source_no_into);
assert_eq!(
alias,
Some(TableAlias {
@ -4418,6 +4430,8 @@ fn parse_merge() {
columns: vec![]
})
);
assert_eq!(alias, alias_no_into);
assert_eq!(
on,
Box::new(Expr::BinaryOp {
@ -4446,6 +4460,8 @@ fn parse_merge() {
})
})
);
assert_eq!(on, on_no_into);
assert_eq!(
clauses,
vec![
@ -4488,7 +4504,8 @@ fn parse_merge() {
},
MergeClause::MatchedDelete(None)
]
)
);
assert_eq!(clauses, clauses_no_into);
}
_ => unreachable!(),
}