This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 802c7d3e PostgreSQL: Add support for `*` (descendant) option in 
TRUNCATE (#2181)
802c7d3e is described below

commit 802c7d3e03df900392a009ce60b9f30fd954ac4e
Author: Michael Victor Zink <[email protected]>
AuthorDate: Sun Jan 25 01:38:09 2026 -0800

    PostgreSQL: Add support for `*` (descendant) option in TRUNCATE (#2181)
---
 src/ast/mod.rs              | 18 ++++++++++---
 src/parser/mod.rs           | 17 ++++++++-----
 tests/sqlparser_common.rs   |  2 ++
 tests/sqlparser_postgres.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 10 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index fcfdf364..33f99bc2 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -6422,10 +6422,18 @@ pub struct TruncateTableTarget {
     /// name of the table being truncated
     #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
     pub name: ObjectName,
-    /// Postgres-specific option
-    /// [ TRUNCATE TABLE ONLY ]
+    /// Postgres-specific option: explicitly exclude descendants (also default 
without ONLY)
+    /// ```sql
+    /// TRUNCATE TABLE ONLY name
+    /// ```
     /// <https://www.postgresql.org/docs/current/sql-truncate.html>
     pub only: bool,
+    /// Postgres-specific option: asterisk after table name to explicitly 
indicate descendants
+    /// ```sql
+    /// TRUNCATE TABLE name [ * ]
+    /// ```
+    /// <https://www.postgresql.org/docs/current/sql-truncate.html>
+    pub has_asterisk: bool,
 }
 
 impl fmt::Display for TruncateTableTarget {
@@ -6433,7 +6441,11 @@ impl fmt::Display for TruncateTableTarget {
         if self.only {
             write!(f, "ONLY ")?;
         };
-        write!(f, "{}", self.name)
+        write!(f, "{}", self.name)?;
+        if self.has_asterisk {
+            write!(f, " *")?;
+        };
+        Ok(())
     }
 }
 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 55fec678..e0712017 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1054,13 +1054,16 @@ impl<'a> Parser<'a> {
         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| {
-                Ok((p.parse_keyword(Keyword::ONLY), 
p.parse_object_name(false)?))
-            })?
-            .into_iter()
-            .map(|(only, name)| TruncateTableTarget { name, only })
-            .collect();
+        let table_names = self.parse_comma_separated(|p| {
+            let only = p.parse_keyword(Keyword::ONLY);
+            let name = p.parse_object_name(false)?;
+            let has_asterisk = p.consume_token(&Token::Mul);
+            Ok(TruncateTableTarget {
+                name,
+                only,
+                has_asterisk,
+            })
+        })?;
 
         let mut partitions = None;
         if self.parse_keyword(Keyword::PARTITION) {
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index b2c41d97..6da4ea53 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -16828,10 +16828,12 @@ fn parse_truncate_only() {
         TruncateTableTarget {
             name: ObjectName::from(vec![Ident::new("employee")]),
             only: false,
+            has_asterisk: false,
         },
         TruncateTableTarget {
             name: ObjectName::from(vec![Ident::new("dept")]),
             only: true,
+            has_asterisk: false,
         },
     ];
 
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 6a4b78b5..7bd7f43c 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -5088,6 +5088,7 @@ fn parse_truncate() {
     let table_names = vec![TruncateTableTarget {
         name: table_name.clone(),
         only: false,
+        has_asterisk: false,
     }];
     assert_eq!(
         Statement::Truncate(Truncate {
@@ -5112,6 +5113,7 @@ fn parse_truncate_with_options() {
     let table_names = vec![TruncateTableTarget {
         name: table_name.clone(),
         only: true,
+        has_asterisk: false,
     }];
 
     assert_eq!(
@@ -5141,10 +5143,12 @@ fn parse_truncate_with_table_list() {
         TruncateTableTarget {
             name: table_name_a.clone(),
             only: false,
+            has_asterisk: false,
         },
         TruncateTableTarget {
             name: table_name_b.clone(),
             only: false,
+            has_asterisk: false,
         },
     ];
 
@@ -5162,6 +5166,64 @@ fn parse_truncate_with_table_list() {
     );
 }
 
+#[test]
+fn parse_truncate_with_descendant() {
+    let truncate = pg_and_generic().verified_stmt("TRUNCATE TABLE t *");
+
+    let table_names = vec![TruncateTableTarget {
+        name: ObjectName::from(vec![Ident::new("t")]),
+        only: false,
+        has_asterisk: true,
+    }];
+
+    assert_eq!(
+        Statement::Truncate(Truncate {
+            table_names,
+            partitions: None,
+            table: true,
+            if_exists: false,
+            identity: None,
+            cascade: None,
+            on_cluster: None,
+        }),
+        truncate
+    );
+
+    let truncate = pg_and_generic()
+        .verified_stmt("TRUNCATE TABLE ONLY parent, child *, grandchild 
RESTART IDENTITY");
+
+    let table_names = vec![
+        TruncateTableTarget {
+            name: ObjectName::from(vec![Ident::new("parent")]),
+            only: true,
+            has_asterisk: false,
+        },
+        TruncateTableTarget {
+            name: ObjectName::from(vec![Ident::new("child")]),
+            only: false,
+            has_asterisk: true,
+        },
+        TruncateTableTarget {
+            name: ObjectName::from(vec![Ident::new("grandchild")]),
+            only: false,
+            has_asterisk: false,
+        },
+    ];
+
+    assert_eq!(
+        Statement::Truncate(Truncate {
+            table_names,
+            partitions: None,
+            table: true,
+            if_exists: false,
+            identity: Some(TruncateIdentityOption::Restart),
+            cascade: None,
+            on_cluster: None,
+        }),
+        truncate
+    );
+}
+
 #[test]
 fn parse_select_regexp_as_column_name() {
     pg_and_generic().verified_only_select(


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to