Skip to content

Commit ee31b64

Browse files
authored
Add support for Redshift SELECT * EXCLUDE (#1936)
1 parent 15f35e1 commit ee31b64

15 files changed

+192
-3
lines changed

src/ast/query.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,11 @@ pub struct Select {
321321
pub top_before_distinct: bool,
322322
/// projection expressions
323323
pub projection: Vec<SelectItem>,
324+
/// Excluded columns from the projection expression which are not specified
325+
/// directly after a wildcard.
326+
///
327+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_EXCLUDE_list.html)
328+
pub exclude: Option<ExcludeSelectItem>,
324329
/// INTO
325330
pub into: Option<SelectInto>,
326331
/// FROM
@@ -401,6 +406,10 @@ impl fmt::Display for Select {
401406
indented_list(f, &self.projection)?;
402407
}
403408

409+
if let Some(exclude) = &self.exclude {
410+
write!(f, " {exclude}")?;
411+
}
412+
404413
if let Some(ref into) = self.into {
405414
f.write_str(" ")?;
406415
into.fmt(f)?;

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,7 @@ impl Spanned for Select {
22202220
distinct: _, // todo
22212221
top: _, // todo, mysql specific
22222222
projection,
2223+
exclude: _,
22232224
into,
22242225
from,
22252226
lateral_views,

src/dialect/duckdb.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,8 @@ impl Dialect for DuckDbDialect {
9494
fn supports_order_by_all(&self) -> bool {
9595
true
9696
}
97+
98+
fn supports_select_wildcard_exclude(&self) -> bool {
99+
true
100+
}
97101
}

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,8 @@ impl Dialect for GenericDialect {
179179
fn supports_filter_during_aggregation(&self) -> bool {
180180
true
181181
}
182+
183+
fn supports_select_wildcard_exclude(&self) -> bool {
184+
true
185+
}
182186
}

src/dialect/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,26 @@ pub trait Dialect: Debug + Any {
570570
false
571571
}
572572

573+
/// Returns true if the dialect supports an exclude option
574+
/// following a wildcard in the projection section. For example:
575+
/// `SELECT * EXCLUDE col1 FROM tbl`.
576+
///
577+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_EXCLUDE_list.html)
578+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/select)
579+
fn supports_select_wildcard_exclude(&self) -> bool {
580+
false
581+
}
582+
583+
/// Returns true if the dialect supports an exclude option
584+
/// as the last item in the projection section, not necessarily
585+
/// after a wildcard. For example:
586+
/// `SELECT *, c1, c2 EXCLUDE c3 FROM tbl`
587+
///
588+
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_EXCLUDE_list.html)
589+
fn supports_select_exclude(&self) -> bool {
590+
false
591+
}
592+
573593
/// Dialect-specific infix parser override
574594
///
575595
/// This method is called to parse the next infix expression.

src/dialect/redshift.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,12 @@ impl Dialect for RedshiftSqlDialect {
131131
fn supports_string_literal_backslash_escape(&self) -> bool {
132132
true
133133
}
134+
135+
fn supports_select_wildcard_exclude(&self) -> bool {
136+
true
137+
}
138+
139+
fn supports_select_exclude(&self) -> bool {
140+
true
141+
}
134142
}

src/dialect/snowflake.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ impl Dialect for SnowflakeDialect {
466466
fn supports_select_expr_star(&self) -> bool {
467467
true
468468
}
469+
470+
fn supports_select_wildcard_exclude(&self) -> bool {
471+
true
472+
}
469473
}
470474

471475
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
11191119
Keyword::FETCH,
11201120
Keyword::UNION,
11211121
Keyword::EXCEPT,
1122+
Keyword::EXCLUDE,
11221123
Keyword::INTERSECT,
11231124
Keyword::MINUS,
11241125
Keyword::CLUSTER,

src/parser/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11740,6 +11740,7 @@ impl<'a> Parser<'a> {
1174011740
top: None,
1174111741
top_before_distinct: false,
1174211742
projection: vec![],
11743+
exclude: None,
1174311744
into: None,
1174411745
from,
1174511746
lateral_views: vec![],
@@ -11782,6 +11783,12 @@ impl<'a> Parser<'a> {
1178211783
self.parse_projection()?
1178311784
};
1178411785

11786+
let exclude = if self.dialect.supports_select_exclude() {
11787+
self.parse_optional_select_item_exclude()?
11788+
} else {
11789+
None
11790+
};
11791+
1178511792
let into = if self.parse_keyword(Keyword::INTO) {
1178611793
Some(self.parse_select_into()?)
1178711794
} else {
@@ -11915,6 +11922,7 @@ impl<'a> Parser<'a> {
1191511922
top,
1191611923
top_before_distinct,
1191711924
projection,
11925+
exclude,
1191811926
into,
1191911927
from,
1192011928
lateral_views,
@@ -15052,8 +15060,7 @@ impl<'a> Parser<'a> {
1505215060
} else {
1505315061
None
1505415062
};
15055-
let opt_exclude = if opt_ilike.is_none()
15056-
&& dialect_of!(self is GenericDialect | DuckDbDialect | SnowflakeDialect)
15063+
let opt_exclude = if opt_ilike.is_none() && self.dialect.supports_select_wildcard_exclude()
1505715064
{
1505815065
self.parse_optional_select_item_exclude()?
1505915066
} else {

tests/sqlparser_clickhouse.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ fn parse_map_access_expr() {
6060
),
6161
})],
6262
})],
63+
exclude: None,
6364
into: None,
6465
from: vec![TableWithJoins {
6566
relation: table_from_name(ObjectName::from(vec![Ident::new("foos")])),

0 commit comments

Comments
 (0)