Skip to content

Commit 75fd2e8

Browse files
yoavcloudayman-sigma
authored andcommitted
Add support for VACUUM in Redshift (apache#2005)
1 parent e36f743 commit 75fd2e8

5 files changed

Lines changed: 138 additions & 0 deletions

File tree

src/ast/mod.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4438,6 +4438,13 @@ pub enum Statement {
44384438
/// ```
44394439
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-user)
44404440
CreateUser(CreateUser),
4441+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
4442+
///
4443+
/// ```sql
4444+
/// VACUUM tbl
4445+
/// ```
4446+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
4447+
Vacuum(VacuumStatement),
44414448
}
44424449

44434450
/// ```sql
@@ -6372,6 +6379,7 @@ impl fmt::Display for Statement {
63726379
Statement::ExportData(e) => write!(f, "{e}"),
63736380
Statement::CreateUser(s) => write!(f, "{s}"),
63746381
Statement::AlterSchema(s) => write!(f, "{s}"),
6382+
Statement::Vacuum(s) => write!(f, "{s}"),
63756383
}
63766384
}
63776385
}
@@ -10633,6 +10641,50 @@ impl fmt::Display for InitializeKind {
1063310641
}
1063410642
}
1063510643

10644+
/// Re-sorts rows and reclaims space in either a specified table or all tables in the current database
10645+
///
10646+
/// '''sql
10647+
/// VACUUM [ FULL | SORT ONLY | DELETE ONLY | REINDEX | RECLUSTER ] [ \[ table_name \] [ TO threshold PERCENT ] \[ BOOST \] ]
10648+
/// '''
10649+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_VACUUM_command.html)
10650+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10651+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10652+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10653+
pub struct VacuumStatement {
10654+
pub full: bool,
10655+
pub sort_only: bool,
10656+
pub delete_only: bool,
10657+
pub reindex: bool,
10658+
pub recluster: bool,
10659+
pub table_name: Option<ObjectName>,
10660+
pub threshold: Option<Value>,
10661+
pub boost: bool,
10662+
}
10663+
10664+
impl fmt::Display for VacuumStatement {
10665+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10666+
write!(
10667+
f,
10668+
"VACUUM{}{}{}{}{}",
10669+
if self.full { " FULL" } else { "" },
10670+
if self.sort_only { " SORT ONLY" } else { "" },
10671+
if self.delete_only { " DELETE ONLY" } else { "" },
10672+
if self.reindex { " REINDEX" } else { "" },
10673+
if self.recluster { " RECLUSTER" } else { "" },
10674+
)?;
10675+
if let Some(table_name) = &self.table_name {
10676+
write!(f, " {table_name}")?;
10677+
}
10678+
if let Some(threshold) = &self.threshold {
10679+
write!(f, " TO {threshold} PERCENT")?;
10680+
}
10681+
if self.boost {
10682+
write!(f, " BOOST")?;
10683+
}
10684+
Ok(())
10685+
}
10686+
}
10687+
1063610688
#[cfg(test)]
1063710689
mod tests {
1063810690
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 => {
@@ -16964,6 +16968,40 @@ impl<'a> Parser<'a> {
1696416968
}))
1696516969
}
1696616970

16971+
fn parse_vacuum(&mut self) -> Result<Statement, ParserError> {
16972+
self.expect_keyword(Keyword::VACUUM)?;
16973+
let full = self.parse_keyword(Keyword::FULL);
16974+
let sort_only = self.parse_keywords(&[Keyword::SORT, Keyword::ONLY]);
16975+
let delete_only = self.parse_keywords(&[Keyword::DELETE, Keyword::ONLY]);
16976+
let reindex = self.parse_keyword(Keyword::REINDEX);
16977+
let recluster = self.parse_keyword(Keyword::RECLUSTER);
16978+
let (table_name, threshold, boost) =
16979+
match self.maybe_parse(|p| p.parse_object_name(false))? {
16980+
Some(table_name) => {
16981+
let threshold = if self.parse_keyword(Keyword::TO) {
16982+
let value = self.parse_value()?;
16983+
self.expect_keyword(Keyword::PERCENT)?;
16984+
Some(value.value)
16985+
} else {
16986+
None
16987+
};
16988+
let boost = self.parse_keyword(Keyword::BOOST);
16989+
(Some(table_name), threshold, boost)
16990+
}
16991+
_ => (None, None, false),
16992+
};
16993+
Ok(Statement::Vacuum(VacuumStatement {
16994+
full,
16995+
sort_only,
16996+
delete_only,
16997+
reindex,
16998+
recluster,
16999+
table_name,
17000+
threshold,
17001+
boost,
17002+
}))
17003+
}
17004+
1696717005
/// Consume the parser and return its underlying token buffer
1696817006
pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1696917007
self.tokens

tests/sqlparser_redshift.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,48 @@ fn parse_string_literal_backslash_escape() {
413413
fn parse_utf8_multibyte_idents() {
414414
redshift().verified_stmt("SELECT 🚀.city AS 🎸 FROM customers AS 🚀");
415415
}
416+
417+
#[test]
418+
fn parse_vacuum() {
419+
let stmt = redshift().verified_stmt("VACUUM FULL");
420+
match stmt {
421+
Statement::Vacuum(v) => {
422+
assert!(v.full);
423+
assert_eq!(v.table_name, None);
424+
}
425+
_ => unreachable!(),
426+
}
427+
let stmt = redshift().verified_stmt("VACUUM tbl");
428+
match stmt {
429+
Statement::Vacuum(v) => {
430+
assert_eq!(
431+
v.table_name,
432+
Some(ObjectName::from(vec![Ident::new("tbl"),]))
433+
);
434+
}
435+
_ => unreachable!(),
436+
}
437+
let stmt = redshift().verified_stmt(
438+
"VACUUM FULL SORT ONLY DELETE ONLY REINDEX RECLUSTER db1.sc1.tbl1 TO 20 PERCENT BOOST",
439+
);
440+
match stmt {
441+
Statement::Vacuum(v) => {
442+
assert!(v.full);
443+
assert!(v.sort_only);
444+
assert!(v.delete_only);
445+
assert!(v.reindex);
446+
assert!(v.recluster);
447+
assert_eq!(
448+
v.table_name,
449+
Some(ObjectName::from(vec![
450+
Ident::new("db1"),
451+
Ident::new("sc1"),
452+
Ident::new("tbl1"),
453+
]))
454+
);
455+
assert_eq!(v.threshold, Some(number("20")));
456+
assert!(v.boost);
457+
}
458+
_ => unreachable!(),
459+
}
460+
}

0 commit comments

Comments
 (0)