From 3969e915630d2e566575be5dac99d2b79b5093b3 Mon Sep 17 00:00:00 2001 From: Neel Patel Date: Thu, 17 Mar 2016 11:39:38 +0000 Subject: [PATCH] Add support for Foreign data wrappers, servers and user mappings. --- .../foreign_data_wrappers/__init__.py | 759 ++++++++++++++++++ .../foreign_servers/__init__.py | 754 +++++++++++++++++ .../static/img/coll-foreign_server.png | Bin 0 -> 476 bytes .../static/img/foreign_server.png | Bin 0 -> 518 bytes .../foreign_servers/js/foreign_servers.js | 180 +++++ .../foreign_servers/sql/9.1_plus/acl.sql | 27 + .../foreign_servers/sql/9.1_plus/create.sql | 40 + .../foreign_servers/sql/9.1_plus/delete.sql | 9 + .../sql/9.1_plus/dependents.sql | 12 + .../sql/9.1_plus/properties.sql | 27 + .../foreign_servers/sql/9.1_plus/update.sql | 87 ++ .../foreign_servers/sql/9.3_plus/acl.sql | 21 + .../foreign_servers/sql/9.3_plus/create.sql | 40 + .../foreign_servers/sql/9.3_plus/delete.sql | 9 + .../sql/9.3_plus/dependents.sql | 12 + .../sql/9.3_plus/properties.sql | 26 + .../foreign_servers/sql/9.3_plus/update.sql | 87 ++ .../foreign_servers/user_mapping/__init__.py | 753 +++++++++++++++++ .../static/img/coll-user_mapping.png | Bin 0 -> 557 bytes .../user_mapping/static/img/user_mapping.png | Bin 0 -> 518 bytes .../user_mappings/js/user_mappings.js | 157 ++++ .../user_mappings/sql/9.1_plus/create.sql | 13 + .../user_mappings/sql/9.1_plus/delete.sql | 9 + .../user_mappings/sql/9.1_plus/properties.sql | 23 + .../user_mappings/sql/9.1_plus/update.sql | 37 + .../static/img/coll-foreign_data_wrapper.png | Bin 0 -> 444 bytes .../static/img/foreign_data_wrapper.png | Bin 0 -> 411 bytes .../js/foreign_data_wrappers.js | 180 +++++ .../sql/9.1_plus/acl.sql | 27 + .../sql/9.1_plus/create.sql | 38 + .../sql/9.1_plus/delete.sql | 8 + .../sql/9.1_plus/handlers.sql | 2 + .../sql/9.1_plus/properties.sql | 14 + .../sql/9.1_plus/update.sql | 99 +++ .../sql/9.1_plus/validators.sql | 2 + .../sql/9.3_plus/acl.sql | 21 + .../sql/9.3_plus/create.sql | 37 + .../sql/9.3_plus/delete.sql | 8 + .../sql/9.3_plus/handlers.sql | 2 + .../sql/9.3_plus/properties.sql | 14 + .../sql/9.3_plus/update.sql | 99 +++ .../sql/9.3_plus/validators.sql | 2 + .../depends/sql/9.1_plus/dependents.sql | 28 +- web/pgadmin/browser/utils.py | 2 + web/pgadmin/misc/depends/static/js/depends.js | 4 +- .../static/js/alertifyjs/pgadmin.defaults.js | 4 +- 46 files changed, 3661 insertions(+), 12 deletions(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/__init__.py create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/img/coll-foreign_server.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/img/foreign_server.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/js/foreign_servers.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/acl.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/dependents.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.1_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/acl.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/dependents.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/templates/foreign_servers/sql/9.3_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/__init__.py create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/img/coll-user_mapping.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/img/user_mapping.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/js/user_mappings.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/coll-foreign_data_wrapper.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/foreign_data_wrapper.png create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/js/foreign_data_wrappers.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/acl.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/handlers.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/validators.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/acl.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/handlers.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/update.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/validators.sql diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/__init__.py new file mode 100644 index 000000000..a6b831e85 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/__init__.py @@ -0,0 +1,759 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Foreign Data Wrapper Node""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers as servers +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +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 + + +class ForeignDataWrapperModule(CollectionNodeModule): + """ + class ForeignDataWrapperModule(CollectionNodeModule) + + A module class for foreign data wrapper node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Foreign data wrapper module and it's base module. + + * get_nodes(gid, sid, did) + - Method is used to generate the browser collection node. + + * script_load() + - Load the module script for foreign data wrapper, when any of the database node is + initialized. + """ + + NODE_TYPE = 'foreign_data_wrapper' + COLLECTION_LABEL = gettext("Foreign Data Wrappers") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the Foreign data wrapper module and it's base module. + + Args: + *args: + **kwargs: + """ + + self.min_ver = None + self.max_ver = None + + super(ForeignDataWrapperModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did): + """ + Method is used to generate the browser collection node + + Args: + gid: Server Group ID + sid: Server ID + did: Database Id + """ + yield self.generate_browser_collection_node(did) + + @property + def script_load(self): + """ + Load the module script for foreign data wrapper, when any of the database node is initialized. + + Returns: node type of the server module. + """ + return servers.ServerModule.NODE_TYPE + +blueprint = ForeignDataWrapperModule(__name__) + + +class ForeignDataWrapperView(PGChildNodeView): + """ + class ForeignDataWrapperView(PGChildNodeView) + + A view class for foreign data wrapper node derived from PGChildNodeView. This class is + responsible for all the stuff related to view like updating foreign data wrapper + node, showing properties, showing sql in sql pane. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the ForeignDataWrapperView and it's base view. + + * module_js() + - This property defines (if javascript) exists for this node. + Override this property for your own logic + + * check_precondition() + - This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + + * list(gid, sid, did) + - This function is used to list all the foreign data wrapper nodes within that collection. + + * nodes(gid, sid, did) + - This function will used to create all the child node within that collection. + Here it will create all the foreign data wrapper node. + + * properties(gid, sid, did, fid) + - This function will show the properties of the selected foreign data wrapper node + + * tokenizeOptions(option_value) + - This function will tokenize the string stored in database + + * create(gid, sid, did) + - This function will create the new foreign data wrapper node + + * delete(gid, sid, did, fid) + - This function will delete the selected foreign data wrapper node + + * update(gid, sid, did, fid) + - This function will update the data for the selected foreign data wrapper node + + * msql(gid, sid, did, fid) + - This function is used to return modified SQL for the selected foreign data wrapper node + + * get_sql(data, fid) + - This function will generate sql from model data + + * get_validators(gid, sid, did) + - This function returns the validators for the selected foreign data wrapper node + + * get_handlers(gid, sid, did) + - This function returns the handlers for the selected foreign data wrapper node + + * sql(gid, sid, did, fid): + - This function will generate sql to show it in sql pane for the selected foreign data wrapper node. + + * dependents(gid, sid, did, fid): + - This function get the dependents and return ajax response for the foreign data wrapper node. + + * dependencies(self, gid, sid, did, fid): + - This function get the dependencies and return ajax response for the foreign data wrapper node. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'} + ] + ids = [ + {'type': 'int', 'id': 'fid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{ + 'delete': 'delete' + }], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'children': [{'get': 'children'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}], + 'get_handlers': [{}, {'get': 'get_handlers'}], + 'get_validators': [{}, {'get': 'get_validators'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "foreign_data_wrappers/js/foreign_data_wrappers.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + # we will set template path for sql scripts + if ver >= 90300: + self.template_path = 'foreign_data_wrappers/sql/9.3_plus' + else: + self.template_path = 'foreign_data_wrappers/sql/9.1_plus' + + return f(*args, **kwargs) + return wrap + + @check_precondition + def list(self, gid, sid, did): + """ + This function is used to list all the foreign data wrapper nodes within that collection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql']), conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + for row in res['rows']: + if row['fdwoptions'] is not None: + row['fdwoptions'] = self.tokenize_options(row['fdwoptions']) + + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did): + """ + This function will used to create all the child node within that collection. + Here it will create all the foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + res = [] + sql = render_template("/".join([self.template_path, 'properties.sql']), conn=self.conn) + status, r_set = self.conn.execute_2darray(sql) + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + res.append( + self.blueprint.generate_browser_node( + row['fdwoid'], + did, + row['name'], + icon="icon-foreign_data_wrapper" + )) + + return make_json_response( + data=res, + status=200 + ) + + def tokenize_options(self, option_value): + """ + This function will tokenize the string stored in database + e.g. database store the value as below + key1=value1, key2=value2, key3=value3, .... + This function will extract key and value from above string + + Args: + option_value: key value option/value pair read from database + """ + if option_value is not None: + option_str = option_value.split(',') + fdw_options = [] + for fdw_option in option_str: + k, v = fdw_option.split('=', 1) + fdw_options.append({'fdwoption': k, 'fdwvalue': v}) + return fdw_options + + @check_precondition + def properties(self, gid, sid, did, fid): + """ + This function will show the properties of the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fdwoptions'] is not None: + res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions']) + + sql = render_template("/".join([self.template_path, 'acl.sql']), fid=fid) + status, fdw_acl_res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=fdw_acl_res) + + for row in fdw_acl_res['rows']: + privilege = parse_priv_from_db(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(privilege) + else: + res['rows'][0][row['deftype']] = [privilege] + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did): + """ + This function will create the foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + required_args = [ + 'name' + ] + + data = request.form if request.form else json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + + if 'fdwacl' in data: + data['fdwacl'] = parse_priv_to_db(data['fdwacl'], ['U']) + + new_list = [] + + # Allow user to set the blank value in fdwvalue field in option model + if 'fdwoptions' in data: + for item in data['fdwoptions']: + new_dict = {} + if item['fdwoption']: + if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '': + new_dict.update(item); + else: + new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''}) + + new_list.append(new_dict) + + data['fdwoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + sql = render_template("/".join([self.template_path, 'properties.sql']), fname=data['name'], conn=self.conn) + + status, r_set = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + return jsonify( + node=self.blueprint.generate_browser_node( + row['fdwoid'], + did, + row['name'], + icon='icon-foreign_data_wrapper' + ) + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, fid): + """ + This function will update the data for the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + data = request.form if request.form else json.loads(request.data.decode()) + sql = self.get_sql(gid, sid, data, did, fid) + try: + if sql and sql.strip('\n') and sql.strip(' '): + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Foreign Data Wrapper updated", + data={ + 'id': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, fid): + """ + This function will delete the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + # Get name of foreign data wrapper from fid + sql = render_template("/".join([self.template_path, 'delete.sql']), fid=fid, conn=self.conn) + status, name = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=name) + # drop foreign data wrapper node + sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade, + conn=self.conn) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Foreign Data Wrapper dropped"), + data={ + 'id': fid, + 'did': did, + 'sid': sid, + 'gid': gid, + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, fid=None): + """ + This function is used to return modified SQL for the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + sql = self.get_sql(gid, sid, data, did, fid) + if sql and sql.strip('\n') and sql.strip(' '): + return make_json_response(data=sql,status=200) + else: + return make_json_response(data='-- Modified SQL --',status=200) + + def get_sql(self, gid, sid, data, did, fid=None): + """ + This function will generate sql from model data. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + data: Contains the data of the selected foreign data wrapper node + fid: foreign data wrapper ID + """ + required_args = [ + 'name' + ] + try: + if fid is not None: + sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fdwoptions'] is not None: + res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions']) + + for key in ['fdwacl']: + if key in data and data[key] is not None: + if 'added' in data[key]: + data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U']) + if 'changed' in data[key]: + data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U']) + if 'deleted' in data[key]: + data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U']) + + old_data = res['rows'][0] + for arg in required_args: + if arg not in data: + data[arg] = old_data[arg] + + new_list_add = [] + new_list_change = [] + + # Allow user to set the blank value in fdwvalue field in option model + if 'fdwoptions' in data and 'added' in data['fdwoptions']: + for item in data['fdwoptions']['added']: + new_dict_add = {} + if item['fdwoption']: + if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '': + new_dict_add.update(item); + else: + new_dict_add.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''}) + + new_list_add.append(new_dict_add) + + data['fdwoptions']['added'] = new_list_add + + # Allow user to set the blank value in fdwvalue field in option model + if 'fdwoptions' in data and 'changed' in data['fdwoptions']: + for item in data['fdwoptions']['changed']: + new_dict_change = {} + if item['fdwoption']: + if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '': + new_dict_change.update(item); + else: + new_dict_change.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''}) + + new_list_change.append(new_dict_change) + + data['fdwoptions']['changed'] = new_list_change + + sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data, + conn=self.conn) + else: + for key in ['fdwacl']: + if key in data and data[key] is not None: + data[key] = parse_priv_to_db(data[key], ['U']) + + new_list = [] + + # Allow user to set the blank value in fdwvalue field in option model + if 'fdwoptions' in data: + for item in data['fdwoptions']: + new_dict = {} + if item['fdwoption']: + if 'fdwvalue' in item and item['fdwvalue'] and item['fdwvalue'] != '': + new_dict.update(item); + else: + new_dict.update({'fdwoption': item['fdwoption'], 'fdwvalue': ''}) + + new_list.append(new_dict) + + data['fdwoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn) + sql += "\n" + return sql + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, fid): + """ + This function will generate sql to show it in sql pane for the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + """ + sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fdwoptions'] is not None: + res['rows'][0]['fdwoptions'] = self.tokenize_options(res['rows'][0]['fdwoptions']) + + sql = render_template("/".join([self.template_path, 'acl.sql']), fid=fid) + status, fdw_acl_res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=fdw_acl_res) + + for row in fdw_acl_res['rows']: + privilege = parse_priv_from_db(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(privilege) + else: + res['rows'][0][row['deftype']] = [privilege] + + # To format privileges + if 'fdwacl' in res['rows'][0]: + res['rows'][0]['fdwacl'] = parse_priv_to_db(res['rows'][0]['fdwacl'], ['U']) + + sql = '' + sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], conn=self.conn) + sql += "\n" + + sql_header = """-- Foreign Data Wrapper: {0} + +-- DROP FOREIGN DATA WRAPPER {0} + +""".format(res['rows'][0]['name']) + + sql = sql_header + sql + + return ajax_response(response=sql) + + @check_precondition + def get_validators(self, gid, sid, did): + """ + This function returns the validators for the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + res = [{'label': '', 'value': ''}] + try: + sql = render_template("/".join([self.template_path, 'validators.sql']), conn=self.conn) + status, r_set = self.conn.execute_2darray(sql) + + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + res.append({'label': row['fdwvalue'], 'value': row['fdwvalue']}) + + return make_json_response( data=res, status=200) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def get_handlers(self, gid, sid, did): + """ + This function returns the handlers for the selected foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + """ + res = [{'label': '', 'value': ''}] + try: + sql = render_template("/".join([self.template_path, 'handlers.sql']), conn=self.conn) + status, r_set = self.conn.execute_2darray(sql) + + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + res.append({'label': row['fdwhan'], 'value': row['fdwhan']}) + + return make_json_response( + data=res, + status=200 + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def dependents(self, gid, sid, did, fid): + """ + This function get the dependents and return ajax response + for the foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + dependents_result = self.get_dependents(self.conn, fid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, fid): + """ + This function get the dependencies and return ajax response + for the foreign data wrapper node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign Data Wrapper ID + """ + dependencies_result = self.get_dependencies(self.conn, fid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +ForeignDataWrapperView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py new file mode 100644 index 000000000..163d2119d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/__init__.py @@ -0,0 +1,754 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Foreign Server Node""" + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers as servers +from pgadmin.utils.ajax import precondition_required +from pgadmin.utils.driver import get_driver +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 + + +class ForeignServerModule(CollectionNodeModule): + """ + class ForeignServerModule(CollectionNodeModule) + + A module class for foreign server node derived from CollectionNodeModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Foreign server module and it's base module. + + * get_nodes(gid, sid, did) + - Method is used to generate the browser collection node. + + * script_load(self) + - Load the module script for foreign server, when any of the database node is + initialized. + """ + + NODE_TYPE = 'foreign_server' + COLLECTION_LABEL = gettext("Foreign Servers") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the Foreign server module and it's base module. + + Args: + *args: + **kwargs: + """ + + self.min_ver = None + self.max_ver = None + + super(ForeignServerModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did, fid): + """ + Method is used to generate the browser collection node + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + yield self.generate_browser_collection_node(fid) + + @property + def script_load(self): + """ + Load the module script for foreign server, when any of the foreign data wrapper node is initialized. + + Returns: node type of the server module. + """ + return servers.ServerModule.NODE_TYPE + +blueprint = ForeignServerModule(__name__) + + +class ForeignServerView(PGChildNodeView): + """ + class ForeignServerView(PGChildNodeView) + + A view class for foreign server node derived from PGChildNodeView. This class is + responsible for all the stuff related to view like updating foreign server + node, showing properties, showing sql in sql pane. + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the ForeignServerView and it's base view. + + * module_js() + - This property defines (if javascript) exists for this node. + Override this property for your own logic + + * check_precondition() + - This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + + * list(gid, sid, did, fid) + - This function is used to list all the foreign server nodes within that collection. + + * nodes(gid, sid, did, fid) + - This function will used to create all the child node within that collection. + Here it will create all the foreign server node. + + * properties(gid, sid, did, fid, fsid) + - This function will show the properties of the selected foreign server node + + * tokenizeOptions(option_value) + - This function will tokenize the string stored in database + + * update(gid, sid, did, fid, fsid) + - This function will update the data for the selected foreign server node + + * create(gid, sid, did, fid) + - This function will create the new foreign server node + + * delete(gid, sid, did, fid, fsid) + - This function will delete the selected foreign server node + + * msql(gid, sid, did, fid, fsid) + - This function is used to return modified SQL for the selected foreign server node + + * get_sql(data, fid, fsid) + - This function will generate sql from model data + + * sql(gid, sid, did, fid, fsid): + - This function will generate sql to show it in sql pane for the selected foreign server node. + + * dependents(gid, sid, did, fid, fsid): + - This function get the dependents and return ajax response for the foreign server node. + + * dependencies(self, gid, sid, did, fid, fsid): + - This function get the dependencies and return ajax response for the foreign server node. + """ + + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'fid'} + ] + ids = [ + {'type': 'int', 'id': 'fsid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{ + 'delete': 'delete' + }], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'children': [{'get': 'children'}], + 'sql': [{'get': 'sql'}], + 'msql': [{'get': 'msql'}, {'get': 'msql'}], + 'stats': [{'get': 'statistics'}], + 'dependency': [{'get': 'dependencies'}], + 'dependent': [{'get': 'dependents'}], + 'module.js': [{}, {}, {'get': 'module_js'}] + }) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "foreign_servers/js/foreign_servers.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + + def check_precondition(f): + """ + This function will behave as a decorator which will checks + database connection before running view, it will also attaches + manager,conn & template_path properties to self + """ + @wraps(f) + def wrap(*args, **kwargs): + # Here args[0] will hold self & kwargs will hold gid,sid,did + self = args[0] + self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid']) + self.conn = self.manager.connection(did=kwargs['did']) + + # If DB not connected then return error to browser + if not self.conn.connected(): + return precondition_required( + gettext( + "Connection to the server has been lost!" + ) + ) + + ver = self.manager.version + # we will set template path for sql scripts + if ver >= 90300: + self.template_path = 'foreign_servers/sql/9.3_plus' + else: + self.template_path = 'foreign_servers/sql/9.1_plus' + + return f(*args, **kwargs) + return wrap + + @check_precondition + def list(self, gid, sid, did, fid): + """ + This function is used to list all the foreign server nodes within that collection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, fid): + """ + This function will used to create all the child node within that collection. + Here it will create all the foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + """ + + res = [] + sql = render_template("/".join([self.template_path, 'properties.sql']), fid=fid, conn=self.conn) + status, r_set = self.conn.execute_2darray(sql) + + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + res.append( + self.blueprint.generate_browser_node( + row['fsrvid'], + fid, + row['name'], + icon="icon-foreign_server" + )) + + return make_json_response( + data=res, + status=200 + ) + + def tokenizeOptions(self, option_value): + """ + This function will tokenize the string stored in database + e.g. database store the value as below + key1=value1, key2=value2, key3=value3, .... + This function will extract key and value from above string + + Args: + option_value: key value option/value pair read from database + """ + + if option_value is not None: + option_str = option_value.split(',') + fs_rv_options = [] + for fs_rv_option in option_str: + k, v = fs_rv_option.split('=', 1) + fs_rv_options.append({'fsrvoption': k, 'fsrvvalue': v}) + return fs_rv_options + + @check_precondition + def properties(self, gid, sid, did, fid, fsid): + """ + This function will show the properties of the selected foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fsrvoptions'] is not None: + res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions']) + + sql = render_template("/".join([self.template_path, 'acl.sql']), fsid=fsid) + status, fs_rv_acl_res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=fs_rv_acl_res) + + for row in fs_rv_acl_res['rows']: + privilege = parse_priv_from_db(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(privilege) + else: + res['rows'][0][row['deftype']] = [privilege] + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, fid): + """ + This function will create the foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + """ + + required_args = [ + 'name' + ] + + data = request.form if request.form else json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + try: + if 'fsrvacl' in data: + data['fsrvacl'] = parse_priv_to_db(data['fsrvacl'], ['U']) + + sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn) + status, res1 = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res1) + + fdw_data = res1['rows'][0] + + new_list = [] + + if 'fsrvoptions' in data: + for item in data['fsrvoptions']: + new_dict = {} + if item['fsrvoption']: + if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '': + new_dict.update(item); + else: + new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''}) + + new_list.append(new_dict) + + data['fsrvoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data, + conn=self.conn) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + sql = render_template("/".join([self.template_path, 'properties.sql']), data=data, fdwdata=fdw_data, + conn=self.conn) + status, r_set = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=r_set) + + return jsonify( + node=self.blueprint.generate_browser_node( + r_set['rows'][0]['fsrvid'], + fid, + r_set['rows'][0]['name'], + icon="icon-foreign_server" + ) + ) + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, fid, fsid): + """ + This function will update the data for the selected foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + """ + + data = request.form if request.form else json.loads(request.data.decode()) + sql = self.get_sql(gid, sid, data, did, fid, fsid) + try: + if sql and sql.strip('\n') and sql.strip(' '): + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Foreign server updated", + data={ + 'id': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, fid, fsid): + """ + This function will delete the selected foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + # Get name of foreign data wrapper from fid + sql = render_template("/".join([self.template_path, 'delete.sql']), fsid=fsid, conn=self.conn) + status, name = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=name) + # drop foreign server + sql = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade, + conn=self.conn) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Foreign Server dropped"), + data={ + 'id': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid, + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, fid, fsid=None): + """ + This function is used to return modified SQL for the selected foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + """ + + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + sql = self.get_sql(gid, sid, data, did, fid, fsid) + if sql and sql.strip('\n') and sql.strip(' '): + return make_json_response( + data=sql, + status=200 + ) + else: + return make_json_response( + data='-- Modified SQL --', + status=200 + ) + + def get_sql(self, gid, sid, data, did, fid, fsid=None): + """ + This function will generate sql from model data. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + data: Contains the data of the selected foreign server node + fid: foreign data wrapper ID + fsid: foreign server ID + """ + + required_args = [ + 'name' + ] + try: + if fsid is not None: + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fsrvoptions'] is not None: + res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions']) + + for key in ['fsrvacl']: + if key in data and data[key] is not None: + if 'added' in data[key]: + data[key]['added'] = parse_priv_to_db(data[key]['added'], ['U']) + if 'changed' in data[key]: + data[key]['changed'] = parse_priv_to_db(data[key]['changed'], ['U']) + if 'deleted' in data[key]: + data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], ['U']) + + old_data = res['rows'][0] + for arg in required_args: + if arg not in data: + data[arg] = old_data[arg] + + new_list_add = [] + new_list_change = [] + + # Allow user to set the blank value in fsrvvalue field in option model + if 'fsrvoptions' in data and 'added' in data['fsrvoptions']: + for item in data['fsrvoptions']['added']: + new_dict_add = {} + if item['fsrvoption']: + if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '': + new_dict_add.update(item); + else: + new_dict_add.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''}) + + new_list_add.append(new_dict_add) + + data['fsrvoptions']['added'] = new_list_add + + # Allow user to set the blank value in fsrvvalue field in option model + if 'fsrvoptions' in data and 'changed' in data['fsrvoptions']: + for item in data['fsrvoptions']['changed']: + new_dict_change = {} + if item['fsrvoption']: + if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '': + new_dict_change.update(item); + else: + new_dict_change.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''}) + + new_list_change.append(new_dict_change) + + data['fsrvoptions']['changed'] = new_list_change + + sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data, + conn=self.conn) + else: + sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + fdw_data = res['rows'][0] + + for key in ['fsrvacl']: + if key in data and data[key] is not None: + data[key] = parse_priv_to_db(data[key], ['U']) + + new_list = [] + + if 'fsrvoptions' in data: + for item in data['fsrvoptions']: + new_dict = {} + if item['fsrvoption']: + if 'fsrvvalue' in item and item['fsrvvalue'] and item['fsrvvalue'] != '': + new_dict.update(item); + else: + new_dict.update({'fsrvoption': item['fsrvoption'], 'fsrvvalue': ''}) + + new_list.append(new_dict) + + data['fsrvoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data, + conn=self.conn) + sql += "\n" + return sql + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, fid, fsid): + """ + This function will generate sql to show it in sql pane for the selected foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['fsrvoptions'] is not None: + res['rows'][0]['fsrvoptions'] = self.tokenizeOptions(res['rows'][0]['fsrvoptions']) + + sql = render_template("/".join([self.template_path, 'acl.sql']), fsid=fsid) + status, fs_rv_acl_res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=fs_rv_acl_res) + + for row in fs_rv_acl_res['rows']: + privilege = parse_priv_from_db(row) + if row['deftype'] in res['rows'][0]: + res['rows'][0][row['deftype']].append(privilege) + else: + res['rows'][0][row['deftype']] = [privilege] + + # To format privileges + if 'fsrvacl' in res['rows'][0]: + res['rows'][0]['fsrvacl'] = parse_priv_to_db(res['rows'][0]['fsrvacl'], ['U']) + + sql = render_template("/".join([self.template_path, 'properties.sql']), fdwid=fid, conn=self.conn) + status, res1 = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res1) + + fdw_data = res1['rows'][0] + + sql = '' + sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], fdwdata=fdw_data, + conn=self.conn) + sql += "\n" + + sql_header = """-- Foreign Server: {0} + +-- DROP SERVER {0} + +""".format(res['rows'][0]['name']) + + sql = sql_header + sql + + return ajax_response(response=sql) + + @check_precondition + def dependents(self, gid, sid, did, fid, fsid): + """ + This function get the dependents and return ajax response + for the foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: Foreign server ID + """ + dependents_result = self.get_dependents(self.conn, fsid) + + # Fetching dependents of foreign servers + query = render_template("/".join([self.template_path, 'dependents.sql']), fsid=fsid) + status, result = self.conn.execute_dict(query) + if not status: + internal_server_error(errormsg=result) + + for row in result['rows']: + dependents_result.append({'type': 'user_mapping', 'name': row['name'], 'field': 'normal' if (row['deptype'] == 'n') else ''}) + + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, fid, fsid): + """ + This function get the dependencies and return ajax response + for the foreign server node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign Data Wrapper ID + fsid: Foreign server ID + """ + dependencies_result = self.get_dependencies(self.conn, fsid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +ForeignServerView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/img/coll-foreign_server.png b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/static/img/coll-foreign_server.png new file mode 100644 index 0000000000000000000000000000000000000000..c1071768f4d2ed9797585b8725bfce1d3dbfafab GIT binary patch literal 476 zcmV<20VDp2P)Px#u~1A@MSGN_gKT`3fsg9n=i=4g*~!(=z01b2z`v%siE@IOhLgCcv+3XE*vZt- zy~@9+xSfQS+tJwS-{#`g-Os+tu$QUO#mn;X^7;7r_3`rh`TFtg@8sLx@b2&S_4eS? z*Tk!}T%@*)tGMs(?)UZf#H+MhptB#^$78X8t@QEo;ndi}s!aedF2ZD=|Nk^wNyodpXOfdtRf^G6C5vUfS~u;c z^sXQ3?XKA$ItAuUtf+&cek(eY9^AAkZ!~tFaSanc< zFu{AMgHmU4B5npOxx+;-vxA42rQ)LK!~moV1V?2hJ2;zWU_Px#&rnQMMX->YgKT`3fsg9n=i=4g*~!(=z01b2z`v%siE@IOhLgCcv+3XE*vZt- zy~@9+xSfQS+tJwS-{#`g-Os+tu$QUO#mn;X^7;7r_3`rh`TFtg@8sLx@b2&S_4eS? z*Tk!}w3VU6t+wy(?)UZf#H+Njkewf6P(;ndi~uDI{+@58FIvXGs`t+&U<$Kcf1 zqKuTamZPDLmd(t}k#&HymZ8I~wydJ3@b2)su(W%Wq)V8njH|fe)Y+kolv<*+AKJ%K zq_by(l3S&=Pb(w<00001bW%=J06^y0W&i*H0b)x>L;#2d9Y_EG010qNS#tmY3ljhU z3ljkVnw%H_000McNliru+XEdH1{&ZijJW^+0HaAnK~xyiMUK4=!Y~Yk8z{7t76^Ya z5NM?^ATeagkRWBT{Ql2Gs~`$KoE z>eI9&&I&GYz3D%=Kl= 90100: + self.template_path = 'user_mappings/sql/9.1_plus' + + return f(*args, **kwargs) + return wrap + + @check_precondition + def list(self, gid, sid, did, fid, fsid): + """ + This function is used to list all the user mapping nodes within that collection. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + return ajax_response( + response=res['rows'], + status=200 + ) + + @check_precondition + def nodes(self, gid, sid, did, fid, fsid): + """ + This function will used to create all the child node within that collection. + Here it will create all the user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + """ + + res = [] + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, conn=self.conn) + status, r_set = self.conn.execute_2darray(sql) + + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + res.append( + self.blueprint.generate_browser_node( + row['um_oid'], + fsid, + row['name'], + icon="icon-user_mapping" + )) + + return make_json_response( + data=res, + status=200 + ) + + def tokenizeOptions(self, option_value): + """ + This function will tokenize the string stored in database + e.g. database store the value as below + key1=value1, key2=value2, key3=value3, .... + This function will extract key and value from above string + + Args: + option_value: key value option/value pair read from database + """ + + if option_value is not None: + option_str = option_value.split(',') + um_options = [] + for um_option in option_str: + k, v = um_option.split('=', 1) + um_options.append({'umoption': k,'umvalue': v}) + return um_options + + @check_precondition + def properties(self, gid, sid, did, fid, fsid, umid): + """ + This function will show the properties of the selected user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + umid: User mapping ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['name'] == 'public': + res['rows'][0]['name'] = 'PUBLIC' + + if res['rows'][0]['umoptions'] is not None: + res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions']) + + return ajax_response( + response=res['rows'][0], + status=200 + ) + + @check_precondition + def create(self, gid, sid, did, fid, fsid): + """ + This function will create the user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + """ + + required_args = [ + 'name' + ] + + data = request.form if request.form else json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + try: + sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn) + status, res1 = self.conn.execute_dict(sql) + + if not status: + return internal_server_error(errormsg=res1) + + fdw_data = res1['rows'][0] + + new_list = [] + + if 'umoptions' in data: + for item in data['umoptions']: + new_dict = {} + if item['umoption']: + if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '': + new_dict.update(item); + else: + new_dict.update({'umoption': item['umoption'], 'umvalue': ''}) + + new_list.append(new_dict) + + data['umoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data, + conn=self.conn) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + sql = render_template("/".join([self.template_path, 'properties.sql']), fsid=fsid, data=data, + conn=self.conn) + status, r_set = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=r_set) + + for row in r_set['rows']: + return jsonify( + node=self.blueprint.generate_browser_node( + row['um_oid'], + fsid, + row['name'], + icon='icon-user_mapping' + ) + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def update(self, gid, sid, did, fid, fsid, umid): + """ + This function will update the data for the selected user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + umid: User mapping ID + """ + + data = request.form if request.form else json.loads(request.data.decode()) + sql = self.get_sql(gid, sid, data, did, fid, fsid, umid) + try: + if sql and sql.strip('\n') and sql.strip(' '): + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="User Mapping updated", + data={ + 'id': umid, + 'fsid': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': umid, + 'fsid': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, fid, fsid, umid): + """ + This function will delete the selected user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + umid: User mapping ID + """ + + if self.cmd == 'delete': + # This is a cascade operation + cascade = True + else: + cascade = False + + try: + # Get name of foreign server from fsid + sql = render_template("/".join([self.template_path, 'delete.sql']), fsid=fsid, conn=self.conn) + status, name = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=name) + + sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + data = res['rows'][0] + + # drop user mapping + sql = render_template("/".join([self.template_path, 'delete.sql']), data=data, name=name, cascade=cascade, + conn=self.conn) + status, res = self.conn.execute_scalar(sql) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("User Mapping dropped"), + data={ + 'id': umid, + 'fsid': fsid, + 'fid': fid, + 'did': did, + 'sid': sid, + 'gid': gid, + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, fid, fsid, umid=None): + """ + This function is used to return modified SQL for the selected user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: foreign server ID + umid: User mapping ID + """ + + data = {} + for k, v in request.args.items(): + try: + data[k] = json.loads(v) + except ValueError: + data[k] = v + + sql = self.get_sql(gid, sid, data, did, fid, fsid, umid) + if sql and sql.strip('\n') and sql.strip(' '): + return make_json_response( + data=sql, + status=200 + ) + else: + return make_json_response( + data='-- Modified SQL --', + status=200 + ) + + def get_sql(self, gid, sid, data, did, fid, fsid, umid=None): + """ + This function will generate sql from model data. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + data: Contains the data of the selected user mapping node + fid: foreign data wrapper ID + fsid: foreign server ID + umid: User mapping ID + """ + + required_args = [ + 'name' + ] + try: + if umid is not None: + sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['umoptions'] is not None: + res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions']) + + old_data = res['rows'][0] + + sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn) + status, res1 = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res1) + + fdw_data = res1['rows'][0] + + for arg in required_args: + if arg not in data: + data[arg] = old_data[arg] + + new_list_add = [] + new_list_change = [] + + # Allow user to set the blank value in fdwvalue field in option model + if 'umoptions' in data and 'added' in data['umoptions']: + for item in data['umoptions']['added']: + new_dict_add = {} + if item['umoption']: + if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '': + new_dict_add.update(item); + else: + new_dict_add.update({'umoption': item['umoption'], 'umvalue': ''}) + + new_list_add.append(new_dict_add) + + data['umoptions']['added'] = new_list_add + + # Allow user to set the blank value in fdwvalue field in option model + if 'umoptions' in data and 'changed' in data['umoptions']: + for item in data['umoptions']['changed']: + new_dict_change = {} + if item['umoption']: + if 'umvalue' in item and item['umvalue'] and item['umvalue'] != '': + new_dict_change.update(item); + else: + new_dict_change.update({'umoption': item['umoption'], 'umvalue': ''}) + + new_list_change.append(new_dict_change) + + data['umoptions']['changed'] = new_list_change + + sql = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data, + fdwdata=fdw_data, conn=self.conn) + else: + sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + fdw_data = res['rows'][0] + + new_list = [] + + if 'umoptions' in data: + for item in data['umoptions']: + new_dict = {} + if item['umoption']: + if 'umvalue' in item and item['umvalue'] \ + and item['umvalue'] != '': + new_dict.update(item); + else: + new_dict.update( + {'umoption': item['umoption'], + 'umvalue': ''} + ) + new_list.append(new_dict) + + data['umoptions'] = new_list + + sql = render_template("/".join([self.template_path, 'create.sql']), data=data, fdwdata=fdw_data, + conn=self.conn) + sql += "\n" + return sql + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def sql(self, gid, sid, did, fid, fsid, umid): + """ + This function will generate sql to show it in sql pane for the selected user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign data wrapper ID + fsid: Foreign server ID + umid: User mapping ID + """ + + sql = render_template("/".join([self.template_path, 'properties.sql']), umid=umid, conn=self.conn) + status, res = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res) + + if res['rows'][0]['umoptions'] is not None: + res['rows'][0]['umoptions'] = self.tokenizeOptions(res['rows'][0]['umoptions']) + + sql = render_template("/".join([self.template_path, 'properties.sql']), fserid=fsid, conn=self.conn) + status, res1 = self.conn.execute_dict(sql) + if not status: + return internal_server_error(errormsg=res1) + + fdw_data = res1['rows'][0] + + sql = '' + sql = render_template("/".join([self.template_path, 'create.sql']), data=res['rows'][0], fdwdata=fdw_data, + conn=self.conn) + sql += "\n" + + sql_header = """-- User Mapping : {0} + +-- DROP USER MAPPING FOR {0} SERVER {1} + +""".format(res['rows'][0]['name'], fdw_data['name']) + + sql = sql_header + sql + + return ajax_response(response=sql) + + @check_precondition + def dependents(self, gid, sid, did, fid, fsid, umid): + """ + This function get the dependents and return ajax response + for the user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: foreign data wrapper ID + fsid: Foreign server ID + umid: user mapping ID + """ + + dependents_result = self.get_dependents(self.conn, umid) + return ajax_response( + response=dependents_result, + status=200 + ) + + @check_precondition + def dependencies(self, gid, sid, did, fid, fsid, umid): + """ + This function get the dependencies and return ajax response + for the user mapping node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + fid: Foreign Data Wrapper ID + fsid: Foreign server ID + umid: user mapping ID + """ + dependencies_result = self.get_dependencies(self.conn, umid) + return ajax_response( + response=dependencies_result, + status=200 + ) + +UserMappingView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/img/coll-user_mapping.png b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/static/img/coll-user_mapping.png new file mode 100644 index 0000000000000000000000000000000000000000..0e68c2b12866ad4b480a41a7d1032a1df34a1f45 GIT binary patch literal 557 zcmV+|0@D47P)Px#}!MO6|+snbbl8uXccXagT+W6|=`0C>K z>g4n0;*yPwlarIZw5<2)=J)F8%E7^KaB$w!%J=H(@#N;Vwzivm&s<-|==9#+-fnVp$mQ|WnV-Pr^Qfq(mZGJ^ zc=v006Z~ zL_t&-(?yQg4#F@D08{qfqYSg$l)WM-NCgS7nEC(z!9fM??M}86Aq-Z?3X4CHXe^#c zM*bm{&SZ1>!q1B(_Hw0aeM7Bo+-`)vq1non+Yrz(rQ54Oe}Im3ha)gyJeh*=*<5MO zzFe&ppfjIov_0go*?K#0Px#%uq~JMYgW7nVFetYHGc`y?%Okx2~t;*v{nG(6+9setLP=%)|KV-}vg`_v+&J z>g3nV$$olz_v+^N>gcwvuJ`Kdg?m#+xP13*38g-dVE`4Tv}XQYHe+%w70ay z$%l}WWM^n&XJ~_tlc>MSnXR{FYi(LuTBESD)avlj>G00y@XO}$$K>(Ej zlBl#;T3dsPjJn?Px83r0g^ZMxl%=z`wA}KLs<>`$ZokvnvfJ{b#@kp}Sj^?_vD)&m z*zi?VRXL11s{jB10d!JMQvg8b*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV z0GgZ_00007bV*G`2ipV@5d#koeph1v005>*L_t&-(?!kO4#FS|fMH-~Pn%P*iBT^d z+q^Q1rW!`z{ol#5$n4$!g%-x9;X-ispD0e!EH9!N;>B{c-jr2np7`F@yRaqFsrLsF zAe&kq1Mu88qSg45qO4H^7yxJ{I91dZkpO(3FIV(!!44j7jY3O307x(d57nZQh}s&E z1kYF7p#nhkhc#pGPuJ6P+M%lheLR}}-sr(Mk}~E-=N!)S1$}xN(oSpy>i_@%07*qo IM6N<$g7m}xI{*Lx literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/js/user_mappings.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/js/user_mappings.js new file mode 100644 index 000000000..74d4b4f66 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/js/user_mappings.js @@ -0,0 +1,157 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Extend the browser's node model class to create a Options model + var OptionsModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + umoption: undefined, + umvalue: undefined + }, + + // Defining schema for the Options model + schema: [ + {id: 'umoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true}, + {id: 'umvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true}, + ], + + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the browser for the respective control. + */ + validate: function() { + // Validation for the option value + if (_.isUndefined(this.get('umoption')) || + _.isNull(this.get('umoption')) || + String(this.get('umoption')).replace(/^\s+|\s+$/g, '') == '') { + var msg = 'Please enter an option name'; + this.errorModel.set('umoption', msg); + return msg; + } else { + this.errorModel.unset('umoption'); + } + return null; + } + }); + + // Extend the browser's collection class for user mapping collection + if (!pgBrowser.Nodes['coll-user_mapping']) { + var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-user_mapping'] = + pgAdmin.Browser.Collection.extend({ + node: 'user_mapping', + label: '{{ _('User Mappings') }}', + type: 'coll-user_mapping', + columns: ['name'] + }); + }; + + // Extend the browser's node class for user mapping node + if (!pgBrowser.Nodes['user_mapping']) { + pgAdmin.Browser.Nodes['user_mapping'] = pgAdmin.Browser.Node.extend({ + parent_type: 'foreign_server', + type: 'user_mapping', + label: '{{ _('User Mapping') }}', + hasSQL: true, + hasDepends: true, + canDrop: true, + canDropCascade: true, + Init: function() { + + // Avoid multiple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + /* Create foreign server context menu at database, + * user mapping collections and user mapping node + */ + pgBrowser.add_menus([{ + name: 'create_user_mapping_on_coll', node: 'coll-user_mapping', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('User Mapping...') }}', + icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'} + },{ + name: 'create_user_mapping', node: 'user_mapping', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('User Mapping...') }}', + icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'} + },{ + name: 'create_user_mapping', node: 'foreign_server', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('User Mapping...') }}', + icon: 'wcTabIcon icon-user_mapping', data: {action: 'create'} + } + ]); + }, + + // Defining model for user mapping node + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + um_options: [] + }, + + // Default values! + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + + if (isNew) { + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + + this.set({'name': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + + // Defining schema for the user mapping node + schema: [{ + id: 'name', label:'{{ _('User') }}', type: 'text', + control: Backform.NodeListByNameControl, node: 'role', + mode: ['edit', 'create', 'properties'], select2: { allowClear: false }, + disabled: function(m) { return !m.isNew(); }, + transform: function(data) { + var self = this; + node = self.field.get('schema_node'); + var res = + Backform.NodeListByNameControl.prototype.defaults.transform.apply( + this, arguments + ); + res.unshift({label: 'CURRENT_USER', value: 'CURRENT_USER', image: 'icon-' + node.type}, + {label: 'PUBLIC', value: 'PUBLIC', image: 'icon-' + node.type}); + return res; + } + },{ + id: 'um_oid', label:'{{ _('OID') }}', cell: 'string', + type: 'text', disabled: true, mode: ['properties'], + },{ + id: 'umoptions', label: 'Options', type: 'collection', group: "Options", + model: OptionsModel, control: 'unique-col-collection', mode: ['create', 'edit'], + canAdd: true, canDelete: true, uniqueCol : ['umoption'] + } + ], + + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the browser for the respective control. + */ + validate: function() { + var name = this.get('name'); + + if (_.isUndefined(name) || _.isNull(name) || + String(name).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Name can not be empty!') }}'; + this.errorModel.set('name', msg); + return msg; + } else { + this.errorModel.unset('name'); + } + return null; + } + }) + }); + + } + + return pgBrowser.Nodes['coll-user_mapping']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/create.sql new file mode 100644 index 000000000..f467ae480 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/create.sql @@ -0,0 +1,13 @@ +{# ============= Create of user mapping for server ============= #} +{% if data and fdwdata %} +CREATE USER MAPPING FOR {% if data.name == "CURRENT_USER" or data.name == "PUBLIC" %}{{ data.name }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} SERVER {{ conn|qtIdent(fdwdata.name) }}{%endif%}{% if data.umoptions %} + +{% set addAlter = "False" %} +{% for variable in data.umoptions %} +{% if variable.umoption and variable.umoption != '' %} +{% if addAlter == "False" %} + OPTIONS ({% set addAlter = "True" %}{% endif %} +{{ conn|qtIdent(variable.umoption) }} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{%else%}){% endif %} +{% endif %} +{% endfor %} +{% endif %}{% if data %};{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/delete.sql new file mode 100644 index 000000000..0f65d6232 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/delete.sql @@ -0,0 +1,9 @@ +{# ============= Get the foreing server name from id ============= #} +{% if fsid %} +SELECT srvname as name FROM pg_foreign_server srv LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw + WHERE srv.oid={{fsid}}::int; +{% endif %} +{# ============= Drop/Delete cascade user mapping ============= #} +{% if name and data %} +DROP USER MAPPING FOR {{ conn|qtIdent(data.name) }} SERVER {{ conn|qtIdent(name) }} +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/properties.sql new file mode 100644 index 000000000..69f0588e5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/properties.sql @@ -0,0 +1,23 @@ +{# ============= Get the properties of user mapping ============= #} +{% if fserid %} +SELECT srv.oid as fsrvid, srvname as name +FROM pg_foreign_server srv + LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass) +WHERE srv.oid = {{fserid}}::int +{% endif %} +{% if fsid or umid or fdwdata or data %} +WITH umapData AS + ( + SELECT u.oid AS um_oid, CASE WHEN u.umuser = 0::oid THEN 'public'::name ELSE a.rolname END AS name, + array_to_string(u.umoptions, ',') AS umoptions FROM pg_user_mapping u + LEFT JOIN pg_authid a ON a.oid = u.umuser {% if fsid %} WHERE u.umserver = {{fsid}}::int {% endif %} {% if umid %} WHERE u.oid= {{umid}}::int {% endif %} + ) + SELECT * FROM umapData +{% if data %} + WHERE {% if data.name == "CURRENT_USER" %} name = {{data.name}} {% elif data.name == "PUBLIC" %} name = {{data.name.lower()|qtLiteral}} {% else %} name = {{data.name|qtLiteral}} {% endif %} +{% endif %} +{% if fdwdata %} + WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text +{% endif %} + ORDER BY 2; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/update.sql new file mode 100644 index 000000000..021ba2460 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/foreign_servers/user_mapping/templates/user_mappings/sql/9.1_plus/update.sql @@ -0,0 +1,37 @@ +{# ============= Update user mapping options and values ============= #} +{% if data.umoptions and data.umoptions.deleted and fdwdata %} +{% set addAlter = "False" %} +{% for variable in data.umoptions.deleted %} +{% if variable.umoption and variable.umoption != '' %} +{% if addAlter == "False" %} +ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }} + OPTIONS ({% set addAlter = "True" %}{%endif%} +DROP {{conn|qtIdent(variable.umoption)}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{% endfor %} + +{% endif %} +{% if o_data.name and data.umoptions and data.umoptions.added and fdwdata %} +{% set addAlter = "False" %} +{% for variable in data.umoptions.added %} +{% if variable.umoption and variable.umoption != '' %} +{% if addAlter == "False" %} +ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }} + OPTIONS ({% set addAlter = "True" %}{% endif %} +ADD {{ conn|qtIdent(variable.umoption) }} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{% endfor %} + +{% endif %} +{% if data.umoptions and data.umoptions.changed and fdwdata %} +{% set addAlter = "False" %} +{% for variable in data.umoptions.changed %} +{% if variable.umoption and variable.umoption != '' %} +{% if addAlter == "False" %} +ALTER USER MAPPING FOR {{ conn|qtIdent(o_data.name) }} SERVER {{ conn|qtIdent(fdwdata.name) }} + OPTIONS ({% set addAlter = "True" %}{%endif%} +SET {{conn|qtIdent(variable.umoption)}} {{variable.umvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{%endif%} +{% endfor %} + +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/coll-foreign_data_wrapper.png b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/coll-foreign_data_wrapper.png new file mode 100644 index 0000000000000000000000000000000000000000..6b1c8543c69448b928acd21032fe50b21f032dd1 GIT binary patch literal 444 zcmV;t0YmPx#u24)=MV5h&gKT{2-{<1h-r333(7nsXvB1BkxruUunTC_NsI%$c<=Dy8&%MgO zsJNYkmfO+T>fh$#)!om&%CMKI(8bI0@$&ij`StPg`uY0t?eFB<-|+75_VxDQ)7QkS zwOpjOjH|fs?(X;X_Qb2STcEQa+Q(zD!I*rNXN8+jm#y^i^5N9j!>Y4erMBol`R4EO&)eYP)Y-$Uv|6IH_Vx9cr0AId0004WQchCj1x*-2O5foEZm^ z`4qFE7i6Pp7#W~t4s-!1H?RTG&^sRBo~lya!!orz>gCg2eO literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/foreign_data_wrapper.png b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/static/img/foreign_data_wrapper.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb967671834e280190bbd5777c3ddc0ab07901d GIT binary patch literal 411 zcmV;M0c8G(P)Px#%uq~JMV5h&gKT{2-{<1h-r333(7nsXvB1BkxruUunTC_NsI%$c<=Dy8&%MgO zsJNYkmfO+T>fh$#)!om&%CMKI(8bI0@$&ij`StPg`uY0t?eFB<-|+75_VxDQ)7QkS zwX~I?#I3gP?(X;X_Qb2SvXGv`t+n>`_2Jam#ICsS?(f5@v$Bw##I3i-#>e2)*rJS- zwU(oyj+V{L%#n3~w3ea6t+uS9r||CZy0El+l%z|TsEn();ndlojFeiUv>)2XQlztI zgOXdNwqq#b)Bpeg0d!JMQvg8b*k%9#00Cl4M??UK1szBL000SaNLh0L01FcU01FcV z0GgZ_00007bV*G`2ipT36%jB#6T$fa0028lL_t&-)4k8p4FEs{1JD|y`!6c4MJ#YW zi1$$e#3`VFu7Q~S0WCEI5;Xx8$OU%L03E#0hyTHSKLJ-#4RL;k+`<3=002ovPDHLk FV1jzK&rSdU literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/js/foreign_data_wrappers.js b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/js/foreign_data_wrappers.js new file mode 100644 index 000000000..c56a2f8cb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/js/foreign_data_wrappers.js @@ -0,0 +1,180 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', + 'alertify', 'pgadmin.browser.collection', + 'pgadmin.browser.server.privilege'], +function($, _, S, pgAdmin, pgBrowser, alertify) { + + // Extend the browser's node model class to create a Options model + var OptionsModel = pgAdmin.Browser.Node.Model.extend({ + defaults: { + fdwoption: undefined, + fdwvalue: undefined + }, + + // Defining schema for the Options model + schema: [ + {id: 'fdwoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true}, + {id: 'fdwvalue', label:'Value', type: 'text', cellHeaderClasses:'width_percent_50', group:null, editable: true}, + ], + + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the browser for the respective control. + */ + validate: function() { + // Validation for the option name + if (_.isUndefined(this.get('fdwoption')) || + _.isNull(this.get('fdwoption')) || + String(this.get('fdwoption')).replace(/^\s+|\s+$/g, '') == '') { + var msg = 'Please enter an option name'; + this.errorModel.set('fdwoption', msg); + return msg; + } else { + this.errorModel.unset('fdwoption'); + } + return null; + } + }); + + // Extend the browser's collection class for foreign data wrapper collection + if (!pgBrowser.Nodes['coll-foreign_data_wrapper']) { + var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-foreign_data_wrapper'] = + pgAdmin.Browser.Collection.extend({ + node: 'foreign_data_wrapper', + label: '{{ _('Foreign Data Wrappers') }}', + type: 'coll-foreign_data_wrapper', + columns: ['name','fdwowner','description'] + }); + }; + + // Extend the browser's node class for foreign data wrapper node + if (!pgBrowser.Nodes['foreign_data_wrapper']) { + pgAdmin.Browser.Nodes['foreign_data_wrapper'] = pgAdmin.Browser.Node.extend({ + parent_type: 'database', + type: 'foreign_data_wrapper', + label: '{{ _('Foreign Data Wrapper') }}', + hasSQL: true, + hasDepends: true, + canDrop: true, + canDropCascade: true, + Init: function() { + + // Avoid multiple registration of menus + if (this.initialized) + return; + + this.initialized = true; + + /* Create foreign data wrapper context menu at database, + * foreign data wrapper collections and foreign data wrapper node + */ + pgBrowser.add_menus([{ + name: 'create_foreign_data_wrapper_on_coll', node: 'coll-foreign_data_wrapper', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}', + icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'} + },{ + name: 'create_foreign_data_wrapper', node: 'foreign_data_wrapper', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}', + icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'} + },{ + name: 'create_foreign_data_wrapper', node: 'database', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Foreign Data Wrapper...') }}', + icon: 'wcTabIcon icon-foreign_data_wrapper', data: {action: 'create'} + } + ]); + }, + + // Defining model for foreign data wrapper node + model: pgAdmin.Browser.Node.Model.extend({ + defaults: { + name: undefined, + fdwowner: undefined, + comment: undefined, + fdwvalue: undefined, + fdwhan: undefined, + fdwoption: undefined, + fdwacl: [] + }, + + // Default values! + initialize: function(attrs, args) { + var isNew = (_.size(attrs) === 0); + + if (isNew) { + var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user; + + this.set({'fdwowner': userInfo.name}, {silent: true}); + } + pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments); + }, + + // Defining schema for the foreign data wrapper node + schema: [{ + id: 'name', label: '{{ _('Name') }}', cell: 'string', + type: 'text', disabled: function(m) { + + // name field will be disabled only if edit mode and server version is below 9.2 + if (this.mode == 'edit' && this.node_info.server.version < 90200) { + return true; + } + else + return false; + } + },{ + id: 'fdwoid', label:'{{ _('OID') }}', cell: 'string', + type: 'text', disabled: true, mode: ['properties'] + },{ + id: 'fdwowner', label:'{{ _('Owner') }}', type: 'text', + control: Backform.NodeListByNameControl, node: 'role', + mode: ['edit', 'create', 'properties'], select2: { allowClear: false } + },{ + id: 'fdwhan', label:'{{ _('Handler') }}', type: 'text', control: 'node-ajax-options', + group: '{{ _('Definition') }}', mode: ['edit', 'create', 'properties'], url:'get_handlers' + },{ + id: 'description', label:'{{ _('Comment') }}', cell: 'string', + type: 'multiline' + },{ + id: 'fdwoptions', label: 'Options', type: 'collection', group: '{{ _('Options') }}', + model: OptionsModel, control: 'unique-col-collection', mode: ['edit', 'create'], + canAdd: true, canDelete: true, uniqueCol : ['fdwoption'], + columns: ['fdwoption','fdwvalue'] + },{ + id: 'fdwvalue', label:'{{ _('Validator') }}', type: 'text', control: 'node-ajax-options', + group: '{{ _('Definition') }}', mode: ['edit', 'create', 'properties'], url:'get_validators' + },{ + id: 'fdwacl', label: 'Privileges', type: 'collection', group: '{{ _('Security') }}', + model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}), control: 'unique-col-collection', + mode: ['edit', 'create'], canAdd: true, canDelete: true, uniqueCol : ['grantee'] + },{ + id: 'acl', label: '{{ _('Privileges') }}', type: 'text', + group: '{{ _('Security') }}', mode: ['properties'], disabled: true + } + ], + + /* validate function is used to validate the input given by + * the user. In case of error, message will be displayed on + * the browser for the respective control. + */ + validate: function() { + var name = this.get('name'); + + if (_.isUndefined(name) || _.isNull(name) || + String(name).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Name can not be empty!') }}'; + this.errorModel.set('name', msg); + return msg; + } else { + this.errorModel.unset('name'); + } + return null; + } + }) + }); + + } + + return pgBrowser.Nodes['coll-foreign_data_wrapper']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/acl.sql new file mode 100644 index 000000000..61da46f2b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/acl.sql @@ -0,0 +1,27 @@ +SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT fdwacl FROM pg_foreign_data_wrapper fdw + LEFT OUTER JOIN pg_shdescription descr ON ( + fdw.oid=descr.objoid AND descr.classoid='pg_foreign_data_wrapper'::regclass) +{% if fid %} + WHERE fdw.oid = {{ fid|qtLiteral }}::OID +{% endif %} + ) acl, + (SELECT (d).grantee AS grantee, (d).grantor AS grantor, (d).is_grantable AS is_grantable, + (d).privilege_type AS privilege_type + FROM (SELECT aclexplode(fdwacl) as d FROM pg_foreign_data_wrapper fdw1 + {% if fid %} + WHERE fdw1.oid = {{ fid|qtLiteral }}::OID ) a + {% endif %} + ) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/create.sql new file mode 100644 index 000000000..996fd7110 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/create.sql @@ -0,0 +1,38 @@ +{# ============= Create foreign data wrapper ============= #} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data.name %} +CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %} + + VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %} + + HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %} + +{% set addAlter = "False" %} +{% for variable in data.fdwoptions %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} + OPTIONS ({% set addAlter = "True" %}{% endif %} +{{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %} +{% endif %} +{% endfor %}{% endif %}{%if data %};{%endif%} + +{# ============= Set the owner for foreign data wrapper ============= #} +{% if data.fdwowner %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.fdwowner) }}; + +{% endif %} +{# ============= Comment on of foreign data wrapper object ============= #} +{% if data.description %} + +COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ============= Create ACL for foreign data wrapper ============= #} +{% if data.fdwacl %} +{% for priv in data.fdwacl %} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} +{% endfor %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/delete.sql new file mode 100644 index 000000000..5677c99af --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/delete.sql @@ -0,0 +1,8 @@ +{# ============= Get foreign data wrapper from fid ============= #} +{% if fid %} +SELECT fdwname as name from pg_foreign_data_wrapper WHERE oid={{fid}}::int; +{% endif %} +{# ============= Delete/Drop cascade foreign data wrapper ============= #} +{% if name %} +DROP FOREIGN DATA WRAPPER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/handlers.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/handlers.sql new file mode 100644 index 000000000..50d27c6d4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/handlers.sql @@ -0,0 +1,2 @@ +{# ============= Get the handlers of foreign data wrapper ============= #} +SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/properties.sql new file mode 100644 index 000000000..af28d13f6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/properties.sql @@ -0,0 +1,14 @@ +{# ============= Get all the properties of foreign data wrapper ============= #} +SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description, + array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl +FROM pg_foreign_data_wrapper fdw +LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler +LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator +LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass) +{% if fid %} +WHERE fdw.oid={{fid}}::int +{% endif %} +{% if fname %} +WHERE fdw.fdwname={{ fname|qtLiteral }}::text +{% endif %} +ORDER BY fdwname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/update.sql new file mode 100644 index 000000000..990deb23d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/update.sql @@ -0,0 +1,99 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{# ============= Update foreign data wrapper name ============= #} +{% if data.name != o_data.name %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{# ============= Update foreign data wrapper owner ============= #} +{% if data.fdwowner and data.fdwowner != o_data.fdwowner %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.fdwowner) }}; + +{% endif %} +{# ============= Update foreign data wrapper validator ============= #} +{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}; + +{% endif %} +{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + NO VALIDATOR; + +{% endif %} +{# ============= Update foreign data wrapper handler ============= #} +{% if data.fdwhan and data.fdwhan != o_data.fdwhan %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + HANDLER {{ conn|qtIdent(data.fdwhan) }}; + +{% endif %} +{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + NO HANDLER; + +{% endif %} +{# ============= Update foreign data wrapper comments ============= #} +{% if data.description and data.description != o_data.description %} +COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ============= Update foreign data wrapper options and values ============= #} +{% if data.fdwoptions and data.fdwoptions.deleted %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.deleted %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{%endif%} +DROP {{ conn|qtIdent(variable.fdwoption) }}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{% endfor %} + + +{% endif %} +{% if data.fdwoptions and data.fdwoptions.added %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.added %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{% endif %} +ADD {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{%endfor%} + + +{% endif %} +{% if data.fdwoptions and data.fdwoptions.changed %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.changed %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{% endif %} +SET {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{%endfor%} + + +{% endif %} +{# Change the privileges #} +{% if data.fdwacl %} +{% if 'deleted' in data.fdwacl %} +{% for priv in data.fdwacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }}{% endfor %} +{% endif %} +{% if 'changed' in data.fdwacl %} +{% for priv in data.fdwacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% if 'added' in data.fdwacl %} +{% for priv in data.fdwacl.added %} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/validators.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/validators.sql new file mode 100644 index 000000000..6f3d0193c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.1_plus/validators.sql @@ -0,0 +1,2 @@ +{# ============= Get the validators of foreign data wrapper ============= #} +SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26; diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/acl.sql new file mode 100644 index 000000000..ea27ed186 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/acl.sql @@ -0,0 +1,21 @@ +SELECT 'fdwacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable +FROM + (SELECT + d.grantee, d.grantor, d.is_grantable, + CASE d.privilege_type + WHEN 'USAGE' THEN 'U' + ELSE 'UNKNOWN' + END AS privilege_type + FROM + (SELECT fdwacl FROM pg_foreign_data_wrapper fdw + LEFT OUTER JOIN pg_shdescription descr ON ( + fdw.oid=descr.objoid AND descr.classoid='pg_foreign_data_wrapper'::regclass) +{% if fid %} + WHERE fdw.oid = {{ fid|qtLiteral }}::OID +{% endif %} + ) acl, + aclexplode(fdwacl) d + ) d + LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid) + LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid) +GROUP BY g.rolname, gt.rolname \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/create.sql new file mode 100644 index 000000000..3d81c67c9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/create.sql @@ -0,0 +1,37 @@ +{# ============= Create foreign data wrapper ============= #} +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data.name %} +CREATE FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }}{% if data.fdwvalue %} + + VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}{%endif%}{% if data.fdwhan %} + + HANDLER {{ conn|qtIdent(data.fdwhan) }}{% endif %}{% if data.fdwoptions %} + +{% set addAlter = "False" %} +{% for variable in data.fdwoptions %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} + OPTIONS ({% set addAlter = "True" %}{% endif %} +{{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %} +{% endif %} +{% endfor %}{% endif %}{%if data %};{%endif%} + +{# ============= Set the owner for foreign data wrapper ============= #} +{% if data.fdwowner %} + +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.fdwowner) }}; + +{% endif %} +{# ============= Comment on of foreign data wrapper object ============= #} +{% if data.description %} +COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ============= Create ACL for foreign data wrapper ============= #} +{% if data.fdwacl %} +{% for priv in data.fdwacl %} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/delete.sql new file mode 100644 index 000000000..5677c99af --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/delete.sql @@ -0,0 +1,8 @@ +{# ============= Get foreign data wrapper from fid ============= #} +{% if fid %} +SELECT fdwname as name from pg_foreign_data_wrapper WHERE oid={{fid}}::int; +{% endif %} +{# ============= Delete/Drop cascade foreign data wrapper ============= #} +{% if name %} +DROP FOREIGN DATA WRAPPER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}; +{% endif %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/handlers.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/handlers.sql new file mode 100644 index 000000000..50d27c6d4 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/handlers.sql @@ -0,0 +1,2 @@ +{# ============= Get the handlers of foreign data wrapper ============= #} +SELECT proname as fdwhan FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE pronargs=0 AND prorettype=3115; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/properties.sql new file mode 100644 index 000000000..daede2ad1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/properties.sql @@ -0,0 +1,14 @@ +{# ============= Get all the properties of foreign data wrapper ============= #} +SELECT fdw.oid as fdwoid, fdwname as name, fdwhandler, fdwvalidator, vh.proname as fdwhan, vp.proname as fdwvalue, description, + array_to_string(fdwoptions, ',') AS fdwoptions, pg_get_userbyid(fdwowner) as fdwowner, array_to_string(fdwacl::text[], ', ') as acl +FROM pg_foreign_data_wrapper fdw + LEFT OUTER JOIN pg_proc vh on vh.oid=fdwhandler + LEFT OUTER JOIN pg_proc vp on vp.oid=fdwvalidator + LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass) +{% if fid %} +WHERE fdw.oid={{fid}}::int +{% endif %} +{% if fname %} +WHERE fdw.fdwname={{ fname|qtLiteral }}::text +{% endif %} +ORDER BY fdwname diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/update.sql new file mode 100644 index 000000000..f97569ada --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/update.sql @@ -0,0 +1,99 @@ +{% import 'macros/privilege.macros' as PRIVILEGE %} +{% if data %} +{# ============= Update foreign data wrapper name ============= #} +{% if data.name != o_data.name %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(o_data.name) }} + RENAME TO {{ conn|qtIdent(data.name) }}; + +{% endif %} +{# ============= Update foreign data wrapper owner ============= #} +{% if data.fdwowner and data.fdwowner != o_data.fdwowner %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OWNER TO {{ conn|qtIdent(data.fdwowner) }}; + +{% endif %} +{# ============= Update foreign data wrapper validator ============= #} +{% if data.fdwvalue and data.fdwvalue != o_data.fdwvalue %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + VALIDATOR {{ conn|qtIdent(data.fdwvalue) }}; + +{% endif %} +{% if data.fdwvalue == '' and data.fdwvalue != o_data.fdwvalue %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + NO VALIDATOR; + +{% endif %} +{# ============= Update foreign data wrapper handler ============= #} +{% if data.fdwhan and data.fdwhan != o_data.fdwhan %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + HANDLER {{ conn|qtIdent(data.fdwhan) }}; + +{% endif %} +{% if data.fdwhan == '' and data.fdwhan != o_data.fdwhan %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + NO HANDLER; + +{% endif %} +{# ============= Update foreign data wrapper comments ============= #} +{% if data.description and data.description != o_data.description %} +COMMENT ON FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + IS {{ data.description|qtLiteral }}; + +{% endif %} +{# ============= Update foreign data wrapper options and values ============= #} +{% if data.fdwoptions and data.fdwoptions.deleted %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.deleted %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{%endif%} +DROP {{ conn|qtIdent(variable.fdwoption) }}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{% endfor %} + + +{% endif %} +{% if data.fdwoptions and data.fdwoptions.added %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.added %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{% endif %} +ADD {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{%endfor%} + + +{% endif %} +{% if data.fdwoptions and data.fdwoptions.changed %} +{% set addAlter = "False" %} +{% for variable in data.fdwoptions.changed %} +{% if variable.fdwoption and variable.fdwoption != '' %} +{% if addAlter == "False" %} +ALTER FOREIGN DATA WRAPPER {{ conn|qtIdent(data.name) }} + OPTIONS ({% set addAlter = "True" %}{% endif %} +SET {{ conn|qtIdent(variable.fdwoption) }} {{variable.fdwvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %} +{% endif %} +{%endfor%} + + +{% endif %} +{# Change the privileges #} +{% if data.fdwacl %} +{% if 'deleted' in data.fdwacl %} +{% for priv in data.fdwacl.deleted %} +{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }} {% endfor %} +{% endif %} +{% if 'changed' in data.fdwacl %} +{% for priv in data.fdwacl.changed %} +{{ PRIVILEGE.RESETALL(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name) }} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }} {% endfor %} +{% endif %} +{% if 'added' in data.fdwacl %} +{% for priv in data.fdwacl.added %} +{{ PRIVILEGE.APPLY(conn, 'FOREIGN DATA WRAPPER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/validators.sql b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/validators.sql new file mode 100644 index 000000000..6f3d0193c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/foreign_data_wrappers/templates/foreign_data_wrappers/sql/9.3_plus/validators.sql @@ -0,0 +1,2 @@ +{# ============= Get the validators of foreign data wrapper ============= #} +SELECT proname as fdwvalue FROM pg_proc p JOIN pg_namespace nsp ON nsp.oid=pronamespace WHERE proargtypes[0]=1009 AND proargtypes[1]=26; diff --git a/web/pgadmin/browser/server_groups/servers/templates/depends/sql/9.1_plus/dependents.sql b/web/pgadmin/browser/server_groups/servers/templates/depends/sql/9.1_plus/dependents.sql index 614694b1d..1a4484c66 100644 --- a/web/pgadmin/browser/server_groups/servers/templates/depends/sql/9.1_plus/dependents.sql +++ b/web/pgadmin/browser/server_groups/servers/templates/depends/sql/9.1_plus/dependents.sql @@ -8,11 +8,14 @@ SELECT DISTINCT dep.deptype, dep.refclassid, cl.relkind, ad.adbin, ad.adsrc, 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 '' + WHEN ad.oid IS NOT NULL THEN 'A'::text + WHEN fs.oid IS NOT NULL THEN 'F'::text + WHEN fdw.oid IS NOT NULL THEN 'f'::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) + ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname, fs.srvname, fdw.fdwname) END AS refname, COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname FROM pg_depend dep @@ -33,10 +36,12 @@ 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 +LEFT JOIN pg_foreign_server fs ON fs.oid=dep.refobjid +LEFT JOIN pg_foreign_data_wrapper fdw ON fdw.oid=dep.refobjid {{where_clause}} 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')) + ('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace', + 'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper')) ORDER BY refclassid, cl.relkind {% endif %} @@ -57,11 +62,14 @@ SELECT DISTINCT dep.deptype, dep.classid, cl.relkind, ad.adbin, ad.adsrc, 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 '' + WHEN ad.oid IS NOT NULL THEN 'A'::text + WHEN fs.oid IS NOT NULL THEN 'F'::text + WHEN fdw.oid IS NOT NULL THEN 'f'::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) + ELSE COALESCE(cl.relname, co.conname, pr.proname, tg.tgname, ty.typname, la.lanname, rw.rulename, ns.nspname, fs.srvname, fdw.fdwname) END AS refname, COALESCE(nsc.nspname, nso.nspname, nsp.nspname, nst.nspname, nsrw.nspname) AS nspname FROM pg_depend dep @@ -82,9 +90,11 @@ 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 +LEFT JOIN pg_foreign_server fs ON fs.oid=dep.objid +LEFT JOIN pg_foreign_data_wrapper fdw ON fdw.oid=dep.objid {{where_clause}} 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')) + ('pg_class', 'pg_constraint', 'pg_conversion', 'pg_language', 'pg_proc', 'pg_rewrite', 'pg_namespace', + 'pg_trigger', 'pg_type', 'pg_attrdef', 'pg_event_trigger', 'pg_foreign_server', 'pg_foreign_data_wrapper')) ORDER BY classid, cl.relkind -{% endif %} \ No newline at end of file +{% endif %} diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py index e78dc5efd..7f525b0c7 100644 --- a/web/pgadmin/browser/utils.py +++ b/web/pgadmin/browser/utils.py @@ -426,6 +426,8 @@ class PGChildNodeView(NodeView): 'y': 'type', 'T': 'trigger', 'l': 'language', + 'f': 'foreign_data_wrapper', + 'F': 'foreign_server', 'R': None, 'C': None, 'A': None diff --git a/web/pgadmin/misc/depends/static/js/depends.js b/web/pgadmin/misc/depends/static/js/depends.js index ffb8980a6..e9aa91fb5 100644 --- a/web/pgadmin/misc/depends/static/js/depends.js +++ b/web/pgadmin/misc/depends/static/js/depends.js @@ -49,7 +49,7 @@ define( (node['node_image']).apply(node, [null, null]) : (node['node_image'] || ('icon-' + res.type))) : ('icon-' + res.type); - res.type = S.capitalize(res.type, true); + res.type = S.titleize(res.type.replace(/_/g, " "), true); return res; } }); @@ -82,7 +82,7 @@ define( cell: Backgrid.Cell.extend({ render: function() { Backgrid.Cell.prototype.render.apply(this, arguments); - this.$el.addClass(this.model.get('icon')).css({"padding-left": "18px"}); + this.$el.prepend($('', {class: "wcTabIcon " + this.model.get('icon')})); return this; } }), diff --git a/web/pgadmin/static/js/alertifyjs/pgadmin.defaults.js b/web/pgadmin/static/js/alertifyjs/pgadmin.defaults.js index 5734b0b2e..a6aca1153 100644 --- a/web/pgadmin/static/js/alertifyjs/pgadmin.defaults.js +++ b/web/pgadmin/static/js/alertifyjs/pgadmin.defaults.js @@ -117,6 +117,8 @@ function(alertify, S) { return; } } - alertify.alert().show().set('message', msg).set('title', promptmsg); + alertify.alert().show().set( + 'message', msg.replace(new RegExp('\r?\n','g'), '
') + ).set('title', promptmsg); }; });