mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-10-24 13:47:39 -05:00
Fix a number of broken connection detection scenarios.
This commit is contained in:
1 parent
d358369ed5
commit
637f3b9d1a
12 files changed
+191
-110
No files matched your search
@@ -444,18 +444,25 @@ define('pgadmin.dashboard', [
|
|||||||
pgAdmin.Dashboard.render_chart(container, data, dataset, sid, did, url, options, counter, refresh);
|
pgAdmin.Dashboard.render_chart(container, data, dataset, sid, did, url, options, counter, refresh);
|
||||||
},
|
},
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
var err = $.parseJSON(xhr.responseText),
|
let err = '';
|
||||||
msg = err.errormsg,
|
let msg = '';
|
||||||
cls;
|
let cls = 'info';
|
||||||
// If we get a 428, it means the server isn't connected
|
|
||||||
if (xhr.status == 428) {
|
if (xhr.readyState === 0) {
|
||||||
if (_.isUndefined(msg) || _.isNull(msg)) {
|
msg = gettext('Not connected to the server or the connection to the server has been closed.');
|
||||||
msg = gettext('Please connect to the selected server to view the graph.');
|
|
||||||
}
|
|
||||||
cls = 'info';
|
|
||||||
} else {
|
} else {
|
||||||
msg = gettext('An error occurred whilst rendering the graph.');
|
err = JSON.parse(xhr.responseText);
|
||||||
cls = 'danger';
|
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.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg = gettext('An error occurred whilst rendering the graph.');
|
||||||
|
cls = 'danger';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(container).addClass('graph-error');
|
$(container).addClass('graph-error');
|
||||||
@@ -576,18 +583,25 @@ define('pgadmin.dashboard', [
|
|||||||
filter.search();
|
filter.search();
|
||||||
},
|
},
|
||||||
error: function(model, xhr) {
|
error: function(model, xhr) {
|
||||||
var err = $.parseJSON(xhr.responseText),
|
let err = '';
|
||||||
msg = err.errormsg,
|
let msg = '';
|
||||||
cls;
|
let cls = 'info';
|
||||||
// If we get a 428, it means the server isn't connected
|
|
||||||
if (xhr.status == 428) {
|
if (xhr.readyState === 0) {
|
||||||
if (_.isUndefined(msg) || _.isNull(msg)) {
|
msg = gettext('Not connected to the server or the connection to the server has been closed.');
|
||||||
msg = gettext('Please connect to the selected server to view the table.');
|
|
||||||
}
|
|
||||||
cls = 'info';
|
|
||||||
} else {
|
} else {
|
||||||
msg = gettext('An error occurred whilst rendering the table.');
|
err = JSON.parse(xhr.responseText);
|
||||||
cls = 'danger';
|
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.');
|
||||||
|
}
|
||||||
|
} 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
|
// Replace the content with the error, if not already present. Always update the message
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ExecuteQuery {
|
|||||||
}, self.sqlServerObject.POLL_FALLBACK_TIME());
|
}, self.sqlServerObject.POLL_FALLBACK_TIME());
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(sqlStatement, explainPlan) {
|
execute(sqlStatement, explainPlan, connect) {
|
||||||
// If it is an empty query, do nothing.
|
// If it is an empty query, do nothing.
|
||||||
if (sqlStatement.length <= 0) return;
|
if (sqlStatement.length <= 0) return;
|
||||||
|
|
||||||
@@ -63,11 +63,8 @@ class ExecuteQuery {
|
|||||||
const sqlStatementWithAnalyze = ExecuteQuery.prepareAnalyzeSql(sqlStatement, explainPlan);
|
const sqlStatementWithAnalyze = ExecuteQuery.prepareAnalyzeSql(sqlStatement, explainPlan);
|
||||||
|
|
||||||
self.initializeExecutionOnSqlEditor(sqlStatementWithAnalyze);
|
self.initializeExecutionOnSqlEditor(sqlStatementWithAnalyze);
|
||||||
|
|
||||||
service.post(
|
service.post(
|
||||||
url_for('sqleditor.query_tool_start', {
|
this.generateURLReconnectionFlag(connect),
|
||||||
'trans_id': self.sqlServerObject.transId,
|
|
||||||
}),
|
|
||||||
JSON.stringify(sqlStatementWithAnalyze),
|
JSON.stringify(sqlStatementWithAnalyze),
|
||||||
{headers: {'Content-Type': 'application/json'}})
|
{headers: {'Content-Type': 'application/json'}})
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
@@ -90,11 +87,22 @@ class ExecuteQuery {
|
|||||||
self.sqlServerObject._highlight_error(httpMessageData.data.result);
|
self.sqlServerObject._highlight_error(httpMessageData.data.result);
|
||||||
}
|
}
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
self.onExecuteHTTPError(error.response.data);
|
self.onExecuteHTTPError(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateURLReconnectionFlag(shouldReconnect) {
|
||||||
|
let url = url_for('sqleditor.query_tool_start', {
|
||||||
|
'trans_id': this.sqlServerObject.transId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldReconnect) {
|
||||||
|
url += '?connect=1';
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
poll() {
|
poll() {
|
||||||
const self = this;
|
const self = this;
|
||||||
let service = axios.create({});
|
let service = axios.create({});
|
||||||
@@ -129,18 +137,21 @@ class ExecuteQuery {
|
|||||||
}
|
}
|
||||||
).catch(
|
).catch(
|
||||||
error => {
|
error => {
|
||||||
const errorData = error.response.data;
|
|
||||||
// Enable/Disable query tool button only if is_query_tool is true.
|
// Enable/Disable query tool button only if is_query_tool is true.
|
||||||
self.sqlServerObject.resetQueryHistoryObject(self.sqlServerObject);
|
self.sqlServerObject.resetQueryHistoryObject(self.sqlServerObject);
|
||||||
|
|
||||||
self.loadingScreen.hide();
|
self.loadingScreen.hide();
|
||||||
if (self.sqlServerObject.is_query_tool) {
|
if (self.sqlServerObject.is_query_tool) {
|
||||||
self.enableSQLEditorButtons();
|
self.enableSQLEditorButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExecuteQuery.wasConnectionLostToServer(errorData)) {
|
if (ExecuteQuery.wasConnectionLostToPythonServer(error.response)) {
|
||||||
self.handleConnectionToServerLost();
|
self.handleConnectionToServerLost();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorData = error.response.data;
|
||||||
|
|
||||||
if (self.userManagement.is_pga_login_required(errorData)) {
|
if (self.userManagement.is_pga_login_required(errorData)) {
|
||||||
return self.userManagement.pga_login();
|
return self.userManagement.pga_login();
|
||||||
}
|
}
|
||||||
@@ -159,7 +170,7 @@ class ExecuteQuery {
|
|||||||
$('#btn-flash').prop('disabled', true);
|
$('#btn-flash').prop('disabled', true);
|
||||||
|
|
||||||
this.sqlServerObject.query_start_time = new Date();
|
this.sqlServerObject.query_start_time = new Date();
|
||||||
if(typeof sqlStatement === 'object') {
|
if (typeof sqlStatement === 'object') {
|
||||||
this.sqlServerObject.query = sqlStatement['sql'];
|
this.sqlServerObject.query = sqlStatement['sql'];
|
||||||
} else {
|
} else {
|
||||||
this.sqlServerObject.query = sqlStatement;
|
this.sqlServerObject.query = sqlStatement;
|
||||||
@@ -182,39 +193,36 @@ class ExecuteQuery {
|
|||||||
this.loadingScreen.hide();
|
this.loadingScreen.hide();
|
||||||
this.enableSQLEditorButtons();
|
this.enableSQLEditorButtons();
|
||||||
|
|
||||||
if (ExecuteQuery.wasConnectionLostToServer(httpMessage)) {
|
if (ExecuteQuery.wasConnectionLostToPythonServer(httpMessage.response)) {
|
||||||
this.handleConnectionToServerLost();
|
this.handleConnectionToServerLost();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.userManagement.is_pga_login_required(httpMessage)) {
|
if (this.userManagement.is_pga_login_required(httpMessage.response)) {
|
||||||
this.sqlServerObject.save_state('execute', [this.explainPlan]);
|
this.sqlServerObject.save_state('execute', [this.explainPlan]);
|
||||||
this.userManagement.pga_login();
|
this.userManagement.pga_login();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.is_new_transaction_required(httpMessage)) {
|
if (transaction.is_new_transaction_required(httpMessage.response)) {
|
||||||
this.sqlServerObject.save_state('execute', [this.explainPlan]);
|
this.sqlServerObject.save_state('execute', [this.explainPlan]);
|
||||||
this.sqlServerObject.init_transaction();
|
this.sqlServerObject.init_transaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = httpMessage.errormsg;
|
if (this.wasDatabaseConnectionLost(httpMessage)) {
|
||||||
if (httpMessage.responseJSON !== undefined) {
|
this.sqlServerObject.save_state('execute', [this.explainPlan]);
|
||||||
if (httpMessage.responseJSON.errormsg !== undefined) {
|
this.sqlServerObject.handle_connection_lost(false, httpMessage);
|
||||||
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;
|
||||||
this.sqlServerObject.update_msg_history(false, msg);
|
this.sqlServerObject.update_msg_history(false, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wasDatabaseConnectionLost(httpMessage) {
|
||||||
|
return httpMessage.response.status === 503 &&
|
||||||
|
httpMessage.response.data.info !== undefined &&
|
||||||
|
httpMessage.response.data.info === 'CONNECTION_LOST';
|
||||||
|
}
|
||||||
|
|
||||||
removeGridViewMarker() {
|
removeGridViewMarker() {
|
||||||
if (this.sqlServerObject.gridView.marker) {
|
if (this.sqlServerObject.gridView.marker) {
|
||||||
this.sqlServerObject.gridView.marker.clear();
|
this.sqlServerObject.gridView.marker.clear();
|
||||||
@@ -236,8 +244,8 @@ class ExecuteQuery {
|
|||||||
$('#btn-cancel-query').prop('disabled', false);
|
$('#btn-cancel-query').prop('disabled', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static wasConnectionLostToServer(errorMessage) {
|
static wasConnectionLostToPythonServer(httpResponse) {
|
||||||
return errorMessage.readyState === 0;
|
return _.isUndefined(httpResponse) || _.isUndefined(httpResponse.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectionToServerLost() {
|
handleConnectionToServerLost() {
|
||||||
|
|||||||
@@ -8,6 +8,15 @@
|
|||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
export function is_new_transaction_required(xhr) {
|
export function is_new_transaction_required(xhr) {
|
||||||
|
/* If responseJSON is undefined then it could be object of
|
||||||
|
* axios(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 &&
|
return xhr.status === 404 && xhr.responseJSON &&
|
||||||
xhr.responseJSON.info &&
|
xhr.responseJSON.info &&
|
||||||
xhr.responseJSON.info === 'DATAGRID_TRANSACTION_REQUIRED';
|
xhr.responseJSON.info === 'DATAGRID_TRANSACTION_REQUIRED';
|
||||||
|
|||||||
@@ -298,8 +298,10 @@ def start_query_tool(trans_id):
|
|||||||
request.data, request.args, request.form
|
request.data, request.args, request.form
|
||||||
)
|
)
|
||||||
|
|
||||||
|
connect = 'connect' in request.args and request.args['connect'] == '1'
|
||||||
|
|
||||||
return StartRunningQuery(blueprint, current_app).execute(
|
return StartRunningQuery(blueprint, current_app).execute(
|
||||||
sql, trans_id, session
|
sql, trans_id, session, connect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1847,13 +1847,21 @@ define('tools.querytool', [
|
|||||||
},
|
},
|
||||||
|
|
||||||
handle_connection_lost: function(create_transaction, xhr) {
|
handle_connection_lost: function(create_transaction, xhr) {
|
||||||
var self= this;
|
/* If responseJSON is undefined then it could be object of
|
||||||
if (xhr.responseJSON.data && !xhr.responseJSON.data.conn_id) {
|
* axios(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.
|
// if conn_id is null then this is maintenance db.
|
||||||
// so attempt connection connect without prompt.
|
// so attempt connection connect without prompt.
|
||||||
self.init_connection(create_transaction);
|
this.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.
|
||||||
|
this.init_connection(create_transaction);
|
||||||
} else {
|
} else {
|
||||||
self.warn_before_continue();
|
this.warn_before_continue();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
warn_before_continue: function() {
|
warn_before_continue: function() {
|
||||||
@@ -3730,7 +3738,7 @@ define('tools.querytool', [
|
|||||||
|
|
||||||
// This function will fetch the sql query from the text box
|
// This function will fetch the sql query from the text box
|
||||||
// and execute the query.
|
// and execute the query.
|
||||||
execute: function(explain_prefix) {
|
execute: function(explain_prefix, shouldReconnect=false) {
|
||||||
var self = this,
|
var self = this,
|
||||||
sql = '';
|
sql = '';
|
||||||
|
|
||||||
@@ -3747,7 +3755,7 @@ define('tools.querytool', [
|
|||||||
sql = self.gridView.query_tool_obj.getValue();
|
sql = self.gridView.query_tool_obj.getValue();
|
||||||
|
|
||||||
const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
|
const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
|
||||||
executeQuery.execute(sql, explain_prefix);
|
executeQuery.execute(sql, explain_prefix, shouldReconnect);
|
||||||
},
|
},
|
||||||
|
|
||||||
/* This function is used to highlight the error line and
|
/* This function is used to highlight the error line and
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ class StartQueryTool(BaseTestGenerator):
|
|||||||
self.assertEquals(response.status, '200 OK')
|
self.assertEquals(response.status, '200 OK')
|
||||||
self.assertEquals(response.data, b'some result')
|
self.assertEquals(response.data, b'some result')
|
||||||
StartRunningQuery_execute_mock \
|
StartRunningQuery_execute_mock \
|
||||||
.assert_called_with('transformed sql', 1234, ANY)
|
.assert_called_with('transformed sql', 1234, ANY, False)
|
||||||
extract_sql_from_network_parameters_mock \
|
extract_sql_from_network_parameters_mock \
|
||||||
.assert_called_with(b'"some sql statement"', ANY, ANY)
|
.assert_called_with(b'"some sql statement"', ANY, ANY)
|
||||||
@@ -36,7 +36,7 @@ class StartRunningQuery:
|
|||||||
self.connection_id = str(random.randint(1, 9999999))
|
self.connection_id = str(random.randint(1, 9999999))
|
||||||
self.logger = logger
|
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(
|
session_obj = StartRunningQuery.retrieve_session_information(
|
||||||
http_session,
|
http_session,
|
||||||
trans_id
|
trans_id
|
||||||
@@ -68,7 +68,7 @@ class StartRunningQuery:
|
|||||||
return internal_server_error(errormsg=str(e))
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
# Connect to the Server if not connected.
|
# Connect to the Server if not connected.
|
||||||
if not conn.connected():
|
if connect and not conn.connected():
|
||||||
status, msg = conn.connect()
|
status, msg = conn.connect()
|
||||||
if not status:
|
if not status:
|
||||||
self.logger.error(msg)
|
self.logger.error(msg)
|
||||||
@@ -108,39 +108,34 @@ class StartRunningQuery:
|
|||||||
self.connection_id = conn_id
|
self.connection_id = conn_id
|
||||||
|
|
||||||
def __execute_query(self, conn, session_obj, sql, trans_id, trans_obj):
|
def __execute_query(self, conn, session_obj, sql, trans_id, trans_obj):
|
||||||
if conn.connected():
|
# on successful connection set the connection id to the
|
||||||
# on successful connection set the connection id to the
|
# transaction object
|
||||||
# transaction object
|
trans_obj.set_connection_id(self.connection_id)
|
||||||
trans_obj.set_connection_id(self.connection_id)
|
|
||||||
|
|
||||||
StartRunningQuery.save_transaction_in_session(session_obj,
|
StartRunningQuery.save_transaction_in_session(session_obj,
|
||||||
trans_id, trans_obj)
|
trans_id, trans_obj)
|
||||||
|
|
||||||
# If auto commit is False and transaction status is Idle
|
# If auto commit is False and transaction status is Idle
|
||||||
# then call is_begin_not_required() function to check BEGIN
|
# then call is_begin_not_required() function to check BEGIN
|
||||||
# is required or not.
|
# is required or not.
|
||||||
|
|
||||||
if StartRunningQuery.is_begin_required_for_sql_query(trans_obj,
|
if StartRunningQuery.is_begin_required_for_sql_query(trans_obj,
|
||||||
conn, sql):
|
conn, sql):
|
||||||
conn.execute_void("BEGIN;")
|
conn.execute_void("BEGIN;")
|
||||||
|
|
||||||
# Execute sql asynchronously with params is None
|
# Execute sql asynchronously with params is None
|
||||||
# and formatted_error is True.
|
# and formatted_error is True.
|
||||||
try:
|
try:
|
||||||
status, result = conn.execute_async(sql)
|
status, result = conn.execute_async(sql)
|
||||||
except ConnectionLost:
|
except ConnectionLost:
|
||||||
raise
|
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;")
|
||||||
|
|
||||||
# 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
|
return result, status
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ else:
|
|||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
get_driver_exception = Exception('get_driver exception')
|
get_driver_exception = Exception('get_driver exception')
|
||||||
|
get_connection_lost_exception = Exception('Unable to connect to server')
|
||||||
|
|
||||||
|
|
||||||
class StartRunningQueryTest(BaseTestGenerator):
|
class StartRunningQueryTest(BaseTestGenerator):
|
||||||
@@ -38,6 +39,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
pickle_load_return=None,
|
pickle_load_return=None,
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -67,6 +69,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
pickle_load_return=None,
|
pickle_load_return=None,
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -97,6 +100,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
pickle_load_return=None,
|
pickle_load_return=None,
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -131,6 +135,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
pickle_load_return=MagicMock(conn_id=1,
|
pickle_load_return=MagicMock(conn_id=1,
|
||||||
update_fetched_row_cnt=MagicMock()),
|
update_fetched_row_cnt=MagicMock()),
|
||||||
get_driver_exception=True,
|
get_driver_exception=True,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -161,6 +166,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
update_fetched_row_cnt=MagicMock()
|
update_fetched_row_cnt=MagicMock()
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=ConnectionLost('1', '2', '3'),
|
manager_connection_exception=ConnectionLost('1', '2', '3'),
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -188,6 +194,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
update_fetched_row_cnt=MagicMock()
|
update_fetched_row_cnt=MagicMock()
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=True,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=False,
|
is_connected_to_server=False,
|
||||||
@@ -202,7 +209,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
expect_internal_server_error_called_with=dict(
|
expect_internal_server_error_called_with=dict(
|
||||||
errormsg='Unable to connect to server'
|
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',
|
expect_execute_void_called_with='some sql',
|
||||||
)),
|
)),
|
||||||
('When server is connected and start query async request, '
|
('When server is connected and start query async request, '
|
||||||
@@ -223,6 +230,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
can_filter=lambda: True
|
can_filter=lambda: True
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=True,
|
is_connected_to_server=True,
|
||||||
@@ -265,6 +273,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
can_filter=lambda: True
|
can_filter=lambda: True
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=True,
|
is_connected_to_server=True,
|
||||||
@@ -307,6 +316,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
can_filter=lambda: True
|
can_filter=lambda: True
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=True,
|
is_connected_to_server=True,
|
||||||
@@ -349,6 +359,7 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
can_filter=lambda: True
|
can_filter=lambda: True
|
||||||
),
|
),
|
||||||
get_driver_exception=False,
|
get_driver_exception=False,
|
||||||
|
get_connection_lost_exception=False,
|
||||||
manager_connection_exception=None,
|
manager_connection_exception=None,
|
||||||
|
|
||||||
is_connected_to_server=True,
|
is_connected_to_server=True,
|
||||||
@@ -431,6 +442,8 @@ class StartRunningQueryTest(BaseTestGenerator):
|
|||||||
manager = self.__create_manager()
|
manager = self.__create_manager()
|
||||||
if self.get_driver_exception:
|
if self.get_driver_exception:
|
||||||
get_driver_mock.side_effect = 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:
|
else:
|
||||||
get_driver_mock.return_value = MagicMock(
|
get_driver_mock.return_value = MagicMock(
|
||||||
connection_manager=lambda session_id: manager)
|
connection_manager=lambda session_id: manager)
|
||||||
|
|||||||
@@ -130,9 +130,18 @@ define([
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_pga_login_required(xhr) {
|
is_pga_login_required(xhr) {
|
||||||
return xhr.status == 401 && xhr.responseJSON &&
|
/* If responseJSON is undefined then it could be object of
|
||||||
|
* axios(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 &&
|
||||||
xhr.responseJSON.info == 'PGADMIN_LOGIN_REQUIRED';
|
xhr.responseJSON.info === 'PGADMIN_LOGIN_REQUIRED';
|
||||||
},
|
},
|
||||||
|
|
||||||
// Callback to draw pgAdmin4 login dialog.
|
// Callback to draw pgAdmin4 login dialog.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from flask_babel import gettext
|
|||||||
from pgadmin.utils.crypto import decrypt
|
from pgadmin.utils.crypto import decrypt
|
||||||
from .connection import Connection
|
from .connection import Connection
|
||||||
from pgadmin.model import Server
|
from pgadmin.model import Server
|
||||||
|
from pgadmin.utils.exception import ConnectionLost
|
||||||
|
|
||||||
|
|
||||||
class ServerManager(object):
|
class ServerManager(object):
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ describe('ExecuteQuery', () => {
|
|||||||
'_init_polling_flags',
|
'_init_polling_flags',
|
||||||
'save_state',
|
'save_state',
|
||||||
'init_transaction',
|
'init_transaction',
|
||||||
|
'handle_connection_lost',
|
||||||
]);
|
]);
|
||||||
sqlEditorMock.transId = 123;
|
sqlEditorMock.transId = 123;
|
||||||
sqlEditorMock.rows_affected = 1000;
|
sqlEditorMock.rows_affected = 1000;
|
||||||
@@ -49,6 +50,10 @@ describe('ExecuteQuery', () => {
|
|||||||
isNewTransactionRequiredMock = spyOn(transaction, 'is_new_transaction_required');
|
isNewTransactionRequiredMock = spyOn(transaction, 'is_new_transaction_required');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
networkMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
describe('#poll', () => {
|
describe('#poll', () => {
|
||||||
let cancelButtonSpy;
|
let cancelButtonSpy;
|
||||||
let response;
|
let response;
|
||||||
@@ -62,9 +67,6 @@ describe('ExecuteQuery', () => {
|
|||||||
executeQuery.delayedPoll = jasmine.createSpy('ExecuteQuery.delayedPoll');
|
executeQuery.delayedPoll = jasmine.createSpy('ExecuteQuery.delayedPoll');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
});
|
|
||||||
|
|
||||||
context('when SQLEditor is the query tool', () => {
|
context('when SQLEditor is the query tool', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sqlEditorMock.is_query_tool = true;
|
sqlEditorMock.is_query_tool = true;
|
||||||
@@ -569,8 +571,7 @@ describe('ExecuteQuery', () => {
|
|||||||
|
|
||||||
describe('when cannot reach the Python Server', () => {
|
describe('when cannot reach the Python Server', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
response = {readyState: 0};
|
networkMock.onGet('/sqleditor/query_tool/poll/123').reply(404, undefined);
|
||||||
networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
|
|
||||||
|
|
||||||
executeQuery.poll();
|
executeQuery.poll();
|
||||||
});
|
});
|
||||||
@@ -972,8 +973,7 @@ describe('ExecuteQuery', () => {
|
|||||||
|
|
||||||
describe('when cannot reach the Python Server', () => {
|
describe('when cannot reach the Python Server', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
response = {readyState: 0};
|
networkMock.onGet('/sqleditor/query_tool/poll/123').reply(404, undefined);
|
||||||
networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
|
|
||||||
|
|
||||||
executeQuery.poll();
|
executeQuery.poll();
|
||||||
});
|
});
|
||||||
@@ -1143,9 +1143,9 @@ describe('ExecuteQuery', () => {
|
|||||||
describe('when HTTP return 200', () => {
|
describe('when HTTP return 200', () => {
|
||||||
describe('when backend informs that query started successfully', () => {
|
describe('when backend informs that query started successfully', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
|
networkMock.onPost('/sqleditor/query_tool/start/123?connect=1').reply(200, response);
|
||||||
pollSpy = spyOn(executeQuery, 'delayedPoll');
|
pollSpy = spyOn(executeQuery, 'delayedPoll');
|
||||||
executeQuery.execute('some sql query', '');
|
executeQuery.execute('some sql query', '', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should changes the loading message to "Waiting for the query execution to complete"', (done) => {
|
it('should changes the loading message to "Waiting for the query execution to complete"', (done) => {
|
||||||
@@ -1310,10 +1310,7 @@ describe('ExecuteQuery', () => {
|
|||||||
|
|
||||||
describe('when cannot reach the Python Server', () => {
|
describe('when cannot reach the Python Server', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
response = {
|
networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, undefined);
|
||||||
readyState: 0,
|
|
||||||
};
|
|
||||||
networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
|
|
||||||
|
|
||||||
|
|
||||||
executeQuery.execute('some sql query', '');
|
executeQuery.execute('some sql query', '');
|
||||||
@@ -1657,7 +1654,32 @@ describe('ExecuteQuery', () => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('when connection to database is lost', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
isNewTransactionRequiredMock.and.returnValue(false);
|
||||||
|
response.info = 'CONNECTION_LOST';
|
||||||
|
networkMock.onAny('/sqleditor/query_tool/start/123').reply(503, response);
|
||||||
|
|
||||||
|
executeQuery.execute('some sql query', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves state', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(sqlEditorMock.save_state).toHaveBeenCalledWith(
|
||||||
|
'execute',
|
||||||
|
['']
|
||||||
|
);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls handle_connection_lost', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(sqlEditorMock.handle_connection_lost).toHaveBeenCalled();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ describe('#is_new_transaction_required', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when status is 404', () => {
|
describe('when status is 404', () => {
|
||||||
describe('when responseJSON is not present', () => {
|
describe('when data is not present', () => {
|
||||||
it('should return false', () => {
|
it('should return false', () => {
|
||||||
expect(is_new_transaction_required({
|
expect(is_new_transaction_required({
|
||||||
status: 404,
|
status: 404,
|
||||||
@@ -27,22 +27,22 @@ describe('#is_new_transaction_required', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when responseJSON is present', () => {
|
describe('when data is present', () => {
|
||||||
describe('when info is not present inside responseJSON', () => {
|
describe('when info is not present inside data', () => {
|
||||||
it('should return false', () => {
|
it('should return false', () => {
|
||||||
expect(is_new_transaction_required({
|
expect(is_new_transaction_required({
|
||||||
status: 404,
|
status: 404,
|
||||||
responseJSON: {},
|
data: {},
|
||||||
})).toBeFalsy();
|
})).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when info is present inside responseJSON', () => {
|
describe('when info is present inside data', () => {
|
||||||
describe('when info value is not "DATAGRID_TRANSACTION_REQUIRED"', () => {
|
describe('when info value is not "DATAGRID_TRANSACTION_REQUIRED"', () => {
|
||||||
it('should return false', () => {
|
it('should return false', () => {
|
||||||
expect(is_new_transaction_required({
|
expect(is_new_transaction_required({
|
||||||
status: 404,
|
status: 404,
|
||||||
responseJSON: {
|
data: {
|
||||||
info: 'some information',
|
info: 'some information',
|
||||||
},
|
},
|
||||||
})).toBe(false);
|
})).toBe(false);
|
||||||
@@ -53,7 +53,7 @@ describe('#is_new_transaction_required', () => {
|
|||||||
it('should return false', () => {
|
it('should return false', () => {
|
||||||
expect(is_new_transaction_required({
|
expect(is_new_transaction_required({
|
||||||
status: 404,
|
status: 404,
|
||||||
responseJSON: {
|
data: {
|
||||||
info: 'DATAGRID_TRANSACTION_REQUIRED',
|
info: 'DATAGRID_TRANSACTION_REQUIRED',
|
||||||
},
|
},
|
||||||
})).toBe(true);
|
})).toBe(true);
|
||||||
|
|||||||
Reference in new issue
Block a user