Add support for Redshift SELECT * EXCLUDE (#1936)
Some checks are pending
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Rust / test (beta) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run

This commit is contained in:
Yoav Cohen 2025-07-11 11:39:29 +02:00 committed by GitHub
parent 15f35e1476
commit ee31b64f9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 192 additions and 3 deletions

View file

@ -459,6 +459,7 @@ fn parse_update_set_from() {
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("name"))),
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("id"))),
],
exclude: None,
into: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident::new("t1")])),
@ -5695,6 +5696,7 @@ fn test_parse_named_window() {
},
},
],
exclude: None,
into: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident {
@ -6351,6 +6353,7 @@ fn parse_interval_and_or_xor() {
quote_style: None,
span: Span::empty(),
}))],
exclude: None,
into: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident {
@ -8620,6 +8623,7 @@ fn lateral_function() {
distinct: None,
top: None,
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())],
exclude: None,
top_before_distinct: false,
into: None,
from: vec![TableWithJoins {
@ -9616,6 +9620,7 @@ fn parse_merge() {
projection: vec![SelectItem::Wildcard(
WildcardAdditionalOptions::default()
)],
exclude: None,
into: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![
@ -11534,6 +11539,7 @@ fn parse_unload() {
top: None,
top_before_distinct: false,
projection: vec![UnnamedExpr(Expr::Identifier(Ident::new("cola"))),],
exclude: None,
into: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident::new("tab")])),
@ -11734,6 +11740,7 @@ fn parse_connect_by() {
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("manager_id"))),
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("title"))),
],
exclude: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident::new("employees")])),
joins: vec![],
@ -11815,6 +11822,7 @@ fn parse_connect_by() {
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("manager_id"))),
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("title"))),
],
exclude: None,
from: vec![TableWithJoins {
relation: table_from_name(ObjectName::from(vec![Ident::new("employees")])),
joins: vec![],
@ -12748,6 +12756,7 @@ fn test_extract_seconds_ok() {
format: None,
}),
})],
exclude: None,
into: None,
from: vec![],
lateral_views: vec![],
@ -14820,6 +14829,7 @@ fn test_select_from_first() {
distinct: None,
top: None,
projection,
exclude: None,
top_before_distinct: false,
into: None,
from: vec![TableWithJoins {
@ -16000,3 +16010,108 @@ fn parse_create_procedure_with_parameter_modes() {
_ => unreachable!(),
}
}
#[test]
fn test_select_exclude() {
let dialects = all_dialects_where(|d| d.supports_select_wildcard_exclude());
match &dialects
.verified_only_select("SELECT * EXCLUDE c1 FROM test")
.projection[0]
{
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
);
}
_ => unreachable!(),
}
match &dialects
.verified_only_select("SELECT * EXCLUDE (c1, c2) FROM test")
.projection[0]
{
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Multiple(vec![
Ident::new("c1"),
Ident::new("c2")
]))
);
}
_ => unreachable!(),
}
let select = dialects.verified_only_select("SELECT * EXCLUDE c1, c2 FROM test");
match &select.projection[0] {
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
);
}
_ => unreachable!(),
}
match &select.projection[1] {
SelectItem::UnnamedExpr(Expr::Identifier(ident)) => {
assert_eq!(*ident, Ident::new("c2"));
}
_ => unreachable!(),
}
let dialects = all_dialects_where(|d| d.supports_select_exclude());
let select = dialects.verified_only_select("SELECT *, c1 EXCLUDE c1 FROM test");
match &select.projection[0] {
SelectItem::Wildcard(additional_options) => {
assert_eq!(*additional_options, WildcardAdditionalOptions::default());
}
_ => unreachable!(),
}
assert_eq!(
select.exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
);
let dialects = all_dialects_where(|d| {
d.supports_select_wildcard_exclude() && !d.supports_select_exclude()
});
let select = dialects.verified_only_select("SELECT * EXCLUDE c1 FROM test");
match &select.projection[0] {
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
);
}
_ => unreachable!(),
}
// Dialects that only support the wildcard form and do not accept EXCLUDE as an implicity alias
// will fail when encountered with the `c2` ident
let dialects = all_dialects_where(|d| {
d.supports_select_wildcard_exclude()
&& !d.supports_select_exclude()
&& d.is_column_alias(&Keyword::EXCLUDE, &mut Parser::new(d))
});
assert_eq!(
dialects
.parse_sql_statements("SELECT *, c1 EXCLUDE c2 FROM test")
.err()
.unwrap(),
ParserError::ParserError("Expected: end of statement, found: c2".to_string())
);
// Dialects that only support the wildcard form and accept EXCLUDE as an implicity alias
// will fail when encountered with the `EXCLUDE` keyword
let dialects = all_dialects_where(|d| {
d.supports_select_wildcard_exclude()
&& !d.supports_select_exclude()
&& !d.is_column_alias(&Keyword::EXCLUDE, &mut Parser::new(d))
});
assert_eq!(
dialects
.parse_sql_statements("SELECT *, c1 EXCLUDE c2 FROM test")
.err()
.unwrap(),
ParserError::ParserError("Expected: end of statement, found: EXCLUDE".to_string())
);
}