fitzee commented on code in PR #45389:
URL: https://github.com/apache/superset/pull/45389#discussion_r4321961052


##########
superset/semantic_layers/mapper.py:
##########
@@ -167,7 +168,9 @@ def _dispatch_semantic_query(
     # ``SemanticResult(results=None)`` for zero rows, and a cached ``None``
     # table would break every later transformation of that entry.
     def normalized_dispatcher(dispatched_query: SemanticQuery) -> 
SemanticResult:
-        result: SemanticResult = execute_semantic_query(dispatcher, 
dispatched_query)
+        result: SemanticResult = execute_semantic_query(
+            dispatcher, with_default_order(dispatched_query)

Review Comment:
   **Non-blocking:** this also adds the default to `get_row_count` dispatches 
(`dispatcher` is `semantic_view.get_row_count` when 
`query_object.is_rowcount`). Order can't change a row count, even with a limit, 
because the count of the first N rows is `min(N, total)` either way. So every 
server-paginated table count now carries a sort it doesn't need. A provider 
that builds `SELECT COUNT(*) FROM (<query>)` also ends up with an ORDER BY in a 
derived table, which T-SQL-family engines reject unless there's TOP/OFFSET. I'd 
apply the default only for `get_table`, and narrow the SDK sentence "A provider 
therefore receives an empty `order` only when no temporal dimension is 
selected" to match.



##########
superset/semantic_layers/cache_repository.py:
##########
@@ -344,6 +353,7 @@ def _is_registered(
         )
         return any(
             entry.value_key == value_key
+            and entry.order_key == order_key

Review Comment:
   **Non-blocking (test gap):** this check is what makes the "recomputed once 
on their next request" upgrade claim true, and no test covers it. With this 
line reverted, all 1751 semantic_layers tests still pass. Without it, an 
ordinary miss stores with `replace=False`. That call would see the pre-upgrade 
descriptor (same `value_key`, `order_key=""`) as registered and skip the store. 
`select_reuse` then rejects that descriptor for EXACT/PROJECT, so every request 
for that chart would go to the provider until the old entry's TTL expired. 
Suggested test: register a descriptor with `order_key=""` for an unordered 
temporal query, call `store(..., replace=False)`, and assert that the 
descriptor is replaced and the next `lookup` returns an EXACT hit.



##########
superset/semantic_layers/cache_transform.py:
##########
@@ -197,6 +199,14 @@ def _rollup(frame: pd.DataFrame, query: SemanticQuery) -> 
pd.DataFrame:
     )
 
 
+def _sort_by_default_order(frame: pd.DataFrame, query: SemanticQuery) -> 
pd.DataFrame:
+    """Restore the default order a rollup's grouping replaced, before the 
limit."""
+    dimension: Dimension | None = default_order_dimension(query)
+    if dimension is None:
+        return frame
+    return frame.sort_values(dimension.name, kind="stable", ignore_index=True)

Review Comment:
   **Nit:** `sort_values` puts NULL time values last. BigQuery, MySQL and 
Spark/Databricks sort NULLs first for ASC. On a provider over one of those 
engines, when the limit cuts, a rolled-up hit and a provider miss return 
different rows for the same query. The SDK text leaves NULL placement 
unspecified, so this is allowed, but it cuts against "cache hits follow the 
same order". It's worth either a line in the docstring saying cache rollups 
place NULLs last, or skipping limited rollups when the default column contains 
nulls.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to