Skip to content

Commit 41af5d4

Browse files
committed
Add support for VACUUM in Redshift
1 parent cb7a51e commit 41af5d4

File tree

5 files changed

+116
-0
lines changed

5 files changed

+116
-0
lines changed

src/ast/mod.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4409,6 +4409,13 @@ pub enum Statement {
44094409
/// ```
44104410
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
44114411
CreateUser(CreateUser),
4412+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
4413+
///
4414+
/// ```sql
4415+
/// VACUUM tbl
4416+
/// ```
4417+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
4418+
Vacuum(VacuumStatement),
44124419
}
44134420

44144421
/// ```sql
@@ -6343,6 +6350,7 @@ impl fmt::Display for Statement {
63436350
Statement::ExportData(e) => write!(f, "{e}"),
63446351
Statement::CreateUser(s) => write!(f, "{s}"),
63456352
Statement::AlterSchema(s) => write!(f, "{s}"),
6353+
Statement::Vacuum(s) => write!(f, "{s}"),
63466354
}
63476355
}
63486356
}
@@ -10604,6 +10612,50 @@ impl fmt::Display for InitializeKind {
1060410612
}
1060510613
}
1060610614

10615+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
10616+
///
10617+
/// '''sql
10618+
/// VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ] [ \[ table_name \] [ TO threshold PERCENT ] \[ BOOST \] ]
10619+
/// '''
10620+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
10621+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10622+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10623+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10624+
pub struct VacuumStatement {
10625+
pub full: bool,
10626+
pub sort_only: bool,
10627+
pub delete_only: bool,
10628+
pub reindex: bool,
10629+
pub recluster: bool,
10630+
pub table_name: Option<ObjectName>,
10631+
pub threshold: Option<Value>,
10632+
pub boost: bool,
10633+
}
10634+
10635+
impl fmt::Display for VacuumStatement {
10636+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10637+
write!(
10638+
f,
10639+
"VACUUM{}{}{}{}{}",
10640+
if self.full { " FULL" } else { "" },
10641+
if self.sort_only { " SORT ONLY" } else { "" },
10642+
if self.delete_only { " DELETE ONLY" } else { "" },
10643+
if self.reindex { " REINDEX" } else { "" },
10644+
if self.recluster { " RECLUSTER" } else { "" },
10645+
)?;
10646+
if let Some(table_name) = &self.table_name {
10647+
write!(f, " {table_name}")?;
10648+
}
10649+
if let Some(threshold) = &self.threshold {
10650+
write!(f, " TO {threshold} PERCENT")?;
10651+
}
10652+
if self.boost {
10653+
write!(f, " BOOST")?;
10654+
}
10655+
Ok(())
10656+
}
10657+
}
10658+
1060710659
#[cfg(test)]
1060810660
mod tests {
1060910661
use crate::tokenizer::Location;

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ impl Spanned for Statement {
552552
),
553553
Statement::CreateUser(..) => Span::empty(),
554554
Statement::AlterSchema(s) => s.span(),
555+
Statement::Vacuum(..) => Span::empty(),
555556
}
556557
}
557558
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ define_keywords!(
144144
BLOOMFILTER,
145145
BOOL,
146146
BOOLEAN,
147+
BOOST,
147148
BOTH,
148149
BOX,
149150
BRIN,
@@ -761,6 +762,7 @@ define_keywords!(
761762
REGR_SXX,
762763
REGR_SXY,
763764
REGR_SYY,
765+
REINDEX,
764766
RELATIVE,
765767
RELAY,
766768
RELEASE,

src/parser/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ impl<'a> Parser<'a> {
649649
self.prev_token();
650650
self.parse_export_data()
651651
}
652+
Keyword::VACUUM => self.parse_vacuum(),
652653
_ => self.expected("an SQL statement", next_token),
653654
},
654655
Token::LParen => {
@@ -16932,6 +16933,38 @@ impl<'a> Parser<'a> {
1693216933
}))
1693316934
}
1693416935

16936+
fn parse_vacuum(&mut self) -> Result<Statement, ParserError> {
16937+
let full = self.parse_keyword(Keyword::FULL);
16938+
let sort_only = self.parse_keywords(&[Keyword::SORT, Keyword::ONLY]);
16939+
let delete_only = self.parse_keywords(&[Keyword::DELETE, Keyword::ONLY]);
16940+
let reindex = self.parse_keyword(Keyword::REINDEX);
16941+
let recluster = self.parse_keyword(Keyword::RECLUSTER);
16942+
let (table_name, threshold, boost) = match self.parse_object_name(false) {
16943+
Ok(table_name) => {
16944+
let threshold = if self.parse_keyword(Keyword::TO) {
16945+
let value = self.parse_value()?;
16946+
self.expect_keyword(Keyword::PERCENT)?;
16947+
Some(value.value)
16948+
} else {
16949+
None
16950+
};
16951+
let boost = self.parse_keyword(Keyword::BOOST);
16952+
(Some(table_name), threshold, boost)
16953+
}
16954+
_ => (None, None, false),
16955+
};
16956+
Ok(Statement::Vacuum(VacuumStatement {
16957+
full,
16958+
sort_only,
16959+
delete_only,
16960+
reindex,
16961+
recluster,
16962+
table_name,
16963+
threshold,
16964+
boost,
16965+
}))
16966+
}
16967+
1693516968
/// Consume the parser and return its underlying token buffer
1693616969
pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1693716970
self.tokens

tests/sqlparser_redshift.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,31 @@ fn parse_string_literal_backslash_escape() {
407407
fn parse_utf8_multibyte_idents() {
408408
redshift().verified_stmt("SELECT 🚀.city AS 🎸 FROM customers AS 🚀");
409409
}
410+
411+
#[test]
412+
fn parse_vacuum() {
413+
redshift().verified_stmt("VACUUM db1.sc1.tbl1");
414+
let stmt = redshift().verified_stmt(
415+
"VACUUM FULL SORT ONLY DELETE ONLY REINDEX RECLUSTER db1.sc1.tbl1 TO 20 PERCENT BOOST",
416+
);
417+
match stmt {
418+
Statement::Vacuum(v) => {
419+
assert!(v.full);
420+
assert!(v.sort_only);
421+
assert!(v.delete_only);
422+
assert!(v.reindex);
423+
assert!(v.recluster);
424+
assert_eq!(
425+
v.table_name,
426+
Some(ObjectName::from(vec![
427+
Ident::new("db1"),
428+
Ident::new("sc1"),
429+
Ident::new("tbl1"),
430+
]))
431+
);
432+
assert_eq!(v.threshold, Some(number("20")));
433+
assert!(v.boost);
434+
}
435+
_ => unreachable!(),
436+
}
437+
}

0 commit comments

Comments
 (0)