Compare commits

...

3 Commits

Author SHA1 Message Date
Evan
6814bc8925 test: annotate query_obj with QueryObjectDict instead of casting at call site
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 04:26:05 -07:00
Evan
4e8d0e0abe test: add type hints to new orderby jinja test
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 19:01:01 -07:00
Evan
5da4fe7996 fix(sqla): render Jinja templates in calculated columns used via orderby adhoc metrics
A calculated column's Jinja expression was rendered correctly in SELECT,
WHERE, and GROUP BY, but not when the column was referenced by a SIMPLE
adhoc metric (e.g. MAX(calc_col)) used in ORDER BY, since
`adhoc_metric_to_sqla` wasn't passed the template processor at that call
site in `get_sqla_query`. Add a failing-first integration test that pins
the bug, then fix it by threading `template_processor` through.

Fixes #29378
2026-07-07 17:21:31 -07:00
2 changed files with 53 additions and 0 deletions

View File

@@ -3331,9 +3331,16 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
)
if utils.is_adhoc_metric(col):
# add adhoc sort by column to columns_by_name if not exists
# Pass the template processor so a SIMPLE adhoc metric that
# references a calculated column has that column's Jinja
# expression rendered, matching SELECT/WHERE/GROUP BY. This
# is a no-op for the SQL expression type, whose
# `sqlExpression` was already rendered above; `processed`
# only guards that branch.
col = self.adhoc_metric_to_sqla(
col,
columns_by_name,
template_processor=template_processor,
processed=True,
)
# use the existing instance, if possible

View File

@@ -31,6 +31,7 @@ from superset.db_engine_specs.odps import OdpsBaseEngineSpec, OdpsEngineSpec
from superset.db_engine_specs.sqlite import SqliteEngineSpec
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
from superset.sql.parse import Table
from superset.superset_typing import QueryObjectDict
from superset.utils.database import get_example_database
from tests.integration_tests.base_tests import SupersetTestCase
from tests.integration_tests.test_app import app
@@ -202,6 +203,51 @@ class SupersetTestCases(SupersetTestCase):
in sql
)
@mock.patch("superset.models.core.Database.db_engine_spec", BaseEngineSpec)
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
def test_jinja_calculated_column_in_order_by(self) -> None:
"""
A calculated column referenced by a SIMPLE adhoc metric in `orderby`
should have its Jinja template rendered, the same way it is for
SELECT/WHERE/GROUP BY.
"""
table = self.get_table(name="birth_names")
TableColumn(
column_name="gender_cc_jinja",
type="VARCHAR(255)",
table=table,
expression="""
case
when gender='boy' then {{ "'male'" }}
else {{ "'female'" }}
end
""",
)
table.database.sqlalchemy_uri = "sqlite://"
query_obj: QueryObjectDict = {
"groupby": ["gender_cc_jinja"],
"is_timeseries": False,
"filter": [],
"orderby": [
(
{
"expressionType": "SIMPLE",
"column": {"column_name": "gender_cc_jinja"},
"aggregate": "MAX",
"label": "max_gender_cc_jinja",
},
True,
)
],
}
sql = table.get_query_str(query_obj)
orderby_clause = sql.split("ORDER BY")[1]
assert "{%" not in orderby_clause
assert "{{" not in orderby_clause
assert "'male'" in orderby_clause
assert "'female'" in orderby_clause
def test_time_grain_denylist():
config = app.config.copy()