Skip to content

Commit e9eee00

Browse files
authored
Add support for VACUUM in Redshift (#2005)
1 parent cb7a51e commit e9eee00

File tree

5 files changed

+138
-0
lines changed

5 files changed

+138
-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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@ impl<'a> Parser<'a> {
649649
self.prev_token();
650650
self.parse_export_data()
651651
}
652+
Keyword::VACUUM => {
653+
self.prev_token();
654+
self.parse_vacuum()
655+
}
652656
_ => self.expected("an SQL statement", next_token),
653657
},
654658
Token::LParen => {
@@ -16932,6 +16936,40 @@ impl<'a> Parser<'a> {
1693216936
}))
1693316937
}
1693416938

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

tests/sqlparser_redshift.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,48 @@ 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+
let stmt = redshift().verified_stmt("VACUUM FULL");
414+
match stmt {
415+
Statement::Vacuum(v) => {
416+
assert!(v.full);
417+
assert_eq!(v.table_name, None);
418+
}
419+
_ => unreachable!(),
420+
}
421+
let stmt = redshift().verified_stmt("VACUUM tbl");
422+
match stmt {
423+
Statement::Vacuum(v) => {
424+
assert_eq!(
425+
v.table_name,
426+
Some(ObjectName::from(vec![Ident::new("tbl"),]))
427+
);
428+
}
429+
_ => unreachable!(),
430+
}
431+
let stmt = redshift().verified_stmt(
432+
"VACUUM FULL SORT ONLY DELETE ONLY REINDEX RECLUSTER db1.sc1.tbl1 TO 20 PERCENT BOOST",
433+
);
434+
match stmt {
435+
Statement::Vacuum(v) => {
436+
assert!(v.full);
437+
assert!(v.sort_only);
438+
assert!(v.delete_only);
439+
assert!(v.reindex);
440+
assert!(v.recluster);
441+
assert_eq!(
442+
v.table_name,
443+
Some(ObjectName::from(vec![
444+
Ident::new("db1"),
445+
Ident::new("sc1"),
446+
Ident::new("tbl1"),
447+
]))
448+
);
449+
assert_eq!(v.threshold, Some(number("20")));
450+
assert!(v.boost);
451+
}
452+
_ => unreachable!(),
453+
}
454+
}

0 commit comments

Comments
 (0)