Don't wait for the database connection before rendering the Query Tool UI, for improved UX. Fixes #4453

In addition, unescape HTML entities in database names in the Query Tool title bar. Fixes #4584
This commit is contained in:
Aditya Toshniwal 2019-08-23 12:14:20 +01:00 committed by Dave Page
parent 25f85fe123
commit 234efc3be7
28 changed files with 337 additions and 449 deletions

View File

@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
New features New features
************ ************
| `Issue #4453 <https://redmine.postgresql.org/issues/4453>`_ - Don't wait for the database connection before rendering the Query Tool UI, for improved UX.
| `Issue #4651 <https://redmine.postgresql.org/issues/4651>`_ - Allow configuration options to be set from the environment in the container distribution. | `Issue #4651 <https://redmine.postgresql.org/issues/4651>`_ - Allow configuration options to be set from the environment in the container distribution.
Housekeeping Housekeeping
@ -22,6 +23,7 @@ Bug fixes
| `Issue #2706 <https://redmine.postgresql.org/issues/2706>`_ - Added ProjectSet icon for explain module. | `Issue #2706 <https://redmine.postgresql.org/issues/2706>`_ - Added ProjectSet icon for explain module.
| `Issue #2828 <https://redmine.postgresql.org/issues/2828>`_ - Added Gather Merge, Named Tuple Store Scan and Table Function Scan icon for explain module. | `Issue #2828 <https://redmine.postgresql.org/issues/2828>`_ - Added Gather Merge, Named Tuple Store Scan and Table Function Scan icon for explain module.
| `Issue #4419 <https://redmine.postgresql.org/issues/4419>`_ - Fix a debugger error when using Python 2.7. | `Issue #4419 <https://redmine.postgresql.org/issues/4419>`_ - Fix a debugger error when using Python 2.7.
| `Issue #4584 <https://redmine.postgresql.org/issues/4584>`_ - Unescape HTML entities in database names in the Query Tool title bar.
| `Issue #4643 <https://redmine.postgresql.org/issues/4643>`_ - Fix Truncate option deselect issue for compound triggers. | `Issue #4643 <https://redmine.postgresql.org/issues/4643>`_ - Fix Truncate option deselect issue for compound triggers.
| `Issue #4644 <https://redmine.postgresql.org/issues/4644>`_ - Fix length and precision enable/disable issue when changing the data type for Domain node. | `Issue #4644 <https://redmine.postgresql.org/issues/4644>`_ - Fix length and precision enable/disable issue when changing the data type for Domain node.
| `Issue #4650 <https://redmine.postgresql.org/issues/4650>`_ - Fix SQL tab issue for Views. It's a regression of compound triggers. | `Issue #4650 <https://redmine.postgresql.org/issues/4650>`_ - Fix SQL tab issue for Views. It's a regression of compound triggers.

View File

@ -47,6 +47,30 @@ def underscore_escape(text):
return text return text
def underscore_unescape(text):
"""
This function mimics the behaviour of underscore js unescape function
The html unescape by jinja is not compatible for underscore escape
function
:param text: input html text
:return: unescaped text
"""
html_map = {
"&amp;": '&',
"&lt;": '<',
"&gt;": '>',
"&quot;": '"',
"&#96;": '`',
"&#x27;": "'"
}
# always replace & first
for c, r in html_map.items():
text = text.replace(c, r)
return text
def is_version_in_range(sversion, min_ver, max_ver): def is_version_in_range(sversion, min_ver, max_ver):
assert (max_ver is None or isinstance(max_ver, int)) assert (max_ver is None or isinstance(max_ver, int))
assert (min_ver is None or isinstance(min_ver, int)) assert (min_ver is None or isinstance(min_ver, int))

View File

@ -73,6 +73,9 @@ define(['jquery', 'sources/gettext', 'sources/url_for'],
return; return;
} }
if($status_el.hasClass('obtaining-conn')){
return;
}
let sqleditor_obj = target; let sqleditor_obj = target;
// Start polling.. // Start polling..
@ -195,22 +198,6 @@ define(['jquery', 'sources/gettext', 'sources/url_for'],
} }
return '1em'; return '1em';
}, },
removeSlashInTheString: (value) => {
let locationList = [];
let idx = 0;
while (value && value.indexOf('/') !== -1) {
locationList.push(value.indexOf('/') + idx);
value = value.replace('/', '');
// No of slashes already removed, so we need to increment the
// index accordingly when adding into location list
idx++;
}
return {
'slashLocations': locationList.join(','),
'title': encodeURIComponent(value),
};
},
}; };
return sqlEditorUtils; return sqlEditorUtils;
}); });

View File

@ -199,3 +199,9 @@ export function fully_qualify(pgBrowser, data, item) {
return quote_ident(data._label); return quote_ident(data._label);
} }
} }
export function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}

View File

@ -30,7 +30,7 @@ from pgadmin.utils.driver import get_driver
from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
from pgadmin.utils.preferences import Preferences from pgadmin.utils.preferences import Preferences
from pgadmin.settings import get_setting from pgadmin.settings import get_setting
from pgadmin.browser.utils import underscore_escape from pgadmin.browser.utils import underscore_unescape
query_tool_close_session_lock = Lock() query_tool_close_session_lock = Lock()
@ -114,13 +114,13 @@ def show_filter():
@blueprint.route( @blueprint.route(
'/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/' '/initialize/datagrid/<int:trans_id>/<int:cmd_type>/<obj_type>/'
'<int:did>/<int:obj_id>', '<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
methods=["PUT", "POST"], methods=["PUT", "POST"],
endpoint="initialize_datagrid" endpoint="initialize_datagrid"
) )
@login_required @login_required
def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id): def initialize_datagrid(trans_id, cmd_type, obj_type, sgid, sid, did, obj_id):
""" """
This method is responsible for creating an asynchronous connection. This method is responsible for creating an asynchronous connection.
After creating the connection it will instantiate and initialize After creating the connection it will instantiate and initialize
@ -183,9 +183,6 @@ def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id):
app.logger.error(e) app.logger.error(e)
return internal_server_error(errormsg=str(e)) return internal_server_error(errormsg=str(e))
# Create a unique id for the transaction
trans_id = str(random.randint(1, 9999999))
if 'gridData' not in session: if 'gridData' not in session:
sql_grid_data = dict() sql_grid_data = dict()
else: else:
@ -193,7 +190,7 @@ def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id):
# Use pickle to store the command object which will be used later by the # Use pickle to store the command object which will be used later by the
# sql grid module. # sql grid module.
sql_grid_data[trans_id] = { sql_grid_data[str(trans_id)] = {
# -1 specify the highest protocol version available # -1 specify the highest protocol version available
'command_obj': pickle.dumps(command_obj, -1) 'command_obj': pickle.dumps(command_obj, -1)
} }
@ -203,51 +200,34 @@ def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id):
return make_json_response( return make_json_response(
data={ data={
'gridTransId': trans_id 'conn_id': conn_id
} }
) )
@blueprint.route( @blueprint.route(
'/panel/<int:trans_id>/<is_query_tool>/<path:editor_title>', '/panel/<int:trans_id>',
methods=["GET"], methods=["POST"],
endpoint='panel' endpoint='panel'
) )
def panel(trans_id, is_query_tool, editor_title): def panel(trans_id):
""" """
This method calls index.html to render the data grid. This method calls index.html to render the data grid.
Args: Args:
trans_id: unique transaction id trans_id: unique transaction id
is_query_tool: True if panel calls when query tool menu is clicked.
editor_title: Title of the editor
""" """
# Let's fetch Script type URL from request
if request.args and request.args['query_url'] != '':
sURL = request.args['query_url']
else:
sURL = None
# Fetch server type from request url_params = None
if request.args and request.args['server_type'] != '': if request.args:
server_type = request.args['server_type'] url_params = {k: v for k, v in request.args.items()}
else:
server_type = None
if request.args and 'server_ver' in request.args: if request.form:
server_ver = request.args['server_ver'] url_params['title'] = request.form['title']
else: if 'sql_filter' in request.form:
server_ver = 0 url_params['sql_filter'] = request.form['sql_filter']
if 'query_url' in request.form:
# If title has slash(es) in it then replace it url_params['query_url'] = request.form['query_url']
if request.args and request.args['fslashes'] != '':
try:
fslashesList = request.args['fslashes'].split(',')
for idx in fslashesList:
idx = int(idx)
editor_title = editor_title[:idx] + '/' + editor_title[idx:]
except IndexError as e:
app.logger.exception(e)
# We need client OS information to render correct Keyboard shortcuts # We need client OS information to render correct Keyboard shortcuts
user_agent = UserAgent(request.headers.get('User-Agent')) user_agent = UserAgent(request.headers.get('User-Agent'))
@ -277,12 +257,8 @@ def panel(trans_id, is_query_tool, editor_title):
# Fetch the server details # Fetch the server details
bgcolor = None bgcolor = None
fgcolor = None fgcolor = None
if 'gridData' in session and str(trans_id) in session['gridData']:
# Fetch the object for the specified transaction id. s = Server.query.filter_by(id=url_params['sid']).first()
# Use pickle.loads function to get the command object
session_obj = session['gridData'][str(trans_id)]
trans_obj = pickle.loads(session_obj['command_obj'])
s = Server.query.filter_by(id=trans_obj.sid).first()
if s and s.bgcolor: if s and s.bgcolor:
# If background is set to white means we do not have to change # If background is set to white means we do not have to change
# the title background else change it as per user specified # the title background else change it as per user specified
@ -293,48 +269,31 @@ def panel(trans_id, is_query_tool, editor_title):
layout = get_setting('SQLEditor/Layout') layout = get_setting('SQLEditor/Layout')
url_params = dict()
if is_query_tool == 'true':
url_params['sgid'] = trans_obj.sgid
url_params['sid'] = trans_obj.sid
url_params['did'] = trans_obj.did
else:
url_params['cmd_type'] = trans_obj.cmd_type
url_params['obj_type'] = trans_obj.object_type
url_params['sgid'] = trans_obj.sgid
url_params['sid'] = trans_obj.sid
url_params['did'] = trans_obj.did
url_params['obj_id'] = trans_obj.obj_id
return render_template( return render_template(
"datagrid/index.html", "datagrid/index.html",
_=gettext, _=gettext,
uniqueId=trans_id, uniqueId=trans_id,
is_query_tool=is_query_tool,
editor_title=underscore_escape(editor_title),
script_type_url=sURL,
is_desktop_mode=app.PGADMIN_RUNTIME, is_desktop_mode=app.PGADMIN_RUNTIME,
is_linux=is_linux_platform, is_linux=is_linux_platform,
server_type=server_type, title=underscore_unescape(url_params['title']),
server_ver=server_ver, url_params=json.dumps(url_params),
client_platform=user_agent.platform, client_platform=user_agent.platform,
bgcolor=bgcolor, bgcolor=bgcolor,
fgcolor=fgcolor, fgcolor=fgcolor,
url_params=json.dumps(url_params),
layout=layout, layout=layout,
) )
@blueprint.route( @blueprint.route(
'/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>', '/initialize/query_tool/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
methods=["POST"], endpoint='initialize_query_tool_with_did' methods=["POST"], endpoint='initialize_query_tool_with_did'
) )
@blueprint.route( @blueprint.route(
'/initialize/query_tool/<int:sgid>/<int:sid>', '/initialize/query_tool/<int:trans_id>/<int:sgid>/<int:sid>',
methods=["POST"], endpoint='initialize_query_tool' methods=["POST"], endpoint='initialize_query_tool'
) )
@login_required @login_required
def initialize_query_tool(sgid, sid, did=None): def initialize_query_tool(trans_id, sgid, sid, did=None):
""" """
This method is responsible for instantiating and initializing This method is responsible for instantiating and initializing
the query tool object. It will also create a unique the query tool object. It will also create a unique
@ -346,16 +305,16 @@ def initialize_query_tool(sgid, sid, did=None):
did: Database Id did: Database Id
""" """
connect = True connect = True
reqArgs = None
# Read the data if present. Skipping read may cause connection # Read the data if present. Skipping read may cause connection
# reset error if data is sent from the client # reset error if data is sent from the client
if request.data: if request.data:
reqArgs = request.data _ = request.data
reqArgs = request.args reqArgs = request.args
if ('recreate' in reqArgs and if ('recreate' in reqArgs and
reqArgs['recreate'] == '1'): reqArgs['recreate'] == '1'):
connect = False connect = False
# Create asynchronous connection using random connection id. # Create asynchronous connection using random connection id.
conn_id = str(random.randint(1, 9999999)) conn_id = str(random.randint(1, 9999999))
@ -389,9 +348,6 @@ def initialize_query_tool(sgid, sid, did=None):
app.logger.error(e) app.logger.error(e)
return internal_server_error(errormsg=str(e)) return internal_server_error(errormsg=str(e))
# Create a unique id for the transaction
trans_id = str(random.randint(1, 9999999))
if 'gridData' not in session: if 'gridData' not in session:
sql_grid_data = dict() sql_grid_data = dict()
else: else:
@ -404,7 +360,7 @@ def initialize_query_tool(sgid, sid, did=None):
# Use pickle to store the command object which will be used # Use pickle to store the command object which will be used
# later by the sql grid module. # later by the sql grid module.
sql_grid_data[trans_id] = { sql_grid_data[str(trans_id)] = {
# -1 specify the highest protocol version available # -1 specify the highest protocol version available
'command_obj': pickle.dumps(command_obj, -1) 'command_obj': pickle.dumps(command_obj, -1)
} }
@ -414,7 +370,7 @@ def initialize_query_tool(sgid, sid, did=None):
return make_json_response( return make_json_response(
data={ data={
'gridTransId': trans_id, 'connId': str(conn_id),
'serverVersion': manager.version, 'serverVersion': manager.version,
} }
) )

View File

@ -13,10 +13,10 @@ define('pgadmin.datagrid', [
'sources/sqleditor_utils', 'backbone', 'sources/sqleditor_utils', 'backbone',
'tools/datagrid/static/js/show_data', 'tools/datagrid/static/js/show_data',
'tools/datagrid/static/js/show_query_tool', 'pgadmin.browser.toolbar', 'tools/datagrid/static/js/show_query_tool', 'pgadmin.browser.toolbar',
'tools/datagrid/static/js/datagrid_panel_title', 'wcdocker', 'tools/datagrid/static/js/datagrid_panel_title', 'sources/utils', 'wcdocker',
], function( ], function(
gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils, gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils,
Backbone, showData, showQueryTool, toolBar, panelTitleFunc Backbone, showData, showQueryTool, toolBar, panelTitleFunc, commonUtils
) { ) {
// Some scripts do export their object in the window only. // Some scripts do export their object in the window only.
// Generally the one, which do no have AMD support. // Generally the one, which do no have AMD support.
@ -52,7 +52,6 @@ define('pgadmin.datagrid', [
self.preferences = pgBrowser.get_preferences_for_module('sqleditor'); self.preferences = pgBrowser.get_preferences_for_module('sqleditor');
}); });
// Define list of nodes on which view data option appears // Define list of nodes on which view data option appears
var supported_nodes = [ var supported_nodes = [
'table', 'view', 'mview', 'table', 'view', 'mview',
@ -195,97 +194,48 @@ define('pgadmin.datagrid', [
// This is a callback function to show data when user click on menu item. // This is a callback function to show data when user click on menu item.
show_data_grid: function(data, i) { show_data_grid: function(data, i) {
showData.showDataGrid(this, pgBrowser, alertify, data, i); const transId = commonUtils.getRandomInt(1, 9999999);
showData.showDataGrid(this, pgBrowser, alertify, data, i, transId);
}, },
// This is a callback function to show filtered data when user click on menu item. // This is a callback function to show filtered data when user click on menu item.
show_filtered_row: function(data, i) { show_filtered_row: function(data, i) {
showData.showDataGrid(this, pgBrowser, alertify, data, i, true, this.preferences); const transId = commonUtils.getRandomInt(1, 9999999);
showData.showDataGrid(this, pgBrowser, alertify, data, i, transId, true, this.preferences);
}, },
// This is a callback function to show query tool when user click on menu item. // This is a callback function to show query tool when user click on menu item.
show_query_tool: function(url, aciTreeIdentifier) { show_query_tool: function(url, aciTreeIdentifier) {
showQueryTool.showQueryTool(this, pgBrowser, alertify, url, aciTreeIdentifier); const transId = commonUtils.getRandomInt(1, 9999999);
showQueryTool.showQueryTool(this, pgBrowser, alertify, url, aciTreeIdentifier, transId);
}, },
create_transaction: function(baseUrl, target, is_query_tool, server_type, sURL, panel_title, sql_filter, recreate) { launch_grid: function(trans_id, panel_url, is_query_tool, panel_title, sURL=null, sql_filter=null) {
var self = this; var self = this;
target = target || self;
if (recreate) { let queryToolForm = `
baseUrl += '?recreate=1'; <form id="queryToolForm" action="${panel_url}" method="post">
<input id="title" name="title" hidden />`;
if(sURL){
queryToolForm +=`<input name="query_url" value="${sURL}" hidden />`;
}
if(sql_filter) {
queryToolForm +=`<textarea name="sql_filter" hidden>${sql_filter}</textarea>`;
} }
/* Send the data only if required. Sending non required data may queryToolForm +=`
* cause connection reset error if data is not read by flask server </form>
*/ <script>
let reqData = null; document.getElementById("title").value = "${panel_title}";
if(sql_filter != '') { document.getElementById("queryToolForm").submit();
reqData = JSON.stringify(sql_filter); </script>
} `;
$.ajax({
url: baseUrl,
method: 'POST',
dataType: 'json',
data: reqData,
contentType: 'application/json',
})
.done(function(res) {
res.data.is_query_tool = is_query_tool;
res.data.server_type = server_type;
res.data.sURL = sURL;
res.data.panel_title = panel_title;
target.trigger('pgadmin-datagrid:transaction:created', res.data);
})
.fail(function(xhr) {
if (target !== self) {
if(xhr.status == 503 && xhr.responseJSON.info != undefined &&
xhr.responseJSON.info == 'CONNECTION_LOST') {
setTimeout(function() {
target.handle_connection_lost(true, xhr);
});
return;
}
}
try {
var err = JSON.parse(xhr.responseText);
alertify.alert(gettext('Query Tool initialization error'),
err.errormsg
);
} catch (e) {
alertify.alert(gettext('Query Tool initialization error'),
e.statusText
);
}
});
},
launch_grid: function(trans_obj) {
var self = this,
panel_title = trans_obj.panel_title,
grid_title = trans_obj.panel_title;
// Open the panel if frame is initialized
let titileForURLObj = sqlEditorUtils.removeSlashInTheString(grid_title);
var url_params = {
'trans_id': trans_obj.gridTransId,
'is_query_tool': trans_obj.is_query_tool,
'editor_title': titileForURLObj.title,
},
baseUrl = url_for('datagrid.panel', url_params) +
'?' + 'query_url=' + encodeURI(trans_obj.sURL) +
'&server_type=' + encodeURIComponent(trans_obj.server_type) +
'&server_ver=' + trans_obj.serverVersion+
'&fslashes=' + titileForURLObj.slashLocations;
if (self.preferences.new_browser_tab) { if (self.preferences.new_browser_tab) {
var newWin = window.open(baseUrl, '_blank'); var newWin = window.open('', '_blank');
newWin.document.write(queryToolForm);
// add a load listener to the window so that the title gets changed on page load
newWin.addEventListener('load', function() {
newWin.document.title = panel_title; newWin.document.title = panel_title;
});
} else { } else {
/* On successfully initialization find the dashboard panel, /* On successfully initialization find the dashboard panel,
* create new panel and add it to the dashboard panel. * create new panel and add it to the dashboard panel.
@ -294,13 +244,13 @@ define('pgadmin.datagrid', [
var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]); var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);
// Set panel title and icon // Set panel title and icon
panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, trans_obj.is_query_tool, panel_title); panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, is_query_tool, panel_title);
queryToolPanel.focus(); queryToolPanel.focus();
// Listen on the panel closed event. // Listen on the panel closed event.
queryToolPanel.on(wcDocker.EVENT.CLOSED, function() { queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
$.ajax({ $.ajax({
url: url_for('datagrid.close', {'trans_id': trans_obj.gridTransId}), url: url_for('datagrid.close', {'trans_id': trans_id}),
method: 'DELETE', method: 'DELETE',
}); });
}); });
@ -325,7 +275,7 @@ define('pgadmin.datagrid', [
frame.onLoaded(()=>{ frame.onLoaded(()=>{
$spinner_el.remove(); $spinner_el.remove();
}); });
frame.openURL(baseUrl); frame.openHTML(queryToolForm);
} }
} }
}, 100); }, 100);

View File

@ -52,6 +52,6 @@ export function setQueryToolDockerTitle(panel, is_query_tool, panel_title, is_fi
panel_icon = 'fa fa-bolt'; panel_icon = 'fa fa-bolt';
} }
panel.title('<span title="'+_.escape(panel_tooltip)+'">'+_.escape(panel_title)+'</span>'); panel.title('<span title="'+ panel_tooltip +'">'+ panel_title +'</span>');
panel.icon(panel_icon); panel.icon(panel_icon);
} }

View File

@ -20,6 +20,7 @@ export function showDataGrid(
alertify, alertify,
connectionData, connectionData,
aciTreeIdentifier, aciTreeIdentifier,
transId,
filter=false, filter=false,
preferences=null preferences=null
) { ) {
@ -42,43 +43,25 @@ export function showDataGrid(
return; return;
} }
const baseUrl = generateUrl(connectionData, node.getData(), parentData); const gridUrl = generateUrl(transId, connectionData, node.getData(), parentData);
const grid_title = generateDatagridTitle(pgBrowser, aciTreeIdentifier); const queryToolTitle = generateDatagridTitle(pgBrowser, aciTreeIdentifier);
if(filter) { if(filter) {
initFilterDialog(alertify, pgBrowser, preferences); initFilterDialog(alertify, pgBrowser, preferences);
const validateUrl = generateFilterValidateUrl(node.getData(), parentData); const validateUrl = generateFilterValidateUrl(node.getData(), parentData);
let okCallback = function(sql) { let okCallback = function(sql) {
datagrid.create_transaction( datagrid.launch_grid(transId, gridUrl, false, queryToolTitle, null, sql);
baseUrl,
null,
'false',
parentData.server.server_type,
'',
grid_title,
sql,
false
);
}; };
$.get(url_for('datagrid.filter'), $.get(url_for('datagrid.filter'),
function(data) { function(data) {
alertify.filterDialog(`Data Filter - ${grid_title}`, data, validateUrl, preferences, okCallback) alertify.filterDialog(`Data Filter - ${queryToolTitle}`, data, validateUrl, preferences, okCallback)
.resizeTo(pgBrowser.stdW.sm,pgBrowser.stdH.sm); .resizeTo(pgBrowser.stdW.sm,pgBrowser.stdH.sm);
} }
); );
} else { } else {
datagrid.create_transaction( datagrid.launch_grid(transId, gridUrl, false, queryToolTitle);
baseUrl,
null,
'false',
parentData.server.server_type,
'',
grid_title,
''
);
} }
} }
@ -96,17 +79,21 @@ export function retrieveNameSpaceName(parentData) {
return ''; return '';
} }
function generateUrl(connectionData, nodeData, parentData) { function generateUrl(trans_id, connectionData, nodeData, parentData) {
const url_params = { let url_endpoint = url_for('datagrid.panel', {
'cmd_type': connectionData.mnuid, 'trans_id': trans_id,
'obj_type': nodeData._type, });
'sgid': parentData.server_group._id,
'sid': parentData.server._id,
'did': parentData.database._id,
'obj_id': nodeData._id,
};
return url_for('datagrid.initialize_datagrid', url_params); url_endpoint += `?is_query_tool=${false}`
+`&cmd_type=${connectionData.mnuid}`
+`&obj_type=${nodeData._type}`
+`&obj_id=${nodeData._id}`
+`&sgid=${parentData.server_group._id}`
+`&sid=${parentData.server._id}`
+`&did=${parentData.database._id}`
+`&server_type=${parentData.server.server_type}`;
return url_endpoint;
} }
function generateFilterValidateUrl(nodeData, parentData) { function generateFilterValidateUrl(nodeData, parentData) {

View File

@ -16,19 +16,21 @@ function hasDatabaseInformation(parentData) {
return parentData.database; return parentData.database;
} }
function generateUrl(parentData) { function generateUrl(trans_id, title, parentData) {
let url_endpoint = 'datagrid.initialize_query_tool'; let url_endpoint = url_for('datagrid.panel', {
let url_params = { 'trans_id': trans_id,
'sgid': parentData.server_group._id, });
'sid': parentData.server._id,
}; url_endpoint += `?is_query_tool=${true}`
+`&sgid=${parentData.server_group._id}`
+`&sid=${parentData.server._id}`
+`&server_type=${parentData.server.server_type}`;
if (hasDatabaseInformation(parentData)) { if (hasDatabaseInformation(parentData)) {
url_params['did'] = parentData.database._id; url_endpoint += `&did=${parentData.database._id}`;
url_endpoint = 'datagrid.initialize_query_tool_with_did';
} }
return url_for(url_endpoint, url_params); return url_endpoint;
} }
function hasServerInformations(parentData) { function hasServerInformations(parentData) {
@ -40,7 +42,7 @@ function generateTitle(pgBrowser, aciTreeIdentifier) {
return baseTitle; return baseTitle;
} }
export function showQueryTool(datagrid, pgBrowser, alertify, url, aciTreeIdentifier) { export function showQueryTool(datagrid, pgBrowser, alertify, url, aciTreeIdentifier, transId) {
const sURL = url || ''; const sURL = url || '';
const queryToolTitle = generateTitle(pgBrowser, aciTreeIdentifier); const queryToolTitle = generateTitle(pgBrowser, aciTreeIdentifier);
@ -60,9 +62,7 @@ export function showQueryTool(datagrid, pgBrowser, alertify, url, aciTreeIdentif
return; return;
} }
const baseUrl = generateUrl(parentData); const gridUrl = generateUrl(transId, queryToolTitle, parentData);
datagrid.create_transaction( datagrid.launch_grid(transId, gridUrl, true, queryToolTitle, sURL);
baseUrl, null, 'true',
parentData.server.server_type, sURL, queryToolTitle, '', false);
} }

View File

@ -1,5 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}{{ config.APP_NAME }} - Datagrid{% endblock %} {% block title %}{{title}}{% endblock %}
{% block body %} {% block body %}
<style> <style>
body {padding: 0px;} body {padding: 0px;}
@ -228,7 +228,7 @@
</button> </button>
<button id="btn-flash" data-test-selector="execute-refresh-button" type="button" class="btn btn-sm btn-secondary" style="width: 32px;" <button id="btn-flash" data-test-selector="execute-refresh-button" type="button" class="btn btn-sm btn-secondary" style="width: 32px;"
title="" title=""
tabindex="0"> tabindex="0" disabled>
<i class="fa fa-bolt sql-icon-lg" aria-hidden="true"></i> <i class="fa fa-bolt sql-icon-lg" aria-hidden="true"></i>
</button> </button>
<button id="btn-query-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split" <button id="btn-query-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split"
@ -256,12 +256,12 @@
<button id="btn-explain" type="button" class="btn btn-sm btn-secondary" <button id="btn-explain" type="button" class="btn btn-sm btn-secondary"
title="" title=""
accesskey="" accesskey=""
tabindex="0"> tabindex="0" disabled>
<i class="fa fa-hand-pointer-o sql-icon-lg" aria-hidden="true"></i> <i class="fa fa-hand-pointer-o sql-icon-lg" aria-hidden="true"></i>
</button> </button>
<button id="btn-explain-analyze" type="button" class="btn btn-sm btn-secondary" <button id="btn-explain-analyze" type="button" class="btn btn-sm btn-secondary"
title="" title=""
accesskey=""> accesskey="" disabled>
<i class="fa fa-list-alt sql-icon-lg" aria-hidden="true"></i> <i class="fa fa-list-alt sql-icon-lg" aria-hidden="true"></i>
</button> </button>
<button id="btn-explain-options-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split" <button id="btn-explain-options-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split"
@ -358,20 +358,20 @@
data-panel-visible="visible" data-panel-visible="visible"
accesskey="" accesskey=""
tabindex="0"> tabindex="0">
<i class="fa-custom fa-query-tool-disconnected" aria-hidden="true" <i class="fa-custom fa-query-tool-disconnected obtaining-conn" aria-hidden="true"
title=""> title="">
</i> </i>
</div> </div>
<div class="editor-title" <div class="editor-title"
style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};"></div> style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">&nbsp;</div>
</div> </div>
<div id="editor-panel" tabindex="0"> <div id="editor-panel" tabindex="0">
<div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching d-none"> <div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
<div class="pg-sp-content"> <div class="pg-sp-content">
<div class="row"> <div class="row">
<div class="col-12 pg-sp-icon sql-editor-busy-icon"></div> <div class="col-12 pg-sp-icon sql-editor-busy-icon"></div>
</div> </div>
<div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">{{ _('Loading {0} v{1}...').format(config.APP_NAME, config.APP_VERSION) }}</div></div> <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">{{ _('Loading...') }}</div></div>
</div> </div>
</div> </div>
</div> </div>
@ -427,13 +427,8 @@
// Start the query tool. // Start the query tool.
sqlEditorController.start( sqlEditorController.start(
{{ uniqueId }}, {{ uniqueId }},
{{ is_query_tool }},
"{{ editor_title }}",
script_type_url,
"{{ server_type }}",
{{ url_params|safe}}, {{ url_params|safe}},
'{{ layout|safe }}', '{{ layout|safe }}',
{{ server_ver }}
); );
}); });
{% endblock %} {% endblock %}

View File

@ -291,6 +291,10 @@ input.editor-checkbox:focus {
background-image: url('../img/disconnect.svg'); background-image: url('../img/disconnect.svg');
} }
.connection_status .obtaining-conn {
background-image: url('../img/loading.gif') !important;
}
.icon-commit, .icon-rollback, .icon-save-data-changes, .icon-view-data { .icon-commit, .icon-rollback, .icon-save-data-changes, .icon-view-data {
display: inline-block; display: inline-block;
align-content: center; align-content: center;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -83,7 +83,6 @@ define('tools.querytool', [
this.handler.preferences = this.preferences; this.handler.preferences = this.preferences;
this.connIntervalId = null; this.connIntervalId = null;
this.layout = opts.layout; this.layout = opts.layout;
this.set_server_version(opts.server_ver);
}, },
// Bind all the events // Bind all the events
@ -188,12 +187,14 @@ define('tools.querytool', [
}); });
}, },
set_editor_title: function(title) {
this.$el.find('.editor-title').text(title);
},
// This function is used to render the template. // This function is used to render the template.
render: function() { render: function() {
var self = this; var self = this;
$('.editor-title').text(_.unescape(self.editor_title));
// Updates connection status flag // Updates connection status flag
self.gain_focus = function() { self.gain_focus = function() {
setTimeout(function() { setTimeout(function() {
@ -207,7 +208,6 @@ define('tools.querytool', [
}, 100); }, 100);
}; };
// Create main wcDocker instance // Create main wcDocker instance
self.docker = new wcDocker( self.docker = new wcDocker(
'#editor-panel', { '#editor-panel', {
@ -338,6 +338,10 @@ define('tools.querytool', [
}); });
self.render_history_grid(); self.render_history_grid();
pgBrowser.Events.on('pgadmin:query_tool:connected:'+self.handler.transId, ()=>{
self.fetch_query_history();
});
queryToolNotifications.renderNotificationsGrid(self.notifications_panel); queryToolNotifications.renderNotificationsGrid(self.notifications_panel);
var text_container = $('<textarea id="sql_query_tool" tabindex="-1"></textarea>'); var text_container = $('<textarea id="sql_query_tool" tabindex="-1"></textarea>');
@ -1327,6 +1331,24 @@ define('tools.querytool', [
} }
}, },
fetch_query_history: function() {
let self = this;
$.ajax({
url: url_for('sqleditor.get_query_history', {
'trans_id': self.handler.transId,
}),
method: 'GET',
contentType: 'application/json',
}).done(function(res) {
res.data.result.map((entry) => {
let newEntry = JSON.parse(entry);
newEntry.start_time = new Date(newEntry.start_time);
self.history_collection.add(newEntry);
});
}).fail(function() {
/* history fetch fail should not affect query tool */
});
},
/* This function is responsible to create and render the /* This function is responsible to create and render the
* new backgrid for the history tab. * new backgrid for the history tab.
*/ */
@ -1363,25 +1385,6 @@ define('tools.querytool', [
}); });
} }
// Make ajax call to get history data
$.ajax({
url: url_for('sqleditor.get_query_history', {
'trans_id': self.handler.transId,
}),
method: 'GET',
contentType: 'application/json',
})
.done(function(res) {
res.data.result.map((entry) => {
let newEntry = JSON.parse(entry);
newEntry.start_time = new Date(newEntry.start_time);
self.history_collection.add(newEntry);
});
})
.fail(function() {
/* history fetch fail should not affect query tool */
});
if(!self.handler.is_query_tool) { if(!self.handler.is_query_tool) {
self.historyComponent.setEditorPref({'copy_to_editor':false}); self.historyComponent.setEditorPref({'copy_to_editor':false});
} }
@ -2016,13 +2019,13 @@ define('tools.querytool', [
}, },
initTransaction: function() { initTransaction: function() {
var url_endpoint; var self = this, url_endpoint;
if (this.is_query_tool) { if (self.is_query_tool) {
url_endpoint = 'datagrid.initialize_query_tool'; url_endpoint = 'datagrid.initialize_query_tool';
// If database not present then use Maintenance database // If database not present then use Maintenance database
// We will handle this at server side // We will handle this at server side
if (this.url_params.did) { if (self.url_params.did) {
url_endpoint = 'datagrid.initialize_query_tool_with_did'; url_endpoint = 'datagrid.initialize_query_tool_with_did';
} }
@ -2030,10 +2033,25 @@ define('tools.querytool', [
url_endpoint = 'datagrid.initialize_datagrid'; url_endpoint = 'datagrid.initialize_datagrid';
} }
var baseUrl = url_for(url_endpoint, this.url_params); var baseUrl = url_for(url_endpoint, {
...self.url_params,
'trans_id': self.transId,
});
Datagrid.create_transaction(baseUrl, this, this.is_query_tool, $.ajax({
this.server_type, '', '', '', true); url: baseUrl,
type: 'POST',
data: self.is_query_tool?null:JSON.stringify(self.url_params.sql_filter),
contentType: 'application/json',
}).done((res)=>{
pgBrowser.Events.trigger(
'pgadmin:query_tool:connected:' + self.transId, res.data
);
}).fail((xhr, status, error)=>{
pgBrowser.Events.trigger(
'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
);
});
}, },
handle_connection_lost: function(create_transaction, xhr) { handle_connection_lost: function(create_transaction, xhr) {
@ -2138,12 +2156,10 @@ define('tools.querytool', [
* call the render method of the grid view to render the backgrid * call the render method of the grid view to render the backgrid
* header and loading icon and start execution of the sql query. * header and loading icon and start execution of the sql query.
*/ */
start: function(transId, is_query_tool, editor_title, script_type_url, start: function(transId, url_params, layout) {
server_type, url_params, layout, server_ver
) {
var self = this; var self = this;
self.is_query_tool = is_query_tool; self.is_query_tool = url_params.is_query_tool==='true'?true:false;
self.rows_affected = 0; self.rows_affected = 0;
self.marked_line_no = 0; self.marked_line_no = 0;
self.has_more_rows = false; self.has_more_rows = false;
@ -2151,9 +2167,8 @@ define('tools.querytool', [
self.close_on_save = false; self.close_on_save = false;
self.close_on_idle_transaction = false; self.close_on_idle_transaction = false;
self.last_transaction_status = -1; self.last_transaction_status = -1;
self.server_type = server_type; self.server_type = url_params.server_type;
self.url_params = url_params; self.url_params = url_params;
self.script_type_url = script_type_url;
self.is_transaction_buttons_disabled = true; self.is_transaction_buttons_disabled = true;
// We do not allow to call the start multiple times. // We do not allow to call the start multiple times.
@ -2164,16 +2179,48 @@ define('tools.querytool', [
el: self.container, el: self.container,
handler: self, handler: self,
layout: layout, layout: layout,
server_ver: server_ver,
}); });
self.transId = self.gridView.transId = transId; self.transId = self.gridView.transId = transId;
self.gridView.editor_title = _.unescape(editor_title);
self.gridView.current_file = undefined; self.gridView.current_file = undefined;
// Render the header // Render the header
self.gridView.render(); self.gridView.render();
self.trigger('pgadmin-sqleditor:loading-icon:hide');
self.gridView.set_editor_title(`(${gettext('Obtaining connection...')} ${_.unescape(url_params.title)}`);
let afterConn = function() {
let enableBtns = [];
if(self.is_query_tool){
enableBtns = ['#btn-flash', '#btn-explain', '#btn-explain-analyze'];
} else {
enableBtns = ['#btn-flash'];
}
enableBtns.forEach((selector)=>{
$(selector).prop('disabled', false);
});
$('#btn-conn-status i').removeClass('obtaining-conn');
self.gridView.set_editor_title(_.unescape(url_params.title));
};
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
pgBrowser.Events.on('pgadmin:query_tool:connected_fail:' + transId, afterConn);
pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, (res_data)=>{
self.gridView.set_server_version(res_data.serverVersion);
});
pgBrowser.Events.on('pgadmin:query_tool:connected_fail:' + transId, (xhr, error)=>{
alertify.pgRespErrorNotify(xhr, error);
});
self.initTransaction();
/* wcDocker focuses on window always, and all our shortcuts are /* wcDocker focuses on window always, and all our shortcuts are
* bind to editor-panel. So when we use wcDocker focus, editor-panel * bind to editor-panel. So when we use wcDocker focus, editor-panel
* loses focus and events don't work. * loses focus and events don't work.
@ -2187,9 +2234,9 @@ define('tools.querytool', [
if (self.is_query_tool) { if (self.is_query_tool) {
// Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT) // Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT)
// Call AJAX only if script type url is present // Call AJAX only if script type url is present
if (script_type_url) { if (url_params.query_url) {
$.ajax({ $.ajax({
url: script_type_url, url: url_params.query_url,
type:'GET', type:'GET',
}) })
.done(function(res) { .done(function(res) {
@ -2224,7 +2271,9 @@ define('tools.querytool', [
cm.className += ' bg-gray-lighter opacity-5 hide-cursor-workaround'; cm.className += ' bg-gray-lighter opacity-5 hide-cursor-workaround';
} }
self.disable_tool_buttons(true); self.disable_tool_buttons(true);
pgBrowser.Events.on('pgadmin:query_tool:connected:'+ transId,()=>{
self.execute_data_query(); self.execute_data_query();
});
} }
}, },
@ -3334,7 +3383,7 @@ define('tools.querytool', [
// Find the title of the visible panel // Find the title of the visible panel
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) { _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
if (p.isVisible()) { if (p.isVisible()) {
self.gridView.panel_title = $(p._title).text(); self.gridView.panel_title = $(p._title).html();
} }
}); });

View File

@ -27,7 +27,7 @@ class TestDownloadCSV(BaseTestGenerator):
'Download csv URL with valid query', 'Download csv URL with valid query',
dict( dict(
sql='SELECT 1 as "A",2 as "B",3 as "C"', sql='SELECT 1 as "A",2 as "B",3 as "C"',
init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}', init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
donwload_url="/sqleditor/query_tool/download/{0}", donwload_url="/sqleditor/query_tool/download/{0}",
output_columns='"A","B","C"', output_columns='"A","B","C"',
output_values='1,2,3', output_values='1,2,3',
@ -39,7 +39,7 @@ class TestDownloadCSV(BaseTestGenerator):
'Download csv URL with wrong TX id', 'Download csv URL with wrong TX id',
dict( dict(
sql='SELECT 1 as "A",2 as "B",3 as "C"', sql='SELECT 1 as "A",2 as "B",3 as "C"',
init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}', init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
donwload_url="/sqleditor/query_tool/download/{0}", donwload_url="/sqleditor/query_tool/download/{0}",
output_columns=None, output_columns=None,
output_values=None, output_values=None,
@ -51,7 +51,7 @@ class TestDownloadCSV(BaseTestGenerator):
'Download csv URL with wrong query', 'Download csv URL with wrong query',
dict( dict(
sql='SELECT * FROM this_table_does_not_exist', sql='SELECT * FROM this_table_does_not_exist',
init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}', init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
donwload_url="/sqleditor/query_tool/download/{0}", donwload_url="/sqleditor/query_tool/download/{0}",
output_columns=None, output_columns=None,
output_values=None, output_values=None,
@ -81,13 +81,12 @@ class TestDownloadCSV(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
# Initialize query tool # Initialize query tool
self.trans_id = str(random.randint(1, 9999999))
url = self.init_url.format( url = self.init_url.format(
test_utils.SERVER_GROUP, self._sid, self._did) self.trans_id, test_utils.SERVER_GROUP, self._sid, self._did)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
# If invalid tx test then make the Tx id invalid so that tests fails # If invalid tx test then make the Tx id invalid so that tests fails
if not self.is_valid_tx: if not self.is_valid_tx:
self.trans_id = self.trans_id + '007' self.trans_id = self.trans_id + '007'

View File

@ -14,6 +14,7 @@ from pgadmin.browser.server_groups.servers.databases.tests import utils as \
from pgadmin.utils.route import BaseTestGenerator from pgadmin.utils.route import BaseTestGenerator
from regression import parent_node_dict from regression import parent_node_dict
from regression.python_test_utils import test_utils as utils from regression.python_test_utils import test_utils as utils
import random
class TestEditorHistory(BaseTestGenerator): class TestEditorHistory(BaseTestGenerator):
@ -68,14 +69,12 @@ class TestEditorHistory(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
# Initialize query tool # Initialize query tool
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
def runTest(self): def runTest(self):
url = '/sqleditor/query_history/{0}'.format(self.trans_id) url = '/sqleditor/query_history/{0}'.format(self.trans_id)

View File

@ -11,7 +11,6 @@
from pgadmin.utils.route import BaseTestGenerator from pgadmin.utils.route import BaseTestGenerator
from pgadmin.browser.server_groups.servers.databases.tests import utils as \ from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils database_utils
from regression import parent_node_dict
from regression.python_test_utils import test_utils from regression.python_test_utils import test_utils
import json import json
from pgadmin.utils import server_utils, IS_PY2 from pgadmin.utils import server_utils, IS_PY2
@ -256,14 +255,13 @@ class TestEncodingCharset(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
# Initialize query tool # Initialize query tool
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
test_utils.SERVER_GROUP, self.encode_sid, self.encode_did) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'\
.format(self.trans_id, test_utils.SERVER_GROUP, self.encode_sid,
self.encode_did)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
# Check character # Check character
url = "/sqleditor/query_tool/start/{0}".format(self.trans_id) url = "/sqleditor/query_tool/start/{0}".format(self.trans_id)
sql = "select E'{0}';".format(self.test_str) sql = "select E'{0}';".format(self.test_str)

View File

@ -8,6 +8,7 @@
########################################################################## ##########################################################################
import json import json
import random
from pgadmin.browser.server_groups.servers.databases.tests import utils as \ from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils database_utils
@ -32,14 +33,12 @@ class TestExplainPlan(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
# Initialize query tool # Initialize query tool
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
# Start query tool transaction # Start query tool transaction
url = '/sqleditor/query_tool/start/{0}'.format(self.trans_id) url = '/sqleditor/query_tool/start/{0}'.format(self.trans_id)
response = self.tester.post( response = self.tester.post(

View File

@ -14,6 +14,7 @@ from pgadmin.browser.server_groups.servers.databases.tests import utils as \
from pgadmin.utils.route import BaseTestGenerator from pgadmin.utils.route import BaseTestGenerator
from regression import parent_node_dict from regression import parent_node_dict
from regression.python_test_utils import test_utils as utils from regression.python_test_utils import test_utils as utils
import random
class TestPollQueryTool(BaseTestGenerator): class TestPollQueryTool(BaseTestGenerator):
@ -75,14 +76,12 @@ NOTICE: Hello, world!
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
# Initialize query tool # Initialize query tool
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
cnt = 0 cnt = 0
for s in self.sql: for s in self.sql:
print("Executing and polling with: " + self.print_messages[cnt]) print("Executing and polling with: " + self.print_messages[cnt])

View File

@ -301,14 +301,12 @@ class TestTransactionControl(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
def _initialize_query_tool(self): def _initialize_query_tool(self):
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
def _initialize_urls(self): def _initialize_urls(self):
self.start_query_tool_url = \ self.start_query_tool_url = \
'/sqleditor/query_tool/start/{0}'.format(self.trans_id) '/sqleditor/query_tool/start/{0}'.format(self.trans_id)

View File

@ -70,15 +70,14 @@ class TestViewData(BaseTestGenerator):
table_id = result[0][0] table_id = result[0][0]
# Initialize query tool # Initialize query tool
url = '/datagrid/initialize/datagrid/3/table/{0}/{1}/{2}/{3}'.format( self.trans_id = str(random.randint(1, 9999999))
test_utils.SERVER_GROUP, self.server_id, self.db_id, table_id) url = '/datagrid/initialize/datagrid/{0}/3/table/{1}/{2}/{3}/{4}'\
.format(self.trans_id, test_utils.SERVER_GROUP, self.server_id,
self.db_id, table_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
url = "/sqleditor/view_data/start/{0}".format(self.trans_id) url = "/sqleditor/view_data/start/{0}".format(self.trans_id)
response = self.tester.get(url) response = self.tester.get(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)

View File

@ -149,14 +149,12 @@ class TestQueryUpdatableResultset(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
def _initialize_query_tool(self): def _initialize_query_tool(self):
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
def _initialize_urls(self): def _initialize_urls(self):
self.start_query_tool_url = \ self.start_query_tool_url = \
'/sqleditor/query_tool/start/{0}'.format(self.trans_id) '/sqleditor/query_tool/start/{0}'.format(self.trans_id)

View File

@ -323,14 +323,12 @@ class TestSaveChangedData(BaseTestGenerator):
raise Exception("Could not connect to the database.") raise Exception("Could not connect to the database.")
def _initialize_query_tool(self): def _initialize_query_tool(self):
url = '/datagrid/initialize/query_tool/{0}/{1}/{2}'.format( self.trans_id = str(random.randint(1, 9999999))
utils.SERVER_GROUP, self.server_id, self.db_id) url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
response = self.tester.post(url) response = self.tester.post(url)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data.decode('utf-8'))
self.trans_id = response_data['data']['gridTransId']
def _initialize_urls_and_select_sql(self): def _initialize_urls_and_select_sql(self):
self.start_query_tool_url = \ self.start_query_tool_url = \
'/sqleditor/query_tool/start/{0}'.format(self.trans_id) '/sqleditor/query_tool/start/{0}'.format(self.trans_id)

View File

@ -17,10 +17,11 @@ describe('#show_data', () => {
let datagrid; let datagrid;
let pgBrowser; let pgBrowser;
let alertify; let alertify;
let transId = 98765432;
beforeEach(() => { beforeEach(() => {
alertify = jasmine.createSpyObj('alertify', ['alert']); alertify = jasmine.createSpyObj('alertify', ['alert']);
datagrid = { datagrid = {
create_transaction: jasmine.createSpy('create_transaction'), launch_grid: jasmine.createSpy('launch_grid'),
}; };
pgBrowser = { pgBrowser = {
treeMenu: new TreeFake(), treeMenu: new TreeFake(),
@ -98,12 +99,12 @@ describe('#show_data', () => {
context('cannot find the tree node', () => { context('cannot find the tree node', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]); showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}], transId);
expect(datagrid.create_transaction).not.toHaveBeenCalled(); expect(datagrid.launch_grid).not.toHaveBeenCalled();
}); });
it('display alert', () => { it('display alert', () => {
showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}]); showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}], transId);
expect(alertify.alert).toHaveBeenCalledWith( expect(alertify.alert).toHaveBeenCalledWith(
'Data Grid Error', 'Data Grid Error',
'No object selected.' 'No object selected.'
@ -113,59 +114,52 @@ describe('#show_data', () => {
context('current node is not underneath a server', () => { context('current node is not underneath a server', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}]); showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}], transId);
expect(datagrid.create_transaction).not.toHaveBeenCalled(); expect(datagrid.launch_grid).not.toHaveBeenCalled();
}); });
}); });
context('current node is not underneath a schema or view or catalog', () => { context('current node is not underneath a schema or view or catalog', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}]); showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}], transId);
expect(datagrid.create_transaction).not.toHaveBeenCalled(); expect(datagrid.launch_grid).not.toHaveBeenCalled();
}); });
}); });
context('current node is underneath a schema', () => { context('current node is underneath a schema', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}]); showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}], transId);
expect(datagrid.create_transaction).toHaveBeenCalledWith(
'/initialize/datagrid/11/schema/1/2/3/4', expect(datagrid.launch_grid).toHaveBeenCalledWith(
null, 98765432,
'false', '/panel/98765432?is_query_tool=false&cmd_type=11&obj_type=schema&obj_id=4&sgid=1&sid=2&did=3&server_type=pg',
'pg', false,
'', 'schema1.schema1/database1/someuser@server1'
'schema1.schema1/database1/someuser@server1',
''
); );
}); });
}); });
context('current node is underneath a view', () => { context('current node is underneath a view', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}]); showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}], transId);
expect(datagrid.create_transaction).toHaveBeenCalledWith(
'/initialize/datagrid/11/view/1/2/3/5', expect(datagrid.launch_grid).toHaveBeenCalledWith(
null, 98765432,
'false', '/panel/98765432?is_query_tool=false&cmd_type=11&obj_type=view&obj_id=5&sgid=1&sid=2&did=3&server_type=pg',
'pg', false,
'', 'view1.view1/database1/someuser@server1'
'view1.view1/database1/someuser@server1',
''
); );
}); });
}); });
context('current node is underneath a catalog', () => { context('current node is underneath a catalog', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}]); showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}], transId);
expect(datagrid.create_transaction).toHaveBeenCalledWith( expect(datagrid.launch_grid).toHaveBeenCalledWith(
'/initialize/datagrid/11/catalog/1/2/3/6', 98765432,
null, '/panel/98765432?is_query_tool=false&cmd_type=11&obj_type=catalog&obj_id=6&sgid=1&sid=2&did=3&server_type=pg',
'false', false,
'pg', 'catalog1.catalog1/database1/someuser@server1'
'',
'catalog1.catalog1/database1/someuser@server1',
''
); );
}); });
}); });

View File

@ -17,10 +17,11 @@ describe('#showQueryTool', () => {
let queryTool; let queryTool;
let pgBrowser; let pgBrowser;
let alertify; let alertify;
let transId = 98765432;
beforeEach(() => { beforeEach(() => {
alertify = jasmine.createSpyObj('alertify', ['alert']); alertify = jasmine.createSpyObj('alertify', ['alert']);
queryTool = { queryTool = {
create_transaction: jasmine.createSpy('create_transaction'), launch_grid: jasmine.createSpy('launch_grid'),
}; };
pgBrowser = { pgBrowser = {
treeMenu: new TreeFake(), treeMenu: new TreeFake(),
@ -66,10 +67,10 @@ describe('#showQueryTool', () => {
context('cannot find the tree node', () => { context('cannot find the tree node', () => {
beforeEach(() => { beforeEach(() => {
showQueryTool(queryTool, pgBrowser, alertify, '', [{id: '10'}]); showQueryTool(queryTool, pgBrowser, alertify, '', [{id: '10'}], transId);
}); });
it('does not create a transaction', () => { it('does not create a transaction', () => {
expect(queryTool.create_transaction).not.toHaveBeenCalled(); expect(queryTool.launch_grid).not.toHaveBeenCalled();
}); });
it('display alert', () => { it('display alert', () => {
@ -82,8 +83,8 @@ describe('#showQueryTool', () => {
context('current node is not underneath a server', () => { context('current node is not underneath a server', () => {
it('does not create a transaction', () => { it('does not create a transaction', () => {
showQueryTool(queryTool, pgBrowser, alertify, '', [{id: 'parent'}], 'title'); showQueryTool(queryTool, pgBrowser, alertify, '', [{id: 'parent'}], transId);
expect(queryTool.create_transaction).not.toHaveBeenCalled(); expect(queryTool.launch_grid).not.toHaveBeenCalled();
}); });
it('no alert is displayed', () => { it('no alert is displayed', () => {
@ -94,32 +95,26 @@ describe('#showQueryTool', () => {
context('current node is underneath a server', () => { context('current node is underneath a server', () => {
context('current node is not underneath a database', () => { context('current node is not underneath a database', () => {
it('creates a transaction', () => { it('creates a transaction', () => {
showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'server1'}]); showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'server1'}], transId);
expect(queryTool.create_transaction).toHaveBeenCalledWith( expect(queryTool.launch_grid).toHaveBeenCalledWith(
'/initialize/query_tool/1/2', 98765432,
null, '/panel/98765432?is_query_tool=true&sgid=1&sid=2&server_type=pg',
'true', true,
'pg',
'http://someurl',
'otherdblabel/someuser@server1', 'otherdblabel/someuser@server1',
'', 'http://someurl'
false
); );
}); });
}); });
context('current node is underneath a database', () => { context('current node is underneath a database', () => {
it('creates a transaction', () => { it('creates a transaction', () => {
showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'database1'}], 'title'); showQueryTool(queryTool, pgBrowser, alertify, 'http://someurl', [{id: 'database1'}], transId);
expect(queryTool.create_transaction).toHaveBeenCalledWith( expect(queryTool.launch_grid).toHaveBeenCalledWith(
'/initialize/query_tool/1/2/3', 98765432,
null, '/panel/98765432?is_query_tool=true&sgid=1&sid=2&server_type=pg&did=3',
'true', true,
'pg',
'http://someurl',
'database1/someuser@server1', 'database1/someuser@server1',
'', 'http://someurl'
false
); );
}); });
}); });

View File

@ -18,5 +18,6 @@ define(function () {
'datagrid.initialize_query_tool': '/initialize/query_tool/<int:sgid>/<int:sid>', 'datagrid.initialize_query_tool': '/initialize/query_tool/<int:sgid>/<int:sid>',
'datagrid.initialize_query_tool_with_did': '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>', 'datagrid.initialize_query_tool_with_did': '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>',
'restore.create_job': '/restore/job/<int:sid>', 'restore.create_job': '/restore/job/<int:sid>',
'datagrid.panel': '/panel/<int:trans_id>',
}; };
}); });

View File

@ -7,7 +7,7 @@
// //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
import { getEpoch, getGCD, getMod, quote_ident, parseFuncParams } from 'sources/utils'; import { getEpoch, getGCD, getMod, quote_ident, parseFuncParams, getRandomInt } from 'sources/utils';
describe('getEpoch', function () { describe('getEpoch', function () {
it('should return non zero', function () { it('should return non zero', function () {
@ -135,3 +135,10 @@ describe('parseFuncParams', function () {
expect(parseFuncParams(funcLabel)).toEqual(expectedObj); expect(parseFuncParams(funcLabel)).toEqual(expectedObj);
}); });
}); });
describe('getRandomInt', function () {
it('is between', function () {
let id = getRandomInt(1, 9999999);
expect(1 <= id && id <= 9999999).toBeTruthy();
});
});

View File

@ -36,61 +36,5 @@ define(['sources/sqleditor_utils'],
expect(SqlEditorUtils.calcFontSize(2)).toEqual('2em'); expect(SqlEditorUtils.calcFontSize(2)).toEqual('2em');
}); });
}); });
describe('Remove the slashes', function () {
it('it will remove the slashes', function () {
expect(
SqlEditorUtils.removeSlashInTheString('/')
).toEqual({
'slashLocations': '0',
'title': '',
});
});
it('it will remove if slashes are present', function () {
expect(
SqlEditorUtils.removeSlashInTheString('my/test')
).toEqual({
'slashLocations': '2',
'title': 'mytest',
});
});
it('it will remove all the slashes are present', function () {
expect(
SqlEditorUtils.removeSlashInTheString('my/test/value')
).toEqual({
'slashLocations': '2,7',
'title': 'mytestvalue',
});
});
it('it will remove all the slashes are present', function () {
expect(
SqlEditorUtils.removeSlashInTheString('a/bb/ccc/dddd/eeeee')
).toEqual({
'slashLocations': '1,4,8,13',
'title': 'abbcccddddeeeee',
});
});
it('it will not remove if slash is not present', function () {
expect(
SqlEditorUtils.removeSlashInTheString('mytest')
).toEqual({
'slashLocations': '',
'title': 'mytest',
});
});
it('it will not remove if value is not present', function () {
expect(
SqlEditorUtils.removeSlashInTheString('')
).toEqual({
'slashLocations': '',
'title': '',
});
});
});
}); });
}); });

View File

@ -3519,9 +3519,9 @@ eslint-scope@^4.0.2, eslint-scope@^4.0.3:
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-utils@^1.3.1: eslint-utils@^1.3.1:
version "1.4.0" version "1.4.2"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.0.tgz#e2c3c8dba768425f897cf0f9e51fe2e241485d4c" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab"
integrity sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ== integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==
dependencies: dependencies:
eslint-visitor-keys "^1.0.0" eslint-visitor-keys "^1.0.0"