Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
__init__.py2231 linesDownload Raw Back to debugger
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 10"""A blueprint module implementing the debugger"""11 12import json13import secrets14import re15import copy16 17from flask import render_template, request, current_app18from flask_babel import gettext19from pgadmin.user_login_check import pga_login_required20from werkzeug.user_agent import UserAgent21 22from pgadmin.utils import PgAdminModule, \23    SHORTCUT_FIELDS as shortcut_fields, \24    ACCESSKEY_FIELDS as accesskey_fields25from pgadmin.utils.ajax import bad_request26from pgadmin.utils.ajax import make_json_response, \27    internal_server_error, gone28from pgadmin.utils.driver import get_driver29from pgadmin.settings import get_setting30 31from config import PG_DEFAULT_DRIVER32from pgadmin.model import db, DebuggerFunctionArguments33from pgadmin.tools.debugger.utils.debugger_instance import DebuggerInstance34from pgadmin.browser.server_groups.servers.databases.extensions.utils \35    import get_extension_details36from pgadmin.utils.constants import PREF_LABEL_KEYBOARD_SHORTCUTS, \37    SERVER_CONNECTION_CLOSED38from pgadmin.preferences import preferences39 40MODULE_NAME = 'debugger'41 42# Constants43PLDBG_EXTN = 'pldbgapi'44ASYNC_OK = 145DEBUGGER_SQL_PATH = 'debugger/sql'46DEBUGGER_SQL_V1_PATH = 'debugger/sql/v1'47DEBUGGER_SQL_V3_PATH = 'debugger/sql/v3'48 49 50class DebuggerModule(PgAdminModule):51    """52    class DebuggerModule(PgAdminModule)53 54        A module class for debugger which is derived from PgAdminModule.55    """56    LABEL = gettext("Debugger")57 58    def register_preferences(self):59        self.preference.register(60            'keyboard_shortcuts', 'btn_start',61            gettext('Accesskey (Continue/Start)'), 'keyboardshortcut',62            {63                'key': {64                    'key_code': 67,65                    'char': 'c'66                }67            },68            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,69            fields=accesskey_fields70        )71 72        self.preference.register(73            'keyboard_shortcuts', 'btn_stop',74            gettext('Accesskey (Stop)'), 'keyboardshortcut',75            {76                'key': {77                    'key_code': 83,78                    'char': 's'79                }80            },81            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,82            fields=accesskey_fields83        )84 85        self.preference.register(86            'keyboard_shortcuts', 'btn_step_into',87            gettext('Accesskey (Step into)'), 'keyboardshortcut',88            {89                'key': {90                    'key_code': 73,91                    'char': 'i'92                }93            },94            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,95            fields=accesskey_fields96        )97 98        self.preference.register(99            'keyboard_shortcuts', 'btn_step_over',100            gettext('Accesskey (Step over)'), 'keyboardshortcut',101            {102                'key': {103                    'key_code': 79,104                    'char': 'o'105                }106            },107            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,108            fields=accesskey_fields109        )110 111        self.preference.register(112            'keyboard_shortcuts', 'btn_toggle_breakpoint',113            gettext('Accesskey (Toggle breakpoint)'), 'keyboardshortcut',114            {115                'key': {116                    'key_code': 84,117                    'char': 't'118                }119            },120            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,121            fields=accesskey_fields122        )123 124        self.preference.register(125            'keyboard_shortcuts', 'btn_clear_breakpoints',126            gettext('Accesskey (Clear all breakpoints)'), 'keyboardshortcut',127            {128                'key': {129                    'key_code': 88,130                    'char': 'x'131                }132            },133            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,134            fields=accesskey_fields135        )136 137        self.preference.register(138            'keyboard_shortcuts',139            'edit_grid_values',140            gettext('Edit grid values'),141            'keyboardshortcut',142            {143                'alt': True,144                'shift': True,145                'control': False,146                'key': {147                    'key_code': 81,148                    'char': 'q'149                }150            },151            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,152            fields=shortcut_fields153        )154 155        self.preference.register(156            'keyboard_shortcuts',157            'move_previous',158            gettext('Previous tab'),159            'keyboardshortcut',160            {161                'alt': True,162                'shift': True,163                'control': False,164                'key': {165                    'key_code': 219,166                    'char': '['167                }168            },169            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,170            fields=shortcut_fields171        )172 173        self.preference.register(174            'keyboard_shortcuts',175            'move_next',176            gettext('Next tab'),177            'keyboardshortcut',178            {179                'alt': True,180                'shift': True,181                'control': False,182                'key': {183                    'key_code': 221,184                    'char': ']'185                }186            },187            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,188            fields=shortcut_fields189        )190 191        self.preference.register(192            'keyboard_shortcuts',193            'switch_panel',194            gettext('Switch Panel'),195            'keyboardshortcut',196            {197                'alt': True,198                'shift': True,199                'control': False,200                'key': {201                    'key_code': 9,202                    'char': 'Tab'203                }204            },205            category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,206            fields=shortcut_fields207        )208 209    def get_exposed_url_endpoints(self):210        """211        Returns the list of URLs exposed to the client.212        """213        return ['debugger.index', 'debugger.init_for_function',214                'debugger.init_for_trigger',215                'debugger.direct', 'debugger.initialize_target_for_function',216                'debugger.initialize_target_for_trigger', 'debugger.close',217                'debugger.restart',218                'debugger.start_listener', 'debugger.execute_query',219                'debugger.messages',220                'debugger.start_execution', 'debugger.set_breakpoint',221                'debugger.clear_all_breakpoint', 'debugger.deposit_value',222                'debugger.select_frame', 'debugger.get_arguments',223                'debugger.set_arguments', 'debugger.clear_arguments',224                'debugger.poll_end_execution_result', 'debugger.poll_result'225                ]226 227    def on_logout(self):228        """229        This is a callback function when user logout from pgAdmin230        :param user:231        :return:232        """233        close_debugger_session(None, close_all=True)234 235 236blueprint = DebuggerModule(MODULE_NAME, __name__)237 238 239@blueprint.route("/", endpoint='index')240@pga_login_required241def index():242    return bad_request(243        errormsg=gettext("This URL cannot be called directly.")244    )245 246 247def execute_dict_search_path(conn, sql, search_path):248    sql_search = "SET search_path={0};".format(search_path)249    status, res = conn.execute_void(sql_search)250 251    if not status:252        current_app.logger.debug(253            "Error setting the search path.")254        return False, res255 256    status, res = conn.execute_dict(sql)257    return status, res258 259 260def execute_async_search_path(conn, sql, search_path):261    sql_search = "SET search_path={0};".format(search_path)262    status, res = conn.execute_void(sql_search)263 264    if not status:265        current_app.logger.debug(266            "Error setting the search path.")267        return False, res268 269    status, res = conn.execute_async(sql)270    return status, res271 272 273def check_server_type(server_type, manager):274    """275    Check for server is ppas or not and is_proc_supported or not.276    :param server_type:277    :param manager:278    :return:279    """280    ppas_server = False281    is_proc_supported = False282    if server_type == 'ppas':283        ppas_server = True284    else:285        is_proc_supported = True if manager.version >= 110000 else False286 287    return ppas_server, is_proc_supported288 289 290def check_node_type(node_type, fid, trid, conn, ppas_server,291                    is_proc_supported):292    """293    Check node type and return fid.294    :param node_type:295    :param fid:296    :param trid:297    :param conn:298    :param ppas_server:299    :param is_proc_supported:300    :return:301    """302    if node_type == 'trigger':303        # Find trigger function id from trigger id304        sql = render_template(305            "/".join([DEBUGGER_SQL_PATH, 'get_trigger_function_info.sql']),306            table_id=fid, trigger_id=trid307        )308 309        status, tr_set = conn.execute_dict(sql)310        if not status:311            current_app.logger.debug(312                "Error retrieving trigger function information from database")313            return True, internal_server_error(errormsg=tr_set), None, None314 315        fid = tr_set['rows'][0]['tgfoid']316 317    # if ppas server and node type is edb function or procedure then extract318    # last argument as function id319    if node_type == 'edbfunc' or node_type == 'edbproc':320        fid = trid321 322    sql = ''323    sql = render_template(324        "/".join([DEBUGGER_SQL_PATH, 'get_function_debug_info.sql']),325        is_ppas_database=ppas_server,326        hasFeatureFunctionDefaults=True,327        fid=fid,328        is_proc_supported=is_proc_supported329 330    )331    status, r_set = conn.execute_dict(sql)332    if not status:333        current_app.logger.debug(334            "Error retrieving function information from database")335        return True, internal_server_error(errormsg=r_set), None, None336 337    if len(r_set['rows']) == 0:338        return True, gone(339            gettext("The specified %s could not be found." % node_type)), \340            None, None341 342    return False, None, r_set, status343 344 345@blueprint.route(346    '/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>',347    methods=['GET'], endpoint='init_for_function'348)349@blueprint.route(350    '/init/<node_type>/<int:sid>/<int:did>/<int:scid>/<int:fid>/<int:trid>',351    methods=['GET'], endpoint='init_for_trigger'352)353@pga_login_required354def init_function(node_type, sid, did, scid, fid, trid=None):355    """356    init_function(node_type, sid, did, scid, fid, trid)357 358    This method is responsible to initialize the function required for359    debugging.360    This method is also responsible for storing the all functions data to361    session variable.362    This is only required for direct debugging. As Indirect debugging does363    not require these data because user will364    provide all the arguments and other functions information through another365    session to invoke the target.366    It will also create a unique transaction id and store the information367    into session variable.368 369    Parameters:370        node_type371        - Node type - Function or Procedure372        sid373        - Server Id374        did375        - Database Id376        scid377        - Schema Id378        fid379        - Function Id380        trid381        - Trigger Function Id382    """383    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)384    conn = manager.connection(did=did)385 386    # Get the server version, server type and user information387    server_type = manager.server_type388 389    # Check server type is ppas or not390    ppas_server, is_proc_supported = check_server_type(server_type, manager)391 392    is_error, err_msg, r_set, status = check_node_type(node_type, fid, trid,393                                                       conn, ppas_server,394                                                       is_proc_supported)395    if is_error:396        return err_msg397 398    ret_status = status399    msg = ''400    # Check that the function is actually debuggable...401    if r_set['rows'][0]:402        # If func/proc is not defined in package body403        # then it is not debuggable404        if (r_set['rows'][0]['pkgname'] is not None or405            r_set['rows'][0]['pkgname'] != '') and \406                r_set['rows'][0]['prosrc'] == '':407            ret_status = False408            msg = r_set['rows'][0]['name'] + ' ' + \409                gettext("is not defined in package body.")410 411        # Function with a colon in the name cannot be debugged.412        # If this is an EDB wrapped function, no debugging allowed413        # Function with return type "trigger" cannot be debugged.414        elif ":" in r_set['rows'][0]['name']:415            ret_status = False416            msg = gettext(417                "Functions with a colon in the name cannot be debugged.")418        elif ppas_server and r_set['rows'][0]['prosrc'].lstrip().startswith(419                '$__EDBwrapped__$'):420            ret_status = False421            msg = gettext(422                "EDB Advanced Server wrapped functions cannot be debugged.")423        # We cannot debug if PPAS and argument mode is VARIADIC424        elif ppas_server and r_set['rows'][0]['lanname'] == 'edbspl' and \425                r_set['rows'][0]['proargmodes'] is not None and \426                'v' in r_set['rows'][0]['proargmodes']:427            ret_status = False428            msg = gettext(429                "An 'edbspl' target with a variadic argument is not supported"430                " and cannot be debugged."431            )432        else:433            is_error, err_msg, ret_status, msg = check_debugger_enabled(434                conn, ret_status)435 436            if is_error:437                return err_msg438 439    # Return the response that function cannot be debug...440    if not ret_status:441        current_app.logger.debug(msg)442        return internal_server_error(msg)443 444    data = {'name': r_set['rows'][0]['proargnames'],445            'type': r_set['rows'][0]['proargtypenames'],446            'use_default': r_set['rows'][0]['pronargdefaults'],447            'default_value': r_set['rows'][0]['proargdefaults'],448            'require_input': True}449 450    # Below will check do we really required for the user input arguments and451    # show input dialog452    data['require_input'] = check_user_ip_req(r_set, data)453 454    r_set['rows'][0]['require_input'] = data['require_input']455 456    # Create a debugger instance457    de_inst = DebuggerInstance()458    de_inst.function_data = {459        'oid': fid,460        'name': r_set['rows'][0]['name'],461        'is_func': r_set['rows'][0]['isfunc'],462        'is_ppas_database': ppas_server,463        'is_callable': False,464        'schema': r_set['rows'][0]['schemaname'],465        'language': r_set['rows'][0]['lanname'],466        'return_type': r_set['rows'][0]['rettype'],467        'args_type': r_set['rows'][0]['proargtypenames'],468        'args_name': r_set['rows'][0]['proargnames'],469        'arg_mode': r_set['rows'][0]['proargmodes'],470        'use_default': r_set['rows'][0]['pronargdefaults'],471        'default_value': r_set['rows'][0]['proargdefaults'],472        'pkgname': r_set['rows'][0]['pkgname'],473        'pkg': r_set['rows'][0]['pkg'],474        'require_input': data['require_input'],475        'args_value': ''476    }477 478    return make_json_response(479        data=dict(480            debug_info=r_set['rows'],481            trans_id=de_inst.trans_id482        ),483        status=200484    )485 486 487def check_debugger_enabled(conn, ret_status):488    """489    Check debugger enabled or not, also check pldbgapi extension is present490    or not491    :param conn:492    :param ret_status:493    :return:494    """495    msg = ''496    status_in, rid_tar = conn.execute_scalar(497        "SELECT count(*) FROM pg_catalog.pg_proc WHERE "498        "proname = 'pldbg_get_target_info'"499    )500    if not status_in:501        current_app.logger.debug(502            "Failed to find the pldbgapi extension in this database.")503        return True, internal_server_error(504            gettext("Failed to find the pldbgapi extension in "505                    "this database.")506        ), None, None507 508    # We also need to check to make sure that the debugger library is509    # also available.510    status_in, ret_oid = conn.execute_scalar(511        "SELECT count(*) FROM pg_catalog.pg_proc WHERE "512        "proname = 'plpgsql_oid_debug'"513    )514    if not status_in:515        current_app.logger.debug(516            "Failed to find the pldbgapi extension in this database.")517        return True, internal_server_error(518            gettext("Failed to find the pldbgapi extension in "519                    "this database.")520        ), None, None521 522    # Debugger plugin is configured but pldggapi extension is not523    # created so return error524    if rid_tar == '0' or ret_oid == '0':525        msg = gettext(526            "The debugger plugin is not enabled. Please create the "527            "pldbgapi extension in this database."528        )529        ret_status = False530    return False, None, ret_status, msg531 532 533def check_user_ip_req(r_set, data):534    """535    Check if user input is required or not for debugger.536    :param r_set:537    :param data:538    :return:539    """540    if not r_set['rows'][0]['proargtypenames']:541        return False542 543    if r_set['rows'][0]['pkg'] != 0 and \544            r_set['rows'][0]['pkgconsoid'] != 0:545        data['require_input'] = True546 547    if r_set['rows'][0]['proargmodes']:548        pro_arg_modes = r_set['rows'][0]['proargmodes'].split(",")549        for pr_arg_mode in pro_arg_modes:550            if pr_arg_mode == 'o' or pr_arg_mode == 't':551                data['require_input'] = False552            else:553                data['require_input'] = True554                break555 556    return data['require_input']557 558 559@blueprint.route('/direct/<int:trans_id>', methods=['GET'], endpoint='direct')560@pga_login_required561def direct_new(trans_id):562    de_inst = DebuggerInstance(trans_id)563 564    # Return from the function if transaction id not found565    if de_inst.debugger_data is None:566        return make_json_response(data={'status': True})567 568    # if indirect debugging pass value 0 to client and for direct debugging569    # pass it to 1570    debug_type = 0 if de_inst.debugger_data['debug_type'] == 'indirect' else 1571 572    """573    Animations and transitions are not automatically GPU accelerated and by574    default use browser's slow rendering engine.575    We need to set 'translate3d' value of '-webkit-transform' property in576    order to use GPU.577    After applying this property under linux, Webkit calculates wrong position578    of the elements so panel contents are not visible.579    To make it work, we need to explicitly set '-webkit-transform' property580    to 'none' for .ajs-notifier, .ajs-message, .ajs-modal classes.581 582    This issue is only with linux runtime application and observed in Query583    tool and debugger. When we open 'Open File' dialog then whole Query-tool584    panel content is not visible though it contains HTML element in back end.585 586    The port number should have already been set by the runtime if we're587    running in desktop mode.588    """589    is_linux_platform = False590 591    from sys import platform as _platform592    if "linux" in _platform:593        is_linux_platform = True594 595    # We need client OS information to render correct Keyboard shortcuts596    user_agent = UserAgent(request.headers.get('User-Agent'))597 598    function_arguments = '('599    if de_inst.function_data is not None and \600        'args_name' in de_inst.function_data and \601        de_inst.function_data['args_name'] is not None and \602            de_inst.function_data['args_name'] != '':603        args_name_list = de_inst.function_data['args_name'].split(",")604        args_type_list = de_inst.function_data['args_type'].split(",")605        index = 0606        for args_name in args_name_list:607            function_arguments = '{}{} {}, '.format(function_arguments,608                                                    args_name,609                                                    args_type_list[index])610            index += 1611        # Remove extra comma and space from the arguments list612        if len(args_name_list) > 0:613            function_arguments = function_arguments[:-2]614 615    function_arguments += ')'616 617    layout = get_setting('Debugger/Layout')618 619    function_name_with_arguments = \620        de_inst.debugger_data['function_name'] + function_arguments621 622    manager = get_driver(PG_DEFAULT_DRIVER).get_connection(623        de_inst.debugger_data['server_id'],624        database=de_inst.debugger_data['database_id'],625        conn_id=de_inst.debugger_data['conn_id'])626    title = get_debugger_title(de_inst.debugger_data['function_name'],627                               function_arguments,628                               de_inst.function_data['schema'], manager.db)629 630    return render_template(631        "debugger/direct.html",632        _=gettext,633        function_name=de_inst.debugger_data['function_name'],634        title=title,635        uniqueId=trans_id,636        debug_type=debug_type,637        is_desktop_mode=current_app.PGADMIN_RUNTIME,638        is_linux=is_linux_platform,639        client_platform=user_agent.platform,640        function_name_with_arguments=function_name_with_arguments,641        layout=layout642    )643 644 645def get_debugger_title(function_name, args, schema, database):646    browser_pref = preferences('browser', 'debugger_tab_title_placeholder')647    placeholders = browser_pref.json['value']648    title = placeholders.replace('%FUNCTION%', function_name)649    if title.find('%ARGS%') != -1:650        args = args.split('(')[-1][:-1]651        title = title.replace('%ARGS%', args)652    title = title.replace('%SCHEMA%', schema)653    title = title.replace('%DATABASE%', database)654 655    return title656 657 658def get_debugger_version(conn, search_path):659    """660    Function returns the debugger version.661    :param conn:662    :return:663    """664    debugger_version = 0665    status, res = conn.execute_void("SET search_path={0};".format(search_path))666 667    if not status:668        return False, internal_server_error(errormsg=res)669 670    status, rid = conn.execute_scalar(671        "SELECT COUNT(*) FROM pg_catalog.pg_proc p"672        " LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid"673        " WHERE n.nspname = ANY(current_schemas(false)) AND"674        " p.proname = 'pldbg_get_proxy_info';"675    )676 677    if not status:678        return False, internal_server_error(errormsg=rid)679 680    if int(rid) == 0:681        debugger_version = 1682    else:683        status, rid = conn.execute_scalar(684            "SELECT proxyapiver FROM pldbg_get_proxy_info();")685 686        if status and rid in (2, 3):687            debugger_version = rid688 689    return True, debugger_version690 691 692def validate_debug(conn, debug_type, is_superuser):693    """694    This function is used to validate the options required for debugger.695    :param conn:696    :param debug_type:697    :param is_superuser:698    :return:699    """700    if debug_type == 'indirect':701        if not is_superuser:702            # If user is super user then we should check debugger library is703            # loaded or not704            msg = gettext("You must be a superuser to set a global breakpoint"705                          " and perform indirect debugging.")706            return False, internal_server_error(errormsg=msg)707 708        status, rid_pre = conn.execute_scalar(709            "SHOW shared_preload_libraries"710        )711 712        if not status:713            return False, internal_server_error(714                gettext("Could not fetch debugger plugin information.")715            )716 717        # Need to check if plugin is really loaded or not with718        # "plugin_debugger" string719        if "plugin_debugger" not in rid_pre:720            msg = gettext(721                "The debugger plugin is not enabled. "722                "Please add the plugin to the shared_preload_libraries "723                "setting in the postgresql.conf file and restart the "724                "database server for indirect debugging."725            )726            current_app.logger.debug(msg)727            return False, internal_server_error(msg)728 729    # Check debugger extension version for EPAS 11 and above.730    # If it is 1.0 then return error to upgrade the extension.731    status, ext_version = conn.execute_scalar(732        "SELECT installed_version FROM pg_catalog.pg_available_extensions "733        "WHERE name = 'pldbgapi'"734    )735    if not status:736        return False, internal_server_error(errormsg=ext_version)737    if conn.manager.server_type == 'ppas' and conn.manager.sversion >= 110000 \738            and float(ext_version) < 1.1:739        return False, internal_server_error(740            errormsg=gettext("Please upgrade the pldbgapi extension "741                             "to 1.1 or above and try again."))742 743    return True, None744 745 746def get_search_path(conn):747    status, res = get_extension_details(conn, PLDBG_EXTN)748    if not status:749        return False, internal_server_error(errormsg=res)750 751    status, res = conn.execute_scalar(752        "SELECT current_setting('search_path')||',{0}'".format(res['schema']))753 754    if not status:755        return False, internal_server_error(errormsg=res)756 757    return True, res758 759 760@blueprint.route(761    '/initialize_target/<debug_type>/<int:trans_id>/<int:sid>/<int:did>/'762    '<int:scid>/<int:func_id>',763    methods=['POST'],764    endpoint='initialize_target_for_function'765)766@blueprint.route(767    '/initialize_target/<debug_type>/<int:trans_id>/<int:sid>/<int:did>/'768    '<int:scid>/<int:func_id>/<int:tri_id>',769    methods=['POST'],770    endpoint='initialize_target_for_trigger'771)772@pga_login_required773def initialize_target(debug_type, trans_id, sid, did,774                      scid, func_id, tri_id=None):775    """776    initialize_target(debug_type, sid, did, scid, func_id, tri_id)777 778    This method is responsible for creating an asynchronous connection.779 780    Parameters:781        debug_type782        - Type of debugging (Direct or Indirect)783        sid784        - Server Id785        did786        - Database Id787        scid788        - Schema Id789        func_id790        - Function Id791        tri_id792        - Trigger Function Id793    """794 795    # Create asynchronous connection using random connection id.796    conn_id = str(secrets.choice(range(1, 9999999)))797    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)798    conn = manager.connection(did=did, conn_id=conn_id)799    data_obj = {}800 801    # Connect the Server802    status, msg = conn.connect()803    if not status:804        return internal_server_error(errormsg=str(msg))805 806    user = manager.user_info807    status, error = validate_debug(conn, debug_type, user['is_superuser'])808    if not status:809        return error810 811    if tri_id is not None:812        # Find trigger function id from trigger id813        sql = render_template(814            "/".join([DEBUGGER_SQL_PATH, 'get_trigger_function_info.sql']),815            table_id=func_id, trigger_id=tri_id816        )817 818        status, tr_set = conn.execute_dict(sql)819        if not status:820            current_app.logger.debug(821                "Error retrieving trigger function information from database")822            return internal_server_error(errormsg=tr_set)823 824        func_id = tr_set['rows'][0]['tgfoid']825 826    status, search_path = get_search_path(conn)827    if not status:828        return search_path829 830    # Find out the debugger version and store it in session variables831    status, debugger_version = get_debugger_version(conn, search_path)832    if not status:833        return debugger_version834 835    # Add the debugger version information to pgadmin4 log file836    current_app.logger.debug("Debugger version is: %d", debugger_version)837 838    de_inst = DebuggerInstance(trans_id)839    # We need to pass the value entered by the user in dialog for direct840    # debugging, Here we get the value in case of direct debugging so update841    # the session variables accordingly, For indirect debugging user will842    # provide the data from another session so below condition will843    # be be required844    if request.data:845        de_inst.function_data['args_value'] = \846            json.loads(request.data)847 848    # Update the debugger data session variable849    # Here frame_id is required when user debug the multilevel function.850    # When user select the frame from client we need to update the frame851    # here and set the breakpoint information on that function oid852    de_inst.debugger_data = {853        'conn_id': conn_id,854        'server_id': sid,855        'database_id': did,856        'schema_id': scid,857        'function_id': func_id,858        'function_name': de_inst.function_data['name'],859        'debug_type': debug_type,860        'debugger_version': debugger_version,861        'search_path': search_path,862        'frame_id': 0,863        'restart_debug': 0864    }865 866    de_inst.update_session()867    data_obj['db_name'] = conn.db868 869    return make_json_response(data={'status': status,870                                    'debuggerTransId': trans_id,871                                    'data_obj': data_obj})872 873 874@blueprint.route(875    '/close/<int:trans_id>', methods=["DELETE"], endpoint='close'876)877def close(trans_id):878    """879    close(trans_id)880 881    This method is used to close the asynchronous connection882    and remove the information of unique transaction id from883    the session variable.884 885    Parameters:886        trans_id887        - unique transaction id.888    """889 890    close_debugger_session(trans_id)891    return make_json_response(data={'status': True})892 893 894@blueprint.route(895    '/restart/<int:trans_id>', methods=['GET'], endpoint='restart'896)897@pga_login_required898def restart_debugging(trans_id):899    """900    restart_debugging(trans_id)901 902    This method is responsible to restart the same function for the debugging.903 904    Parameters:905        trans_id906        - Transaction ID907    """908 909    de_inst = DebuggerInstance(trans_id)910    if de_inst.debugger_data is None:911        return make_json_response(912            data={913                'status': False,914                'result': SERVER_CONNECTION_CLOSED915            }916        )917 918    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(919        de_inst.debugger_data['server_id'])920    conn = manager.connection(921        did=de_inst.debugger_data['database_id'],922        conn_id=de_inst.debugger_data['conn_id'])923 924    if conn.connected():925        # Update the session variable "restart_debug" to know that same926        # function debugging has been restarted. Delete the existing debugger927        # data in session variable and update with new data928        if de_inst.debugger_data['restart_debug'] == 0:929            de_inst.debugger_data['restart_debug'] = 1930            de_inst.update_session()931 932        de_inst.function_data.update({933            'server_id': de_inst.debugger_data['server_id'],934            'database_id': de_inst.debugger_data['database_id'],935            'schema_id': de_inst.debugger_data['schema_id'],936            'function_id': de_inst.debugger_data['function_id'],937            'trans_id': str(trans_id),938            'proargmodes': de_inst.function_data['arg_mode'],939            'proargtypenames': de_inst.function_data['args_type'],940            'pronargdefaults': de_inst.function_data['use_default'],941            'proargdefaults': de_inst.function_data['default_value'],942            'proargnames': de_inst.function_data['args_name'],943            'require_input': de_inst.function_data['require_input']944        })945 946        return make_json_response(947            data={948                'status': True, 'restart_debug': True,949                'result': de_inst.function_data950            }951        )952    else:953        status = False954        result = SERVER_CONNECTION_CLOSED955        return make_json_response(data={'status': status, 'result': result})956 957 958@blueprint.route(959    '/start_listener/<int:trans_id>', methods=['POST'],960    endpoint='start_listener'961)962@pga_login_required963def start_debugger_listener(trans_id):964    """965    start_debugger_listener(trans_id)966 967    This method is responsible to listen and get the required information968    requested by user during debugging969 970    Parameters:971        trans_id972        - Transaction ID973    """974 975    de_inst = DebuggerInstance(trans_id)976    if de_inst.debugger_data is None:977        return make_json_response(978            data={979                'status': False,980                'result': SERVER_CONNECTION_CLOSED981            }982        )983 984    driver = get_driver(PG_DEFAULT_DRIVER)985    manager = driver.connection_manager(de_inst.debugger_data['server_id'])986    conn = manager.connection(987        did=de_inst.debugger_data['database_id'],988        conn_id=de_inst.debugger_data['conn_id'])989 990    ver = manager.version991    server_type = manager.server_type992 993    # find the debugger version and execute the query accordingly994    dbg_version = de_inst.debugger_data['debugger_version']995    if dbg_version <= 2:996        template_path = DEBUGGER_SQL_V1_PATH997    else:998        template_path = DEBUGGER_SQL_V3_PATH999 1000    # If user again start the same debug function with different arguments1001    # then we need to save that values to session variable and database.1002    if request.data:1003        data = json.loads(request.data)1004        if data:1005            de_inst.function_data['args_value'] = data1006            de_inst.update_session()1007 1008    if conn.connected():1009 1010        # For the direct debugging extract the function arguments values from1011        # user and pass to jinja template to create the query for execution.1012        if de_inst.debugger_data['debug_type'] == 'direct':1013            str_query = ''1014            if de_inst.function_data['pkg'] == 0:1015                # Form the function name with schema name1016                func_name = driver.qtIdent(1017                    conn,1018                    de_inst.function_data['schema'],1019                    de_inst.function_data['name']1020                )1021            else:1022                # Form the edb package function/procedure name with schema name1023                func_name = driver.qtIdent(1024                    conn, de_inst.function_data['schema'],1025                    de_inst.function_data['pkgname'],1026                    de_inst.function_data['name']1027                )1028 1029            if de_inst.debugger_data['restart_debug'] == 0:1030                # render the SQL template and send the query to server1031                if de_inst.function_data['language'] == 'plpgsql':1032                    sql = render_template(1033                        "/".join([template_path,1034                                  'debug_plpgsql_execute_target.sql']),1035                        packge_oid=de_inst.function_data['pkg'],1036                        function_oid=de_inst.debugger_data['function_id']1037                    )1038                else:1039                    sql = render_template(1040                        "/".join([template_path,1041                                  'debug_spl_execute_target.sql']),1042                        packge_oid=de_inst.function_data['pkg'],1043                        function_oid=de_inst.debugger_data['function_id']1044                    )1045                status, res = execute_dict_search_path(1046                    conn, sql, de_inst.debugger_data['search_path'])1047                if not status:1048                    return internal_server_error(errormsg=res)1049 1050            if de_inst.function_data['arg_mode']:1051                # In EDBAS 90, if an SPL-function has both an OUT-parameter1052                # and a return value (which is not possible on PostgreSQL1053                # otherwise), the return value is transformed into an extra1054                # OUT-parameter named "_retval_"1055                if de_inst.function_data['args_name']:1056                    arg_name = de_inst.function_data['args_name'].split(",")1057                    if '_retval_' in arg_name:1058                        arg_mode = de_inst.function_data['arg_mode'].split(",")1059                        arg_mode.pop()1060                    else:1061                        arg_mode = de_inst.function_data['arg_mode'].split(",")1062                else:1063                    arg_mode = de_inst.function_data['arg_mode'].split(",")1064            else:1065                arg_mode = ['i'] * len(1066                    de_inst.function_data['args_type'].split(",")1067                )1068 1069            if de_inst.function_data['args_type']:1070                if de_inst.function_data['args_name']:1071                    arg_name = de_inst.function_data['args_name'].split(",")1072                    if '_retval_' in arg_name:1073                        arg_type = de_inst.function_data[1074                            'args_type'].split(",")1075                        arg_type.pop()1076                    else:1077                        arg_type = de_inst.function_data[1078                            'args_type'].split(",")1079                else:1080                    arg_type = de_inst.function_data['args_type'].split(",")1081 1082            debugger_args_values = []1083            if de_inst.function_data['args_value']:1084                debugger_args_values = copy.deepcopy(1085                    de_inst.function_data['args_value'])1086                for arg in debugger_args_values:1087                    if arg['type'].endswith('[]') and arg['value'] and arg[1088                            'value'] != 'NULL':1089                        val_list = arg['value'][1:-1].split(',')1090                        arg['value'] = get_debugger_arg_val(val_list)1091 1092            # Below are two different template to execute and start executer1093            if manager.server_type != 'pg' and manager.version < 90300:1094                str_query = render_template(1095                    "/".join([DEBUGGER_SQL_PATH, 'execute_edbspl.sql']),1096                    func_name=func_name,1097                    is_func=de_inst.function_data['is_func'],1098                    lan_name=de_inst.function_data['language'],1099                    ret_type=de_inst.function_data['return_type'],1100                    data=debugger_args_values,1101                    arg_type=arg_type,1102                    args_mode=arg_mode,1103                    conn=conn1104                )1105            else:1106                str_query = render_template(1107                    "/".join([DEBUGGER_SQL_PATH, 'execute_plpgsql.sql']),1108                    func_name=func_name,1109                    is_func=de_inst.function_data['is_func'],1110                    ret_type=de_inst.function_data['return_type'],1111                    data=debugger_args_values,1112                    is_ppas_database=de_inst.function_data['is_ppas_database'],1113                    conn=conn1114                )1115 1116            status, result = execute_async_search_path(1117                conn, str_query, de_inst.debugger_data['search_path'])1118 1119            if not status:1120                return internal_server_error(errormsg=result)1121        else:1122 1123            sql = render_template(1124                "/".join([template_path, 'create_listener.sql']))1125 1126            status, res = execute_dict_search_path(1127                conn, sql, de_inst.debugger_data['search_path'])1128            if not status:1129                return internal_server_error(errormsg=res)1130 1131            # Get and store the session variable which is required to fetch1132            # other information during debugging1133            int_session_id = res['rows'][0]['pldbg_create_listener']1134 1135            # In EnterpriseDB versions <= 9.1 the1136            # pldbg_set_global_breakpoint function took five arguments,1137            # the 2nd argument being the package's OID, if any. Starting1138            # with 9.2, the package OID argument is gone, and the function1139            # takes four arguments like the community version has always1140            # done.1141            if server_type == 'ppas' and ver <= 90100:1142                sql = render_template(1143                    "/".join([template_path, 'add_breakpoint_edb.sql']),1144                    session_id=int_session_id,1145                    function_oid=de_inst.debugger_data['function_id']1146                )1147 1148                status, res = execute_dict_search_path(1149                    conn, sql, de_inst.debugger_data['search_path'])1150                if not status:1151                    return internal_server_error(errormsg=res)1152            else:1153                sql = render_template(1154                    "/".join([template_path, 'add_breakpoint_pg.sql']),1155                    session_id=int_session_id,1156                    function_oid=de_inst.debugger_data['function_id']1157                )1158 1159                status, res = execute_dict_search_path(1160                    conn, sql, de_inst.debugger_data['search_path'])1161                if not status:1162                    return internal_server_error(errormsg=res)1163 1164            # wait for the target1165            sql = render_template(1166                "/".join([template_path, 'wait_for_target.sql']),1167                session_id=int_session_id1168            )1169 1170            de_inst.debugger_data['exe_conn_id'] = \1171                de_inst.debugger_data['conn_id']1172            de_inst.debugger_data['restart_debug'] = 11173            de_inst.debugger_data['frame_id'] = 01174            de_inst.debugger_data['session_id'] = int_session_id1175            de_inst.update_session()1176 1177            status, res = execute_async_search_path(1178                conn, sql, de_inst.debugger_data['search_path'])1179            if not status:1180                return internal_server_error(errormsg=res)1181 1182            return make_json_response(1183                data={'status': status, 'result': res}1184            )1185    else:1186        status = False1187        result = SERVER_CONNECTION_CLOSED1188 1189    return make_json_response(data={'status': status, 'result': result})1190 1191 1192def get_debugger_arg_val(val_list):1193    """Get debugger arguments is list"""1194    debugger_args_data = []1195    for _val in val_list:1196        debugger_args_data.append({1197            'value': _val1198        })1199    return debugger_args_data1200 

Showing the first 1,200 of 2231 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai