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


##########
superset/sql/execution/executor.py:
##########
@@ -404,6 +406,147 @@ 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 stored as Arrow IPC bytes (MessagePack payloads) or as
+    a list of records (JSON payloads); 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
+    """
+    import pyarrow as pa
+
+    data = stmt_data.get("data")
+    if data is None:
+        return None
+
+    if isinstance(data, (bytes, bytearray, memoryview)):
+        from superset.result_set import SupersetResultSet
+
+        table = pa.ipc.open_stream(pa.BufferReader(data)).read_all()
+        return SupersetResultSet.convert_table_to_df(table)

Review Comment:
   My earlier reply overstated the nested-type guarantee. Fixed the 
oversized-integer crash and supported nested scalar type loss in 
23be919b260402b6a56d554e2aea0cb682a882fc. A typed Base64 MessagePack sidecar 
keeps arbitrary-sized integers, datetime (naive or datetime.timezone 
fixed-offset), UUID, Decimal, and bytes out of the outer transport's lossy 
serializer. Both backend modes retain these values and Python types.
   
   Remaining: unsupported nested types use Arrow's string representation for 
the entire affected column, rather than synchronous Python objects. This 
includes sets, tuples, scalar subclasses, and datetimes using timezone 
implementations such as zoneinfo.ZoneInfo. Existing untyped sidecars remain 
readable but cannot recover types already lost. The dead arrow_schema removal 
in the legacy test helper is also gone.
   
   Regression tests added by this PR in 
`tests/unit_tests/sql/execution/test_async_results.py`:
   - `test_async_nested_oversized_integer_matches_sync`: before, both 
MessagePack cases raised TypeError; after, all 4 cases pass, including negative 
oversized integers and reader-setting changes.
   - `test_async_nested_scalar_types_match_sync`: before, datetime/UUID/Decimal 
failed in both transports and bytes failed in JSON; after, all 14 cases pass 
with explicit value/type comparisons against synchronous execution.
   - `test_async_unsupported_nested_types_use_arrow_strings`: all 8 cases 
failed before and pass after, testing the documented whole-column string 
fallback for sets, tuples, scalar subclasses, and named-timezone datetimes.
   - `test_legacy_nested_oversized_integer_matches_sync`: failed before with 
OverflowError; passes after.
   
   I ran `.venv/bin/pytest tests/unit_tests/sql/execution/test_async_results.py 
-q -k 'nested_oversized_integer or nested_scalar_types or 
unsupported_nested_types or legacy_nested_sidecar' -m 'not manual and not 
llm_compliance'` with implementation files restored to 
`98a05a717c1ed67250f39b1ab7a0b0cbcf320a9d`: **23 failed, 5 passed**; with this 
commit: **28 passed**. The five previously passing cases are compatibility 
controls, not claimed reproductions. `.venv/bin/pytest tests/unit_tests/sql/ -q 
-m 'not manual and not llm_compliance'`: **2455 passed**. `pre-commit run` and 
`pre-commit run --files <all branch-changed paths>` passed.
   
   Siblings checked:
   - superset/sql/execution/celery_task.py:193 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/serialization.py:72 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/serialization.py:95 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/executor.py:434 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/executor.py:473 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/celery_task.py:122 — not affected: the outer 
serializer receives Base64 sidecars, not raw nested scalars.
   - superset/sql/execution/celery_task.py:308 — not affected: statement Arrow 
IPC serialization is unchanged; original nested objects travel in the typed 
sidecar.
   - superset/sql/execution/executor.py:411 — not affected: outer transport 
detection only decodes the payload map; the statement reader decodes the 
sidecar.
   - superset/sql/execution/executor.py:1185 — not affected: cache records with 
object dtype use pandas Series directly, not the async results serializer.
   - superset/sql/execution/executor.py:1238 — not affected: cache writes 
retain Python records and dtype metadata, with no results-backend 
JSON/MessagePack conversion.
   - superset/result_set.py:368 — not affected: synchronous execution restores 
original nested values directly, providing the tests' expected results.
   - superset/sql_lab.py:392 — not affected: the separate SQL Lab writer uses 
Arrow bytes for MessagePack and JSON records for JSON; it does not write this 
nested sidecar.
   - superset/views/utils.py:650 — not affected: the separate SQL Lab reader 
decodes its top-level data contract, not SQLExecutor statement sidecars.
   - superset/commands/sql_lab/results.py:170 — not affected: calls the 
separate SQL Lab reader.
   - superset/commands/sql_lab/export.py:121 — not affected: calls the separate 
SQL Lab reader for CSV export.
   - superset/sqllab/utils.py:76 — not affected: writes the existing Arrow 
table, whose nested values are already strings.
   - superset/connectors/sqla/utils.py:224 — not affected: expands column 
metadata, not stored result values.
   - superset/db_engine_specs/base.py:2788 — not affected: expands column 
metadata, not stored result values.
   - superset/db_engine_specs/bigquery.py:1125 — not affected: expands STRUCT 
column metadata, not stored result values.
   - superset/mcp_service/server.py:654 — not affected: serializes MCP 
tool-search results, not SQLExecutor result payloads.
   - superset/mcp_service/caching.py:30 — not affected: versions MCP tool cache 
keys, not SQLExecutor result serialization.
   



##########
docs/admin_docs/configuration/async-queries-celery.mdx:
##########
@@ -76,6 +76,14 @@ For performance gains, 
[MessagePack](https://github.com/msgpack/msgpack-python)
 disabled by setting `RESULTS_BACKEND_USE_MSGPACK = False` in your 
`superset_config.py`, should any
 issues arise. Please clear your existing results cache store when upgrading an 
existing environment.
 
+The Python `Database.execute_async()` result handle preserves date, time, 
timestamp, interval,
+large integer, decimal, and binary column types with either serialization 
format. JSON
+results include Arrow schema metadata; binary columns use Base64 encoding in 
storage.
+Older JSON results without schema metadata still load but retain their 
serialized strings;
+rerun those queries to obtain typed columns. Data cache hits also retain 
timestamp units
+and timezones.
+This does not change the JSON representation returned by the SQL Lab results 
API.

Review Comment:
   My earlier reply and docs wording overstated what was restored. Corrected in 
23be919b260402b6a56d554e2aea0cb682a882fc: 
`docs/developer_docs/extensions/mcp.md` explicitly lists the supported nested 
types and the fallback. Nested JSON scalars (including arbitrary-sized 
integers), datetime (naive or datetime.timezone fixed-offset, including 
microseconds), UUID, Decimal, and bytes retain their value and type in both 
backend modes. The integer crash and nested scalar loss have regression 
coverage.
   
   Remaining: other nested types, including sets, tuples, scalar subclasses, 
and named-timezone datetimes, use the Arrow string representation for the whole 
affected column rather than matching synchronous Python objects. Old JSON 
records and untyped sidecars cannot recover types already converted to 
strings/floats; rerunning the query is required. The docs state these limits 
and remain in the developer extension documentation.
   
   Regression tests added by this PR in 
`tests/unit_tests/sql/execution/test_async_results.py`:
   - `test_async_nested_oversized_integer_matches_sync`: before, both 
MessagePack cases raised TypeError; after, all 4 cases pass, including negative 
oversized integers and reader-setting changes.
   - `test_async_nested_scalar_types_match_sync`: before, datetime/UUID/Decimal 
failed in both transports and bytes failed in JSON; after, all 14 cases pass 
with explicit value/type comparisons against synchronous execution.
   - `test_async_unsupported_nested_types_use_arrow_strings`: all 8 cases 
failed before and pass after, testing the documented whole-column string 
fallback for sets, tuples, scalar subclasses, and named-timezone datetimes.
   - `test_legacy_nested_oversized_integer_matches_sync`: failed before with 
OverflowError; passes after.
   
   I ran `.venv/bin/pytest tests/unit_tests/sql/execution/test_async_results.py 
-q -k 'nested_oversized_integer or nested_scalar_types or 
unsupported_nested_types or legacy_nested_sidecar' -m 'not manual and not 
llm_compliance'` with implementation files restored to 
`98a05a717c1ed67250f39b1ab7a0b0cbcf320a9d`: **23 failed, 5 passed**; with this 
commit: **28 passed**. The five previously passing cases are compatibility 
controls, not claimed reproductions. `.venv/bin/pytest tests/unit_tests/sql/ -q 
-m 'not manual and not llm_compliance'`: **2455 passed**. `pre-commit run` and 
`pre-commit run --files <all branch-changed paths>` passed.
   
   Siblings checked:
   - docs/developer_docs/extensions/mcp.md:31 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - docs/admin_docs/configuration/async-queries-celery.mdx — not affected: the 
Python result-handle paragraph is absent from the admin deployment guide; `rg 
-n 'Python SQL query results|Nested JSON values|Arrow IPC' 
docs/admin_docs/configuration/async-queries-celery.mdx` returned no matches.
   - superset/sql/execution/celery_task.py:193 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/serialization.py:72 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/serialization.py:95 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/executor.py:434 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/executor.py:473 — fixed in 
23be919b260402b6a56d554e2aea0cb682a882fc
   - superset/sql/execution/celery_task.py:122 — not affected: the outer 
serializer receives Base64 sidecars, not raw nested scalars.
   - superset/sql/execution/celery_task.py:308 — not affected: statement Arrow 
IPC serialization is unchanged; original nested objects travel in the typed 
sidecar.
   - superset/sql/execution/executor.py:411 — not affected: outer transport 
detection only decodes the payload map; the statement reader decodes the 
sidecar.
   - superset/sql/execution/executor.py:1185 — not affected: cache records with 
object dtype use pandas Series directly, not the async results serializer.
   - superset/sql/execution/executor.py:1238 — not affected: cache writes 
retain Python records and dtype metadata, with no results-backend 
JSON/MessagePack conversion.
   - superset/result_set.py:368 — not affected: synchronous execution restores 
original nested values directly, providing the tests' expected results.
   - superset/sql_lab.py:392 — not affected: the separate SQL Lab writer uses 
Arrow bytes for MessagePack and JSON records for JSON; it does not write this 
nested sidecar.
   - superset/views/utils.py:650 — not affected: the separate SQL Lab reader 
decodes its top-level data contract, not SQLExecutor statement sidecars.
   - superset/commands/sql_lab/results.py:170 — not affected: calls the 
separate SQL Lab reader.
   - superset/commands/sql_lab/export.py:121 — not affected: calls the separate 
SQL Lab reader for CSV export.
   - superset/sqllab/utils.py:76 — not affected: writes the existing Arrow 
table, whose nested values are already strings.
   - superset/connectors/sqla/utils.py:224 — not affected: expands column 
metadata, not stored result values.
   - superset/db_engine_specs/base.py:2788 — not affected: expands column 
metadata, not stored result values.
   - superset/db_engine_specs/bigquery.py:1125 — not affected: expands STRUCT 
column metadata, not stored result values.
   - superset/mcp_service/server.py:654 — not affected: serializes MCP 
tool-search results, not SQLExecutor result payloads.
   - superset/mcp_service/caching.py:30 — not affected: versions MCP tool cache 
keys, not SQLExecutor result serialization.
   



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