This is an automated email from the ASF dual-hosted git repository. github-bot pushed a commit to branch gh-readonly-queue/main/pr-2222-31e19429a779793c1ee0338e658d76de053cd203 in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 1da2ff779c0e71932fc882fe7ae4e6d674b8cc76 Author: Andriy Romanov <[email protected]> AuthorDate: Tue Mar 3 04:13:17 2026 -0800 Redshift: Added DISTSTYLE and DISTKEY keywords parsing (#2222) --- src/ast/ddl.rs | 40 ++++++++++++++++++++++++++++++++++++ src/ast/helpers/stmt_create_table.rs | 29 ++++++++++++++++++++++---- src/ast/mod.rs | 21 ++++++++++--------- src/ast/spans.rs | 2 ++ src/keywords.rs | 3 +++ src/parser/mod.rs | 34 ++++++++++++++++++++++++++++++ tests/sqlparser_duckdb.rs | 2 ++ tests/sqlparser_mssql.rs | 4 ++++ tests/sqlparser_postgres.rs | 2 ++ tests/sqlparser_redshift.rs | 15 ++++++++++++++ 10 files changed, 138 insertions(+), 14 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 3a951f66..895959a3 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -3032,6 +3032,12 @@ pub struct CreateTable { /// Snowflake "REQUIRE USER" clause for dybamic tables /// <https://docs.snowflake.com/en/sql-reference/sql/create-dynamic-table> pub require_user: bool, + /// Redshift `DISTSTYLE` option + /// <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html> + pub diststyle: Option<DistStyle>, + /// Redshift `DISTKEY` option + /// <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html> + pub distkey: Option<Ident>, } impl fmt::Display for CreateTable { @@ -3330,6 +3336,12 @@ impl fmt::Display for CreateTable { if self.strict { write!(f, " STRICT")?; } + if let Some(diststyle) = &self.diststyle { + write!(f, " DISTSTYLE {diststyle}")?; + } + if let Some(distkey) = &self.distkey { + write!(f, " DISTKEY({distkey})")?; + } if let Some(query) = &self.query { write!(f, " AS {query}")?; } @@ -3417,6 +3429,34 @@ impl fmt::Display for PartitionBoundValue { } } +/// Redshift distribution style for `CREATE TABLE`. +/// +/// See [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html) +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum DistStyle { + /// `DISTSTYLE AUTO` + Auto, + /// `DISTSTYLE EVEN` + Even, + /// `DISTSTYLE KEY` + Key, + /// `DISTSTYLE ALL` + All, +} + +impl fmt::Display for DistStyle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + DistStyle::Auto => write!(f, "AUTO"), + DistStyle::Even => write!(f, "EVEN"), + DistStyle::Key => write!(f, "KEY"), + DistStyle::All => write!(f, "ALL"), + } + } +} + #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs index e63c90db..258f9c83 100644 --- a/src/ast/helpers/stmt_create_table.rs +++ b/src/ast/helpers/stmt_create_table.rs @@ -25,10 +25,11 @@ use serde::{Deserialize, Serialize}; use sqlparser_derive::{Visit, VisitMut}; use crate::ast::{ - ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableLikeKind, CreateTableOptions, Expr, - FileFormat, ForValues, HiveDistributionStyle, HiveFormat, Ident, InitializeKind, ObjectName, - OnCommit, OneOrManyWithParens, Query, RefreshModeKind, RowAccessPolicy, Statement, - StorageSerializationPolicy, TableConstraint, TableVersion, Tag, WrappedCollection, + ClusteredBy, ColumnDef, CommentDef, CreateTable, CreateTableLikeKind, CreateTableOptions, + DistStyle, Expr, FileFormat, ForValues, HiveDistributionStyle, HiveFormat, Ident, + InitializeKind, ObjectName, OnCommit, OneOrManyWithParens, Query, RefreshModeKind, + RowAccessPolicy, Statement, StorageSerializationPolicy, TableConstraint, TableVersion, Tag, + WrappedCollection, }; use crate::parser::ParserError; @@ -170,6 +171,10 @@ pub struct CreateTableBuilder { pub initialize: Option<InitializeKind>, /// Whether operations require a user identity. pub require_user: bool, + /// Redshift `DISTSTYLE` option. + pub diststyle: Option<DistStyle>, + /// Redshift `DISTKEY` option. + pub distkey: Option<Ident>, } impl CreateTableBuilder { @@ -229,6 +234,8 @@ impl CreateTableBuilder { refresh_mode: None, initialize: None, require_user: false, + diststyle: None, + distkey: None, } } /// Set `OR REPLACE` for the CREATE TABLE statement. @@ -504,6 +511,16 @@ impl CreateTableBuilder { self.require_user = require_user; self } + /// Set Redshift `DISTSTYLE` option. + pub fn diststyle(mut self, diststyle: Option<DistStyle>) -> Self { + self.diststyle = diststyle; + self + } + /// Set Redshift `DISTKEY` option. + pub fn distkey(mut self, distkey: Option<Ident>) -> Self { + self.distkey = distkey; + self + } /// Consume the builder and produce a `CreateTable`. pub fn build(self) -> CreateTable { CreateTable { @@ -560,6 +577,8 @@ impl CreateTableBuilder { refresh_mode: self.refresh_mode, initialize: self.initialize, require_user: self.require_user, + diststyle: self.diststyle, + distkey: self.distkey, } } } @@ -635,6 +654,8 @@ impl From<CreateTable> for CreateTableBuilder { refresh_mode: table.refresh_mode, initialize: table.initialize, require_user: table.require_user, + diststyle: table.diststyle, + distkey: table.distkey, } } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 97cc6193..6a2f9bd9 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -70,16 +70,17 @@ pub use self::ddl::{ ConstraintCharacteristics, CreateConnector, CreateDomain, CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType, CreateTable, CreateTrigger, CreateView, Deduplicate, - DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass, - DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger, ForValues, GeneratedAs, - GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, - IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, - KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, OperatorClassItem, - OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition, - PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, - TagsColumnOption, TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef, - UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, - UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef, + DeferrableInitial, DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator, + DropOperatorClass, DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger, + ForValues, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty, + IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, + IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, + OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose, + Owner, Partition, PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, + ReplicaIdentity, TagsColumnOption, TriggerObjectKind, Truncate, + UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength, + UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, UserDefinedTypeSqlDefinitionOption, + UserDefinedTypeStorage, ViewColumnDef, }; pub use self::dml::{ Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, diff --git a/src/ast/spans.rs b/src/ast/spans.rs index dd62c5ba..7c075197 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -582,6 +582,8 @@ impl Spanned for CreateTable { refresh_mode: _, initialize: _, require_user: _, + diststyle: _, // enum, no span + distkey: _, // Ident, todo } = self; union_spans( diff --git a/src/keywords.rs b/src/keywords.rs index 80f679c0..37a82227 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -335,7 +335,9 @@ define_keywords!( DISCONNECT, DISTINCT, DISTINCTROW, + DISTKEY, DISTRIBUTE, + DISTSTYLE, DIV, DO, DOMAIN, @@ -379,6 +381,7 @@ define_keywords!( ESCAPE, ESCAPED, ESTIMATE, + EVEN, EVENT, EVERY, EVOLVE, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 75450f75..2749969c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8097,6 +8097,23 @@ impl<'a> Parser<'a> { } } + /// Parse Redshift `DISTSTYLE { AUTO | EVEN | KEY | ALL }`. + /// + /// See <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html> + fn parse_dist_style(&mut self) -> Result<DistStyle, ParserError> { + let token = self.next_token(); + match &token.token { + Token::Word(w) => match w.keyword { + Keyword::AUTO => Ok(DistStyle::Auto), + Keyword::EVEN => Ok(DistStyle::Even), + Keyword::KEY => Ok(DistStyle::Key), + Keyword::ALL => Ok(DistStyle::All), + _ => self.expected("AUTO, EVEN, KEY, or ALL", token), + }, + _ => self.expected("AUTO, EVEN, KEY, or ALL", token), + } + } + /// Parse Hive formats. pub fn parse_hive_formats(&mut self) -> Result<Option<HiveFormat>, ParserError> { let mut hive_format: Option<HiveFormat> = None; @@ -8367,6 +8384,21 @@ impl<'a> Parser<'a> { let strict = self.parse_keyword(Keyword::STRICT); + // Redshift: DISTSTYLE, DISTKEY + let diststyle = if self.parse_keyword(Keyword::DISTSTYLE) { + Some(self.parse_dist_style()?) + } else { + None + }; + let distkey = if self.parse_keyword(Keyword::DISTKEY) { + self.expect_token(&Token::LParen)?; + let column = self.parse_identifier()?; + self.expect_token(&Token::RParen)?; + Some(column) + } else { + None + }; + // Parse optional `AS ( query )` let query = if self.parse_keyword(Keyword::AS) { Some(self.parse_query()?) @@ -8406,6 +8438,8 @@ impl<'a> Parser<'a> { .table_options(create_table_config.table_options) .primary_key(primary_key) .strict(strict) + .diststyle(diststyle) + .distkey(distkey) .build()) } diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index a061876d..0512053a 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -788,6 +788,8 @@ fn test_duckdb_union_datatype() { refresh_mode: None, initialize: None, require_user: Default::default(), + diststyle: Default::default(), + distkey: Default::default(), }), stmt ); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 9033efe0..aa31b632 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -2006,6 +2006,8 @@ fn parse_create_table_with_valid_options() { refresh_mode: None, initialize: None, require_user: false, + diststyle: None, + distkey: None, }) ); } @@ -2174,6 +2176,8 @@ fn parse_create_table_with_identity_column() { refresh_mode: None, initialize: None, require_user: false, + diststyle: None, + distkey: None, }), ); } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 434c5fd7..7dd624a2 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -6418,6 +6418,8 @@ fn parse_trigger_related_functions() { refresh_mode: None, initialize: None, require_user: false, + diststyle: None, + distkey: None, } ); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 90652ff4..03dfda2c 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -452,3 +452,18 @@ fn parse_vacuum() { _ => unreachable!(), } } + +#[test] +fn test_create_table_diststyle_distkey() { + redshift().verified_stmt( + "CREATE TEMPORARY TABLE tmp_sbk_summary_pp DISTSTYLE KEY DISTKEY(bet_id) AS SELECT 1 AS bet_id", + ); +} + +#[test] +fn test_create_table_diststyle() { + redshift().verified_stmt("CREATE TABLE t1 (c1 INT) DISTSTYLE AUTO"); + redshift().verified_stmt("CREATE TABLE t1 (c1 INT) DISTSTYLE EVEN"); + redshift().verified_stmt("CREATE TABLE t1 (c1 INT) DISTSTYLE KEY DISTKEY(c1)"); + redshift().verified_stmt("CREATE TABLE t1 (c1 INT) DISTSTYLE ALL"); +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
