I3eka commented on code in PR #45561:
URL: https://github.com/apache/superset/pull/45561#discussion_r4336288758


##########
superset/sql/execution/executor.py:
##########
@@ -577,6 +579,7 @@ def execute(
 
         opts: QueryOptionsType = options or QueryOptionsType()
         start_time = time.time()
+        query: Query | None = None

Review Comment:
   This does not reproduce: the module already enables postponed annotations 
[at line 
57](https://github.com/apache/superset/blob/8a9f0ed4f129554e080975ab406219555733eee5/superset/sql/execution/executor.py#L57).
 I checked a fresh import: `Query` is absent from the runtime globals, and the 
`_create_error_result` query annotation is the string `"Query | None"`. The 
import succeeds without a `NameError`. No code change is needed here.



##########
superset/sql/execution/executor.py:
##########
@@ -1166,7 +1204,7 @@ def _create_query_record(
         catalog: str | None,
         schema: str | None,
         status: QueryStatus,
-    ) -> Any:
+    ) -> Query:

Review Comment:
   The return annotation is also covered by the existing [`from __future__ 
import 
annotations`](https://github.com/apache/superset/blob/8a9f0ed4f129554e080975ab406219555733eee5/superset/sql/execution/executor.py#L57).
 A fresh import confirms 
`SQLExecutor._create_query_record.__annotations__["return"] == "Query"`; the 
class is not resolved at import time. Keeping `Query` under `TYPE_CHECKING` is 
valid here.



##########
superset/sql/execution/executor.py:
##########
@@ -1008,14 +1020,40 @@ def _create_error_result(
         :param start_time: Start time for calculating execution duration
         :param partial_results: Optional list of StatementResult from 
successful
             statements before the failure
+        :param query: Persisted audit record, if execution reached that stage
         :returns: QueryResult with error status
         """
-        from superset_core.queries.types import QueryResult as QueryResultType
+        from superset_core.queries.types import (
+            QueryResult as QueryResultType,
+            QueryStatus,
+        )
+
+        from superset.sql.execution.utils import commit_query_unless_stopped
+
+        query_id = None
+        if query is not None:
+            try:
+                # A failed metadata flush must be rolled back before recording 
failure.
+                if not db.session.is_active:
+                    db.session.rollback()  # pylint: 
disable=consider-using-transaction
+                query_id = query.id
+                query.status = status.value
+                query.error_message = error_message
+                query.end_time = now_as_float()

Review Comment:
   Confirmed and fixed in 
[8a9f0ed4f1](https://github.com/apache/superset/pull/45561/commits/8a9f0ed4f129554e080975ab406219555733eee5).
 The handler captures `inspect(query).identity[0]` before metadata recovery. 
Moving `query.id` alone would still fail when that attribute is expired and 
reading it requires the unavailable metadata database.
   
   The regressions cover failed rollback on a real inactive SQLite session and 
failed loading of expired attributes. They verify that the original SQL error 
and persisted ID survive. Four checks failed before the fix; all 18 
failure-handling checks pass afterward. The full SQL execution suite passes 
locally: 772 passed, 3 skipped.



##########
superset/sql/execution/executor.py:
##########
@@ -1008,14 +1020,40 @@ def _create_error_result(
         :param start_time: Start time for calculating execution duration
         :param partial_results: Optional list of StatementResult from 
successful
             statements before the failure
+        :param query: Persisted audit record, if execution reached that stage
         :returns: QueryResult with error status
         """
-        from superset_core.queries.types import QueryResult as QueryResultType
+        from superset_core.queries.types import (
+            QueryResult as QueryResultType,
+            QueryStatus,
+        )
+
+        from superset.sql.execution.utils import commit_query_unless_stopped
+
+        query_id = None
+        if query is not None:
+            try:
+                # A failed metadata flush must be rolled back before recording 
failure.
+                if not db.session.is_active:
+                    db.session.rollback()  # pylint: 
disable=consider-using-transaction
+                query_id = query.id
+                query.status = status.value
+                query.error_message = error_message
+                query.end_time = now_as_float()
+                if not commit_query_unless_stopped(query):
+                    status = QueryStatus.STOPPED
+                    error_message = query.error_message

Review Comment:
   The refresh already happens in 
[`commit_query_unless_stopped`](https://github.com/apache/superset/blob/8a9f0ed4f129554e080975ab406219555733eee5/superset/sql/execution/utils.py#L178-L193):
 when the guarded update loses to Stop, it calls `db.session.refresh(query, 
with_for_update=True)` inside `no_autoflush` before returning `False`. This 
reloads the winning row and discards the pending failure fields.
   
   
[`test_execute_failure_preserves_concurrent_stop`](https://github.com/apache/superset/blob/8a9f0ed4f129554e080975ab406219555733eee5/tests/unit_tests/sql/execution/test_executor.py#L1019-L1103)
 accepts Stop through a separate SQLite session, performs another caller commit 
after failure handling, then verifies status, end time, error message, and the 
cancellation flag through a fresh session. All three cases (SQL error, timeout, 
OAuth error) pass. A second refresh in this caller would duplicate the shared 
helper's work.



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