Skip to content

Commit 63e9a33

Browse files
authored
Redshift: PartiQL AT <index> (#2303)
1 parent 19a7468 commit 63e9a33

File tree

7 files changed

+55
-2
lines changed

7 files changed

+55
-2
lines changed

src/ast/query.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,12 @@ pub struct TableAlias {
25332533
pub name: Ident,
25342534
/// Optional column aliases declared in parentheses after the table alias.
25352535
pub columns: Vec<TableAliasColumnDef>,
2536+
/// Optional PartiQL index alias declared with `AT`. For example:
2537+
/// ```sql
2538+
/// SELECT element, index FROM bar AS b, b.data.scalar_array AS element AT index
2539+
/// ```
2540+
/// See: <https://docs.aws.amazon.com/redshift/latest/dg/query-super.html>
2541+
pub at: Option<Ident>,
25362542
}
25372543

25382544
impl fmt::Display for TableAlias {
@@ -2541,6 +2547,9 @@ impl fmt::Display for TableAlias {
25412547
if !self.columns.is_empty() {
25422548
write!(f, " ({})", display_comma_separated(&self.columns))?;
25432549
}
2550+
if let Some(at) = &self.at {
2551+
write!(f, " AT {at}")?;
2552+
}
25442553
Ok(())
25452554
}
25462555
}

src/ast/spans.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,8 +2186,13 @@ impl Spanned for TableAlias {
21862186
explicit: _,
21872187
name,
21882188
columns,
2189+
at,
21892190
} = self;
2190-
union_spans(core::iter::once(name.span).chain(columns.iter().map(Spanned::span)))
2191+
union_spans(
2192+
core::iter::once(name.span)
2193+
.chain(columns.iter().map(Spanned::span))
2194+
.chain(at.iter().map(|at| at.span)),
2195+
)
21912196
}
21922197
}
21932198

src/parser/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12822,10 +12822,16 @@ impl<'a> Parser<'a> {
1282212822
match self.parse_optional_alias_inner(None, validator)? {
1282312823
Some(name) => {
1282412824
let columns = self.parse_table_alias_column_defs()?;
12825+
let at = if self.dialect.supports_partiql() && self.parse_keyword(Keyword::AT) {
12826+
Some(self.parse_identifier()?)
12827+
} else {
12828+
None
12829+
};
1282512830
Ok(Some(TableAlias {
1282612831
explicit,
1282712832
name,
1282812833
columns,
12834+
at,
1282912835
}))
1283012836
}
1283112837
None => Ok(None),
@@ -14474,6 +14480,7 @@ impl<'a> Parser<'a> {
1447414480
explicit: false,
1447514481
name,
1447614482
columns: vec![],
14483+
at: None,
1447714484
},
1447814485
query,
1447914486
from: None,
@@ -14518,6 +14525,7 @@ impl<'a> Parser<'a> {
1451814525
explicit: false,
1451914526
name,
1452014527
columns,
14528+
at: None,
1452114529
},
1452214530
query,
1452314531
from: None,

src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ pub fn table_alias(explicit: bool, name: impl Into<String>) -> Option<TableAlias
374374
explicit,
375375
name: Ident::new(name),
376376
columns: vec![],
377+
at: None,
377378
})
378379
}
379380

tests/sqlparser_common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ fn parse_select_with_table_alias() {
669669
TableAliasColumnDef::from_name("B"),
670670
TableAliasColumnDef::from_name("C"),
671671
],
672+
at: None,
672673
}),
673674
args: None,
674675
with_hints: vec![],
@@ -7861,6 +7862,7 @@ fn parse_recursive_cte() {
78617862
span: Span::empty(),
78627863
},
78637864
columns: vec![TableAliasColumnDef::from_name("val")],
7865+
at: None,
78647866
},
78657867
query: Box::new(cte_query),
78667868
from: None,
@@ -11347,6 +11349,7 @@ fn parse_pivot_table() {
1134711349
TableAliasColumnDef::from_name("c"),
1134811350
TableAliasColumnDef::from_name("d"),
1134911351
],
11352+
at: None,
1135011353
})
1135111354
}
1135211355
);
@@ -11485,6 +11488,7 @@ fn parse_unpivot_table() {
1148511488
.into_iter()
1148611489
.map(TableAliasColumnDef::from_name)
1148711490
.collect(),
11491+
at: None,
1148811492
}),
1148911493
};
1149011494
pretty_assertions::assert_eq!(verified_only_select(sql).from[0].relation, base_unpivot);

tests/sqlparser_mssql.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ fn parse_mssql_delimited_identifiers() {
106106
&Some(TableAlias {
107107
explicit: false,
108108
name: Ident::with_quote('[', "WHERE"),
109-
columns: vec![]
109+
columns: vec![],
110+
at: None,
110111
})
111112
);
112113
}

tests/sqlparser_redshift.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,3 +517,28 @@ fn test_null_treatment_inside_and_outside_window_function() {
517517
redshift().verified_stmt("SELECT FIRST_VALUE(1 IGNORE NULLS) OVER () FROM (SELECT 1) t");
518518
redshift().verified_stmt("SELECT FIRST_VALUE(1) IGNORE NULLS OVER () FROM (SELECT 1) t");
519519
}
520+
521+
#[test]
522+
fn test_partiql_from_alias_with_at_index() {
523+
let dialects = all_dialects_where(|d| d.supports_partiql());
524+
dialects.verified_stmt("SELECT * FROM lineitem AS l (a, b, c) AT idx");
525+
526+
let sql =
527+
"SELECT index, val FROM (SELECT array('AAA', 'BBB') AS val) AS b, b.val AS val AT index";
528+
let select = dialects.verified_only_select(sql);
529+
530+
match &select.from[1].relation {
531+
TableFactor::Table { name, alias, .. } => {
532+
assert_eq!(
533+
name,
534+
&ObjectName::from(vec![Ident::new("b"), Ident::new("val")])
535+
);
536+
assert_eq!(alias.as_ref().map(|a| &a.name), Some(&Ident::new("val")));
537+
assert_eq!(
538+
alias.as_ref().and_then(|a| a.at.as_ref()),
539+
Some(&Ident::new("index"))
540+
);
541+
}
542+
_ => panic!("expected table factor"),
543+
}
544+
}

0 commit comments

Comments
 (0)