fix(sqla): convert prequery results to native python types (#17195)

This commit is contained in:
Ville Brofeldt 2021-10-22 11:33:44 +02:00 committed by GitHub
parent 35cbcc4643
commit 2ba046f228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 5 deletions

View File

@ -1391,7 +1391,7 @@ class SqlaTable(Model, BaseDatasource): # pylint: disable=too-many-public-metho
if c not in metrics and c in groupby_series_columns
]
top_groups = self._get_top_groups(
result.df, dimensions, groupby_series_columns
result.df, dimensions, groupby_series_columns, columns_by_name
)
qry = qry.where(top_groups)
@ -1436,20 +1436,23 @@ class SqlaTable(Model, BaseDatasource): # pylint: disable=too-many-public-metho
return ob
def _get_top_groups(
self, df: pd.DataFrame, dimensions: List[str], groupby_exprs: Dict[str, Any],
self,
df: pd.DataFrame,
dimensions: List[str],
groupby_exprs: Dict[str, Any],
columns_by_name: Dict[str, TableColumn],
) -> ColumnElement:
column_map = {column.column_name: column for column in self.columns}
groups = []
for _unused, row in df.iterrows():
group = []
for dimension in dimensions:
value = row[dimension]
value = utils.normalize_prequery_result_type(row[dimension])
# Some databases like Druid will return timestamps as strings, but
# do not perform automatic casting when comparing these strings to
# a timestamp. For cases like this we convert the value from a
# string into a timestamp.
if column_map[dimension].is_temporal and isinstance(value, str):
if columns_by_name[dimension].is_temporal and isinstance(value, str):
dttm = dateutil.parser.parse(value)
value = text(self.db_engine_spec.convert_dttm("TIMESTAMP", dttm))

View File

@ -1813,3 +1813,35 @@ def escape_sqla_query_binds(sql: str) -> str:
sql = sql.replace(bind, bind.replace(":", "\\:"))
processed_binds.add(bind)
return sql
def normalize_prequery_result_type(
value: Union[str, int, float, bool, np.generic]
) -> Union[str, int, float, bool]:
"""
Convert a value that is potentially a numpy type into its equivalent Python type.
:param value: primitive datatype in either numpy or python format
:return: equivalent primitive python type
>>> normalize_prequery_result_type('abc')
'abc'
>>> normalize_prequery_result_type(True)
True
>>> normalize_prequery_result_type(123)
123
>>> normalize_prequery_result_type(np.int16(123))
123
>>> normalize_prequery_result_type(np.uint32(123))
123
>>> normalize_prequery_result_type(np.int64(123))
123
>>> normalize_prequery_result_type(123.456)
123.456
>>> normalize_prequery_result_type(np.float32(123.456))
123.45600128173828
>>> normalize_prequery_result_type(np.float64(123.456))
123.456
"""
if isinstance(value, np.generic):
return value.item()
return value