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 2025/11/25 15:55:06 UTC
(datafusion-sqlparser-rs) branch gh-readonly-queue/main/pr-2103-2b8e99c6652bb0870a6ae495a22732d860e7d53d created (now 2ceae006)
This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a change to branch gh-readonly-queue/main/pr-2103-2b8e99c6652bb0870a6ae495a22732d860e7d53d
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
at 2ceae006 Preserve optional `AS` keyword in aliases (#2103)
This branch includes the following new commits:
new 2ceae006 Preserve optional `AS` keyword in aliases (#2103)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org
(datafusion-sqlparser-rs) 01/01: Preserve optional `AS` keyword in aliases (#2103)
Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch gh-readonly-queue/main/pr-2103-2b8e99c6652bb0870a6ae495a22732d860e7d53d
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 2ceae006a45b090a76b982f04a413603ab87ab61
Author: xitep <xi...@users.noreply.github.com>
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: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org