This is an automated email from the ASF dual-hosted git repository.
hugh 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 6e5036d87f fix: add timegrains to data payload (#20938)
6e5036d87f is described below
commit 6e5036d87fcb444eaf01d7a8a1f274426597a69f
Author: Hugh A. Miles II <[email protected]>
AuthorDate: Thu Aug 4 13:26:49 2022 -0400
fix: add timegrains to data payload (#20938)
* add timegrains to data payload
* fix
* opps
* save
* integrate type casting for engiines
* add perm object
* change how wwe raise_for_access
* fix orderby on column types
* linting
---
superset/common/query_context_processor.py | 7 ++-
superset/models/helpers.py | 80 +++++++++++++++++++++++++++++-
superset/models/sql_lab.py | 18 +++++++
3 files changed, 103 insertions(+), 2 deletions(-)
diff --git a/superset/common/query_context_processor.py
b/superset/common/query_context_processor.py
index 3b174dc7a2..9d7f8305e5 100644
--- a/superset/common/query_context_processor.py
+++ b/superset/common/query_context_processor.py
@@ -46,6 +46,7 @@ from superset.models.helpers import QueryResult
from superset.utils import csv
from superset.utils.cache import generate_cache_key, set_and_log_cache
from superset.utils.core import (
+ DatasourceType,
DTTM_ALIAS,
error_msg_from_exception,
get_column_names_from_columns,
@@ -512,4 +513,8 @@ class QueryContextProcessor:
"""
for query in self._query_context.queries:
query.validate()
- security_manager.raise_for_access(query_context=self._query_context)
+
+ if self._qc_datasource.type == DatasourceType.QUERY:
+ security_manager.raise_for_access(query=self._qc_datasource)
+ else:
+
security_manager.raise_for_access(query_context=self._query_context)
diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index d549158e66..81dc53cfae 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -1201,6 +1201,47 @@ class ExploreMixin: # pylint:
disable=too-many-public-methods
return or_(*groups)
+ def dttm_sql_literal(self, dttm: sa.DateTime, col_type: Optional[str]) ->
str:
+ """Convert datetime object to a SQL expression string"""
+
+ sql = (
+ self.db_engine_spec.convert_dttm(col_type, dttm, db_extra=None)
+ if col_type
+ else None
+ )
+
+ if sql:
+ return sql
+
+ return f'{dttm.strftime("%Y-%m-%d %H:%M:%S.%f")}'
+
+ def get_time_filter(
+ self,
+ time_col: Dict[str, Any],
+ start_dttm: sa.DateTime,
+ end_dttm: sa.DateTime,
+ ) -> ColumnElement:
+ label = "__time"
+ col = time_col.get("column_name")
+ sqla_col = literal_column(col)
+ my_col = self.make_sqla_column_compatible(sqla_col, label)
+ l = []
+ if start_dttm:
+ l.append(
+ my_col
+ >= self.db_engine_spec.get_text_clause(
+ self.dttm_sql_literal(start_dttm, time_col.get("type"))
+ )
+ )
+ if end_dttm:
+ l.append(
+ my_col
+ < self.db_engine_spec.get_text_clause(
+ self.dttm_sql_literal(end_dttm, time_col.get("type"))
+ )
+ )
+ return and_(*l)
+
def values_for_column(self, column_name: str, limit: int = 10000) ->
List[Any]:
"""Runs query against sqla to retrieve some
sample values for the given column.
@@ -1257,6 +1298,12 @@ class ExploreMixin: # pylint:
disable=too-many-public-methods
time_expr = self.db_engine_spec.get_timestamp_expr(col, None,
time_grain)
return self.make_sqla_column_compatible(time_expr, label)
+ def get_sqla_col(self, col: Dict[str, Any]) -> Column:
+ label = col.get("column_name")
+ col_type = col.get("type")
+ col = sa.column(label, type_=col_type)
+ return self.make_sqla_column_compatible(col, label)
+
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,
@@ -1393,7 +1440,11 @@ class ExploreMixin: # pylint:
disable=too-many-public-methods
col = metrics_exprs_by_expr.get(str(col), col)
need_groupby = True
elif col in columns_by_name:
- col = columns_by_name[col].get_sqla_col()
+ gb_column_obj = columns_by_name[col]
+ if isinstance(gb_column_obj, dict):
+ col = self.get_sqla_col(gb_column_obj)
+ else:
+ col = gb_column_obj.get_sqla_col()
elif col in metrics_exprs_by_label:
col = metrics_exprs_by_label[col]
need_groupby = True
@@ -1490,6 +1541,33 @@ class ExploreMixin: # pylint:
disable=too-many-public-methods
select_exprs.insert(0, timestamp)
groupby_all_columns[timestamp.name] = timestamp
+ # Use main dttm column to support index with secondary dttm
columns.
+ if (
+ db_engine_spec.time_secondary_columns
+ and self.main_dttm_col in self.dttm_cols
+ and self.main_dttm_col != dttm_col.column_name
+ ):
+ if isinstance(self.main_dttm_col, dict):
+ time_filters.append(
+ self.get_time_filter(
+ self.main_dttm_col,
+ from_dttm,
+ to_dttm,
+ )
+ )
+ else:
+ time_filters.append(
+ columns_by_name[self.main_dttm_col].get_time_filter(
+ from_dttm,
+ to_dttm,
+ )
+ )
+
+ if isinstance(dttm_col, dict):
+ time_filters.append(self.get_time_filter(dttm_col, from_dttm,
to_dttm))
+ else:
+ time_filters.append(dttm_col.get_time_filter(from_dttm,
to_dttm))
+
# Always remove duplicates by column name, as sometimes `metrics_exprs`
# can have the same name as a groupby column (e.g. when users use
# raw columns as custom SQL adhoc metric).
diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py
index 4449b3dfa1..d0e0470d4b 100644
--- a/superset/models/sql_lab.py
+++ b/superset/models/sql_lab.py
@@ -218,7 +218,20 @@ class Query(
@property
def data(self) -> Dict[str, Any]:
+ order_by_choices = []
+ for col in self.columns:
+ column_name = str(col.get("column_name") or "")
+ order_by_choices.append(
+ (json.dumps([column_name, True]), column_name + " [asc]")
+ )
+ order_by_choices.append(
+ (json.dumps([column_name, False]), column_name + " [desc]")
+ )
+
return {
+ "time_grain_sqla": [
+ (g.duration, g.name) for g in self.database.grains() or []
+ ],
"filter_select": True,
"name": self.tab_name,
"columns": self.columns,
@@ -228,6 +241,7 @@ class Query(
"sql": self.sql,
"owners": self.owners_data,
"database": {"id": self.database_id, "backend":
self.database.backend},
+ "order_by_choices": order_by_choices,
}
def raise_for_access(self) -> None:
@@ -282,6 +296,10 @@ class Query(
def schema_perm(self) -> str:
return f"{self.database.database_name}.{self.schema}"
+ @property
+ def perm(self) -> str:
+ return
f"[{self.database.database_name}].[{self.tab_name}](id:{self.id})"
+
@property
def default_endpoint(self) -> str:
return ""