You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by lu...@apache.org on 2026/10/02 19:43:46 UTC
(datafusion-sqlparser-rs) branch upstream/postgres-unqualified-wildcard-alias created (now 52826bbe)
This is an automated email from the ASF dual-hosted git repository.
LucaCappelletti94 pushed a change to branch upstream/postgres-unqualified-wildcard-alias
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
at 52826bbe PostgreSQL: reject an alias on an unqualified wildcard
This branch includes the following new commits:
new 52826bbe PostgreSQL: reject an alias on an unqualified wildcard
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: PostgreSQL: reject an alias on an unqualified wildcard
Posted by lu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
LucaCappelletti94 pushed a commit to branch upstream/postgres-unqualified-wildcard-alias
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 52826bbef20c31f6ee9a9ac203dae011db96baac
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Fri Oct 2 21:43:33 2026 +0200
PostgreSQL: reject an alias on an unqualified wildcard
---
src/dialect/mod.rs | 18 +++++++++++++++++-
src/dialect/postgresql.rs | 4 +++-
src/parser/mod.rs | 21 ++++++++++++++-------
tests/sqlparser_common.rs | 18 +++++++++++-------
tests/sqlparser_postgres.rs | 30 ++++++++++++++++++++++++++++++
5 files changed, 75 insertions(+), 16 deletions(-)
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 2beb5655..a81d732f 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1754,17 +1754,33 @@ pub trait Dialect: Debug + Any {
false
}
- /// Returns true if this dialect supports aliasing a wildcard select item.
+ /// Returns true if this dialect supports aliasing a wildcard select item,
+ /// qualified or not.
///
/// Example:
/// ```sql
/// SELECT t.* alias FROM t
/// SELECT t.* AS alias FROM t
+ /// SELECT * alias FROM t
+ /// SELECT * AS alias FROM t
/// ```
fn supports_select_wildcard_with_alias(&self) -> bool {
false
}
+ /// Returns true if this dialect supports aliasing a qualified wildcard
+ /// select item, such as `t.*`, without extending that support to the
+ /// unqualified `*` form.
+ ///
+ /// Example:
+ /// ```sql
+ /// SELECT t.* alias FROM t
+ /// SELECT t.* AS alias FROM t
+ /// ```
+ fn supports_select_qualified_wildcard_with_alias(&self) -> bool {
+ false
+ }
+
/// Returns true if this dialect supports the `OPTIMIZE TABLE` statement.
///
/// Example:
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index 3bec6ceb..2eb89a93 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -345,7 +345,9 @@ impl Dialect for PostgreSqlDialect {
true
}
- fn supports_select_wildcard_with_alias(&self) -> bool {
+ /// PostgreSQL accepts an alias on a qualified wildcard (`t.* alias`) but
+ /// rejects one on a bare `*`.
+ fn supports_select_qualified_wildcard_with_alias(&self) -> bool {
true
}
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1a46c6e5..0f91f0f8 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -19078,9 +19078,9 @@ impl<'a> Parser<'a> {
wildcard_expr: Expr,
) -> Result<FunctionArgExpr, ParserError> {
Ok(match wildcard_expr {
- Expr::Wildcard(ref token) if self.dialect.supports_select_wildcard_exclude() => {
+ Expr::Wildcard(token) if self.dialect.supports_select_wildcard_exclude() => {
// Parse the options the same way SELECT items do.
- let opts = self.parse_wildcard_additional_options(token.0.clone())?;
+ let opts = self.parse_wildcard_additional_options(token.0, false)?;
if opts.opt_exclude.is_some()
|| opts.opt_except.is_some()
|| opts.opt_replace.is_some()
@@ -19089,7 +19089,7 @@ impl<'a> Parser<'a> {
{
FunctionArgExpr::WildcardWithOptions(opts)
} else {
- wildcard_expr.into()
+ Expr::Wildcard(opts.wildcard_token).into()
}
}
other => other.into(),
@@ -19321,10 +19321,10 @@ impl<'a> Parser<'a> {
match self.parse_wildcard_expr()? {
Expr::QualifiedWildcard(prefix, token) => Ok(SelectItem::QualifiedWildcard(
SelectItemQualifiedWildcardKind::ObjectName(prefix),
- self.parse_wildcard_additional_options(token.0)?,
+ self.parse_wildcard_additional_options(token.0, true)?,
)),
Expr::Wildcard(token) => Ok(SelectItem::Wildcard(
- self.parse_wildcard_additional_options(token.0)?,
+ self.parse_wildcard_additional_options(token.0, false)?,
)),
Expr::Identifier(v) if v.value.to_lowercase() == "from" && v.quote_style.is_none() => {
parser_err!(
@@ -19363,7 +19363,7 @@ impl<'a> Parser<'a> {
let wildcard_token = self.get_previous_token().clone();
Ok(SelectItem::QualifiedWildcard(
SelectItemQualifiedWildcardKind::Expr(expr),
- self.parse_wildcard_additional_options(wildcard_token)?,
+ self.parse_wildcard_additional_options(wildcard_token, true)?,
))
}
expr if self.dialect.supports_select_item_multi_column_alias()
@@ -19397,6 +19397,7 @@ impl<'a> Parser<'a> {
pub fn parse_wildcard_additional_options(
&mut self,
wildcard_token: TokenWithSpan,
+ is_qualified_wildcard: bool,
) -> Result<WildcardAdditionalOptions, ParserError> {
let opt_ilike = if self.dialect.supports_select_wildcard_ilike() {
self.parse_optional_select_item_ilike()?
@@ -19425,7 +19426,13 @@ impl<'a> Parser<'a> {
None
};
- let opt_alias = if self.dialect.supports_select_wildcard_with_alias() {
+ let supports_wildcard_alias = if is_qualified_wildcard {
+ self.dialect.supports_select_wildcard_with_alias()
+ || self.dialect.supports_select_qualified_wildcard_with_alias()
+ } else {
+ self.dialect.supports_select_wildcard_with_alias()
+ };
+ let opt_alias = if supports_wildcard_alias {
self.maybe_parse_select_item_alias()?
} else {
None
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 8e08c657..b951927a 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -1320,7 +1320,9 @@ fn parse_select_expr_star() {
#[test]
fn parse_select_wildcard_with_alias() {
- let dialects = all_dialects_where(|d| d.supports_select_wildcard_with_alias());
+ let dialects = all_dialects_where(|d| {
+ d.supports_select_wildcard_with_alias() || d.supports_select_qualified_wildcard_with_alias()
+ });
// qualified wildcard with alias
dialects
@@ -1335,12 +1337,6 @@ fn parse_select_wildcard_with_alias() {
"SELECT t.* AS all_cols, other_col FROM t",
);
- // unqualified wildcard with alias
- dialects
- .parse_sql_statements("SELECT * AS all_cols FROM t")
- .unwrap();
- dialects.one_statement_parses_to("SELECT * all_cols FROM t", "SELECT * AS all_cols FROM t");
-
// mixed: regular column + qualified wildcard with alias
dialects
.parse_sql_statements("SELECT a.id, b.* AS b_cols FROM a JOIN b ON (a.id = b.a_id)")
@@ -1349,6 +1345,14 @@ fn parse_select_wildcard_with_alias() {
"SELECT a.id, b.* b_cols FROM a JOIN b ON (a.id = b.a_id)",
"SELECT a.id, b.* AS b_cols FROM a JOIN b ON (a.id = b.a_id)",
);
+
+ // unqualified wildcard with alias: only dialects granting the broad capability accept it
+ let unqualified_dialects = all_dialects_where(|d| d.supports_select_wildcard_with_alias());
+ unqualified_dialects
+ .parse_sql_statements("SELECT * AS all_cols FROM t")
+ .unwrap();
+ unqualified_dialects
+ .one_statement_parses_to("SELECT * all_cols FROM t", "SELECT * AS all_cols FROM t");
}
#[test]
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index fb998510..a9bbc50c 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -10126,3 +10126,33 @@ fn parse_bitstring_literal_escaping() {
pg_and_generic().verified_stmt("SELECT B''''");
pg_and_generic().verified_stmt("SELECT B'it''s'");
}
+
+#[test]
+fn parse_unqualified_wildcard_alias_rejected() {
+ for (sql, found) in [
+ ("SELECT * x FROM t", "x"),
+ ("SELECT * AS x FROM t", "AS"),
+ ("SELECT * \"x\" FROM t", "\"x\""),
+ ] {
+ assert_eq!(
+ pg().parse_sql_statements(sql).unwrap_err().to_string(),
+ format!("sql parser error: Expected: end of statement, found: {found}")
+ );
+ }
+
+ // nested consumers reject the same bare-wildcard alias
+ assert!(pg()
+ .parse_sql_statements("SELECT a FROM (SELECT * x FROM t) AS y")
+ .is_err());
+ assert!(pg()
+ .parse_sql_statements("WITH c AS (SELECT * x FROM t) SELECT * FROM c")
+ .is_err());
+
+ // controls: qualified wildcard aliases, unaliased wildcards and ordinary expression aliases
+ pg().verified_stmt("SELECT t.* AS x FROM t");
+ pg().one_statement_parses_to("SELECT t.* x FROM t", "SELECT t.* AS x FROM t");
+ pg().verified_stmt("SELECT * FROM t");
+ pg().verified_stmt("SELECT t.* FROM t");
+ pg().verified_stmt("SELECT a AS x FROM t");
+ pg().one_statement_parses_to("SELECT a x FROM t", "SELECT a AS x FROM t");
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org