Skip to content

Commit f622300

Browse files
committed
PostgreSQL: Add support for * (descendant) option in TRUNCATE
Add support for the PostgreSQL TRUNCATE syntax `TRUNCATE TABLE name *` which indicates that descendant tables should also be truncated. This is the opposite of the `ONLY` keyword, and Postgres doesn't allow both. We are more permissive when parsing it and allow both to appear. Adds a `has_asterisk` field to `TruncateTableTarget` that is set to true when the `*` token follows a table name. This could be called `include_descendants` or something more semantically meaningful, but it's kind of peculiar syntax, so just mirroring the syntax itself seemed less confusing.
1 parent 6550ec8 commit f622300

4 files changed

Lines changed: 89 additions & 10 deletions

File tree

src/ast/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6412,18 +6412,30 @@ pub struct TruncateTableTarget {
64126412
/// name of the table being truncated
64136413
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
64146414
pub name: ObjectName,
6415-
/// Postgres-specific option
6416-
/// [ TRUNCATE TABLE ONLY ]
6415+
/// Postgres-specific option: explicitly exclude descendants (also default without ONLY)
6416+
/// ```sql
6417+
/// TRUNCATE TABLE ONLY name
6418+
/// ```
64176419
/// <https://www.postgresql.org/docs/current/sql-truncate.html>
64186420
pub only: bool,
6421+
/// Postgres-specific option: asterisk after table name to explicitly indicate descendants
6422+
/// ```sql
6423+
/// TRUNCATE TABLE name [ * ]
6424+
/// ```
6425+
/// <https://www.postgresql.org/docs/current/sql-truncate.html>
6426+
pub has_asterisk: bool,
64196427
}
64206428

64216429
impl fmt::Display for TruncateTableTarget {
64226430
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64236431
if self.only {
64246432
write!(f, "ONLY ")?;
64256433
};
6426-
write!(f, "{}", self.name)
6434+
write!(f, "{}", self.name)?;
6435+
if self.has_asterisk {
6436+
write!(f, " *")?;
6437+
};
6438+
Ok(())
64276439
}
64286440
}
64296441

src/parser/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,13 +1056,16 @@ impl<'a> Parser<'a> {
10561056
let table = self.parse_keyword(Keyword::TABLE);
10571057
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
10581058

1059-
let table_names = self
1060-
.parse_comma_separated(|p| {
1061-
Ok((p.parse_keyword(Keyword::ONLY), p.parse_object_name(false)?))
1062-
})?
1063-
.into_iter()
1064-
.map(|(only, name)| TruncateTableTarget { name, only })
1065-
.collect();
1059+
let table_names = self.parse_comma_separated(|p| {
1060+
let only = p.parse_keyword(Keyword::ONLY);
1061+
let name = p.parse_object_name(false)?;
1062+
let has_asterisk = p.consume_token(&Token::Mul);
1063+
Ok(TruncateTableTarget {
1064+
name,
1065+
only,
1066+
has_asterisk,
1067+
})
1068+
})?;
10661069

10671070
let mut partitions = None;
10681071
if self.parse_keyword(Keyword::PARTITION) {

tests/sqlparser_common.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16806,10 +16806,12 @@ fn parse_truncate_only() {
1680616806
TruncateTableTarget {
1680716807
name: ObjectName::from(vec![Ident::new("employee")]),
1680816808
only: false,
16809+
has_asterisk: false,
1680916810
},
1681016811
TruncateTableTarget {
1681116812
name: ObjectName::from(vec![Ident::new("dept")]),
1681216813
only: true,
16814+
has_asterisk: false,
1681316815
},
1681416816
];
1681516817

tests/sqlparser_postgres.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5083,6 +5083,7 @@ fn parse_truncate() {
50835083
let table_names = vec![TruncateTableTarget {
50845084
name: table_name.clone(),
50855085
only: false,
5086+
has_asterisk: false,
50865087
}];
50875088
assert_eq!(
50885089
Statement::Truncate(Truncate {
@@ -5107,6 +5108,7 @@ fn parse_truncate_with_options() {
51075108
let table_names = vec![TruncateTableTarget {
51085109
name: table_name.clone(),
51095110
only: true,
5111+
has_asterisk: false,
51105112
}];
51115113

51125114
assert_eq!(
@@ -5136,10 +5138,12 @@ fn parse_truncate_with_table_list() {
51365138
TruncateTableTarget {
51375139
name: table_name_a.clone(),
51385140
only: false,
5141+
has_asterisk: false,
51395142
},
51405143
TruncateTableTarget {
51415144
name: table_name_b.clone(),
51425145
only: false,
5146+
has_asterisk: false,
51435147
},
51445148
];
51455149

@@ -5157,6 +5161,64 @@ fn parse_truncate_with_table_list() {
51575161
);
51585162
}
51595163

5164+
#[test]
5165+
fn parse_truncate_with_descendant() {
5166+
let truncate = pg_and_generic().verified_stmt("TRUNCATE TABLE t *");
5167+
5168+
let table_names = vec![TruncateTableTarget {
5169+
name: ObjectName::from(vec![Ident::new("t")]),
5170+
only: false,
5171+
has_asterisk: true,
5172+
}];
5173+
5174+
assert_eq!(
5175+
Statement::Truncate(Truncate {
5176+
table_names,
5177+
partitions: None,
5178+
table: true,
5179+
if_exists: false,
5180+
identity: None,
5181+
cascade: None,
5182+
on_cluster: None,
5183+
}),
5184+
truncate
5185+
);
5186+
5187+
let truncate = pg_and_generic()
5188+
.verified_stmt("TRUNCATE TABLE ONLY parent, child *, grandchild RESTART IDENTITY");
5189+
5190+
let table_names = vec![
5191+
TruncateTableTarget {
5192+
name: ObjectName::from(vec![Ident::new("parent")]),
5193+
only: true,
5194+
has_asterisk: false,
5195+
},
5196+
TruncateTableTarget {
5197+
name: ObjectName::from(vec![Ident::new("child")]),
5198+
only: false,
5199+
has_asterisk: true,
5200+
},
5201+
TruncateTableTarget {
5202+
name: ObjectName::from(vec![Ident::new("grandchild")]),
5203+
only: false,
5204+
has_asterisk: false,
5205+
},
5206+
];
5207+
5208+
assert_eq!(
5209+
Statement::Truncate(Truncate {
5210+
table_names,
5211+
partitions: None,
5212+
table: true,
5213+
if_exists: false,
5214+
identity: Some(TruncateIdentityOption::Restart),
5215+
cascade: None,
5216+
on_cluster: None,
5217+
}),
5218+
truncate
5219+
);
5220+
}
5221+
51605222
#[test]
51615223
fn parse_select_regexp_as_column_name() {
51625224
pg_and_generic().verified_only_select(

0 commit comments

Comments
 (0)