Repository navigation
Keep open Query Tool connections when the Object Explorer connects their server. #10742 #10743
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dpage
merged 4 commits into
pgadmin-org:master
from
lukbaum:fix/restored-tab-keeps-connection
Nov 7, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
15f49e7
Keep open Query Tool connections when the Object Explorer connects th…
lukbaum 78bcd1d
Merge branch 'master' into fix/restored-tab-keeps-connection
lukbaum 5c0cb40
Improve thread safety in rare cases
lukbaum b27d432
Wrap an overlong line in has_connected_connections() to satisfy pycod…
dpage File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
web/pgadmin/browser/server_groups/servers/tests/test_connect_keeps_tool_connections.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| ########################################################################## | ||
| # | ||
| # pgAdmin 4 - PostgreSQL Tools | ||
| # | ||
| # Copyright (C) 2013 - 2026, The pgAdmin Development Team | ||
| # This software is released under the PostgreSQL Licence | ||
| # | ||
| ########################################################################## | ||
|
|
||
| """ | ||
| Connecting a server from the Object Explorer must not release the | ||
| connections of open Query Tool and View/Edit Data tabs (#10742). | ||
|
|
||
| Restored tabs connect to their database before the Object Explorer | ||
| connects the server, so the maintenance database connection is still | ||
| closed when the tree's connect request arrives. ServerNode.connect() used | ||
| to call ServerManager.update() in that case, which releases every | ||
| connection of the server: each restored tab then answered its status poll | ||
| with "Transaction status check failed.". | ||
| """ | ||
|
|
||
| import unittest | ||
| from types import SimpleNamespace | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| from flask import Flask | ||
|
|
||
| from pgadmin.browser.server_groups.servers import ServerNode | ||
| from pgadmin.utils.driver.psycopg3.server_manager import ServerManager | ||
| from pgadmin.utils.exception import CryptKeyMissing | ||
| from pgadmin.utils.route import BaseTestGenerator | ||
|
|
||
| MODULE = 'pgadmin.browser.server_groups.servers' | ||
|
|
||
|
|
||
| def connection(connected): | ||
| return MagicMock(**{'connected.return_value': connected}) | ||
|
|
||
|
|
||
| class TestConnectKeepsToolConnections(BaseTestGenerator): | ||
| """ServerNode.connect() re-reads the server details with | ||
| ServerManager.update() only while none of the server's connections is | ||
| open, and never for the Query Tool.""" | ||
|
|
||
| scenarios = [ | ||
| ('Nothing connected: update the manager', dict( | ||
| tool_connected=False, is_qt=False, expect_update=True)), | ||
| ('Restored tool tab connected, tree not yet: keep its connection', | ||
| dict(tool_connected=True, is_qt=False, expect_update=False)), | ||
| ('Request from the Query Tool: never update', dict( | ||
| tool_connected=False, is_qt=True, expect_update=False)), | ||
| ] | ||
|
|
||
| def setUp(self): | ||
| # Pure logic test with mocked connections: skip the server setup. | ||
| unittest.TestCase.setUp(self) | ||
|
|
||
| def runTest(self): | ||
| maintenance_conn = connection(False) | ||
|
|
||
| manager = ServerManager.__new__(ServerManager) | ||
| manager.connections = { | ||
| 'DB:postgres': maintenance_conn, | ||
| 'DB:appdb': connection(self.tool_connected), | ||
| 'CONN:4584253': connection(self.tool_connected), | ||
| } | ||
| manager.connection = MagicMock(return_value=maintenance_conn) | ||
| manager.update = MagicMock() | ||
|
|
||
| driver = MagicMock() | ||
| driver.connection_manager.return_value = manager | ||
| server = SimpleNamespace(id=1, username='postgres', service=None, | ||
| shared=False, use_ssh_tunnel=0) | ||
|
|
||
| app = Flask(__name__) | ||
| with app.test_request_context(method='POST', data='{}'), \ | ||
| patch(MODULE + '.get_driver', return_value=driver), \ | ||
| patch(MODULE + '._is_non_owner', return_value=False), \ | ||
| patch(MODULE + '.current_user', SimpleNamespace(id=1)), \ | ||
| patch(MODULE + '.User'), \ | ||
| patch(MODULE + '.get_crypt_key', return_value=(False, None)): | ||
| # A missing crypt key ends the request right after the | ||
| # update decision, before any connection is attempted. | ||
| with self.assertRaises(CryptKeyMissing): | ||
| ServerNode.connect(ServerNode.__new__(ServerNode), 1, 1, | ||
| is_qt=self.is_qt, server=server) | ||
|
|
||
| if self.expect_update: | ||
| manager.update.assert_called_once_with(server) | ||
| else: | ||
| manager.update.assert_not_called() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
web/pgadmin/utils/driver/psycopg3/tests/test_manager_has_live_connection.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| ########################################################################## | ||
| # | ||
| # pgAdmin 4 - PostgreSQL Tools | ||
| # | ||
| # Copyright (C) 2013 - 2026, The pgAdmin Development Team | ||
| # This software is released under the PostgreSQL Licence | ||
| # | ||
| ########################################################################## | ||
|
|
||
| """ | ||
| Unit tests for ServerManager.has_live_connection, which decides whether | ||
| connecting a server from the Object Explorer may re-read the server | ||
| details with ServerManager.update() — that releases every connection, | ||
| including those of open Query Tool and View/Edit Data tabs. | ||
|
|
||
| Run as plain unittest TestCases without a Postgres server connection. | ||
| """ | ||
|
|
||
| import unittest | ||
| from unittest.mock import MagicMock | ||
|
|
||
| from pgadmin.utils.route import BaseTestGenerator | ||
| from pgadmin.utils.driver.psycopg3.server_manager import ServerManager | ||
|
|
||
|
|
||
| def make_manager(**connections): | ||
| """A ServerManager with the given connections (conn_id -> connected), | ||
| built without a Server row.""" | ||
| manager = ServerManager.__new__(ServerManager) | ||
| manager.connections = { | ||
| conn_id: MagicMock(**{'connected.return_value': connected}) | ||
| for conn_id, connected in connections.items() | ||
| } | ||
| return manager | ||
|
|
||
|
|
||
| class _PureUnitTestSetupMixin: | ||
| """setUp here calls unittest.TestCase.setUp directly, skipping | ||
| BaseTestGenerator.setUp's Postgres connection.""" | ||
|
|
||
| def setUp(self): | ||
| unittest.TestCase.setUp(self) | ||
|
|
||
|
|
||
| class TestHasLiveConnectionWithoutConnections( | ||
| _PureUnitTestSetupMixin, BaseTestGenerator): | ||
| """A manager that never connected has no live connection.""" | ||
|
|
||
| scenarios = [('default', dict())] | ||
|
|
||
| def runTest(self): | ||
| self.assertFalse(make_manager().has_live_connection()) | ||
|
|
||
|
|
||
| class TestHasLiveConnectionAllClosed( | ||
| _PureUnitTestSetupMixin, BaseTestGenerator): | ||
| """Connections that are all closed (e.g. after a disconnect) do not | ||
| count.""" | ||
|
|
||
| scenarios = [('default', dict())] | ||
|
|
||
| def runTest(self): | ||
| manager = make_manager(**{'DB:postgres': False, 'CONN:42': False}) | ||
| self.assertFalse(manager.has_live_connection()) | ||
|
|
||
|
|
||
| class TestHasLiveConnectionQueryToolOnly( | ||
| _PureUnitTestSetupMixin, BaseTestGenerator): | ||
| """A View/Edit Data tab restored at login opens its own connection | ||
| and the one to its database, while the Object Explorer's maintenance | ||
| database connection is still closed: the manager has a live | ||
| connection, so update() must not run.""" | ||
|
|
||
| scenarios = [('default', dict())] | ||
|
|
||
| def runTest(self): | ||
| manager = make_manager(**{ | ||
| 'DB:postgres': False, # Object Explorer, not yet connected | ||
| 'DB:appdb': True, # database of the tab | ||
| 'CONN:4584253': True, # the tab's own connection | ||
| }) | ||
| self.assertTrue(manager.has_live_connection()) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.