Part of #1212.
Problem
Closing a table browser tab force-closes the read-only session that other views are using.
GenericTableView.dispose() calls widget.delegate.cancel(interruptIfBusy: true) (lib/features/workspace/generic_table_view.dart:289).
- Despite the name, the delegates never check whether anything is running. They interrupt the
readOnly and the tableWrite slot of the connection and database unconditionally:
- PostgreSQL:
PostgresTableDataDelegate.cancel (lib/features/postgresql/postgres_table_view.dart:238)
- MySQL:
lib/features/mysql/mysql_table_view.dart:237
- SQLite:
lib/features/sqlite/sqlite_table_view.dart:225
interrupt removes the pool entry and calls forceClose() on its connection. That connection is the same object every readOnly caller of this database holds: the tree catalog, stats dashboards, other open table tabs, the Relations view and MCP calls.
Effects:
Scope
dispose() releases the lease and never closes a shared session.
- Track in-flight calls per delegate. Only when this delegate has a statement running, cancel that statement:
- PostgreSQL: a cancel request for the backend (
Connection.cancelPendingStatement() in postgres 3.x) — the socket stays open.
- MySQL:
KILL QUERY <connection id> from a separate short-lived connection, or release the lease and let the statement finish.
- SQLite: release only (statements are local).
- Rename
interruptIfBusy or make it do what it says.
- Check the other callers of
interrupt / interruptAllModes for the same pattern (workspace close, connection switch) and keep force-close only where the whole connection is meant to go away (disconnect, delete).
Acceptance
Part of #1212.
Problem
Closing a table browser tab force-closes the read-only session that other views are using.
GenericTableView.dispose()callswidget.delegate.cancel(interruptIfBusy: true)(lib/features/workspace/generic_table_view.dart:289).readOnlyand thetableWriteslot of the connection and database unconditionally:PostgresTableDataDelegate.cancel(lib/features/postgresql/postgres_table_view.dart:238)lib/features/mysql/mysql_table_view.dart:237lib/features/sqlite/sqlite_table_view.dart:225interruptremoves the pool entry and callsforceClose()on its connection. That connection is the same object everyreadOnlycaller of this database holds: the tree catalog, stats dashboards, other open table tabs, the Relations view and MCP calls.Effects:
Not connected to PostgreSQL/Not connected to MySQL/ a closed-connection error. Closing tab A breaks tab B's page load, the tree expansion, or an MCPrun_queryrunning at that moment.tableWriteis cut as well.Scope
dispose()releases the lease and never closes a shared session.Connection.cancelPendingStatement()inpostgres3.x) — the socket stays open.KILL QUERY <connection id>from a separate short-lived connection, or release the lease and let the statement finish.interruptIfBusyor make it do what it says.interrupt/interruptAllModesfor the same pattern (workspace close, connection switch) and keep force-close only where the whole connection is meant to go away (disconnect, delete).Acceptance
disposeof an idle table delegate does not callinterrupt/forceClose.disposewhile its own statement runs sends a cancel for that statement and leaves the shared connection connected.