///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2023, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import { Box } from '@material-ui/core'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import gettext from 'sources/gettext'; import url_for from 'sources/url_for'; import Loader from 'sources/components/Loader'; import Layout, { LayoutHelper } from '../../../../../static/js/helpers/Layout'; import EventBus from '../../../../../static/js/helpers/EventBus'; import getApiInstance from '../../../../../static/js/api_instance'; import Notify from '../../../../../static/js/helpers/Notifier'; import { evalFunc } from '../../../../../static/js/utils'; import { PANELS, DEBUGGER_EVENTS, MENUS } from '../DebuggerConstants'; import { retrieveNodeName } from '../../../../sqleditor/static/js/show_view_data'; import { useModal } from '../../../../../static/js/helpers/ModalProvider'; import DebuggerEditor from './DebuggerEditor'; import DebuggerMessages from './DebuggerMessages'; import { ToolBar } from './ToolBar'; import { Stack } from './Stack'; import { Results } from './Results'; import { LocalVariablesAndParams } from './LocalVariablesAndParams'; import DebuggerArgumentComponent from './DebuggerArgumentComponent'; export const DebuggerContext = React.createContext(); export const DebuggerEventsContext = React.createContext(); export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panel, eventBusObj, layout, params }) { const savedLayout = layout; const containerRef = React.useRef(null); const docker = useRef(null); const api = getApiInstance(); const modal = useModal(); const eventBus = useRef(eventBusObj || (new EventBus())); const [loaderText, setLoaderText] = React.useState(''); const editor = useRef(null); let timeOut = null; const [qtState, _setQtState] = useState({ preferences: { browser: {}, debugger: {}, }, is_new_tab: window.location == window.parent?.location, params: { ...params, node_name: retrieveNodeName(selectedNodeInfo), } }); const setQtState = (state) => { _setQtState((prev) => ({ ...prev, ...evalFunc(null, state, prev) })); }; const disableToolbarButtons = () => { eventBus.current.fireEvent(DEBUGGER_EVENTS.DISABLE_MENU); eventBus.current.fireEvent(DEBUGGER_EVENTS.GET_TOOL_BAR_BUTTON_STATUS, { disabled: true }); }; const enableToolbarButtons = (key = null) => { if (key) { eventBus.current.fireEvent(DEBUGGER_EVENTS.ENABLE_SPECIFIC_MENU, key); } else { eventBus.current.fireEvent(DEBUGGER_EVENTS.ENABLE_MENU); } eventBus.current.fireEvent(DEBUGGER_EVENTS.GET_TOOL_BAR_BUTTON_STATUS, { disabled: false }); }; const reflectPreferences = useCallback(() => { setQtState({ preferences: { browser: pgAdmin.Browser.get_preferences_for_module('browser'), debugger: pgAdmin.Browser.get_preferences_for_module('debugger'), } }); }, []); // 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, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { result = res.data.data.result; if (callBackFunc) { callBackFunc(result); } } else if (res.data.data.status === 'NotConnected') { raiseFetchingBreakpointError(); } }) .catch(raiseFetchingBreakpointError); return result; }; const clearAllBreakpoint = (transId) => { let clearBreakpoint = (br_list) => { // If there is no break point to clear then we should return from here. if ((br_list.length == 1) && (br_list[0].linenumber == -1)) return; disableToolbarButtons(); let breakpoint_list = getBreakpointList(br_list); // Make ajax call to listen the database message let baseUrl = url_for('debugger.clear_all_breakpoint', { 'trans_id': transId, }); api({ url: baseUrl, method: 'POST', data: { 'breakpoint_list': breakpoint_list.lenght > 0 ? breakpoint_list.join() : null, }, }) .then(function (res) { if (res.data.data.status) { executeQuery(transId); setUnsetBreakpoint(res, breakpoint_list); } enableToolbarButtons(); }) .catch(raiseClearBrekpointError); }; getBreakpointInformation(transId, clearBreakpoint); }; const raiseJSONError = (xhr) => { try { let err = xhr.response.data; if (err.success == 0) { Notify.alert(gettext('Debugger Error'), err.errormsg, () => { if (panel) { panel.close(); } }); } } catch (e) { alert(xhr); Notify.alert( gettext('Debugger Error'), gettext('Error while starting debugging listener.') ); } }; const raisePollingError = () => { Notify.alert( gettext('Debugger Error'), gettext('Error while polling result.') ); }; const raiseClearBrekpointError = () => { Notify.alert( gettext('Debugger Error'), gettext('Error while clearing all breakpoint.') ); }; const raiseFetchingBreakpointError = () => { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching breakpoint information.') ); }; const messages = (transId) => { // Make ajax call to listen the database message let baseUrl = url_for('debugger.messages', { 'trans_id': transId, }); api({ url: baseUrl, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { 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') { // If status is Busy then poll the result by recursive call to the poll function messages(transId); } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Not connected to server or connection with the server has been closed.'), res.data.result ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching messages information.') ); }); }; const startExecution = (transId, port_num) => { // Make ajax call to listen the database message let baseUrl = url_for( 'debugger.start_execution', { 'trans_id': transId, 'port_num': port_num, }); api({ url: baseUrl, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { // If status is Success then find the port number to attach the executer. executeQuery(transId); } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while starting debugging session.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while starting debugging session.') ); }); }; 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, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { // set the return code to the code editor text area if ( res.data.data.result[0].src != null && res.data.data.result[0].linenumber != null ) { editor.current.setValue(res.data.data.result[0].src); setActiveLine(res.data.data.result[0].linenumber - 2); } // Call function to create and update Stack information .... getStackInformation(transId); if (params.directDebugger.debug_type) { pollEndExecutionResult(transId); } } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while executing requested debugging information.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while executing requested debugging information.') ); }); }; const setActiveLine = (lineNo) => { /* If lineNo sent, remove active line */ if (lineNo && editor.current.activeLineNo) { editor.current.removeLineClass( editor.current.activeLineNo, 'wrap', 'CodeMirror-activeline-background' ); } /* If lineNo not sent, set it to active line */ if (!lineNo && editor.current.activeLineNo) { lineNo = editor.current.activeLineNo; } /* Set new active line only if positive */ if (lineNo > 0) { editor.current.activeLineNo = lineNo; editor.current.addLineClass( editor.current.activeLineNo, 'wrap', 'CodeMirror-activeline-background' ); /* centerOnLine is codemirror extension in bundle/codemirror.js */ editor.current.centerOnLine(editor.current.activeLineNo); } }; const selectFrame = (frameId) => { // Make ajax call to listen the database message let baseUrl = url_for('debugger.select_frame', { 'trans_id': params.transId, 'frame_id': frameId, }); api({ url: baseUrl, method: 'GET', }) .then(function (res) { if (res.data.data.status) { editor.current.setValue(res.data.data.result[0].src); updateBreakpoint(params.transId, true); setActiveLine(res.data.data.result[0].linenumber - 2); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while selecting frame.') ); }); }; useEffect(() => { let baseUrl = ''; if (params.transId != undefined && !params.directDebugger.debug_type) { // Make ajax call to execute the and start the target for execution baseUrl = url_for('debugger.start_listener', { 'trans_id': params.transId, }); api({ url: baseUrl, method: 'POST', }) .then(function (res) { if (res.data.data.status) { enableToolbarButtons(); pollResult(params.transId); } }) .catch(raiseJSONError); enableToolbarButtons(); pollResult(params.transId); } else if (params.transId != undefined) { // Make api call to execute the and start the target for execution baseUrl = url_for('debugger.start_listener', { 'trans_id': params.transId, }); api({ url: baseUrl, method: 'POST', }) .then(function (res) { if (res.data.data.status) { messages(params.transId); } }) .catch(raiseJSONError); messages(params.transId); } }, []); const setUnsetBreakpoint = (res, breakpoint_list) => { if (res.data.data.status) { for (let brk_val of breakpoint_list) { let info = editor.current.lineInfo((brk_val - 1)); if (info) { if (info.gutterMarkers != undefined) { editor.current.setGutterMarker((brk_val - 1), 'breakpoints', null); } } } } }; const triggerClearBreakpoint = () => { let clearBreakpoint = (br_list) => { // If there is no break point to clear then we should return from here. if ((br_list.length == 1) && (br_list[0].linenumber == -1)) return; disableToolbarButtons(); let breakpoint_list = getBreakpointList(br_list); // Make ajax call to listen the database message let _baseUrl = url_for('debugger.clear_all_breakpoint', { 'trans_id': params.transId, }); api({ url: _baseUrl, method: 'POST', data: { 'breakpoint_list': breakpoint_list.join(), }, }) .then(function (res) { setUnsetBreakpoint(res, breakpoint_list); enableToolbarButtons(); }) .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(); } }) .catch(raiseFetchingBreakpointError); }; const debuggerMark = () => { let marker = document.createElement('div'); marker.style.color = '#822'; marker.innerHTML = '●'; return marker; }; const triggerToggleBreakpoint = () => { disableToolbarButtons(); let info = editor.current.lineInfo(editor.current.activeLineNo); let baseUrl = ''; // If gutterMarker is undefined that means there is no marker defined previously // So we need to set the breakpoint command here... if (info.gutterMarkers == undefined) { baseUrl = url_for('debugger.set_breakpoint', { 'trans_id': params.transId, 'line_no': editor.current.activeLineNo + 1, 'set_type': '1', }); } else { baseUrl = url_for('debugger.set_breakpoint', { 'trans_id': params.transId, 'line_no': editor.current.activeLineNo + 1, 'set_type': '0', }); } api({ url: baseUrl, method: 'GET', }) .then(function (res) { if (res.data.data.status) { // Call function to create and update local variables .... let info_local = editor.current.lineInfo(editor.current.activeLineNo); if (info_local.gutterMarkers != undefined) { editor.current.setGutterMarker(editor.current.activeLineNo, 'breakpoints', null); } else { editor.current.setGutterMarker(editor.current.activeLineNo, 'breakpoints', debuggerMark()); } enableToolbarButtons(); } else if (res.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while toggling breakpoint.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while toggling breakpoint.') ); }); }; 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, method: 'GET', }) .then(function (res) { if (res.data.data.status) { // Remove active time in the editor setActiveLine(-1); // Clear timeout on stop debugger. clearTimeout(timeOut); params.directDebugger.direct_execution_completed = true; params.directDebugger.is_user_aborted_debugging = true; // Stop further pooling params.directDebugger.is_polling_required = false; // Restarting debugging in the same transaction do not work // We will give same behaviour as pgAdmin3 and disable all buttons disableToolbarButtons(); // Set the message to inform the user that execution // is completed. Notify.success(res.data.info, 3000); } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while executing stop in debugging session.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while executing stop in debugging session.') ); }); }; const restart = (transId) => { let baseUrl = url_for('debugger.restart', { 'trans_id': transId }); disableToolbarButtons(); api({ url: baseUrl, }) .then(function (res) { // Restart the same function debugging with previous arguments let restart_dbg = res.data.data.restart_debug ? 1 : 0; // Start pooling again params.directDebugger.polling_timeout_idle = false; params.directDebugger.is_polling_required = true; pollResult(transId); if (restart_dbg) { params.directDebugger.debug_restarted = true; } /* Need to check if restart debugging really require to open the input dialog? If yes then we will get the previous arguments from database and populate the input dialog, If no then we should directly start the listener. */ if (res.data.data.result.require_input) { let t = pgAdmin.Browser.tree, i = t.selected(), d = i ? t.itemData(i) : undefined; let treeInfo = t.getTreeNodeHierarchy(i); if (d) { let isEdbProc = d._type == 'edbproc'; modal.showModal(gettext('Debugger'), (closeModal) => { return ; }, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md }); } } else { // This will start lister for void de restartDebuggerListener(); } }) .catch(raiseJSONError); }; function restartDebuggerListener() { // Debugging of void function is started again so we need to start // the listener again let base_url = url_for('debugger.start_listener', { 'trans_id': params.transId, }); api({ url: base_url, method: 'POST', }) .then(function () { if (params.directDebugger.debug_type) { pollEndExecutionResult(params.transId); } }) .catch(raisePollingError); } const pollEndExecuteError = (res) => { params.directDebugger.direct_execution_completed = true; setActiveLine(-1); //Set the notification message to inform the user that execution is // completed with error. if (!params.directDebugger.is_user_aborted_debugging) { Notify.error(res.data.info, 3000); } // Update the message tab of the debugger updateMessages(res.data.data.status_message); eventBus.current.fireEvent(DEBUGGER_EVENTS.FOCUS_PANEL, PANELS.MESSAGES); disableToolbarButtons(); // If debugging is stopped by user then do not enable // continue/restart button if (!params.directDebugger.is_user_aborted_debugging) { enableToolbarButtons(); params.directDebugger.is_user_aborted_debugging = false; } // Stop further pooling params.directDebugger.is_polling_required = false; }; const updateResultAndMessages = (res) => { if (res.data.data.result != null) { setActiveLine(-1); // Call function to update results information and set result panel focus eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_RESULTS, res.data.data.col_info, res.data.data.result); eventBus.current.fireEvent(DEBUGGER_EVENTS.FOCUS_PANEL, PANELS.RESULTS); params.directDebugger.direct_execution_completed = true; params.directDebugger.polling_timeout_idle = true; //Set the message to inform the user that execution is completed. Notify.success(res.data.info, 3000); // Update the message tab of the debugger updateMessages(res.data.data.status_message); // Execution completed so disable the buttons other than // "Continue/Start" button because user can still // start the same execution again. disableToolbarButtons(); enableToolbarButtons(MENUS.START); // Stop further pooling params.directDebugger.is_polling_required = false; } }; /* For the direct debugging, we need to check weather the functions execution is completed or not. After completion of the debugging, we will stop polling the result until new execution starts. */ const pollEndExecutionResult = (transId) => { // Do we need to poll? if (!params.directDebugger.is_polling_required) { return; } // Make ajax call to listen the database message let baseUrl = url_for('debugger.poll_end_execution_result', { 'trans_id': transId, }), poll_end_timeout; /* * During the execution we should poll the result in minimum seconds * but once the execution is completed and wait for the another * debugging session then we should decrease the polling frequency. */ if (params.directDebugger.polling_timeout_idle) { // Poll the result to check that execution is completed or not // after 1200 ms poll_end_timeout = 1200; } else { // Poll the result to check that execution is completed or not // after 350 ms poll_end_timeout = 250; } timeOut = setTimeout( function () { api({ url: baseUrl, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { if (res.data.data.result == undefined) { /* "result" is undefined only in case of EDB procedure. As Once the EDB procedure execution is completed then we are not getting any result so we need ignore the result. */ setActiveLine(-1); params.directDebugger.direct_execution_completed = true; params.directDebugger.polling_timeout_idle = true; //Set the message to inform the user that execution is completed. Notify.success(res.data.info, 3000); // Update the message tab of the debugger updateMessages(res.data.data.status_message); // Execution completed so disable the buttons other than // "Continue/Start" button because user can still // start the same execution again. disableToolbarButtons(); enableToolbarButtons(MENUS.START); // Stop further polling params.directDebugger.is_polling_required = false; } else { updateResultAndMessages(res); } } else if (res.data.data.status === 'Busy') { // If status is Busy then poll the result by recursive call to // the poll function pollEndExecutionResult(transId); // Update the message tab of the debugger updateMessages(res.data.data.status_message); } else if (res.data.status === 'NotConnected') { Notify.alert( gettext('Debugger poll end execution error'), res.data.result ); } else if (res.data.data.status === 'ERROR') { pollEndExecuteError(res); } }) .catch(raisePollingError); }, poll_end_timeout); }; // This function will update messages tab const updateMessages = (msg) => { if (msg) { // Call function to update messages information eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_MESSAGES, msg, true); } }; const continueDebugger = () => { disableToolbarButtons(); //Check first if previous execution was completed or not if (params.directDebugger.direct_execution_completed && params.directDebugger.direct_execution_completed == params.directDebugger.polling_timeout_idle) { 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, method: 'GET', }) .then(function (res) { if (res.data.data.status) { pollResult(params.transId); } else { Notify.alert( gettext('Debugger Error'), gettext('Error while executing continue in debugging session.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while executing continue in debugging session.') ); }); } }; const stepOver = () => { 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, method: 'GET', }) .then(function (res) { if (res.data.data.status) { pollResult(params.transId); } else { Notify.alert( gettext('Debugger Error'), gettext('Error while executing step over in debugging session.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while executing step over in debugging session.') ); }); }; const getBreakpointList = (br_list) => { let breakpoint_list = []; for (let val of br_list) { if (val.linenumber != -1) { breakpoint_list.push(val.linenumber); } } return breakpoint_list; }; // Function to get the latest breakpoint information and update the // gutters of codemirror const updateBreakpoint = (transId, updateLocalVar = false) => { let callBackFunc = (br_list) => { // If there is no break point to clear then we should return from here. if ((br_list.length == 1) && (br_list[0].linenumber == -1)) return; let breakpoint_list = getBreakpointList(br_list); for (let brk_val of breakpoint_list) { let info = editor.current.lineInfo((brk_val - 1)); if (info.gutterMarkers != undefined) { editor.current.setGutterMarker((brk_val - 1), 'breakpoints', null); } else { editor.current.setGutterMarker((brk_val - 1), 'breakpoints', debuggerMark()); } } if (updateLocalVar) { // Call function to create and update local variables .... getLocalVariables(params.transId); } }; getBreakpointInformation(transId, callBackFunc); }; // 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, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { // 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); let parametersResult = res.data.data.result.filter((lvar) => { return lvar.varclass == 'A'; }); // update Parameter panel data. eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_PARAMETERS, parametersResult); // If debug function is restarted then again start listener to // read the updated messages. if (params.directDebugger.debug_restarted) { if (params.directDebugger.debug_type) { pollEndExecutionResult(transId); } params.directDebugger.debug_restarted = false; } } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching variable information.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching variable information.') ); }); }; 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, method: 'GET', }) .then(function (res) { if (res.data.data.status === 'Success') { // 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 getLocalVariables(params.transId); } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching stack information.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while fetching stack information.') ); }); }; const updateBreakpointInfo = (res, transId) => { if (res.data.data.result[0].src != editor.current.getValue()) { editor.current.setValue(res.data.data.result[0].src); try { updateBreakpoint(transId); } catch (err) { Notify.alert(gettext('Error in update'), err); } } }; const updateInfo = (res, transId) => { if (!params.directDebugger.debug_type && !params.directDebugger.first_time_indirect_debug) { setLoaderText(''); setActiveLine(-1); clearAllBreakpoint(transId); params.directDebugger.first_time_indirect_debug = true; params.directDebugger.polling_timeout_idle = false; } else { params.directDebugger.polling_timeout_idle = false; setLoaderText(''); // If the source is really changed then only update the breakpoint information updateBreakpointInfo(res, transId); setActiveLine(res.data.data.result[0].linenumber - 2); // Update the stack, local variables and parameters information setTimeout(function () { getStackInformation(transId); }, 10); } }; const checkDebuggerStatus = (transId) => { // If status is Busy then poll the result by recursive call to the poll function if (!params.directDebugger.debug_type) { setLoaderText(gettext('Waiting for another session to invoke the target...')); // As we are waiting for another session to invoke the target,disable all the buttons disableToolbarButtons(); params.directDebugger.first_time_indirect_debug = false; pollResult(transId); } else { pollResult(transId); } }; /* poll the actual result after user has executed the "continue", "step-into", "step-over" actions and get the other updated information from the server. */ const pollResult = (transId) => { // Do we need to poll? if (!params.directDebugger.is_polling_required) { return; } // Make ajax call to listen the database message let baseUrl = url_for('debugger.poll_result', { 'trans_id': transId, }), poll_timeout; /* During the execution we should poll the result in minimum seconds but once the execution is completed and wait for the another debugging session then we should decrease the polling frequency. */ if (params.directDebugger.polling_timeout_idle) { // Poll the result after 1 second poll_timeout = 1000; } else { // Poll the result after 200 ms poll_timeout = 200; } setTimeout( function () { api({ url: baseUrl, method: 'GET', beforeSend: function (xhr) { xhr.setRequestHeader( pgAdmin.csrf_token_header, pgAdmin.csrf_token ); }, }) .then(function (res) { if (res.data.data.status === 'Success') { // 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') { params.directDebugger.polling_timeout_idle = true; checkDebuggerStatus(transId); } else if (res.data.data.status === 'NotConnected') { Notify.alert( gettext('Debugger Error: poll_result'), gettext('Error while polling result.') ); } }) .catch(raisePollingError); }, poll_timeout); }; 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, method: 'GET', }) .then(function (res) { if (res.data.data.status) { pollResult(params.transId); } else { Notify.alert( gettext('Debugger Error'), gettext('Error while executing step into in debugging session.') ); } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while executing step into in debugging session.') ); }); }; const onChangesLocalVarParameters = (data) => { // Make api call to listen the database message let baseUrl = url_for('debugger.deposit_value', { 'trans_id': params.transId, }); api({ url: baseUrl, method: 'POST', data: data, }) .then(function (res) { 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 if (res.data.data.result) { Notify.success(res.data.data.info, 3000); } else { Notify.error(res.data.data.info, 3000); } } }) .catch(function () { Notify.alert( gettext('Debugger Error'), gettext('Error while depositing variable value.') ); }); }; useEffect(() => { // Register all eventes for debugger. eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_CLEAR_ALL_BREAKPOINTS, triggerClearBreakpoint); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_TOGGLE_BREAKPOINTS, triggerToggleBreakpoint); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_STOP_DEBUGGING, stopDebugging); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_CONTINUE_DEBUGGING, continueDebugger); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_STEPOVER_DEBUGGING, stepOver); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_STEINTO_DEBUGGING, stepInto); eventBus.current.registerListener( DEBUGGER_EVENTS.SET_LOCAL_VARIABLE_VALUE_CHANGE, onChangesLocalVarParameters); eventBus.current.registerListener( DEBUGGER_EVENTS.SET_PARAMETERS_VALUE_CHANGE, onChangesLocalVarParameters); eventBus.current.registerListener(DEBUGGER_EVENTS.SET_FRAME, (frameId) => { selectFrame(frameId); }); eventBus.current.registerListener(DEBUGGER_EVENTS.FOCUS_PANEL, (panelId) => { LayoutHelper.focus(docker.current, panelId); }); eventBus.current.registerListener( DEBUGGER_EVENTS.TRIGGER_RESET_LAYOUT, () => { docker.current?.resetLayout(); }); }, []); useEffect(() => { reflectPreferences(); pgAdmin.Browser.onPreferencesChange('debugger', function () { reflectPreferences(); }); // /* Clear the timeout if unmounted */ return () => { clearTimeout(timeOut); }; }, []); const DebuggerContextValue = React.useMemo(() => ({ docker: docker.current, api: api, modal: modal, params: qtState.params, preferences: qtState.preferences, }), [qtState.params, qtState.preferences]); // Define the debugger layout components such as DebuggerEditor to show queries and let defaultLayout = { dockbox: { mode: 'vertical', children: [ { mode: 'horizontal', children: [ { tabs: [ LayoutHelper.getPanel({ id: PANELS.DEBUGGER, title: gettext('Debugger'), content: { editor.current = edRef; }} params={{ transId: params.transId, debuggerDirect: params.directDebugger }} /> }) ], } ] }, { mode: 'horizontal', children: [ { tabs: [ LayoutHelper.getPanel({ id: PANELS.PARAMETERS, title: gettext('Parameters'), content: , }), LayoutHelper.getPanel({ id: PANELS.LOCAL_VARIABLES, title: gettext('Local Variables'), content: , }), LayoutHelper.getPanel({ id: PANELS.MESSAGES, title: gettext('Messages'), content: , }), LayoutHelper.getPanel({ id: PANELS.RESULTS, title: gettext('Result'), content: , }), LayoutHelper.getPanel({ id: PANELS.STACK, title: gettext('Stack'), content: , }), ], } ] }, ] }, }; return ( docker.current = obj} defaultLayout={defaultLayout} layoutId="Debugger/Layout" savedLayout={savedLayout} /> ); } DebuggerComponent.propTypes = { pgAdmin: PropTypes.object, selectedNodeInfo: PropTypes.object, panel: PropTypes.object, eventBusObj: PropTypes.object, layout: PropTypes.string, params: PropTypes.object };