Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py619 linesDownload Raw Back to extensions
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""" Implements Extension Node """11 12import json13from functools import wraps14 15from pgadmin.browser.server_groups.servers import databases16from flask import render_template, request, jsonify17from flask_babel import gettext18from pgadmin.browser.collection import CollectionNodeModule19from pgadmin.browser.utils import PGChildNodeView20from pgadmin.utils.ajax import make_json_response, \21    make_response as ajax_response, internal_server_error, gone22from pgadmin.utils.driver import get_driver23from pgadmin.browser.server_groups.servers.databases.extensions.utils \24    import get_extension_details25from config import PG_DEFAULT_DRIVER26from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry27from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare28 29 30class ExtensionModule(CollectionNodeModule):31    """32    class ExtensionModule():33 34        A collection Node which inherits CollectionNodeModule35        class and define methods to get child nodes, to load its own36        javascript file.37    """38    _NODE_TYPE = "extension"39    _COLLECTION_LABEL = gettext("Extensions")40 41    def __init__(self, *args, **kwargs):42        """43        Initialising the base class44        """45        super().__init__(*args, **kwargs)46 47    def get_nodes(self, gid, sid, did):48        """49        Generate the collection node50        """51        if self.has_nodes(sid, did,52                          base_template_path=ExtensionView.EXT_TEMPLATE_PATH):53            yield self.generate_browser_collection_node(did)54 55    @property56    def node_inode(self):57        """58        If a node have child return True otherwise False59        """60        return False61 62    @property63    def script_load(self):64        """65        Load the module script for extension, when any of the database nodes66        are initialized.67        """68        return databases.DatabaseModule.node_type69 70    @property71    def module_use_template_javascript(self):72        """73        Returns whether Jinja2 template is used for generating the javascript74        module.75        """76        return False77 78 79# Create blueprint of extension module80blueprint = ExtensionModule(__name__)81 82 83class ExtensionView(PGChildNodeView, SchemaDiffObjectCompare):84    """85    This is a class for extension nodes which inherits the86    properties and methods from NodeView class and define87    various methods to list, create, update and delete extension.88 89    Variables:90    ---------91    * node_type - tells which type of node it is92    * parent_ids - id with its type and name of parent nodes93    * ids - id with type and name of extension module being used.94    * operations - function routes mappings defined.95    """96    EXT_TEMPLATE_PATH = 'extensions/sql'97    node_type = blueprint.node_type98 99    parent_ids = [100        {'type': 'int', 'id': 'gid'},101        {'type': 'int', 'id': 'sid'},102        {'type': 'int', 'id': 'did'}103    ]104    ids = [105        {'type': 'int', 'id': 'eid'}106    ]107 108    operations = dict({109        'obj': [110            {'get': 'properties', 'delete': 'delete', 'put': 'update'},111            {'get': 'list', 'post': 'create', 'delete': 'delete'}112        ],113        'delete': [{'delete': 'delete'}, {'delete': 'delete'}],114        'nodes': [{'get': 'node'}, {'get': 'nodes'}],115        'sql': [{'get': 'sql'}],116        'msql': [{'get': 'msql'}, {'get': 'msql'}],117        'stats': [{'get': 'statistics'}],118        'dependency': [{'get': 'dependencies'}],119        'dependent': [{'get': 'dependents'}],120        'avails': [{}, {'get': 'avails'}],121        'schemas': [{}, {'get': 'schemas'}],122        'children': [{'get': 'children'}]123    })124 125    keys_to_ignore = ['oid', 'oid-2', 'owner']126 127    def check_precondition(f):128        """129        This function will behave as a decorator which will checks130        database connection before running view, it will also attaches131        manager,conn & template_path properties to self132        """133 134        @wraps(f)135        def wrap(*args, **kwargs):136            # Here args[0] will hold self & kwargs will hold gid,sid,did137            self = args[0]138            self.manager = get_driver(139                PG_DEFAULT_DRIVER140            ).connection_manager(kwargs['sid'])141            self.conn = self.manager.connection(did=kwargs['did'])142            self.template_path = self.EXT_TEMPLATE_PATH143 144            self.datistemplate = False145            if (146                self.manager.db_info is not None and147                kwargs['did'] in self.manager.db_info and148                'datistemplate' in self.manager.db_info[kwargs['did']]149            ):150                self.datistemplate = self.manager.db_info[151                    kwargs['did']]['datistemplate']152 153            return f(*args, **kwargs)154 155        return wrap156 157    @check_precondition158    def list(self, gid, sid, did):159        """160        Fetches all extensions properties and render into properties tab161        """162        SQL = render_template("/".join([self.template_path,163                                        self._PROPERTIES_SQL]),164                              conn=self.conn)165        status, res = self.conn.execute_dict(SQL)166 167        if not status:168            return internal_server_error(errormsg=res)169        return ajax_response(170            response=res['rows'],171            status=200172        )173 174    @check_precondition175    def nodes(self, gid, sid, did):176        """177        Lists all extensions under the Extensions Collection node178        """179        res = []180        SQL = render_template("/".join([self.template_path,181                                        self._PROPERTIES_SQL]),182                              conn=self.conn)183        status, rset = self.conn.execute_2darray(SQL)184        if not status:185            return internal_server_error(errormsg=rset)186 187        for row in rset['rows']:188            res.append(189                self.blueprint.generate_browser_node(190                    row['oid'],191                    did,192                    row['name'],193                    'icon-extension',194                    description=row['comment']195                ))196 197        return make_json_response(198            data=res,199            status=200200        )201 202    @check_precondition203    def node(self, gid, sid, did, eid):204        """205        This function will fetch the properties of extension206        """207        SQL = render_template("/".join([self.template_path,208                                        self._PROPERTIES_SQL]),209                              eid=eid, conn=self.conn)210        status, rset = self.conn.execute_2darray(SQL)211        if not status:212            return internal_server_error(errormsg=rset)213 214        for row in rset['rows']:215            return make_json_response(216                data=self.blueprint.generate_browser_node(217                    row['oid'],218                    did,219                    row['name'],220                    'icon-extension'221                ),222                status=200223            )224 225        return gone(gettext("Could not find the specified extension."))226 227    @check_precondition228    def properties(self, gid, sid, did, eid):229        """230        Fetch the properties of a single extension and render in properties tab231        """232        status, res = self._fetch_properties(did, eid)233        if not status:234            return res235 236        return ajax_response(237            response=res,238            status=200239        )240 241    def _fetch_properties(self, did, eid):242        """243        This function fetch the properties of the extension.244        :param did:245        :param eid:246        :return:247        """248        SQL = render_template("/".join(249            [self.template_path, self._PROPERTIES_SQL]), eid=eid,250            conn=self.conn)251        status, res = self.conn.execute_dict(SQL)252        if not status:253            return False, internal_server_error(errormsg=res)254 255        if len(res['rows']) == 0:256            return False, gone(257                gettext("Could not find the extension information.")258            )259 260        res['rows'][0]['is_sys_obj'] = (261            res['rows'][0]['oid'] <= self._DATABASE_LAST_SYSTEM_OID or262            self.datistemplate)263 264        return True, res['rows'][0]265 266    @check_precondition267    def create(self, gid, sid, did):268        """269        Create a new extension object270        """271        required_args = [272            'name'273        ]274 275        data = request.form if request.form else json.loads(276            request.data277        )278 279        for arg in required_args:280            if arg not in data:281                return make_json_response(282                    status=410,283                    success=0,284                    errormsg=gettext(285                        "Could not find the required parameter ({})."286                    ).format(arg)287                )288 289        status, res = self.conn.execute_dict(290            render_template(291                "/".join([self.template_path, self._CREATE_SQL]),292                data=data293            )294        )295 296        if not status:297            return internal_server_error(errormsg=res)298 299        status, res = get_extension_details(300            self.conn, data['name'],301            "/".join([self.template_path, self._PROPERTIES_SQL]))302        if not status:303            return internal_server_error(errormsg=res)304 305        return jsonify(306            node=self.blueprint.generate_browser_node(307                res['oid'],308                did,309                res['name'],310                'icon-extension'311            )312        )313 314    @check_precondition315    def update(self, gid, sid, did, eid):316        """317        This function will update an extension object318        """319        data = request.form if request.form else json.loads(320            request.data321        )322 323        try:324            SQL, name = self.getSQL(gid, sid, data, did, eid)325            # Most probably this is due to error326            if not isinstance(SQL, str):327                return SQL328            SQL = SQL.strip('\n').strip(' ')329            status, res = self.conn.execute_dict(SQL)330            if not status:331                return internal_server_error(errormsg=res)332 333            other_node_info = {}334            if 'comment' in data:335                other_node_info['description'] = data['comment']336 337            return jsonify(338                node=self.blueprint.generate_browser_node(339                    eid,340                    did,341                    name,342                    icon="icon-%s" % self.node_type,343                    **other_node_info344                )345            )346        except Exception as e:347            return internal_server_error(errormsg=str(e))348 349    @check_precondition350    def delete(self, gid, sid, did, eid=None, only_sql=False):351        """352        This function will drop/drop cascade a extension object353        """354 355        if eid is None:356            data = request.form if request.form else json.loads(357                request.data358            )359        else:360            data = {'ids': [eid]}361 362        cascade = self._check_cascade_operation()363 364        try:365            for eid in data['ids']:366                # check if extension with eid exists367                SQL = render_template("/".join(368                    [self.template_path, self._DELETE_SQL]), eid=eid)369                status, name = self.conn.execute_scalar(SQL)370                if not status:371                    return internal_server_error(errormsg=name)372 373                if name is None:374                    return make_json_response(375                        status=410,376                        success=0,377                        errormsg=gettext(378                            'Error: Object not found.'379                        ),380                        info=gettext(381                            'The specified extension could not be found.\n'382                        )383                    )384 385                # drop extension386                SQL = render_template("/".join(387                    [self.template_path, self._DELETE_SQL]388                ), name=name, cascade=cascade)389 390                # Used for schema diff tool391                if only_sql:392                    return SQL393 394                status, res = self.conn.execute_scalar(SQL)395                if not status:396                    return internal_server_error(errormsg=res)397 398            return make_json_response(399                success=1,400                info=gettext("Extension dropped")401            )402        except Exception as e:403            return internal_server_error(errormsg=str(e))404 405    @check_precondition406    def msql(self, gid, sid, did, eid=None):407        """408        This function returns modified SQL409        """410        data = {}411        for k, v in request.args.items():412            try:413                data[k] = json.loads(v)414            except ValueError:415                data[k] = v416 417        try:418            SQL, _ = self.getSQL(gid, sid, data, did, eid)419            # Most probably this is due to error420            if not isinstance(SQL, str):421                return SQL422            SQL = SQL.strip('\n').strip(' ')423            if SQL == '':424                SQL = "--modified SQL"425 426            return make_json_response(427                data=SQL,428                status=200429            )430        except Exception as e:431            return internal_server_error(errormsg=str(e))432 433    def getSQL(self, gid, sid, data, did, eid=None):434        """435        This function will generate sql from model data436        """437        required_args = [438            'name'439        ]440 441        if eid is not None:442            SQL = render_template("/".join(443                [self.template_path, self._PROPERTIES_SQL]444            ), eid=eid, conn=self.conn)445            status, res = self.conn.execute_dict(SQL)446            if not status:447                return internal_server_error(errormsg=res)448 449            if len(res['rows']) == 0:450                return gone(451                    gettext("Could not find the extension information.")452                )453 454            old_data = res['rows'][0]455            for arg in required_args:456                if arg not in data:457                    data[arg] = old_data[arg]458            SQL = render_template("/".join(459                [self.template_path, self._UPDATE_SQL]460            ), data=data, o_data=old_data)461            return SQL, data['name'] if 'name' in data else old_data['name']462        else:463            SQL = render_template("/".join(464                [self.template_path, self._CREATE_SQL]465            ), data=data)466            return SQL, data['name']467 468    @check_precondition469    def avails(self, gid, sid, did):470        """471        This function with fetch all the available extensions472        """473        SQL = render_template("/".join([self.template_path, 'extensions.sql']))474        status, rset = self.conn.execute_dict(SQL)475        if not status:476            return internal_server_error(errormsg=rset)477        return make_json_response(478            data=rset['rows'],479            status=200480        )481 482    @check_precondition483    def schemas(self, gid, sid, did):484        """485        This function with fetch all the schemas486        """487        SQL = render_template("/".join([self.template_path, 'schemas.sql']))488        status, rset = self.conn.execute_dict(SQL)489        if not status:490            return internal_server_error(errormsg=rset)491        return make_json_response(492            data=rset['rows'],493            status=200494        )495 496    @check_precondition497    def sql(self, gid, sid, did, eid, json_resp=True):498        """499        This function will generate sql for the sql panel500        """501        SQL = render_template("/".join(502            [self.template_path, self._PROPERTIES_SQL]503        ), eid=eid, conn=self.conn)504        status, res = self.conn.execute_dict(SQL)505        if not status:506            return internal_server_error(errormsg=res)507        if len(res['rows']) == 0:508            return gone(509                gettext("Could not find the extension on the server.")510            )511 512        result = res['rows'][0]513 514        SQL = render_template("/".join(515            [self.template_path, self._CREATE_SQL]516        ),517            data=result,518            conn=self.conn,519            display_comments=True,520            add_not_exists_clause=True521        )522 523        if not json_resp:524            return SQL525 526        return ajax_response(response=SQL)527 528    @check_precondition529    def dependents(self, gid, sid, did, eid):530        """531        This function gets the dependents and returns an ajax response532        for the extension node.533 534        Args:535            gid: Server Group ID536            sid: Server ID537            did: Database ID538            eid: Extension ID539        """540        dependents_result = self.get_dependents(self.conn, eid)541        return ajax_response(542            response=dependents_result,543            status=200544        )545 546    @check_precondition547    def dependencies(self, gid, sid, did, eid):548        """549        This function gets the dependencies and returns an ajax response550        for the extension node.551 552        Args:553            gid: Server Group ID554            sid: Server ID555            did: Database ID556            lid: Extension ID557        """558        dependencies_result = self.get_dependencies(self.conn, eid)559        return ajax_response(560            response=dependencies_result,561            status=200562        )563 564    @check_precondition565    def fetch_objects_to_compare(self, sid, did):566        """567        This function will fetch the list of all the extensions for568        specified database id.569 570        :param sid: Server Id571        :param did: Database Id572        :return:573        """574        res = dict()575 576        sql = render_template("/".join([self.template_path,577                                        self._PROPERTIES_SQL]),578                              conn=self.conn)579        status, rset = self.conn.execute_2darray(sql)580        if not status:581            return internal_server_error(errormsg=rset)582 583        for row in rset['rows']:584            status, data = self._fetch_properties(did, row['oid'])585            if status:586                res[row['name']] = data587 588        return res589 590    def get_sql_from_diff(self, **kwargs):591        """592        This function is used to get the DDL/DML statements.593        :param kwargs594        :return:595        """596        gid = kwargs.get('gid')597        sid = kwargs.get('sid')598        did = kwargs.get('did')599        oid = kwargs.get('oid')600        data = kwargs.get('data', None)601        drop_sql = kwargs.get('drop_sql', False)602 603        if data:604            sql, _ = self.getSQL(gid=gid, sid=sid, did=did, data=data,605                                 eid=oid)606        else:607            if drop_sql:608                sql = self.delete(gid=gid, sid=sid, did=did,609                                  eid=oid, only_sql=True)610            else:611                sql = self.sql(gid=gid, sid=sid, did=did, eid=oid,612                               json_resp=False)613        return sql614 615 616SchemaDiffRegistry(blueprint.node_type, ExtensionView, 'Database')617# Register and add ExtensionView as blueprint618ExtensionView.register_node_view(blueprint)619 
codekingpro/portable-devtools · Team Ai