This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 64f4b1fa Added SORTKEY keyword parsing for redshift queries (#2261)
64f4b1fa is described below
commit 64f4b1fa2b47f957b1c8d39c3c54b0380daa32b8
Author: Andriy Romanov <[email protected]>
AuthorDate: Thu Mar 5 05:45:41 2026 -0800
Added SORTKEY keyword parsing for redshift queries (#2261)
---
src/ast/ddl.rs | 18 +++++++++++++++++-
src/ast/helpers/stmt_create_table.rs | 14 ++++++++++++--
src/ast/spans.rs | 6 ++++--
src/keywords.rs | 1 +
src/parser/mod.rs | 32 +++++++++++++++++++++++++++++---
tests/sqlparser_duckdb.rs | 1 +
tests/sqlparser_mssql.rs | 2 ++
tests/sqlparser_postgres.rs | 1 +
tests/sqlparser_redshift.rs | 26 ++++++++++++++++++++++++++
9 files changed, 93 insertions(+), 8 deletions(-)
diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 895959a3..6bea28cb 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -457,6 +457,12 @@ pub enum AlterTableOperation {
},
/// Remove the clustering key from the table.
DropClusteringKey,
+ /// Redshift `ALTER SORTKEY (column_list)`
+ /// <https://docs.aws.amazon.com/redshift/latest/dg/r_ALTER_TABLE.html>
+ AlterSortKey {
+ /// Column references in the sort key.
+ columns: Vec<Expr>,
+ },
/// Suspend background reclustering operations.
SuspendRecluster,
/// Resume background reclustering operations.
@@ -993,6 +999,10 @@ impl fmt::Display for AlterTableOperation {
write!(f, "DROP CLUSTERING KEY")?;
Ok(())
}
+ AlterTableOperation::AlterSortKey { columns } => {
+ write!(f, "ALTER SORTKEY({})",
display_comma_separated(columns))?;
+ Ok(())
+ }
AlterTableOperation::SuspendRecluster => {
write!(f, "SUSPEND RECLUSTER")?;
Ok(())
@@ -3037,7 +3047,10 @@ pub struct CreateTable {
pub diststyle: Option<DistStyle>,
/// Redshift `DISTKEY` option
///
<https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html>
- pub distkey: Option<Ident>,
+ pub distkey: Option<Expr>,
+ /// Redshift `SORTKEY` option
+ ///
<https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html>
+ pub sortkey: Option<Vec<Expr>>,
}
impl fmt::Display for CreateTable {
@@ -3342,6 +3355,9 @@ impl fmt::Display for CreateTable {
if let Some(distkey) = &self.distkey {
write!(f, " DISTKEY({distkey})")?;
}
+ if let Some(sortkey) = &self.sortkey {
+ write!(f, " SORTKEY({})", display_comma_separated(sortkey))?;
+ }
if let Some(query) = &self.query {
write!(f, " AS {query}")?;
}
diff --git a/src/ast/helpers/stmt_create_table.rs
b/src/ast/helpers/stmt_create_table.rs
index 258f9c83..5963e940 100644
--- a/src/ast/helpers/stmt_create_table.rs
+++ b/src/ast/helpers/stmt_create_table.rs
@@ -174,7 +174,9 @@ pub struct CreateTableBuilder {
/// Redshift `DISTSTYLE` option.
pub diststyle: Option<DistStyle>,
/// Redshift `DISTKEY` option.
- pub distkey: Option<Ident>,
+ pub distkey: Option<Expr>,
+ /// Redshift `SORTKEY` option.
+ pub sortkey: Option<Vec<Expr>>,
}
impl CreateTableBuilder {
@@ -236,6 +238,7 @@ impl CreateTableBuilder {
require_user: false,
diststyle: None,
distkey: None,
+ sortkey: None,
}
}
/// Set `OR REPLACE` for the CREATE TABLE statement.
@@ -517,10 +520,15 @@ impl CreateTableBuilder {
self
}
/// Set Redshift `DISTKEY` option.
- pub fn distkey(mut self, distkey: Option<Ident>) -> Self {
+ pub fn distkey(mut self, distkey: Option<Expr>) -> Self {
self.distkey = distkey;
self
}
+ /// Set Redshift `SORTKEY` option.
+ pub fn sortkey(mut self, sortkey: Option<Vec<Expr>>) -> Self {
+ self.sortkey = sortkey;
+ self
+ }
/// Consume the builder and produce a `CreateTable`.
pub fn build(self) -> CreateTable {
CreateTable {
@@ -579,6 +587,7 @@ impl CreateTableBuilder {
require_user: self.require_user,
diststyle: self.diststyle,
distkey: self.distkey,
+ sortkey: self.sortkey,
}
}
}
@@ -656,6 +665,7 @@ impl From<CreateTable> for CreateTableBuilder {
require_user: table.require_user,
diststyle: table.diststyle,
distkey: table.distkey,
+ sortkey: table.sortkey,
}
}
}
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 3f73af40..57d57b24 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -582,8 +582,9 @@ impl Spanned for CreateTable {
refresh_mode: _,
initialize: _,
require_user: _,
- diststyle: _, // enum, no span
- distkey: _, // Ident, todo
+ diststyle: _,
+ distkey: _,
+ sortkey: _,
} = self;
union_spans(
@@ -1193,6 +1194,7 @@ impl Spanned for AlterTableOperation {
AlterTableOperation::OwnerTo { .. } => Span::empty(),
AlterTableOperation::ClusterBy { exprs } =>
union_spans(exprs.iter().map(|e| e.span())),
AlterTableOperation::DropClusteringKey => Span::empty(),
+ AlterTableOperation::AlterSortKey { .. } => Span::empty(),
AlterTableOperation::SuspendRecluster => Span::empty(),
AlterTableOperation::ResumeRecluster => Span::empty(),
AlterTableOperation::Refresh { .. } => Span::empty(),
diff --git a/src/keywords.rs b/src/keywords.rs
index 37a82227..9ea85fd3 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -953,6 +953,7 @@ define_keywords!(
SOME,
SORT,
SORTED,
+ SORTKEY,
SOURCE,
SPATIAL,
SPECIFIC,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 75b5bfa7..eaaa95ec 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -8384,7 +8384,7 @@ impl<'a> Parser<'a> {
let strict = self.parse_keyword(Keyword::STRICT);
- // Redshift: DISTSTYLE, DISTKEY
+ // Redshift: DISTSTYLE, DISTKEY, SORTKEY
let diststyle = if self.parse_keyword(Keyword::DISTSTYLE) {
Some(self.parse_dist_style()?)
} else {
@@ -8392,9 +8392,17 @@ impl<'a> Parser<'a> {
};
let distkey = if self.parse_keyword(Keyword::DISTKEY) {
self.expect_token(&Token::LParen)?;
- let column = self.parse_identifier()?;
+ let expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
- Some(column)
+ Some(expr)
+ } else {
+ None
+ };
+ let sortkey = if self.parse_keyword(Keyword::SORTKEY) {
+ self.expect_token(&Token::LParen)?;
+ let columns = self.parse_comma_separated(|p| p.parse_expr())?;
+ self.expect_token(&Token::RParen)?;
+ Some(columns)
} else {
None
};
@@ -8440,6 +8448,7 @@ impl<'a> Parser<'a> {
.strict(strict)
.diststyle(diststyle)
.distkey(distkey)
+ .sortkey(sortkey)
.build())
}
@@ -9979,6 +9988,18 @@ impl<'a> Parser<'a> {
})
}
+ /// Parse Redshift `ALTER SORTKEY (column_list)`.
+ ///
+ /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_ALTER_TABLE.html>
+ fn parse_alter_sort_key(&mut self) -> Result<AlterTableOperation,
ParserError> {
+ self.expect_keyword_is(Keyword::ALTER)?;
+ self.expect_keyword_is(Keyword::SORTKEY)?;
+ self.expect_token(&Token::LParen)?;
+ let columns = self.parse_comma_separated(|p| p.parse_expr())?;
+ self.expect_token(&Token::RParen)?;
+ Ok(AlterTableOperation::AlterSortKey { columns })
+ }
+
/// Parse a single `ALTER TABLE` operation and return an
`AlterTableOperation`.
pub fn parse_alter_table_operation(&mut self) ->
Result<AlterTableOperation, ParserError> {
let operation = if self.parse_keyword(Keyword::ADD) {
@@ -10257,6 +10278,11 @@ impl<'a> Parser<'a> {
column_position,
}
} else if self.parse_keyword(Keyword::ALTER) {
+ if self.peek_keyword(Keyword::SORTKEY) {
+ self.prev_token();
+ return self.parse_alter_sort_key();
+ }
+
let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ]
let column_name = self.parse_identifier()?;
let is_postgresql = dialect_of!(self is PostgreSqlDialect);
diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs
index 0512053a..33a6cb45 100644
--- a/tests/sqlparser_duckdb.rs
+++ b/tests/sqlparser_duckdb.rs
@@ -790,6 +790,7 @@ fn test_duckdb_union_datatype() {
require_user: Default::default(),
diststyle: Default::default(),
distkey: Default::default(),
+ sortkey: Default::default(),
}),
stmt
);
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index aa31b632..da6ecace 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -2008,6 +2008,7 @@ fn parse_create_table_with_valid_options() {
require_user: false,
diststyle: None,
distkey: None,
+ sortkey: None,
})
);
}
@@ -2178,6 +2179,7 @@ fn parse_create_table_with_identity_column() {
require_user: false,
diststyle: None,
distkey: None,
+ sortkey: None,
}),
);
}
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index c83860ff..60aca14b 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -6476,6 +6476,7 @@ fn parse_trigger_related_functions() {
require_user: false,
diststyle: None,
distkey: None,
+ sortkey: None,
}
);
diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs
index 243b0646..184aa5b6 100644
--- a/tests/sqlparser_redshift.rs
+++ b/tests/sqlparser_redshift.rs
@@ -474,3 +474,29 @@ fn test_copy_credentials() {
"COPY t1 FROM 's3://bucket/file.csv' CREDENTIALS
'aws_access_key_id=AK;aws_secret_access_key=SK' CSV",
);
}
+
+#[test]
+fn test_create_table_sortkey() {
+ redshift().verified_stmt("CREATE TABLE t1 (c1 INT, c2 INT, c3 TIMESTAMP)
SORTKEY(c3)");
+ redshift().verified_stmt("CREATE TABLE t1 (c1 INT, c2 INT) SORTKEY(c1,
c2)");
+}
+
+#[test]
+fn test_create_table_distkey_sortkey_with_ctas() {
+ redshift().verified_stmt(
+ "CREATE TABLE t1 DISTKEY(1) SORTKEY(1, 3) AS SELECT eventid, venueid,
dateid, eventname FROM event",
+ );
+}
+
+#[test]
+fn test_create_table_diststyle_distkey_sortkey() {
+ redshift().verified_stmt(
+ "CREATE TABLE t1 (c1 INT, c2 INT) DISTSTYLE KEY DISTKEY(c1)
SORTKEY(c1, c2)",
+ );
+}
+
+#[test]
+fn test_alter_table_alter_sortkey() {
+ redshift().verified_stmt("ALTER TABLE users ALTER SORTKEY(created_at)");
+ redshift().verified_stmt("ALTER TABLE users ALTER SORTKEY(c1, c2)");
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]