Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py904 linesDownload Raw Back to event_triggers
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 json11import re12from functools import wraps13 14import pgadmin.browser.server_groups.servers.databases as database15from flask import render_template, request, jsonify16from flask_babel import gettext17from pgadmin.browser.collection import CollectionNodeModule18from pgadmin.browser.utils import PGChildNodeView19from pgadmin.utils.ajax import make_json_response, internal_server_error, \20    make_response as ajax_response, gone21from pgadmin.utils.driver import get_driver22from config import PG_DEFAULT_DRIVER23from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry24from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare25 26 27class EventTriggerModule(CollectionNodeModule):28    """29    class EventTriggerModule(CollectionNodeModule)30 31        A module class for Event trigger node derived from32        CollectionNodeModule.33 34    Methods:35    -------36    * __init__(*args, **kwargs)37      - Method is used to initialize the EventTriggerModule and it's base38        module.39 40    * get_nodes(gid, sid, did)41      - Method is used to generate the browser collection node.42 43    * script_load()44      - Load the module script for Event trigger, when any of the database node45        is initialized.46    """47 48    _NODE_TYPE = 'event_trigger'49    _COLLECTION_LABEL = gettext("Event Triggers")50 51    def __init__(self, *args, **kwargs):52        """53        Method is used to initialize the EventTriggerModule and it's base54        module.55 56        Args:57            *args:58            **kwargs:59        """60        super().__init__(*args, **kwargs)61        self.min_ver = 9030062        self.max_ver = None63 64    def get_nodes(self, gid, sid, did):65        """66        Generate the event_trigger node67        """68        if self.has_nodes(69            sid, did,70                base_template_path=EventTriggerView.BASE_TEMPLATE_PATH):71            yield self.generate_browser_collection_node(sid)72 73    @property74    def node_inode(self):75        """76        Always returns false, it is a leaf node, and do not have children77        nodes.78        """79        return False80 81    @property82    def script_load(self):83        """84        Load the module script for event_trigger, when any of the database node85        is initialized.86        """87        return database.DatabaseModule.node_type88 89    @property90    def module_use_template_javascript(self):91        """92        Returns whether Jinja2 template is used for generating the javascript93        module.94        """95        return False96 97 98blueprint = EventTriggerModule(__name__)99 100 101class EventTriggerView(PGChildNodeView, SchemaDiffObjectCompare):102    """103    class EventTriggerView(PGChildNodeView)104 105        A view class for event trigger node derived from PGChildNodeView.106        This class is responsible for all the stuff related to view like107        updating event trigger node, showing properties, showing sql in sql108        pane.109 110    Methods:111    -------112    * __init__(**kwargs)113      - Method is used to initialize the EventTriggerView and it's base view.114 115    * check_precondition()116      - This function will behave as a decorator which will checks117        database connection before running view, it will also attaches118        manager,conn & template_path properties to self119 120    * list()121      - Lists proroperties of all the nodes of type - event trigger.122 123    * nodes()124      - Creates all the child nodes of type - event trigger.125 126    * properties(gid, sid, did, etid)127      - Returns the properties of the given event trigger node128 129    * update(gid, sid, did, etid)130      - Updates the data for the given event trigger node.131 132    * msql(gid, sid, did, etid)133      - Return modified SQL for the given event trigger node based on the134        request data.135 136    * get_sql(data, etid)137      - Generates the sql from model data138 139    * sql(gid, sid, did, etid):140      - Generates the reversed engineered query for the given event trigger141        node.142 143    * get_event_funcs(gid, sid, did, etid):144      - Returns the event functions available in that database.145 146    * dependents(gid, sid, did, etid):147      - Returns the dependents list for the given event trigger node.148 149    * dependencies(self, gid, sid, did, etid):150      - Returns the dependencies list for the given event trigger node.151    """152 153    node_type = blueprint.node_type154    node_icon = "icon-%s" % blueprint.node_type155    BASE_TEMPLATE_PATH = 'event_triggers/sql/#{0}#'156 157    parent_ids = [158        {'type': 'int', 'id': 'gid'},159        {'type': 'int', 'id': 'sid'},160        {'type': 'int', 'id': 'did'}161    ]162    ids = [163        {'type': 'int', 'id': 'etid'}164    ]165 166    operations = dict({167        'obj': [168            {'get': 'properties', 'delete': 'delete', 'put': 'update'},169            {'get': 'list', 'post': 'create', 'delete': 'delete'}170        ],171        'nodes': [{'get': 'node'}, {'get': 'nodes'}],172        'children': [{'get': 'children'}],173        'sql': [{'get': 'sql'}],174        'msql': [{'get': 'msql'}, {'get': 'msql'}],175        'stats': [{'get': 'statistics'}],176        'dependency': [{'get': 'dependencies'}],177        'dependent': [{'get': 'dependents'}],178        'fopts': [{'get': 'get_event_funcs'}, {'get': 'get_event_funcs'}]179    })180 181    keys_to_ignore = ['oid', 'xmin', 'oid-2', 'eventfuncoid', 'schemaoid',182                      'source']183 184    def check_precondition(f):185        """186        This function will behave as a decorator which will checks187        database connection before running view, it will also attaches188        manager,conn & template_path properties to self189        """190 191        @wraps(f)192        def wrap(*args, **kwargs):193 194            # Here - args[0] will always hold self & kwargs will hold gid,195            # sid, did196            self = args[0]197            self.manager = get_driver(198                PG_DEFAULT_DRIVER199            ).connection_manager(kwargs['sid'])200            self.conn = self.manager.connection(did=kwargs['did'])201            self.template_path = self.BASE_TEMPLATE_PATH.format(202                self.manager.version)203 204            self.datistemplate = False205            if (206                self.manager.db_info is not None and207                kwargs['did'] in self.manager.db_info and208                'datistemplate' in self.manager.db_info[kwargs['did']]209            ):210                self.datistemplate = self.manager.db_info[211                    kwargs['did']]['datistemplate']212 213            return f(*args, **kwargs)214 215        return wrap216 217    @check_precondition218    def list(self, gid, sid, did):219        """220        This function is used to list all the event trigger221        nodes within that collection.222 223        Args:224          gid: Server Group ID225          sid: Server ID226          did: Database ID227          etid: Event trigger ID228 229        Returns:230 231        """232        sql = render_template("/".join([self.template_path,233                                        self._PROPERTIES_SQL]))234        status, res = self.conn.execute_dict(sql)235 236        if not status:237            return internal_server_error(errormsg=res)238 239        return ajax_response(240            response=res['rows'],241            status=200242        )243 244    @check_precondition245    def nodes(self, gid, sid, did):246        """247        This function is used to create all the child nodes within the248        collection. Here it will create all the event trigger nodes.249 250        Args:251          gid: Server Group ID252          sid: Server ID253          did: Database ID254          etid: Event trigger ID255 256        Returns:257 258        """259        result = []260        sql = render_template("/".join([self.template_path, self._NODES_SQL]))261        status, res = self.conn.execute_2darray(sql)262        if not status:263            return internal_server_error(errormsg=res)264 265        for row in res['rows']:266            result.append(267                self.blueprint.generate_browser_node(268                    row['oid'],269                    did,270                    row['name'],271                    self.node_icon,272                    description=row['comment']273                ))274 275        return make_json_response(276            data=result,277            status=200278        )279 280    @check_precondition281    def node(self, gid, sid, did, etid):282        """283        This function will fetch properties of trigger node.284 285        Args:286          gid: Server Group ID287          sid: Server ID288          did: Database ID289          etid: Event trigger ID290 291        Returns:292          Json object of trigger node293        """294        sql = render_template("/".join([self.template_path, self._NODES_SQL]),295                              etid=etid)296        status, res = self.conn.execute_2darray(sql)297        if not status:298            return internal_server_error(errormsg=res)299 300        for row in res['rows']:301            return make_json_response(302                data=self.blueprint.generate_browser_node(303                    row['oid'],304                    did,305                    row['name'],306                    self.node_icon307                ),308                status=200309            )310 311        return gone(gettext("Could not find the specified event trigger."))312 313    def _formatter(self, result):314        """315        This function is ued to parse security lables316        """317        seclabels = []318        if 'seclabels' in result and result['seclabels'] is not None:319            for sec in result['seclabels']:320                sec = re.search(r'([^=]+)=(.*$)', sec)321                seclabels.append({322                    'provider': sec.group(1),323                    'label': sec.group(2)324                })325 326        result['seclabels'] = seclabels327        return result328 329    @check_precondition330    def properties(self, gid, sid, did, etid):331        """332        This function is used to list all the event trigger333        nodes within that collection.334 335        Args:336          gid: Server Group ID337          sid: Server ID338          did: Database ID339          etid: Event trigger ID340 341        Returns:342 343        """344        status, res = self._fetch_properties(did, etid)345        if not status:346            return res347 348        return ajax_response(349            response=res,350            status=200351        )352 353    def _fetch_properties(self, did, etid):354        """355        This function fetch the properties of the event trigger.356        :param did:357        :param etid:358        :return:359        """360        sql = render_template(361            "/".join([self.template_path, self._PROPERTIES_SQL]),362            etid=etid, conn=self.conn363        )364        status, res = self.conn.execute_dict(sql)365        if not status:366            return False, internal_server_error(errormsg=res)367 368        if len(res['rows']) == 0:369            return False, gone(370                gettext("Could not find the event trigger information."))371 372        result = res['rows'][0]373        result['is_sys_obj'] = (374            result['oid'] <= self._DATABASE_LAST_SYSTEM_OID or375            self.datistemplate)376        result = self._formatter(result)377 378        return True, result379 380    @check_precondition381    def create(self, gid, sid, did):382        """383        This function will create a event trigger object.384 385        Args:386          gid: Server Group ID387          sid: Server ID388          did: Database ID389          etid: Event trigger ID390 391        Returns:392 393        """394        data = request.form if request.form else json.loads(395            request.data396        )397 398        required_args = {399            'name': 'Name',400            'eventowner': 'Owner',401            'eventfunname': 'Trigger function',402            'enabled': 'Enabled status',403            'eventname': 'Events'404        }405        err = []406        for arg in required_args:407            if arg not in data:408                err.append(required_args.get(arg, arg))409        if err:410            return make_json_response(411                status=400,412                success=0,413                errormsg=gettext(414                    "Could not find the required parameter ({}).").format(err)415            )416        try:417            sql = render_template(418                "/".join([self.template_path, self._CREATE_SQL]),419                data=data, conn=self.conn420            )421            status, res = self.conn.execute_scalar(sql)422            if not status:423                return internal_server_error(errormsg=res)424            sql = render_template(425                "/".join([self.template_path, self._GRANT_SQL]),426                data=data, conn=self.conn427            )428            sql = sql.strip('\n').strip(' ')429 430            status, res = self.conn.execute_scalar(sql)431            if not status:432                return internal_server_error(errormsg=res)433 434            sql = render_template(435                "/".join([self.template_path, self._OID_SQL]),436                data=data, conn=self.conn437            )438            status, etid = self.conn.execute_scalar(sql)439            if not status:440                return internal_server_error(errormsg=etid)441 442            return jsonify(443                node=self.blueprint.generate_browser_node(444                    etid,445                    did,446                    data['name'],447                    self.node_icon448                )449            )450        except Exception as e:451            return internal_server_error(errormsg=str(e))452 453    @check_precondition454    def update(self, gid, sid, did, etid):455        """456        This function will update the data for the selected457        event trigger node.458 459        Args:460          gid: Server Group ID461          sid: Server ID462          did: Database ID463          etid: Event trigger ID464 465        Returns:466 467        """468        data = request.form if request.form else json.loads(469            request.data470        )471 472        try:473            sql = self.get_sql(data, etid)474            # Most probably this is due to error475            if not isinstance(sql, str):476                return sql477 478            if sql != "":479                status, res = self.conn.execute_scalar(sql)480                if not status:481                    return internal_server_error(errormsg=res)482 483                sql = render_template(484                    "/".join([self.template_path, self._OID_SQL]),485                    data=data, conn=self.conn486                )487                status, etid = self.conn.execute_scalar(sql)488 489                other_node_info = {}490                if 'comment' in data:491                    other_node_info['description'] = data['comment']492 493                return jsonify(494                    node=self.blueprint.generate_browser_node(495                        etid,496                        did,497                        data['name'],498                        self.node_icon,499                        **other_node_info500                    )501                )502            else:503                return make_json_response(504                    success=1,505                    info="Nothing to update",506                    data={507                        'id': etid,508                        'sid': sid,509                        'gid': gid,510                        'did': did511                    }512                )513 514        except Exception as e:515            return internal_server_error(errormsg=str(e))516 517    @staticmethod518    def get_delete_data(cmd, etid, request_object):519        """520        This function is used to get the data and cascade information.521        :param cmd: Command522        :param etid: Object ID523        :param request_object: request object524        :return:525        """526        cascade = False527        # Below will decide if it's simple drop or drop with cascade call528        if cmd == 'delete':529            # This is a cascade operation530            cascade = True531 532        if etid is None:533            data = request_object.form if request_object.form else \534                json.loads(request_object.data)535        else:536            data = {'ids': [etid]}537 538        return cascade, data539 540    @check_precondition541    def delete(self, gid, sid, did, etid=None, only_sql=False):542        """543        This function will delete an existing event trigger object.544 545        Args:546          gid: Server Group ID547          sid: Server ID548          did: Database ID549          etid: Event trigger ID550          only_sql:551 552        Returns:553 554        """555        # get the value of cascade and data556        cascade, data = self.get_delete_data(self.cmd, etid, request)557 558        try:559            for etid in data['ids']:560                sql = render_template(561                    "/".join([self.template_path, self._DELETE_SQL]),562                    etid=etid563                )564                status, name = self.conn.execute_scalar(sql)565                if not status:566                    return internal_server_error(errormsg=name)567 568                if name is None:569                    return make_json_response(570                        status=410,571                        success=0,572                        errormsg=gettext(573                            'Error: Object not found.'574                        ),575                        info=gettext(576                            'The specified event trigger could not be found.\n'577                        )578                    )579 580                sql = render_template(581                    "/".join([self.template_path, self._DELETE_SQL]),582                    name=name, cascade=cascade583                )584 585                # Used for schema diff tool586                if only_sql:587                    return sql588 589                status, res = self.conn.execute_scalar(sql)590                if not status:591                    return internal_server_error(errormsg=res)592 593            return make_json_response(594                success=1,595                info=gettext("Event trigger dropped")596            )597 598        except Exception as e:599            return internal_server_error(errormsg=str(e))600 601    @check_precondition602    def msql(self, gid, sid, did, etid=None):603        """604        This function is used to return modified SQL for the selected605        event trigger node.606 607        Args:608          gid: Server Group ID609          sid: Server ID610          did: Database ID611          etid: Event trigger ID612 613        Returns:614 615        """616        data = {}617        for k, v in request.args.items():618            try:619                data[k] = json.loads(v)620            except ValueError:621                data[k] = v622        try:623            sql = self.get_sql(data, etid)624            # Most probably this is due to error625            if not isinstance(sql, str):626                return sql627 628            sql = re.sub('\n{2,}', '\n\n', sql)629            if sql == '':630                sql = "--modified SQL"631            return make_json_response(632                data=sql,633                status=200634            )635 636        except Exception as e:637            return internal_server_error(errormsg=str(e))638 639    def get_sql(self, data, etid=None):640        """641        This function will generate sql from model data.642 643        Args:644          data: Contains the data of the selected event trigger node.645          etid: Event trigger ID646 647        Returns:648 649        """650        required_args = [651            'name'652        ]653 654        if etid is not None:655            sql = render_template(656                "/".join([self.template_path, self._PROPERTIES_SQL]),657                etid=etid658            )659            status, res = self.conn.execute_dict(sql)660            if not status:661                return internal_server_error(errormsg=res)662 663            if len(res['rows']) == 0:664                return gone(665                    gettext("Could not find the event trigger information.")666                )667 668            old_data = res['rows'][0]669            old_data = self._formatter(old_data)670 671            for arg in required_args:672                if arg not in data:673                    data[arg] = old_data[arg]674            sql = render_template(675                "/".join([self.template_path, self._UPDATE_SQL]),676                data=data, o_data=old_data677            )678        else:679            sql = self._get_create_with_grant_sql(data)680        return sql.strip('\n')681 682    def _get_create_with_grant_sql(self, data):683 684        required_args = {685            'name': 'Name',686            'eventowner': 'Owner',687            'eventfunname': 'Trigger function',688            'enabled': 'Enabled status',689            'eventname': 'Events'690        }691        err = []692        for arg in required_args:693            if arg not in data:694                err.append(required_args.get(arg, arg))695        if err:696            return make_json_response(697                status=410,698                success=0,699                errormsg=gettext(700                    "Could not find the required parameter ({})."701                ).format(arg)702            )703        sql = render_template(704            "/".join([self.template_path, self._CREATE_SQL]),705            data=data706        )707        sql += "\n"708        sql += render_template(709            "/".join([self.template_path, self._GRANT_SQL]),710            data=data711        )712        return sql.strip('\n').strip(' ')713 714    @check_precondition715    def sql(self, gid, sid, did, etid, json_resp=True):716        """717        This function will generate sql to show in the sql pane for the718        selected event trigger node.719 720        Args:721          gid: Server Group ID722          sid: Server ID723          did: Database ID724          etid: Event trigger ID725          json_resp:726 727        Returns:728 729        """730        sql = render_template(731            "/".join([self.template_path, self._PROPERTIES_SQL]),732            etid=etid733        )734        status, res = self.conn.execute_dict(sql)735        if not status:736            return internal_server_error(errormsg=res)737 738        if len(res['rows']) == 0:739            return gone(740                gettext(741                    "Could not find the specified event trigger on the "742                    "server.")743            )744 745        result = res['rows'][0]746        result = self._formatter(result)747 748        sql = render_template(749            "/".join([self.template_path, self._CREATE_SQL]),750            data=result, conn=self.conn751        )752        sql += "\n\n"753        sql += render_template(754            "/".join([self.template_path, self._GRANT_SQL]),755            data=result, conn=self.conn756        )757 758        db_sql = render_template(759            "/".join([self.template_path, 'get_db.sql']),760            did=did761        )762        status, db_name = self.conn.execute_scalar(db_sql)763        if not status:764            return internal_server_error(errormsg=db_name)765 766        sql_header = "-- Event Trigger: {0} on database {1}\n\n-- ".format(767            result['name'], db_name768        )769 770        sql_header += render_template(771            "/".join([self.template_path, self._DELETE_SQL]),772            name=result['name'], )773        sql_header += "\n"774 775        sql = sql_header + sql776        sql = re.sub('\n{2,}', '\n\n', sql)777 778        if not json_resp:779            return sql780 781        return ajax_response(response=sql)782 783    @check_precondition784    def get_event_funcs(self, gid, sid, did, etid=None):785        """786        This function gets the event functions and returns an ajax response787        for the event trigger node.788 789        Args:790          gid: Server Group ID791          sid: Server ID792          did: Database ID793          etid: Event trigger ID794 795        Returns:796 797        """798        res = [{'label': '', 'value': ''}]799        sql = render_template(800            "/".join([self.template_path, 'eventfunctions.sql'])801        )802        status, rest = self.conn.execute_2darray(sql)803        if not status:804            return internal_server_error(errormsg=rest)805        for row in rest['rows']:806            res.append(807                {'label': row['tfname'], 'value': row['tfname']}808            )809        return make_json_response(810            data=res,811            status=200812        )813 814    @check_precondition815    def dependents(self, gid, sid, did, etid=None):816        """817        This function gets the dependents and returns an ajax response818        for the event trigger node.819 820        Args:821            gid: Server Group ID822            sid: Server ID823            did: Database ID824            etid: Event trigger ID825        """826        dependents_result = self.get_dependents(self.conn, etid)827        return ajax_response(828            response=dependents_result,829            status=200830        )831 832    @check_precondition833    def dependencies(self, gid, sid, did, etid):834        """835        This function gets the dependencies and returns an ajax response836        for the event trigger node.837 838        Args:839            gid: Server Group ID840            sid: Server ID841            did: Database ID842            etid: Event trigger ID843        """844        dependencies_result = self.get_dependencies(self.conn, etid)845        return ajax_response(846            response=dependencies_result,847            status=200848        )849 850    @check_precondition851    def fetch_objects_to_compare(self, sid, did):852        """853        This function will fetch the list of all the event triggers for854        specified database id.855 856        :param sid: Server Id857        :param did: Database Id858        :return:859        """860        res = dict()861 862        sql = render_template(863            "/".join([self.template_path, self._NODES_SQL]),864            schema_diff=True865        )866        status, rset = self.conn.execute_2darray(sql)867        if not status:868            return internal_server_error(errormsg=rset)869 870        for row in rset['rows']:871            status, data = self._fetch_properties(did, row['oid'])872            if status:873                res[row['name']] = data874 875        return res876 877    def get_sql_from_diff(self, **kwargs):878        """879        This function is used to get the DDL/DML statements.880        :param kwargs:881        :return:882        """883        gid = kwargs.get('gid')884        sid = kwargs.get('sid')885        did = kwargs.get('did')886        oid = kwargs.get('oid')887        data = kwargs.get('data', None)888        drop_sql = kwargs.get('drop_sql', False)889 890        if data:891            sql = self.get_sql(data=data, etid=oid)892        else:893            if drop_sql:894                sql = self.delete(gid=gid, sid=sid, did=did,895                                  etid=oid, only_sql=True)896            else:897                sql = self.sql(gid=gid, sid=sid, did=did, etid=oid,898                               json_resp=False)899        return sql900 901 902SchemaDiffRegistry(blueprint.node_type, EventTriggerView, 'Database')903EventTriggerView.register_node_view(blueprint)904 
codekingpro/portable-devtools · Team Ai