Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ pub enum Statement {
or_replace: bool,
temporary: bool,
external: bool,
global: Option<bool>,
if_not_exists: bool,
/// Table name
name: ObjectName,
Expand All @@ -804,6 +805,7 @@ pub enum Statement {
engine: Option<String>,
default_charset: Option<String>,
collation: Option<String>,
on_commit: Option<OnCommit>,
},
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
CreateVirtualTable {
Expand Down Expand Up @@ -1300,6 +1302,7 @@ impl fmt::Display for Statement {
hive_distribution,
hive_formats,
external,
global,
temporary,
file_format,
location,
Expand All @@ -1309,6 +1312,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:
Expand All @@ -1319,9 +1323,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 = if global.is_some() {
if global.unwrap() == true {
"GLOBAL "
} else {
"LOCAL "
}
} else {
""
},
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
temporary = if *temporary { "TEMPORARY " } else { "" },
name = name,
Expand Down Expand Up @@ -1443,6 +1456,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 {
Expand Down Expand Up @@ -2343,6 +2367,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.
///
/// <https://www.postgresql.org/docs/14/sql-copy.html>
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ define_keywords!(
PRECEDING,
PRECISION,
PREPARE,
PRESERVE,
PRIMARY,
PRIVILEGES,
PROCEDURE,
Expand Down
33 changes: 32 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,11 +1503,20 @@ impl<'a> Parser<'a> {
/// Parse a SQL CREATE statement
pub fn parse_create(&mut self) -> Result<Statement, ParserError> {
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<bool> = 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)
Expand Down Expand Up @@ -1617,6 +1626,7 @@ impl<'a> Parser<'a> {
or_replace,
if_not_exists,
external: true,
global: None,
temporary: false,
file_format,
location,
Expand All @@ -1626,6 +1636,7 @@ impl<'a> Parser<'a> {
default_charset: None,
engine: None,
collation: None,
on_commit: None,
})
}

Expand Down Expand Up @@ -1774,6 +1785,7 @@ impl<'a> Parser<'a> {
&mut self,
or_replace: bool,
temporary: bool,
global: Option<bool>,
) -> Result<Statement, ParserError> {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let table_name = self.parse_object_name()?;
Expand Down Expand Up @@ -1830,6 +1842,23 @@ impl<'a> Parser<'a> {
None
};

let on_commit: Option<OnCommit> =
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,
Expand All @@ -1842,6 +1871,7 @@ impl<'a> Parser<'a> {
hive_distribution,
hive_formats: Some(hive_formats),
external: false,
global,
file_format: None,
location: None,
query,
Expand All @@ -1850,6 +1880,7 @@ impl<'a> Parser<'a> {
engine,
default_charset,
collation,
on_commit,
})
}

Expand Down
20 changes: 12 additions & 8 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand All @@ -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,
});
Expand Down
15 changes: 15 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {})],
Expand Down