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]