aminghadersohi commented on code in PR #45348:
URL: https://github.com/apache/superset/pull/45348#discussion_r4291021227


##########
superset/sql/execution/executor.py:
##########
@@ -404,6 +408,118 @@ def execute_sql_with_cursor(
     return results
 
 
+def _decode_async_results_payload(blob: bytes) -> dict[str, Any]:
+    """
+    Decode a results backend blob written by the async SQL execution task.
+
+    The task stores a zlib-compressed payload serialized either as MessagePack
+    or as UTF-8 JSON, depending on ``RESULTS_BACKEND_USE_MSGPACK``. The format
+    is detected from the payload itself (a JSON object starts with ``{``, which
+    is never the first byte of a MessagePack map), so results stay readable if
+    the setting differs between the writing worker and the reading process.
+
+    :param blob: Compressed payload read from the results backend
+    :returns: The deserialized payload
+    """
+    import msgpack
+
+    from superset.utils import json
+
+    decompressed = cast(bytes, utils.zlib_decompress(blob, decode=False))
+    if decompressed.lstrip()[:1] == b"{":
+        return json.loads(decompressed)
+    return msgpack.loads(decompressed, raw=False)
+
+
+def _decode_async_statement_data(stmt_data: dict[str, Any]) -> pd.DataFrame | 
None:
+    """
+    Convert one stored statement's data into a DataFrame.
+
+    SELECT results are Arrow IPC bytes (MessagePack) or Base64 strings (JSON).
+    Older JSON payloads contain records; DML statements store ``None``. A
+    SELECT returning no rows yields an empty DataFrame that keeps its columns,
+    matching the synchronous execution path.
+
+    :param stmt_data: Statement entry from the stored payload
+    :returns: DataFrame of the statement rows, or ``None`` for DML
+    """
+    data = stmt_data.get("data")
+    if data is None:
+        return None
+
+    if isinstance(data, str):
+        data = base64.b64decode(data)
+    if isinstance(data, (bytes, bytearray, memoryview)):
+        from superset.result_set import SupersetResultSet
+
+        table = pa.ipc.open_stream(pa.BufferReader(data)).read_all()
+        df = SupersetResultSet.convert_table_to_df(table)
+        # Older sidecars contain untyped values; do not guess lost types.
+        nested_columns = {
+            **stmt_data.get("nested_columns", {}),
+            
**deserialize_nested_columns(stmt_data.get("nested_columns_msgpack", {})),
+        }
+        for name, values in nested_columns.items():
+            df[name] = values

Review Comment:
   Fixed the remaining malformed-Decimal gap in 
807fa02847106d703d7ef4be5d66e1294e5fca5d: the per-column fallback catches 
DecimalException (including InvalidOperation), keeping the Arrow column and 
independently decoded sibling columns.
   Regression: 
tests/unit_tests/sql/execution/test_async_results.py::test_bad_nested_sidecar_keeps_arrow_and_other_columns[tag4-True]
 and [tag4-False] failed before this commit and pass after it. The expanded 
test covers all seven tag decoders, timedelta unpack/shape/type/overflow 
errors, non-ASCII Decimal bytes, unknown tags and both transports. Focused 
before: 3 failed / 28 passed (including the separate docs regression); final 
two-module run: 408 passed / 2 skipped.
   Search: `rg -n 
'deserialize_nested_columns|_decode_nested_scalar|_decode_async_statement_data|nested_columns_msgpack'
 superset`.
   
   Siblings checked:
   - superset/sql/execution/executor.py:432 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - superset/sql/execution/executor.py:1502 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - superset/sql/execution/serialization.py:72 — not affected: tag 1 int 
raises ValueError, already caught per column.
   - superset/sql/execution/serialization.py:74 — not affected: tag 2 ASCII/ISO 
datetime errors are ValueError subclasses, already caught.
   - superset/sql/execution/serialization.py:76 — not affected: tag 3 UUID gets 
bytes from MessagePack and rejects incorrect lengths with ValueError, already 
caught.
   - superset/sql/execution/serialization.py:78 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - superset/sql/execution/serialization.py:80 — not affected: tag 5 ASCII/ISO 
date errors are ValueError subclasses, already caught.
   - superset/sql/execution/serialization.py:82 — not affected: tag 6 ASCII/ISO 
time errors are ValueError subclasses, already caught.
   - superset/sql/execution/serialization.py:84 — not affected: tag 7 malformed 
MessagePack, unpack shape, timedelta types and overflow raise 
UnpackException/ValueError/TypeError/OverflowError, already caught.
   - superset/sql/execution/serialization.py:118 — not affected: 
Base64/MessagePack format and recursive-container failures use existing caught 
exception families; unknown tags remain rejected by the low-level decoder.
   - superset/sql/execution/celery_task.py:171 — not affected: this is the 
sidecar writer, not a reader.
   - superset/sql/execution/executor.py:1198 — not affected: cache hits rebuild 
original records without decoding sidecars.
   - superset/result_set.py:369 — not affected: synchronous results restore 
original nested values without decoding sidecars.
   



##########
docs/developer_docs/extensions/mcp.md:
##########
@@ -28,6 +28,34 @@ under the License.
 
 Model Context Protocol (MCP) integration allows extensions to register custom 
AI agent capabilities that integrate seamlessly with Superset's MCP service. 
Extensions can provide both **tools** (executable functions) and **prompts** 
(interactive guidance) that AI agents can discover and use.
 
+## Python SQL query results

Review Comment:
   Fixed the remaining section-scope gap in 
807fa02847106d703d7ef4be5d66e1294e5fca5d: the Python SQL query results 
subsection follows the existing Backend APIs endpoint text and complete 
DatasetReferencesAPI example, before Automatic Context Detection.
   Regression: 
tests/unit_tests/sql/execution/test_async_results.py::test_python_sql_results_documented_under_backend_apis
 failed before this commit and passes after it. It explicitly asserts the 
/extensions namespace text and DatasetReferencesAPI example precede, and are 
outside, the SQL-results subsection. Final two-module run: 408 passed / 2 
skipped.
   Search: `rg -n 'Python SQL query results|DatasetReferencesAPI|Extension 
endpoints are registered' docs/developer_docs/extensions`.
   
   Siblings checked:
   - docs/developer_docs/extensions/development.md:209 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - docs/developer_docs/extensions/development.md:223 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - docs/developer_docs/extensions/development.md:254 — fixed in 
807fa02847106d703d7ef4be5d66e1294e5fca5d
   - docs/developer_docs/extensions/development.md:286 — not affected: 
Automatic Context Detection remains a separate level-three section.
   - docs/developer_docs/extensions/mcp.md — not affected: the generic Python 
results section remains absent, asserted by the regression test.
   - superset-core/src/superset_core/queries/types.py — not affected: no Python 
result-handle contract changes in this documentation correction.
   



-- 
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