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]