diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4a40e746ab..4c068e3f78 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -794,6 +794,7 @@ pub enum Statement { or_replace: bool, temporary: bool, external: bool, + global: Option, if_not_exists: bool, /// Table name name: ObjectName, @@ -812,6 +813,7 @@ pub enum Statement { engine: Option, default_charset: Option, collation: Option, + on_commit: Option, }, /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` CreateVirtualTable { @@ -1308,6 +1310,7 @@ impl fmt::Display for Statement { hive_distribution, hive_formats, external, + global, temporary, file_format, location, @@ -1317,6 +1320,7 @@ impl fmt::Display for Statement { default_charset, engine, collation, + on_commit, } => { // We want to allow the following options // Empty column list, allowed by PostgreSQL: @@ -1327,9 +1331,18 @@ impl fmt::Display for Statement { // `CREATE TABLE t (a INT) AS SELECT a from t2` write!( f, - "CREATE {or_replace}{external}{temporary}TABLE {if_not_exists}{name}", + "CREATE {or_replace}{external}{global}{temporary}TABLE {if_not_exists}{name}", or_replace = if *or_replace { "OR REPLACE " } else { "" }, external = if *external { "EXTERNAL " } else { "" }, + global = global + .map(|global| { + if global { + "GLOBAL " + } else { + "LOCAL " + } + }) + .unwrap_or(""), if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, temporary = if *temporary { "TEMPORARY " } else { "" }, name = name, @@ -1451,6 +1464,17 @@ impl fmt::Display for Statement { if let Some(collation) = collation { write!(f, " COLLATE={}", collation)?; } + + if on_commit.is_some() { + let on_commit = match on_commit { + Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", + Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", + Some(OnCommit::Drop) => "ON COMMIT DROP", + None => "", + }; + write!(f, " {}", on_commit)?; + } + Ok(()) } Statement::CreateVirtualTable { @@ -2351,6 +2375,14 @@ impl fmt::Display for CopyTarget { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum OnCommit { + DeleteRows, + PreserveRows, + Drop, +} + /// An option in `COPY` statement. /// /// diff --git a/src/keywords.rs b/src/keywords.rs index 37ebc02e2e..a4fbe1e65b 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -380,6 +380,7 @@ define_keywords!( PRECEDING, PRECISION, PREPARE, + PRESERVE, PRIMARY, PRIVILEGES, PROCEDURE, diff --git a/src/parser.rs b/src/parser.rs index db9f341c57..1f9985f26d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1510,11 +1510,20 @@ impl<'a> Parser<'a> { /// Parse a SQL CREATE statement pub fn parse_create(&mut self) -> Result { let or_replace = self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]); + let local = self.parse_one_of_keywords(&[Keyword::LOCAL]).is_some(); + let global = self.parse_one_of_keywords(&[Keyword::GLOBAL]).is_some(); + let global: Option = if global { + Some(true) + } else if local { + Some(false) + } else { + None + }; let temporary = self .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); if self.parse_keyword(Keyword::TABLE) { - self.parse_create_table(or_replace, temporary) + self.parse_create_table(or_replace, temporary, global) } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { self.prev_token(); self.parse_create_view(or_replace) @@ -1624,6 +1633,7 @@ impl<'a> Parser<'a> { or_replace, if_not_exists, external: true, + global: None, temporary: false, file_format, location, @@ -1633,6 +1643,7 @@ impl<'a> Parser<'a> { default_charset: None, engine: None, collation: None, + on_commit: None, }) } @@ -1781,6 +1792,7 @@ impl<'a> Parser<'a> { &mut self, or_replace: bool, temporary: bool, + global: Option, ) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let table_name = self.parse_object_name()?; @@ -1837,6 +1849,23 @@ impl<'a> Parser<'a> { None }; + let on_commit: Option = + if self.parse_keywords(&[Keyword::ON, Keyword::COMMIT, Keyword::DELETE, Keyword::ROWS]) + { + Some(OnCommit::DeleteRows) + } else if self.parse_keywords(&[ + Keyword::ON, + Keyword::COMMIT, + Keyword::PRESERVE, + Keyword::ROWS, + ]) { + Some(OnCommit::PreserveRows) + } else if self.parse_keywords(&[Keyword::ON, Keyword::COMMIT, Keyword::DROP]) { + Some(OnCommit::Drop) + } else { + None + }; + Ok(Statement::CreateTable { name: table_name, temporary, @@ -1849,6 +1878,7 @@ impl<'a> Parser<'a> { hive_distribution, hive_formats: Some(hive_formats), external: false, + global, file_format: None, location: None, query, @@ -1857,6 +1887,7 @@ impl<'a> Parser<'a> { engine, default_charset, collation, + on_commit, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 1295d8f569..6073767d38 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2499,10 +2499,12 @@ fn parse_expr_interval() { value: "HOUR".to_string(), quote_style: None, }]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident { - value: "order_date".to_string(), - quote_style: None, - })))], + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "order_date".to_string(), + quote_style: None, + }), + ))], over: None, distinct: false, }); @@ -2528,10 +2530,12 @@ fn parse_expr_interval() { value: "QUARTER".to_string(), quote_style: None, }]), - args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Identifier(Ident { - value: "order_date".to_string(), - quote_style: None, - })))], + args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr( + Expr::Identifier(Ident { + value: "order_date".to_string(), + quote_style: None, + }), + ))], over: None, distinct: false, }); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index e22a734311..2a7b626b35 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1264,6 +1264,21 @@ fn parse_quoted_identifier() { pg_and_generic().verified_stmt(r#"SELECT "quoted "" ident""#); } +#[test] +fn parse_local_and_global() { + pg_and_generic().verified_stmt("CREATE LOCAL TEMPORARY TABLE table (COL INT)"); +} + +#[test] +fn parse_on_commit() { + pg_and_generic() + .verified_stmt("CREATE TEMPORARY TABLE table (COL INT) ON COMMIT PRESERVE ROWS"); + + pg_and_generic().verified_stmt("CREATE TEMPORARY TABLE table (COL INT) ON COMMIT DELETE ROWS"); + + pg_and_generic().verified_stmt("CREATE TEMPORARY TABLE table (COL INT) ON COMMIT DROP"); +} + fn pg() -> TestedDialects { TestedDialects { dialects: vec![Box::new(PostgreSqlDialect {})],