Skip to content

Commit 777d03a

Browse files
committed
Add support for VACUUM in Redshift
1 parent 4b8797e commit 777d03a

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
@@ -4403,6 +4403,13 @@ pub enum Statement {
44034403
/// ```
44044404
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
44054405
CreateUser(CreateUser),
4406+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
4407+
///
4408+
/// ```sql
4409+
/// VACUUM tbl
4410+
/// ```
4411+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
4412+
Vacuum(VacuumStatement),
44064413
}
44074414

44084415
/// ```sql
@@ -6336,6 +6343,7 @@ impl fmt::Display for Statement {
63366343
Statement::Remove(command) => write!(f, "REMOVE {command}"),
63376344
Statement::ExportData(e) => write!(f, "{e}"),
63386345
Statement::CreateUser(s) => write!(f, "{s}"),
6346+
Statement::Vacuum(s) => write!(f, "{s}"),
63396347
}
63406348
}
63416349
}
@@ -10597,6 +10605,50 @@ impl fmt::Display for InitializeKind {
1059710605
}
1059810606
}
1059910607

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

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ impl Spanned for Statement {
548548
.chain(connection.iter().map(|i| i.span())),
549549
),
550550
Statement::CreateUser(..) => Span::empty(),
551+
Statement::Vacuum(..) => Span::empty(),
551552
}
552553
}
553554
}

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 => {
@@ -16874,6 +16875,38 @@ impl<'a> Parser<'a> {
1687416875
}))
1687516876
}
1687616877

16878+
fn parse_vacuum(&mut self) -> Result<Statement, ParserError> {
16879+
let full = self.parse_keyword(Keyword::FULL);
16880+
let sort_only = self.parse_keywords(&[Keyword::SORT, Keyword::ONLY]);
16881+
let delete_only = self.parse_keywords(&[Keyword::DELETE, Keyword::ONLY]);
16882+
let reindex = self.parse_keyword(Keyword::REINDEX);
16883+
let recluster = self.parse_keyword(Keyword::RECLUSTER);
16884+
let (table_name, threshold, boost) = match self.parse_object_name(false) {
16885+
Ok(table_name) => {
16886+
let threshold = if self.parse_keyword(Keyword::TO) {
16887+
let value = self.parse_value()?;
16888+
self.expect_keyword(Keyword::PERCENT)?;
16889+
Some(value.value)
16890+
} else {
16891+
None
16892+
};
16893+
let boost = self.parse_keyword(Keyword::BOOST);
16894+
(Some(table_name), threshold, boost)
16895+
}
16896+
_ => (None, None, false),
16897+
};
16898+
Ok(Statement::Vacuum(VacuumStatement {
16899+
full,
16900+
sort_only,
16901+
delete_only,
16902+
reindex,
16903+
recluster,
16904+
table_name,
16905+
threshold,
16906+
boost,
16907+
}))
16908+
}
16909+
1687716910
/// Consume the parser and return its underlying token buffer
1687816911
pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1687916912
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)