From 1bf1e589300764951aa97031e080ca956e675049 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Wed, 15 Apr 2026 08:41:28 -0400 Subject: [PATCH 1/3] Redshift: PartiQL AT --- src/ast/query.rs | 5 +++++ src/ast/spans.rs | 7 ++++++- src/parser/mod.rs | 8 ++++++++ src/test_utils.rs | 1 + tests/sqlparser_common.rs | 4 ++++ tests/sqlparser_mssql.rs | 3 ++- tests/sqlparser_redshift.rs | 24 ++++++++++++++++++++++++ 7 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 7ffd640797..4bec9e0a5f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2533,6 +2533,8 @@ pub struct TableAlias { pub name: Ident, /// Optional column aliases declared in parentheses after the table alias. pub columns: Vec, + /// Optional PartiQL index alias declared with `AT`. + pub at: Option, } impl fmt::Display for TableAlias { @@ -2541,6 +2543,9 @@ impl fmt::Display for TableAlias { if !self.columns.is_empty() { write!(f, " ({})", display_comma_separated(&self.columns))?; } + if let Some(at) = &self.at { + write!(f, " AT {at}")?; + } Ok(()) } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index adc1443fc7..dc8be4aec1 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -2186,8 +2186,13 @@ impl Spanned for TableAlias { explicit: _, name, columns, + at, } = self; - union_spans(core::iter::once(name.span).chain(columns.iter().map(Spanned::span))) + union_spans( + core::iter::once(name.span) + .chain(columns.iter().map(Spanned::span)) + .chain(at.iter().map(|at| at.span)), + ) } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 7501919a0c..7be826b22d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12822,10 +12822,16 @@ impl<'a> Parser<'a> { match self.parse_optional_alias_inner(None, validator)? { Some(name) => { let columns = self.parse_table_alias_column_defs()?; + let at = if self.dialect.supports_partiql() && self.parse_keyword(Keyword::AT) { + Some(self.parse_identifier()?) + } else { + None + }; Ok(Some(TableAlias { explicit, name, columns, + at, })) } None => Ok(None), @@ -14474,6 +14480,7 @@ impl<'a> Parser<'a> { explicit: false, name, columns: vec![], + at: None, }, query, from: None, @@ -14518,6 +14525,7 @@ impl<'a> Parser<'a> { explicit: false, name, columns, + at: None, }, query, from: None, diff --git a/src/test_utils.rs b/src/test_utils.rs index 9ba5960e83..669ea1ecdb 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -374,6 +374,7 @@ pub fn table_alias(explicit: bool, name: impl Into) -> Option { + assert_eq!( + name, + &ObjectName::from(vec![Ident::new("b"), Ident::new("val")]) + ); + assert_eq!(alias.as_ref().map(|a| &a.name), Some(&Ident::new("val"))); + assert_eq!( + alias.as_ref().and_then(|a| a.at.as_ref()), + Some(&Ident::new("index")) + ); + } + _ => panic!("expected table factor"), + } +} From 684d6fc091e697bb14df6125b7d7d753372c2daa Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Wed, 15 Apr 2026 08:45:45 -0400 Subject: [PATCH 2/3] Redshift: PartiQL AT --- tests/sqlparser_redshift.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 7bf99bfcf2..e75267f099 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -521,7 +521,7 @@ fn test_null_treatment_inside_and_outside_window_function() { #[test] fn test_partiql_from_alias_with_at_index() { redshift().verified_stmt("SELECT * FROM lineitem AS l (a, b, c) AT idx"); - + let sql = "SELECT index, val FROM (SELECT array('AAA', 'BBB') AS val) AS b, b.val AS val AT index"; let select = redshift().verified_only_select(sql); From cca9fc709d03afa5b2b891d4ebd0536e241f48d0 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Mon, 20 Apr 2026 11:23:55 +0200 Subject: [PATCH 3/3] Code review comments --- src/ast/query.rs | 6 +++++- tests/sqlparser_redshift.rs | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 4bec9e0a5f..4d0774e165 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2533,7 +2533,11 @@ pub struct TableAlias { pub name: Ident, /// Optional column aliases declared in parentheses after the table alias. pub columns: Vec, - /// Optional PartiQL index alias declared with `AT`. + /// Optional PartiQL index alias declared with `AT`. For example: + /// ```sql + /// SELECT element, index FROM bar AS b, b.data.scalar_array AS element AT index + /// ``` + /// See: pub at: Option, } diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index e75267f099..9609f52196 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -520,11 +520,12 @@ fn test_null_treatment_inside_and_outside_window_function() { #[test] fn test_partiql_from_alias_with_at_index() { - redshift().verified_stmt("SELECT * FROM lineitem AS l (a, b, c) AT idx"); + let dialects = all_dialects_where(|d| d.supports_partiql()); + dialects.verified_stmt("SELECT * FROM lineitem AS l (a, b, c) AT idx"); let sql = "SELECT index, val FROM (SELECT array('AAA', 'BBB') AS val) AS b, b.val AS val AT index"; - let select = redshift().verified_only_select(sql); + let select = dialects.verified_only_select(sql); match &select.from[1].relation { TableFactor::Table { name, alias, .. } => {