Add support for LISTEN/NOTIFY in the query tool. Fixes #3204

This commit is contained in:
Akshay Joshi
2018-05-30 21:58:28 -04:00
committed by Dave Page
parent 2b4605a9d3
commit 38ee39ae7a
11 changed files with 335 additions and 19 deletions

View File

@@ -543,10 +543,12 @@ def poll(trans_id):
# There may be additional messages even if result is present
# eg: Function can provide result as well as RAISE messages
additional_messages = None
notifies = None
if status == 'Success':
messages = conn.messages()
if messages:
additional_messages = ''.join(messages)
notifies = conn.get_notifies()
# Procedure/Function output may comes in the form of Notices from the
# database server, so we need to append those outputs with the
@@ -564,6 +566,7 @@ def poll(trans_id):
'rows_fetched_from': rows_fetched_from,
'rows_fetched_to': rows_fetched_to,
'additional_messages': additional_messages,
'notifies': notifies,
'has_more_rows': has_more_rows,
'colinfo': columns_info,
'primary_keys': primary_keys,
@@ -1476,12 +1479,18 @@ def query_tool_status(trans_id):
if conn and trans_obj and session_obj:
status = conn.transaction_status()
# Check for the asynchronous notifies statements.
conn.check_notifies(True)
notifies = conn.get_notifies()
return make_json_response(
data={
'status': status,
'message': gettext(
CONNECTION_STATUS_MESSAGE_MAPPING.get(status)
)
CONNECTION_STATUS_MESSAGE_MAPPING.get(status),
),
'notifies': notifies
}
)
else:

View File

@@ -20,6 +20,7 @@ define('tools.querytool', [
'react', 'react-dom',
'sources/keyboard_shortcuts',
'sources/sqleditor/query_tool_actions',
'sources/sqleditor/query_tool_notifications',
'pgadmin.datagrid',
'sources/modify_animation',
'sources/sqleditor/calculate_query_run_time',
@@ -36,8 +37,8 @@ define('tools.querytool', [
pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
HistoryBundle, queryHistory, React, ReactDOM,
keyboardShortcuts, queryToolActions, Datagrid, modifyAnimation,
calculateQueryRunTime, callRenderAfterPoll) {
keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
modifyAnimation, calculateQueryRunTime, callRenderAfterPoll) {
/* Return back, this has been called more than once */
if (pgAdmin.SqlEditor)
return pgAdmin.SqlEditor;
@@ -242,19 +243,32 @@ define('tools.querytool', [
content: '<div id ="history_grid" class="sql-editor-history-container" tabindex: "0"></div>',
});
var notifications = new pgAdmin.Browser.Panel({
name: 'notifications',
title: gettext('Notifications'),
width: '100%',
height: '100%',
isCloseable: false,
isPrivate: true,
content: '<div id ="notification_grid" class="sql-editor-notifications" tabindex: "0"></div>',
});
// Load all the created panels
data_output.load(main_docker);
explain.load(main_docker);
messages.load(main_docker);
history.load(main_docker);
notifications.load(main_docker);
// Add all the panels to the docker
self.data_output_panel = main_docker.addPanel('data_output', wcDocker.DOCK.BOTTOM, sql_panel_obj);
self.explain_panel = main_docker.addPanel('explain', wcDocker.DOCK.STACKED, self.data_output_panel);
self.messages_panel = main_docker.addPanel('messages', wcDocker.DOCK.STACKED, self.data_output_panel);
self.history_panel = main_docker.addPanel('history', wcDocker.DOCK.STACKED, self.data_output_panel);
self.notifications_panel = main_docker.addPanel('notifications', wcDocker.DOCK.STACKED, self.data_output_panel);
self.render_history_grid();
queryToolNotifications.renderNotificationsGrid(self.notifications_panel);
if (!self.handler.is_new_browser_tab) {
// Listen on the panel closed event and notify user to save modifications.
@@ -3832,6 +3846,12 @@ define('tools.querytool', [
}
});
},
/* This function is used to raise notify messages and update
* the notification grid.
*/
update_notifications: function (notifications) {
queryToolNotifications.updateNotifications(notifications);
},
});
pgAdmin.SqlEditor = {

View File

@@ -47,6 +47,7 @@ class StartRunningQuery:
transaction_object = pickle.loads(session_obj['command_obj'])
can_edit = False
can_filter = False
notifies = None
if transaction_object is not None and session_obj is not None:
# set fetched row count to 0 as we are executing query again.
transaction_object.update_fetched_row_cnt(0)
@@ -88,6 +89,8 @@ class StartRunningQuery:
can_edit = transaction_object.can_edit()
can_filter = transaction_object.can_filter()
# Get the notifies
notifies = conn.get_notifies()
else:
status = False
result = gettext(
@@ -97,7 +100,8 @@ class StartRunningQuery:
'status': status, 'result': result,
'can_edit': can_edit, 'can_filter': can_filter,
'info_notifier_timeout':
self.blueprint_object.info_notifier_timeout.get()
self.blueprint_object.info_notifier_timeout.get(),
'notifies': notifies
}
)

View File

@@ -117,7 +117,8 @@ class StartRunningQueryTest(BaseTestGenerator):
'not found.',
can_edit=False,
can_filter=False,
info_notifier_timeout=5
info_notifier_timeout=5,
notifies=None
)
),
expect_internal_server_error_called_with=None,
@@ -276,7 +277,8 @@ class StartRunningQueryTest(BaseTestGenerator):
result='async function result output',
can_edit=True,
can_filter=True,
info_notifier_timeout=5
info_notifier_timeout=5,
notifies=None
)
),
expect_internal_server_error_called_with=None,
@@ -319,7 +321,8 @@ class StartRunningQueryTest(BaseTestGenerator):
result='async function result output',
can_edit=True,
can_filter=True,
info_notifier_timeout=5
info_notifier_timeout=5,
notifies=None
)
),
expect_internal_server_error_called_with=None,
@@ -362,7 +365,8 @@ class StartRunningQueryTest(BaseTestGenerator):
result='async function result output',
can_edit=True,
can_filter=True,
info_notifier_timeout=5
info_notifier_timeout=5,
notifies=None
)
),
expect_internal_server_error_called_with=None,
@@ -406,7 +410,8 @@ class StartRunningQueryTest(BaseTestGenerator):
result='async function result output',
can_edit=True,
can_filter=True,
info_notifier_timeout=5
info_notifier_timeout=5,
notifies=None
)
),
expect_internal_server_error_called_with=None,
@@ -511,8 +516,10 @@ class StartRunningQueryTest(BaseTestGenerator):
connect=MagicMock(),
execute_async=MagicMock(),
execute_void=MagicMock(),
get_notifies=MagicMock(),
)
self.connection.connect.return_value = self.connection_connect_return
self.connection.get_notifies.return_value = None
self.connection.execute_async.return_value = \
self.execute_async_return_value
if self.manager_connection_exception is None: