Add support for Foreign data wrappers, servers and user mappings.

pull/3/head
Neel Patel 2016-03-17 11:39:38 +00:00 committed by Dave Page
parent 05e527505d
commit 3969e91563
46 changed files with 3661 additions and 12 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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: {
fsrvoption: undefined,
fsrvvalue: undefined
},
// Defining schema for the Options model
schema: [
{id: 'fsrvoption', label:'Options', type:'text', cellHeaderClasses:'width_percent_50', group: null, editable: true},
{id: 'fsrvvalue', 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('fsrvoption')) ||
_.isNull(this.get('fsrvoption')) ||
String(this.get('fsrvoption')).replace(/^\s+|\s+$/g, '') == '') {
var msg = 'Please enter an option name';
this.errorModel.set('fsrvoption', msg);
return msg;
} else {
this.errorModel.unset('fsrvoption');
}
return null;
}
});
// Extend the browser's collection class for foreign server collection
if (!pgBrowser.Nodes['coll-foreign_server']) {
var foreign_data_wrappers = pgAdmin.Browser.Nodes['coll-foreign_server'] =
pgAdmin.Browser.Collection.extend({
node: 'foreign_server',
label: '{{ _('Foreign Servers') }}',
type: 'coll-foreign_server',
columns: ['name','fsrvowner','description']
});
};
// Extend the browser's node class for foreign server node
if (!pgBrowser.Nodes['foreign_server']) {
pgAdmin.Browser.Nodes['foreign_server'] = pgAdmin.Browser.Node.extend({
parent_type: 'foreign_data_wrapper',
type: 'foreign_server',
label: '{{ _('Foreign Server') }}',
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,
* foreign server collections and foreign server node
*/
pgBrowser.add_menus([{
name: 'create_foreign_server_on_coll', node: 'coll-foreign_server', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
},{
name: 'create_foreign_server', node: 'foreign_server', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
},{
name: 'create_foreign_server', node: 'foreign_data_wrapper', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Foreign Server...') }}',
icon: 'wcTabIcon icon-foreign_server', data: {action: 'create'}
}
]);
},
// Defining model for foreign server node
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
name: undefined,
fsrvtype: undefined,
fsrvversion: undefined,
fsrvvalue: undefined,
fsrvoptions: [],
fsrvowner: undefined,
description: undefined,
fsrvacl: []
},
// 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({'fsrvowner': userInfo.name}, {silent: true});
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
// Defining schema for the foreign server node
schema: [{
id: 'name', label: '{{ _('Name') }}', cell: 'string',
type: 'text', disabled: function(m) {
if (this.mode == 'edit' && this.node_info.server.version < 90200) {
return true;
}
else
return false;
}
},{
id: 'fsrvid', label:'{{ _('OID') }}', cell: 'string',
type: 'text', disabled: true, mode: ['properties']
},{
id: 'fsrvowner', label:'{{ _('Owner') }}', type: 'text',
control: Backform.NodeListByNameControl, node: 'role',
mode: ['edit', 'create', 'properties'], select2: { allowClear: false }
},{
id: 'fsrvtype', label:'{{ _('Type') }}', cell: 'string',
group: '{{ _('Definition') }}', type: 'text', mode: ['edit','create','properties'], disabled: function(m) {
return !m.isNew();
}
},{
id: 'fsrvversion', label:'{{ _('Version') }}', cell: 'string',
group: '{{ _('Definition') }}', type: 'text'
},{
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
type: 'multiline'
},{
id: 'fsrvoptions', label: 'Options', type: 'collection', group: "Options",
model: OptionsModel, control: 'unique-col-collection', mode: ['edit', 'create'],
canAdd: true, canDelete: true, uniqueCol : ['fsrvoption'],
columns: ['fsrvoption','fsrvvalue']
},{
id: 'fsrvacl', 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_server'];
});

View File

@ -0,0 +1,27 @@
SELECT 'fsrvacl' 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 srvacl FROM pg_foreign_server fsrv
LEFT OUTER JOIN pg_shdescription descr ON (
fsrv.oid=descr.objoid AND descr.classoid='pg_foreign_server'::regclass)
{% if fsid %}
WHERE fsrv.oid = {{ fsid|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(srvacl) as d FROM pg_foreign_server fsrv1
{% if fsid %}
WHERE fsrv1.oid = {{ fsid|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

View File

@ -0,0 +1,40 @@
{# ============= Create foreign server ============= #}
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data %}
CREATE SERVER {{ conn|qtIdent(data.name) }}{% if data.fsrvtype %}
TYPE {{ data.fsrvtype|qtLiteral }}{% endif %}{% if data.fsrvversion %}
VERSION {{ data.fsrvversion|qtLiteral }}{%-endif %}{% if fdwdata %}
FOREIGN DATA WRAPPER {{ conn|qtIdent(fdwdata.name) }}{% endif %}{% if data.fsrvoptions %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
OPTIONS ({% set addAlter = "True" %}{% endif %}
{{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
{% endif %}
{% endfor %}
{% endif %}{% if data %};{% endif %}
{# ============= Set the owner for foreign server ============= #}
{% if data.fsrvowner %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
{% endif %}
{# ============= Set the comment for foreign server ============= #}
{% if data.description %}
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ============= Set the ACL for foreign server ============= #}
{% if data.fsrvacl %}
{% for priv in data.fsrvacl %}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,9 @@
{# ============= Give foreign server name from foreign server 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 %}
{# ============= Delete/Drop cascade foreign server ============= #}
{% if name %}
DROP SERVER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
{% endif %}

View File

@ -0,0 +1,12 @@
{# ============= Get dependents of foreign server ============= #}
{% if fsid %}
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 WHERE u.umserver = {{ fsid }}::OID
)
SELECT um.um_oid, name, dep.deptype FROM umapData um
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
{% endif %}

View File

@ -0,0 +1,27 @@
{# ============= Give all the properties of foreign server ============= #}
{% if fdwid %}
SELECT fdw.oid as fdwoid,
fdwname as name
FROM pg_foreign_data_wrapper fdw
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
WHERE fdw.oid={{fdwid}}::int
{% else %}
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
array_to_string(srvoptions, ',') AS fsrvoptions,
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
FROM pg_foreign_server srv
LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
{% if data and fdwdata %}
WHERE fdw.fdwname = {{ fdwdata.name|qtLiteral }}::text and srvname = {{ data.name|qtLiteral }}::text
{% elif fdwdata %}
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
{% endif %}
{% if fid %}
WHERE srvfdw={{fid}}::int
{% endif %}
{% if fsid %}
WHERE srv.oid={{fsid}}::int
{% endif %}
ORDER BY srvname;
{% endif %}

View File

@ -0,0 +1,87 @@
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data %}
{# ============= Update foreign server name ============= #}
{% if data.name != o_data.name %}
ALTER SERVER {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{# ============= Update foreign server owner ============= #}
{% if data.fsrvowner and data.fsrvowner != o_data.fsrvowner %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
{% endif %}
{# ============= Update foreign server version ============= #}
{% if data.fsrvversion and data.fsrvversion != o_data.fsrvversion %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
VERSION {{ data.fsrvversion|qtLiteral }};
{% endif %}
{# ============= Update foreign server comments ============= #}
{% if data.description and data.description != o_data.description %}
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ============= Update foreign server options and values ============= #}
{% if data.fsrvoptions and data.fsrvoptions.deleted %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.deleted %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
DROP {{conn|qtIdent(variable.fsrvoption)}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if data.fsrvoptions and data.fsrvoptions.added %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.added %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
ADD {{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if data.fsrvoptions and data.fsrvoptions.changed %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.changed %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
SET {{conn|qtIdent(variable.fsrvoption)}} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{# Change the privileges #}
{% if data.fsrvacl %}
{% if 'deleted' in data.fsrvacl %}
{% for priv in data.fsrvacl.deleted %}
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
{% endfor %}
{% endif %}
{% if 'changed' in data.fsrvacl %}
{% for priv in data.fsrvacl.changed %}
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% if 'added' in data.fsrvacl %}
{% for priv in data.fsrvacl.added %}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,21 @@
SELECT 'fsrvacl' 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 srvacl FROM pg_foreign_server fsrv
LEFT OUTER JOIN pg_shdescription descr ON (
fsrv.oid=descr.objoid AND descr.classoid='pg_foreign_server'::regclass)
{% if fsid %}
WHERE fsrv.oid = {{ fsid|qtLiteral }}::OID
{% endif %}
) acl,
aclexplode(srvacl) 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

View File

@ -0,0 +1,40 @@
{# ============= Create foreign server ============= #}
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data.name %}
CREATE SERVER {{ conn|qtIdent(data.name) }}{% if data.fsrvtype %}
TYPE {{ data.fsrvtype|qtLiteral }}{% endif %}{% if data.fsrvversion %}
VERSION {{ data.fsrvversion|qtLiteral }}{%-endif %}{% if fdwdata %}
FOREIGN DATA WRAPPER {{ conn|qtIdent(fdwdata.name) }}{% endif %}{% if data.fsrvoptions %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
OPTIONS ({% set addAlter = "True" %}{% endif %}
{{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %}){% endif %}
{% endif %}
{% endfor %}
{% endif %}{% if data %};{% endif %}
{# ============= Set the owner for foreign server ============= #}
{% if data.fsrvowner %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
{% endif %}
{# ============= Set the comment for foreign server ============= #}
{% if data.description %}
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ============= Set the ACL for foreign server ============= #}
{% if data.fsrvacl %}
{% for priv in data.fsrvacl %}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}{% endfor %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,9 @@
{# ============= Give foreign server name from foreign server 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 %}
{# ============= Delete/Drop cascade foreign server ============= #}
{% if name %}
DROP SERVER {{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %};
{% endif %}

View File

@ -0,0 +1,12 @@
{# ============= Get dependents of foreign server ============= #}
{% if fsid %}
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 WHERE u.umserver = {{ fsid }}::OID
)
SELECT um.um_oid, name, dep.deptype FROM umapData um
LEFT JOIN pg_depend dep ON dep.objid=um.um_oid
{% endif %}

View File

@ -0,0 +1,26 @@
{# ============= Give all the properties of foreign server ============= #}
{% if fdwid %}
SELECT fdw.oid as fdwoid, fdwname as name
FROM pg_foreign_data_wrapper fdw
LEFT OUTER JOIN pg_description des ON (des.objoid=fdw.oid AND des.objsubid=0 AND des.classoid='pg_foreign_data_wrapper'::regclass)
WHERE fdw.oid={{fdwid}}::int
{% else %}
SELECT srv.oid as fsrvid, srvname as name, srvtype as fsrvtype, srvversion as fsrvversion, fdw.fdwname as fdwname, description,
array_to_string(srvoptions, ',') AS fsrvoptions,
pg_get_userbyid(srvowner) as fsrvowner, array_to_string(srvacl::text[], ', ') as acl
FROM pg_foreign_server srv
LEFT OUTER JOIN pg_foreign_data_wrapper fdw on fdw.oid=srvfdw
LEFT OUTER JOIN pg_description des ON (des.objoid=srv.oid AND des.objsubid=0 AND des.classoid='pg_foreign_server'::regclass)
{% if data and fdwdata %}
WHERE fdw.fdwname = {{ fdwdata.name|qtLiteral }}::text and srvname = {{ data.name|qtLiteral }}::text
{% elif fdwdata %}
WHERE fdw.fdwname = {{fdwdata.name|qtLiteral}}::text
{% endif %}
{% if fid %}
WHERE srvfdw={{fid}}::int
{% endif %}
{% if fsid %}
WHERE srv.oid={{fsid}}::int
{% endif %}
ORDER BY srvname;
{% endif %}

View File

@ -0,0 +1,87 @@
{% import 'macros/privilege.macros' as PRIVILEGE %}
{% if data %}
{# ============= Update foreign server name ============= #}
{% if data.name != o_data.name %}
ALTER SERVER {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{# ============= Update foreign server owner ============= #}
{% if data.fsrvowner and data.fsrvowner != o_data.fsrvowner %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OWNER TO {{ conn|qtIdent(data.fsrvowner) }};
{% endif %}
{# ============= Update foreign server version ============= #}
{% if data.fsrvversion and data.fsrvversion != o_data.fsrvversion %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
VERSION {{ data.fsrvversion|qtLiteral }};
{% endif %}
{# ============= Update foreign server comments ============= #}
{% if data.description and data.description != o_data.description %}
COMMENT ON SERVER {{ conn|qtIdent(data.name) }}
IS {{ data.description|qtLiteral }};
{% endif %}
{# ============= Update foreign server options and values ============= #}
{% if data.fsrvoptions and data.fsrvoptions.deleted %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.deleted %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
DROP {{conn|qtIdent(variable.fsrvoption)}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if data.fsrvoptions and data.fsrvoptions.added %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.added %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
ADD {{ conn|qtIdent(variable.fsrvoption) }} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% if data.fsrvoptions and data.fsrvoptions.changed %}
{% set addAlter = "False" %}
{% for variable in data.fsrvoptions.changed %}
{% if variable.fsrvoption and variable.fsrvoption != '' %}
{% if addAlter == "False" %}
ALTER SERVER {{ conn|qtIdent(data.name) }}
OPTIONS ({% set addAlter = "True" %}{%endif%}
SET {{conn|qtIdent(variable.fsrvoption)}} {{variable.fsrvvalue|qtLiteral}}{% if not loop.last %},{% else %});{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{# Change the privileges #}
{% if data.fsrvacl %}
{% if 'deleted' in data.fsrvacl %}
{% for priv in data.fsrvacl.deleted %}
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
{% endfor %}
{% endif %}
{% if 'changed' in data.fsrvacl %}
{% for priv in data.fsrvacl.changed %}
{{ PRIVILEGE.RESETALL(conn, 'FOREIGN SERVER', priv.grantee, data.name) }}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% if 'added' in data.fsrvacl %}
{% for priv in data.fsrvacl.added %}
{{ PRIVILEGE.APPLY(conn, 'FOREIGN SERVER', priv.grantee, data.name, priv.without_grant, priv.with_grant) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,753 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Implements User Mapping 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 functools import wraps
class UserMappingModule(CollectionNodeModule):
"""
class UserMappingModule(CollectionNodeModule)
A module class for user mapping node derived from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the user mapping module and it's base module.
* get_nodes(gid, sid, did, fid, fsid)
- Method is used to generate the browser collection node.
* node_inode()
- Method is overridden from its base class to make the node as leaf node.
* script_load(self)
- Load the module script for user mapping, when any of the foreign server node is
initialized.
"""
NODE_TYPE = 'user_mapping'
COLLECTION_LABEL = gettext("User Mappings")
def __init__(self, *args, **kwargs):
"""
Method is used to initialize the User mapping module and it's base module.
Args:
*args:
**kwargs:
"""
self.min_ver = None
self.max_ver = None
super(UserMappingModule, self).__init__(*args, **kwargs)
def get_nodes(self, gid, sid, did, fid, fsid):
"""
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
fsid: Foreign server ID
"""
yield self.generate_browser_collection_node(fsid)
@property
def node_inode(self):
"""
node_inode
Override this property to make the node as leaf node.
"""
return False
@property
def script_load(self):
"""
Load the module script for user mapping, when any of the foreign server node is initialized.
Returns: node type of the server module.
"""
return servers.ServerModule.NODE_TYPE
blueprint = UserMappingModule(__name__)
class UserMappingView(PGChildNodeView):
"""
class UserMappingView(PGChildNodeView)
A view class for user mapping node derived from PGChildNodeView. This class is
responsible for all the stuff related to view like updating user mapping
node, showing properties, showing sql in sql pane.
Methods:
-------
* __init__(**kwargs)
- Method is used to initialize the UserMappingView 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, fsid)
- This function is used to list all the user mapping nodes within that collection.
* nodes(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.
* properties(gid, sid, did, fid, fsid, umid)
- This function will show the properties of the selected user mapping node
* tokenizeOptions(option_value)
- This function will tokenize the string stored in database
* update(gid, sid, did, fid, fsid, umid)
- This function will update the data for the selected user mapping node
* create(gid, sid, did, fid, fsid)
- This function will create the new user mapping node
* delete(gid, sid, did, fid, fsid, umid)
- This function will delete the selected user mapping node
* msql(gid, sid, did, fid, fsid, umid)
- This function is used to return modified SQL for the selected user mapping node
* get_sql(data, fid, fsid, umid)
- This function will generate sql from model data
* sql(gid, sid, did, fid, fsid, umid):
- This function will generate sql to show it in sql pane for the selected user mapping node.
* dependents(gid, sid, did, fid, fsid, umid):
- This function get the dependents and return ajax response for the user mapping node.
* dependencies(self, gid, sid, did, fid, fsid, umid):
- This function get the dependencies and return ajax response for the user mapping node.
"""
node_type = blueprint.node_type
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'},
{'type': 'int', 'id': 'fid'},
{'type': 'int', 'id': 'fsid'}
]
ids = [
{'type': 'int', 'id': 'umid'}
]
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(
"user_mappings/js/user_mappings.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!"
)
)
self.template_path = 'user_mappings/sql/9.1_plus'
ver = self.manager.version
# we will set template path for sql scripts
if ver >= 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)

View File

@ -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'];
});

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

View File

@ -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'];
});

View File

@ -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

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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;

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -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

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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;

View File

@ -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

View File

@ -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 %}

View File

@ -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;

View File

@ -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 %}
{% endif %}

View File

@ -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

View File

@ -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($('<i>', {class: "wcTabIcon " + this.model.get('icon')}));
return this;
}
}),

View File

@ -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'), '<br />')
).set('title', promptmsg);
};
});