From 38868c9fbaf4da2d71079167657dec5ad8362f8b Mon Sep 17 00:00:00 2001 From: Andriy Romanov Date: Fri, 16 Jan 2026 15:35:40 -0800 Subject: [PATCH] Fixed truncate table if exists for snowflake --- src/ast/ddl.rs | 7 +++++-- src/parser/mod.rs | 2 ++ tests/sqlparser_common.rs | 1 + tests/sqlparser_postgres.rs | 3 +++ tests/sqlparser_snowflake.rs | 7 +++++++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 23fcc0101..fcd14b6d1 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -4062,7 +4062,7 @@ impl fmt::Display for DropTrigger { /// A `TRUNCATE` statement. /// /// ```sql -/// TRUNCATE TABLE table_names [PARTITION (partitions)] [RESTART IDENTITY | CONTINUE IDENTITY] [CASCADE | RESTRICT] [ON CLUSTER cluster_name] +/// TRUNCATE TABLE [IF EXISTS] table_names [PARTITION (partitions)] [RESTART IDENTITY | CONTINUE IDENTITY] [CASCADE | RESTRICT] [ON CLUSTER cluster_name] /// ``` #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -4074,6 +4074,8 @@ pub struct Truncate { pub partitions: Option>, /// TABLE - optional keyword pub table: bool, + /// Snowflake/Redshift-specific option: [ IF EXISTS ] + pub if_exists: bool, /// Postgres-specific option: [ RESTART IDENTITY | CONTINUE IDENTITY ] pub identity: Option, /// Postgres-specific option: [ CASCADE | RESTRICT ] @@ -4086,10 +4088,11 @@ pub struct Truncate { impl fmt::Display for Truncate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let table = if self.table { "TABLE " } else { "" }; + let if_exists = if self.if_exists { "IF EXISTS " } else { "" }; write!( f, - "TRUNCATE {table}{table_names}", + "TRUNCATE {table}{if_exists}{table_names}", table_names = display_comma_separated(&self.table_names) )?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6fd7b5ca4..d784235dd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1054,6 +1054,7 @@ impl<'a> Parser<'a> { /// Parse `TRUNCATE` statement. pub fn parse_truncate(&mut self) -> Result { let table = self.parse_keyword(Keyword::TABLE); + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let table_names = self .parse_comma_separated(|p| { @@ -1091,6 +1092,7 @@ impl<'a> Parser<'a> { table_names, partitions, table, + if_exists, identity, cascade, on_cluster, diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index bbbf0d835..c67bcb18e 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -16773,6 +16773,7 @@ fn parse_truncate_only() { table_names, partitions: None, table: true, + if_exists: false, identity: None, cascade: None, on_cluster: None, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 325e3939e..57bddc656 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5089,6 +5089,7 @@ fn parse_truncate() { table_names, partitions: None, table: false, + if_exists: false, identity: None, cascade: None, on_cluster: None, @@ -5113,6 +5114,7 @@ fn parse_truncate_with_options() { table_names, partitions: None, table: true, + if_exists: false, identity: Some(TruncateIdentityOption::Restart), cascade: Some(CascadeOption::Cascade), on_cluster: None, @@ -5146,6 +5148,7 @@ fn parse_truncate_with_table_list() { table_names, partitions: None, table: true, + if_exists: false, identity: Some(TruncateIdentityOption::Restart), cascade: Some(CascadeOption::Cascade), on_cluster: None, diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 5889b2bd0..72f60f1a6 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -4533,3 +4533,10 @@ fn test_alter_external_table() { snowflake() .verified_stmt("ALTER EXTERNAL TABLE IF EXISTS some_table REFRESH 'year=2025/month=12/'"); } + +#[test] +fn test_truncate_table_if_exists() { + snowflake().verified_stmt("TRUNCATE TABLE IF EXISTS my_table"); + snowflake().verified_stmt("TRUNCATE TABLE my_table"); + snowflake().verified_stmt("TRUNCATE IF EXISTS my_table"); +}