From c61a507e14491b400c8d5155317960a8671ab3e2 Mon Sep 17 00:00:00 2001 From: "Hugh A. Miles II" Date: Fri, 26 Aug 2022 19:12:21 -0700 Subject: [PATCH] fix: add back custom sql filtering with Query as source (#21190) --- ...etControlValuesCompatibleWithDatasource.ts | 2 ++ superset/config.py | 2 +- superset/db_engine_specs/trino.py | 2 +- superset/models/helpers.py | 31 +++++++++++-------- superset/models/sql_lab.py | 4 +++ 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts b/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts index e070e82464d52..346768557c9dd 100644 --- a/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts +++ b/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts @@ -23,6 +23,7 @@ import { isAdhocMetricSimple, isSavedMetric, isSimpleAdhocFilter, + isFreeFormAdhocFilter, JsonValue, SimpleAdhocFilter, } from '@superset-ui/core'; @@ -70,6 +71,7 @@ const isControlValueCompatibleWithDatasource = ( column.column_name === (value as SimpleAdhocFilter).subject, ); } + if (isFreeFormAdhocFilter(value)) return true; return false; }; diff --git a/superset/config.py b/superset/config.py index bf5106d0e514c..647dc81ea78d6 100644 --- a/superset/config.py +++ b/superset/config.py @@ -211,7 +211,7 @@ def _try_json_readsha(filepath: str, length: int) -> Optional[str]: # # e.g.: # -# class AesGcmEncryptedAdapter( # pylint: disable=too-few-public-methods +# class AesGcmEncryptedAdapter( # AbstractEncryptedFieldAdapter # ): # def create( diff --git a/superset/db_engine_specs/trino.py b/superset/db_engine_specs/trino.py index 2a23d1c969593..9aa89ce34a06f 100644 --- a/superset/db_engine_specs/trino.py +++ b/superset/db_engine_specs/trino.py @@ -35,7 +35,7 @@ from superset.models.core import Database try: - from trino.dbapi import Cursor # pylint: disable=unused-import + from trino.dbapi import Cursor except ImportError: pass diff --git a/superset/models/helpers.py b/superset/models/helpers.py index a476fa0c8106d..bc58cee8c6c59 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -750,6 +750,9 @@ def get_fetch_values_predicate(self) -> List[Any]: def get_extra_cache_keys(query_obj: Dict[str, Any]) -> List[str]: raise NotImplementedError() + def get_template_processor(self, **kwargs: Any) -> BaseTemplateProcessor: + raise NotImplementedError() + def _process_sql_expression( # pylint: disable=no-self-use self, expression: Optional[str], @@ -1291,9 +1294,7 @@ def get_timestamp_expression( column: Dict[str, Any], time_grain: Optional[str], label: Optional[str] = None, - template_processor: Optional[ # pylint: disable=unused-argument - BaseTemplateProcessor - ] = None, + template_processor: Optional[BaseTemplateProcessor] = None, ) -> Union[TimestampExpression, Label]: """ Return a SQLAlchemy Core element representation of self to be used in a query. @@ -1307,6 +1308,11 @@ def get_timestamp_expression( column_spec = self.db_engine_spec.get_column_spec(column.get("type")) type_ = column_spec.sqla_type if column_spec else sa.DateTime col = sa.column(column.get("column_name"), type_=type_) + + if template_processor: + expression = template_processor.process_template(column["column_name"]) + col = sa.literal_column(expression, type_=type_) + time_expr = self.db_engine_spec.get_timestamp_expr(col, None, time_grain) return self.make_sqla_column_compatible(time_expr, label) @@ -1377,7 +1383,7 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma applied_template_filters: List[str] = [] template_kwargs["removed_filters"] = removed_filters template_kwargs["applied_filters"] = applied_template_filters - template_processor = None # self.get_template_processor(**template_kwargs) + template_processor = self.get_template_processor(**template_kwargs) db_engine_spec = self.db_engine_spec prequeries: List[str] = [] orderby = orderby or [] @@ -1487,7 +1493,10 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma table_col = columns_by_name[selected] if isinstance(table_col, dict): outer = self.get_timestamp_expression( - table_col, time_grain, selected, template_processor + column=table_col, + time_grain=time_grain, + label=selected, + template_processor=template_processor, ) else: outer = table_col.get_timestamp_expression( @@ -1550,7 +1559,7 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma if is_timeseries: if isinstance(dttm_col, dict): timestamp = self.get_timestamp_expression( - dttm_col, time_grain, template_processor + dttm_col, time_grain, template_processor=template_processor ) else: timestamp = dttm_col.get_timestamp_expression( @@ -1639,7 +1648,7 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma elif col_obj and filter_grain: if isinstance(col_obj, dict): sqla_col = self.get_timestamp_expression( - col_obj, time_grain, template_processor + col_obj, time_grain, template_processor=template_processor ) else: sqla_col = col_obj.get_timestamp_expression( @@ -1770,9 +1779,7 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma where = extras.get("where") if where: try: - where = template_processor.process_template( # type: ignore - f"({where})" - ) + where = template_processor.process_template(f"{where}") except TemplateError as ex: raise QueryObjectValidationError( _( @@ -1784,9 +1791,7 @@ def get_sqla_query( # pylint: disable=too-many-arguments,too-many-locals,too-ma having = extras.get("having") if having: try: - having = template_processor.process_template( # type: ignore - f"({having})" - ) + having = template_processor.process_template(f"{having}") except TemplateError as ex: raise QueryObjectValidationError( _( diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py index d0e0470d4b1f4..d12af490818d5 100644 --- a/superset/models/sql_lab.py +++ b/superset/models/sql_lab.py @@ -42,6 +42,7 @@ from sqlalchemy.orm import backref, relationship from superset import security_manager +from superset.jinja_context import BaseTemplateProcessor, get_template_processor from superset.models.helpers import ( AuditMixinNullable, ExploreMixin, @@ -126,6 +127,9 @@ class Query( __table_args__ = (sqla.Index("ti_user_id_changed_on", user_id, changed_on),) + def get_template_processor(self, **kwargs: Any) -> BaseTemplateProcessor: + return get_template_processor(query=self, database=self.database, **kwargs) + def to_dict(self) -> Dict[str, Any]: return { "changedOn": self.changed_on,