geido commented on code in PR #44004:
URL: https://github.com/apache/superset/pull/44004#discussion_r3989537142


##########
superset/mcp_service/chart/tool/get_chart_data.py:
##########
@@ -416,38 +431,66 @@ async def get_chart_data(  # noqa: C901
             chart = find_chart_by_identifier(
                 request.identifier, query_options=chart_query_options
             )
-            if chart is not None:
-                guest_dashboard_id = guest_scope.guest_dashboard_id(chart)
+            if not chart:
+                await ctx.warning(
+                    "Chart not found: identifier=%s" % (request.identifier,)
+                )
+                logger.warning(
+                    "get_chart_data: chart not found: identifier=%s", 
request.identifier
+                )
+                display_id = str(request.identifier)[:200]
+                return ChartError(
+                    error=(
+                        f"No chart found with identifier: {display_id}."
+                        " Use list_charts to get valid chart IDs."
+                    ),
+                    error_type="NotFound",
+                )
 
-        if not chart:
-            await ctx.warning("Chart not found: identifier=%s" % 
(request.identifier,))
-            logger.warning(
-                "get_chart_data: chart not found: identifier=%s", 
request.identifier
-            )
-            display_id = str(request.identifier)[:200]
-            return ChartError(
-                error=(
-                    f"No chart found with identifier: {display_id}."
-                    " Use list_charts to get valid chart IDs."
-                ),
-                error_type="NotFound",
+            # Copy the values this function needs into plain locals while the
+            # instance is freshly loaded and still attached.
+            #
+            # Reading them off the ORM object later is not safe: exiting an
+            # event_logger.log_context() commits the session
+            # (DBEventLogger.log -> db.session.commit), and a commit expires
+            # every loaded attribute. If the instance is also detached before
+            # the next read -- this tool is async and crosses many await
+            # points -- that read raises DetachedInstanceError, which the
+            # broad SQLAlchemyError handler below turns into a confusing
+            # internal-session error instead of chart data. Plain locals are
+            # immune to both expiry and detachment.
+            chart_id = chart.id
+            chart_name = chart.slice_name
+            chart_viz_type = chart.viz_type
+            chart_datasource_id = chart.datasource_id
+            chart_datasource_type = chart.datasource_type
+            chart_params = chart.params
+            chart_query_context = chart.query_context
+            chart_facts = _ChartFacts(
+                chart_id,
+                chart_name,
+                chart_viz_type,
+                chart_datasource_id,
+                chart_datasource_type,
             )
 
+            guest_dashboard_id = guest_scope.guest_dashboard_id(chart)

Review Comment:
   Fixed in a07f05c9ce — confirmed and addressed.
   
   Reproduced it first: with the looked-up `Slice` detached, the guest request 
ends as `InternalError: Instance <Slice at 0x0> is not bound to a Session; 
attribute refresh operation cannot proceed (attribute: id)`, exactly as you 
described. Verified the guard reads all three — `stored_chart.id`, 
`stored_chart.query_context` and `stored_chart.params_dict` 
(`superset/security/manager.py:1626` onward).
   
   Went with the re-fetch rather than a stand-in. The snapshotted scalars 
deliberately can't be used here: `query_context_modified` compares the guest 
payload *against the stored chart*, so substituting values this request already 
carries would make the comparison self-referential. The re-fetch goes back 
through `find_chart_by_identifier`, so the guest's `ChartFilter` still applies, 
and an empty result fails closed with `NotFound` instead of proceeding 
unauthorized.
   
   Regression test added as you suggested — it runs the real 
`guest_scope.authorize_query` after the chart detaches, then asserts the pinned 
`query_context.slice_` is the re-fetched instance and that each of the three 
attributes is readable. It fails without the re-fetch with the error above.
   
   Side effect worth noting: the ORM instance no longer escapes the lookup 
block at all.
   
   ruff-format also fixed — a missing blank line where the rebase merged your 
xlsxwriter test against the new fixture.



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