codekingpro/portable-devtools
114k
1##########################################################################2#3# pgAdmin 4 - PostgreSQL Tools4#5# Copyright (C) 2013 - 2024, The pgAdmin Development Team6# This software is released under the PostgreSQL Licence7#8##########################################################################9 10import re11from functools import wraps12 13import json14from flask import render_template, request, jsonify, current_app15from flask_babel import gettext16 17from pgadmin.browser.server_groups import servers18from config import PG_DEFAULT_DRIVER19from pgadmin.browser.collection import CollectionNodeModule, PGChildModule20from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \21 parse_priv_to_db22from pgadmin.browser.utils import PGChildNodeView23from pgadmin.utils.ajax import make_json_response, internal_server_error, \24 make_response as ajax_response, gone, bad_request25from pgadmin.utils.driver import get_driver26from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry27from pgadmin.model import Database28 29"""30 This module is responsible for generating two nodes31 1) Schema32 2) Catalog33 34 We have created single file because catalog & schema has same35 functionality, the only difference is we cannot perform DDL/DML operations36 on catalog, also - it allows us to share the same submodules for both37 catalog, and schema modules.38 39 This modules uses separate template paths for each respective node40 - templates/catalog for Catalog node41 - templates/schema for Schema node42 43 [Each path contains node specific js files as well as sql template files.]44"""45 46 47class SchemaModule(CollectionNodeModule):48 """49 class SchemaModule(CollectionNodeModule)50 51 A module class for Schema node derived from CollectionNodeModule.52 53 Methods:54 -------55 * __init__(*args, **kwargs)56 - Method is used to initialize the Schema and it's base module.57 58 * get_nodes(gid, sid, did)59 - Method is used to generate the browser collection node.60 61 * node_inode()62 - Method is overridden from its base class to make the node as leaf node.63 64 * script_load()65 - Load the module script for schema, when any of the server node is66 initialized.67 """68 _NODE_TYPE = 'schema'69 _COLLECTION_LABEL = gettext("Schemas")70 71 def __init__(self, *args, **kwargs):72 """73 Method is used to initialize the SchemaModule and it's base module.74 75 Args:76 *args:77 **kwargs:78 """79 self.min_ver = None80 self.max_ver = None81 82 super().__init__(*args, **kwargs)83 84 def get_nodes(self, gid, sid, did):85 """86 Generate the collection node87 """88 if self.has_nodes(89 sid, did,90 base_template_path=SchemaView.BASE_TEMPLATE_PATH +91 '/' + SchemaView._SQL_PREFIX):92 yield self.generate_browser_collection_node(did)93 94 @property95 def script_load(self):96 """97 Load the module script for schema, when any of the server node is98 initialized.99 """100 return servers.ServerModule.node_type101 102 @property103 def module_use_template_javascript(self):104 """105 Returns whether Jinja2 template is used for generating the javascript106 module.107 """108 return False109 110 def register(self, app, options):111 """112 Override the default register function to automagically register113 sub-modules at once.114 """115 from .aggregates import blueprint as module116 self.submodules.append(module)117 118 from .catalog_objects import blueprint as module119 self.submodules.append(module)120 121 from .collations import blueprint as module122 self.submodules.append(module)123 124 from .domains import blueprint as module125 self.submodules.append(module)126 127 from .foreign_tables import blueprint as module128 self.submodules.append(module)129 130 from .fts_configurations import blueprint as module131 self.submodules.append(module)132 133 from .fts_dictionaries import blueprint as module134 self.submodules.append(module)135 136 from .fts_parsers import blueprint as module137 self.submodules.append(module)138 139 from .fts_templates import blueprint as module140 self.submodules.append(module)141 142 from .functions import blueprint as module143 self.submodules.append(module)144 145 from .functions import trigger_function_blueprint as module146 self.submodules.append(module)147 148 from .functions import procedure_blueprint as module149 self.submodules.append(module)150 151 from .operators import blueprint as module152 self.submodules.append(module)153 154 from .packages import blueprint as module155 self.submodules.append(module)156 157 from .sequences import blueprint as module158 self.submodules.append(module)159 160 from .synonyms import blueprint as module161 self.submodules.append(module)162 163 from .tables import blueprint as module164 self.submodules.append(module)165 166 from .types import blueprint as module167 self.submodules.append(module)168 169 from .views import view_blueprint as module170 self.submodules.append(module)171 172 from .views import mview_blueprint as module173 self.submodules.append(module)174 175 super().register(app, options)176 177 178class CatalogModule(SchemaModule):179 """180 class CatalogModule(SchemaModule)181 182 A module class for the catalog schema node derived from SchemaModule.183 """184 185 _NODE_TYPE = 'catalog'186 _COLLECTION_LABEL = gettext("Catalogs")187 188 def register(self, app, options):189 """190 Override the default register function to automagically register191 sub-modules at once.192 """193 super().register(app, options)194 195 def get_nodes(self, gid, sid, did):196 """197 Generate the collection node198 """199 yield self.generate_browser_collection_node(did)200 201 202schema_blueprint = SchemaModule(__name__)203catalog_blueprint = CatalogModule(__name__)204 205 206def check_precondition(f):207 """208 This function will behave as a decorator which will checks209 database connection before running view, it will also attaches210 manager,conn & template_path properties to instance of the method.211 212 Assumptions:213 This function will always be used as decorator of a class method.214 """215 216 @wraps(f)217 def wrap(*args, **kwargs):218 # Here args[0] will hold self & kwargs will hold gid,sid,did219 self = args[0]220 self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(221 kwargs['sid']222 )223 if not self.manager:224 return gone(errormsg=gettext("Could not find the server."))225 226 self.conn = self.manager.connection(did=kwargs['did'])227 self.datistemplate = False228 if (229 self.manager.db_info is not None and230 kwargs['did'] in self.manager.db_info and231 'datistemplate' in self.manager.db_info[kwargs['did']]232 ):233 self.datistemplate = self.manager.db_info[234 kwargs['did']]['datistemplate']235 236 # Set the template path for the SQL scripts237 self.template_path = self.BASE_TEMPLATE_PATH.format(238 self.manager.server_type, self.manager.version)239 240 return f(*args, **kwargs)241 242 return wrap243 244 245class SchemaView(PGChildNodeView):246 """247 This class is responsible for generating routes for schema node.248 249 Methods:250 -------251 * __init__(**kwargs)252 - Method is used to initialize the SchemaView and it's base view.253 254 * list()255 - This function is used to list all the schema nodes within the256 collection.257 258 * nodes()259 - This function will used to create all the child node within the260 collection, Here it will create all the schema node.261 262 * properties(gid, sid, did, scid)263 - This function will show the properties of the selected schema node.264 265 * create(gid, sid, did, scid)266 - This function will create the new schema object.267 268 * update(gid, sid, did, scid)269 - This function will update the data for the selected schema node.270 271 * delete(self, gid, sid, scid):272 - This function will drop the schema object273 274 * msql(gid, sid, did, scid)275 - This function is used to return modified SQL for the selected schema276 node.277 278 * get_sql(data, scid)279 - This function will generate sql from model data280 281 * sql(gid, sid, did, scid):282 - This function will generate sql to show it in sql pane for the schema283 node.284 285 * dependency(gid, sid, did, scid):286 - This function will generate dependency list show it in dependency287 pane for the selected schema node.288 289 * dependent(gid, sid, did, scid):290 - This function will generate dependent list to show it in dependent291 pane for the selected schema node.292 """293 node_type = schema_blueprint.node_type294 _SQL_PREFIX = 'sql/'295 node_icon = 'icon-%s' % node_type296 BASE_TEMPLATE_PATH = 'schemas/{0}/#{1}#'297 298 parent_ids = [299 {'type': 'int', 'id': 'gid'},300 {'type': 'int', 'id': 'sid'},301 {'type': 'int', 'id': 'did'}302 ]303 ids = [304 {'type': 'int', 'id': 'scid'}305 ]306 307 operations = dict({308 'obj': [309 {'get': 'properties', 'delete': 'delete', 'put': 'update'},310 {'get': 'list', 'post': 'create', 'delete': 'delete'}311 ],312 'children': [{'get': 'children'}],313 'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],314 'sql': [{'get': 'sql'}],315 'msql': [{'get': 'msql'}, {'get': 'msql'}],316 'stats': [{'get': 'statistics'}],317 'dependency': [{'get': 'dependencies'}],318 'dependent': [{'get': 'dependents'}],319 'delete': [{'delete': 'delete'},320 {'delete': 'delete'}]321 })322 323 def __init__(self, *args, **kwargs):324 """325 Initialize the variables used by methods of SchemaView.326 """327 328 super().__init__(*args, **kwargs)329 330 self.manager = None331 self.conn = None332 self.template_path = None333 334 @staticmethod335 def ppas_template_path(ver):336 """337 Returns the template path for PPAS servers.338 """339 return 'ppas/#{0}#'.format(ver)340 341 @staticmethod342 def pg_template_path(ver):343 """344 Returns the template path for PostgreSQL servers.345 """346 return 'pg/#{0}#'.format(ver)347 348 def format_request_acls(self, data, modified=False, specific=None):349 acls = {}350 try:351 acls = render_template(352 "/".join([self.template_path, 'allowed_privs.json'])353 )354 acls = json.loads(acls)355 except Exception as e:356 current_app.logger.exception(e)357 358 # Privileges359 for aclcol in acls:360 if specific is not None and aclcol not in specific:361 continue362 if aclcol in data:363 allowedacl = acls[aclcol]364 if modified:365 for modifier in ['added', 'changed', 'deleted']:366 if modifier in data[aclcol]:367 data[aclcol][modifier] = parse_priv_to_db(368 data[aclcol][modifier], allowedacl['acl']369 )370 else:371 data[aclcol] = parse_priv_to_db(data[aclcol],372 allowedacl['acl'])373 374 return acls375 376 @staticmethod377 def formatdbacl(acl):378 """379 Args:380 acl: Privileges from ACL query381 382 Returns:383 Formatted output required for client side parsing384 """385 # Reset any data for that acl if its already present in result set386 data = dict()387 for row in acl['rows']:388 priv = parse_priv_from_db(row)389 390 if row['deftype'] in data:391 data[row['deftype']].append(priv)392 else:393 data[row['deftype']] = [priv]394 return data395 396 def _formatter_no_defacl(self, data, scid=None):397 """398 Args:399 data: Result of properties query400 scid: Schema OID401 402 Returns:403 It will return formatted output of collections like404 security lables, privileges405 """406 # Need to format security labels according to client js collection407 seclabels = []408 if 'seclabels' in data and data['seclabels'] is not None:409 for sec in data['seclabels']:410 sec = re.search(r'([^=]+)=(.*$)', sec)411 seclabels.append({412 'provider': sec.group(1),413 'label': sec.group(2)414 })415 416 data['seclabels'] = seclabels417 418 # We need to parse & convert ACL coming from database to json format419 SQL = render_template(420 "/".join([self.template_path, 'sql/acl.sql']),421 _=gettext,422 scid=scid,423 conn=self.conn424 )425 status, acl = self.conn.execute_dict(SQL)426 if not status:427 return internal_server_error(errormsg=acl)428 429 data.update(self.formatdbacl(acl))430 431 return data432 433 def _formatter(self, data, scid=None):434 435 self._formatter_no_defacl(data, scid)436 437 # We need to parse & convert DEFAULT ACL coming from438 # database to json format439 SQL = render_template(440 "/".join([self.template_path, 'sql/defacl.sql']),441 _=gettext,442 scid=scid443 )444 445 status, defacl = self.conn.execute_dict(SQL)446 if not status:447 return internal_server_error(errormsg=defacl)448 449 data.update(self.formatdbacl(defacl))450 451 return data452 453 @check_precondition454 def list(self, gid, sid, did):455 """456 This function is used to list all the schema nodes within the457 collection.458 459 Args:460 gid: Server group ID461 sid: Server ID462 did: Database ID463 464 Returns:465 JSON of available schema nodes466 """467 database = Database.query.filter_by(id=did, server=sid).first()468 param = None469 if database:470 schema_restrictions = database.schema_res471 472 if schema_restrictions:473 schema_res = ",".join(474 ["'%s'"] * len(schema_restrictions.split(',')))475 param = schema_res % (tuple(schema_restrictions.split(',')))476 477 SQL = render_template(478 "/".join([self.template_path,479 self._SQL_PREFIX + self._PROPERTIES_SQL]),480 _=gettext,481 show_sysobj=self.blueprint.show_system_objects,482 schema_restrictions=param483 )484 status, res = self.conn.execute_dict(SQL)485 486 if not status:487 return internal_server_error(errormsg=res)488 return ajax_response(489 response=res['rows'],490 status=200491 )492 493 @check_precondition494 def nodes(self, gid, sid, did, scid=None, is_schema_diff=False):495 """496 This function will create all the child nodes within the collection497 Here it will create all the schema node.498 499 Args:500 gid: Server Group ID501 sid: Server ID502 did: Database ID503 scid: Schema ID504 is_schema_diff: True if called by schema diff tool505 506 Returns:507 JSON of available schema child nodes508 """509 res = []510 database = Database.query.filter_by(id=did, server=sid).first()511 param = None512 if database:513 schema_restrictions = database.schema_res514 515 if schema_restrictions:516 schema_res = ",".join(517 ["'%s'"] * len(schema_restrictions.split(',')))518 param = schema_res % (tuple(schema_restrictions.split(',')))519 520 show_system_objects = self.blueprint.show_system_objects521 if is_schema_diff:522 show_system_objects = False523 524 SQL = render_template(525 "/".join([self.template_path, self._SQL_PREFIX + self._NODES_SQL]),526 show_sysobj=show_system_objects,527 _=gettext,528 scid=scid,529 schema_restrictions=param,530 conn=self.conn531 )532 533 status, rset = self.conn.execute_2darray(SQL)534 if not status:535 return internal_server_error(errormsg=rset)536 537 if scid is not None:538 if len(rset['rows']) == 0:539 return gone(gettext(540 """Could not find the schema in the database.541 It may have been removed by another user."""))542 row = rset['rows'][0]543 return make_json_response(544 data=self.blueprint.generate_browser_node(545 row['oid'],546 did,547 row['name'],548 icon=self.node_icon,549 can_create=row['can_create'],550 has_usage=row['has_usage'],551 description=row['description']552 ),553 status=200554 )555 556 for row in rset['rows']:557 res.append(558 self.blueprint.generate_browser_node(559 row['oid'],560 did,561 row['name'],562 icon=self.node_icon,563 can_create=row['can_create'],564 has_usage=row['has_usage'],565 description=row['description']566 )567 )568 569 return make_json_response(570 data=res,571 status=200572 )573 574 @check_precondition575 def node(self, gid, sid, did, scid):576 """577 This function will fetch the properties of the schema node.578 579 Args:580 gid: Server Group ID581 sid: Server ID582 did: Database ID583 scid: Schema ID584 585 Returns:586 JSON of given schema child node587 """588 SQL = render_template(589 "/".join([self.template_path, self._SQL_PREFIX + self._NODES_SQL]),590 show_sysobj=self.blueprint.show_system_objects,591 _=gettext,592 scid=scid,593 conn=self.conn594 )595 596 status, rset = self.conn.execute_2darray(SQL)597 if not status:598 return internal_server_error(errormsg=rset)599 600 if scid is not None and len(rset['rows']) == 0:601 return gone(gettext("""602Could not find the schema in the database.603It may have been removed by another user.604"""))605 606 for row in rset['rows']:607 return make_json_response(608 data=self.blueprint.generate_browser_node(609 row['oid'],610 did,611 row['name'],612 icon=self.node_icon,613 can_create=row['can_create'],614 has_usage=row['has_usage']615 ),616 status=200617 )618 619 @check_precondition620 def properties(self, gid, sid, did, scid):621 """622 This function will show the properties of the selected schema node.623 624 Args:625 gid: Server Group ID626 sid: Server ID627 did: Database ID628 scid: Schema ID629 630 Returns:631 JSON of selected schema node632 """633 SQL = render_template(634 "/".join([self.template_path,635 self._SQL_PREFIX + self._PROPERTIES_SQL]),636 scid=scid,637 _=gettext,638 show_sysobj=self.blueprint.show_system_objects639 )640 641 status, res = self.conn.execute_dict(SQL)642 643 if not status:644 return internal_server_error(errormsg=res)645 646 if len(res['rows']) == 0:647 return gone(gettext(648 "Could not find the schema in the database. "649 "It may have been removed by another user."650 ))651 652 # Making copy of output for future use653 copy_data = dict(res['rows'][0])654 copy_data['is_sys_obj'] = (655 copy_data['oid'] <= self._DATABASE_LAST_SYSTEM_OID or656 self.datistemplate)657 copy_data = self._formatter(copy_data, scid)658 659 return ajax_response(660 response=copy_data,661 status=200662 )663 664 @check_precondition665 def create(self, gid, sid, did):666 """667 This function will create a schema object668 669 Args:670 gid: Server Group ID671 sid: Server ID672 did: Database ID673 """674 data = request.form if request.form else json.loads(675 request.data676 )677 678 for k, v in data.items():679 try:680 # comments should be taken as is because if user enters a681 # json comment it is parsed by loads which should not happen682 if k in ('comment',):683 data[k] = v684 else:685 data[k] = json.loads(v)686 except (ValueError, TypeError, KeyError):687 data[k] = v688 689 required_args = {690 'name': 'Name'691 }692 693 for arg in required_args:694 if arg not in data:695 return make_json_response(696 status=410,697 success=0,698 errormsg=gettext(699 "Could not find the required parameter ({})."700 ).format(arg)701 )702 try:703 self.format_request_acls(data)704 SQL = render_template(705 "/".join([self.template_path,706 self._SQL_PREFIX + self._CREATE_SQL]),707 data=data, conn=self.conn, _=gettext708 )709 status, res = self.conn.execute_scalar(SQL)710 if not status:711 return make_json_response(712 status=410,713 success=0,714 errormsg=res715 )716 717 # we need oid to add object in tree at browser,718 # below sql will gives the same719 SQL = render_template(720 "/".join([self.template_path, 'sql/oid.sql']),721 schema=data['name'], _=gettext, conn=self.conn722 )723 724 status, scid = self.conn.execute_scalar(SQL)725 if not status:726 return internal_server_error(errormsg=scid)727 728 return jsonify(729 node=self.blueprint.generate_browser_node(730 scid,731 did,732 data['name'],733 icon=self.node_icon734 )735 )736 except Exception as e:737 current_app.logger.exception(e)738 return internal_server_error(errormsg=str(e))739 740 @check_precondition741 def update(self, gid, sid, did, scid):742 """743 This function will update an existing schema object744 745 Args:746 gid: Server Group ID747 sid: Server ID748 did: Database ID749 scid: Schema ID750 """751 data = request.form if request.form else json.loads(752 request.data753 )754 try:755 SQL, name = self.get_sql(gid, sid, data, scid)756 757 SQL = SQL.strip('\n').strip(' ')758 status, res = self.conn.execute_scalar(SQL)759 if not status:760 return internal_server_error(errormsg=res)761 762 other_node_info = {}763 if 'description' in data:764 other_node_info['description'] = data['description']765 766 return jsonify(767 node=self.blueprint.generate_browser_node(768 scid,769 did,770 name,771 icon=self.node_icon,772 **other_node_info773 )774 )775 except Exception as e:776 return internal_server_error(errormsg=str(e))777 778 @check_precondition779 def delete(self, gid, sid, did, scid=None):780 """781 This function will delete an existing schema object782 783 Args:784 gid: Server Group ID785 sid: Server ID786 did: Database ID787 scid: Schema ID788 """789 790 if scid is None:791 data = request.form if request.form else json.loads(792 request.data793 )794 else:795 data = {'ids': [scid]}796 797 for scid in data['ids']:798 try:799 # Get name for schema from did800 SQL = render_template(801 "/".join([self.template_path, 'sql/get_name.sql']),802 _=gettext,803 scid=scid804 )805 806 status, name = self.conn.execute_scalar(SQL)807 if not status:808 return internal_server_error(errormsg=name)809 810 if name is None:811 return make_json_response(812 status=410,813 success=0,814 errormsg=gettext(815 'Error: Object not found.'816 ),817 info=gettext(818 'The specified schema could not be found.\n'819 )820 )821 822 # drop schema823 SQL = render_template(824 "/".join([self.template_path,825 self._SQL_PREFIX + self._DELETE_SQL]),826 _=gettext, name=name, conn=self.conn,827 cascade=self._check_cascade_operation()828 )829 status, res = self.conn.execute_scalar(SQL)830 if not status:831 return internal_server_error(errormsg=res)832 833 except Exception as e:834 current_app.logger.exception(e)835 return internal_server_error(errormsg=str(e))836 837 return make_json_response(838 success=1,839 info=gettext("Schema dropped")840 )841 842 @check_precondition843 def msql(self, gid, sid, did, scid=None):844 """845 This function will generate modified sql for schema object based on846 the input from the user. This route is used by the SQL tab in the847 edit/create dialog.848 849 Args:850 gid: Server Group ID851 sid: Server ID852 did: Database ID853 scid: Schema ID (When working with existing schema node)854 """855 data = dict()856 for k, v in request.args.items():857 try:858 # comments should be taken as is because if user enters a859 # json comment it is parsed by loads which should not happen860 if k in ('description',):861 data[k] = v862 else:863 data[k] = json.loads(v)864 except ValueError:865 data[k] = v866 867 try:868 SQL, _ = self.get_sql(gid, sid, data, scid)869 if SQL and SQL.strip('\n') and SQL.strip(' '):870 return make_json_response(871 data=SQL.strip('\n'),872 status=200873 )874 except Exception as e:875 return internal_server_error(errormsg=str(e))876 877 def get_sql(self, gid, sid, data, scid=None):878 """879 This function will generate sql from model data received from client880 """881 if scid is not None:882 SQL = render_template(883 "/".join([self.template_path,884 self._SQL_PREFIX + self._PROPERTIES_SQL]),885 _=gettext, scid=scid,886 show_sysobj=self.blueprint.show_system_objects887 )888 889 status, res = self.conn.execute_dict(SQL)890 if not status:891 return internal_server_error(errormsg=res)892 old_data = res['rows'][0]893 # old_data contains all the existing data for requested schema894 old_data = self._formatter(old_data, scid)895 896 # if name is not present in request data then use old name897 if 'name' not in data:898 data['name'] = old_data['name']899 900 # Privileges and Default privileges901 self.format_request_acls(data, True)902 903 SQL = render_template(904 "/".join([self.template_path,905 self._SQL_PREFIX + self._UPDATE_SQL]),906 _=gettext, data=data, o_data=old_data, conn=self.conn907 )908 return SQL, data['name'] if 'name' in data else old_data['nam']909 else:910 required_args = ['name']911 912 for arg in required_args:913 if arg not in data:914 return " -- " + gettext("Definition incomplete.")915 916 # Privileges917 self.format_request_acls(data)918 919 SQL = render_template(920 "/".join([self.template_path,921 self._SQL_PREFIX + self._CREATE_SQL]),922 data=data, conn=self.conn, _=gettext923 )924 925 return SQL, data['name']926 927 @check_precondition928 def sql(self, gid, sid, did, scid):929 """930 This function will generate reverse engineered sql for the schema931 object.932 933 Args:934 gid: Server Group ID935 sid: Server ID936 did: Database ID937 scid: Schema ID938 """939 SQL = render_template(940 "/".join([self.template_path,941 self._SQL_PREFIX + self._PROPERTIES_SQL]),942 scid=scid, _=gettext943 )944 945 status, res = self.conn.execute_dict(SQL)946 if not status:947 return internal_server_error(errormsg=res)948 949 if len(res['rows']) == 0:950 return gone(gettext(951 'Could not find the schema in the database.'952 ' It may have been removed by another user.'))953 954 data = res['rows'][0]955 data = self._formatter(data, scid)956 957 # Privileges and Default privileges958 self.format_request_acls(data)959 960 # Render sql from create & alter sql using properties & acl data961 SQL = ''962 SQL = render_template(963 "/".join([self.template_path,964 self._SQL_PREFIX + self._CREATE_SQL]),965 _=gettext, data=data, conn=self.conn, add_not_exists_clause=True966 )967 968 sql_header = "-- SCHEMA: {0}\n\n-- ".format(data['name'])969 970 # drop schema971 sql_header += render_template(972 "/".join([self.template_path,973 self._SQL_PREFIX + self._DELETE_SQL]),974 _=gettext, name=data['name'], conn=self.conn, cascade=False)975 976 SQL = sql_header + '\n\n' + SQL977 978 return ajax_response(response=SQL.strip("\n"))979 980 @check_precondition981 def dependents(self, gid, sid, did, scid):982 """983 This function gets the dependencies and returns an ajax response.984 for the schema node.985 986 Args:987 gid: Server Group ID988 sid: Server ID989 did: Database ID990 scid: Schema ID991 """992 dependents_result = self.get_dependents(self.conn, scid)993 return ajax_response(994 response=dependents_result,995 status=200996 )997 998 @check_precondition999 def dependencies(self, gid, sid, did, scid):1000 """1001 This function get the dependencies and return ajax response1002 for the schema node.1003 1004 Args:1005 gid: Server Group ID1006 sid: Server ID1007 did: Database ID1008 scid: Schema ID1009 """1010 dependencies_result = self.get_dependencies(self.conn, scid)1011 return ajax_response(1012 response=dependencies_result,1013 status=2001014 )1015 1016 @check_precondition1017 def children(self, **kwargs):1018 """Build a list of treeview nodes from the child nodes."""1019 1020 SQL = render_template(1021 "/".join([self.template_path, 'sql/is_catalog.sql']),1022 scid=kwargs['scid'], _=gettext, conn=self.conn1023 )1024 1025 status, res = self.conn.execute_dict(SQL)1026 1027 if not status:1028 return internal_server_error(errormsg=res)1029 1030 if len(res['rows']) == 0:1031 return gone(gettext(1032 """Could not find the schema in the database.1033 It may have been removed by another user."""))1034 1035 data = res['rows'][0]1036 backend_support_keywords = kwargs.copy()1037 backend_support_keywords['is_catalog'] = data['is_catalog']1038 backend_support_keywords['db_support'] = data['db_support']1039 backend_support_keywords['schema_name'] = data['schema_name']1040 1041 nodes = []1042 for module in self.blueprint.submodules:1043 if isinstance(module, PGChildModule):1044 if (1045 self.manager is not None and1046 module.backend_supported(1047 self.manager, **backend_support_keywords1048 )1049 ):1050 nodes.extend(module.get_nodes(**kwargs))1051 else:1052 nodes.extend(module.get_nodes(**kwargs))1053 1054 return make_json_response(data=nodes)1055 1056 1057class CatalogView(SchemaView):1058 """1059 This class is responsible for generating routes for catalog schema node.1060 1061 Methods:1062 -------1063 * __init__(**kwargs)1064 - Method is used to initialize the CatalogView and it's base view.1065 1066 * create(gid, sid, did, scid)1067 - Raise an error - we cannot create a catalog.1068 1069 * update(gid, sid, did, scid)1070 - This function will update the data for the selected catalog node1071 1072 * delete(self, gid, sid, scid):1073 - Raise an error - we cannot delete a catalog.1074 1075 * get_sql(data, scid)1076 - This function will generate sql from model data1077 1078 """1079 1080 node_type = catalog_blueprint.node_type1081 BASE_TEMPLATE_PATH = 'catalog/{0}/#{1}#'1082 1083 def __init__(self, *args, **kwargs):1084 """1085 Initialize the variables used by methods of SchemaView.1086 """1087 1088 super().__init__(*args, **kwargs)1089 1090 def _formatter(self, data, scid=None):1091 1092 """1093 Overriding _formatter, because - we won't show the Default1094 privileges with the catalog schema.1095 """1096 1097 self._formatter_no_defacl(data, scid)1098 1099 return data1100 1101 def get_sql(self, gid, sid, data, scid=None):1102 """1103 This function will generate sql from model data1104 """1105 if scid is None:1106 return bad_request('Cannot create a catalog schema!')1107 1108 return super().get_sql(gid, sid, data, scid)1109 1110 @check_precondition1111 def sql(self, gid, sid, did, scid):1112 """1113 This function will generate reverse engineered sql for schema object1114 1115 Args:1116 gid: Server Group ID1117 sid: Server ID1118 did: Database ID1119 scid: Schema ID1120 """1121 SQL = render_template(1122 "/".join([self.template_path,1123 self._SQL_PREFIX + self._PROPERTIES_SQL]),1124 scid=scid, _=gettext1125 )1126 1127 status, res = self.conn.execute_dict(SQL)1128 if not status:1129 return internal_server_error(errormsg=res)1130 1131 if len(res['rows']) == 0:1132 return gone(gettext("""1133Could not find the schema in the database.1134It may have been removed by another user.1135"""))1136 1137 old_data = res['rows'][0]1138 old_data = self._formatter(old_data, scid)1139 1140 # Privileges1141 self.format_request_acls(old_data, specific=['nspacl'])1142 1143 # Render sql from create & alter sql using properties & acl data1144 SQL = ''1145 SQL = render_template(1146 "/".join([self.template_path,1147 self._SQL_PREFIX + self._CREATE_SQL]),1148 _=gettext, data=old_data, conn=self.conn,1149 add_not_exists_clause=True1150 )1151 1152 sql_header = """1153-- CATALOG: {0}1154 1155-- DROP SCHEMA IF EXISTS {0};1156 1157""".format(old_data['name'])1158 1159 SQL = sql_header + SQL1160 1161 return ajax_response(response=SQL.strip("\n"))1162 1163 1164SchemaDiffRegistry(schema_blueprint.node_type, SchemaView)1165SchemaView.register_node_view(schema_blueprint)1166CatalogView.register_node_view(catalog_blueprint)1167 