diff --git a/web/pgadmin/dashboard/static/js/dashboard.js b/web/pgadmin/dashboard/static/js/dashboard.js index dd6ecf9..4d817ad 100644 --- a/web/pgadmin/dashboard/static/js/dashboard.js +++ b/web/pgadmin/dashboard/static/js/dashboard.js @@ -444,18 +444,28 @@ define('pgadmin.dashboard', [ pgAdmin.Dashboard.render_chart(container, data, dataset, sid, did, url, options, counter, refresh); }, error: function(xhr) { - var err = $.parseJSON(xhr.responseText), - msg = err.errormsg, + var err = '', + msg = '', cls; - // If we get a 428, it means the server isn't connected - if (xhr.status == 428) { - if (_.isUndefined(msg) || _.isNull(msg)) { - msg = gettext('Please connect to the selected server to view the graph.'); - } + + if (xhr.readyState == 0) { + msg = gettext('Not connected to the server or the connection to the server has been closed.'); cls = 'info'; - } else { - msg = gettext('An error occurred whilst rendering the graph.'); - cls = 'danger'; + } else + { + err = $.parseJSON(xhr.responseText); + msg = err.errormsg; + + // If we get a 428, it means the server isn't connected + if (xhr.status == 428) { + if (_.isUndefined(msg) || _.isNull(msg)) { + msg = gettext('Please connect to the selected server to view the graph.'); + } + cls = 'info'; + } else { + msg = gettext('An error occurred whilst rendering the graph.'); + cls = 'danger'; + } } $(container).addClass('graph-error'); @@ -576,18 +586,27 @@ define('pgadmin.dashboard', [ filter.search(); }, error: function(model, xhr) { - var err = $.parseJSON(xhr.responseText), - msg = err.errormsg, + var err = '', + msg = '', cls; - // If we get a 428, it means the server isn't connected - if (xhr.status == 428) { - if (_.isUndefined(msg) || _.isNull(msg)) { - msg = gettext('Please connect to the selected server to view the table.'); - } + + if (xhr.readyState == 0) { + msg = gettext('Not connected to the server or the connection to the server has been closed.'); cls = 'info'; } else { - msg = gettext('An error occurred whilst rendering the table.'); - cls = 'danger'; + err = $.parseJSON(xhr.responseText); + msg = err.errormsg; + + // If we get a 428, it means the server isn't connected + if (xhr.status == 428) { + if (_.isUndefined(msg) || _.isNull(msg)) { + msg = gettext('Please connect to the selected server to view the table.'); + } + cls = 'info'; + } else { + msg = gettext('An error occurred whilst rendering the table.'); + cls = 'danger'; + } } // Replace the content with the error, if not already present. Always update the message diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js index e91c9e8..0feb1e6 100644 --- a/web/pgadmin/static/js/sqleditor/execute_query.js +++ b/web/pgadmin/static/js/sqleditor/execute_query.js @@ -52,7 +52,7 @@ class ExecuteQuery { }, self.sqlServerObject.POLL_FALLBACK_TIME()); } - execute(sqlStatement, explainPlan) { + execute(sqlStatement, explainPlan, connect) { // If it is an empty query, do nothing. if (sqlStatement.length <= 0) return; @@ -64,10 +64,20 @@ class ExecuteQuery { self.initializeExecutionOnSqlEditor(sqlStatementWithAnalyze); + let url = url_for('sqleditor.query_tool_start', { + 'trans_id': self.sqlServerObject.transId, + }); + + /* If connect parameter is true, then send this parameter + * as a part of requested url to connect to the server if not + * already connected. + */ + if (connect) { + url += '?connect=1'; + } + service.post( - url_for('sqleditor.query_tool_start', { - 'trans_id': self.sqlServerObject.transId, - }), + url, JSON.stringify(sqlStatementWithAnalyze), {headers: {'Content-Type': 'application/json'}}) .then(function (result) { @@ -90,7 +100,12 @@ class ExecuteQuery { self.sqlServerObject._highlight_error(httpMessageData.data.result); } }).catch(function (error) { - self.onExecuteHTTPError(error.response.data); + // If response is undefined then connection to the server has been lost. + if (error.response != undefined && error.response.data != undefined) { + self.onExecuteHTTPError(error); + } else { + self.handleConnectionToServerLost(); + } } ); } @@ -129,6 +144,12 @@ class ExecuteQuery { } ).catch( error => { + // If response is undefined then connection to the server has been lost. + if (error.response == undefined || error.response.data == undefined) { + self.handleConnectionToServerLost(); + return; + } + const errorData = error.response.data; // Enable/Disable query tool button only if is_query_tool is true. self.sqlServerObject.resetQueryHistoryObject(self.sqlServerObject); @@ -137,7 +158,7 @@ class ExecuteQuery { self.enableSQLEditorButtons(); } - if (ExecuteQuery.wasConnectionLostToServer(errorData)) { + if (ExecuteQuery.wasConnectionLostToServer(error.request)) { self.handleConnectionToServerLost(); return; } @@ -179,40 +200,40 @@ class ExecuteQuery { } onExecuteHTTPError(httpMessage) { - this.loadingScreen.hide(); - this.enableSQLEditorButtons(); + const self = this; - if (ExecuteQuery.wasConnectionLostToServer(httpMessage)) { - this.handleConnectionToServerLost(); + self.loadingScreen.hide(); + self.enableSQLEditorButtons(); + + if (ExecuteQuery.wasConnectionLostToServer(httpMessage.request) || + httpMessage.response == undefined || + httpMessage.response.data == undefined) + { + self.handleConnectionToServerLost(); return; } - if (this.userManagement.is_pga_login_required(httpMessage)) { - this.sqlServerObject.save_state('execute', [this.explainPlan]); - this.userManagement.pga_login(); + if (self.userManagement.is_pga_login_required(httpMessage.response)) { + self.sqlServerObject.save_state('execute', [self.explainPlan]); + self.userManagement.pga_login(); } - if (transaction.is_new_transaction_required(httpMessage)) { - this.sqlServerObject.save_state('execute', [this.explainPlan]); - this.sqlServerObject.init_transaction(); + if (transaction.is_new_transaction_required(httpMessage.response)) { + self.sqlServerObject.save_state('execute', [self.explainPlan]); + self.sqlServerObject.init_transaction(); } - let msg = httpMessage.errormsg; - if (httpMessage.responseJSON !== undefined) { - if (httpMessage.responseJSON.errormsg !== undefined) { - msg = httpMessage.responseJSON.errormsg; - } - - if (httpMessage.status === 503 && httpMessage.responseJSON.info !== undefined && - httpMessage.responseJSON.info === 'CONNECTION_LOST') { - setTimeout(function () { - this.sqlServerObject.save_state('execute', [this.explainPlan]); - this.sqlServerObject.handle_connection_lost(false, httpMessage); - }); - } + let msg = httpMessage.response.data.errormsg; + if (httpMessage.response.status === 503 && + httpMessage.response.data.info !== undefined && + httpMessage.response.data.info === 'CONNECTION_LOST') { + setTimeout(function () { + self.sqlServerObject.save_state('execute', [self.explainPlan]); + self.sqlServerObject.handle_connection_lost(false, httpMessage); + }); } - this.sqlServerObject.update_msg_history(false, msg); + self.sqlServerObject.update_msg_history(false, msg); } removeGridViewMarker() { diff --git a/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js b/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js index 9d83c92..8197ba1 100644 --- a/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js +++ b/web/pgadmin/static/js/sqleditor/is_new_transaction_required.js @@ -8,6 +8,15 @@ ////////////////////////////////////////////////////////////////////////// export function is_new_transaction_required(xhr) { + /* If responseJSON is undefined then it could be object of + * axois(Promise HTTP) response, so we should check accordingly. + */ + if (xhr.responseJSON == undefined && xhr.data != undefined) { + return xhr.status === 404 && xhr.data && + xhr.data.info && + xhr.data.info === 'DATAGRID_TRANSACTION_REQUIRED'; + } + return xhr.status === 404 && xhr.responseJSON && xhr.responseJSON.info && xhr.responseJSON.info === 'DATAGRID_TRANSACTION_REQUIRED'; diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index cc0aa4c..ea9a973 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -298,8 +298,11 @@ def start_query_tool(trans_id): request.data, request.args, request.form ) + connect = True if 'connect' in request.args and \ + request.args['connect'] == '1' else False + return StartRunningQuery(blueprint, current_app).execute( - sql, trans_id, session + sql, trans_id, session, connect ) diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index ff5138d..0e07a14 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -1848,7 +1848,17 @@ define('tools.querytool', [ handle_connection_lost: function(create_transaction, xhr) { var self= this; - if (xhr.responseJSON.data && !xhr.responseJSON.data.conn_id) { + + /* If responseJSON is undefined then it could be object of + * axois(Promise HTTP) response, so we should check accordingly. + */ + if (xhr.responseJSON != undefined && + xhr.responseJSON.data && !xhr.responseJSON.data.conn_id) { + // if conn_id is null then this is maintenance db. + // so attempt connection connect without prompt. + self.init_connection(create_transaction); + } else if (xhr.data != undefined && + xhr.data.data && !xhr.data.data.conn_id) { // if conn_id is null then this is maintenance db. // so attempt connection connect without prompt. self.init_connection(create_transaction); @@ -3747,7 +3757,13 @@ define('tools.querytool', [ sql = self.gridView.query_tool_obj.getValue(); const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement); - executeQuery.execute(sql, explain_prefix); + + if (arguments.length > 0 && + arguments[arguments.length - 1] == 'connect') { + executeQuery.execute(sql, explain_prefix, true); + } else { + executeQuery.execute(sql, explain_prefix); + } }, /* This function is used to highlight the error line and diff --git a/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py b/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py index 2a50259..c2ff21c 100644 --- a/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py +++ b/web/pgadmin/tools/sqleditor/tests/test_start_query_tool.py @@ -42,6 +42,6 @@ class StartQueryTool(BaseTestGenerator): self.assertEquals(response.status, '200 OK') self.assertEquals(response.data, b'some result') StartRunningQuery_execute_mock \ - .assert_called_with('transformed sql', 1234, ANY) + .assert_called_with('transformed sql', 1234, ANY, False) extract_sql_from_network_parameters_mock \ .assert_called_with(b'"some sql statement"', ANY, ANY) diff --git a/web/pgadmin/tools/sqleditor/utils/start_running_query.py b/web/pgadmin/tools/sqleditor/utils/start_running_query.py index 11b946b..3b3c502 100644 --- a/web/pgadmin/tools/sqleditor/utils/start_running_query.py +++ b/web/pgadmin/tools/sqleditor/utils/start_running_query.py @@ -36,7 +36,7 @@ class StartRunningQuery: self.connection_id = str(random.randint(1, 9999999)) self.logger = logger - def execute(self, sql, trans_id, http_session): + def execute(self, sql, trans_id, http_session, connect=False): session_obj = StartRunningQuery.retrieve_session_information( http_session, trans_id @@ -68,7 +68,7 @@ class StartRunningQuery: return internal_server_error(errormsg=str(e)) # Connect to the Server if not connected. - if not conn.connected(): + if connect and not conn.connected(): status, msg = conn.connect() if not status: self.logger.error(msg) @@ -108,39 +108,34 @@ class StartRunningQuery: self.connection_id = conn_id def __execute_query(self, conn, session_obj, sql, trans_id, trans_obj): - if conn.connected(): - # on successful connection set the connection id to the - # transaction object - trans_obj.set_connection_id(self.connection_id) + # on successful connection set the connection id to the + # transaction object + trans_obj.set_connection_id(self.connection_id) + + StartRunningQuery.save_transaction_in_session(session_obj, + trans_id, trans_obj) + + # If auto commit is False and transaction status is Idle + # then call is_begin_not_required() function to check BEGIN + # is required or not. + + if StartRunningQuery.is_begin_required_for_sql_query(trans_obj, + conn, sql): + conn.execute_void("BEGIN;") + + # Execute sql asynchronously with params is None + # and formatted_error is True. + try: + status, result = conn.execute_async(sql) + except ConnectionLost: + raise + + # If the transaction aborted for some reason and + # Auto RollBack is True then issue a rollback to cleanup. + if StartRunningQuery.is_rollback_statement_required(trans_obj, + conn): + conn.execute_void("ROLLBACK;") - StartRunningQuery.save_transaction_in_session(session_obj, - trans_id, trans_obj) - - # If auto commit is False and transaction status is Idle - # then call is_begin_not_required() function to check BEGIN - # is required or not. - - if StartRunningQuery.is_begin_required_for_sql_query(trans_obj, - conn, sql): - conn.execute_void("BEGIN;") - - # Execute sql asynchronously with params is None - # and formatted_error is True. - try: - status, result = conn.execute_async(sql) - except ConnectionLost: - raise - - # If the transaction aborted for some reason and - # Auto RollBack is True then issue a rollback to cleanup. - if StartRunningQuery.is_rollback_statement_required(trans_obj, - conn): - conn.execute_void("ROLLBACK;") - else: - status = False - result = gettext( - 'Not connected to server or connection with the server has ' - 'been closed.') return result, status @staticmethod diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py index 23a5c7f..4ad0891 100644 --- a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py +++ b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py @@ -21,6 +21,7 @@ else: from unittest.mock import patch, MagicMock get_driver_exception = Exception('get_driver exception') +get_connection_lost_exception = Exception('Unable to connect to server') class StartRunningQueryTest(BaseTestGenerator): @@ -38,6 +39,7 @@ class StartRunningQueryTest(BaseTestGenerator): ), pickle_load_return=None, get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=False, @@ -67,6 +69,7 @@ class StartRunningQueryTest(BaseTestGenerator): ), pickle_load_return=None, get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=False, @@ -97,6 +100,7 @@ class StartRunningQueryTest(BaseTestGenerator): ), pickle_load_return=None, get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=False, @@ -131,6 +135,7 @@ class StartRunningQueryTest(BaseTestGenerator): pickle_load_return=MagicMock(conn_id=1, update_fetched_row_cnt=MagicMock()), get_driver_exception=True, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=False, @@ -161,6 +166,7 @@ class StartRunningQueryTest(BaseTestGenerator): update_fetched_row_cnt=MagicMock() ), get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=ConnectionLost('1', '2', '3'), is_connected_to_server=False, @@ -188,6 +194,7 @@ class StartRunningQueryTest(BaseTestGenerator): update_fetched_row_cnt=MagicMock() ), get_driver_exception=False, + get_connection_lost_exception=True, manager_connection_exception=None, is_connected_to_server=False, @@ -202,7 +209,7 @@ class StartRunningQueryTest(BaseTestGenerator): expect_internal_server_error_called_with=dict( errormsg='Unable to connect to server' ), - expected_logger_error='Unable to connect to server', + expected_logger_error=get_connection_lost_exception, expect_execute_void_called_with='some sql', )), ('When server is connected and start query async request, ' @@ -223,6 +230,7 @@ class StartRunningQueryTest(BaseTestGenerator): can_filter=lambda: True ), get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=True, @@ -265,6 +273,7 @@ class StartRunningQueryTest(BaseTestGenerator): can_filter=lambda: True ), get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=True, @@ -307,6 +316,7 @@ class StartRunningQueryTest(BaseTestGenerator): can_filter=lambda: True ), get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=True, @@ -349,6 +359,7 @@ class StartRunningQueryTest(BaseTestGenerator): can_filter=lambda: True ), get_driver_exception=False, + get_connection_lost_exception=False, manager_connection_exception=None, is_connected_to_server=True, @@ -431,6 +442,8 @@ class StartRunningQueryTest(BaseTestGenerator): manager = self.__create_manager() if self.get_driver_exception: get_driver_mock.side_effect = get_driver_exception + elif self.get_connection_lost_exception: + get_driver_mock.side_effect = get_connection_lost_exception else: get_driver_mock.return_value = MagicMock( connection_manager=lambda session_id: manager) diff --git a/web/pgadmin/tools/user_management/static/js/user_management.js b/web/pgadmin/tools/user_management/static/js/user_management.js index 2a3b2f0..3352c15 100644 --- a/web/pgadmin/tools/user_management/static/js/user_management.js +++ b/web/pgadmin/tools/user_management/static/js/user_management.js @@ -130,6 +130,15 @@ define([ }, is_pga_login_required(xhr) { + /* If responseJSON is undefined then it could be object of + * axois(Promise HTTP) response, so we should check accordingly. + */ + if (xhr.responseJSON == undefined && xhr.data != undefined) { + return xhr.status == 401 && xhr.data && + xhr.data.info && + xhr.data.info == 'PGADMIN_LOGIN_REQUIRED'; + } + return xhr.status == 401 && xhr.responseJSON && xhr.responseJSON.info && xhr.responseJSON.info == 'PGADMIN_LOGIN_REQUIRED';