Fixed an issue where the debugger hangs when stepping into nested function/procedure. #8443
parent
8cf14222ff
commit
8031c35160
|
|
@ -41,6 +41,7 @@ MODULE_NAME = 'debugger'
|
|||
# Constants
|
||||
PLDBG_EXTN = 'pldbgapi'
|
||||
ASYNC_OK = 1
|
||||
BUSY = 3
|
||||
DEBUGGER_SQL_PATH = 'debugger/sql'
|
||||
DEBUGGER_SQL_V1_PATH = 'debugger/sql/v1'
|
||||
DEBUGGER_SQL_V3_PATH = 'debugger/sql/v3'
|
||||
|
|
@ -263,6 +264,9 @@ def index():
|
|||
|
||||
|
||||
def execute_dict_search_path(conn, sql, search_path):
|
||||
if conn.transaction_status() == 1:
|
||||
return True, BUSY
|
||||
|
||||
sql_search = SET_SEARCH_PATH.format(search_path)
|
||||
status, res = conn.execute_void(sql_search)
|
||||
|
||||
|
|
@ -276,6 +280,9 @@ def execute_dict_search_path(conn, sql, search_path):
|
|||
|
||||
|
||||
def execute_async_search_path(conn, sql, search_path):
|
||||
if conn.transaction_status() == 1:
|
||||
return True, BUSY
|
||||
|
||||
sql_search = SET_SEARCH_PATH.format(search_path)
|
||||
status, res = conn.execute_void(sql_search)
|
||||
|
||||
|
|
@ -1214,18 +1221,24 @@ def execute_debugger_query(trans_id, query_type):
|
|||
status, result = execute_async_search_path(
|
||||
conn, sql, de_inst.debugger_data['search_path'])
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
if result and 'select() failed waiting for target' in result:
|
||||
status = True
|
||||
result = None
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
return make_json_response(
|
||||
data={'status': status, 'result': result}
|
||||
)
|
||||
|
||||
status, result = execute_dict_search_path(
|
||||
conn, sql, de_inst.debugger_data['search_path'])
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
if query_type == 'abort_target':
|
||||
|
|
@ -1234,6 +1247,11 @@ def execute_debugger_query(trans_id, query_type):
|
|||
data={'status': 'Success', 'result': result}
|
||||
)
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
data={'status': 'Success', 'result': result['rows']}
|
||||
)
|
||||
|
|
@ -1356,6 +1374,11 @@ def start_execution(trans_id, port_num):
|
|||
if not status_port:
|
||||
return internal_server_error(errormsg=res_port)
|
||||
|
||||
if res_port == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
de_inst.debugger_data['restart_debug'] = 0
|
||||
de_inst.debugger_data['frame_id'] = 0
|
||||
de_inst.debugger_data['exe_conn_id'] = exe_conn_id
|
||||
|
|
@ -1430,6 +1453,11 @@ def set_clear_breakpoint(trans_id, line_no, set_type):
|
|||
if not status:
|
||||
return internal_server_error(errormsg=res_stack)
|
||||
|
||||
if res_stack == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
# For multilevel function debugging, we need to fetch current selected
|
||||
# frame's function oid for setting the breakpoint. For single function
|
||||
# the frame id will be 0.
|
||||
|
|
@ -1450,9 +1478,16 @@ def set_clear_breakpoint(trans_id, line_no, set_type):
|
|||
|
||||
status, result = execute_dict_search_path(
|
||||
conn, sql, de_inst.debugger_data['search_path'])
|
||||
result = result['rows']
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
result = result['rows']
|
||||
else:
|
||||
status = False
|
||||
result = SERVER_CONNECTION_CLOSED
|
||||
|
|
@ -1536,6 +1571,11 @@ def clear_all_breakpoint(trans_id):
|
|||
conn, sql, de_inst.debugger_data['search_path'])
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
result = result['rows']
|
||||
else:
|
||||
return make_json_response(data={'status': False})
|
||||
|
|
@ -1599,6 +1639,11 @@ def deposit_parameter_value(trans_id):
|
|||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
# Check if value deposited successfully or not and depending on
|
||||
# the result, return the message information.
|
||||
if result['rows'][0]['pldbg_deposit_value']:
|
||||
|
|
@ -1670,6 +1715,12 @@ def select_frame(trans_id, frame_id):
|
|||
|
||||
status, result = execute_dict_search_path(
|
||||
conn, sql, de_inst.debugger_data['search_path'])
|
||||
|
||||
if result == BUSY:
|
||||
return make_json_response(
|
||||
data={'status': 'Busy', 'result': []}
|
||||
)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=result)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -66,21 +66,34 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
eventBus.current.fireEvent(DEBUGGER_EVENTS.GET_TOOL_BAR_BUTTON_STATUS, { disabled: false });
|
||||
};
|
||||
|
||||
const getExecuteQueryUrl = (transId, queryType) => {
|
||||
return url_for('debugger.execute_query', {
|
||||
'trans_id': transId,
|
||||
'query_type': queryType,
|
||||
});
|
||||
};
|
||||
|
||||
const isSuccess = (httpStatus) => {
|
||||
return httpStatus.data.data.status === 'Success';
|
||||
};
|
||||
|
||||
const isBusy = (httpStatus) => {
|
||||
return httpStatus.data.data.status === 'Busy';
|
||||
};
|
||||
|
||||
// Function to get the breakpoint information from the server
|
||||
const getBreakpointInformation = (transId, callBackFunc) => {
|
||||
let result = '';
|
||||
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for('debugger.execute_query', {
|
||||
'trans_id': transId,
|
||||
'query_type': 'get_breakpoints',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(transId, 'get_breakpoints'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isBusy(res)) {
|
||||
getBreakpointInformation(transId, callBackFunc);
|
||||
} else if (isSuccess(res)) {
|
||||
result = res.data.data.result;
|
||||
if (callBackFunc) {
|
||||
callBackFunc(result);
|
||||
|
|
@ -189,11 +202,11 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isSuccess(res)) {
|
||||
enableToolbarButtons();
|
||||
// If status is Success then find the port number to attach the executer.
|
||||
startExecution(transId, res.data.data.result);
|
||||
} else if (res.data.data.status === 'Busy') {
|
||||
} else if (isBusy(res)) {
|
||||
// If status is Busy then poll the result by recursive call to the poll function
|
||||
messages(transId);
|
||||
} else if (res.data.data.status === 'NotConnected') {
|
||||
|
|
@ -224,7 +237,9 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isBusy(res)) {
|
||||
startExecution(transId, port_num);
|
||||
} else if (isSuccess(res)) {
|
||||
// If status is Success then find the port number to attach the executer.
|
||||
executeQuery(transId);
|
||||
} else if (res.data.data.status === 'NotConnected') {
|
||||
|
|
@ -244,17 +259,12 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
|
||||
const executeQuery = (transId) => {
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for(
|
||||
'debugger.execute_query', {
|
||||
'trans_id': transId,
|
||||
'query_type': 'wait_for_breakpoint',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(transId, 'wait_for_breakpoint'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isSuccess(res)) {
|
||||
// set the return code to the code editor text area
|
||||
if (
|
||||
res.data.data.result[0].src != null &&
|
||||
|
|
@ -388,24 +398,26 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
})
|
||||
.catch(raiseClearBrekpointError);
|
||||
};
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for('debugger.execute_query', {
|
||||
'trans_id': params.transId,
|
||||
'query_type': 'get_breakpoints',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
let result = res.data.data.result;
|
||||
clearBreakpoint(result);
|
||||
} else if (res.data.data.status === 'NotConnected') {
|
||||
raiseFetchingBreakpointError();
|
||||
}
|
||||
|
||||
let get_breakpoint = () => {
|
||||
// Make ajax call to listen the database message
|
||||
api({
|
||||
url: getExecuteQueryUrl(params.transId, 'get_breakpoints'),
|
||||
method: 'GET',
|
||||
})
|
||||
.catch(raiseFetchingBreakpointError);
|
||||
.then(function (res) {
|
||||
if (isBusy(res)) {
|
||||
get_breakpoint();
|
||||
} else if (isSuccess(res)) {
|
||||
let result = res.data.data.result;
|
||||
clearBreakpoint(result);
|
||||
} else if (res.data.data.status === 'NotConnected') {
|
||||
raiseFetchingBreakpointError();
|
||||
}
|
||||
})
|
||||
.catch(raiseFetchingBreakpointError);
|
||||
};
|
||||
get_breakpoint();
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -419,13 +431,8 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
const stopDebugging = () => {
|
||||
disableToolbarButtons();
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for(
|
||||
'debugger.execute_query', {
|
||||
'trans_id': params.transId,
|
||||
'query_type': 'abort_target',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(params.transId, 'abort_target'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
|
|
@ -627,7 +634,7 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isSuccess(res)) {
|
||||
if (res.data.data.result == undefined) {
|
||||
/*
|
||||
"result" is undefined only in case of EDB procedure.
|
||||
|
|
@ -655,7 +662,7 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
} else {
|
||||
updateResultAndMessages(res);
|
||||
}
|
||||
} else if (res.data.data.status === 'Busy') {
|
||||
} else if (isBusy(res)) {
|
||||
// If status is Busy then poll the result by recursive call to
|
||||
// the poll function
|
||||
pollEndExecutionResult(transId);
|
||||
|
|
@ -692,16 +699,14 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
restart(params.transId);
|
||||
} else {
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for('debugger.execute_query', {
|
||||
'trans_id': params.transId,
|
||||
'query_type': 'continue',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(params.transId, 'continue'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status) {
|
||||
if (isBusy(res)) {
|
||||
continueDebugger();
|
||||
} else if (res.data.data.status) {
|
||||
pollResult(params.transId);
|
||||
} else {
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
|
|
@ -723,12 +728,8 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
disableToolbarButtons();
|
||||
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for('debugger.execute_query', {
|
||||
'trans_id': params.transId,
|
||||
'query_type': 'step_over',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(params.transId, 'step_over'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
|
|
@ -775,22 +776,21 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
// Get the local variable information of the functions and update the grid
|
||||
const getLocalVariables = (transId) => {
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for(
|
||||
'debugger.execute_query', {
|
||||
'trans_id': transId,
|
||||
'query_type': 'get_variables',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(transId, 'get_variables'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isBusy(res)) {
|
||||
getLocalVariables(transId);
|
||||
}
|
||||
else if (isSuccess(res)) {
|
||||
// Call function to update local variables
|
||||
let variablesResult = res.data.data.result.filter((lvar) => {
|
||||
return lvar.varclass == 'L';
|
||||
});
|
||||
eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_LOCAL_VARIABLES, variablesResult);
|
||||
enableToolbarButtons();
|
||||
|
||||
let parametersResult = res.data.data.result.filter((lvar) => {
|
||||
return lvar.varclass == 'A';
|
||||
|
|
@ -822,17 +822,14 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
|
||||
const getStackInformation = (transId) => {
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for(
|
||||
'debugger.execute_query', {
|
||||
'trans_id': transId,
|
||||
'query_type': 'get_stack_info',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(transId, 'get_stack_info'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isBusy(res)) {
|
||||
getStackInformation(transId);
|
||||
} else if (isSuccess(res)) {
|
||||
// Call function to update stack information
|
||||
eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_STACK, res.data.data.result);
|
||||
// Call function to create and update stack information
|
||||
|
|
@ -867,6 +864,8 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
|
||||
const updateInfo = (res, transId) => {
|
||||
if (!params.directDebugger.debug_type && !params.directDebugger.first_time_indirect_debug) {
|
||||
// Enable all the buttons as we got the results
|
||||
enableToolbarButtons();
|
||||
setLoaderText('');
|
||||
editor.current.setActiveLine(-1);
|
||||
clearAllBreakpoint(transId);
|
||||
|
|
@ -940,16 +939,14 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
},
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status === 'Success') {
|
||||
if (isSuccess(res)) {
|
||||
// If no result then poll again to wait for results.
|
||||
if (res.data.data.result == null || res.data.data.result.length == 0) {
|
||||
pollResult(transId);
|
||||
} else {
|
||||
updateInfo(res, transId);
|
||||
// Enable all the buttons as we got the results
|
||||
enableToolbarButtons();
|
||||
}
|
||||
} else if (res.data.data.status === 'Busy') {
|
||||
} else if (isBusy(res)) {
|
||||
params.directDebugger.polling_timeout_idle = true;
|
||||
checkDebuggerStatus(transId);
|
||||
} else if (res.data.data.status === 'NotConnected') {
|
||||
|
|
@ -967,16 +964,14 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
const stepInto = () => {
|
||||
disableToolbarButtons();
|
||||
// Make ajax call to listen the database message
|
||||
let baseUrl = url_for('debugger.execute_query', {
|
||||
'trans_id': params.transId,
|
||||
'query_type': 'step_into',
|
||||
});
|
||||
api({
|
||||
url: baseUrl,
|
||||
url: getExecuteQueryUrl(params.transId, 'step_into'),
|
||||
method: 'GET',
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status) {
|
||||
if(isBusy(res)) {
|
||||
stepInto();
|
||||
} else if (res.data.data.status) {
|
||||
pollResult(params.transId);
|
||||
} else {
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
|
|
@ -1005,7 +1000,9 @@ export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId,
|
|||
data: data,
|
||||
})
|
||||
.then(function (res) {
|
||||
if (res.data.data.status) {
|
||||
if(isBusy(res)) {
|
||||
onChangesLocalVarParameters(data);
|
||||
} else if (res.data.data.status) {
|
||||
// Get the updated variables value
|
||||
getLocalVariables(params.transId);
|
||||
// Show the message to the user that deposit value is success or failure
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
|||
By.CSS_SELECTOR, "div[data-label='Debugging']")
|
||||
).perform()
|
||||
|
||||
# time.sleep(2)
|
||||
wait.until(EC.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, "li[data-label='Debug']")))
|
||||
|
||||
|
|
@ -87,7 +86,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
|||
|
||||
# We need to check if debugger plugin is installed or not
|
||||
try:
|
||||
wait = WebDriverWait(self.page.driver, 2)
|
||||
wait = WebDriverWait(self.page.driver, 10)
|
||||
is_error = wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, "//div[contains(@class,'MuiDialogTitle-root')]"
|
||||
"//div[text()='Debugger Error']")
|
||||
|
|
@ -120,6 +119,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
|
|||
wait.until(EC.presence_of_element_located(
|
||||
(By.XPATH, "//span[contains(.,'Hello, pgAdmin4')]"))
|
||||
)
|
||||
|
||||
self.page.click_element(
|
||||
self.page.driver.find_elements(By.XPATH, "//button")[2]
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue