Skip to content

Commit 8fbbbdc

Browse files
yoavcloudLucaCappelletti94
authored andcommitted
Redshift: PartiQL AT <index> (apache#2303)
1 parent 4ac128e commit 8fbbbdc

7 files changed

Lines changed: 55 additions & 2 deletions

File tree

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
@@ -2188,8 +2188,13 @@ impl Spanned for TableAlias {
21882188
explicit: _,
21892189
name,
21902190
columns,
2191+
at,
21912192
} = self;
2192-
union_spans(core::iter::once(name.span).chain(columns.iter().map(Spanned::span)))
2193+
union_spans(
2194+
core::iter::once(name.span)
2195+
.chain(columns.iter().map(Spanned::span))
2196+
.chain(at.iter().map(|at| at.span)),
2197+
)
21932198
}
21942199
}
21952200

src/parser/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12962,10 +12962,16 @@ impl<'a> Parser<'a> {
1296212962
match self.parse_optional_alias_inner(None, validator)? {
1296312963
Some(name) => {
1296412964
let columns = self.parse_table_alias_column_defs()?;
12965+
let at = if self.dialect.supports_partiql() && self.parse_keyword(Keyword::AT) {
12966+
Some(self.parse_identifier()?)
12967+
} else {
12968+
None
12969+
};
1296512970
Ok(Some(TableAlias {
1296612971
explicit,
1296712972
name,
1296812973
columns,
12974+
at,
1296912975
}))
1297012976
}
1297112977
None => Ok(None),
@@ -14614,6 +14620,7 @@ impl<'a> Parser<'a> {
1461414620
explicit: false,
1461514621
name,
1461614622
columns: vec![],
14623+
at: None,
1461714624
},
1461814625
query,
1461914626
from: None,
@@ -14658,6 +14665,7 @@ impl<'a> Parser<'a> {
1465814665
explicit: false,
1465914666
name,
1466014667
columns,
14668+
at: None,
1466114669
},
1466214670
query,
1466314671
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)