This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new c41942a38ad chore(deps): Upgrade sqlglot from 27.15.2 to 28.10.0
(#37841)
c41942a38ad is described below
commit c41942a38ad1a4139526340042a31a6df8f67bef
Author: Michael S. Molina <[email protected]>
AuthorDate: Tue Feb 10 13:13:11 2026 -0300
chore(deps): Upgrade sqlglot from 27.15.2 to 28.10.0 (#37841)
---
pyproject.toml | 2 +-
requirements/base.txt | 2 +-
requirements/development.txt | 2 +-
superset-core/pyproject.toml | 2 +-
superset/sql/dialects/pinot.py | 2 ++
superset/sql/parse.py | 10 +++++-----
tests/unit_tests/sql/parse_tests.py | 17 +++++++++++++++++
7 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index b9100a9b2c5..77abbf8b977 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -100,7 +100,7 @@ dependencies = [
"slack_sdk>=3.19.0, <4",
"sqlalchemy>=1.4, <2",
"sqlalchemy-utils>=0.38.3, <0.39",
- "sqlglot>=27.15.2, <28",
+ "sqlglot>=28.10.0, <29",
# newer pandas needs 0.9+
"tabulate>=0.9.0, <1.0",
"typing-extensions>=4, <5",
diff --git a/requirements/base.txt b/requirements/base.txt
index 445c3ae8812..e96797714a0 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -404,7 +404,7 @@ sqlalchemy-utils==0.38.3
# apache-superset (pyproject.toml)
# apache-superset-core
# flask-appbuilder
-sqlglot==27.15.2
+sqlglot==28.10.0
# via
# apache-superset (pyproject.toml)
# apache-superset-core
diff --git a/requirements/development.txt b/requirements/development.txt
index 82bb3e76232..7bb5c766343 100644
--- a/requirements/development.txt
+++ b/requirements/development.txt
@@ -996,7 +996,7 @@ sqlalchemy-utils==0.38.3
# apache-superset
# apache-superset-core
# flask-appbuilder
-sqlglot==27.15.2
+sqlglot==28.10.0
# via
# -c requirements/base-constraint.txt
# apache-superset
diff --git a/superset-core/pyproject.toml b/superset-core/pyproject.toml
index 80b57b49b1c..ff8a1400eb7 100644
--- a/superset-core/pyproject.toml
+++ b/superset-core/pyproject.toml
@@ -46,7 +46,7 @@ dependencies = [
"pydantic>=2.8.0",
"sqlalchemy>=1.4.0,<2.0",
"sqlalchemy-utils>=0.38.0",
- "sqlglot>=27.15.2, <28",
+ "sqlglot>=28.10.0, <29",
"typing-extensions>=4.0.0",
]
diff --git a/superset/sql/dialects/pinot.py b/superset/sql/dialects/pinot.py
index e0aca308171..6344fee4a0d 100644
--- a/superset/sql/dialects/pinot.py
+++ b/superset/sql/dialects/pinot.py
@@ -144,6 +144,8 @@ class Pinot(MySQL):
e.args.get("expression"),
e.args.get("variant"),
),
+ # Preserve Pinot's YEAROFWEEK (sqlglot normalizes to YEAR_OF_WEEK)
+ exp.YearOfWeek: lambda self, e: self.func("YEAROFWEEK", e.this),
}
# Remove DATE_TRUNC transformation - Pinot supports standard SQL
DATE_TRUNC
TRANSFORMS.pop(exp.DateTrunc, None)
diff --git a/superset/sql/parse.py b/superset/sql/parse.py
index 843962eb091..fdbc524b149 100644
--- a/superset/sql/parse.py
+++ b/superset/sql/parse.py
@@ -271,7 +271,7 @@ class RLSAsSubqueryTransformer(RLSTransformer):
this=exp.Select(
expressions=[exp.Star()],
where=exp.Where(this=predicate),
- **{"from": exp.From(this=node.copy())},
+ from_=exp.From(this=node.copy()),
),
alias=alias,
)
@@ -816,7 +816,7 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
limit=exp.Limit(
expression=exp.Literal(this=str(limit), is_string=False)
),
- **{"from":
exp.From(this=exp.Subquery(this=self._parsed.copy()))},
+ from_=exp.From(this=exp.Subquery(this=self._parsed.copy())),
)
else: # method == LimitMethod.FETCH_MANY
pass
@@ -827,7 +827,7 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
:return: True if the statement has a CTE at the top level.
"""
- return "with" in self._parsed.args
+ return bool(self._parsed.args.get("with_"))
def as_cte(self, alias: str = "__cte") -> SQLStatement:
"""
@@ -840,8 +840,8 @@ class SQLStatement(BaseSQLStatement[exp.Expression]):
:param alias: The alias to use for the CTE.
:return: A new SQLStatement with the CTE.
"""
- existing_ctes = self._parsed.args["with"].expressions if
self.has_cte() else []
- self._parsed.args["with"] = None
+ existing_ctes = self._parsed.args["with_"].expressions if
self.has_cte() else []
+ self._parsed.args["with_"] = None
new_cte = exp.CTE(
this=self._parsed.copy(),
alias=exp.TableAlias(this=exp.Identifier(this=alias)),
diff --git a/tests/unit_tests/sql/parse_tests.py
b/tests/unit_tests/sql/parse_tests.py
index b50718173cb..57bab2b9589 100644
--- a/tests/unit_tests/sql/parse_tests.py
+++ b/tests/unit_tests/sql/parse_tests.py
@@ -1867,6 +1867,23 @@ def test_as_cte(sql: str, engine: str, expected: str) ->
None:
assert SQLStatement(sql, engine).as_cte().format() == expected
+def test_as_cte_called_twice() -> None:
+ """
+ Test that calling as_cte() multiple times on the same instance works.
+
+ Regression test for a bug where as_cte() sets self._parsed.args["with_"] =
None
+ after extracting CTEs, but has_cte() only checked if the key existed, not
if
+ the value was truthy. This caused an AttributeError on subsequent as_cte()
calls.
+ """
+ sql = "WITH cte AS (SELECT 1) SELECT * FROM cte"
+ stmt = SQLStatement(sql, "postgresql")
+
+ assert stmt.has_cte() is True
+ stmt.as_cte()
+ assert stmt.has_cte() is False
+ stmt.as_cte()
+
+
@pytest.mark.parametrize(
"sql, rules, expected",
[