diff --git a/web/pgadmin/browser/server_groups/servers/databases/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/__init__.py index 5d1d7a5..040d970 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/__init__.py @@ -20,6 +20,7 @@ from pgadmin.utils.ajax import precondition_required from pgadmin.utils.driver import get_driver from config import PG_DEFAULT_DRIVER from functools import wraps +from pgadmin.browser.server_groups.servers.depends import get_dependencies, get_dependents class DatabaseModule(CollectionNodeModule): NODE_TYPE = 'database' @@ -725,4 +726,38 @@ class DatabaseView(NodeView): return ajax_response(response=SQL) + @check_precondition() + def dependents(self, gid, sid, did): + """ + This function get the dependents and return ajax response + for the tablespace node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + dependents_result = get_dependents(self.conn, did, 'database') + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition() + def dependencies(self, gid, sid, did): + """ + This function get the dependencies and return ajax response + for the tablespace node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + dependencies_result = get_dependencies(self.conn, did, 'database') + return ajax_response( + response=dependencies_result, + status=200 + ) + DatabaseView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/templates/databases/js/databases.js b/web/pgadmin/browser/server_groups/servers/databases/templates/databases/js/databases.js index 655fd9d..220265a 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/templates/databases/js/databases.js +++ b/web/pgadmin/browser/server_groups/servers/databases/templates/databases/js/databases.js @@ -46,6 +46,7 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) { parent_type: 'server', type: 'database', hasSQL: true, + hasDepends: true, canDrop: true, label: '{{ _('Database') }}', node_image: function() { diff --git a/web/pgadmin/browser/server_groups/servers/depends.py b/web/pgadmin/browser/server_groups/servers/depends.py new file mode 100644 index 0000000..84ecb63 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/depends.py @@ -0,0 +1,636 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Dependencies/Dependents helper utilities""" + +from flask import current_app +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER + + +def get_dependencies(conn, object_id, object_type_name, show_system_object=False, where=None): + """ + This function is used to fetch the dependencies for the selected node. + + Args: + conn: Connection object + object_id: Object Id of the selected node. + object_type_name: Type name of the selected node. + show_system_object: True or False (optional) + where: where clause for the sql query (optional) + + Returns: Dictionary of dependencies for the selected node. + """ + + if where is None: + where_clause = "WHERE dep.objid={0}::oid".format(object_id) + else: + where_clause = where + + query = """ +SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc, + CASE WHEN cl.relkind IS NOT NULL THEN cl.relkind || COALESCE(dep.refobjsubid::text, '') + WHEN tg.oid IS NOT NULL THEN 'T'::text + WHEN ty.oid IS NOT NULL THEN 'y'::text + WHEN ns.oid IS NOT NULL THEN 'n'::text + WHEN pr.oid IS NOT NULL THEN 'p'::text + WHEN la.oid IS NOT NULL THEN 'l'::text + WHEN rw.oid IS NOT NULL THEN 'R'::text + WHEN co.oid IS NOT NULL THEN 'C'::text || contype + WHEN ad.oid IS NOT NULL THEN 'A'::text ELSE '' + END AS type, + COALESCE(coc.relname, clrw.relname) AS ownertable, + CASE WHEN cl.relname IS NOT NULL OR att.attname IS NOT NULL THEN cl.relname || '.' || att.attname + ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname) + END AS refname, + COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname +FROM pg_depend dep +LEFT JOIN pg_class cl ON dep.refobjid=cl.oid +LEFT JOIN pg_attribute att ON dep.refobjid=att.attrelid AND dep.refobjsubid=att.attnum +LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid +LEFT JOIN pg_proc pr ON dep.refobjid=pr.oid +LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid +LEFT JOIN pg_trigger tg ON dep.refobjid=tg.oid +LEFT JOIN pg_type ty ON dep.refobjid=ty.oid +LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid +LEFT JOIN pg_constraint co ON dep.refobjid=co.oid +LEFT JOIN pg_class coc ON co.conrelid=coc.oid +LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid +LEFT JOIN pg_rewrite rw ON dep.refobjid=rw.oid +LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class +LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid +LEFT JOIN pg_language la ON dep.refobjid=la.oid +LEFT JOIN pg_namespace ns ON dep.refobjid=ns.oid +LEFT JOIN pg_attrdef ad ON ad.adrelid=att.attrelid AND ad.adnum=att.attnum +{0} AND +refclassid IN ( SELECT oid FROM pg_class WHERE relname IN + ('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace', + 'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger')) +ORDER BY refclassid, cl.relkind""".format(where_clause) + + # fetch the dependency for the selected object + dependencies = fetch_dependency(conn, query, show_system_object) + + # fetch role dependencies + if where_clause.find('subid') < 0: + sql = """ +SELECT rolname AS refname, refclassid, deptype +FROM pg_shdepend dep +LEFT JOIN pg_roles r ON refclassid=1260 AND refobjid=r.oid +{0} ORDER BY 1""".format(where_clause) + + status, result = conn.execute_dict(sql) + if not status: + current_app.logger.error(result) + + for row in result['rows']: + ref_name = row['refname'] + dep_str = row['deptype'] + dep_type = '' + + if dep_str == 'a': + dep_type = 'ACL' + elif dep_str == 'o': + dep_type = 'Owner' + + if row['refclassid'] == 1260: + dependencies.append({'type': 'role', 'name': ref_name, 'field': dep_type}) + + # A Corner case, reported by Guillaume Lelarge, could be found at: + # http://archives.postgresql.org/pgadmin-hackers/2009-03/msg00026.php + # + # SQL: + # CREATE TABLE t1 (id serial); + # CREATE TABLE t2 (LIKE t1 INCLUDING DEFAULTS); + # + # When we try to drop the table t1, it gives the following notice: + # "NOTICE: default for table t2 column id depends on sequence t1_id_seq" + # + # This suggests that the column 't2.id' should be shown in the "Dependency" list + # of the sequence 't1_seq_id' + # + # As we could not find any direct relationship between 't1_seq_id' and 't2' + # table, we come up with this solution. + + if object_type_name == 'sequence': + sql = """ +SELECT + CASE WHEN att.attname IS NOT NULL AND ref.relname IS NOT NULL THEN ref.relname || '.' || att.attname + ELSE ref.relname + END AS refname, + d2.refclassid, d1.deptype AS deptype +FROM pg_depend d1 + LEFT JOIN pg_depend d2 ON d1.objid=d2.objid AND d1.refobjid != d2.refobjid + LEFT JOIN pg_class ref ON ref.oid = d2.refobjid + LEFT JOIN pg_attribute att ON d2.refobjid=att.attrelid AND d2.refobjsubid=att.attnum +WHERE d1.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') + AND d2.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d1.refobjid) + AND d1.refobjid={0}::oid""".format(object_id) + + status, result = conn.execute_dict(sql) + if not status: + current_app.logger.error(result) + + for row in result['rows']: + ref_name = row['refname'] + if ref_name is None: + continue + + dep_type = '' + dep_str = row['deptype'] + if dep_str == 'a': + dep_type = 'auto' + elif dep_str == 'n': + dep_type = 'normal' + elif dep_str == 'i': + dep_type = 'internal' + + dependencies.append({'type': 'column', 'name': ref_name, 'field': dep_type}) + + return dependencies + + +def get_dependents(conn, object_id, object_type_name, show_system_object=False, where=None): + """ + This function is used to fetch the dependents for the selected node. + + Args: + conn: Connection object + object_id: Object Id of the selected node. + object_type_name: Type of the selected node. + show_system_object: True or False (optional) + where: where clause for the sql query (optional) + + Returns: Dictionary of dependents for the selected node. + """ + + if where is None: + where_clause = "WHERE dep.refobjid={0}::oid".format(object_id) + else: + where_clause = where + + query = """ +SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, ad.adsrc, + CASE WHEN cl.relkind IS NOT NULL THEN cl.relkind || COALESCE(dep.objsubid::text, '') + WHEN tg.oid IS NOT NULL THEN 'T'::text + WHEN ty.oid IS NOT NULL THEN 'y'::text + WHEN ns.oid IS NOT NULL THEN 'n'::text + WHEN pr.oid IS NOT NULL THEN 'p'::text + WHEN la.oid IS NOT NULL THEN 'l'::text + WHEN rw.oid IS NOT NULL THEN 'R'::text + WHEN co.oid IS NOT NULL THEN 'C'::text || contype + WHEN ad.oid IS NOT NULL THEN 'A'::text ELSE '' + END AS type, + COALESCE(coc.relname, clrw.relname) AS ownertable, + CASE WHEN cl.relname IS NOT NULL AND att.attname IS NOT NULL THEN cl.relname || '.' || att.attname + ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname) + END AS refname, + COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname +FROM pg_depend dep +LEFT JOIN pg_class cl ON dep.objid=cl.oid +LEFT JOIN pg_attribute att ON dep.objid=att.attrelid AND dep.objsubid=att.attnum +LEFT JOIN pg_namespace nsc ON cl.relnamespace=nsc.oid +LEFT JOIN pg_proc pr ON dep.objid=pr.oid +LEFT JOIN pg_namespace nsp ON pr.pronamespace=nsp.oid +LEFT JOIN pg_trigger tg ON dep.objid=tg.oid +LEFT JOIN pg_type ty ON dep.objid=ty.oid +LEFT JOIN pg_namespace nst ON ty.typnamespace=nst.oid +LEFT JOIN pg_constraint co ON dep.objid=co.oid +LEFT JOIN pg_class coc ON co.conrelid=coc.oid +LEFT JOIN pg_namespace nso ON co.connamespace=nso.oid +LEFT JOIN pg_rewrite rw ON dep.objid=rw.oid +LEFT JOIN pg_class clrw ON clrw.oid=rw.ev_class +LEFT JOIN pg_namespace nsrw ON clrw.relnamespace=nsrw.oid +LEFT JOIN pg_language la ON dep.objid=la.oid +LEFT JOIN pg_namespace ns ON dep.objid=ns.oid +LEFT JOIN pg_attrdef ad ON ad.oid=dep.objid +{0} AND +classid IN ( SELECT oid FROM pg_class WHERE relname IN + ('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace', + 'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger')) +ORDER BY classid, cl.relkind""".format(where_clause) + + # fetch the dependency for the selected object + dependents = fetch_dependency(conn, query, show_system_object) + + # A Corner case, reported by Guillaume Lelarge, could be found at: + # http://archives.postgresql.org/pgadmin-hackers/2009-03/msg00026.php + # + # SQL: + # CREATE TABLE t1 (id serial); + # CREATE TABLE t2 (LIKE t1 INCLUDING DEFAULTS); + # + # When we try to drop the table t1, it gives the following notice: + # "NOTICE: default for table t2 column id depends on sequence t1_id_seq" + # + # This suggests that the sequence 't1_seq_id' should be shown in the + # "Dependents" list of the table 't2' and column 't2.id' + # + # As we could not find any direct relationship between 't1_seq_id' and 't2' + # table, we come up with this solution. + + if object_type_name == 'table' or object_type_name == 'column': + sql = """ +SELECT + ref.relname AS refname, d2.refclassid, dep.deptype AS deptype +FROM pg_depend dep +LEFT JOIN pg_depend d2 ON dep.objid=d2.objid AND dep.refobjid != d2.refobjid +LEFT JOIN pg_class ref ON ref.oid=d2.refobjid +LEFT JOIN pg_attribute att ON d2.refclassid=att.attrelid AND d2.refobjsubid=att.attnum +{0} AND +dep.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef') AND +dep.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d2.refobjid)""".format(where_clause) + + status, result = conn.execute_dict(sql) + if not status: + current_app.logger.error(result) + + for row in result['rows']: + ref_name = row['refname'] + if ref_name is None: + continue + + dep_type = '' + dep_str = row['deptype'] + if dep_str == 'a': + dep_type = 'auto' + elif dep_str == 'n': + dep_type = 'normal' + elif dep_str == 'i': + dep_type = 'internal' + + dependents.append({'type': 'sequence', 'name': ref_name, 'field': dep_type}) + + return dependents + + +def fetch_dependency(conn, query, show_system_object): + """ + This function is used to fetch the dependency for the selected node. + + Args: + conn: Connection object + query: sql query to fetch dependencies/dependents + show_system_object: true or false + + Returns: Dictionary of dependency for the selected node. + """ + + # Dictionary for the object types + types = { + # None specified special handling for this type + 'r': None, + 'i': 'index', + 'S': 'sequence', + 'v': 'view', + 'x': 'external_table', + 'p': 'function', + 'n': 'schema', + 'y': 'type', + 'T': 'trigger', + 'l': 'language', + 'R': None, + 'C': None, + 'A': None + } + + # Dictionary for the restrictions + dep_types = { + # None specified special handling for this type + 'n': 'normal', + 'a': 'auto', + 'i': None, + 'p': None + } + + status, result = conn.execute_dict(query) + if not status: + current_app.logger.error(result) + + dependency = list() + + for row in result['rows']: + _ref_name = row['refname'] + type_str = row['type'] + dep_str = row['deptype'] + nsp_name = row['nspname'] + + ref_name = '' + if nsp_name is not None: + ref_name = nsp_name + '.' + + type_name = '' + + # Fetch the type name from the dictionary + # if type is not present in the types dictionary then + # we will continue and not going to add it. + if type_str[0] in types: + + # if type is present in the types dictionary, but it's + # value is None then it requires special handling. + if types[type_str[0]] is None: + if type_str[0] == 'r': + if int(type_str[1]) > 0: + type_name = 'column' + else: + type_name = 'table' + elif type_str[0] == 'R': + type_name = 'rule' + ref_name = _ref_name + ' ON ' + _ref_name + row['ownertable'] + _ref_name = None + elif type_str[0] == 'C': + if type_str[1] == 'c': + type_name = 'check' + elif type_str[1] == 'f': + type_name = 'foreign_key' + ref_name += row['ownertable'] + '.' + elif type_str[1] == 'p': + type_name = 'primary_key' + elif type_str[1] == 'u': + type_name = 'unique' + elif type_str[1] == 'x': + type_name = 'exclude' + elif type_str[0] == 'A': + # Include only functions + if row['adbin'].startswith('{FUNCEXPR'): + type_name = 'function' + ref_name = row['adsrc'] + else: + continue + else: + type_name = types[type_str[0]] + else: + continue + + if _ref_name is not None: + ref_name += _ref_name + + dep_type = '' + if dep_str[0] in dep_types: + + # if dep_type is present in the dep_types dictionary, but it's + # value is None then it requires special handling. + if dep_types[dep_str[0]] is None: + if dep_str[0] == 'i': + if show_system_object: + dep_type = 'internal' + else: + continue + elif dep_str[0] == 'p': + dep_type = 'pin' + type_name = '' + else: + dep_type = dep_types[dep_str[0]] + + dependency.append({'type': type_name, 'name': ref_name, 'field': dep_type}) + + return dependency + + +def get_tablespace_dependents(conn, sid, object_id): + """ + This function is used to fetch the dependents for the tablespace. + + Args: + conn: Connection object + sid: Server ID + object_id: Object Id of the selected node. + + Returns: Dictionary of dependents for the tablespace. + """ + + # Fetching databases with CONNECT privileges status. + query = """ +SELECT datname, datallowconn AND pg_catalog.has_database_privilege(datname, 'CONNECT') AS datallowconn, dattablespace +FROM pg_database db ORDER BY datname""" + + status, result = conn.execute_dict(query) + if not status: + current_app.logger.error(result) + + dependents = list() + + # Get the server manager + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + + for row in result['rows']: + oid = row['dattablespace'] + + # Append all the databases to the dependents list if oid is same + if object_id == oid: + dependents.append({ + 'type': 'database', 'name': '', 'field': row['datname'] + }) + + # If connection to the database is not allowed then continue + # with the next database + if not row['datallowconn']: + continue + + # Get the connection from the manager for the specified database. + # Check the connect status and if it is not connected then create + # a new connection to run the query and fetch the dependents. + is_connected = True + try: + temp_conn = manager.connection(row['datname']) + is_connected = temp_conn.connected() + if not is_connected: + temp_conn.connect() + except Exception as e: + current_app.logger.exception(e) + + if temp_conn.connected(): + query = """ +SELECT cl.relkind, COALESCE(cin.nspname, cln.nspname) as nspname, COALESCE(ci.relname, cl.relname) as relname, + cl.relname as indname +FROM pg_class cl +JOIN pg_namespace cln ON cl.relnamespace=cln.oid +LEFT OUTER JOIN pg_index ind ON ind.indexrelid=cl.oid +LEFT OUTER JOIN pg_class ci ON ind.indrelid=ci.oid +LEFT OUTER JOIN pg_namespace cin ON ci.relnamespace=cin.oid, +pg_database WHERE datname = current_database() AND +(cl.reltablespace = {0}::oid OR (cl.reltablespace=0 AND dattablespace = {0}::oid)) ORDER BY 1,2,3 +""".format(object_id) + + fetch_dependents(temp_conn, query, row['datname'], dependents) + + # Release only those connections which we have created above. + if not is_connected: + manager.release(row['datname']) + + return dependents + + +def get_role_dependents(conn, sid, object_id): + """ + This function is used to fetch the dependents for the role. + + Args: + conn: Connection object + sid: Server ID + object_id: Object Id of the selected node. + + Returns: Dictionary of dependents for the role. + """ + + # Fetching databases with CONNECT privileges status. + query = """ +SELECT 'd' as type, datname, + datallowconn AND pg_catalog.has_database_privilege(datname, 'CONNECT') AS datallowconn, datdba, datlastsysoid +FROM pg_database db +UNION +SELECT 'M', spcname, null, null, null + FROM pg_tablespace where spcowner= {0} +ORDER BY 1, 2 + """.format(object_id) + + status, result = conn.execute_dict(query) + if not status: + current_app.logger.error(result) + + dependents = list() + + # Get the server manager + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + + for row in result['rows']: + oid = row['datdba'] + if row['type'] == 'd': + if object_id == oid: + dependents.append({'type': 'database', 'name': '', 'field': row['datname']}) + else: + dependents.append({'type': 'tablespace', 'name': row['datname'], 'field': ''}) + + # If connection to the database is not allowed then continue + # with the next database + if not row['datallowconn']: + continue + + # Get the connection from the manager for the specified database. + # Check the connect status and if it is not connected then create + # a new connection to run the query and fetch the dependents. + is_connected = True + try: + temp_conn = manager.connection(row['datname']) + is_connected = temp_conn.connected() + if not is_connected: + temp_conn.connect() + except Exception as e: + current_app.logger.exception(e) + + if temp_conn.connected(): + + # Fetch the dependent ids from pg_shdepend + dep_ids = """SELECT objid FROM pg_shdepend WHERE refobjid={0}::oid """.format(object_id) + + query = """ +SELECT cl.relkind, COALESCE(cin.nspname, cln.nspname) as nspname, + COALESCE(ci.relname, cl.relname) as relname, cl.relname as indname +FROM pg_class cl +JOIN pg_namespace cln ON cl.relnamespace=cln.oid +LEFT OUTER JOIN pg_index ind ON ind.indexrelid=cl.oid +LEFT OUTER JOIN pg_class ci ON ind.indrelid=ci.oid +LEFT OUTER JOIN pg_namespace cin ON ci.relnamespace=cin.oid +WHERE cl.oid IN ({1}) AND cl.oid > {0} +UNION ALL SELECT 'n', null, nspname, null + FROM pg_namespace nsp + WHERE nsp.oid IN ({1}) AND nsp.oid > {0} +UNION ALL SELECT CASE WHEN typtype='d' THEN 'd' ELSE 'y' END, null, typname, null + FROM pg_type ty + WHERE ty.oid IN ({1}) AND ty.oid > {0} +UNION ALL SELECT 'C', null, conname, null + FROM pg_conversion co + WHERE co.oid IN ({1}) AND co.oid > {0} +UNION ALL SELECT CASE WHEN prorettype=2279 THEN 'T' ELSE 'p' END, null, proname, null + FROM pg_proc pr + WHERE pr.oid IN ({1}) AND pr.oid > {0} +UNION ALL SELECT 'o', null, oprname || '('::text || COALESCE(tl.typname, ''::text) || CASE WHEN tl.oid IS NOT NULL + AND tr.oid IS NOT NULL THEN ','::text END || COALESCE(tr.typname, ''::text) || ')'::text, null + FROM pg_operator op + LEFT JOIN pg_type tl ON tl.oid=op.oprleft + LEFT JOIN pg_type tr ON tr.oid=op.oprright + WHERE op.oid IN ({1}) AND op.oid > {0} +ORDER BY 1,2,3 +""".format(row['datlastsysoid'], dep_ids) + + fetch_dependents(temp_conn, query, row['datname'], dependents) + + # Release only those connections which we have created above. + if not is_connected: + manager.release(row['datname']) + + return dependents + + +def fetch_dependents(conn, query, db_name, dependents): + """ + This function is used to fetch the dependents for Tablespace, + Role and User node. + + Args: + conn: Connection object + query: sql query to fetch dependents + db_name: Database Name + dependents: Dictionary of dependents + """ + + # Dictionary for the object types + types = { + # None specified special handling for this type + 'r': 'table', + 'i': None, + 'S': 'sequence', + 'v': 'view', + 'x': 'external_table', + 'p': 'function', + 'n': 'schema', + 'y': 'type', + 'd': 'domain', + 'T': 'trigger_function', + 'C': 'conversion', + 'o': None + } + + status, result = conn.execute_dict(query) + if not status: + current_app.logger.error(result) + + for row in result['rows']: + rel_name = row['nspname'] + if rel_name is not None: + rel_name += '.' + + if rel_name is None: + rel_name = row['relname'] + else: + rel_name += row['relname'] + + type_name = '' + type_str = row['relkind'] + + # Fetch the type name from the dictionary + # if type is not present in the types dictionary then + # we will continue and not going to add it. + if type_str[0] in types: + + # if type is present in the types dictionary, but it's + # value is None then it requires special handling. + if types[type_str[0]] is None: + if type_str[0] == 'i': + type_name = 'index' + rel_name = row['indname'] + ' ON ' + rel_name + elif type_str[0] == 'o': + type_name = 'operator' + rel_name = row['relname'] + else: + type_name = types[type_str[0]] + else: + continue + + dependents.append({'type': type_name, 'name': rel_name, 'field': db_name}) diff --git a/web/pgadmin/browser/server_groups/servers/roles/__init__.py b/web/pgadmin/browser/server_groups/servers/roles/__init__.py index 1068387..3ca3123 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/roles/__init__.py @@ -21,6 +21,7 @@ import re import datetime from functools import wraps import simplejson as json +from pgadmin.browser.server_groups.servers.depends import get_dependencies, get_role_dependents class RoleModule(CollectionNodeModule): @@ -847,11 +848,37 @@ rolmembership:{ @check_precondition() def dependencies(self, gid, sid, rid): - return not_implemented() + """ + This function get the dependencies and return ajax response + for the role. + + Args: + gid: Server Group ID + sid: Server ID + rid: Role ID + """ + dependencies_result = get_dependencies(self.conn, rid, 'role') + return ajax_response( + response=dependencies_result, + status=200 + ) @check_precondition() def dependents(self, gid, sid, rid): - return not_implemented() + """ + This function get the dependents and return ajax response + for the role. + + Args: + gid: Server Group ID + sid: Server ID + rid: Role ID + """ + dependents_result = get_role_dependents(self.conn, sid, rid) + return ajax_response( + response=dependents_result, + status=200 + ) @check_precondition() def variables(self, gid, sid, rid): diff --git a/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js b/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js index 77a1a22..a6ecfff 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js +++ b/web/pgadmin/browser/server_groups/servers/roles/templates/role/js/role.js @@ -293,10 +293,13 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backform) { label: '{{ _('Login/Group Role') }}', hasSQL: true, canDrop: true, + hasDepends: true, node_label: function(r) { return r.label; }, node_image: function(r) { + if (r == null || r == undefined) + return 'icon-role'; return (r.can_login ? 'icon-role' : 'icon-group'); }, title: function(d) { diff --git a/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py b/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py index 1b29b96..34d08d7 100644 --- a/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/tablespaces/__init__.py @@ -20,6 +20,7 @@ from config import PG_DEFAULT_DRIVER from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \ parse_priv_to_db from functools import wraps +from pgadmin.browser.server_groups.servers.depends import get_dependencies, get_tablespace_dependents class TablespaceModule(CollectionNodeModule): @@ -504,5 +505,39 @@ class TablespaceView(NodeView): result = 'Tablespace Size: {0}'.format(res) return ajax_response(response=result) + @check_precondition + def dependents(self, gid, sid, did): + """ + This function get the dependents and return ajax response + for the tablespace node. + + Args: + gid: Server Group ID + sid: Server ID + did: Tablespace ID + """ + dependents_result = get_tablespace_dependents(self.conn, sid, did) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did): + """ + This function get the dependencies and return ajax response + for the tablespace node. + + Args: + gid: Server Group ID + sid: Server ID + did: Tablespace ID + """ + dependencies_result = get_dependencies(self.conn, did, 'tablespace') + return ajax_response( + response=dependencies_result, + status=200 + ) + TablespaceView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/tablespaces/templates/tablespaces/js/tablespaces.js b/web/pgadmin/browser/server_groups/servers/tablespaces/templates/tablespaces/js/tablespaces.js index 33985a7..a304c58 100644 --- a/web/pgadmin/browser/server_groups/servers/tablespaces/templates/tablespaces/js/tablespaces.js +++ b/web/pgadmin/browser/server_groups/servers/tablespaces/templates/tablespaces/js/tablespaces.js @@ -48,6 +48,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { label: '{{ _('Tablespace') }}', hasSQL: true, canDrop: true, + hasDepends: true, Init: function() { /* Avoid mulitple registration of menus */ if (this.initialized) diff --git a/web/pgadmin/browser/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js index ccf3217..3aac518 100644 --- a/web/pgadmin/browser/templates/browser/js/browser.js +++ b/web/pgadmin/browser/templates/browser/js/browser.js @@ -94,7 +94,7 @@ function(require, $, _, S, Bootstrap, pgAdmin, alertify, CodeMirror) { width: 500, isCloseable: false, isPrivate: true, - content: '
Depedencies pane
', + content: 'Dependent pane
', + content: '