This commit is contained in:
@@ -452,6 +452,11 @@ def panel(trans_id):
|
||||
params=json.dumps(params))
|
||||
|
||||
|
||||
# Execute options that can be changed from the Query Tool toolbar, and which
|
||||
# are kept on the transaction object.
|
||||
EXECUTE_OPTIONS = ('auto_commit', 'auto_rollback', 'server_cursor')
|
||||
|
||||
|
||||
@blueprint.route(
|
||||
'/initialize/sqleditor/<int:trans_id>/<int:sgid>/<int:sid>/'
|
||||
'<did>',
|
||||
@@ -496,6 +501,13 @@ def initialize_sqleditor(trans_id, sgid, sid, did=None):
|
||||
'role': data['role'] if 'role' in data else None,
|
||||
'password': data['password'] if 'password' in data else None
|
||||
}
|
||||
# The execute options currently chosen on the toolbar, sent when the
|
||||
# transaction is re-created, so they are kept rather than reset to the
|
||||
# preferences. Anything other than a boolean falls back to the
|
||||
# preference in _init_sqleditor.
|
||||
for option in EXECUTE_OPTIONS:
|
||||
if isinstance(data.get(option), bool):
|
||||
kwargs[option] = data[option]
|
||||
|
||||
is_error, errmsg, conn_id, version = _init_sqleditor(
|
||||
trans_id, connect, sgid, sid, did, data['dbname'], **kwargs)
|
||||
@@ -721,9 +733,13 @@ def update_sqleditor_connection(trans_id, sgid, sid, did):
|
||||
'user': data['user'],
|
||||
'role': data['role'] if 'role' in data else None,
|
||||
'password': data['password'] if 'password' in data else None,
|
||||
'auto_commit': getattr(trans_obj, 'auto_commit', None),
|
||||
'auto_rollback': getattr(trans_obj, 'auto_rollback', None),
|
||||
}
|
||||
# Keep the execute options from the old transaction, but only
|
||||
# booleans, as initialize_sqleditor() does; anything else falls
|
||||
# back to the preferences.
|
||||
for option in EXECUTE_OPTIONS:
|
||||
value = getattr(trans_obj, option, None)
|
||||
kwargs[option] = value if isinstance(value, bool) else None
|
||||
|
||||
is_error, errmsg, conn_id, version = _init_sqleditor(
|
||||
new_trans_id, connect, sgid, sid, did, data['database_name'],
|
||||
|
||||
@@ -115,6 +115,21 @@ const FIXED_PREF = {
|
||||
},
|
||||
};
|
||||
|
||||
// The body of the request that initialises the transaction. A Query Tool
|
||||
// sends the execute options currently chosen on the toolbar, so that they
|
||||
// are kept rather than reset to the preferences when the transaction is
|
||||
// re-created; only boolean values are sent, and the server uses the
|
||||
// preference for any that are missing.
|
||||
export function getInitialiseRequestData(params, connParams, toolbarOptions) {
|
||||
if(params.is_query_tool) {
|
||||
return {
|
||||
..._.pickBy(_.pick(toolbarOptions ?? {}, ['auto_commit', 'auto_rollback', 'server_cursor']), _.isBoolean),
|
||||
...connParams,
|
||||
};
|
||||
}
|
||||
return {sql_filter: params.sql_filter, server_cursor: params.server_cursor, ...connParams};
|
||||
}
|
||||
|
||||
export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedNodeInfo, qtPanelDocker, qtPanelId, eventBusObj}) {
|
||||
const containerRef = React.useRef(null);
|
||||
const preferencesStore = usePreferences();
|
||||
@@ -397,9 +412,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
password: password,
|
||||
dbname: selectedConn.database_name
|
||||
};
|
||||
api.post(baseUrl, qtState.params.is_query_tool ?
|
||||
{...requestParams} :
|
||||
{sql_filter: qtState.params.sql_filter, server_cursor: qtState.params.server_cursor, ...requestParams})
|
||||
api.post(baseUrl, getInitialiseRequestData(qtState.params, requestParams, toolbarOptionsRef.current))
|
||||
.then(()=>{
|
||||
setQtStatePartial({
|
||||
connected: true,
|
||||
|
||||
118
web/pgadmin/tools/sqleditor/tests/test_execute_options_reinit.py
Normal file
118
web/pgadmin/tools/sqleditor/tests/test_execute_options_reinit.py
Normal file
@@ -0,0 +1,118 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import json
|
||||
import pickle
|
||||
import secrets
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
|
||||
# The preference defaults, which the freshly created test database uses.
|
||||
PREFERENCE_OPTIONS = {
|
||||
'auto_commit': True,
|
||||
'auto_rollback': False,
|
||||
'server_cursor': False,
|
||||
}
|
||||
CHANGED_OPTIONS = {
|
||||
'auto_commit': False,
|
||||
'auto_rollback': True,
|
||||
'server_cursor': True,
|
||||
}
|
||||
|
||||
|
||||
class TestExecuteOptionsReinit(BaseTestGenerator):
|
||||
"""
|
||||
The execute options chosen on the Query Tool toolbar must be kept when
|
||||
the transaction is re-created, rather than reset to the preferences
|
||||
(issue #10598).
|
||||
"""
|
||||
scenarios = [
|
||||
('Initialise with the toolbar execute options', dict(
|
||||
change_connection=False, init_options=CHANGED_OPTIONS,
|
||||
expected=CHANGED_OPTIONS)),
|
||||
('Initialise with some of the toolbar execute options', dict(
|
||||
change_connection=False,
|
||||
init_options={'server_cursor': True},
|
||||
expected=dict(PREFERENCE_OPTIONS, server_cursor=True))),
|
||||
('Initialise without execute options uses the preferences', dict(
|
||||
change_connection=False, init_options={},
|
||||
expected=PREFERENCE_OPTIONS)),
|
||||
('Initialise ignores execute options that are not booleans', dict(
|
||||
change_connection=False,
|
||||
init_options={'auto_commit': 'false', 'auto_rollback': None,
|
||||
'server_cursor': 1},
|
||||
expected=PREFERENCE_OPTIONS)),
|
||||
('Changing the connection keeps the execute options', dict(
|
||||
change_connection=True, init_options={},
|
||||
toolbar_options=CHANGED_OPTIONS, expected=CHANGED_OPTIONS)),
|
||||
('Changing the connection ignores execute options that are not '
|
||||
'booleans', dict(
|
||||
change_connection=True, init_options={},
|
||||
toolbar_options={'auto_commit': 'false', 'auto_rollback': 1,
|
||||
'server_cursor': 1},
|
||||
expected=PREFERENCE_OPTIONS)),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
database_info = parent_node_dict["database"][-1]
|
||||
self.db_name = database_info["db_name"]
|
||||
self.server_id = database_info["server_id"]
|
||||
self.db_id = database_info["db_id"]
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to the database.")
|
||||
|
||||
self.trans_id = str(secrets.choice(range(1, 9999999)))
|
||||
url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
|
||||
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
|
||||
response = self.tester.post(url, data=json.dumps(dict(
|
||||
self.init_options, dbname=self.db_name)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def runTest(self):
|
||||
if self.change_connection:
|
||||
# Change the options from the toolbar, then change the
|
||||
# connection, which creates a new transaction.
|
||||
for option, value in self.toolbar_options.items():
|
||||
response = self.tester.post(
|
||||
'/sqleditor/{0}/{1}'.format(option, self.trans_id),
|
||||
data=json.dumps(value))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
url = ('/sqleditor/initialize/sqleditor/update_connection/'
|
||||
'{0}/{1}/{2}/{3}').format(self.trans_id,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
response = self.tester.post(url, data=json.dumps({
|
||||
'user': self.server['username'],
|
||||
'role': None,
|
||||
'password': self.server['db_password'],
|
||||
'database_name': self.db_name,
|
||||
}), content_type='html/json')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.trans_id = json.loads(
|
||||
response.data.decode('utf-8'))['data']['trans_id']
|
||||
|
||||
with self.tester.session_transaction() as sess:
|
||||
command_obj = pickle.loads(
|
||||
sess['gridData'][str(self.trans_id)]['command_obj'])
|
||||
|
||||
for option, value in self.expected.items():
|
||||
self.assertIs(getattr(command_obj, option), value, option)
|
||||
|
||||
def tearDown(self):
|
||||
self.tester.delete('/sqleditor/close/{0}'.format(self.trans_id))
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -0,0 +1,68 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
// QueryToolComponent.jsx pulls in editors and viewers that this test never
|
||||
// renders, and whose ESM-only dependencies Jest cannot load.
|
||||
jest.mock('../../../pgadmin/static/js/components/ReactCodeMirror', () => ({
|
||||
__esModule: true,
|
||||
default: ({ value }) => value,
|
||||
}));
|
||||
jest.mock('../../../pgadmin/static/js/components/JsonEditor', () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}));
|
||||
jest.mock('../../../pgadmin/tools/sqleditor/static/js/components/sections/GeometryViewer', () => ({
|
||||
GeometryViewer: () => null,
|
||||
}));
|
||||
jest.mock('../../../pgadmin/tools/sqleditor/static/js/components/sections/GraphVisualiser', () => ({
|
||||
GraphVisualiser: () => null,
|
||||
}));
|
||||
|
||||
import { getInitialiseRequestData } from '../../../pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx';
|
||||
|
||||
const connParams = {
|
||||
user: 'postgres',
|
||||
role: null,
|
||||
password: '',
|
||||
dbname: 'postgres',
|
||||
};
|
||||
|
||||
describe('getInitialiseRequestData', () => {
|
||||
it('sends the toolbar execute options for a Query Tool', () => {
|
||||
expect(getInitialiseRequestData({is_query_tool: true}, connParams, {
|
||||
auto_commit: false,
|
||||
auto_rollback: true,
|
||||
server_cursor: true,
|
||||
explain_verbose: true,
|
||||
open_in_new_tab: false,
|
||||
})).toEqual({
|
||||
...connParams,
|
||||
auto_commit: false,
|
||||
auto_rollback: true,
|
||||
server_cursor: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('leaves out execute options that are not known yet', () => {
|
||||
expect(getInitialiseRequestData({is_query_tool: true}, connParams, null))
|
||||
.toEqual(connParams);
|
||||
expect(getInitialiseRequestData({is_query_tool: true}, connParams, {}))
|
||||
.toEqual(connParams);
|
||||
expect(getInitialiseRequestData({is_query_tool: true}, connParams, {
|
||||
auto_commit: undefined, server_cursor: false,
|
||||
})).toEqual({...connParams, server_cursor: false});
|
||||
});
|
||||
|
||||
it('does not send the toolbar execute options for View/Edit Data', () => {
|
||||
expect(getInitialiseRequestData({
|
||||
is_query_tool: false, sql_filter: 'id > 1', server_cursor: true,
|
||||
}, connParams, {auto_commit: false, auto_rollback: true, server_cursor: false}))
|
||||
.toEqual({...connParams, sql_filter: 'id > 1', server_cursor: true});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user