Skip to content

Commit b6baa84

Browse files
Handle query execution on View/Edit Data when server is disconnected with server reconnect option. pgadmin-org#8605
1 parent 695f870 commit b6baa84

3 files changed

Lines changed: 94 additions & 25 deletions

File tree

web/pgadmin/tools/sqleditor/__init__.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,9 @@ def start_view_data(trans_id):
825825
if not status and error_msg and type(error_msg) is Response:
826826
return error_msg
827827

828+
# Check if connect is passed in the request.
829+
connect = 'connect' in request.args and request.args['connect'] == '1'
830+
828831
# get the default connection as current connection which is attached to
829832
# trans id holds the cursor which has query result so we cannot use that
830833
# connection to execute another query otherwise we'll lose query result.
@@ -841,19 +844,21 @@ def start_view_data(trans_id):
841844

842845
# Connect to the Server if not connected.
843846
if not default_conn.connected():
844-
# This will check if view/edit data tool connection is lost or not,
845-
# if lost then it will reconnect
846-
status, error_msg, conn, trans_obj, session_obj, response = \
847-
query_tool_connection_check(trans_id)
848-
# This is required for asking user to enter password
849-
# when password is not saved for the server
850-
if response is not None:
851-
return response
847+
if connect:
848+
# This will check if view/edit data tool connection is lost or not,
849+
# if lost then it will reconnect
850+
status, error_msg, conn, trans_obj, session_obj, response = \
851+
query_tool_connection_check(trans_id)
852+
# This is required for asking user to enter password
853+
# when password is not saved for the server
854+
if response is not None:
855+
return response
852856

853857
status, msg = default_conn.connect()
854858
if not status:
855-
return make_json_response(
856-
data={'status': status, 'result': "{}".format(msg)}
859+
return service_unavailable(
860+
gettext("Connection to the server has been lost."),
861+
info="CONNECTION_LOST"
857862
)
858863

859864
if status and conn is not None and \
@@ -1398,6 +1403,8 @@ def save(trans_id):
13981403
changed_data = json.loads(request.data)
13991404
else:
14001405
changed_data = request.args or request.form
1406+
# Check if connect is passed in the request.
1407+
connect = 'connect' in request.args and request.args['connect'] == '1'
14011408

14021409
# Check the transaction and connection status
14031410
status, error_msg, conn, trans_obj, session_obj = \
@@ -1423,10 +1430,21 @@ def save(trans_id):
14231430
}
14241431
)
14251432

1433+
if connect:
1434+
# This will check if view/edit data tool connection is lost or not,
1435+
# if lost then it will reconnect
1436+
status, error_msg, conn, trans_obj, session_obj, response = \
1437+
query_tool_connection_check(trans_id)
1438+
# This is required for asking user to enter password
1439+
# when password is not saved for the server
1440+
if response is not None:
1441+
return response
1442+
14261443
is_error, errmsg, conn = _check_and_connect(trans_obj)
14271444
if is_error:
1428-
return make_json_response(
1429-
data={'status': status, 'result': "{}".format(errmsg)}
1445+
return service_unavailable(
1446+
gettext("Connection to the server has been lost."),
1447+
info="CONNECTION_LOST"
14301448
)
14311449

14321450
status, res, query_results, _rowid = trans_obj.save(

web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,9 +543,8 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
543543
<br />
544544
<span>{gettext('Do you want to continue and establish a new session')}</span>
545545
</p>,
546-
function() {
547-
handleParams?.connectionLostCallback?.();
548-
}, null,
546+
() => handleParams?.connectionLostCallback?.(),
547+
() => handleParams?.cancelCallback?.(),
549548
gettext('Continue'),
550549
gettext('Cancel')
551550
);

web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class ResultSetUtils {
5151
this.historyQuerySource = null;
5252
this.hasQueryCommitted = false;
5353
this.queryToolCtx = queryToolCtx;
54+
this.setLoaderText = null;
5455
}
5556

5657
static generateURLReconnectionFlag(baseUrl, transId, shouldReconnect) {
@@ -445,23 +446,72 @@ export class ResultSetUtils {
445446
);
446447
}
447448

448-
saveData(reqData) {
449+
saveData(reqData, shouldReconnect) {
450+
// Generate the URL with the optional `connect=1` parameter.
451+
const url = ResultSetUtils.generateURLReconnectionFlag('sqleditor.save', this.transId, shouldReconnect);
452+
449453
return this.api.post(
450-
url_for('sqleditor.save', {
451-
'trans_id': this.transId
452-
}),
454+
url,
453455
JSON.stringify(reqData)
454-
).then(response => {
456+
).then((response) => {
455457
if (response.data?.data?.status) {
456458
// Set the commit flag to true if the save was successful
457459
this.hasQueryCommitted = true;
458460
}
459461
return response;
460-
}).catch((error) => {
461-
// Set the commit flag to false if there was an error
462-
this.hasQueryCommitted = false;
463-
throw error;
464-
});
462+
})
463+
.catch(async (error) => {
464+
if (error.response?.status === 428) {
465+
// Handle 428: Show password dialog.
466+
return new Promise((resolve, reject) => {
467+
this.connectServerModal(
468+
error.response?.data?.result,
469+
async (formData) => {
470+
try {
471+
await this.connectServer(
472+
this.queryToolCtx.params.sid,
473+
this.queryToolCtx.params.user,
474+
formData,
475+
async () => {
476+
let retryRespData = await this.saveData(reqData);
477+
// Set the commit flag to true if the save was successful
478+
this.hasQueryCommitted = true;
479+
pgAdmin.Browser.notifier.success(gettext('Server Connected.'));
480+
resolve(retryRespData);
481+
}
482+
);
483+
484+
} catch (retryError) {
485+
reject(retryError);
486+
}
487+
},
488+
() => this.setLoaderText(null)
489+
);
490+
});
491+
} else if (error.response?.status === 503) {
492+
// Handle 503: Fire HANDLE_API_ERROR and wait for connectionLostCallback.
493+
return new Promise((resolve, reject) => {
494+
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error, {
495+
connectionLostCallback: async () => {
496+
try {
497+
// Retry saveData with connect=1
498+
let retryRespData = await this.saveData(reqData, true);
499+
resolve(retryRespData);
500+
} catch (retryError) {
501+
reject(retryError);
502+
}
503+
},
504+
checkTransaction: true,
505+
cancelCallback: () => this.setLoaderText(null)
506+
},
507+
);
508+
});
509+
} else {
510+
// Set the commit flag to false if there was an error
511+
this.hasQueryCommitted = false;
512+
throw error;
513+
}
514+
});
465515
}
466516

467517
async saveResultsToFile(fileName) {
@@ -861,6 +911,8 @@ export function ResultSet() {
861911

862912
rsu.current.setEventBus(eventBus);
863913
rsu.current.setQtPref(queryToolCtx.preferences?.sqleditor);
914+
// To use setLoaderText to the ResultSetUtils.
915+
rsu.current.setLoaderText = setLoaderText;
864916

865917
const isDataChanged = ()=>{
866918
return Boolean(_.size(dataChangeStore.updated) || _.size(dataChangeStore.added) || _.size(dataChangeStore.deleted));

0 commit comments

Comments
 (0)