Hi Dave,
Please find updated patch.
On Tue, Jul 18, 2017 at 8:05 PM, Murtuza Zabuawala <murtuza.zabuawala@
enterprisedb.com> wrote:
> Hi Shirley,
>
> On Tue, Jul 18, 2017 at 1:21 AM, Shirley Wang <[email protected]> wrote:
>
>> Hi!
>>
>> I can't seem to get the patch to completely work on my computer, only the
>> close icon shows up plus the dialog and success/error messages, but here
>> are some comments:
>>
>> Because we are just cancelling the active running query, so if the start
> of the session is 'Active' when you cancel it will simply goto 'Idle' stat.
>
>> +1 to Dave's comment about refreshing after the cancel operation
>>
>> I'll fix this.
>
>> - We're working on a patch for updating alerts in the Dashboard tab which
>> updates the grays in the Database activities panel and changes the border
>> around the refresh button and search bar to 1px. This hasn't been submitted
>> yet but just a heads up as you work on the alignment.
>>
>> - Something to consider is how a super user will identify which session
>> should be closed. Is that information there?
>>
>> I think super user can cancel everything except main connection session &
> as Dave mentioned in previous email that background workers in PG10.
>
>> - Are there sessions that should never be closed? If so, do they also
>> need close buttons? (Probably not, because that will lead the user to an
>> error message, which is not fun)
>>
>> In Backgrid, we can not exclude specific column from certain rows if it
> renders in one row then it will render for every row in the grid, What we
> can do is, it will throw an error when user is not eligible to cancel the
> active running query.
>
>> - Perhaps this is a good feature to review with Chethana! :)
>>
>> On Mon, Jul 17, 2017 at 5:37 AM Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> On Mon, Jul 17, 2017 at 3:01 PM, Dave Page <[email protected]> wrote:
>>>
>>>> Hi
>>>>
>>>> On Thu, Jul 13, 2017 at 2:53 PM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>> hiHi,
>>>>>
>>>>> PFA patch to add functionality which will allow super user to cancel
>>>>> long running queries from dashboard.
>>>>> RM#1812
>>>>>
>>>>> *Steps used to test:*
>>>>> 1) Open psql session, Connect to 'test' database on respective server
>>>>> 2) Execute "select pg_sleep(1000);"
>>>>> 3) Open pgAdmin4
>>>>> 4) Connect to respective server
>>>>> 5) Click on Dashboard
>>>>> 6) Check "Sessions" tab under "Server activity" section then look for
>>>>> active sessions for test database.
>>>>> 7) Click on cancel button and cancel the active session
>>>>> 8) Check psql session now, you will see "ERROR: canceling statement
>>>>> due to user request"
>>>>>
>>>>> Some comments:
>>>>
>>>> - The action here is to cancel the active query in the backend, not the
>>>> session - so messages etc. should say things like "Cancel Active Query?"
>>>>
>>>>
>>> - The grid should refresh following the cancel operation.
>>>>
>>>> - Can you fix the vertical alignment while you're working on this? The
>>>> new button really makes the poor alignment stand out.
>>>>
>>>> - This should not be superuser only - regular users should be able to
>>>> cancel their own queries.
>>>>
>>>> - On PG10, background workers are also shown in the dashboard. Should
>>>> we prevent attempts to cancel their work (they'll fail anyway I believe).
>>>>
>>>> Thanks!
>>>>
>>>>
>>> Sure, I'll work on these comments & send updated patch.
>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>
diff --git a/web/pgadmin/dashboard/__init__.py
b/web/pgadmin/dashboard/__init__.py
index b91ccb2..a4c18c9 100644
--- a/web/pgadmin/dashboard/__init__.py
+++ b/web/pgadmin/dashboard/__init__.py
@@ -456,3 +456,30 @@ def config(sid=None):
:return:
"""
return get_data(sid, None, 'config.sql')
+
+
[email protected](
+ '/cancel_session/<int:sid>/<int:pid>', methods=['DELETE']
+)
[email protected](
+ '/cancel_session/<int:sid>/<int:did>/<int:pid>', methods=['DELETE']
+)
+@login_required
+@check_precondition
+def cancel_session(sid=None, did=None, pid=None):
+ """
+ This function cancel the specific session
+ :param sid: server id
+ :param did: database id
+ :param pid: session/process id
+ :return: Response
+ """
+ sql = "SELECT pg_cancel_backend({0});".format(pid)
+ status, res = g.conn.execute_scalar(sql)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ return ajax_response(
+ response=gettext("Success") if res else gettext("Failed"),
+ status=200
+ )
diff --git a/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js
b/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js
index 8bd62d6..205639d 100644
--- a/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js
+++ b/web/pgadmin/dashboard/templates/dashboard/js/dashboard.js
@@ -1,9 +1,11 @@
define('pgadmin.dashboard', [
'sources/url_for', 'sources/gettext', 'require', 'jquery', 'underscore',
- 'pgadmin', 'backbone', 'backgrid', 'flotr2', 'backgrid.filter',
+ 'pgadmin', 'backbone', 'backgrid', 'flotr2', 'alertify',
+ 'sources/alerts/alertify_wrapper', 'backgrid.filter',
'pgadmin.browser', 'bootstrap', 'wcdocker'
],
-function(url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, Flotr) {
+function(url_for, gettext, r, $, _, pgAdmin, Backbone, Backgrid, Flotr,
+ alertify, AlertifyWrapper) {
var wcDocker = window.wcDocker,
pgBrowser = pgAdmin.Browser;
@@ -12,7 +14,81 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
if (pgAdmin.Dashboard)
return;
- var dashboardVisible = true;
+ var dashboardVisible = true,
+ cancel_session_url = '',
+ is_super_user = false,
+ current_user, maintenance_database,
+ is_server_dashboard = false,
+ is_database_dashboard = false;
+
+ // Custom BackGrid cell, Responsible for cancelling active sessions
+ var cancelSessionCell = Backgrid.Extension.DeleteCell.extend({
+ render: function () {
+ this.$el.empty();
+ this.$el.html(
+ "<i class='fa fa-times-circle' data-toggle='tooltip' " +
+ "title='" + gettext('Cancel the current active query') +
+ "'></i>"
+ );
+ this.delegateEvents();
+ return this;
+ },
+ deleteRow: function(e) {
+ var self = this;
+ e.preventDefault();
+
+ var canDeleteRow = Backgrid.callByNeed(
+ self.column.get('canDeleteRow'), self.column, self.model
+ );
+ // If we are not allowed to cancel the query, return from here
+ if(!canDeleteRow)
+ return;
+
+ // This will refresh the grid
+ var refresh_grid = function() {
+ if(is_server_dashboard) {
+ $('#btn_server_activity_refresh').click();
+ } else if(is_database_dashboard) {
+ $('#btn_database_activity_refresh').click();
+ }
+ };
+
+ var title = gettext('Cancel Active Query?'),
+ txtConfirm = gettext('Are you sure you wish to cancel active
query?');
+
+ alertify.confirm(
+ title,
+ txtConfirm,
+ function(evt) {
+ $.ajax({
+ url: cancel_session_url + self.model.get('pid'),
+ type:'DELETE',
+ success: function(res) {
+ var alertifyWrapper = new AlertifyWrapper();
+ if (res == gettext('Success')) {
+ alertifyWrapper.success(gettext('Active query cancelled
successfully.'));
+ refresh_grid();
+ } else {
+ alertifyWrapper.error(gettext('Error during canceling
active query.'));
+ }
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ var alertifyWrapper = new AlertifyWrapper();
+ alertifyWrapper.error(err.errormsg);
+ }
+ } catch (e) {}
+ }
+ });
+ },
+ function(evt) {
+ return true;
+ }
+ );
+ }
+ });
pgAdmin.Dashboard = {
init: function() {
@@ -73,6 +149,20 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
sid = -1, did = -1, b = pgAdmin.Browser,
m = b && b.Nodes[itemData._type];
+ cancel_session_url = url_for('dashboard.index') +
'cancel_session/';
+
+ // Check if user is super user
+ var server = treeHierarchy['server'];
+ maintenance_database = (server && server.db) || null;
+
+ if(server && server.user && server.user.is_superuser) {
+ is_super_user = true;
+ } else {
+ is_super_user = false;
+ // Set current user
+ current_user = (server && server.user) ? server.user.name :
null;
+ }
+
if (m && m.dashboard) {
if (_.isFunction(m.dashboard)) {
url = m.dashboard.apply(
@@ -85,10 +175,16 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
if ('database' in treeHierarchy) {
sid = treeHierarchy.server._id;
did = treeHierarchy.database._id;
+ is_server_dashboard = false;
+ is_database_dashboard = true;
url += sid + '/' + did;
+ cancel_session_url += sid + '/' + did + '/';
} else if ('server' in treeHierarchy) {
sid = treeHierarchy.server._id;
+ is_server_dashboard = true;
+ is_database_dashboard = false;
url += sid;
+ cancel_session_url += sid + '/';
}
}
@@ -486,6 +582,15 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
}]);
}
+ // Add cancel active query button
+ server_activity_columns.unshift({
+ name: "pg-backform-delete", label: "",
+ cell: cancelSessionCell,
+ editable: false, cell_priority: -1,
+ canDeleteRow: pgAdmin.Dashboard.can_cancel_active_query,
+ postgres_version: version
+ });
+
var server_locks_columns = [{
name: "pid",
label: gettext('PID'),
@@ -790,6 +895,14 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
}]);
}
+ database_activity_columns.unshift({
+ name: "pg-backform-delete", label: "",
+ cell: cancelSessionCell,
+ editable: false, cell_priority: -1,
+ canDeleteRow: pgAdmin.Dashboard.can_cancel_active_query,
+ postgres_version: version
+ });
+
var database_locks_columns = [{
name: "pid",
label: gettext('PID'),
@@ -954,6 +1067,52 @@ function(url_for, gettext, r, $, _, pgAdmin, Backbone,
Backgrid, Flotr) {
},
toggleVisibility: function(flag) {
dashboardVisible = flag;
+ },
+ can_cancel_active_query: function(m) {
+ // We will validate if user is allowed to cancel the active query
+ // If there is only one active session means it probably our main
+ // connection session
+ var active_sessions = m.collection.where({'state': 'active'}),
+ alertifyWrapper = new AlertifyWrapper(),
+ pg_version = this.get('postgres_version') || null;
+
+ // With PG10, We have background process showing on dashboard
+ // We will not allow user to cancel them as they will fail with error
+ // anyway, so better usability we will throw our on notification
+
+ // Background processes do not have database field populated
+ if(pg_version && pg_version >= 100000 && !m.get('datname')) {
+ alertifyWrapper.info(
+ gettext('You cannot cancel the postgres\'s internal session.')
+ );
+ return false;
+ // If it is the last active connection on maintenance db then error
out
+ } else if(maintenance_database == m.get('datname') &&
+ m.get('state') == 'active' && active_sessions.length == 1) {
+ alertifyWrapper.error(
+ gettext('You are not allowed to cancel the main active session.')
+ );
+ return false;
+ } else if(m.get('state') == 'idle') {
+ // If this session is already idle then do nothing
+ alertifyWrapper.info(
+ gettext('The session is already in idle state.')
+ );
+ return false;
+ } else if(is_super_user) {
+ // Super user can do anything
+ return true;
+ } else if (current_user && current_user == m.get('usename')) {
+ // Non-super user can cancel only their active queries
+ return true;
+ } else {
+ // Do not allow to cancel someone else session to non-super user
+ var alertifyWrapper = new AlertifyWrapper();
+ alertifyWrapper.error(
+ gettext('You do not have required account privileges.')
+ );
+ return false;
+ }
}
};
diff --git a/web/pgadmin/static/css/bootstrap.overrides.css
b/web/pgadmin/static/css/bootstrap.overrides.css
index a590aa7..6274c74 100755
--- a/web/pgadmin/static/css/bootstrap.overrides.css
+++ b/web/pgadmin/static/css/bootstrap.overrides.css
@@ -71,7 +71,7 @@ iframe {
}
/*
- * Bootstrap 3 remove submenus as they don't work overly well on Mobile. The
+ * Bootstrap 3 remove submenus as they don't work overly well on Mobile. The
* following CSS adds them back in - for our purposes they actually work fine
* on Mobile and are perfectly responsive
*/
@@ -570,7 +570,7 @@ fieldset[disabled] .form-control {
}
.backgrid.presentation td.renderable {
- padding: 3px;
+ padding: 6px 3px 3px 3px;
font-size: 12px;
}
@@ -984,7 +984,7 @@ ul.nav.nav-tabs {
background-position: 0px 2px;
}
-/* This rule will stop Chrome apply highlighting to elements such as DIV's
used as modals */
+/* This rule will stop Chrome apply highlighting to elements such as DIV's
used as modals */
*:focus {
outline: none;
}
@@ -993,7 +993,7 @@ ul.nav.nav-tabs {
.alert-info-panel {
border: 2px solid #a1a1a1;
margin-top: 2em;
- padding: 5px 5px;
+ padding: 5px 5px;
background: #dddddd;
border-radius: 5px;
height: 8em;
@@ -1334,4 +1334,4 @@ body {
color: #333;
font-size: 14px;
font-weight: normal;
-}
\ No newline at end of file
+}
diff --git a/web/pgadmin/static/js/alerts/alertify_wrapper.js
b/web/pgadmin/static/js/alerts/alertify_wrapper.js
index d3e7ed6..10d5f60 100644
--- a/web/pgadmin/static/js/alerts/alertify_wrapper.js
+++ b/web/pgadmin/static/js/alerts/alertify_wrapper.js
@@ -31,10 +31,25 @@ define([
return alert;
};
+ var info = function(message, timeout) {
+ var alertMessage = '\
+ <div class="media alert-info font-blue text-14">\
+ <div class="media-body media-middle">\
+ <div class="alert-icon info-icon">\
+ <i class="fa fa-info" aria-hidden="true"></i>\
+ </div>\
+ <div class="alert-text">' + message + '</div>\
+ </div>\
+ </div>';
+ var alert = alertify.notify(alertMessage, timeout);
+ return alert;
+ };
+
$.extend(this, {
'success': success,
'error': error,
+ 'info': info,
});
};
return AlertifyWrapper;
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/static/scss/_alert.scss
b/web/pgadmin/static/scss/_alert.scss
index 28b5f2a..fdd4546 100644
--- a/web/pgadmin/static/scss/_alert.scss
+++ b/web/pgadmin/static/scss/_alert.scss
@@ -110,6 +110,7 @@ category: alerts
width: 50px;
height: 50px;
font-size: 14px;
+ text-align: center;
}
.success-icon {
@@ -120,36 +121,8 @@ category: alerts
background: #d0021b;
}
-.alert-text {
- display: inline-block;
- padding: 0 12px 0 10px;
-}
-
-.alert-row {
- display: block;
-}
-
-.alert-box {
- padding: 0px;
- display: inline-block;
-}
-
-.alert-icon {
- display: inline-block;
- color: white;
- padding: 15px;
- width: 50px;
- height: 50px;
- font-size: 14px;
- text-align: center;
-}
-
-.success-icon {
- background: #3a773a;
-}
-
-.error-icon {
- background: #d0021b;
+.info-icon {
+ background: #2c76b4;
}
.alert-text {
diff --git a/web/pgadmin/static/scss/_alertify.overrides.scss
b/web/pgadmin/static/scss/_alertify.overrides.scss
index f1987f9..8344956 100644
--- a/web/pgadmin/static/scss/_alertify.overrides.scss
+++ b/web/pgadmin/static/scss/_alertify.overrides.scss
@@ -170,6 +170,12 @@ button.pg-alertify-button {
@extend .ajs-text-smoothing;
}
+.ajs-message.ajs-visible {
+ @extend .bg-blue-1;
+ @extend .border-blue-2;
+ @extend .ajs-text-smoothing;
+}
+
.media-body {
display: table-row;
-}
\ No newline at end of file
+}