Fixed the issue of renaming the database by another user. Fixes #4203

This commit is contained in:
Rahul Shirsat 2021-05-31 12:41:09 +05:30 committed by Akshay Joshi
parent a487a51135
commit 658a2de619
16 changed files with 645 additions and 478 deletions

View File

@ -20,3 +20,4 @@ Housekeeping
Bug fixes
*********
| `Issue #4203 <https://redmine.postgresql.org/issues/4203>`_ - Fixed the issue of renaming the database by another user.

View File

@ -18,19 +18,9 @@ let _browserPanel = null;
// Default Tool Bar Buttons.
let _defaultToolBarButtons = [
{
label: gettext('Query Tool'),
ariaLabel: gettext('Query Tool'),
btnClass: 'pg-font-icon icon-query_tool',
text: '',
toggled: false,
toggleClass: '',
parentClass: 'pg-toolbar-btn btn-primary-icon',
enabled: false,
},
{
label: gettext('View Data'),
ariaLabel: gettext('View Data'),
btnClass: 'pg-font-icon sql-icon-lg icon-view_data',
label: gettext('Search objects'),
ariaLabel: gettext('Search objects'),
btnClass: 'fa fa-search',
text: '',
toggled: false,
toggleClass: '',
@ -48,9 +38,19 @@ let _defaultToolBarButtons = [
enabled: false,
},
{
label: gettext('Search objects'),
ariaLabel: gettext('Search objects'),
btnClass: 'fa fa-search',
label: gettext('View Data'),
ariaLabel: gettext('View Data'),
btnClass: 'pg-font-icon sql-icon-lg icon-view_data',
text: '',
toggled: false,
toggleClass: '',
parentClass: 'pg-toolbar-btn btn-primary-icon',
enabled: false,
},
{
label: gettext('Query Tool'),
ariaLabel: gettext('Query Tool'),
btnClass: 'pg-font-icon icon-query_tool',
text: '',
toggled: false,
toggleClass: '',
@ -60,7 +60,7 @@ let _defaultToolBarButtons = [
];
if(pgAdmin['enable_psql']) {
_defaultToolBarButtons.push({
_defaultToolBarButtons.unshift({
label: gettext('PSQL Tool'),
ariaLabel: gettext('PSQL Tool'),
btnClass: 'fas fa-terminal',

View File

@ -50,6 +50,8 @@ export function callRenderAfterPoll(sqlEditor, alertify, res) {
sqlEditor.disable_tool_buttons(false);
}
sqlEditor.trigger('pgadmin-sqleditor:check_synchronous_db_name_change', res);
sqlEditor.setIsQueryRunning(false);
sqlEditor.trigger('pgadmin-sqleditor:loading-icon:hide');
}
}

View File

@ -64,7 +64,7 @@
display: flex;
}
.wcFloating .wcFrameButtonBar {
.wcFrameButtonBar {
flex-direction: row-reverse;
}

View File

@ -9,6 +9,10 @@
import {getTreeNodeHierarchyFromIdentifier} from '../../../../static/js/tree/pgadmin_tree_node';
import gettext from 'sources/gettext';
import Alertify from 'pgadmin.alertifyjs';
import pgWindow from 'sources/window';
const pgAdmin = pgWindow.pgAdmin;
export function getDatabaseLabel(parentData) {
return parentData.database ? parentData.database.label
@ -108,3 +112,19 @@ export function generateTitle(title_placeholder, title_data) {
return _.escape(title_placeholder);
}
/*
* This function is used refresh the db node after showing alert to the user
*/
export function refresh_db_node(message, dbNode) {
Alertify.alert()
.setting({
'title': gettext('Database moved/renamed'),
'label':gettext('OK'),
'message': gettext(message),
'onok': function(){
//Set the original db name as soon as user clicks ok button
pgAdmin.Browser.Nodes.database.callbacks.refresh(undefined, dbNode);
},
}).show();
}

View File

@ -13,6 +13,7 @@ import {getDatabaseLabel, generateTitle} from './datagrid_panel_title';
import CodeMirror from 'bundled_codemirror';
import * as SqlEditorUtils from 'sources/sqleditor_utils';
import $ from 'jquery';
import _ from 'underscore';
export function showDataGrid(
datagrid,
@ -289,7 +290,7 @@ function hasSchemaOrCatalogOrViewInformation(parentData) {
parentData.catalog !== undefined;
}
export function generateDatagridTitle(pgBrowser, aciTreeIdentifier, custom_title=null) {
export function generateDatagridTitle(pgBrowser, aciTreeIdentifier, custom_title=null, backend_entity=null) {
//const baseTitle = getPanelTitle(pgBrowser, aciTreeIdentifier);
var preferences = pgBrowser.get_preferences_for_module('browser');
const parentData = getTreeNodeHierarchyFromIdentifier.call(
@ -298,7 +299,7 @@ export function generateDatagridTitle(pgBrowser, aciTreeIdentifier, custom_title
);
const namespaceName = retrieveNameSpaceName(parentData);
const db_label = getDatabaseLabel(parentData);
const db_label = !_.isUndefined(backend_entity) && backend_entity != null && backend_entity.hasOwnProperty('db_name') ? backend_entity['db_name'] : getDatabaseLabel(parentData);
const node = pgBrowser.treeMenu.findNodeByDomElement(aciTreeIdentifier);
var dtg_title_placeholder = '';

View File

@ -836,6 +836,7 @@ def initialize_target(debug_type, trans_id, sid, did,
conn_id = str(random.randint(1, 9999999))
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection(did=did, conn_id=conn_id)
data_obj = {}
# Connect the Server
status, msg = conn.connect()
@ -903,9 +904,11 @@ def initialize_target(debug_type, trans_id, sid, did,
}
de_inst.update_session()
data_obj['db_name'] = conn.db
return make_json_response(data={'status': status,
'debuggerTransId': trans_id})
'debuggerTransId': trans_id,
'data_obj': data_obj})
@blueprint.route(

View File

@ -13,11 +13,12 @@ define([
'backbone', 'pgadmin.backgrid', 'codemirror', 'pgadmin.backform',
'pgadmin.tools.debugger.ui', 'pgadmin.tools.debugger.utils',
'tools/datagrid/static/js/show_query_tool', 'sources/utils',
'pgadmin.authenticate.kerberos', 'wcdocker', 'pgadmin.browser.frame',
'pgadmin.authenticate.kerberos', 'tools/datagrid/static/js/datagrid_panel_title',
'wcdocker', 'pgadmin.browser.frame',
], function(
gettext, url_for, $, _, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
CodeMirror, Backform, get_function_arguments, debuggerUtils, showQueryTool,
pgadminUtils, Kerberos
pgadminUtils, Kerberos, panelTitleFunc
) {
var pgTools = pgAdmin.Tools = pgAdmin.Tools || {},
wcDocker = window.wcDocker;
@ -333,10 +334,15 @@ define([
//Callback function when user start the indirect debugging ( Listen to another session to invoke the target )
start_global_debugger: function(args, item, trans_id) {
// Initialize the target and create asynchronous connection and unique transaction ID
var self = this;
var t = pgBrowser.tree,
i = item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type];
node = d && pgBrowser.Nodes[d._type],
tree_data = pgBrowser.treeMenu.translateTreeNodeIdFromACITree(i),
db_data = pgBrowser.treeMenu.findNode(tree_data.slice(0,4)),
dbNode = db_data.domNode;
if (!d)
return;
@ -432,9 +438,17 @@ define([
),
panel = pgBrowser.docker.addPanel(
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
);
),
db_label = treeInfo.database.label;
if(res.data.data_obj.db_name != treeInfo.database.label) {
db_label = res.data.data_obj.db_name;
var message = `Current database has been moved or renamed to ${db_label}. Click on the OK button to refresh the database name.`;
panelTitleFunc.refresh_db_node(message, dbNode);
}
var label = treeInfo.function ? treeInfo.function.label : treeInfo.trigger_function ? treeInfo.trigger_function.label : treeInfo.trigger ? treeInfo.trigger.label : treeInfo.procedure.label;
debuggerUtils.setDebuggerTitle(panel, browser_preferences, label, treeInfo.schema.label, treeInfo.database.label, null, pgBrowser);
debuggerUtils.setDebuggerTitle(panel, browser_preferences, label, db_label, db_label, null, pgBrowser);
panel.focus();
@ -498,10 +512,14 @@ define([
in the user input dialog
*/
get_function_information: function(args, item) {
var t = pgBrowser.tree,
i = item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type];
node = d && pgBrowser.Nodes[d._type],
tree_data = pgBrowser.treeMenu.translateTreeNodeIdFromACITree(i),
db_data = pgBrowser.treeMenu.findNode(tree_data.slice(0,4)),
dbNode = db_data.domNode;
if (!d)
return;
@ -565,7 +583,9 @@ define([
url: baseUrl,
method: 'GET',
})
.done(function() {
.done(function(res) {
var data = res.data;
var url = url_for('debugger.direct', {
'trans_id': trans_id,
@ -592,9 +612,17 @@ define([
),
panel = pgBrowser.docker.addPanel(
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
);
),
db_label = newTreeInfo.database.label;
if(data && data.data_obj && data.data_obj.db_name != newTreeInfo.database.label) {
db_label = data.data_obj.db_name;
var message = `Current database has been moved or renamed to ${db_label}. Click on the OK button to refresh the database name.`;
panelTitleFunc.refresh_db_node(message, dbNode);
}
var label = newTreeInfo.function ? newTreeInfo.function.label : newTreeInfo.trigger_function ? newTreeInfo.trigger_function.label : newTreeInfo.trigger ? newTreeInfo.trigger.label : newTreeInfo.procedure.label;
debuggerUtils.setDebuggerTitle(panel, browser_preferences, label, newTreeInfo.schema.label, newTreeInfo.database.label, null, pgBrowser);
debuggerUtils.setDebuggerTitle(panel, browser_preferences, label, newTreeInfo.schema.label, db_label, null, pgBrowser);
panel.focus();

View File

@ -21,6 +21,7 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.driver import get_driver
from ... import socketio as sio
from pgadmin.utils import get_complete_file_path
from pgadmin.utils.ajax import internal_server_error
session_input = dict()
@ -28,6 +29,7 @@ session_input_cursor = dict()
session_last_cmd = dict()
pdata = dict()
cdata = dict()
_NODES_SQL = 'nodes.sql'
class PSQLModule(PgAdminModule):
@ -95,6 +97,8 @@ def panel(trans_id):
# Set TERM env for xterm.
os.environ['TERM'] = 'xterm'
o_db_name = _get_database(params['sid'], params['did'])
return render_template('editor_template.html',
sid=params['sid'],
db=underscore_unescape(params['db']) if params[
@ -102,7 +106,8 @@ def panel(trans_id):
server_type=params['server_type'],
is_enable=config.ENABLE_PSQL,
title=underscore_unescape(params['title']),
theme=params['theme']
theme=params['theme'],
o_db_name=o_db_name
)
@ -684,3 +689,53 @@ def disconnect_socket():
os.close(app.config['sessions'][request.sid])
os.close(cdata[request.sid])
del app.config['sessions'][request.sid]
def _get_database(sid, did):
"""
This method is used to get database based on sid, did.
"""
try:
from pgadmin.utils.driver import get_driver
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(int(sid))
conn = manager.connection()
db_name = None
if conn.connected():
is_connected = True
else:
is_connected = False
if is_connected:
if conn.manager and conn.manager.db_info \
and conn.manager.db_info[int(did)] is not None:
db_name = conn.manager.db_info[int(did)]['datname']
return db_name
elif sid:
template_path = 'databases/sql/#{0}#'.format(manager.version)
last_system_oid = 0
server_node_res = manager
db_disp_res = None
params = None
if server_node_res and server_node_res.db_res:
db_disp_res = ", ".join(
['%s'] * len(server_node_res.db_res.split(','))
)
params = tuple(server_node_res.db_res.split(','))
sql = render_template(
"/".join([template_path, _NODES_SQL]),
last_system_oid=last_system_oid,
db_restrictions=db_disp_res,
did=did
)
status, databases = conn.execute_dict(sql, params)
database = databases['rows'][0]
if database is not None:
db_name = database['name']
return db_name
else:
return db_name
except Exception:
return None

View File

@ -16,9 +16,10 @@ import {enable} from 'pgadmin.browser.toolbar';
import clipboard from 'sources/selection/clipboard';
import 'wcdocker';
import {getRandomInt} from 'sources/utils';
import pgWindow from 'sources/window';
import {getTreeNodeHierarchyFromIdentifier} from 'sources/tree/pgadmin_tree_node';
import {generateTitle} from 'tools/datagrid/static/js/datagrid_panel_title';
import {generateTitle, refresh_db_node} from 'tools/datagrid/static/js/datagrid_panel_title';
export function setPanelTitle(psqlToolPanel, panelTitle) {
@ -285,6 +286,7 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser)
openUrl += `?sgid=${parentData.server_group._id}`
+`&sid=${parentData.server._id}`
+`&did=${parentData.database._id}`
+`&server_type=${parentData.server.server_type}`
+ `&theme=${theme}`;
@ -417,7 +419,19 @@ export function initialize(gettext, url_for, $, _, pgAdmin, csrfToken, Browser)
term.onKey(function (ev) {
socket.emit('socket_input', {'input': ev.key, 'key_name': ev.domEvent.code});
});
}
},
check_db_name_change: function(db_name, o_db_name) {
if (db_name != o_db_name) {
var selected_item = pgWindow.pgAdmin.Browser.treeMenu.selected(),
tree_data = pgWindow.pgAdmin.Browser.treeMenu.translateTreeNodeIdFromACITree(selected_item),
database_data = pgWindow.pgAdmin.Browser.treeMenu.findNode(tree_data.slice(0,4)),
dbNode = database_data.domNode;
var message = `Current database has been moved or renamed to ${o_db_name}. Click on the OK button to refresh the database name, and reopen the psql again.`;
refresh_db_node(message, dbNode);
}
},
};
return pgBrowser.psql;

View File

@ -34,6 +34,7 @@ require(
const socket = self.pgAdmin.Browser.psql.psql_socket();
self.pgAdmin.Browser.psql.psql_socket_io(socket, '{{is_enable}}', '{{sid}}', '{{db}}', '{{server_type}}', fitAddon, term);
self.pgAdmin.Browser.psql.psql_terminal_io(term, socket);
self.pgAdmin.Browser.psql.check_db_name_change('{{db}}', '{{o_db_name}}');
<!-- Resize the terminal -->
function fitToscreen(){

View File

@ -17,10 +17,10 @@ class PSQLPanel(BaseTestGenerator):
def runTest(self):
trans_id = random.randint(1, 9999999)
url = '/psql/panel/{trans_id}?sgid={sgid}&sid={sid}&server_type=pg' \
'&db={db_name}&theme={theme}'.\
url = '/psql/panel/{trans_id}?sgid={sgid}&sid={sid}&did={did}' \
'&server_type=pg&db={db_name}&theme={theme}'.\
format(trans_id=trans_id, sgid=self.sgid, sid=self.sid,
db_name=self.db_name, theme=self.theme)
did=self.did, db_name=self.db_name, theme=self.theme)
response = self.tester.post(
url, data={"title": "panel_title"},

View File

@ -351,6 +351,7 @@ def poll(trans_id):
oids = None
additional_messages = None
notifies = None
data_obj = {}
# Check the transaction and connection status
status, error_msg, conn, trans_obj, session_obj = \
@ -503,6 +504,7 @@ def poll(trans_id):
result = error_msg
transaction_status = conn.transaction_status()
data_obj['db_name'] = conn.db
return make_json_response(
data={
@ -520,6 +522,7 @@ def poll(trans_id):
'has_oids': has_oids,
'oids': oids,
'transaction_status': transaction_status,
'data_obj': data_obj,
},
encoding=conn.python_encoding
)

View File

@ -13,6 +13,7 @@ __webpack_public_path__ = window.resourceBasePath;
/* eslint-enable */
import {launchDataGrid} from 'tools/datagrid/static/js/show_query_tool';
import {generateDatagridTitle} from 'tools/datagrid/static/js/show_data';
define('tools.querytool', [
'sources/gettext', 'sources/url_for', 'jquery', 'jquery.ui',
@ -424,6 +425,10 @@ define('tools.querytool', [
self.fetch_query_history();
});
self.handler.on('pgadmin-sqleditor:check_synchronous_db_name_change', (result)=>{
self.handler.check_db_name_change(result);
});
queryToolNotifications.renderNotificationsGrid(self.notifications_panel);
var text_container = $('<textarea id="sql_query_tool" tabindex="-1"></textarea>');
@ -5087,6 +5092,65 @@ define('tools.querytool', [
update_notifications: function (notifications) {
queryToolNotifications.updateNotifications(notifications);
},
/* This function is used to set editor title based on title
* & db_name passed as parameters
*/
set_title_and_render_connection: function(title, db_name) {
var self = this;
self.gridView.set_editor_title(_.unescape(title));
self.gridView.handler.setTitle(_.unescape(title));
self.gridView.connection_list.forEach(option =>{
if(db_name == option['database_name']) {
option.database_name = db_name;
option.title = title;
if('is_selected' in option && option['is_selected']) {
self.gridView.$el.find('ul#connections-list li.selected-connection > a').text(title);
}
return true;
}
});
},
/* This function is used to check the synchronous db name change by users.
* if changed, alert will be generated with message prompting user to
* click OK for automatic db node refresh.
*/
check_db_name_change: function(data) {
var self = this;
var selected_item = pgWindow.default.pgAdmin.Browser.treeMenu.selected(),
tree_data = pgWindow.default.pgAdmin.Browser.treeMenu.translateTreeNodeIdFromACITree(selected_item),
server_data = pgWindow.default.pgAdmin.Browser.treeMenu.findNode(tree_data.slice(0,2)),
database_data = pgWindow.default.pgAdmin.Browser.treeMenu.findNode(tree_data.slice(0,4)),
db_name = database_data.data.label;
if(!_.isEqual(db_name, data.data_obj.db_name)) {
var message = `Current database has been moved or renamed to ${data.data_obj.db_name}. Click on the OK button to refresh the database name.`,
title = self.url_params.title;
if(self.is_query_tool) {// for query tool
var qt_title_placeholder = self.gridView.browser_preferences['qt_tab_title_placeholder'];
var title_data = {
'database': data.data_obj.db_name,
'username': server_data.data.user.name,
'server': server_data.data.label,
'type': 'query_tool'
};
title = panelTitleFunc.generateTitle(qt_title_placeholder, title_data);
}
else { // for datagrid
title = generateDatagridTitle(pgWindow.default.pgAdmin.Browser, selected_item, null, data.data_obj);
}
panelTitleFunc.refresh_db_node(message, database_data.domNode);
self.set_title_and_render_connection(title, database_data.data.label);
}
},
});
pgAdmin.SqlEditor = {

View File

@ -25,6 +25,7 @@ describe('#callRenderAfterPoll', () => {
disable_transaction_buttons: jasmine.createSpy('SQLEditor.disable_transaction_buttons'),
reset_data_store: jasmine.createSpy('SQLEditor.reset_data_store'),
enable_disable_download_btn: jasmine.createSpy('SQLEditor.enable_disable_download_btn'),
check_db_name_change: jasmine.createSpy('SQLEditor.check_db_name_change'),
query_start_time: new Date(),
};
alertify = jasmine.createSpyObj('alertify', ['success']);
@ -229,6 +230,14 @@ describe('#callRenderAfterPoll', () => {
expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
});
it('check whether database has been moved/renamed', () => {
callRenderAfterPoll(sqlEditorSpy, alertify, queryResult);
expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:check_synchronous_db_name_change', queryResult);
expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
});
});
});
});

File diff suppressed because it is too large Load Diff