You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/04/20 13:49:01 UTC

(datafusion-sqlparser-rs) 01/01: Redshift: PartiQL AT (#2303)

This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-2303-19a7468cfe69f03934bc2619a65b4ca2bfcaae9a
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 63e9a339d5e7de67dbe19dcfb313cbb4bcf799a7
Author: Yoav Cohen <59...@users.noreply.github.com>
AuthorDate: Mon Apr 20 15:48:36 2026 +0200

    Redshift: PartiQL AT <index> (#2303)
---
 src/ast/query.rs            |  9 +++++++++
 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 | 25 +++++++++++++++++++++++++
 7 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/src/ast/query.rs b/src/ast/query.rs
index 7ffd6407..4d0774e1 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -2533,6 +2533,12 @@ pub struct TableAlias {
     pub name: Ident,
     /// Optional column aliases declared in parentheses after the table alias.
     pub columns: Vec<TableAliasColumnDef>,
+    /// 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: <https://docs.aws.amazon.com/redshift/latest/dg/query-super.html>
+    pub at: Option<Ident>,
 }
 
 impl fmt::Display for TableAlias {
@@ -2541,6 +2547,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 adc1443f..dc8be4ae 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 7501919a..7be826b2 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 9ba5960e..669ea1ec 100644
--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -374,6 +374,7 @@ pub fn table_alias(explicit: bool, name: impl Into<String>) -> Option<TableAlias
         explicit,
         name: Ident::new(name),
         columns: vec![],
+        at: None,
     })
 }
 
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index aaede9c8..5a15285c 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -669,6 +669,7 @@ fn parse_select_with_table_alias() {
                         TableAliasColumnDef::from_name("B"),
                         TableAliasColumnDef::from_name("C"),
                     ],
+                    at: None,
                 }),
                 args: None,
                 with_hints: vec![],
@@ -7861,6 +7862,7 @@ fn parse_recursive_cte() {
                 span: Span::empty(),
             },
             columns: vec![TableAliasColumnDef::from_name("val")],
+            at: None,
         },
         query: Box::new(cte_query),
         from: None,
@@ -11347,6 +11349,7 @@ fn parse_pivot_table() {
                     TableAliasColumnDef::from_name("c"),
                     TableAliasColumnDef::from_name("d"),
                 ],
+                at: None,
             })
         }
     );
@@ -11485,6 +11488,7 @@ fn parse_unpivot_table() {
                 .into_iter()
                 .map(TableAliasColumnDef::from_name)
                 .collect(),
+            at: None,
         }),
     };
     pretty_assertions::assert_eq!(verified_only_select(sql).from[0].relation, base_unpivot);
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index 555a4cd2..1e053da7 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -106,7 +106,8 @@ fn parse_mssql_delimited_identifiers() {
                         &Some(TableAlias {
                             explicit: false,
                             name: Ident::with_quote('[', "WHERE"),
-                            columns: vec![]
+                            columns: vec![],
+                            at: None,
                         })
                     );
                 }
diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs
index e5dc5114..9609f521 100644
--- a/tests/sqlparser_redshift.rs
+++ b/tests/sqlparser_redshift.rs
@@ -517,3 +517,28 @@ fn test_null_treatment_inside_and_outside_window_function() {
     redshift().verified_stmt("SELECT FIRST_VALUE(1 IGNORE NULLS) OVER () FROM (SELECT 1) t");
     redshift().verified_stmt("SELECT FIRST_VALUE(1) IGNORE NULLS OVER () FROM (SELECT 1) t");
 }
+
+#[test]
+fn test_partiql_from_alias_with_at_index() {
+    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 = dialects.verified_only_select(sql);
+
+    match &select.from[1].relation {
+        TableFactor::Table { name, alias, .. } => {
+            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"),
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org