codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9 10import { Box } from '@mui/material';11import React, { useEffect, useRef, useState } from 'react';12import PropTypes from 'prop-types';13 14import gettext from 'sources/gettext';15import url_for from 'sources/url_for';16import Loader from 'sources/components/Loader';17 18import Layout, { LayoutDocker } from '../../../../../static/js/helpers/Layout';19import EventBus from '../../../../../static/js/helpers/EventBus';20import getApiInstance, { callFetch } from '../../../../../static/js/api_instance';21 22import { PANELS, DEBUGGER_EVENTS, MENUS } from '../DebuggerConstants';23import { retrieveNodeName } from '../../../../sqleditor/static/js/show_view_data';24import { useModal } from '../../../../../static/js/helpers/ModalProvider';25import DebuggerEditor from './DebuggerEditor';26import DebuggerMessages from './DebuggerMessages';27import { ToolBar } from './ToolBar';28import { Stack } from './Stack';29import { Results } from './Results';30import { LocalVariablesAndParams } from './LocalVariablesAndParams';31import DebuggerArgumentComponent from './DebuggerArgumentComponent';32import usePreferences from '../../../../../preferences/static/js/store';33 34export const DebuggerContext = React.createContext();35export const DebuggerEventsContext = React.createContext();36 37export default function DebuggerComponent({ pgAdmin, selectedNodeInfo, panelId, panelDocker, eventBusObj, layout, params }) {38 const savedLayout = layout;39 const containerRef = React.useRef(null);40 const docker = useRef(null);41 const api = getApiInstance();42 const modal = useModal();43 const eventBus = useRef(eventBusObj || (new EventBus()));44 const [loaderText, setLoaderText] = React.useState('');45 const editor = useRef(null);46 const preferencesStore = usePreferences();47 let timeOut = null;48 49 const [preferences, setPreferences] = useState({50 browser: preferencesStore.getPreferencesForModule('browser'),51 debugger: preferencesStore.getPreferencesForModule('debugger'),52 });53 54 const disableToolbarButtons = () => {55 eventBus.current.fireEvent(DEBUGGER_EVENTS.DISABLE_MENU);56 eventBus.current.fireEvent(DEBUGGER_EVENTS.GET_TOOL_BAR_BUTTON_STATUS, { disabled: true });57 };58 59 const enableToolbarButtons = (key = null) => {60 if (key) {61 eventBus.current.fireEvent(DEBUGGER_EVENTS.ENABLE_SPECIFIC_MENU, key);62 } else {63 eventBus.current.fireEvent(DEBUGGER_EVENTS.ENABLE_MENU);64 }65 66 eventBus.current.fireEvent(DEBUGGER_EVENTS.GET_TOOL_BAR_BUTTON_STATUS, { disabled: false });67 };68 69 // Function to get the breakpoint information from the server70 const getBreakpointInformation = (transId, callBackFunc) => {71 let result = '';72 73 // Make ajax call to listen the database message74 let baseUrl = url_for('debugger.execute_query', {75 'trans_id': transId,76 'query_type': 'get_breakpoints',77 });78 api({79 url: baseUrl,80 method: 'GET',81 })82 .then(function (res) {83 if (res.data.data.status === 'Success') {84 result = res.data.data.result;85 if (callBackFunc) {86 callBackFunc(result);87 }88 } else if (res.data.data.status === 'NotConnected') {89 raiseFetchingBreakpointError();90 }91 })92 .catch(raiseFetchingBreakpointError);93 94 return result;95 };96 97 const clearAllBreakpoint = (transId) => {98 let clearBreakpoint = (br_list) => {99 // If there is no break point to clear then we should return from here.100 if ((br_list.length == 1) && (br_list[0].linenumber == -1))101 return;102 103 disableToolbarButtons();104 let breakpoint_list = getBreakpointList(br_list);105 106 // Make ajax call to listen the database message107 let baseUrl = url_for('debugger.clear_all_breakpoint', {108 'trans_id': transId,109 });110 111 api({112 url: baseUrl,113 method: 'POST',114 data: {115 'breakpoint_list': breakpoint_list.length > 0 ? breakpoint_list.join() : null,116 },117 })118 .then(function (res) {119 if (res.data.data.status) {120 executeQuery(transId);121 editor.current.clearBreakpoints();122 }123 enableToolbarButtons();124 })125 .catch(raiseClearBrekpointError);126 };127 getBreakpointInformation(transId, clearBreakpoint);128 };129 130 const raiseJSONError = (xhr) => {131 try {132 let err = xhr.response.data;133 if (err.success == 0) {134 let header_msg = gettext('Debugger Error'),135 err_msg = err.errormsg;136 137 // Stopped Debugger forcefully. 57014 is the SQL State138 if (err.errormsg.indexOf('57014') !== -1) {139 header_msg = gettext('Debugger Aborted');140 err_msg = gettext('Debugger has been aborted. '141 + 'On clicking the ok button, debugger panel will be closed.');142 }143 144 pgAdmin.Browser.notifier.alert(header_msg, err_msg, () => {145 if (panelId) {146 panelDocker.close(panelId, true);147 }148 });149 }150 } catch (e) {151 alert(xhr);152 pgAdmin.Browser.notifier.alert(153 gettext('Debugger Error'),154 gettext('Error while starting debugging listener.')155 );156 }157 };158 159 const raisePollingError = (error) => {160 console.error(error);161 pgAdmin.Browser.notifier.alert(162 gettext('Debugger Error'),163 gettext('Error while polling result.')164 );165 };166 167 const raiseClearBrekpointError = () => {168 pgAdmin.Browser.notifier.alert(169 gettext('Debugger Error'),170 gettext('Error while clearing all breakpoint.')171 );172 };173 174 const raiseFetchingBreakpointError = () => {175 pgAdmin.Browser.notifier.alert(176 gettext('Debugger Error'),177 gettext('Error while fetching breakpoint information.')178 );179 };180 181 const messages = (transId) => {182 // Make ajax call to listen the database message183 let baseUrl = url_for('debugger.messages', {184 'trans_id': transId,185 });186 187 api({188 url: baseUrl,189 method: 'GET',190 })191 .then(function (res) {192 if (res.data.data.status === 'Success') {193 enableToolbarButtons();194 // If status is Success then find the port number to attach the executer.195 startExecution(transId, res.data.data.result);196 } else if (res.data.data.status === 'Busy') {197 // If status is Busy then poll the result by recursive call to the poll function198 messages(transId);199 } else if (res.data.data.status === 'NotConnected') {200 pgAdmin.Browser.notifier.alert(201 gettext('Not connected to server or connection with the server has been closed.'),202 res.data.result203 );204 }205 })206 .catch(function () {207 pgAdmin.Browser.notifier.alert(208 gettext('Debugger Error'),209 gettext('Error while fetching messages information.')210 );211 });212 213 };214 215 const startExecution = (transId, port_num) => {216 // Make ajax call to listen the database message217 let baseUrl = url_for(218 'debugger.start_execution', {219 'trans_id': transId,220 'port_num': port_num,221 });222 api({223 url: baseUrl,224 method: 'GET',225 })226 .then(function (res) {227 if (res.data.data.status === 'Success') {228 // If status is Success then find the port number to attach the executer.229 executeQuery(transId);230 } else if (res.data.data.status === 'NotConnected') {231 pgAdmin.Browser.notifier.alert(232 gettext('Debugger Error'),233 gettext('Error while starting debugging session.')234 );235 }236 })237 .catch(function () {238 pgAdmin.Browser.notifier.alert(239 gettext('Debugger Error'),240 gettext('Error while starting debugging session.')241 );242 });243 };244 245 const executeQuery = (transId) => {246 // Make ajax call to listen the database message247 let baseUrl = url_for(248 'debugger.execute_query', {249 'trans_id': transId,250 'query_type': 'wait_for_breakpoint',251 });252 api({253 url: baseUrl,254 method: 'GET',255 })256 .then(function (res) {257 if (res.data.data.status === 'Success') {258 // set the return code to the code editor text area259 if (260 res.data.data.result[0].src != null &&261 res.data.data.result[0].linenumber != null262 ) {263 editor.current.setValue(res.data.data.result[0].src);264 265 editor.current.setActiveLine(res.data.data.result[0].linenumber - 1);266 }267 // Call function to create and update Stack information ....268 getStackInformation(transId);269 if (params.directDebugger.debug_type) {270 pollEndExecutionResult(transId);271 }272 } else if (res.data.data.status === 'NotConnected') {273 pgAdmin.Browser.notifier.alert(274 gettext('Debugger Error'),275 gettext('Error while executing requested debugging information.')276 );277 }278 })279 .catch(function (error) {280 console.error(error);281 pgAdmin.Browser.notifier.alert(282 gettext('Debugger Error'),283 gettext('Error while executing requested debugging information.')284 );285 });286 };287 288 const selectFrame = (frameId) => {289 // Make ajax call to listen the database message290 let baseUrl = url_for('debugger.select_frame', {291 'trans_id': params.transId,292 'frame_id': frameId,293 });294 api({295 url: baseUrl,296 method: 'GET',297 })298 .then(function (res) {299 if (res.data.data.status) {300 editor.current.setValue(res.data.data.result[0].src);301 updateBreakpoint(params.transId, true);302 editor.current.setActiveLine(res.data.data.result[0].linenumber - 1);303 }304 })305 .catch(function () {306 pgAdmin.Browser.notifier.alert(307 gettext('Debugger Error'),308 gettext('Error while selecting frame.')309 );310 });311 };312 313 useEffect(() => {314 let baseUrl = '';315 if (params.transId != undefined && !params.directDebugger.debug_type) {316 // Make ajax call to execute the and start the target for execution317 baseUrl = url_for('debugger.start_listener', {318 'trans_id': params.transId,319 });320 321 api({322 url: baseUrl,323 method: 'POST',324 })325 .catch(raiseJSONError);326 enableToolbarButtons();327 pollResult(params.transId);328 } else if (params.transId != undefined) {329 // Make api call to execute the and start the target for execution330 baseUrl = url_for('debugger.start_listener', {331 'trans_id': params.transId,332 });333 334 api({335 url: baseUrl,336 method: 'POST',337 })338 .catch(raiseJSONError);339 messages(params.transId);340 }341 342 const closeConn = ()=>{343 /* Using fetch with keepalive as the browser may344 cancel the axios request on tab close. keepalive will345 make sure the request is completed */346 callFetch(347 url_for('debugger.close', {348 'trans_id': params.transId,349 }), {350 keepalive: true,351 method: 'DELETE',352 }353 )354 .then(()=>{/* Success */})355 .catch((err)=>console.error(err));356 };357 window.addEventListener('unload', closeConn);358 359 return ()=>{360 window.removeEventListener('unload', closeConn);361 };362 }, []);363 364 const triggerClearBreakpoint = () => {365 let clearBreakpoint = (br_list) => {366 // If there is no break point to clear then we should return from here.367 if ((br_list.length == 1) && (br_list[0].linenumber == -1))368 return;369 370 disableToolbarButtons();371 let breakpoint_list = getBreakpointList(br_list);372 373 // Make ajax call to listen the database message374 let _baseUrl = url_for('debugger.clear_all_breakpoint', {375 'trans_id': params.transId,376 });377 378 api({379 url: _baseUrl,380 method: 'POST',381 data: {382 'breakpoint_list': breakpoint_list.join(),383 },384 })385 .then(function () {386 editor.current.clearBreakpoints();387 enableToolbarButtons();388 })389 .catch(raiseClearBrekpointError);390 };391 // Make ajax call to listen the database message392 let baseUrl = url_for('debugger.execute_query', {393 'trans_id': params.transId,394 'query_type': 'get_breakpoints',395 });396 api({397 url: baseUrl,398 method: 'GET',399 })400 .then(function (res) {401 if (res.data.data.status === 'Success') {402 let result = res.data.data.result;403 clearBreakpoint(result);404 } else if (res.data.data.status === 'NotConnected') {405 raiseFetchingBreakpointError();406 }407 })408 .catch(raiseFetchingBreakpointError);409 410 };411 412 const triggerToggleBreakpoint = () => {413 disableToolbarButtons();414 const lineNo = editor.current.getActiveLine();415 editor.current.toggleBreakpoint(lineNo);416 enableToolbarButtons();417 };418 419 const stopDebugging = () => {420 disableToolbarButtons();421 // Make ajax call to listen the database message422 let baseUrl = url_for(423 'debugger.execute_query', {424 'trans_id': params.transId,425 'query_type': 'abort_target',426 });427 api({428 url: baseUrl,429 method: 'GET',430 })431 .then(function (res) {432 if (res.data.data.status) {433 // Remove active time in the editor434 editor.current.setActiveLine(-1);435 // Clear timeout on stop debugger.436 clearTimeout(timeOut);437 438 params.directDebugger.direct_execution_completed = true;439 params.directDebugger.is_user_aborted_debugging = true;440 441 // Stop further pooling442 params.directDebugger.is_polling_required = false;443 444 // Restarting debugging in the same transaction do not work445 // We will give same behaviour as pgAdmin3 and disable all buttons446 disableToolbarButtons();447 448 // Set the message to inform the user that execution449 // is completed.450 pgAdmin.Browser.notifier.success(res.data.info, 3000);451 } else if (res.data.data.status === 'NotConnected') {452 pgAdmin.Browser.notifier.alert(453 gettext('Debugger Error'),454 gettext('Error while executing stop in debugging session.')455 );456 }457 })458 .catch(function () {459 pgAdmin.Browser.notifier.alert(460 gettext('Debugger Error'),461 gettext('Error while executing stop in debugging session.')462 );463 });464 };465 466 const restart = (transId) => {467 let baseUrl = url_for('debugger.restart', { 'trans_id': transId });468 disableToolbarButtons();469 470 api({471 url: baseUrl,472 })473 .then(function (res) {474 // Restart the same function debugging with previous arguments475 let restart_dbg = res.data.data.restart_debug ? 1 : 0;476 477 // Start pooling again478 params.directDebugger.polling_timeout_idle = false;479 params.directDebugger.is_polling_required = true;480 pollResult(transId);481 482 if (restart_dbg) {483 params.directDebugger.debug_restarted = true;484 }485 486 /*487 Need to check if restart debugging really require to open the input488 dialog? If yes then we will get the previous arguments from database489 and populate the input dialog, If no then we should directly start the490 listener.491 */492 if (res.data.data.result.require_input) {493 let t = pgAdmin.Browser.tree,494 i = t.selected(),495 d = i ? t.itemData(i) : undefined;496 497 let treeInfo = t.getTreeNodeHierarchy(i);498 499 if (d) {500 let isEdbProc = d._type == 'edbproc';501 modal.showModal(gettext('Debugger'), (closeModal) => {502 return <DebuggerArgumentComponent503 closeModal={closeModal}504 debuggerInfo={res.data.data.result}505 restartDebug={restart_dbg}506 isEdbProc={isEdbProc}507 transId={params.transId.toString()}508 pgData={d}509 pgTreeInfo={treeInfo}510 ></DebuggerArgumentComponent>;511 }, { isFullScreen: false, isResizeable: true, showFullScreen: true, isFullWidth: true, dialogWidth: pgAdmin.Browser.stdW.md, dialogHeight: pgAdmin.Browser.stdH.md });512 }513 } else {514 // This will start lister for void de515 restartDebuggerListener();516 }517 })518 .catch(raiseJSONError);519 };520 521 function restartDebuggerListener() {522 // Debugging of void function is started again so we need to start523 // the listener again524 let base_url = url_for('debugger.start_listener', {525 'trans_id': params.transId,526 });527 528 api({529 url: base_url,530 method: 'POST',531 })532 .catch(raisePollingError);533 if (params.directDebugger.debug_type) {534 pollEndExecutionResult(params.transId);535 }536 }537 538 const pollEndExecuteError = (res) => {539 params.directDebugger.direct_execution_completed = true;540 editor.current.setActiveLine(-1);541 542 //Set the notification message to inform the user that execution is543 // completed with error.544 if (!params.directDebugger.is_user_aborted_debugging) {545 pgAdmin.Browser.notifier.error(res.data.info, 3000);546 }547 548 // Update the message tab of the debugger549 updateMessages(res.data.data.status_message);550 551 eventBus.current.fireEvent(DEBUGGER_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);552 disableToolbarButtons();553 // If debugging is stopped by user then do not enable554 // continue/restart button555 if (!params.directDebugger.is_user_aborted_debugging) {556 enableToolbarButtons();557 params.directDebugger.is_user_aborted_debugging = false;558 }559 560 // Stop further pooling561 params.directDebugger.is_polling_required = false;562 };563 564 const updateResultAndMessages = (res) => {565 if (res.data.data.result != null) {566 editor.current.setActiveLine(-1);567 // Call function to update results information and set result panel focus568 eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_RESULTS, res.data.data.col_info, res.data.data.result);569 eventBus.current.fireEvent(DEBUGGER_EVENTS.FOCUS_PANEL, PANELS.RESULTS);570 571 params.directDebugger.direct_execution_completed = true;572 params.directDebugger.polling_timeout_idle = true;573 574 //Set the message to inform the user that execution is completed.575 pgAdmin.Browser.notifier.success(res.data.info, 3000);576 577 // Update the message tab of the debugger578 updateMessages(res.data.data.status_message);579 580 // Execution completed so disable the buttons other than581 // "Continue/Start" button because user can still582 // start the same execution again.583 disableToolbarButtons();584 enableToolbarButtons(MENUS.START);585 586 // Stop further pooling587 params.directDebugger.is_polling_required = false;588 }589 };590 591 /*592 For the direct debugging, we need to check weather the functions execution593 is completed or not. After completion of the debugging, we will stop polling594 the result until new execution starts.595 */596 const pollEndExecutionResult = (transId) => {597 // Do we need to poll?598 if (!params.directDebugger.is_polling_required) {599 return;600 }601 602 // Make ajax call to listen the database message603 let baseUrl = url_for('debugger.poll_end_execution_result', {604 'trans_id': transId,605 }),606 poll_end_timeout;607 608 /*609 * During the execution we should poll the result in minimum seconds610 * but once the execution is completed and wait for the another611 * debugging session then we should decrease the polling frequency.612 */613 if (params.directDebugger.polling_timeout_idle) {614 // Poll the result to check that execution is completed or not615 // after 1200 ms616 poll_end_timeout = 1200;617 } else {618 // Poll the result to check that execution is completed or not619 // after 350 ms620 poll_end_timeout = 250;621 }622 623 timeOut = setTimeout(624 function () {625 api({626 url: baseUrl,627 method: 'GET',628 })629 .then(function (res) {630 if (res.data.data.status === 'Success') {631 if (res.data.data.result == undefined) {632 /*633 "result" is undefined only in case of EDB procedure.634 As Once the EDB procedure execution is completed then we are635 not getting any result so we need to ignore the result.636 */637 editor.current.setActiveLine(-1);638 params.directDebugger.direct_execution_completed = true;639 params.directDebugger.polling_timeout_idle = true;640 641 //Set the message to inform the user that execution is completed.642 pgAdmin.Browser.notifier.success(res.data.info, 3000);643 644 // Update the message tab of the debugger645 updateMessages(res.data.data.status_message);646 647 // Execution completed so disable the buttons other than648 // "Continue/Start" button because user can still649 // start the same execution again.650 disableToolbarButtons();651 enableToolbarButtons(MENUS.START);652 653 // Stop further polling654 params.directDebugger.is_polling_required = false;655 } else {656 updateResultAndMessages(res);657 }658 } else if (res.data.data.status === 'Busy') {659 // If status is Busy then poll the result by recursive call to660 // the poll function661 pollEndExecutionResult(transId);662 // Update the message tab of the debugger663 updateMessages(res.data.data.status_message);664 } else if (res.data.status === 'NotConnected') {665 pgAdmin.Browser.notifier.alert(666 gettext('Debugger poll end execution error'),667 res.data.result668 );669 } else if (res.data.data.status === 'ERROR') {670 pollEndExecuteError(res);671 }672 })673 .catch(raisePollingError);674 }, poll_end_timeout);675 676 };677 678 // This function will update messages tab679 const updateMessages = (msg) => {680 if (msg) {681 // Call function to update messages information682 eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_MESSAGES, msg, true);683 }684 };685 686 const continueDebugger = () => {687 disableToolbarButtons();688 689 //Check first if previous execution was completed or not690 if (params.directDebugger.direct_execution_completed &&691 params.directDebugger.direct_execution_completed == params.directDebugger.polling_timeout_idle) {692 restart(params.transId);693 } else {694 // Make ajax call to listen the database message695 let baseUrl = url_for('debugger.execute_query', {696 'trans_id': params.transId,697 'query_type': 'continue',698 });699 api({700 url: baseUrl,701 method: 'GET',702 })703 .then(function (res) {704 if (res.data.data.status) {705 pollResult(params.transId);706 } else {707 pgAdmin.Browser.notifier.alert(708 gettext('Debugger Error'),709 gettext('Error while executing continue in debugging session.')710 );711 }712 })713 .catch(function () {714 pgAdmin.Browser.notifier.alert(715 gettext('Debugger Error'),716 gettext('Error while executing continue in debugging session.')717 );718 });719 }720 };721 722 const stepOver = () => {723 disableToolbarButtons();724 725 // Make ajax call to listen the database message726 let baseUrl = url_for('debugger.execute_query', {727 'trans_id': params.transId,728 'query_type': 'step_over',729 });730 api({731 url: baseUrl,732 method: 'GET',733 })734 .then(function (res) {735 if (res.data.data.status) {736 pollResult(params.transId);737 } else {738 pgAdmin.Browser.notifier.alert(739 gettext('Debugger Error'),740 gettext('Error while executing step over in debugging session.')741 );742 }743 })744 .catch(function () {745 pgAdmin.Browser.notifier.alert(746 gettext('Debugger Error'),747 gettext('Error while executing step over in debugging session.')748 );749 });750 751 };752 753 const getBreakpointList = (br_list) => {754 let breakpoint_list = [];755 for (let val of br_list) {756 if (val.linenumber != -1) {757 breakpoint_list.push(val.linenumber);758 }759 }760 761 return breakpoint_list;762 };763 764 // Function to get the latest breakpoint information765 const updateBreakpoint = (transId, updateLocalVar = false) => {766 let callBackFunc = () => {767 if (updateLocalVar) {768 // Call function to create and update local variables ....769 getLocalVariables(params.transId);770 }771 };772 getBreakpointInformation(transId, callBackFunc);773 };774 775 // Get the local variable information of the functions and update the grid776 const getLocalVariables = (transId) => {777 // Make ajax call to listen the database message778 let baseUrl = url_for(779 'debugger.execute_query', {780 'trans_id': transId,781 'query_type': 'get_variables',782 });783 api({784 url: baseUrl,785 method: 'GET',786 })787 .then(function (res) {788 if (res.data.data.status === 'Success') {789 // Call function to update local variables790 let variablesResult = res.data.data.result.filter((lvar) => {791 return lvar.varclass == 'L';792 });793 eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_LOCAL_VARIABLES, variablesResult);794 795 let parametersResult = res.data.data.result.filter((lvar) => {796 return lvar.varclass == 'A';797 });798 // update Parameter panel data.799 eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_PARAMETERS, parametersResult);800 // If debug function is restarted then again start listener to801 // read the updated messages.802 if (params.directDebugger.debug_restarted) {803 if (params.directDebugger.debug_type) {804 pollEndExecutionResult(transId);805 }806 params.directDebugger.debug_restarted = false;807 }808 } else if (res.data.data.status === 'NotConnected') {809 pgAdmin.Browser.notifier.alert(810 gettext('Debugger Error'),811 gettext('Error while fetching variable information.')812 );813 }814 })815 .catch(function () {816 pgAdmin.Browser.notifier.alert(817 gettext('Debugger Error'),818 gettext('Error while fetching variable information.')819 );820 });821 };822 823 const getStackInformation = (transId) => {824 // Make ajax call to listen the database message825 let baseUrl = url_for(826 'debugger.execute_query', {827 'trans_id': transId,828 'query_type': 'get_stack_info',829 });830 api({831 url: baseUrl,832 method: 'GET',833 })834 .then(function (res) {835 if (res.data.data.status === 'Success') {836 // Call function to update stack information837 eventBus.current.fireEvent(DEBUGGER_EVENTS.SET_STACK, res.data.data.result);838 // Call function to create and update stack information839 getLocalVariables(params.transId);840 } else if (res.data.data.status === 'NotConnected') {841 pgAdmin.Browser.notifier.alert(842 gettext('Debugger Error'),843 gettext('Error while fetching stack information.')844 );845 }846 })847 .catch(function () {848 pgAdmin.Browser.notifier.alert(849 gettext('Debugger Error'),850 gettext('Error while fetching stack information.')851 );852 });853 };854 855 856 const updateBreakpointInfo = (res, transId) => {857 if (res.data.data.result[0].src != editor.current.getValue()) {858 editor.current.setValue(res.data.data.result[0].src);859 try {860 updateBreakpoint(transId);861 } catch (err) {862 pgAdmin.Browser.notifier.alert(gettext('Error in update'), err);863 }864 865 }866 };867 868 const updateInfo = (res, transId) => {869 if (!params.directDebugger.debug_type && !params.directDebugger.first_time_indirect_debug) {870 setLoaderText('');871 editor.current.setActiveLine(-1);872 clearAllBreakpoint(transId);873 874 params.directDebugger.first_time_indirect_debug = true;875 params.directDebugger.polling_timeout_idle = false;876 } else {877 params.directDebugger.polling_timeout_idle = false;878 setLoaderText('');879 // If the source is really changed then only update the breakpoint information880 updateBreakpointInfo(res, transId);881 882 editor.current.setActiveLine(res.data.data.result[0].linenumber - 1);883 // Update the stack, local variables and parameters information884 setTimeout(function () {885 getStackInformation(transId);886 }, 10);887 }888 };889 890 const checkDebuggerStatus = (transId) => {891 // If status is Busy then poll the result by recursive call to the poll function892 if (!params.directDebugger.debug_type) {893 setLoaderText(gettext('Waiting for another session to invoke the target...'));894 // As we are waiting for another session to invoke the target,disable all the buttons895 disableToolbarButtons();896 params.directDebugger.first_time_indirect_debug = false;897 pollResult(transId);898 } else {899 pollResult(transId);900 }901 };902 903 /* poll the actual result after user has executed the "continue", "step-into",904 "step-over" actions and get the other updated information from the server.905 */906 const pollResult = (transId) => {907 // Do we need to poll?908 if (!params.directDebugger.is_polling_required) {909 return;910 }911 912 // Make ajax call to listen the database message913 let baseUrl = url_for('debugger.poll_result', {914 'trans_id': transId,915 }),916 poll_timeout;917 918 /*919 During the execution we should poll the result in minimum seconds but920 once the execution is completed and wait for the another debugging921 session then we should decrease the polling frequency.922 */923 if (params.directDebugger.polling_timeout_idle) {924 // Poll the result after 1 second925 poll_timeout = 1000;926 } else {927 // Poll the result after 200 ms928 poll_timeout = 200;929 }930 931 setTimeout(932 function () {933 api({934 url: baseUrl,935 method: 'GET',936 beforeSend: function (xhr) {937 xhr.setRequestHeader(938 pgAdmin.csrf_token_header, pgAdmin.csrf_token939 );940 },941 })942 .then(function (res) {943 if (res.data.data.status === 'Success') {944 // If no result then poll again to wait for results.945 if (res.data.data.result == null || res.data.data.result.length == 0) {946 pollResult(transId);947 } else {948 updateInfo(res, transId);949 // Enable all the buttons as we got the results950 enableToolbarButtons();951 }952 } else if (res.data.data.status === 'Busy') {953 params.directDebugger.polling_timeout_idle = true;954 checkDebuggerStatus(transId);955 } else if (res.data.data.status === 'NotConnected') {956 pgAdmin.Browser.notifier.alert(957 gettext('Debugger Error: poll_result'),958 gettext('Error while polling result.')959 );960 }961 })962 .catch(raisePollingError);963 }, poll_timeout);964 965 };966 967 const stepInto = () => {968 disableToolbarButtons();969 // Make ajax call to listen the database message970 let baseUrl = url_for('debugger.execute_query', {971 'trans_id': params.transId,972 'query_type': 'step_into',973 });974 api({975 url: baseUrl,976 method: 'GET',977 })978 .then(function (res) {979 if (res.data.data.status) {980 pollResult(params.transId);981 } else {982 pgAdmin.Browser.notifier.alert(983 gettext('Debugger Error'),984 gettext('Error while executing step into in debugging session.')985 );986 }987 })988 .catch(function () {989 pgAdmin.Browser.notifier.alert(990 gettext('Debugger Error'),991 gettext('Error while executing step into in debugging session.')992 );993 });994 };995 996 997 const onChangesLocalVarParameters = (data) => {998 // Make api call to listen the database message999 let baseUrl = url_for('debugger.deposit_value', {1000 'trans_id': params.transId,1001 });1002 api({1003 url: baseUrl,1004 method: 'POST',1005 data: data,1006 })1007 .then(function (res) {1008 if (res.data.data.status) {1009 // Get the updated variables value1010 getLocalVariables(params.transId);1011 // Show the message to the user that deposit value is success or failure1012 if (res.data.data.result) {1013 pgAdmin.Browser.notifier.success(res.data.data.info, 3000);1014 } else {1015 pgAdmin.Browser.notifier.error(res.data.data.info, 3000);1016 }1017 }1018 })1019 .catch(function () {1020 pgAdmin.Browser.notifier.alert(1021 gettext('Debugger Error'),1022 gettext('Error while depositing variable value.')1023 );1024 });1025 };1026 1027 useEffect(() => {1028 // Register all eventes for debugger.1029 eventBus.current.registerListener(1030 DEBUGGER_EVENTS.TRIGGER_CLEAR_ALL_BREAKPOINTS, triggerClearBreakpoint);1031 1032 eventBus.current.registerListener(1033 DEBUGGER_EVENTS.TRIGGER_TOGGLE_BREAKPOINTS, triggerToggleBreakpoint);1034 1035 eventBus.current.registerListener(1036 DEBUGGER_EVENTS.TRIGGER_STOP_DEBUGGING, stopDebugging);1037 1038 eventBus.current.registerListener(1039 DEBUGGER_EVENTS.TRIGGER_CONTINUE_DEBUGGING, continueDebugger);1040 1041 eventBus.current.registerListener(1042 DEBUGGER_EVENTS.TRIGGER_STEPOVER_DEBUGGING, stepOver);1043 1044 eventBus.current.registerListener(1045 DEBUGGER_EVENTS.TRIGGER_STEINTO_DEBUGGING, stepInto);1046 1047 eventBus.current.registerListener(1048 DEBUGGER_EVENTS.SET_LOCAL_VARIABLE_VALUE_CHANGE, onChangesLocalVarParameters);1049 1050 eventBus.current.registerListener(1051 DEBUGGER_EVENTS.SET_PARAMETERS_VALUE_CHANGE, onChangesLocalVarParameters);1052 1053 eventBus.current.registerListener(DEBUGGER_EVENTS.SET_FRAME, (frameId) => {1054 selectFrame(frameId);1055 });1056 1057 eventBus.current.registerListener(DEBUGGER_EVENTS.FOCUS_PANEL, (panelId) => {1058 docker.current.focus(panelId);1059 });1060 1061 eventBus.current.registerListener(1062 DEBUGGER_EVENTS.TRIGGER_RESET_LAYOUT, () => {1063 docker.current?.resetLayout();1064 });1065 }, []);1066 1067 1068 useEffect(() => usePreferences.subscribe(1069 state => {1070 setPreferences({1071 browser: state.getPreferencesForModule('browser'),1072 debugger: state.getPreferencesForModule('debugger'),1073 });1074 }1075 ), []);1076 1077 const DebuggerContextValue = React.useMemo(() => ({1078 docker: docker.current,1079 api: api,1080 modal: modal,1081 params: {1082 ...params,1083 node_name: retrieveNodeName(selectedNodeInfo),1084 },1085 preferences: preferences,1086 }), [preferences]);1087 1088 // Define the debugger layout components such as DebuggerEditor to show queries and1089 let defaultLayout = {1090 dockbox: {1091 mode: 'vertical',1092 children: [1093 {1094 mode: 'horizontal',1095 children: [1096 {1097 tabs: [1098 LayoutDocker.getPanel({1099 id: PANELS.DEBUGGER, title: gettext('Debugger'), content: <DebuggerEditor getEditor={(edRef) => {1100 editor.current = edRef;1101 }} params={{ transId: params.transId, debuggerDirect: params.directDebugger }} />1102 })1103 ],1104 }1105 ]1106 },1107 {1108 mode: 'horizontal',1109 children: [1110 {1111 tabs: [1112 LayoutDocker.getPanel({1113 id: PANELS.PARAMETERS, title: gettext('Parameters'), content: <LocalVariablesAndParams type={1} />,1114 }),1115 LayoutDocker.getPanel({1116 id: PANELS.LOCAL_VARIABLES, title: gettext('Local Variables'), content: <LocalVariablesAndParams type={2} />,1117 }),1118 LayoutDocker.getPanel({1119 id: PANELS.MESSAGES, title: gettext('Messages'), content: <DebuggerMessages />,1120 }),1121 LayoutDocker.getPanel({1122 id: PANELS.RESULTS, title: gettext('Result'), content: <Results />,1123 }),1124 LayoutDocker.getPanel({1125 id: PANELS.STACK, title: gettext('Stack'), content: <Stack />,1126 }),1127 ],1128 }1129 ]1130 },1131 ]1132 },1133 };1134 1135 return (1136 <DebuggerContext.Provider value={DebuggerContextValue}>1137 <DebuggerEventsContext.Provider value={eventBus.current}>1138 <Loader message={loaderText} />1139 <Box width="100%" height="100%" display="flex" flexDirection="column" flexGrow="1" tabIndex="0" ref={containerRef}>1140 <ToolBar1141 containerRef={containerRef}1142 />1143 <Layout1144 getLayoutInstance={(obj) => docker.current = obj}1145 defaultLayout={defaultLayout}1146 layoutId="Debugger/Layout"1147 savedLayout={savedLayout}1148 />1149 </Box>1150 </DebuggerEventsContext.Provider>1151 </DebuggerContext.Provider>1152 );1153}1154 1155DebuggerComponent.propTypes = {1156 pgAdmin: PropTypes.object,1157 selectedNodeInfo: PropTypes.object,1158 panelId: PropTypes.string,1159 panelDocker: PropTypes.object,1160 eventBusObj: PropTypes.object,1161 layout: PropTypes.string,1162 params: PropTypes.object1163};1164 