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

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 2ceae006 Preserve optional `AS` keyword in aliases (#2103)
2ceae006 is described below

commit 2ceae006a45b090a76b982f04a413603ab87ab61
Author: xitep <[email protected]>
AuthorDate: Tue Nov 25 16:54:42 2025 +0100

    Preserve optional `AS` keyword in aliases (#2103)
---
 src/ast/query.rs             |  40 ++++++-----
 src/ast/spans.rs             |  26 ++++++--
 src/parser/mod.rs            |  14 +++-
 src/test_utils.rs            |  14 ++--
 tests/sqlparser_bigquery.rs  |  14 ++--
 tests/sqlparser_common.rs    | 116 +++++++++++---------------------
 tests/sqlparser_mssql.rs     |  75 +++++++++------------
 tests/sqlparser_mysql.rs     |  15 +----
 tests/sqlparser_postgres.rs  |   2 +-
 tests/sqlparser_snowflake.rs | 155 +++++++++++++++++--------------------------
 10 files changed, 202 insertions(+), 269 deletions(-)

diff --git a/src/ast/query.rs b/src/ast/query.rs
index 33c92614..16fc9ec0 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -1902,7 +1902,7 @@ impl fmt::Display for TableFactor {
                     write!(f, " {sample}")?;
                 }
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 if !index_hints.is_empty() {
                     write!(f, " {}", display_separated(index_hints, " "))?;
@@ -1932,7 +1932,7 @@ impl fmt::Display for TableFactor {
                 NewLine.fmt(f)?;
                 f.write_str(")")?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -1948,14 +1948,14 @@ impl fmt::Display for TableFactor {
                 write!(f, "{name}")?;
                 write!(f, "({})", display_comma_separated(args))?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
             TableFactor::TableFunction { expr, alias } => {
                 write!(f, "TABLE({expr})")?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -1973,13 +1973,13 @@ impl fmt::Display for TableFactor {
                 }
 
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 if *with_offset {
                     write!(f, " WITH OFFSET")?;
                 }
                 if let Some(alias) = with_offset_alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -1995,7 +1995,7 @@ impl fmt::Display for TableFactor {
                     columns = display_comma_separated(columns)
                 )?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2014,7 +2014,7 @@ impl fmt::Display for TableFactor {
                     write!(f, " WITH ({})", display_comma_separated(columns))?;
                 }
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2024,7 +2024,7 @@ impl fmt::Display for TableFactor {
             } => {
                 write!(f, "({table_with_joins})")?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2051,8 +2051,8 @@ impl fmt::Display for TableFactor {
                     write!(f, " DEFAULT ON NULL ({expr})")?;
                 }
                 write!(f, ")")?;
-                if alias.is_some() {
-                    write!(f, " AS {}", alias.as_ref().unwrap())?;
+                if let Some(alias) = alias {
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2075,8 +2075,8 @@ impl fmt::Display for TableFactor {
                     name,
                     display_comma_separated(columns)
                 )?;
-                if alias.is_some() {
-                    write!(f, " AS {}", alias.as_ref().unwrap())?;
+                if let Some(alias) = alias {
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2109,8 +2109,8 @@ impl fmt::Display for TableFactor {
                 }
                 write!(f, "PATTERN ({pattern}) ")?;
                 write!(f, "DEFINE {})", display_comma_separated(symbols))?;
-                if alias.is_some() {
-                    write!(f, " AS {}", alias.as_ref().unwrap())?;
+                if let Some(alias) = alias {
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2135,7 +2135,7 @@ impl fmt::Display for TableFactor {
                     columns = display_comma_separated(columns)
                 )?;
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
                 Ok(())
             }
@@ -2168,7 +2168,7 @@ impl fmt::Display for TableFactor {
                 write!(f, ")")?;
 
                 if let Some(alias) = alias {
-                    write!(f, " AS {alias}")?;
+                    write!(f, " {alias}")?;
                 }
 
                 Ok(())
@@ -2181,13 +2181,17 @@ impl fmt::Display for TableFactor {
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct TableAlias {
+    /// Tells whether the alias was introduced with an explicit, preceding "AS"
+    /// keyword, e.g. `AS name`. Typically, the keyword is preceding the name
+    /// (e.g. `.. FROM table AS t ..`).
+    pub explicit: bool,
     pub name: Ident,
     pub columns: Vec<TableAliasColumnDef>,
 }
 
 impl fmt::Display for TableAlias {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.name)?;
+        write!(f, "{}{}", if self.explicit { "AS " } else { "" }, self.name)?;
         if !self.columns.is_empty() {
             write!(f, " ({})", display_comma_separated(&self.columns))?;
         }
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index d54290b6..b50cc56d 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -15,10 +15,13 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::ast::{
-    ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, 
AlterSchemaOperation, AlterTable,
-    ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, 
CreateView,
-    ExportData, Owner, TypedString,
+use crate::{
+    ast::{
+        ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, 
AlterSchemaOperation, AlterTable,
+        ColumnOptions, CreateOperator, CreateOperatorClass, 
CreateOperatorFamily, CreateView,
+        ExportData, Owner, TypedString,
+    },
+    tokenizer::TokenWithSpan,
 };
 use core::iter;
 
@@ -96,6 +99,12 @@ pub trait Spanned {
     fn span(&self) -> Span;
 }
 
+impl Spanned for TokenWithSpan {
+    fn span(&self) -> Span {
+        self.span
+    }
+}
+
 impl Spanned for Query {
     fn span(&self) -> Span {
         let Query {
@@ -2079,9 +2088,12 @@ impl Spanned for FunctionArgExpr {
 
 impl Spanned for TableAlias {
     fn span(&self) -> Span {
-        let TableAlias { name, columns } = self;
-
-        union_spans(iter::once(name.span).chain(columns.iter().map(|i| 
i.span())))
+        let TableAlias {
+            explicit: _,
+            name,
+            columns,
+        } = self;
+        
union_spans(core::iter::once(name.span).chain(columns.iter().map(Spanned::span)))
     }
 }
 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1fa7f796..57d94bee 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -11140,10 +11140,15 @@ impl<'a> Parser<'a> {
         fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> 
bool {
             parser.dialect.is_table_factor_alias(explicit, kw, parser)
         }
+        let explicit = self.peek_keyword(Keyword::AS);
         match self.parse_optional_alias_inner(None, validator)? {
             Some(name) => {
                 let columns = self.parse_table_alias_column_defs()?;
-                Ok(Some(TableAlias { name, columns }))
+                Ok(Some(TableAlias {
+                    explicit,
+                    name,
+                    columns,
+                }))
             }
             None => Ok(None),
         }
@@ -12775,6 +12780,7 @@ impl<'a> Parser<'a> {
             let closing_paren_token = self.expect_token(&Token::RParen)?;
 
             let alias = TableAlias {
+                explicit: false,
                 name,
                 columns: vec![],
             };
@@ -12801,7 +12807,11 @@ impl<'a> Parser<'a> {
             let query = self.parse_query()?;
             let closing_paren_token = self.expect_token(&Token::RParen)?;
 
-            let alias = TableAlias { name, columns };
+            let alias = TableAlias {
+                explicit: false,
+                name,
+                columns,
+            };
             Cte {
                 alias,
                 query,
diff --git a/src/test_utils.rs b/src/test_utils.rs
index b6100d49..73d29312 100644
--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -368,8 +368,9 @@ pub fn single_quoted_string(s: impl Into<String>) -> Value {
     Value::SingleQuotedString(s.into())
 }
 
-pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
+pub fn table_alias(explicit: bool, name: impl Into<String>) -> 
Option<TableAlias> {
     Some(TableAlias {
+        explicit,
         name: Ident::new(name),
         columns: vec![],
     })
@@ -405,13 +406,14 @@ pub fn table_from_name(name: ObjectName) -> TableFactor {
     }
 }
 
-pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> 
TableFactor {
+pub fn table_with_alias(
+    name: impl Into<String>,
+    with_as_keyword: bool,
+    alias: impl Into<String>,
+) -> TableFactor {
     TableFactor::Table {
         name: ObjectName::from(vec![Ident::new(name)]),
-        alias: Some(TableAlias {
-            name: Ident::new(alias),
-            columns: vec![],
-        }),
+        alias: table_alias(with_as_keyword, alias),
         args: None,
         with_hints: vec![],
         version: None,
diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs
index 15bf59cd..f2b9f2af 100644
--- a/tests/sqlparser_bigquery.rs
+++ b/tests/sqlparser_bigquery.rs
@@ -1690,7 +1690,7 @@ fn parse_table_identifiers() {
 fn parse_hyphenated_table_identifiers() {
     bigquery().one_statement_parses_to(
         "select * from foo-bar f join baz-qux b on f.id = b.id",
-        "SELECT * FROM foo-bar AS f JOIN baz-qux AS b ON f.id = b.id",
+        "SELECT * FROM foo-bar f JOIN baz-qux b ON f.id = b.id",
     );
 
     assert_eq!(
@@ -1766,7 +1766,7 @@ fn parse_join_constraint_unnest_alias() {
         .joins,
         vec![Join {
             relation: TableFactor::UNNEST {
-                alias: table_alias("f"),
+                alias: table_alias(true, "f"),
                 array_exprs: vec![Expr::CompoundIdentifier(vec![
                     Ident::new("t1"),
                     Ident::new("a")
@@ -1841,10 +1841,7 @@ fn parse_merge() {
             assert_eq!(
                 TableFactor::Table {
                     name: ObjectName::from(vec![Ident::new("inventory")]),
-                    alias: Some(TableAlias {
-                        name: Ident::new("T"),
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "T"),
                     args: Default::default(),
                     with_hints: Default::default(),
                     version: Default::default(),
@@ -1859,10 +1856,7 @@ fn parse_merge() {
             assert_eq!(
                 TableFactor::Table {
                     name: ObjectName::from(vec![Ident::new("newArrivals")]),
-                    alias: Some(TableAlias {
-                        name: Ident::new("S"),
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "S"),
                     args: Default::default(),
                     with_hints: Default::default(),
                     version: Default::default(),
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index ba1e6448..3649e8d3 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -512,10 +512,7 @@ fn parse_update_set_from() {
                         format_clause: None,
                         pipe_operators: vec![],
                     }),
-                    alias: Some(TableAlias {
-                        name: Ident::new("t2"),
-                        columns: vec![],
-                    })
+                    alias: table_alias(true, "t2")
                 },
                 joins: vec![]
             }])),
@@ -558,10 +555,7 @@ fn parse_update_with_table_alias() {
                 TableWithJoins {
                     relation: TableFactor::Table {
                         name: ObjectName::from(vec![Ident::new("users")]),
-                        alias: Some(TableAlias {
-                            name: Ident::new("u"),
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "u"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -627,10 +621,14 @@ fn parse_update_or() {
 
 #[test]
 fn parse_select_with_table_alias_as() {
+    one_statement_parses_to(
+        "SELECT a, b, c FROM lineitem AS l (A, B, C)",
+        "SELECT a, b, c FROM lineitem AS l (A, B, C)",
+    );
     // AS is optional
     one_statement_parses_to(
         "SELECT a, b, c FROM lineitem l (A, B, C)",
-        "SELECT a, b, c FROM lineitem AS l (A, B, C)",
+        "SELECT a, b, c FROM lineitem l (A, B, C)",
     );
 }
 
@@ -651,6 +649,7 @@ fn parse_select_with_table_alias() {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("lineitem")]),
                 alias: Some(TableAlias {
+                    explicit: true,
                     name: Ident::new("l"),
                     columns: vec![
                         TableAliasColumnDef::from_name("A"),
@@ -851,10 +850,7 @@ fn parse_where_delete_with_alias_statement() {
             assert_eq!(
                 TableFactor::Table {
                     name: ObjectName::from(vec![Ident::new("basket")]),
-                    alias: Some(TableAlias {
-                        name: Ident::new("a"),
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "a"),
                     args: None,
                     with_hints: vec![],
                     version: None,
@@ -870,10 +866,7 @@ fn parse_where_delete_with_alias_statement() {
                 Some(vec![TableWithJoins {
                     relation: TableFactor::Table {
                         name: ObjectName::from(vec![Ident::new("basket")]),
-                        alias: Some(TableAlias {
-                            name: Ident::new("b"),
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "b"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -6819,7 +6812,7 @@ fn parse_table_function() {
                 ),
                 expr
             );
-            assert_eq!(alias, table_alias("a"))
+            assert_eq!(alias, table_alias(true, "a"))
         }
         _ => panic!("Expecting TableFactor::TableFunction"),
     }
@@ -6916,10 +6909,7 @@ fn parse_unnest_in_from_clause() {
         &dialects,
         vec![TableWithJoins {
             relation: TableFactor::UNNEST {
-                alias: Some(TableAlias {
-                    name: Ident::new("numbers"),
-                    columns: vec![],
-                }),
+                alias: table_alias(true, "numbers"),
                 array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
                 with_offset: true,
                 with_offset_alias: None,
@@ -6973,10 +6963,7 @@ fn parse_unnest_in_from_clause() {
         &dialects,
         vec![TableWithJoins {
             relation: TableFactor::UNNEST {
-                alias: Some(TableAlias {
-                    name: Ident::new("numbers"),
-                    columns: vec![],
-                }),
+                alias: table_alias(true, "numbers"),
                 array_exprs: vec![Expr::Identifier(Ident::new("expr"))],
                 with_offset: false,
                 with_offset_alias: None,
@@ -7266,14 +7253,14 @@ fn parse_joins_on() {
         only(&verified_only_select("SELECT * FROM t1 JOIN t2 AS foo ON c1 = 
c2").from).joins,
         vec![join_with_constraint(
             "t2",
-            table_alias("foo"),
+            table_alias(true, "foo"),
             false,
             JoinOperator::Join,
         )]
     );
     one_statement_parses_to(
         "SELECT * FROM t1 JOIN t2 foo ON c1 = c2",
-        "SELECT * FROM t1 JOIN t2 AS foo ON c1 = c2",
+        "SELECT * FROM t1 JOIN t2 foo ON c1 = c2",
     );
     // Test parsing of different join operators
     assert_eq!(
@@ -7406,13 +7393,17 @@ fn parse_joins_using() {
         only(&verified_only_select("SELECT * FROM t1 JOIN t2 AS foo 
USING(c1)").from).joins,
         vec![join_with_constraint(
             "t2",
-            table_alias("foo"),
+            table_alias(true, "foo"),
             JoinOperator::Join,
         )]
     );
     one_statement_parses_to(
-        "SELECT * FROM t1 JOIN t2 foo USING(c1)",
         "SELECT * FROM t1 JOIN t2 AS foo USING(c1)",
+        "SELECT * FROM t1 JOIN t2 AS foo USING(c1)",
+    );
+    one_statement_parses_to(
+        "SELECT * FROM t1 JOIN t2 foo USING(c1)",
+        "SELECT * FROM t1 JOIN t2 foo USING(c1)",
     );
     // Test parsing of different join operators
     assert_eq!(
@@ -7536,7 +7527,7 @@ fn parse_natural_join() {
     // natural join another table with alias
     assert_eq!(
         only(&verified_only_select("SELECT * FROM t1 NATURAL JOIN t2 AS 
t3").from).joins,
-        vec![natural_join(JoinOperator::Join, table_alias("t3"))]
+        vec![natural_join(JoinOperator::Join, table_alias(true, "t3"))]
     );
 
     let sql = "SELECT * FROM t1 natural";
@@ -7595,7 +7586,7 @@ fn parse_join_nesting() {
                 relation: table("a"),
                 joins: vec![join(table("b"))],
             }),
-            alias: table_alias("c"),
+            alias: table_alias(true, "c"),
         }
     );
     assert_eq!(from.joins, vec![]);
@@ -7634,6 +7625,7 @@ fn parse_ctes() {
         for (i, exp) in expected.iter().enumerate() {
             let Cte { alias, query, .. } = 
&sel.with.as_ref().unwrap().cte_tables[i];
             assert_eq!(*exp, query.to_string());
+            assert_eq!(false, alias.explicit);
             assert_eq!(
                 if i == 0 {
                     Ident::new("a")
@@ -7711,6 +7703,7 @@ fn parse_recursive_cte() {
     assert_eq!(with.cte_tables.len(), 1);
     let expected = Cte {
         alias: TableAlias {
+            explicit: false,
             name: Ident {
                 value: "nums".to_string(),
                 quote_style: None,
@@ -7783,10 +7776,7 @@ fn parse_derived_tables() {
                 relation: TableFactor::Derived {
                     lateral: false,
                     subquery: Box::new(verified_query("(SELECT 1) UNION 
(SELECT 2)")),
-                    alias: Some(TableAlias {
-                        name: "t1".into(),
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "t1"),
                 },
                 joins: vec![Join {
                     relation: 
table_from_name(ObjectName::from(vec!["t2".into()])),
@@ -9812,10 +9802,7 @@ fn parse_merge() {
                 table,
                 TableFactor::Table {
                     name: ObjectName::from(vec![Ident::new("s"), 
Ident::new("bar")]),
-                    alias: Some(TableAlias {
-                        name: Ident::new("dest"),
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "dest"),
                     args: None,
                     with_hints: vec![],
                     version: None,
@@ -9875,14 +9862,7 @@ fn parse_merge() {
                         format_clause: None,
                         pipe_operators: vec![],
                     }),
-                    alias: Some(TableAlias {
-                        name: Ident {
-                            value: "stg".to_string(),
-                            quote_style: None,
-                            span: Span::empty(),
-                        },
-                        columns: vec![],
-                    }),
+                    alias: table_alias(true, "stg"),
                 }
             );
             assert_eq!(source, source_no_into);
@@ -11078,10 +11058,7 @@ fn parse_pivot_table() {
         Pivot {
             table: Box::new(TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("monthly_sales")]),
-                alias: Some(TableAlias {
-                    name: Ident::new("a"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "a"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -11118,6 +11095,7 @@ fn parse_pivot_table() {
             ]),
             default_on_null: None,
             alias: Some(TableAlias {
+                explicit: true,
                 name: Ident {
                     value: "p".to_string(),
                     quote_style: None,
@@ -11127,7 +11105,7 @@ fn parse_pivot_table() {
                     TableAliasColumnDef::from_name("c"),
                     TableAliasColumnDef::from_name("d"),
                 ],
-            }),
+            })
         }
     );
     assert_eq!(verified_stmt(sql).to_string(), sql);
@@ -11226,10 +11204,7 @@ fn parse_unpivot_table() {
     let base_unpivot = Unpivot {
         table: Box::new(TableFactor::Table {
             name: ObjectName::from(vec![Ident::new("sales")]),
-            alias: Some(TableAlias {
-                name: Ident::new("s"),
-                columns: vec![],
-            }),
+            alias: table_alias(true, "s"),
             args: None,
             with_hints: vec![],
             version: None,
@@ -11250,6 +11225,7 @@ fn parse_unpivot_table() {
             })
             .collect(),
         alias: Some(TableAlias {
+            explicit: true,
             name: Ident::new("u"),
             columns: ["product", "quarter", "quantity"]
                 .into_iter()
@@ -11475,7 +11451,7 @@ fn parse_select_table_with_index_hints() {
     let sql = "SELECT * FROM T USE LIMIT 1";
     let unsupported_dialects = all_dialects_where(|d| 
!d.supports_table_hints());
     let select = unsupported_dialects
-        .verified_only_select_with_canonical(sql, "SELECT * FROM T AS USE 
LIMIT 1");
+        .verified_only_select_with_canonical(sql, "SELECT * FROM T USE LIMIT 
1");
     assert_eq!(
         select.from,
         vec![TableWithJoins {
@@ -11483,10 +11459,7 @@ fn parse_select_table_with_index_hints() {
                 name: 
ObjectName(vec![sqlparser::ast::ObjectNamePart::Identifier(
                     Ident::new("T")
                 )]),
-                alias: Some(TableAlias {
-                    name: Ident::new("USE"),
-                    columns: vec![],
-                }),
+                alias: table_alias(false, "USE"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -11515,10 +11488,7 @@ fn parse_pivot_unpivot_table() {
             table: Box::new(Unpivot {
                 table: Box::new(TableFactor::Table {
                     name: ObjectName::from(vec![Ident::new("census")]),
-                    alias: Some(TableAlias {
-                        name: Ident::new("c"),
-                        columns: vec![]
-                    }),
+                    alias: table_alias(true, "c"),
                     args: None,
                     with_hints: vec![],
                     version: None,
@@ -11538,10 +11508,7 @@ fn parse_pivot_unpivot_table() {
                         alias: None,
                     })
                     .collect(),
-                alias: Some(TableAlias {
-                    name: Ident::new("u"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "u"),
             }),
             aggregate_functions: vec![ExprWithAlias {
                 expr: call("sum", 
[Expr::Identifier(Ident::new("population"))]),
@@ -11565,10 +11532,7 @@ fn parse_pivot_unpivot_table() {
                 },
             ]),
             default_on_null: None,
-            alias: Some(TableAlias {
-                name: Ident::new("p"),
-                columns: vec![]
-            }),
+            alias: table_alias(true, "p"),
         }
     );
     assert_eq!(verified_stmt(sql).to_string(), sql);
@@ -16347,10 +16311,10 @@ fn parse_pipeline_operator() {
         "SELECT * FROM users |> LEFT JOIN orders USING(user_id, order_date)",
     );
 
-    // join pipe operator with alias
+    // join pipe operator with alias (with an omitted "AS" keyword)
     dialects.verified_query_with_canonical(
         "SELECT * FROM users |> JOIN orders o ON users.id = o.user_id",
-        "SELECT * FROM users |> JOIN orders AS o ON users.id = o.user_id",
+        "SELECT * FROM users |> JOIN orders o ON users.id = o.user_id",
     );
     dialects.verified_stmt("SELECT * FROM users |> LEFT JOIN orders AS o ON 
users.id = o.user_id");
 
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index a947db49..99a298f8 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -92,10 +92,31 @@ fn parse_mssql_single_quoted_aliases() {
 
 #[test]
 fn parse_mssql_delimited_identifiers() {
-    let _ = ms().one_statement_parses_to(
+    let s = ms().one_statement_parses_to(
         "SELECT [a.b!] [FROM] FROM foo [WHERE]",
-        "SELECT [a.b!] AS [FROM] FROM foo AS [WHERE]",
+        "SELECT [a.b!] AS [FROM] FROM foo [WHERE]",
     );
+    if let Statement::Query(q) = s {
+        match &q.body.as_select().expect("not a SELECT").from[..] {
+            [from] => match &from.relation {
+                TableFactor::Table { name, alias, .. } => {
+                    assert_eq!(&format!("{name}"), "foo");
+                    assert_eq!(
+                        alias,
+                        &Some(TableAlias {
+                            explicit: false,
+                            name: Ident::with_quote('[', "WHERE"),
+                            columns: vec![]
+                        })
+                    );
+                }
+                _ => panic!("unexpected FROM type"),
+            },
+            _ => panic!("unexpected number of FROMs"),
+        }
+    } else {
+        panic!("statement not parsed as QUERY");
+    }
 }
 
 #[test]
@@ -454,10 +475,7 @@ fn parse_mssql_openjson() {
         vec![TableWithJoins {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("t_test_table")]),
-                alias: Some(TableAlias {
-                    name: Ident::new("A"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "A"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -494,10 +512,7 @@ fn parse_mssql_openjson() {
                             as_json: true
                         }
                     ],
-                    alias: Some(TableAlias {
-                        name: Ident::new("B"),
-                        columns: vec![]
-                    })
+                    alias: table_alias(true, "B")
                 },
                 global: false,
                 join_operator: JoinOperator::CrossApply
@@ -514,10 +529,7 @@ fn parse_mssql_openjson() {
         vec![TableWithJoins {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("t_test_table"),]),
-                alias: Some(TableAlias {
-                    name: Ident::new("A"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "A"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -554,10 +566,7 @@ fn parse_mssql_openjson() {
                             as_json: true
                         }
                     ],
-                    alias: Some(TableAlias {
-                        name: Ident::new("B"),
-                        columns: vec![]
-                    })
+                    alias: table_alias(true, "B")
                 },
                 global: false,
                 join_operator: JoinOperator::CrossApply
@@ -574,10 +583,7 @@ fn parse_mssql_openjson() {
         vec![TableWithJoins {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("t_test_table")]),
-                alias: Some(TableAlias {
-                    name: Ident::new("A"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "A"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -614,10 +620,7 @@ fn parse_mssql_openjson() {
                             as_json: false
                         }
                     ],
-                    alias: Some(TableAlias {
-                        name: Ident::new("B"),
-                        columns: vec![]
-                    })
+                    alias: table_alias(true, "B")
                 },
                 global: false,
                 join_operator: JoinOperator::CrossApply
@@ -634,10 +637,7 @@ fn parse_mssql_openjson() {
         vec![TableWithJoins {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("t_test_table")]),
-                alias: Some(TableAlias {
-                    name: Ident::new("A"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "A"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -654,10 +654,7 @@ fn parse_mssql_openjson() {
                     ),
                     json_path: 
Some(Value::SingleQuotedString("$.config".into())),
                     columns: vec![],
-                    alias: Some(TableAlias {
-                        name: Ident::new("B"),
-                        columns: vec![]
-                    })
+                    alias: table_alias(true, "B")
                 },
                 global: false,
                 join_operator: JoinOperator::CrossApply
@@ -674,10 +671,7 @@ fn parse_mssql_openjson() {
         vec![TableWithJoins {
             relation: TableFactor::Table {
                 name: ObjectName::from(vec![Ident::new("t_test_table")]),
-                alias: Some(TableAlias {
-                    name: Ident::new("A"),
-                    columns: vec![]
-                }),
+                alias: table_alias(true, "A"),
                 args: None,
                 with_hints: vec![],
                 version: None,
@@ -694,10 +688,7 @@ fn parse_mssql_openjson() {
                     ),
                     json_path: None,
                     columns: vec![],
-                    alias: Some(TableAlias {
-                        name: Ident::new("B"),
-                        columns: vec![]
-                    })
+                    alias: table_alias(true, "B")
                 },
                 global: false,
                 join_operator: JoinOperator::CrossApply
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index bc5d48ba..e847d3ed 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -2638,10 +2638,7 @@ fn parse_update_with_joins() {
                 TableWithJoins {
                     relation: TableFactor::Table {
                         name: ObjectName::from(vec![Ident::new("orders")]),
-                        alias: Some(TableAlias {
-                            name: Ident::new("o"),
-                            columns: vec![]
-                        }),
+                        alias: table_alias(true, "o"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -2654,10 +2651,7 @@ fn parse_update_with_joins() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("customers")]),
-                            alias: Some(TableAlias {
-                                name: Ident::new("c"),
-                                columns: vec![]
-                            }),
+                            alias: table_alias(true, "c"),
                             args: None,
                             with_hints: vec![],
                             version: None,
@@ -3716,10 +3710,7 @@ fn parse_json_table() {
                     on_error: Some(JsonTableColumnErrorHandling::Null),
                 }),
             ],
-            alias: Some(TableAlias {
-                name: Ident::new("t"),
-                columns: vec![],
-            }),
+            alias: table_alias(true, "t"),
         }
     );
 }
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index fbfa6658..a14ff5ec 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -5109,7 +5109,7 @@ fn parse_join_constraint_unnest_alias() {
         .joins,
         vec![Join {
             relation: TableFactor::UNNEST {
-                alias: table_alias("f"),
+                alias: table_alias(true, "f"),
                 array_exprs: vec![Expr::CompoundIdentifier(vec![
                     Ident::new("t1"),
                     Ident::new("a")
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index f187af1b..22a63266 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -1197,17 +1197,17 @@ fn test_single_table_in_parenthesis() {
 fn test_single_table_in_parenthesis_with_alias() {
     snowflake_and_generic().one_statement_parses_to(
         "SELECT * FROM (a NATURAL JOIN (b) c )",
-        "SELECT * FROM (a NATURAL JOIN b AS c)",
+        "SELECT * FROM (a NATURAL JOIN b c)",
     );
 
     snowflake_and_generic().one_statement_parses_to(
         "SELECT * FROM (a NATURAL JOIN ((b)) c )",
-        "SELECT * FROM (a NATURAL JOIN b AS c)",
+        "SELECT * FROM (a NATURAL JOIN b c)",
     );
 
     snowflake_and_generic().one_statement_parses_to(
         "SELECT * FROM (a NATURAL JOIN ( (b) c ) )",
-        "SELECT * FROM (a NATURAL JOIN b AS c)",
+        "SELECT * FROM (a NATURAL JOIN b c)",
     );
 
     snowflake_and_generic().one_statement_parses_to(
@@ -1215,9 +1215,13 @@ fn test_single_table_in_parenthesis_with_alias() {
         "SELECT * FROM (a NATURAL JOIN b AS c)",
     );
 
+    snowflake_and_generic().one_statement_parses_to(
+        "SELECT * FROM (a as alias1 NATURAL JOIN ( (b) c ) )",
+        "SELECT * FROM (a AS alias1 NATURAL JOIN b c)",
+    );
     snowflake_and_generic().one_statement_parses_to(
         "SELECT * FROM (a alias1 NATURAL JOIN ( (b) c ) )",
-        "SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)",
+        "SELECT * FROM (a alias1 NATURAL JOIN b c)",
     );
 
     snowflake_and_generic().one_statement_parses_to(
@@ -1226,7 +1230,7 @@ fn test_single_table_in_parenthesis_with_alias() {
     );
 
     snowflake_and_generic().one_statement_parses_to(
-        "SELECT * FROM (a NATURAL JOIN b) c",
+        "SELECT * FROM (a NATURAL JOIN b) AS c",
         "SELECT * FROM (a NATURAL JOIN b) AS c",
     );
 
@@ -3051,9 +3055,9 @@ fn asof_joins() {
     assert_eq!(
         query.from[0],
         TableWithJoins {
-            relation: table_with_alias("trades_unixtime", "tu"),
+            relation: table_with_alias("trades_unixtime", true, "tu"),
             joins: vec![Join {
-                relation: table_with_alias("quotes_unixtime", "qu"),
+                relation: table_with_alias("quotes_unixtime", true, "qu"),
                 global: false,
                 join_operator: JoinOperator::AsOf {
                     match_condition: Expr::BinaryOp {
@@ -3644,10 +3648,37 @@ fn test_sql_keywords_as_table_aliases() {
         "OPEN",
     ];
 
+    fn assert_implicit_alias(mut select: Select, 
canonical_with_explicit_alias: &str) {
+        if let TableFactor::Table { alias, .. } = &mut select
+            .from
+            .get_mut(0)
+            .as_mut()
+            .expect("missing FROM")
+            .relation
+        {
+            let alias = alias.as_mut().expect("missing ALIAS");
+            assert!(!alias.explicit);
+            alias.explicit = true;
+            assert_eq!(&format!("{select}"), canonical_with_explicit_alias);
+        } else {
+            panic!("unexpected FROM <table-factor>");
+        }
+    }
+
+    fn assert_no_alias(select: Select) {
+        if let TableFactor::Table { alias, .. } =
+            &select.from.first().expect("missing FROM").relation
+        {
+            assert_eq!(alias, &None);
+        } else {
+            panic!("unexpected FROM <table-factor>");
+        }
+    }
+
     for kw in unreserved_kws {
         snowflake().verified_stmt(&format!("SELECT * FROM tbl AS {kw}"));
-        snowflake().one_statement_parses_to(
-            &format!("SELECT * FROM tbl {kw}"),
+        assert_implicit_alias(
+            snowflake().verified_only_select(&format!("SELECT * FROM tbl 
{kw}")),
             &format!("SELECT * FROM tbl AS {kw}"),
         );
     }
@@ -3663,13 +3694,17 @@ fn test_sql_keywords_as_table_aliases() {
     }
 
     // LIMIT is alias
-    snowflake().one_statement_parses_to("SELECT * FROM tbl LIMIT", "SELECT * 
FROM tbl AS LIMIT");
+    assert_implicit_alias(
+        snowflake().verified_only_select("SELECT * FROM tbl LIMIT"),
+        "SELECT * FROM tbl AS LIMIT",
+    );
+
     // LIMIT is not an alias
-    snowflake().verified_stmt("SELECT * FROM tbl LIMIT 1");
-    snowflake().verified_stmt("SELECT * FROM tbl LIMIT $1");
-    snowflake().verified_stmt("SELECT * FROM tbl LIMIT ''");
-    snowflake().verified_stmt("SELECT * FROM tbl LIMIT NULL");
-    snowflake().verified_stmt("SELECT * FROM tbl LIMIT $$$$");
+    assert_no_alias(snowflake().verified_only_select("SELECT * FROM tbl LIMIT 
1"));
+    assert_no_alias(snowflake().verified_only_select("SELECT * FROM tbl LIMIT 
$1"));
+    assert_no_alias(snowflake().verified_only_select("SELECT * FROM tbl LIMIT 
''"));
+    assert_no_alias(snowflake().verified_only_select("SELECT * FROM tbl LIMIT 
NULL"));
+    assert_no_alias(snowflake().verified_only_select("SELECT * FROM tbl LIMIT 
$$$$"));
 }
 
 #[test]
@@ -3911,14 +3946,7 @@ fn test_nested_join_without_parentheses() {
                 table_with_joins: Box::new(TableWithJoins {
                     relation: TableFactor::Table {
                         name: 
ObjectName::from(vec![Ident::new("customers".to_string())]),
-                        alias: Some(TableAlias {
-                            name: Ident {
-                                value: "c".to_string(),
-                                quote_style: None,
-                                span: Span::empty(),
-                            },
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "c"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -3931,14 +3959,7 @@ fn test_nested_join_without_parentheses() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("products".to_string())]),
-                            alias: Some(TableAlias {
-                                name: Ident {
-                                    value: "p".to_string(),
-                                    quote_style: None,
-                                    span: Span::empty(),
-                                },
-                                columns: vec![],
-                            }),
+                            alias: table_alias(true, "p"),
                             args: None,
                             with_hints: vec![],
                             version: None,
@@ -3992,14 +4013,7 @@ fn test_nested_join_without_parentheses() {
                 table_with_joins: Box::new(TableWithJoins {
                     relation: TableFactor::Table {
                         name: 
ObjectName::from(vec![Ident::new("customers".to_string())]),
-                        alias: Some(TableAlias {
-                            name: Ident {
-                                value: "c".to_string(),
-                                quote_style: None,
-                                span: Span::empty(),
-                            },
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "c"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -4012,14 +4026,7 @@ fn test_nested_join_without_parentheses() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("products".to_string())]),
-                            alias: Some(TableAlias {
-                                name: Ident {
-                                    value: "p".to_string(),
-                                    quote_style: None,
-                                    span: Span::empty(),
-                                },
-                                columns: vec![],
-                            }),
+                            alias: table_alias(true, "p"),
                             args: None,
                             with_hints: vec![],
                             version: None,
@@ -4073,14 +4080,7 @@ fn test_nested_join_without_parentheses() {
                 table_with_joins: Box::new(TableWithJoins {
                     relation: TableFactor::Table {
                         name: 
ObjectName::from(vec![Ident::new("customers".to_string())]),
-                        alias: Some(TableAlias {
-                            name: Ident {
-                                value: "c".to_string(),
-                                quote_style: None,
-                                span: Span::empty(),
-                            },
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "c"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -4093,14 +4093,7 @@ fn test_nested_join_without_parentheses() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("products".to_string())]),
-                            alias: Some(TableAlias {
-                                name: Ident {
-                                    value: "p".to_string(),
-                                    quote_style: None,
-                                    span: Span::empty(),
-                                },
-                                columns: vec![],
-                            }),
+                            alias: table_alias(true, "p"),
                             args: None,
                             with_hints: vec![],
                             version: None,
@@ -4154,14 +4147,7 @@ fn test_nested_join_without_parentheses() {
                 table_with_joins: Box::new(TableWithJoins {
                     relation: TableFactor::Table {
                         name: 
ObjectName::from(vec![Ident::new("customers".to_string())]),
-                        alias: Some(TableAlias {
-                            name: Ident {
-                                value: "c".to_string(),
-                                quote_style: None,
-                                span: Span::empty(),
-                            },
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "c"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -4174,14 +4160,7 @@ fn test_nested_join_without_parentheses() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("products".to_string())]),
-                            alias: Some(TableAlias {
-                                name: Ident {
-                                    value: "p".to_string(),
-                                    quote_style: None,
-                                    span: Span::empty(),
-                                },
-                                columns: vec![],
-                            }),
+                            alias: table_alias(true, "p"),
                             args: None,
                             with_hints: vec![],
                             version: None,
@@ -4235,14 +4214,7 @@ fn test_nested_join_without_parentheses() {
                 table_with_joins: Box::new(TableWithJoins {
                     relation: TableFactor::Table {
                         name: 
ObjectName::from(vec![Ident::new("customers".to_string())]),
-                        alias: Some(TableAlias {
-                            name: Ident {
-                                value: "c".to_string(),
-                                quote_style: None,
-                                span: Span::empty(),
-                            },
-                            columns: vec![],
-                        }),
+                        alias: table_alias(true, "c"),
                         args: None,
                         with_hints: vec![],
                         version: None,
@@ -4255,14 +4227,7 @@ fn test_nested_join_without_parentheses() {
                     joins: vec![Join {
                         relation: TableFactor::Table {
                             name: 
ObjectName::from(vec![Ident::new("products".to_string())]),
-                            alias: Some(TableAlias {
-                                name: Ident {
-                                    value: "p".to_string(),
-                                    quote_style: None,
-                                    span: Span::empty(),
-                                },
-                                columns: vec![],
-                            }),
+                            alias: table_alias(true, "p"),
                             args: None,
                             with_hints: vec![],
                             version: None,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to