Skip to content

Commit d0fcc06

Browse files
authored
Snowflake ALTER TABLE clustering options (#1579)
1 parent 7b50ac3 commit d0fcc06

File tree

5 files changed

+123
-15
lines changed

5 files changed

+123
-15
lines changed

src/ast/ddl.rs

+68-15
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ pub enum AlterTableOperation {
7070
///
7171
/// Note: this is a ClickHouse-specific operation.
7272
/// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#drop-projection)
73-
DropProjection { if_exists: bool, name: Ident },
73+
DropProjection {
74+
if_exists: bool,
75+
name: Ident,
76+
},
7477

7578
/// `MATERIALIZE PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
7679
///
@@ -99,11 +102,15 @@ pub enum AlterTableOperation {
99102
/// `DISABLE RULE rewrite_rule_name`
100103
///
101104
/// Note: this is a PostgreSQL-specific operation.
102-
DisableRule { name: Ident },
105+
DisableRule {
106+
name: Ident,
107+
},
103108
/// `DISABLE TRIGGER [ trigger_name | ALL | USER ]`
104109
///
105110
/// Note: this is a PostgreSQL-specific operation.
106-
DisableTrigger { name: Ident },
111+
DisableTrigger {
112+
name: Ident,
113+
},
107114
/// `DROP CONSTRAINT [ IF EXISTS ] <name>`
108115
DropConstraint {
109116
if_exists: bool,
@@ -152,31 +159,43 @@ pub enum AlterTableOperation {
152159
/// `ENABLE ALWAYS RULE rewrite_rule_name`
153160
///
154161
/// Note: this is a PostgreSQL-specific operation.
155-
EnableAlwaysRule { name: Ident },
162+
EnableAlwaysRule {
163+
name: Ident,
164+
},
156165
/// `ENABLE ALWAYS TRIGGER trigger_name`
157166
///
158167
/// Note: this is a PostgreSQL-specific operation.
159-
EnableAlwaysTrigger { name: Ident },
168+
EnableAlwaysTrigger {
169+
name: Ident,
170+
},
160171
/// `ENABLE REPLICA RULE rewrite_rule_name`
161172
///
162173
/// Note: this is a PostgreSQL-specific operation.
163-
EnableReplicaRule { name: Ident },
174+
EnableReplicaRule {
175+
name: Ident,
176+
},
164177
/// `ENABLE REPLICA TRIGGER trigger_name`
165178
///
166179
/// Note: this is a PostgreSQL-specific operation.
167-
EnableReplicaTrigger { name: Ident },
180+
EnableReplicaTrigger {
181+
name: Ident,
182+
},
168183
/// `ENABLE ROW LEVEL SECURITY`
169184
///
170185
/// Note: this is a PostgreSQL-specific operation.
171186
EnableRowLevelSecurity,
172187
/// `ENABLE RULE rewrite_rule_name`
173188
///
174189
/// Note: this is a PostgreSQL-specific operation.
175-
EnableRule { name: Ident },
190+
EnableRule {
191+
name: Ident,
192+
},
176193
/// `ENABLE TRIGGER [ trigger_name | ALL | USER ]`
177194
///
178195
/// Note: this is a PostgreSQL-specific operation.
179-
EnableTrigger { name: Ident },
196+
EnableTrigger {
197+
name: Ident,
198+
},
180199
/// `RENAME TO PARTITION (partition=val)`
181200
RenamePartitions {
182201
old_partitions: Vec<Expr>,
@@ -197,7 +216,9 @@ pub enum AlterTableOperation {
197216
new_column_name: Ident,
198217
},
199218
/// `RENAME TO <table_name>`
200-
RenameTable { table_name: ObjectName },
219+
RenameTable {
220+
table_name: ObjectName,
221+
},
201222
// CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
202223
ChangeColumn {
203224
old_name: Ident,
@@ -218,7 +239,10 @@ pub enum AlterTableOperation {
218239
/// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
219240
///
220241
/// Note: this is a PostgreSQL-specific operation.
221-
RenameConstraint { old_name: Ident, new_name: Ident },
242+
RenameConstraint {
243+
old_name: Ident,
244+
new_name: Ident,
245+
},
222246
/// `ALTER [ COLUMN ]`
223247
AlterColumn {
224248
column_name: Ident,
@@ -227,14 +251,27 @@ pub enum AlterTableOperation {
227251
/// 'SWAP WITH <table_name>'
228252
///
229253
/// Note: this is Snowflake specific <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
230-
SwapWith { table_name: ObjectName },
254+
SwapWith {
255+
table_name: ObjectName,
256+
},
231257
/// 'SET TBLPROPERTIES ( { property_key [ = ] property_val } [, ...] )'
232-
SetTblProperties { table_properties: Vec<SqlOption> },
233-
258+
SetTblProperties {
259+
table_properties: Vec<SqlOption>,
260+
},
234261
/// `OWNER TO { <new_owner> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
235262
///
236263
/// Note: this is PostgreSQL-specific <https://www.postgresql.org/docs/current/sql-altertable.html>
237-
OwnerTo { new_owner: Owner },
264+
OwnerTo {
265+
new_owner: Owner,
266+
},
267+
/// Snowflake table clustering options
268+
/// <https://docs.snowflake.com/en/sql-reference/sql/alter-table#clustering-actions-clusteringaction>
269+
ClusterBy {
270+
exprs: Vec<Expr>,
271+
},
272+
DropClusteringKey,
273+
SuspendRecluster,
274+
ResumeRecluster,
238275
}
239276

240277
/// An `ALTER Policy` (`Statement::AlterPolicy`) operation
@@ -548,6 +585,22 @@ impl fmt::Display for AlterTableOperation {
548585
}
549586
Ok(())
550587
}
588+
AlterTableOperation::ClusterBy { exprs } => {
589+
write!(f, "CLUSTER BY ({})", display_comma_separated(exprs))?;
590+
Ok(())
591+
}
592+
AlterTableOperation::DropClusteringKey => {
593+
write!(f, "DROP CLUSTERING KEY")?;
594+
Ok(())
595+
}
596+
AlterTableOperation::SuspendRecluster => {
597+
write!(f, "SUSPEND RECLUSTER")?;
598+
Ok(())
599+
}
600+
AlterTableOperation::ResumeRecluster => {
601+
write!(f, "RESUME RECLUSTER")?;
602+
Ok(())
603+
}
551604
}
552605
}
553606
}

src/ast/spans.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,10 @@ impl Spanned for AlterTableOperation {
10201020
union_spans(table_properties.iter().map(|i| i.span()))
10211021
}
10221022
AlterTableOperation::OwnerTo { .. } => Span::empty(),
1023+
AlterTableOperation::ClusterBy { exprs } => union_spans(exprs.iter().map(|e| e.span())),
1024+
AlterTableOperation::DropClusteringKey => Span::empty(),
1025+
AlterTableOperation::SuspendRecluster => Span::empty(),
1026+
AlterTableOperation::ResumeRecluster => Span::empty(),
10231027
}
10241028
}
10251029
}

src/keywords.rs

+4
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ define_keywords!(
168168
CLOSE,
169169
CLUSTER,
170170
CLUSTERED,
171+
CLUSTERING,
171172
COALESCE,
172173
COLLATE,
173174
COLLATION,
@@ -622,6 +623,7 @@ define_keywords!(
622623
READS,
623624
READ_ONLY,
624625
REAL,
626+
RECLUSTER,
625627
RECURSIVE,
626628
REF,
627629
REFERENCES,
@@ -656,6 +658,7 @@ define_keywords!(
656658
RESTRICTIVE,
657659
RESULT,
658660
RESULTSET,
661+
RESUME,
659662
RETAIN,
660663
RETURN,
661664
RETURNING,
@@ -745,6 +748,7 @@ define_keywords!(
745748
SUM,
746749
SUPER,
747750
SUPERUSER,
751+
SUSPEND,
748752
SWAP,
749753
SYMMETRIC,
750754
SYNC,

src/parser/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -7273,6 +7273,8 @@ impl<'a> Parser<'a> {
72737273
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
72747274
let name = self.parse_identifier(false)?;
72757275
AlterTableOperation::DropProjection { if_exists, name }
7276+
} else if self.parse_keywords(&[Keyword::CLUSTERING, Keyword::KEY]) {
7277+
AlterTableOperation::DropClusteringKey
72767278
} else {
72777279
let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ]
72787280
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
@@ -7444,6 +7446,15 @@ impl<'a> Parser<'a> {
74447446
partition,
74457447
with_name,
74467448
}
7449+
} else if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) {
7450+
self.expect_token(&Token::LParen)?;
7451+
let exprs = self.parse_comma_separated(|parser| parser.parse_expr())?;
7452+
self.expect_token(&Token::RParen)?;
7453+
AlterTableOperation::ClusterBy { exprs }
7454+
} else if self.parse_keywords(&[Keyword::SUSPEND, Keyword::RECLUSTER]) {
7455+
AlterTableOperation::SuspendRecluster
7456+
} else if self.parse_keywords(&[Keyword::RESUME, Keyword::RECLUSTER]) {
7457+
AlterTableOperation::ResumeRecluster
74477458
} else {
74487459
let options: Vec<SqlOption> =
74497460
self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?;

tests/sqlparser_snowflake.rs

+36
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,42 @@ fn test_alter_table_swap_with() {
14111411
};
14121412
}
14131413

1414+
#[test]
1415+
fn test_alter_table_clustering() {
1416+
let sql = r#"ALTER TABLE tab CLUSTER BY (c1, "c2", TO_DATE(c3))"#;
1417+
match alter_table_op(snowflake_and_generic().verified_stmt(sql)) {
1418+
AlterTableOperation::ClusterBy { exprs } => {
1419+
assert_eq!(
1420+
exprs,
1421+
[
1422+
Expr::Identifier(Ident::new("c1")),
1423+
Expr::Identifier(Ident::with_quote('"', "c2")),
1424+
Expr::Function(Function {
1425+
name: ObjectName(vec![Ident::new("TO_DATE")]),
1426+
parameters: FunctionArguments::None,
1427+
args: FunctionArguments::List(FunctionArgumentList {
1428+
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
1429+
Expr::Identifier(Ident::new("c3"))
1430+
))],
1431+
duplicate_treatment: None,
1432+
clauses: vec![],
1433+
}),
1434+
filter: None,
1435+
null_treatment: None,
1436+
over: None,
1437+
within_group: vec![]
1438+
})
1439+
],
1440+
);
1441+
}
1442+
_ => unreachable!(),
1443+
}
1444+
1445+
snowflake_and_generic().verified_stmt("ALTER TABLE tbl DROP CLUSTERING KEY");
1446+
snowflake_and_generic().verified_stmt("ALTER TABLE tbl SUSPEND RECLUSTER");
1447+
snowflake_and_generic().verified_stmt("ALTER TABLE tbl RESUME RECLUSTER");
1448+
}
1449+
14141450
#[test]
14151451
fn test_drop_stage() {
14161452
match snowflake_and_generic().verified_stmt("DROP STAGE s1") {

0 commit comments

Comments
 (0)