Implemented Synonym node for EPAS

This commit is contained in:
Murtuza Zabuawala 2016-08-26 13:01:57 +05:30 committed by Akshay Joshi
parent 7b72448ec9
commit 5a78dd7ad3
18 changed files with 1550 additions and 20 deletions

View File

@ -46,7 +46,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
name: 'create_package', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Package...') }}',
icon: 'wcTabIcon icon-package', data: {action: 'create', check: false},
icon: 'wcTabIcon icon-package', data: {action: 'create', check: true},
enable: 'canCreate'
}
]);
@ -59,26 +59,16 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
if (data && data.check == false)
return true;
var t = pgBrowser.tree, i = item, d = itemData;
// To iterate over tree to check parent node
while (i) {
// If it is schema then allow user to create collation
if (_.indexOf(['schema'], d._type) > -1)
return true;
var treeData = this.getTreeNodeHierarchy(item),
server = treeData['server'];
if (server && server.server_type === 'pg')
return false;
// If it is catalog then don't allow user to create package
if (treeData['catalog'] != undefined)
return false;
if ('coll-package' == d._type) {
//Check if we are not child of catalog
prev_i = t.hasParent(i) ? t.parent(i) : null;
prev_d = prev_i ? t.itemData(prev_i) : null;
if( prev_d._type == 'catalog') {
return false;
} else {
return true;
}
}
i = t.hasParent(i) ? t.parent(i) : null;
d = i ? t.itemData(i) : null;
}
// by default we want to allow create menu
return true;
},

View File

@ -0,0 +1,634 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements Synonym Node """
import simplejson as json
from functools import wraps
import pgadmin.browser.server_groups.servers.databases as database
from flask import render_template, request, jsonify
from flask_babel import gettext
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
import SchemaChildModule
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, \
make_response as ajax_response, internal_server_error
from pgadmin.utils.ajax import precondition_required
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
class SynonymModule(SchemaChildModule):
"""
class SynonymModule(CollectionNodeModule)
A module class for Synonym node derived from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the Synonym and it's base module.
* get_nodes(gid, sid, did, scid, syid)
- 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()
- Load the module script for schema, when any of the server node is
initialized.
"""
NODE_TYPE = 'synonym'
COLLECTION_LABEL = gettext("Synonyms")
def __init__(self, *args, **kwargs):
"""
Method is used to initialize the SynonymModule and it's base module.
Args:
*args:
**kwargs:
"""
super(SynonymModule, self).__init__(*args, **kwargs)
self.min_ver = 90100
self.max_ver = None
self.server_type = ['ppas']
def get_nodes(self, gid, sid, did, scid):
"""
Generate the collection node
"""
yield self.generate_browser_collection_node(scid)
@property
def script_load(self):
"""
Load the module script for database, when any of the database node is
initialized.
"""
return database.DatabaseModule.NODE_TYPE
@property
def node_inode(self):
return False
blueprint = SynonymModule(__name__)
class SynonymView(PGChildNodeView):
"""
This class is responsible for generating routes for Synonym node
Methods:
-------
* __init__(**kwargs)
- Method is used to initialize the SynonymView and it's base view.
* 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()
- This function is used to list all the Synonym nodes within that
collection.
* nodes()
- This function will used to create all the child node within that
collection, Here it will create all the Synonym node.
* properties(gid, sid, did, scid, syid)
- This function will show the properties of the selected Synonym node
* create(gid, sid, did, scid)
- This function will create the new Synonym object
* update(gid, sid, did, scid, syid)
- This function will update the data for the selected Synonym node
* delete(self, gid, sid, scid, syid):
- This function will drop the Synonym object
* msql(gid, sid, did, scid, syid)
- This function is used to return modified SQL for the selected
Synonym node
* get_sql(data, scid, syid)
- This function will generate sql from model data
* sql(gid, sid, did, scid):
- This function will generate sql to show it in sql pane for the
selected Synonym node.
* dependency(gid, sid, did, scid):
- This function will generate dependency list show it in dependency
pane for the selected Synonym node.
* dependent(gid, sid, did, scid):
- This function will generate dependent list to show it in dependent
pane for the selected Synonym node.
"""
node_type = blueprint.node_type
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'},
{'type': 'int', 'id': 'scid'}
]
ids = [
{'type': 'string', 'id': 'syid'}
]
operations = dict({
'obj': [
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
{'get': 'list', 'post': 'create'}
],
'delete': [{'delete': 'delete'}],
'children': [{'get': 'children'}],
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
'sql': [{'get': 'sql'}],
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'stats': [{'get': 'statistics'}],
'dependency': [{'get': 'dependencies'}],
'dependent': [{'get': 'dependents'}],
'module.js': [{}, {}, {'get': 'module_js'}],
'get_target_objects': [{'get': 'get_target_objects'},
{'get': 'get_target_objects'}]
})
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!"
)
)
# we will set template path for sql scripts
self.template_path = 'synonym/sql/9.1_plus'
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid, did, scid):
"""
This function is used to list all the synonym nodes within that collection.
Args:
gid: Server group ID
sid: Server ID
did: Database ID
scid: Schema ID
Returns:
JSON of available synonym nodes
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']), scid=scid)
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, scid):
"""
This function will used to create all the child node within that collection.
Here it will create all the synonym node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
Returns:
JSON of available synonym child nodes
"""
res = []
SQL = render_template("/".join([self.template_path,
'nodes.sql']), scid=scid)
status, rset = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
res.append(
self.blueprint.generate_browser_node(
row['name'],
scid,
row['name'],
icon="icon-synonym"
))
return make_json_response(
data=res,
status=200
)
@check_precondition
def get_target_objects(self, gid, sid, did, scid, syid=None):
"""
This function will provide list of objects as per user selection.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
scid: Schema ID
syid: Synonym ID
Returns:
List of objects
"""
res = []
data = dict()
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
sql = render_template("/".join([self.template_path,
'get_objects.sql']),
trgTyp=data['trgTyp'],
trgSchema=data['trgSchema'])
status, rset = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
res.append({'label': row['name'],
'value': row['name']})
return make_json_response(
data=res,
status=200
)
@check_precondition
def properties(self, gid, sid, did, scid, syid):
"""
This function will show the properties of the selected synonym node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
scid: Schema ID
syid: Synonym ID
Returns:
JSON of selected synonym node
"""
try:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, syid=syid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
return ajax_response(
response=res['rows'][0],
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def create(self, gid, sid, did, scid):
"""
This function will creates new the synonym object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
"""
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
required_args = [
'name', 'targettype', 'synobjschema', 'synobjname'
]
for arg in required_args:
if arg not in data:
return make_json_response(
status=410,
success=0,
errormsg=gettext(
"Could not find the required parameter (%s)." % arg
)
)
try:
SQL = render_template("/".join([self.template_path,
'create.sql']),
data=data, conn=self.conn, comment=False)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return jsonify(
node=self.blueprint.generate_browser_node(
data['name'],
scid,
data['name'],
icon="icon-synonym"
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, scid, syid):
"""
This function will delete existing the synonym object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
# Below will decide if it's simple drop or drop with cascade call
try:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, syid=syid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
data = res['rows'][0]
if data['name'] is None:
return make_json_response(
success=0,
errormsg=gettext(
'Error: Object not found.'
),
info=gettext(
'The specified synonym could not be found.\n'
)
)
SQL = render_template("/".join([self.template_path,
'delete.sql']),
data=data,
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("Synonym dropped"),
data={
'id': syid,
'scid': scid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def update(self, gid, sid, did, scid, syid):
"""
This function will updates existing the synonym object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
data = request.form if request.form else json.loads(
request.data, encoding='utf-8'
)
SQL = self.get_sql(gid, sid, data, scid, syid)
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="Synonym updated",
data={
'id': syid,
'scid': scid,
'did': did
}
)
else:
return make_json_response(
success=1,
info="Nothing to update",
data={
'id': syid,
'scid': scid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def msql(self, gid, sid, did, scid, syid=None):
"""
This function will generates modified sql for synonym object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
data = dict()
for k, v in request.args.items():
try:
data[k] = json.loads(v, encoding='utf-8')
except ValueError:
data[k] = v
try:
SQL = self.get_sql(gid, sid, data, scid, syid)
if SQL and SQL.strip('\n') and SQL.strip(' '):
return make_json_response(
data=SQL,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
def get_sql(self, gid, sid, data, scid, syid=None):
"""
This function will genrate sql from model data
"""
if syid is not None:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, syid=syid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
old_data = res['rows'][0]
# If target schema/object is not present then take it from
# old data, it means it does not changed
if 'synobjschema' not in data:
data['synobjschema'] = old_data['synobjschema']
if 'synobjname' not in data:
data['synobjname'] = old_data['synobjname']
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data, conn=self.conn
)
else:
required_args = [
'name', 'targettype', 'synobjschema', 'synobjname'
]
for arg in required_args:
if arg not in data:
return "-- missing definition"
SQL = render_template("/".join([self.template_path,
'create.sql']), comment=False,
data=data, conn=self.conn)
return SQL.strip('\n')
@check_precondition
def sql(self, gid, sid, did, scid, syid):
"""
This function will generates reverse engineered sql for synonym object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, syid=syid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
data = res['rows'][0]
SQL = render_template("/".join([self.template_path,
'create.sql']),
data=data, conn=self.conn, comment=True)
return ajax_response(response=SQL)
@check_precondition
def dependents(self, gid, sid, did, scid, syid):
"""
This function get the dependents and return ajax response
for the Synonym node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
dependents_result = self.get_dependents(
self.conn, syid, where="WHERE dep.objid=0::oid"
)
return ajax_response(
response=dependents_result,
status=200
)
@check_precondition
def dependencies(self, gid, sid, did, scid, syid):
"""
This function get the dependencies and return ajax response
for the Synonym node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
syid: Synonym ID
"""
dependencies_result = self.get_dependencies(
self.conn, syid, where="WHERE dep.objid=0::oid"
)
return ajax_response(
response=dependencies_result,
status=200
)
SynonymView.register_node_view(blueprint)

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

View File

@ -0,0 +1,248 @@
define(
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
function($, _, S, pgAdmin, pgBrowser, alertify) {
if (!pgBrowser.Nodes['coll-synonym']) {
var databases = pgAdmin.Browser.Nodes['coll-synonym'] =
pgAdmin.Browser.Collection.extend({
node: 'synonym',
label: '{{ _('Synonyms') }}',
type: 'coll-synonym',
columns: ['name', 'owner','is_public_synonym']
});
};
if (!pgBrowser.Nodes['synonym']) {
pgAdmin.Browser.Nodes['synonym'] = pgBrowser.Node.extend({
type: 'synonym',
dialogHelp: '{{ url_for('help.static', filename='synonym_dialog.html') }}',
label: '{{ _('Synonym') }}',
collection_type: 'coll-synonym',
hasSQL: true,
hasDepends: true,
parent_type: ['schema', 'catalog'],
Init: function() {
/* Avoid mulitple registration of menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_synonym_on_coll', node: 'coll-synonym', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Synonym...') }}',
icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_synonym', node: 'synonym', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Synonym...') }}',
icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_synonym', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Synonym...') }}',
icon: 'wcTabIcon icon-synonym', data: {action: 'create', check: true},
enable: 'canCreate'
}
]);
},
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
model: pgAdmin.Browser.Node.Model.extend({
isNew: function() {
return !this.fetchFromServer;
},
idAttribute: 'name',
// Default values!
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
if (isNew) {
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
var schemaInfo = args.node_info.schema;
this.set({
'owner': userInfo.name,
'synobjschema': schemaInfo.label,
'schema': schemaInfo.label,
'targettype': 'r'
}, {silent: true});
} else {
this.fetchFromServer = true;
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
schema: [{
id: 'name', label: '{{ _('Name') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
disabled: 'inSchemaWithModelCheck'
},{
id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
disabled: true , control: 'node-list-by-name',
node: 'role'
},{
id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
disabled: true , control: 'node-list-by-name',
node: 'schema'
},{
type: 'nested', control: 'fieldset', label: '{{ _('Definition') }}',
schema:[{
id: 'targettype', label:'{{ _('Target Type') }}', cell: 'string',
disabled: 'inSchema', group: '{{ _('Definition') }}',
select2: { width: "50%", allowClear: false },
options: function(obj) {
return [
{label: "Table", value: "r"},
{label: "Sequence", value: "S"},
{label: "View", value: "v"},
{label: "Function", value: "f"},
{label: "Procedure", value: "p"},
{label: "Public Synonym", value: "s"}
]
},
control: 'select2'
},{
id: 'synobjschema', label:'{{ _('Target Schema') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'],
group: '{{ _('Definition') }}', deps: ['targettype'],
select2: { allowClear: false }, control: 'node-list-by-name',
node: 'schema', filter: function(d) {
// Exclude PPAS catalogs
var exclude_catalogs = ['pg_catalog', 'sys', 'dbo',
'pgagent', 'information_schema',
'dbms_job_procedure'];
return d && _.indexOf(exclude_catalogs, d.label) == -1;
},
disabled: function(m) {
// If tagetType is synonym then disable it
if(!m.inSchema.apply(this, [m])) {
var is_synonym = (m.get('targettype') == 's');
if(is_synonym) {
m.set('synobjschema', 'public', {silent: true});
return true;
} else {
return false;
}
}
return true;
}
},{
id: 'synobjname', label:'{{ _('Target Object') }}', cell: 'string',
type: 'text', disabled: 'inSchema', group: '{{ _('Definition') }}',
deps: ['targettype', 'synobjschema'],
control: 'node-ajax-options',
options: function(control) {
var trgTyp = control.model.get('targettype');
var trgSchema = control.model.get('synobjschema');
var res = [];
var node = control.field.get('schema_node'),
_url = node.generate_url.apply(
node, [
null, 'get_target_objects', control.field.get('node_data'), false,
control.field.get('node_info') ]);
$.ajax({
type: 'GET',
timeout: 30000,
url: _url,
cache: false,
async: false,
data: {"trgTyp" : trgTyp, "trgSchema" : trgSchema},
// On success return function list from server
success: function(result) {
res = result.data;
return res;
},
// On failure show error appropriate error message to user
error: function(xhr, status, error) {
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
alertify.error(err.errormsg);
}
} catch (e) {}
}
});
return res;
}
}]
},{
id: 'is_public_synonym', label:'{{ _('Public Synonym?') }}',
disabled: true, type: 'switch', mode: ['properties'], cell: 'switch',
options: { onText: 'Yes', offText: 'No', onColor: 'success',
offColor: 'primary', size: 'mini'}
}
],
validate: function() {
var err = {},
msg = undefined;
this.errorModel.clear();
if (_.isUndefined(this.get('name'))
|| String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Name cannot be empty.') }}';
this.errorModel.set('name', msg);
} else if (_.isUndefined(this.get('synobjschema'))
|| String(this.get('synobjschema')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Target schema cannot be empty.') }}';
this.errorModel.set('synobjschema', msg);
} else if (_.isUndefined(this.get('synobjname'))
|| String(this.get('synobjname')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Target object cannot be empty.') }}';
this.errorModel.set('synobjname', msg);
}
return null;
},
// We will disable everything if we are under catalog node
inSchema: function() {
if(this.node_info && 'catalog' in this.node_info)
{
return true;
}
return false;
},
// We will check if we are under schema node & in 'create' mode
inSchemaWithModelCheck: function(m) {
if(this.node_info && 'schema' in this.node_info)
{
// We will disbale control if it's in 'edit' mode
if (m.isNew()) {
return false;
} else {
return true;
}
}
return true;
}
}),
canCreate: function(itemData, item, data) {
//If check is false then , we will allow create menu
if (data && data.check == false)
return true;
var treeData = this.getTreeNodeHierarchy(item),
server = treeData['server'];
if (server && server.server_type === 'pg')
return false;
// If it is catalog then don't allow user to create synonyms
if (treeData['catalog'] != undefined)
return false;
// by default we do not want to allow create menu
return true;
}
});
}
return pgBrowser.Nodes['synonym'];
});

View File

@ -0,0 +1,17 @@
{% set is_public = False %}
{% if data.schema == 'public' %}
{% set is_public = True %}
{% endif %}
{% if comment %}
-- {% if is_public %}Public{% else %}Private{% endif %} synonym: {{ conn|qtIdent(data.schema, data.name) }};
-- DROP {% if is_public %}PUBLIC {% endif %}SYNONYM {{ conn|qtIdent(data.schema, data.name) }};
{% endif %}
CREATE OR REPLACE {% if is_public %}
PUBLIC SYNONYM {{ conn|qtIdent(data.name) }}
{% else %}
SYNONYM {{ conn|qtIdent(data.schema, data.name) }}
{% endif %}
FOR {{ conn|qtIdent(data.synobjschema, data.synobjname) }};

View File

@ -0,0 +1,8 @@
{% set is_public = False %}
{% if data.schema == 'public' %}
{% set is_public = True %}
{% endif %}
DROP {% if is_public %}
PUBLIC SYNONYM {{ conn|qtIdent(data.name) }}{% else %}
SYNONYM {{ conn|qtIdent(data.schema, data.name) }}
{% endif %};

View File

@ -0,0 +1,43 @@
{###########################################}
{### If Target Type is Function ###}
{###########################################}
{% if trgTyp == 'f' %}
SELECT DISTINCT proname AS name
FROM pg_proc p, pg_namespace n
WHERE p.pronamespace = n.oid AND
n.nspname = {{ trgSchema|qtLiteral }} AND
p.protype = '0'
ORDER BY proname;
{###########################################}
{### If Target Type is Procedure ###}
{###########################################}
{% elif trgTyp == 'p' %}
SELECT DISTINCT proname AS name
FROM pg_proc p, pg_namespace n
WHERE p.pronamespace = n.oid AND
n.nspname = {{ trgSchema|qtLiteral }} AND
p.protype = '1'
ORDER BY proname;
{###########################################}
{### If Target Type is Synonym ###}
{###########################################}
{% elif trgTyp == 's' %}
SELECT synname AS name
FROM pg_synonym
ORDER BY synname;
{% else %}
{###################################################}
{### If Target Type is Table/View/M.View/Sequnce ###}
{###################################################}
SELECT relname AS name
FROM pg_class c, pg_namespace n
WHERE c.relnamespace = n.oid AND
n.nspname = {{ trgSchema|qtLiteral }} AND
{% if trgTyp == 'v' %}
{# If view is select then we need to fetch both view and materialized view #}
(c.relkind = 'v' OR c.relkind = 'm')
{% else %}
c.relkind = {{ trgTyp|qtLiteral }}
{% endif %}
ORDER BY relname;
{% endif %}

View File

@ -0,0 +1,8 @@
{# Below will provide oid for newly created collation #}
{% if data %}
SELECT c.oid
FROM pg_collation c, pg_namespace n
WHERE c.collnamespace=n.oid AND
n.nspname = {{ data.schema|qtLiteral }} AND
c.collname = {{ data.name|qtLiteral }}
{% endif %}

View File

@ -0,0 +1,5 @@
SELECT synname as name
FROM pg_synonym s
JOIN pg_namespace ns ON s.synnamespace = ns.oid
AND s.synnamespace = {{scid}}::oid
ORDER BY synname;

View File

@ -0,0 +1,19 @@
SELECT synname AS name, pg_get_userbyid(synowner) AS owner,
synobjschema, synobjname, ns.nspname as schema,
COALESCE((SELECT relkind
FROM pg_class c, pg_namespace n
WHERE c.relnamespace = n.oid
AND n.nspname = synobjschema
AND c.relname = synobjname),
(SELECT CASE WHEN p.protype = '0' THEN 'f'::"char" ELSE 'p'::"char" END
FROM pg_proc p, pg_namespace n
WHERE p.pronamespace = n.oid
AND n.nspname = synobjschema
AND p.proname = synobjname LIMIT 1), 's') AS targettype, -- Default s = Synonym
CASE WHEN ns.nspname = 'public' THEN true ELSE false END AS is_public_synonym
FROM pg_synonym s JOIN pg_namespace ns ON s.synnamespace = ns.oid
WHERE s.synnamespace={{scid}}::oid
{% if syid %}
AND s.synname={{ syid|qtLiteral }}
{% endif %}
ORDER BY synname;

View File

@ -0,0 +1,10 @@
{% set is_public = False %}
{% if o_data.schema == 'public' %}
{% set is_public = True %}
{% endif %}
CREATE OR REPLACE {% if is_public %}
PUBLIC SYNONYM {{ conn|qtIdent(o_data.name) }}
{% else %}
SYNONYM {{ conn|qtIdent(o_data.schema, o_data.name) }}
{% endif %}
FOR {{ conn|qtIdent(data.synobjschema, data.synobjname) }};

View File

@ -0,0 +1,17 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
class SynonymTestGenerator(BaseTestGenerator):
def generate_tests(self):
return

View File

@ -0,0 +1,74 @@
# #################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# ##################################################################
from regression import test_utils as utils
from pgadmin.utils.route import BaseTestGenerator
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from . import utils as synonym_utils
class SynonymAddTestCase(BaseTestGenerator):
""" This class will add new synonym under schema node. """
scenarios = [
# Fetching default URL for synonym node.
('Default Node URL', dict(url='/browser/synonym/obj/'))
]
@classmethod
def setUpClass(cls):
"""
This function perform the three tasks
1. Add the test server
2. Connect to server
3. Add the databases
4. Add the schemas
:return: None
"""
# Firstly, add the server
server_utils.add_server(cls.tester)
# Connect to server
cls.server_connect_response, cls.server_group, cls.server_ids = \
server_utils.connect_server(cls.tester)
if len(cls.server_connect_response) == 0:
raise Exception("No Server(s) connected to add the database!!!")
# Add database
database_utils.add_database(cls.tester, cls.server_connect_response,
cls.server_ids)
# Add schemas
schema_utils.add_schemas(cls.tester)
def runTest(self):
""" This function will add synonym under schema node. """
synonym_utils.add_synonym(
self.tester, self.server_connect_response, self.server_ids)
@classmethod
def tearDownClass(cls):
"""
This function deletes the added synonyms, schemas, database,
server and the 'parent_id.pkl' file which is created in setup()
function.
:return: None
"""
synonym_utils.delete_synonym(cls.tester)
schema_utils.delete_schema(cls.tester)
database_utils.delete_database(cls.tester)
server_utils.delete_server(cls.tester)
utils.delete_parent_id_file()

View File

@ -0,0 +1,77 @@
# #################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# ##################################################################
from regression import test_utils as utils
from pgadmin.utils.route import BaseTestGenerator
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from . import utils as synonym_utils
class SynonymDeleteTestCase(BaseTestGenerator):
""" This class will delete added synonym under schema node. """
scenarios = [
# Fetching default URL for synonym node.
('Fetch synonym Node URL', dict(url='/browser/synonym/obj/'))
]
@classmethod
def setUpClass(cls):
"""
This function perform the three tasks
1. Add the test server
2. Connect to server
3. Add the databases
4. Add the schemas
5. Add the synonyms
:return: None
"""
# Firstly, add the server
server_utils.add_server(cls.tester)
# Connect to server
cls.server_connect_response, cls.server_group, cls.server_ids = \
server_utils.connect_server(cls.tester)
if len(cls.server_connect_response) == 0:
raise Exception("No Server(s) connected to add the database!!!")
# Add database
database_utils.add_database(cls.tester, cls.server_connect_response,
cls.server_ids)
# Add schemas
schema_utils.add_schemas(cls.tester)
# Add synonyms
synonym_utils.add_synonym(cls.tester, cls.server_connect_response,
cls.server_ids)
def runTest(self):
""" This function will delete synonym under schema node. """
synonym_utils.delete_synonym(self.tester)
@classmethod
def tearDownClass(cls):
"""
This function deletes the added schemas, database,
server and the 'parent_id.pkl' file which is created in setup()
function.
:return: None
"""
schema_utils.delete_schema(cls.tester)
database_utils.delete_database(cls.tester)
server_utils.delete_server(cls.tester)
utils.delete_parent_id_file()

View File

@ -0,0 +1,101 @@
# #################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# ##################################################################
import json
from regression import test_utils as utils
from pgadmin.utils.route import BaseTestGenerator
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from . import utils as synonym_utils
class SynonymGetTestCase(BaseTestGenerator):
""" This class will fetch new synonym under schema node. """
scenarios = [
# Fetching default URL for synonym node.
('Fetch synonym Node URL', dict(url='/browser/synonym/obj/'))
]
@classmethod
def setUpClass(cls):
"""
This function perform the three tasks
1. Add the test server
2. Connect to server
3. Add the databases
4. Add the schemas
5. Add the synonyms
:return: None
"""
# Firstly, add the server
server_utils.add_server(cls.tester)
# Connect to server
cls.server_connect_response, cls.server_group, cls.server_ids = \
server_utils.connect_server(cls.tester)
if len(cls.server_connect_response) == 0:
raise Exception("No Server(s) connected to add the database!!!")
# Add database
database_utils.add_database(cls.tester, cls.server_connect_response,
cls.server_ids)
# Add schemas
schema_utils.add_schemas(cls.tester)
# Add synonyms
synonym_utils.add_synonym(cls.tester, cls.server_connect_response,
cls.server_ids)
def runTest(self):
""" This function will fetch synonym under schema node. """
all_id = utils.get_ids()
server_ids = all_id["sid"]
db_ids_dict = all_id["did"][0]
schema_ids_dict = all_id["scid"][0]
synonym_ids_dict = all_id["syid"][0]
for server_id in server_ids:
db_id = db_ids_dict[int(server_id)]
db_con = database_utils.verify_database(self.tester,
utils.SERVER_GROUP,
server_id, db_id)
if db_con['data']["connected"]:
schema_info = schema_ids_dict[int(server_id)]
schema_response = schema_utils.verify_schemas(
self.tester, server_id, db_id, schema_info[0])
schema_response = json.loads(
schema_response.data.decode('utf-8'))
if len(schema_response) != 0:
synonym_id = synonym_ids_dict[int(server_id)]
get_response = synonym_utils.verify_synonym(
self.tester, server_id, db_id, schema_info[0],
synonym_id)
self.assertEquals(get_response.status_code, 200)
@classmethod
def tearDownClass(cls):
"""
This function deletes the added synonyms, schemas, database,
server and the 'parent_id.pkl' file which is created in setup()
function.
:return: None
"""
synonym_utils.delete_synonym(cls.tester)
schema_utils.delete_schema(cls.tester)
database_utils.delete_database(cls.tester)
server_utils.delete_server(cls.tester)
utils.delete_parent_id_file()

View File

@ -0,0 +1,125 @@
# #################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
# ##################################################################
import json
from regression import test_utils as utils
from pgadmin.utils.route import BaseTestGenerator
from regression.test_setup import advanced_config_data
from pgadmin.browser.server_groups.servers.tests import utils as server_utils
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
database_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from . import utils as synonym_utils
class SynonymPutTestCase(BaseTestGenerator):
""" This class will update added synonym under schema node. """
scenarios = [
# Fetching default URL for synonym node.
('Fetch synonym Node URL', dict(url='/browser/synonym/obj/'))
]
@classmethod
def setUpClass(cls):
"""
This function perform the three tasks
1. Add the test server
2. Connect to server
3. Add the databases
4. Add the schemas
5. Add the synonyms
:return: None
"""
# Firstly, add the server
server_utils.add_server(cls.tester)
# Connect to server
cls.server_connect_response, cls.server_group, cls.server_ids = \
server_utils.connect_server(cls.tester)
if len(cls.server_connect_response) == 0:
raise Exception("No Server(s) connected to add the database!!!")
# Add database
database_utils.add_database(cls.tester, cls.server_connect_response,
cls.server_ids)
# Add schemas
schema_utils.add_schemas(cls.tester)
# Add synonyms
synonym_utils.add_synonym(cls.tester, cls.server_connect_response,
cls.server_ids)
def runTest(self):
""" This function will update synonym under schema node. """
all_id = utils.get_ids()
server_ids = all_id["sid"]
db_ids_dict = all_id["did"][0]
schema_ids_dict = all_id["scid"][0]
synonym_ids_dict = all_id["syid"][0]
for server_id in server_ids:
db_id = db_ids_dict[int(server_id)]
db_con = database_utils.verify_database(self.tester,
utils.SERVER_GROUP,
server_id, db_id)
if db_con['data']["connected"]:
schema_info = schema_ids_dict[int(server_id)]
schema_response = schema_utils.verify_schemas(self.tester,
server_id,
db_id,
schema_info[0])
schema_response = json.loads(
schema_response.data.decode('utf-8'))
if len(schema_response) != 0:
synonym_id = synonym_ids_dict[int(server_id)]
get_response = synonym_utils.verify_synonym(
self.tester, server_id, db_id, schema_info[0],
synonym_id)
get_response_data = json.loads(
get_response.data.decode('utf-8'))
if len(get_response_data) == 0:
raise Exception("No synonym node to update.")
data = {
"description":
advanced_config_data['synonym_update_data']
['comment'],
"id": synonym_id,
}
put_response = self.tester.put(
self.url + str(utils.SERVER_GROUP) + '/' +
str(server_id) + '/' +
str(db_id) + '/' +
str(schema_info[0]) + '/' +
str(synonym_id),
data=json.dumps(data),
follow_redirects=True)
self.assertEquals(put_response.status_code, 200)
@classmethod
def tearDownClass(cls):
"""
This function deletes the added synonyms, schemas, database,
server and the 'parent_id.pkl' file which is created in setup()
function.
:return: None
"""
synonym_utils.delete_synonym(cls.tester)
schema_utils.delete_schema(cls.tester)
database_utils.delete_database(cls.tester)
server_utils.delete_server(cls.tester)
utils.delete_parent_id_file()

View File

@ -0,0 +1,154 @@
# ##########################################################################
#
# #pgAdmin 4 - PostgreSQL Tools
#
# #Copyright (C) 2013 - 2016, The pgAdmin Development Team
# #This software is released under the PostgreSQL Licence
#
# ##########################################################################
import json
import os
import pickle
from regression.test_setup import pickle_path, advanced_config_data
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
utils as schema_utils
from regression import test_utils as utils
SYNONYM_URL = '/browser/synonym/obj/'
def get_synonym_config_data(server_connect_data):
"""This function returns the synonym config data"""
adv_config_data = None
data = None
db_user = server_connect_data['data']['user']['name']
# Get the config data of appropriate db user
for config_test_data in \
advanced_config_data['synonym_credentials']:
if db_user == config_test_data['owner']:
adv_config_data = config_test_data
if adv_config_data is not None:
data = {
"name": adv_config_data['name'],
"schema": adv_config_data['schema'],
"synobjname": adv_config_data['synobjname'],
"synobjschema": adv_config_data['synobjschema'],
"targettype": adv_config_data['targettype']
}
return data
def write_synonym_id(response_data, server_id):
"""
This function writes the server and synonym id
:param response_data: synonym response data
:type response_data: dict
:param server_id: server id
:type server_id: int
:return: None
"""
synonym_id = response_data['node']['_id']
pickle_id_dict = utils.get_pickle_id_dict()
if os.path.isfile(pickle_path):
existing_server_id = open(pickle_path, 'rb')
tol_server_id = pickle.load(existing_server_id)
pickle_id_dict = tol_server_id
if 'syid' in pickle_id_dict:
if pickle_id_dict['syid']:
# Add the db_id as value in dict
pickle_id_dict["syid"][0].update(
{int(server_id): synonym_id})
else:
# Create new dict with server_id and db_id
pickle_id_dict["syid"].append(
{int(server_id): synonym_id})
db_output = open(pickle_path, 'wb')
pickle.dump(pickle_id_dict, db_output)
db_output.close()
def add_synonym(tester, server_connect_response, server_ids):
"""This function add the synonym to schemas"""
all_id = utils.get_ids()
db_ids_dict = all_id["did"][0]
schema_ids_dict = all_id["scid"][0]
for server_connect_response, server_id in zip(server_connect_response,
server_ids):
db_id = db_ids_dict[int(server_id)]
db_con = database_utils.verify_database(tester, utils.SERVER_GROUP,
server_id, db_id)
if db_con['data']["connected"]:
schema_info = schema_ids_dict[int(server_id)]
schema_utils.verify_schemas(tester, server_id, db_id,
schema_info[0])
data = get_synonym_config_data(server_connect_response)
data['schema'] = schema_info[1]
response = tester.post(
SYNONYM_URL + str(utils.SERVER_GROUP) + '/' + str(server_id)
+ '/' + str(db_id) + '/' + str(schema_info[0]) + '/',
data=json.dumps(data), content_type='html/json')
response_data = json.loads(response.data.decode('utf-8'))
write_synonym_id(response_data, server_id)
def verify_synonym(tester, server_id, db_id, schema_id, synonym_id):
"""This function verifies the synonym using GET API"""
get_response = tester.get(
SYNONYM_URL + str(utils.SERVER_GROUP) + '/' + str(server_id) + '/' +
str(db_id) + '/' + str(schema_id) + '/' + str(synonym_id),
content_type='html/json')
return get_response
def delete_synonym(tester):
"""This function deletes the synonyms from schema"""
all_id = utils.get_ids()
server_ids = all_id["sid"]
db_ids_dict = all_id["did"][0]
schema_ids_dict = all_id["scid"][0]
synonym_ids_dict = all_id["syid"][0]
for server_id in server_ids:
db_id = db_ids_dict[int(server_id)]
db_con = database_utils.verify_database(tester, utils.SERVER_GROUP,
server_id, db_id)
if db_con['data']["connected"]:
schema_info = schema_ids_dict[int(server_id)]
schema_response = schema_utils.verify_schemas(tester, server_id,
db_id,
schema_info[0])
schema_response = json.loads(schema_response.data.decode('utf-8'))
if len(schema_response) != 0:
synonym_id = synonym_ids_dict[int(server_id)]
get_response = verify_synonym(
tester, server_id, db_id, schema_info[0], synonym_id)
get_response_data = json.loads(
get_response.data.decode('utf-8'))
if len(get_response_data) == 0:
raise Exception("No synonym node to delete.")
del_response = tester.delete(
SYNONYM_URL + str(utils.SERVER_GROUP) + '/' +
str(server_id) + '/' + str(db_id) + '/' +
str(schema_info[0]) + '/' + str(synonym_id),
follow_redirects=True)
assert del_response.status_code == 200
del_response_data = json.loads(
del_response.data.decode('utf-8'))
assert del_response_data['success'] == 1