fix: allow adhoc columns in non-aggregate query (#21729)
This commit is contained in:
parent
f58227a912
commit
d1a6f0ebc4
|
|
@ -119,6 +119,7 @@ from superset.sql_parse import (
|
|||
from superset.superset_typing import (
|
||||
AdhocColumn,
|
||||
AdhocMetric,
|
||||
Column as ColumnTyping,
|
||||
Metric,
|
||||
OrderBy,
|
||||
QueryObjectDict,
|
||||
|
|
@ -1242,7 +1243,7 @@ class SqlaTable(Model, BaseDatasource): # pylint: disable=too-many-public-metho
|
|||
def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
|
||||
self,
|
||||
apply_fetch_values_predicate: bool = False,
|
||||
columns: Optional[List[Column]] = None,
|
||||
columns: Optional[List[ColumnTyping]] = None,
|
||||
extras: Optional[Dict[str, Any]] = None,
|
||||
filter: Optional[ # pylint: disable=redefined-builtin
|
||||
List[QueryObjectFilterClause]
|
||||
|
|
@ -1438,15 +1439,24 @@ class SqlaTable(Model, BaseDatasource): # pylint: disable=too-many-public-metho
|
|||
select_exprs.append(outer)
|
||||
elif columns:
|
||||
for selected in columns:
|
||||
if is_adhoc_column(selected):
|
||||
_sql = selected["sqlExpression"]
|
||||
_column_label = selected["label"]
|
||||
elif isinstance(selected, str):
|
||||
_sql = selected
|
||||
_column_label = selected
|
||||
|
||||
selected = validate_adhoc_subquery(
|
||||
selected,
|
||||
_sql,
|
||||
self.database_id,
|
||||
self.schema,
|
||||
)
|
||||
select_exprs.append(
|
||||
columns_by_name[selected].get_sqla_col()
|
||||
if selected in columns_by_name
|
||||
else self.make_sqla_column_compatible(literal_column(selected))
|
||||
if isinstance(selected, str) and selected in columns_by_name
|
||||
else self.make_sqla_column_compatible(
|
||||
literal_column(selected), _column_label
|
||||
)
|
||||
)
|
||||
metrics_exprs = []
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ class AdhocMetric(TypedDict, total=False):
|
|||
|
||||
class AdhocColumn(TypedDict, total=False):
|
||||
hasCustomLabel: Optional[bool]
|
||||
label: Optional[str]
|
||||
sqlExpression: Optional[str]
|
||||
label: str
|
||||
sqlExpression: str
|
||||
columnType: Optional[Literal["BASE_AXIS", "SERIES"]]
|
||||
timeGrain: Optional[str]
|
||||
|
||||
|
|
|
|||
|
|
@ -1271,7 +1271,9 @@ def is_adhoc_metric(metric: Metric) -> TypeGuard[AdhocMetric]:
|
|||
|
||||
|
||||
def is_adhoc_column(column: Column) -> TypeGuard[AdhocColumn]:
|
||||
return isinstance(column, dict)
|
||||
return isinstance(column, dict) and ({"label", "sqlExpression"}).issubset(
|
||||
column.keys()
|
||||
)
|
||||
|
||||
|
||||
def get_base_axis_labels(columns: Optional[List[Column]]) -> Tuple[str, ...]:
|
||||
|
|
|
|||
|
|
@ -764,6 +764,47 @@ class TestPostChartDataApi(BaseTestChartDataApi):
|
|||
assert "':xyz:qwerty'" in result["query"]
|
||||
assert "':qwerty:'" in result["query"]
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_with_table_columns_without_metrics(self):
|
||||
request_payload = self.query_context_payload
|
||||
request_payload["queries"][0]["columns"] = ["name", "gender"]
|
||||
request_payload["queries"][0]["metrics"] = None
|
||||
request_payload["queries"][0]["orderby"] = []
|
||||
|
||||
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
|
||||
result = rv.json["result"][0]
|
||||
|
||||
assert rv.status_code == 200
|
||||
assert "name" in result["colnames"]
|
||||
assert "gender" in result["colnames"]
|
||||
assert "name" in result["query"]
|
||||
assert "gender" in result["query"]
|
||||
assert list(result["data"][0].keys()) == ["name", "gender"]
|
||||
|
||||
@pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
|
||||
def test_with_adhoc_column_without_metrics(self):
|
||||
request_payload = self.query_context_payload
|
||||
request_payload["queries"][0]["columns"] = [
|
||||
"name",
|
||||
{
|
||||
"label": "num divide by 10",
|
||||
"sqlExpression": "num/10",
|
||||
"expressionType": "SQL",
|
||||
},
|
||||
]
|
||||
request_payload["queries"][0]["metrics"] = None
|
||||
request_payload["queries"][0]["orderby"] = []
|
||||
|
||||
rv = self.post_assert_metric(CHART_DATA_URI, request_payload, "data")
|
||||
result = rv.json["result"][0]
|
||||
|
||||
assert rv.status_code == 200
|
||||
assert "num divide by 10" in result["colnames"]
|
||||
assert "name" in result["colnames"]
|
||||
assert "num divide by 10" in result["query"]
|
||||
assert "name" in result["query"]
|
||||
assert list(result["data"][0].keys()) == ["name", "num divide by 10"]
|
||||
|
||||
|
||||
@pytest.mark.chart_data_flow
|
||||
class TestGetChartDataApi(BaseTestChartDataApi):
|
||||
|
|
|
|||
Loading…
Reference in New Issue