Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions superset/connectors/sqla/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,12 @@ def adhoc_metric_to_sqla(
label = utils.get_metric_name(metric, self.verbose_map)

if expression_type == utils.AdhocMetricExpressionType.SIMPLE:
aggregate: Any = metric.get("aggregate")
if (
not isinstance(aggregate, str)
or aggregate not in self.sqla_aggregations
):
raise QueryObjectValidationError(_("Adhoc metric aggregate is invalid"))
metric_column = metric.get("column") or {}
column_name = cast(str, metric_column.get("column_name"))
table_column: TableColumn | None = columns_by_name.get(column_name)
Expand All @@ -1696,9 +1702,13 @@ def adhoc_metric_to_sqla(
)
else:
sqla_column = column(column_name)
sqla_metric = self.sqla_aggregations[metric["aggregate"]](sqla_column)
sqla_metric = self.sqla_aggregations[aggregate](sqla_column)
elif expression_type == utils.AdhocMetricExpressionType.SQL:
expression = metric.get("sqlExpression")
expression: str | None = metric.get("sqlExpression")
if not isinstance(expression, str) or not expression.strip():
raise QueryObjectValidationError(
_("Adhoc metric SQL expression is invalid")
)

if not processed:
try:
Expand Down
46 changes: 27 additions & 19 deletions superset/models/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
from superset.jinja_context import BaseTemplateProcessor
from superset.sql.parse import sanitize_clause, SQLScript, SQLStatement
from superset.superset_typing import (
AdhocColumn,
AdhocMetric,
Column as ColumnTyping,
FilterValue,
Expand Down Expand Up @@ -2585,16 +2586,26 @@ def adhoc_metric_to_sqla(
label = utils.get_metric_name(metric)

if expression_type == utils.AdhocMetricExpressionType.SIMPLE:
aggregate: Any = metric.get("aggregate")
if (
not isinstance(aggregate, str)
or aggregate not in self.sqla_aggregations
):
raise QueryObjectValidationError(_("Adhoc metric aggregate is invalid"))
metric_column = metric.get("column") or {}
column_name = cast(str, metric_column.get("column_name"))
sqla_column = sa.column(column_name)
sqla_metric = self.sqla_aggregations[metric["aggregate"]](sqla_column)
sqla_metric = self.sqla_aggregations[aggregate](sqla_column)
elif expression_type == utils.AdhocMetricExpressionType.SQL:
expression = metric.get("sqlExpression")
expression: Any = metric.get("sqlExpression")
if not isinstance(expression, str) or not expression.strip():
raise QueryObjectValidationError(
_("Adhoc metric SQL expression is invalid")
)

if not processed:
expression = self._process_select_expression(
expression=metric["sqlExpression"],
expression=expression,
database_id=self.database_id,
engine=self.database.backend,
schema=self.schema,
Expand Down Expand Up @@ -2753,7 +2764,7 @@ def _reapply_query_filters(

def adhoc_column_to_sqla(
self,
col: "AdhocColumn", # type: ignore # noqa: F821
col: AdhocColumn,
force_type_check: bool = False,
template_processor: Optional[BaseTemplateProcessor] = None,
) -> tuple[ColumnElement, Optional[GenericDataType]]:
Expand Down Expand Up @@ -3282,6 +3293,13 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma
# use the key of the ColumnClause for the expected label
metrics_exprs_by_label = {m.key: m for m in metrics_exprs}
metrics_exprs_by_expr = {str(m): m for m in metrics_exprs}
adhoc_columns_by_label: dict[str, AdhocColumn] = {}
for selected in columns:
if not utils.is_adhoc_column(selected):
continue
selected_label = selected.get("label")
if isinstance(selected_label, str) and selected_label:
adhoc_columns_by_label[selected_label] = selected

# Since orderby may use adhoc metrics, too; we need to process them first
orderby_exprs: list[ColumnElement] = []
Expand Down Expand Up @@ -3313,6 +3331,11 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma
elif col in metrics_exprs_by_label:
col = metrics_exprs_by_label[col]
need_groupby = True
elif isinstance(col, str) and col in adhoc_columns_by_label:
col, _unused = self.adhoc_column_to_sqla(
col=adhoc_columns_by_label[col],
template_processor=template_processor,
)
elif col in metrics_by_name:
col = metrics_by_name[col].get_sqla_col(
template_processor=template_processor
Expand All @@ -3322,21 +3345,6 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma
col = self.convert_tbl_column_to_sqla_col(
columns_by_name[col], template_processor=template_processor
)
elif isinstance(col, str) and columns:
# Check if this is a label reference to an adhoc column
adhoc_col = next(
(
c
for c in columns
if utils.is_adhoc_column(c) and c.get("label") == col
),
None,
)
if adhoc_col:
col, _unused = self.adhoc_column_to_sqla(
col=adhoc_col,
template_processor=template_processor,
)

if isinstance(col, ColumnElement):
orderby_exprs.append(col)
Expand Down
Loading
Loading