This is an automated email from the ASF dual-hosted git repository.
msyavuz 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 ecc7f726a4e fix: guard potential null derefs and remove dead branches
(#42358)
ecc7f726a4e is described below
commit ecc7f726a4ea2694ed888bcc2429624f3d4c3f1e
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Tue Jul 28 11:43:46 2026 +0300
fix: guard potential null derefs and remove dead branches (#42358)
Co-authored-by: Enzo Martellucci <[email protected]>
---
superset/common/query_context_factory.py | 2 +-
superset/common/utils/query_cache_manager.py | 6 ++--
superset/db_engine_specs/databricks.py | 1 +
superset/db_engine_specs/presto.py | 3 +-
.../common/test_query_context_factory.py | 36 ++++++++++++++++++++++
5 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/superset/common/query_context_factory.py
b/superset/common/query_context_factory.py
index 50807370495..57d5494d8e9 100644
--- a/superset/common/query_context_factory.py
+++ b/superset/common/query_context_factory.py
@@ -318,7 +318,7 @@ class QueryContextFactory: # pylint:
disable=too-few-public-methods
# another temporal filter. A new filter based on the value of
# the granularity will be added later in the code.
# In practice, this is replacing the previous default temporal
filter.
- if is_adhoc_column(filter_to_remove): # type: ignore
+ if filter_to_remove and is_adhoc_column(filter_to_remove): #
type: ignore
filter_to_remove = filter_to_remove.get("sqlExpression")
if filter_to_remove:
diff --git a/superset/common/utils/query_cache_manager.py
b/superset/common/utils/query_cache_manager.py
index 6d7b64041a5..adaf8293b42 100644
--- a/superset/common/utils/query_cache_manager.py
+++ b/superset/common/utils/query_cache_manager.py
@@ -206,11 +206,9 @@ class QueryCacheManager:
)
query_cache.status = QueryStatus.SUCCESS
query_cache.is_loaded = True
- query_cache.is_cached = cache_value is not None
+ query_cache.is_cached = True
query_cache.sql_rowcount = cache_value.get("sql_rowcount",
None)
- query_cache.cache_dttm = (
- cache_value["dttm"] if cache_value is not None else None
- )
+ query_cache.cache_dttm = cache_value["dttm"]
query_cache.queried_dttm = cache_value.get(
"queried_dttm", cache_value.get("dttm")
)
diff --git a/superset/db_engine_specs/databricks.py
b/superset/db_engine_specs/databricks.py
index 255a8c15909..17531c74067 100644
--- a/superset/db_engine_specs/databricks.py
+++ b/superset/db_engine_specs/databricks.py
@@ -534,6 +534,7 @@ class DatabricksDynamicBaseEngineSpec(BasicParametersMixin,
DatabricksBaseEngine
],
) -> list[SupersetError]:
errors: list[SupersetError] = []
+ connect_args: dict[str, Any] = {}
if extra := json.loads(properties.get("extra")): # type: ignore
engine_params = extra.get("engine_params", {})
connect_args = engine_params.get("connect_args", {})
diff --git a/superset/db_engine_specs/presto.py
b/superset/db_engine_specs/presto.py
index 3f100a17134..e667a091b94 100644
--- a/superset/db_engine_specs/presto.py
+++ b/superset/db_engine_specs/presto.py
@@ -1235,6 +1235,7 @@ class PrestoEngineSpec(PrestoBaseEngineSpec):
all_columns: list[ResultSetColumnType] = []
expanded_columns = []
current_array_level = None
+ unnested_rows: dict[int, int] = defaultdict(int)
while to_process:
column, level = to_process.popleft()
if column["column_name"] not in [
@@ -1248,7 +1249,7 @@ class PrestoEngineSpec(PrestoBaseEngineSpec):
# added by the first. every time we change a level in the nested
arrays
# we reinitialize this.
if level != current_array_level:
- unnested_rows: dict[int, int] = defaultdict(int)
+ unnested_rows = defaultdict(int)
current_array_level = level
name = column["column_name"]
diff --git a/tests/unit_tests/common/test_query_context_factory.py
b/tests/unit_tests/common/test_query_context_factory.py
index 3f092c87694..29ad4c6f609 100644
--- a/tests/unit_tests/common/test_query_context_factory.py
+++ b/tests/unit_tests/common/test_query_context_factory.py
@@ -532,6 +532,42 @@ class TestQueryContextFactory:
assert query_object.columns == ["ds", "other_col"]
+ def test_apply_granularity_no_filter_to_remove(self):
+ """No x-axis and no temporal filters leaves the filters untouched."""
+ query_object = Mock(spec=QueryObject)
+ query_object.granularity = "P1D"
+ query_object.columns = ["other_col"]
+ query_object.post_processing = []
+ query_object.filter = [{"col": "other_col", "op": "==", "val":
"value"}]
+
+ datasource = Mock()
+ datasource.columns = [{"column_name": "ds", "is_dttm": True}]
+
+ self.factory._apply_granularity(query_object, {}, datasource)
+
+ assert query_object.filter == [{"col": "other_col", "op": "==", "val":
"value"}]
+
+ def test_apply_granularity_with_adhoc_temporal_filter(self):
+ """An adhoc temporal filter is matched on its SQL expression."""
+ adhoc_column = {"label": "ds_expr", "sqlExpression": "DATE(ds)"}
+ query_object = Mock(spec=QueryObject)
+ query_object.granularity = "P1D"
+ query_object.columns = ["other_col"]
+ query_object.post_processing = []
+ query_object.filter = [
+ {"col": adhoc_column, "op": "TEMPORAL_RANGE", "val": "a : b"},
+ {"col": "DATE(ds)", "op": "TEMPORAL_RANGE", "val": "a : b"},
+ ]
+
+ datasource = Mock()
+ datasource.columns = [{"column_name": "ds", "is_dttm": True}]
+
+ self.factory._apply_granularity(query_object, {}, datasource)
+
+ assert query_object.filter == [
+ {"col": adhoc_column, "op": "TEMPORAL_RANGE", "val": "a : b"}
+ ]
+
def test_apply_filters_with_time_range(self):
"""Test _apply_filters with time_range"""
query_object = Mock(spec=QueryObject)