Skip to content

Commit 719765a

Browse files
committed
Add support for VACUUM in Redshift
1 parent b660a3b commit 719765a

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
}
@@ -10521,6 +10529,50 @@ impl fmt::Display for CreateTableLike {
1052110529
}
1052210530
}
1052310531

10532+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
10533+
///
10534+
/// '''sql
10535+
/// VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ] [ \[ table_name \] [ TO threshold PERCENT ] \[ BOOST \] ]
10536+
/// '''
10537+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
10538+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10539+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10540+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10541+
pub struct VacuumStatement {
10542+
pub full: bool,
10543+
pub sort_only: bool,
10544+
pub delete_only: bool,
10545+
pub reindex: bool,
10546+
pub recluster: bool,
10547+
pub table_name: Option<ObjectName>,
10548+
pub threshold: Option<Value>,
10549+
pub boost: bool,
10550+
}
10551+
10552+
impl fmt::Display for VacuumStatement {
10553+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10554+
write!(
10555+
f,
10556+
"VACUUM{}{}{}{}{}",
10557+
if self.full { " FULL" } else { "" },
10558+
if self.sort_only { " SORT ONLY" } else { "" },
10559+
if self.delete_only { " DELETE ONLY" } else { "" },
10560+
if self.reindex { " REINDEX" } else { "" },
10561+
if self.recluster { " RECLUSTER" } else { "" },
10562+
)?;
10563+
if let Some(table_name) = &self.table_name {
10564+
write!(f, " {table_name}")?;
10565+
}
10566+
if let Some(threshold) = &self.threshold {
10567+
write!(f, " TO {threshold} PERCENT")?;
10568+
}
10569+
if self.boost {
10570+
write!(f, " BOOST")?;
10571+
}
10572+
Ok(())
10573+
}
10574+
}
10575+
1052410576
#[cfg(test)]
1052510577
mod tests {
1052610578
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,
@@ -753,6 +754,7 @@ define_keywords!(
753754
REGR_SXX,
754755
REGR_SXY,
755756
REGR_SYY,
757+
REINDEX,
756758
RELATIVE,
757759
RELAY,
758760
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 => {
@@ -16852,6 +16853,38 @@ impl<'a> Parser<'a> {
1685216853
}))
1685316854
}
1685416855

16856+
fn parse_vacuum(&mut self) -> Result<Statement, ParserError> {
16857+
let full = self.parse_keyword(Keyword::FULL);
16858+
let sort_only = self.parse_keywords(&[Keyword::SORT, Keyword::ONLY]);
16859+
let delete_only = self.parse_keywords(&[Keyword::DELETE, Keyword::ONLY]);
16860+
let reindex = self.parse_keyword(Keyword::REINDEX);
16861+
let recluster = self.parse_keyword(Keyword::RECLUSTER);
16862+
let (table_name, threshold, boost) = match self.parse_object_name(false) {
16863+
Ok(table_name) => {
16864+
let threshold = if self.parse_keyword(Keyword::TO) {
16865+
let value = self.parse_value()?;
16866+
self.expect_keyword(Keyword::PERCENT)?;
16867+
Some(value.value)
16868+
} else {
16869+
None
16870+
};
16871+
let boost = self.parse_keyword(Keyword::BOOST);
16872+
(Some(table_name), threshold, boost)
16873+
}
16874+
_ => (None, None, false),
16875+
};
16876+
Ok(Statement::Vacuum(VacuumStatement {
16877+
full,
16878+
sort_only,
16879+
delete_only,
16880+
reindex,
16881+
recluster,
16882+
table_name,
16883+
threshold,
16884+
boost,
16885+
}))
16886+
}
16887+
1685516888
/// Consume the parser and return its underlying token buffer
1685616889
pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1685716890
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)