diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 959b64f9..3fe1945b 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -831,6 +831,7 @@ pub enum Statement { or_replace: bool, temporary: bool, external: bool, + global: Option, if_not_exists: bool, /// Table name name: ObjectName, @@ -849,6 +850,7 @@ pub enum Statement { engine: Option, default_charset: Option, collation: Option, + on_commit: Option, }, /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` CreateVirtualTable { @@ -1313,6 +1315,7 @@ impl fmt::Display for Statement { hive_distribution, hive_formats, external, + global, temporary, file_format, location, @@ -1322,6 +1325,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: @@ -1332,9 +1336,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, @@ -1456,6 +1469,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 { @@ -2276,6 +2300,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 1dbb623e..ad31bda5 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -376,6 +376,7 @@ define_keywords!( PRECEDING, PRECISION, PREPARE, + PRESERVE, PRIMARY, PRIVILEGES, PROCEDURE, diff --git a/src/parser.rs b/src/parser.rs index cf53d3ec..b9368007 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1519,11 +1519,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) @@ -1633,6 +1642,7 @@ impl<'a> Parser<'a> { or_replace, if_not_exists, external: true, + global: None, temporary: false, file_format, location, @@ -1642,6 +1652,7 @@ impl<'a> Parser<'a> { default_charset: None, engine: None, collation: None, + on_commit: None, }) } @@ -1790,6 +1801,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()?; @@ -1846,6 +1858,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, @@ -1858,6 +1887,7 @@ impl<'a> Parser<'a> { hive_distribution, hive_formats: Some(hive_formats), external: false, + global, file_format: None, location: None, query, @@ -1866,6 +1896,7 @@ impl<'a> Parser<'a> { engine, default_charset, collation, + on_commit, }) } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index e3e2d087..eb42edc8 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1347,6 +1347,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 {})],