Add support for domains.

This commit is contained in:
Khushboo Vashi 2016-04-05 16:32:36 +01:00 committed by Dave Page
parent d1eb3fd9d5
commit 2add0155a3
40 changed files with 2573 additions and 0 deletions

View File

@ -0,0 +1,824 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Implements the Domain 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, gone
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.browser.collection import CollectionNodeModule
import pgadmin.browser.server_groups.servers.databases.schemas as schemas
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.databases.schemas.utils import \
SchemaChildModule, DataTypeReader
from pgadmin.browser.server_groups.servers.databases.utils import \
parse_sec_labels_from_db
from functools import wraps
class DomainModule(SchemaChildModule):
"""
class DomainModule(SchemaChildModule):
This class represents The Domain Module.
Methods:
-------
* __init__(*args, **kwargs)
- Initialize the Domain Module.
* get_nodes(gid, sid, did, scid)
- Generate the domain collection node.
* script_load()
- Load the module script for domain, when schema node is
initialized.
"""
NODE_TYPE = 'domain'
COLLECTION_LABEL = gettext("Domains")
def __init__(self, *args, **kwargs):
super(DomainModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
def get_nodes(self, gid, sid, did, scid):
"""
Generate the domain collection node.
"""
yield self.generate_browser_collection_node(scid)
@property
def script_load(self):
"""
Load the module script for domain, when schema node is
initialized.
"""
return schemas.SchemaModule.NODE_TYPE
blueprint = DomainModule(__name__)
class DomainView(PGChildNodeView, DataTypeReader):
"""
class DomainView
This class inherits PGChildNodeView to get the different routes for
the module. Also, inherits DataTypeReader to get data types.
The class is responsible to Create, Read, Update and Delete operations for
the Domain.
Methods:
-------
* validate_request(f):
- Works as a decorator.
Validating request on the request of create, update and modified SQL.
* module_js():
- Load JS file (domains.js) for this module.
* check_precondition(f):
- Works as a decorator.
- Checks database connection status.
- Attach connection object and template path.
* list(gid, sid, did, scid, doid):
- List the Domains.
* nodes(gid, sid, did, scid):
- Returns all the Domains to generate Nodes in the browser.
* properties(gid, sid, did, scid, doid):
- Returns the Domain properties.
* get_collations(gid, sid, did, scid, doid=None):
- Returns Collations.
* create(gid, sid, did, scid):
- Creates a new Domain object.
* update(gid, sid, did, scid, doid):
- Updates the Domain object.
* delete(gid, sid, did, scid, doid):
- Drops the Domain object.
* sql(gid, sid, did, scid, doid=None):
- Returns the SQL for the Domain object.
* msql(gid, sid, did, scid, doid=None):
- Returns the modified SQL.
* get_sql(gid, sid, data, scid, doid=None):
- Generates the SQL statements to create/update the Domain object.
* dependents(gid, sid, did, scid, doid):
- Returns the dependents for the Domain object.
* dependencies(gid, sid, did, scid, doid):
- Returns the dependencies for the Domain object.
* types(gid, sid, did, scid, fnid=None):
- Returns Data Types.
"""
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': 'int', 'id': 'doid'}
]
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_types': [{'get': 'types'}, {'get': 'types'}],
'get_collations': [
{'get': 'get_collations'},
{'get': 'get_collations'}
]
})
def validate_request(f):
"""
Works as a decorator.
Validating request on the request of create, update and modified SQL.
Required Args:
name: Name of the Domain
owner: Domain Owner
basensp: Schema Name
basetype: Data Type of the Domain
Above both the arguments will not be validated in the update action.
"""
@wraps(f)
def wrap(self, **kwargs):
data = {}
if request.data:
req = json.loads(request.data.decode())
else:
req = request.args or request.form
if 'doid' not in kwargs:
required_args = [
'name',
'basetype'
]
for arg in required_args:
if arg not in req or req[arg] == '':
return make_json_response(
status=410,
success=0,
errormsg=gettext(
"Couldn't find the required parameter \
(%s)." % arg
)
)
try:
list_params = []
if request.method == 'GET':
list_params = ['constraints', 'seclabels']
for key in req:
if key in list_params and req[key] != '' \
and req[key] is not None:
# Coverts string into python list as expected.
data[key] = json.loads(req[key])
elif key == 'typnotnull':
data[key] = True if req[key] == 'true' or req[key] is\
True else\
(False if req[key] == 'false' or req[key] is
False else '')
else:
data[key] = req[key]
except Exception as e:
return internal_server_error(errormsg=str(e))
self.request = data
return f(self, **kwargs)
return wrap
def module_js(self):
"""
Load JS file (domains.js) for this module.
"""
return make_response(
render_template(
"domains/js/domains.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
Works as a decorator.
Checks database connection status.
Attach connection object and template path.
"""
@wraps(f)
def wrap(*args, **kwargs):
self = args[0]
driver = get_driver(PG_DEFAULT_DRIVER)
self.manager = driver.connection_manager(kwargs['sid'])
# Get database connection
self.conn = self.manager.connection(did=kwargs['did'])
self.qtIdent = driver.qtIdent
if not self.conn.connected():
return precondition_required(
gettext("Connection to the server has been lost!")
)
ver = self.manager.version
server_type = self.manager.server_type
# we will set template path for sql scripts
if ver >= 90200:
self.template_path = 'domains/sql/9.2_plus'
elif ver >= 90100:
self.template_path = 'domains/sql/9.1_plus'
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid, did, scid):
"""
List the Domains.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
"""
SQL = render_template("/".join([self.template_path, 'node.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):
"""
Returns all the Domains to generate Nodes in the browser.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
"""
res = []
SQL = render_template("/".join([self.template_path, 'node.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['oid'],
scid,
row['name'],
icon="icon-domain"
))
return make_json_response(
data=res,
status=200
)
@check_precondition
def properties(self, gid, sid, did, scid, doid):
"""
Returns the Domain properties.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
SQL = render_template("/".join([self.template_path, 'properties.sql']),
scid=scid, doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(gettext("""
Could not find the domain in the database.
It may have been removed by another user or
shifted to the another schema.
"""))
data = res['rows'][0]
# Get Type Length and Precision
data.update(self._parse_type(data['fulltype']))
# Get Domain Constraints
SQL = render_template("/".join([self.template_path,
'get_constraints.sql']),
doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
data['constraints'] = res['rows']
# Get formatted Security Labels
if 'seclabels' in data:
data.update(parse_sec_labels_from_db(data['seclabels']))
# Set System Domain Status
data['sysdomain'] = False
if doid <= self.manager.db_info[did]['datlastsysoid']:
data['sysdomain'] = True
return ajax_response(
response=data,
status=200
)
def _parse_type(self, basetype):
"""
Returns Type and Data Type from the basetype.
"""
typ_len = ''
typ_precision = ''
# The Length and the precision of the Datatype should be separate.
# The Format we getting from database is: numeric(1,1)
# So, we need to separate Length: 1, Precision: 1
if basetype != '' and basetype.find("(") > 0:
substr = basetype[basetype.find("(") + 1:len(
basetype) - 1]
typlen = substr.split(",")
if len(typlen) > 1:
typ_len = typlen[0]
typ_precision = typlen[1]
else:
typ_len = typlen
typ_precision = ''
return {'typlen': typ_len, 'precision': typ_precision}
@check_precondition
def get_collations(self, gid, sid, did, scid, doid=None):
"""
Returns Collations.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
res = [{'label': '', 'value': ''}]
try:
SQL = render_template("/".join([self.template_path,
'get_collations.sql']))
status, rset = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=res)
for row in rset['rows']:
res.append({'label': row['copy_collation'],
'value': row['copy_collation']}
)
return make_json_response(
data=res,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def types(self, gid, sid, did, scid, doid=None):
"""
Returns the Data Types.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
fnid: Function Id
"""
condition = """typisdefined AND typtype IN ('b', 'c', 'd', 'e', 'r')
AND NOT EXISTS (SELECT 1 FROM pg_class WHERE relnamespace=typnamespace
AND relname = typname AND relkind != 'c') AND
(typname NOT LIKE '_%' OR NOT EXISTS (SELECT 1 FROM pg_class WHERE
relnamespace=typnamespace AND relname = substring(typname FROM 2)::name
AND relkind != 'c'))"""
if self.blueprint.show_system_objects:
condition += " AND nsp.nspname != 'information_schema'"
# Get Types
status, types = self.get_types(self.conn, condition)
if not status:
return internal_server_error(errormsg=types)
return make_json_response(
data=types,
status=200
)
@check_precondition
@validate_request
def create(self, gid, sid, did, scid):
"""
Creates a new Domain object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
Required Args:
name: Domain Name
owner: Owner Name
basensp: Schema Name
basetype: Domain Base Type
Returns:
Domain object in json format.
"""
data = self.request
try:
status, SQL = self.get_sql(gid, sid, data, scid)
if not status:
return internal_server_error(errormsg=SQL)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
# We need oid to to add object in tree at browser, below sql will
# gives the same
SQL = render_template("/".join([self.template_path,
'get_oid.sql']),
basensp=data['basensp'],
name=data['name'])
status, res = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=res)
doid, scid = res['rows'][0]
return jsonify(
node=self.blueprint.generate_browser_node(
doid,
scid,
data['name'],
icon="icon-domain"
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, scid, doid):
"""
Drops the Domain object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
try:
SQL = render_template("/".join([self.template_path,
'delete.sql']),
scid=scid, doid=doid)
status, res = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=res)
name, basensp = res['rows'][0]
SQL = render_template("/".join([self.template_path,
'delete.sql']),
name=name, basensp=basensp, cascade=cascade)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Domain dropped"),
data={
'id': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
@validate_request
def update(self, gid, sid, did, scid, doid):
"""
Updates the Domain object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
status, SQL = self.get_sql(gid, sid, self.request, scid, doid)
if not status:
return internal_server_error(errormsg=SQL)
try:
if SQL:
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
# Get Schema Id
SQL = render_template("/".join([self.template_path,
'get_oid.sql']),
doid=doid)
status, res = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=res)
scid = res['rows'][0]['scid']
return make_json_response(
success=1,
info="Domain updated",
data={
'id': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
else:
return make_json_response(
success=1,
info="Nothing to update",
data={
'id': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def sql(self, gid, sid, did, scid, doid=None):
"""
Returns the SQL for the Domain object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return False, internal_server_error(errormsg=res)
data = res['rows'][0]
# Get Type Length and Precision
data.update(self._parse_type(data['fulltype']))
# Get Domain Constraints
SQL = render_template("/".join([self.template_path,
'get_constraints.sql']),
doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
data['constraints'] = res['rows']
SQL = render_template("/".join([self.template_path,
'create.sql']), data=data)
sql_header = """-- DOMAIN: {0}
-- DROP DOMAIN {0};
""".format(data['basensp'] + '.' + data['name'])
SQL = sql_header + SQL
return ajax_response(response=SQL.strip('\n'))
@check_precondition
@validate_request
def msql(self, gid, sid, did, scid, doid=None):
"""
Returns the modified SQL.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
Required Args:
name: Domain Name
owner: Owner Name
basensp: Schema Name
basetype: Domain Base Type
Returns:
SQL statements to create/update the Domain.
"""
status, SQL = self.get_sql(gid, sid, self.request, scid, doid)
if SQL:
return make_json_response(
data=SQL,
status=200
)
else:
return SQL
def get_sql(self, gid, sid, data, scid, doid=None):
"""
Generates the SQL statements to create/update the Domain.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
try:
if doid is not None:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
scid=scid, doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return False, internal_server_error(errormsg=res)
old_data = res['rows'][0]
# Get Domain Constraints
SQL = render_template("/".join([self.template_path,
'get_constraints.sql']),
doid=doid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
con_data = {}
for c in res['rows']:
con_data[c['conoid']] = c
old_data['constraints'] = con_data
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data)
else:
SQL = render_template("/".join([self.template_path,
'create.sql']),
data=data)
return True, SQL.strip('\n')
except Exception as e:
return False, e
@check_precondition
def dependents(self, gid, sid, did, scid, doid):
"""
This function get the dependents and return ajax response
for the Domain node.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
dependents_result = self.get_dependents(self.conn, doid)
return ajax_response(
response=dependents_result,
status=200
)
@check_precondition
def dependencies(self, gid, sid, did, scid, doid):
"""
This function get the dependencies and return ajax response
for the Domain node.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
dependencies_result = self.get_dependencies(self.conn, doid)
return ajax_response(
response=dependencies_result,
status=200
)
DomainView.register_node_view(blueprint)

View File

@ -0,0 +1,691 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Implements the Domain Constraint Module."""
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.databases.schemas.domains \
as domains
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 DomainConstraintModule(CollectionNodeModule):
"""
class DomainConstraintModule(CollectionNodeModule):
This class represents The Domain Constraint Module.
Methods:
-------
* __init__(*args, **kwargs)
- Initialize the Domain Constraint Module.
* get_nodes(gid, sid, did, scid)
- Generate the Domain Constraint collection node.
* node_inode(gid, sid, did, scid)
- Returns Domain Constraint node as leaf node.
* script_load()
- Load the module script for the Domain Constraint, when any of the
Domain node is initialized.
"""
NODE_TYPE = 'domain-constraints'
COLLECTION_LABEL = gettext("Domain Constraints")
def __init__(self, *args, **kwargs):
super(DomainConstraintModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
def get_nodes(self, gid, sid, did, scid, doid):
"""
Generate the Domain Constraint collection node.
"""
yield self.generate_browser_collection_node(doid)
@property
def node_inode(self):
"""
Returns Domain Constraint node as leaf node.
"""
return False
@property
def script_load(self):
"""
Load the module script for the Domain Constraint, when any of the
Domain node is initialized.
"""
return domains.DomainModule.NODE_TYPE
@property
def csssnippets(self):
"""
Returns a snippet of css to include in the page
"""
return [
render_template(
"domain-constraints/css/domain-constraints.css",
node_type=self.node_type
)
]
blueprint = DomainConstraintModule(__name__)
class DomainConstraintView(PGChildNodeView):
"""
class DomainConstraintView(PGChildNodeView):
This class inherits PGChildNodeView to get the different routes for
the module.
The class is responsible to Create, Read, Update and Delete operations for
the Domain Constraint.
Methods:
-------
* module_js():
- Load JS file (domain-constraints.js) for this module.
* check_precondition(f):
- Works as a decorator.
- Checks database connection status.
- Attach connection object and template path.
* list(gid, sid, did, scid, doid):
- List the Domain Constraints.
* nodes(gid, sid, did, scid):
- Returns all the Domain Constraints to generate Nodes in the browser.
* properties(gid, sid, did, scid, doid):
- Returns the Domain Constraint properties.
* create(gid, sid, did, scid):
- Creates a new Domain Constraint object.
* update(gid, sid, did, scid, doid):
- Updates the Domain Constraint object.
* delete(gid, sid, did, scid, doid):
- Drops the Domain Constraint object.
* sql(gid, sid, did, scid, doid=None):
- Returns the SQL for the Domain Constraint object.
* msql(gid, sid, did, scid, doid=None):
- Returns the modified SQL.
* get_sql(gid, sid, data, scid, doid=None):
- Generates the SQL statements to create/update the Domain Constraint.
object.
* dependents(gid, sid, did, scid, doid, coid):
- Returns the dependents for the Domain Constraint object.
* dependencies(gid, sid, did, scid, doid, coid):
- Returns the dependencies for the Domain Constraint object.
"""
node_type = blueprint.node_type
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'},
{'type': 'int', 'id': 'scid'},
{'type': 'int', 'id': 'doid'}
]
ids = [
{'type': 'int', 'id': 'coid'}
]
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'}]
})
def validate_request(f):
"""
Works as a decorator.
Validating request on the request of create, update and modified SQL.
Required Args:
name: Name of the Domain Constraint
consrc: Check Constraint Definition
Above both the arguments will not be validated in the update action.
"""
@wraps(f)
def wrap(self, **kwargs):
data = {}
if request.data:
req = json.loads(request.data.decode())
else:
req = request.args or request.form
if 'coid' not in kwargs:
required_args = [
'name',
'consrc'
]
for arg in required_args:
if arg not in req or req[arg] == '':
return make_json_response(
status=410,
success=0,
errormsg=gettext(
"Couldn't find the required parameter \
(%s)." % arg
)
)
try:
for key in req:
if key == 'convalidated':
data[key] = True if (req[key] == 'true' or req[key] is
True) else False
else:
data[key] = req[key]
except Exception as e:
return internal_server_error(errormsg=str(e))
self.request = data
return f(self, **kwargs)
return wrap
def module_js(self):
"""
Load JS file (domain-constraints.js) for this module.
"""
return make_response(
render_template(
"domain-constraints/js/domain-constraints.js",
_=gettext
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(f):
"""
Works as a decorator.
Checks database connection status.
Attach connection object and template path.
"""
@wraps(f)
def wrap(*args, **kwargs):
self = args[0]
driver = get_driver(PG_DEFAULT_DRIVER)
self.manager = driver.connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
self.qtIdent = driver.qtIdent
# 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 >= 90200:
self.template_path = 'domain-constraints/sql/9.2_plus'
elif ver >= 90100:
self.template_path = 'domain-constraints/sql/9.1_plus'
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid, did, scid, doid):
"""
List the Domain Constraints.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid)
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, doid):
"""
Returns all the Domain Constraints.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
"""
res = []
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid)
status, rset = self.conn.execute_2darray(SQL)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
if 'convalidated' not in row:
icon = 'icon-domain-constraints'
elif row['convalidated']:
icon = 'icon-domain-constraints'
else:
icon = 'icon-domain-constraints-bad'
res.append(
self.blueprint.generate_browser_node(
row['oid'],
doid,
row['name'],
icon=icon
))
return make_json_response(
data=res,
status=200
)
@check_precondition
def properties(self, gid, sid, did, scid, doid, coid):
"""
Returns the Domain Constraints property.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid, coid=coid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
data = res['rows'][0]
return ajax_response(
response=data,
status=200
)
@check_precondition
@validate_request
def create(self, gid, sid, did, scid, doid):
"""
Creates a new Domain Constraint object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
Returns:
Domain Constraint object in json format.
"""
data = self.request
try:
status, SQL = self.get_sql(gid, sid, data, scid, doid)
if not status:
return internal_server_error(errormsg=SQL)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
# Get the recently added constraints oid
SQL = render_template("/".join([self.template_path,
'get_oid.sql']),
doid=doid, name=data['name'])
status, coid = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=coid)
if 'convalidated' not in data:
icon = 'icon-domain-constraints'
elif 'convalidated' in data and data['convalidated']:
icon = 'icon-domain-constraints'
else:
icon = 'icon-domain-constraints-bad'
return jsonify(
node=self.blueprint.generate_browser_node(
coid,
doid,
data['name'],
icon=icon
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, scid, doid, coid):
"""
Drops the Domain Constraint object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
try:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid, coid=coid)
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,
'delete.sql']),
data=data)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Domain Constraint dropped"),
data={
'id': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
@validate_request
def update(self, gid, sid, did, scid, doid, coid):
"""
Updates the Domain Constraint object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
data = self.request
status, SQL = self.get_sql(gid, sid, data, scid, doid, coid)
try:
if SQL and status:
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
if 'convalidated' in data and data['convalidated']:
icon = 'icon-domain-constraints'
elif 'convalidated' in data and not data['convalidated']:
icon = 'icon-domain-constraints-bad'
else:
icon = ''
return make_json_response(
success=1,
info="Domain Constraint updated",
data={
'id': coid,
'doid': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did,
'icon': icon
}
)
else:
return make_json_response(
success=1,
info="Nothing to update",
data={
'id': coid,
'doid': doid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def sql(self, gid, sid, did, scid, doid, coid=None):
"""
Returns the SQL for the Domain Constraint object.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
# Get Schema and Domain.
domain, schema = self._get_domain(doid)
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid, coid=coid)
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, domain=domain, schema=schema)
sql_header = """-- CHECK: {1}.{0}
-- ALTER DOMAIN {1} DROP CONSTRAINT {0};
""".format(data['name'], schema + '.' + domain)
SQL = sql_header + SQL
return ajax_response(response=SQL)
@check_precondition
@validate_request
def msql(self, gid, sid, did, scid, doid, coid=None):
"""
Returns the modified SQL.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
Returns:
Domain Constraint object in json format.
"""
data = self.request
status, SQL = self.get_sql(gid, sid, data, scid, doid, coid)
if status and SQL:
return make_json_response(
data=SQL,
status=200
)
else:
return SQL
def get_sql(self, gid, sid, data, scid, doid, coid=None):
"""
Generates the SQL statements to create/update the Domain Constraint.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
try:
if coid is not None:
SQL = render_template("/".join([self.template_path,
'properties.sql']),
doid=doid, coid=coid)
status, res = self.conn.execute_dict(SQL)
if not status:
return False, internal_server_error(errormsg=res)
old_data = res['rows'][0]
SQL = render_template(
"/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data, conn=self.conn
)
else:
domain, schema = self._get_domain(doid)
SQL = render_template("/".join([self.template_path,
'create.sql']),
data=data, domain=domain, schema=schema)
return True, SQL.strip('\n')
except Exception as e:
return False, internal_server_error(errormsg=str(e))
def _get_domain(self, doid):
"""
Returns Domain and Schema name.
Args:
doid: Domain Id
"""
SQL = render_template("/".join([self.template_path,
'get_domain.sql']),
doid=doid)
status, res = self.conn.execute_2darray(SQL)
if not status:
return False, internal_server_error(errormsg=res)
return res['rows'][0]
@check_precondition
def dependents(self, gid, sid, did, scid, doid, coid):
"""
This function get the dependents and return ajax response
for the Domain Constraint node.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
dependents_result = self.get_dependents(self.conn, coid)
return ajax_response(
response=dependents_result,
status=200
)
@check_precondition
def dependencies(self, gid, sid, did, scid, doid, coid):
"""
This function get the dependencies and return ajax response
for the Domain Constraint node.
Args:
gid: Server Group Id
sid: Server Id
did: Database Id
scid: Schema Id
doid: Domain Id
coid: Domain Constraint Id
"""
dependencies_result = self.get_dependencies(self.conn, coid)
return ajax_response(
response=dependencies_result,
status=200
)
DomainConstraintView.register_node_view(blueprint)

View File

@ -0,0 +1,23 @@
.icon-coll-domain-constraints {
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-domain-constraints.png' )}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}
.icon-check-bad, .icon-domain-constraints-bad {
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints-bad.png' )}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}
.icon-check, .icon-domain-constraints {
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain-constraints.png' )}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}

View File

@ -0,0 +1,142 @@
// Domain Constraint Module: Collection and Node
define(
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
function($, _, S, pgAdmin, pgBrowser, alertify) {
// Define Domain Constraint Collection Node
if (!pgBrowser.Nodes['coll-domain-constraints']) {
var domain_constraints = pgAdmin.Browser.Nodes['coll-domain-constraints'] =
pgAdmin.Browser.Collection.extend({
node: 'domain-constraints',
label: '{{ _('Domain Constraints') }}',
type: 'coll-domain-constraints',
columns: ['name', 'description']
});
};
// Domain Constraint Node
if (!pgBrowser.Nodes['domain-constraints']) {
pgAdmin.Browser.Nodes['domain-constraints'] = pgBrowser.Node.extend({
type: 'domain-constraints',
label: '{{ _('Domain Constraints') }}',
collection_type: 'coll-domain-constraints',
hasSQL: true,
hasDepends: true,
parent_type: ['domain'],
Init: function() {
// Avoid mulitple registration of menus
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_domain_on_coll', node: 'coll-domain-constraints', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}',
icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_domain-constraints', node: 'domain-constraints', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}',
icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_domain-constraints', node: 'domain', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 5, label: '{{ _('Domain Constraint...') }}',
icon: 'wcTabIcon icon-domain-constraints', data: {action: 'create', check: false},
enable: 'canCreate'
}
]);
},
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
name: undefined,
oid: undefined,
description: undefined,
consrc: undefined,
connoinherit: undefined,
convalidated: true
},
// Domain Constraint Schema
schema: [{
id: 'name', label: '{{ _('Name') }}', type:'text', cell:'string',
disabled: 'isDisabled'
},{
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
type: 'text' , mode: ['properties']
},{
id: 'description', label: '{{ _('Comment') }}', type: 'multiline', cell:
'string', mode: ['properties', 'create', 'edit'], min_version: 90500,
},{
id: 'consrc', label: '{{ _('Check') }}', type: 'multiline', cel:
'string', group: '{{ _('Definition') }}', mode: ['properties',
'create', 'edit'], disabled: function(m) { return !m.isNew(); }
},{
id: 'connoinherit', label: '{{ _('No Inherit') }}', type:
'switch', cell: 'boolean', group: '{{ _('Definition') }}', mode:
['properties', 'create', 'edit'], disabled: 'isDisabled',
visible: false
},{
id: 'convalidated', label: "{{ _("Validate?") }}", type: 'switch', cell:
'boolean', group: '{{ _('Definition') }}', min_version: 90200,
disabled: function(m) {
if (!m.isNew()) {
var server = this.node_info.server;
if (server.version < 90200) { return true;
}
else if(m.get('convalidated')) {
return true;
}
return false;
}
return false;
},
mode: ['properties', 'create', 'edit']
}],
// Client Side Validation
validate: function() {
var err = {},
errmsg;
if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
err['name'] = '{{ _('Name can not be empty!') }}';
errmsg = errmsg || err['name'];
}
if (_.isUndefined(this.get('consrc')) || String(this.get('consrc')).replace(/^\s+|\s+$/g, '') == '') {
err['consrc'] = '{{ _('Check can not be empty!') }}';
errmsg = errmsg || err['consrc'];
}
this.errorModel.clear().set(err);
if (_.size(err)) {
this.trigger('on-status', {msg: errmsg});
return errmsg;
}
return null;
},
isDisabled: function(m){
if (!m.isNew()) {
var server = this.node_info.server;
if (server.version < 90200)
{
return true;
}
}
return false;
}
}),
});
}
return pgBrowser.Nodes['domain'];
});

View File

@ -0,0 +1,3 @@
{% if data and schema and domain %}
ALTER DOMAIN {{ conn|qtIdent(schema, domain) }}
ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }});{% endif -%}

View File

@ -0,0 +1,4 @@
{% if data %}
ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }}
DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
{% endif %}

View File

@ -0,0 +1,8 @@
SELECT
d.typname as domain, bn.nspname as schema
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.oid = {{doid}};

View File

@ -0,0 +1,7 @@
SELECT
oid, conname as name
FROM
pg_constraint
WHERE
contypid = {{doid}}::oid
AND conname={{ name|qtLiteral }};

View File

@ -0,0 +1,14 @@
SELECT
c.oid, conname AS name, typname AS relname, nspname,
regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc
FROM
pg_constraint c
JOIN
pg_type t ON t.oid=contypid
JOIN
pg_namespace nl ON nl.oid=typnamespace
WHERE
contype = 'c' AND contypid = {{doid}}::oid
{% if coid %}
AND c.oid = {{ coid }}
{% endif %}

View File

@ -0,0 +1,3 @@
{% if data.name %}
ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif %}

View File

@ -0,0 +1,10 @@
{% if data and schema and domain %}
ALTER DOMAIN {{ conn|qtIdent(schema, domain) }}
ADD CONSTRAINT {{ conn|qtIdent(data.name) }} CHECK ({{ data.consrc }}){% if not data.convalidated %}
NOT VALID{% endif %};{% if data.description %}
COMMENT ON CONSTRAINT {{ conn|qtIdent(data.name) }} ON DOMAIN {{ conn|qtIdent(schema, domain) }}
IS '{{ data.description }}';{% endif %}
{% endif %}

View File

@ -0,0 +1,4 @@
{% if data %}
ALTER DOMAIN {{ conn|qtIdent(data.nspname, data.relname) }}
DROP CONSTRAINT {{ conn|qtIdent(data.name) }};
{% endif %}

View File

@ -0,0 +1,8 @@
SELECT
d.typname as domain, bn.nspname as schema
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.oid = {{doid}};

View File

@ -0,0 +1,7 @@
SELECT
oid, conname as name
FROM
pg_constraint
WHERE
contypid = {{doid}}::oid
AND conname={{ name|qtLiteral }};

View File

@ -0,0 +1,5 @@
SELECT
typcategory
FROM
pg_type
WHERE typname = {{datatype}};

View File

@ -0,0 +1,17 @@
SELECT
c.oid, conname AS name, typname AS relname, nspname, description,
regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') AS consrc,
connoinherit, convalidated, convalidated AS convalidated_p
FROM
pg_constraint c
JOIN
pg_type t ON t.oid=contypid
JOIN
pg_namespace nl ON nl.oid=typnamespace
LEFT OUTER JOIN
pg_description des ON (des.objoid=c.oid AND des.classoid='pg_constraint'::regclass)
WHERE
contype = 'c' AND contypid = {{doid}}::oid
{% if coid %}
AND c.oid = {{ coid }}
{% endif %}

View File

@ -0,0 +1,13 @@
{% set name = o_data.name %}
{% if data.name %}
{% set name = data.name %}
ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
RENAME CONSTRAINT {{ conn|qtIdent(o_data.name) }} TO {{ conn|qtIdent(data.name) }};{% endif -%}{% if data.convalidated %}
ALTER DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
VALIDATE CONSTRAINT {{ conn|qtIdent(name) }};{% endif -%}{% if data.description %}
COMMENT ON CONSTRAINT {{ conn|qtIdent(name) }} ON DOMAIN {{ conn|qtIdent(o_data.nspname, o_data.relname) }}
IS '{{ data.description }}';{% endif %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,362 @@
// Domain Module: Collection and Node.
define(
['jquery', 'underscore', 'underscore.string', 'pgadmin',
'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
function($, _, S, pgAdmin, pgBrowser, alertify) {
// Define Domain Collection Node
if (!pgBrowser.Nodes['coll-domain']) {
var domains = pgAdmin.Browser.Nodes['coll-domain'] =
pgAdmin.Browser.Collection.extend({
node: 'domain',
label: '{{ _('Domains') }}',
type: 'coll-domain',
columns: ['name', 'owner', 'description']
});
};
// Security Model
var SecurityModel = Backform.SecurityModel = pgAdmin.Browser.Node.Model.extend({
defaults: {
provider: null,
security_label: null
},
schema: [{
id: 'provider', label: '{{ _('Provider') }}',
type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
},{
id: 'security_label', label: '{{ _('Security Label') }}',
type: 'text', editable: true, cellHeaderClasses:'width_percent_50'
}],
validate: function() {
var err = {},
errmsg = null;
if (_.isUndefined(this.get('security_label')) ||
_.isNull(this.get('security_label')) ||
String(this.get('security_label')).replace(/^\s+|\s+$/g, '') == '') {
errmsg = '{{ _('Please specify the value for all the security providers.')}}';
this.errorModel.set('security_label', errmsg);
return errmsg;
} else {
this.errorModel.unset('security_label');
}
return null;
}
});
// Constraint Model
var ConstraintModel = pgAdmin.Browser.Node.Model.extend({
idAttribute: 'conoid',
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
if (!isNew) {
this.convalidated_default = this.get('convalidated')
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
defaults: {
conoid: undefined,
conname: undefined,
consrc: undefined,
convalidated: true
},
convalidated_default: true,
schema: [{
id: 'conoid', type: 'text', cell: 'string', visible: false
},{
id: 'conname', label: '{{ _('Name') }}', type: 'text', cell: 'string',
cellHeaderClasses: 'width_percent_40',
editable: function(m) {
if (_.isUndefined(m.isNew)) { return true; }
if (!m.isNew()) {
var server = this.get('node_info').server;
if (server.version < 90200) { return false;
}
}
return true;
}
},{
id: 'consrc', label: '{{ _('Check') }}', type: 'multiline',
cell: Backgrid.Extension.TextareaCell, group: '{{ _('Definition') }}',
cellHeaderClasses: 'width_percent_60', editable: function(m) {
return _.isUndefined(m.isNew) ? true : m.isNew();
}
},{
id: 'convalidated', label: '{{ _('Validate?') }}', type: 'switch', cell:
'boolean', group: '{{ _('Definition') }}',
editable: function(m) {
var server = this.get('node_info').server;
if (server.version < 90200) { return false;
}
if (_.isUndefined(m.isNew)) { return true; }
if (!m.isNew()) {
if(m.get('convalidated') && m.convalidated_default) {
return false;
}
return true;
}
return true;
}
}],
toJSON: Backbone.Model.prototype.toJSON,
validate: function() {
return null;
}
});
// Domain Node
if (!pgBrowser.Nodes['domain']) {
pgAdmin.Browser.Nodes['domain'] = pgBrowser.Node.extend({
type: 'domain',
label: '{{ _('Domain') }}',
collection_type: 'coll-domain',
hasSQL: true,
hasDepends: true,
parent_type: ['schema'],
Init: function() {
// Avoid mulitple registration of menus
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_domain_on_coll', node: 'coll-domain', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Domain...') }}',
icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_domain', node: 'domain', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Domain...') }}',
icon: 'wcTabIcon icon-domain', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_domain', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Domain...') }}',
icon: 'wcTabIcon icon-domain', data: {action: 'create', check: false},
enable: 'canCreate'
}
]);
},
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
// Domain Node Model
model: pgAdmin.Browser.Node.Model.extend({
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
if (isNew) {
// Set Selected Schema
schema = args.node_info.schema.label
this.set({'basensp': schema}, {silent: true});
// Set Current User
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
this.set({'owner': userInfo.name}, {silent: true});
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
defaults: {
name: undefined,
oid: undefined,
owner: undefined,
basensp: undefined,
description: undefined,
basetype: undefined,
typlen: undefined,
precision: undefined,
typdefault: undefined,
typnotnull: undefined,
sysdomain: undefined,
collname: undefined,
constraints: [],
seclabels: []
},
type_options: undefined,
// Domain Schema
schema: [{
id: 'name', label: '{{ _('Name') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit']
},{
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
type: 'text' , mode: ['properties']
},{
id: 'owner', label:'{{ _('Owner') }}', cell: 'string', control: Backform.NodeListByNameControl,
node: 'role', type: 'text', mode: ['edit', 'create', 'properties']
},{
id: 'basensp', label:'{{ _('Schema') }}', cell: 'node-list-by-name',
control: 'node-list-by-name', cache_level: 'database', type: 'text',
node: 'schema', mode: ['create', 'edit']
},{
id: 'sysdomain', label:'{{ _('System domain?') }}', cell: 'boolean',
type: 'switch', mode: ['properties'],
options: {
'onText': 'Yes', 'offText': 'No',
'onColor': 'success', 'offColor': 'primary',
'size': 'small'
}
},{
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
type: 'multiline'
},{
id: 'basetype', label:'{{ _('Base type') }}', cell: 'string', control: 'node-ajax-options',
type: 'text', mode:['properties', 'create', 'edit'], group: '{{ _('Definition') }}', url: 'get_types',
disabled: function(m) { return !m.isNew(); }, first_empty: true,
transform: function(d){
this.model.type_options = d;
return d;
}
},{
id: 'typlen', label:'{{ _('Length') }}', cell: 'string',
type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'],
disabled: function(m) {
// We will store type from selected from combobox
if (!m.isNew()) {
return true;
}
var of_type = m.get('basetype');
if(m.type_options) {
// iterating over all the types
_.each(m.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if length is allowed for selected type
if(o.length)
{
// set the values in model
m.set('is_tlength', true, {silent: true});
m.set('min_val', o.min_val, {silent: true});
m.set('max_val', o.max_val, {silent: true});
}
}
});
}
return !m.get('is_tlength');
}
},{
id: 'precision', label:'{{ _('Precision') }}', cell: 'string',
type: 'text', group: '{{ _('Definition') }}', deps: ['basetype'],
disabled: function(m) {
// We will store type from selected from combobox
if (!m.isNew()) {
return true;
}
var of_type = m.get('basetype');
if(m.type_options) {
// iterating over all the types
_.each(m.type_options, function(o) {
// if type from selected from combobox matches in options
if ( of_type == o.value ) {
// if precession is allowed for selected type
if(o.precision)
{
// set the values in model
m.set('is_precision', true, {silent: true});
m.set('min_val', o.min_val, {silent: true});
m.set('max_val', o.max_val, {silent: true});
}
}
});
}
return !m.get('is_precision');
}
},{
id: 'typdefault', label:'{{ _('Default') }}', cell: 'string',
type: 'text', group: '{{ _('Definition') }}',
placeholder: "Enter an expression or a value."
},{
id: 'typnotnull', label:'{{ _('Not Null?') }}', cell: 'boolean',
type: 'switch', group: '{{ _('Definition') }}',
options: {
'onText': 'Yes', 'offText': 'No',
'onColor': 'success', 'offColor': 'primary',
'size': 'small'
}
},{
id: 'collname', label:'{{ _('Collation') }}', cell: 'string', control: 'node-ajax-options',
type: 'text', group: '{{ _('Definition') }}', url: 'get_collations', disabled: function(m) {
return !m.isNew();
}
},{
id: 'constraints', label:'{{ _('Constraints') }}', cell: 'string',
type: 'collection', group: '{{ _('Constraints') }}', mode: ['edit', 'create'],
model: ConstraintModel, canAdd: true, canDelete: true,
canEdit: false, columns: ['conname','consrc', 'convalidated']
},{
id: 'seclabels', label: '{{ _('Security Labels') }}',
model: SecurityModel, type: 'collection',
group: '{{ _('Security') }}', mode: ['edit', 'create'],
min_version: 90100, canAdd: true,
canEdit: false, canDelete: true,
control: 'unique-col-collection', uniqueCol : ['provider']
}
],
validate: function() // Client Side Validation
{
var err = {},
errmsg,
seclabels = this.get('seclabels');
if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
err['name'] = '{{ _('Name can not be empty!') }}';
errmsg = errmsg || err['name'];
}
if (_.isUndefined(this.get('basetype')) || String(this.get('basetype')).replace(/^\s+|\s+$/g, '') == '') {
err['basetype'] = '{{ _('Base Type can not be empty!') }}';
errmsg = errmsg || err['basetype'];
}
this.errorModel.clear().set(err);
return null;
}
}),
canCreate: function(itemData, item, data) {
//If check is false then , we will allow create menu
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 domain
if (_.indexOf(['schema'], d._type) > -1)
return true;
if ('coll-domain' == 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 do not want to allow create menu
return true;
},
isDisabled: function(m){
if (!m.isNew()) {
var server = this.node_info.server;
if (server.version < 90200)
{
return false;
}
}
return true;
}
});
}
return pgBrowser.Nodes['domain'];
});

View File

@ -0,0 +1,30 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% if data %}
CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname %}
COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %}
DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %}
NOT NULL{% endif %}{% if data.constraints %}{% for c in data.constraints %}{% if c.conname and c.consrc %}
CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% endif -%}
{% endfor -%}
{% endif -%};
{% if data.owner %}
ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}{% if data.description %}
COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
IS '{{ data.description }}';{% endif -%}{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.security_label and r.provider %}
{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%}
{% endfor -%}
{% endif -%}
{% endif -%}

View File

@ -0,0 +1,16 @@
{% if scid and doid %}
SELECT
d.typname as name, bn.nspname as basensp
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.typnamespace = {{scid}}::oid
AND
d.oid={{doid}}::int;
{% endif %}
{% if name %}
DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %};
{% endif %}

View File

@ -0,0 +1,10 @@
SELECT --nspname, collname,
CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
concat(nspname, '."', collname,'"')
ELSE '' END AS copy_collation
FROM
pg_collation c, pg_namespace n
WHERE
c.collnamespace=n.oid
ORDER BY
nspname, collname;

View File

@ -0,0 +1,15 @@
SELECT
'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description,
regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as cons
FROM
pg_constraint c
JOIN
pg_type t ON t.oid=contypid
JOIN
pg_namespace nl ON nl.oid=typnamespace
LEFT OUTER JOIN
pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass)
WHERE
contype = 'c'
AND contypid = {{doid}}::oid
ORDER BY conname;

View File

@ -0,0 +1,18 @@
{% if doid %}
SELECT
d.typnamespace as scid
FROM
pg_type d
WHERE
d.oid={{ doid }}::oid;
{% else %}
SELECT
d.oid, d.typnamespace
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
bn.nspname = {{ basensp|qtLiteral }}
AND d.typname={{ name|qtLiteral }};
{% endif %}

View File

@ -0,0 +1,13 @@
SELECT
d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner,
bn.nspname as basensp
FROM
pg_type d
JOIN
pg_type b ON b.oid = d.typbasetype
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.typnamespace = {{scid}}::oid
ORDER BY
d.typname;

View File

@ -0,0 +1,35 @@
SELECT
d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype,
pg_get_userbyid(d.typowner) as owner,
c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype,
CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN
concat(cn.nspname, '."', c.collname,'"')
ELSE '' END AS collname,
d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp,
description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup,
(SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup,
(SELECT
array_agg(provider || '=' || label)
FROM
pg_seclabel sl1
WHERE
sl1.objoid=d.oid) AS seclabels
FROM
pg_type d
JOIN
pg_type b ON b.oid = d.typbasetype
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
LEFT OUTER JOIN
pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass)
LEFT OUTER JOIN
pg_collation c ON d.typcollation=c.oid
LEFT OUTER JOIN
pg_namespace cn ON c.collnamespace=cn.oid
WHERE
d.typnamespace = {{scid}}::oid
{% if doid %}
AND d.oid={{doid}}::int
{% endif %}
ORDER BY
d.typname;

View File

@ -0,0 +1,69 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% if data %}
{% set name = o_data.name %}
{% if data.name %}
{% if data.name != o_data.name %}
ALTER TYPE {{ conn|qtIdent(o_data.basensp, o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% set name = data.name %}
{% endif %}
{% endif -%}
{% if data.typnotnull and not o_data.typnotnull %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET NOT NULL;
{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP NOT NULL;
{% endif -%}{% if data.typdefault %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET DEFAULT {{ data.typdefault }};
{% elif data.typdefault == '' and o_data.typdefault %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP DEFAULT;
{% endif -%}{% if data.owner %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
OWNER TO {{ conn|qtIdent(data.owner) }};
{% endif -%}{% if data.constraints %}
{% for c in data.constraints.deleted %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP CONSTRAINT {{ conn|qtIdent(c.conname) }};
{% endfor -%}
{% for c in data.constraints.added %}
{% if c.conname and c.consrc %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }} );{% endif -%}
{% endfor -%}{% endif -%}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }}
{% endfor -%}
{% endif %}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
{% endfor -%}
{% endif %}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
{% endfor -%}
{% endif -%}{% if data.description %}
COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
IS {{ data.description|qtLiteral }};
{% endif -%}{% if data.basensp %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%}
{% endif -%}

View File

@ -0,0 +1,36 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% if data %}
CREATE DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
AS {{ conn|qtTypeIdent(data.basetype) }}{% if data.typlen %}({{data.typlen}}{% if data.precision %},{{data.precision}}{% endif %}){% endif %}{% if data.collname and data.collname != "pg_catalog.\"default\"" %}
COLLATE {{ data.collname }}{% endif %}{% if data.typdefault %}
DEFAULT {{ data.typdefault }}{% endif %}{% if data.typnotnull %}
NOT NULL{% endif %};
{% if data.owner %}
ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }} OWNER TO {{ conn|qtIdent(data.owner) }};{% endif %}
{% if data.constraints %}
{% for c in data.constraints %}{% if c.conname and c.consrc %}
ALTER DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% endif -%};
{% endfor -%}
{% endif %}
{% if data.description %}
COMMENT ON DOMAIN {{ conn|qtIdent(data.basensp, data.name) }}
IS '{{ data.description }}';{% endif -%}
{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.security_label and r.provider %}
{{ SECLABLE.SET(conn, 'DOMAIN', data.name, r.provider, r.security_label, data.basensp) }}{% endif -%}
{% endfor -%}
{% endif -%}
{% endif -%}

View File

@ -0,0 +1,16 @@
{% if scid and doid %}
SELECT
d.typname as name, bn.nspname as basensp
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.typnamespace = {{scid}}::oid
AND
d.oid={{doid}}::int;
{% endif %}
{% if name %}
DROP DOMAIN {{ conn|qtIdent(basensp, name) }}{% if cascade %} CASCADE{% endif %};
{% endif %}

View File

@ -0,0 +1,10 @@
SELECT --nspname, collname,
CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
concat(nspname, '."', collname,'"')
ELSE '' END AS copy_collation
FROM
pg_collation c, pg_namespace n
WHERE
c.collnamespace=n.oid
ORDER BY
nspname, collname;

View File

@ -0,0 +1,15 @@
SELECT
'DOMAIN' AS objectkind, c.oid as conoid, conname, typname as relname, nspname, description,
regexp_replace(pg_get_constraintdef(c.oid, true), E'CHECK \\((.*)\\).*', E'\\1') as consrc, connoinherit, convalidated
FROM
pg_constraint c
JOIN
pg_type t ON t.oid=contypid
JOIN
pg_namespace nl ON nl.oid=typnamespace
LEFT OUTER JOIN
pg_description des ON (des.objoid=t.oid AND des.classoid='pg_constraint'::regclass)
WHERE
contype = 'c' AND contypid = {{doid}}::oid
ORDER BY
conname;

View File

@ -0,0 +1,18 @@
{% if doid %}
SELECT
d.typnamespace as scid
FROM
pg_type d
WHERE
d.oid={{ doid }}::oid;
{% else %}
SELECT
d.oid, d.typnamespace
FROM
pg_type d
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
bn.nspname = {{ basensp|qtLiteral }}
AND d.typname={{ name|qtLiteral }};
{% endif %}

View File

@ -0,0 +1,13 @@
SELECT
d.oid, d.typname as name, pg_get_userbyid(d.typowner) as owner,
bn.nspname as basensp
FROM
pg_type d
JOIN
pg_type b ON b.oid = d.typbasetype
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
WHERE
d.typnamespace = {{scid}}::oid
ORDER BY
d.typname;

View File

@ -0,0 +1,34 @@
SELECT
d.oid, d.typname as name, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as owner,
c.oid AS colloid, format_type(b.oid, d.typtypmod) AS fulltype,
CASE WHEN length(cn.nspname) > 0 AND length(c.collname) > 0 THEN
concat(cn.nspname, '."', c.collname,'"')
ELSE '' END AS collname,
d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp,
description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup,
(SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup,
(SELECT
array_agg(provider || '=' || label)
FROM
pg_shseclabel sl1
WHERE
sl1.objoid=d.oid) AS seclabels
FROM
pg_type d
JOIN
pg_type b ON b.oid = d.typbasetype
JOIN
pg_namespace bn ON bn.oid=d.typnamespace
LEFT OUTER JOIN
pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass)
LEFT OUTER JOIN
pg_collation c ON d.typcollation=c.oid
LEFT OUTER JOIN
pg_namespace cn ON c.collnamespace=cn.oid
WHERE
d.typnamespace = {{scid}}::oid
{% if doid %}
AND d.oid={{doid}}::int
{% endif %}
ORDER BY
d.typname;

View File

@ -0,0 +1,80 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% if data %}
{% set name = o_data.name %}
{% if data.name %}
{% if data.name != o_data.name %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% set name = data.name %}
{% endif %}
{% endif -%}
{% if data.typnotnull and not o_data.typnotnull %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET NOT NULL;
{% elif 'typnotnull' in data and not data.typnotnull and o_data.typnotnull%}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP NOT NULL;
{% endif -%}{% if data.typdefault %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET DEFAULT {{ data.typdefault }};
{% elif data.typdefault == '' and o_data.typdefault %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP DEFAULT;
{% endif -%}{% if data.owner %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
OWNER TO {{ conn|qtIdent(data.owner) }};
{% endif -%}{% if data.constraints %}
{% for c in data.constraints.deleted %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
DROP CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }};
{% endfor -%}
{% for c in data.constraints.changed %}
{% if c.conname and c.conname !=o_data['constraints'][c.conoid]['conname'] %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
RENAME CONSTRAINT {{ conn|qtIdent(o_data['constraints'][c.conoid]['conname']) }} TO {{ conn|qtIdent(c.conname) }};
{% endif %}
{% if c.convalidated and not o_data['constraints'][c.conoid]['convalidated'] %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
VALIDATE CONSTRAINT {{ conn|qtIdent(c.conname) }};
{% endif %}
{% endfor -%}
{% for c in data.constraints.added %}
{% if c.conname and c.consrc %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
ADD CONSTRAINT {{ conn|qtIdent(c.conname) }} CHECK ({{ c.consrc }}){% if not c.convalidated %} NOT VALID{% endif %}{% if c.connoinherit %} NO INHERIT{% endif -%};{% endif -%}
{% endfor -%}{% endif -%}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABLE.UNSET(conn, 'DOMAIN', name, r.provider, o_data.basensp) }}
{% endfor %}
{% endif -%}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
{% endfor %}
{% endif -%}{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABLE.SET(conn, 'DOMAIN', name, r.provider, r.security_label, o_data.basensp) }}
{% endfor %}
{% endif -%}{% if data.description %}
COMMENT ON DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
IS {{ data.description|qtLiteral }};
{% endif -%}{% if data.basensp %}
ALTER DOMAIN {{ conn|qtIdent(o_data.basensp, name) }}
SET SCHEMA {{ conn|qtIdent(data.basensp) }};{% endif -%}
{% endif -%}