Add support for Sequences.

pull/3/head
Harshal Dhumal 2016-03-10 15:23:04 +00:00 committed by Dave Page
parent 986375d60e
commit def1a30251
15 changed files with 1170 additions and 0 deletions

View File

@ -0,0 +1,692 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Implements Sequence Node"""
import json
from flask import render_template, make_response, request, jsonify
from flask.ext.babel import gettext as _
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.server_groups.servers.utils import parse_priv_from_db, \
parse_priv_to_db
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
import SchemaChildModule
import pgadmin.browser.server_groups.servers.databases as database
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 SequenceModule(SchemaChildModule):
"""
class SequenceModule(CollectionNodeModule)
A module class for Sequence node derived from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the SequenceModule and it's base module.
* get_nodes(gid, sid, did)
- Method is used to generate the browser collection node.
* script_load()
- Load the module script for sequence, when any of the database node is
initialized.
* node_inode()
- Method is overridden from its base class to make the node as leaf node.
"""
NODE_TYPE = 'sequence'
COLLECTION_LABEL = _("Sequences")
def __init__(self, *args, **kwargs):
super(SequenceModule, self).__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
def get_nodes(self, gid, sid, did, scid):
"""
Generate the sequence 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):
"""
Override this property to make the node a leaf node.
Returns: False as this is the leaf node
"""
return False
blueprint = SequenceModule(__name__)
class SequenceView(PGChildNodeView):
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': 'seid'}
]
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 module_js(self):
"""
This property defines whether javascript exists for this node.
"""
return make_response(
render_template(
"sequence/js/sequence.js",
_=_
),
200, {'Content-Type': 'application/x-javascript'}
)
def check_precondition(action=None):
"""
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
"""
def wrap(f):
@wraps(f)
def wrapped(self, *args, **kwargs):
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
if action and action in ["drop"]:
self.conn = self.manager.connection()
elif 'did' in kwargs:
self.conn = self.manager.connection(did=kwargs['did'])
else:
self.conn = self.manager.connection()
# If DB not connected then return error to browser
if not self.conn.connected():
return precondition_required(
_(
"Connection to the server has been lost!"
)
)
self.template_path = 'sequence/sql/9.1_plus'
return f(self, *args, **kwargs)
return wrapped
return wrap
@check_precondition(action='list')
def list(self, gid, sid, did, scid):
"""
This function is used to list all the sequence nodes within the collection.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
Returns:
"""
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(action='nodes')
def nodes(self, gid, sid, did, scid):
"""
This function is used to create all the child nodes within the collection.
Here it will create all the sequence nodes.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
Returns:
"""
res = []
SQL = render_template("/".join([self.template_path, 'nodes.sql']), scid=scid)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
res.append(
self.blueprint.generate_browser_node(
row['oid'],
sid,
row['name'],
icon="icon-%s" % self.node_type
))
return make_json_response(
data=res,
status=200
)
@check_precondition(action='properties')
def properties(self, gid, sid, did, scid, seid):
"""
This function will show the properties of the selected sequence node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
Returns:
"""
SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
sec_lbls = []
if 'securities' in res and res['securities'] is not None:
for sec in res['seclabels']:
sec = re.search(r'([^=]+)=(.*$)', sec)
sec_lbls.append({
'provider': sec.group(1),
'security_label': sec.group(2)
})
res['securities'] = sec_lbls
for row in res['rows']:
SQL = render_template("/".join([self.template_path, 'get_def.sql']), data=row)
status, rset1 = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset1)
row['current_value'] = rset1['rows'][0]['last_value']
row['minimum'] = rset1['rows'][0]['min_value']
row['maximum'] = rset1['rows'][0]['max_value']
row['increment'] = rset1['rows'][0]['increment_by']
row['cache'] = rset1['rows'][0]['cache_value']
row['cycled'] = rset1['rows'][0]['is_cycled']
SQL = render_template("/".join([self.template_path, 'acl.sql']), scid=scid, seid=seid)
status, dataclres = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
for row in dataclres['rows']:
priv = parse_priv_from_db(row)
if row['deftype'] in res['rows'][0]:
res['rows'][0][row['deftype']].append(priv)
else:
res['rows'][0][row['deftype']] = [priv]
return ajax_response(
response=res['rows'][0],
status=200
)
@check_precondition(action="create")
def create(self, gid, sid, did, scid):
"""
Create the sequence.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
Returns:
"""
required_args = [
u'name',
u'schema',
u'seqowner',
]
data = request.form if request.form else json.loads(request.data.decode())
for arg in required_args:
if arg not in data:
return make_json_response(
status=400,
success=0,
errormsg=_(
"Couldn't find the required parameter (%s)." % arg
)
)
try:
# The SQL below will execute CREATE DDL only
SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
status, msg = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=msg)
if 'relacl' in data:
data['relacl'] = parse_priv_to_db(data['relacl'], 'DATABASE')
# The SQL below will execute rest DMLs because we can not execute CREATE with any other
SQL = render_template("/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, msg = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=msg)
# We need oid of newly created sequence.
SQL = render_template("/".join([self.template_path, 'get_oid.sql']), name=data['name'], scid=scid)
SQL = SQL.strip('\n').strip(' ')
if SQL and SQL != "":
status, seid = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return jsonify(
node=self.blueprint.generate_browser_node(
seid,
scid,
data['name'],
icon="icon-%s" % self.node_type
)
)
except Exception as e:
return make_json_response(
status=500,
success=0,
errormsg=str(e)
)
@check_precondition(action='delete')
def delete(self, gid, sid, did, scid, seid):
"""
This function will drop the object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
Returns:
"""
# Below will decide if it's simple drop or drop with cascade call
if self.cmd == 'delete':
# This is a cascade operation
cascade = True
else:
cascade = False
try:
SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template("/".join([self.template_path, 'delete.sql']), data=res['rows'][0], 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=_("Sequence dropped"),
data={
'id': seid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition(action='update')
def update(self, gid, sid, did, scid, seid):
"""
This function will update the object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
Returns:
"""
data = request.form if request.form else json.loads(request.data.decode())
try:
SQL = self.getSQL(gid, sid, did, data, scid, seid)
SQL = SQL.strip('\n').strip(' ')
if SQL != "":
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info="Sequence updated",
data={
'id': seid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
else:
return make_json_response(
success=1,
info="Nothing to update",
data={
'id': seid,
'scid': scid,
'sid': sid,
'gid': gid,
'did': did
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition(action='msql')
def msql(self, gid, sid, did, scid, seid=None):
"""
This function to return modified SQL.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
"""
data = {}
for k, v in request.args.items():
try:
data[k] = json.loads(v)
except ValueError:
data[k] = v
if seid is None:
required_args = [
'name',
'schema'
]
for arg in required_args:
if arg not in data:
return make_json_response(
status=400,
success=0,
errormsg=_(
"Couldn't find the required parameter (%s)." % arg
)
)
try:
SQL = self.getSQL(gid, sid, did, data, scid, seid)
SQL = SQL.strip('\n').strip(' ')
return make_json_response(
data=SQL,
status=200
)
except Exception as e:
return make_json_response(
data="-- modified SQL",
status=200
)
def getSQL(self, gid, sid, did, data, scid, seid=None):
"""
This function will generate sql from model data.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
"""
required_args = [
u'name'
]
if seid is not None:
SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
# Making copy of output for further processing
old_data = dict(res['rows'][0])
old_data = self._formatter(old_data, scid, seid)
# To format privileges data coming from client
for key in ['relacl']:
if key in data and data[key] is not None:
if 'added' in data[key]:
data[key]['added'] = parse_priv_to_db(data[key]['added'], 'SEQUENCE')
if 'changed' in data[key]:
data[key]['changed'] = parse_priv_to_db(data[key]['changed'], 'SEQUENCE')
if 'deleted' in data[key]:
data[key]['deleted'] = parse_priv_to_db(data[key]['deleted'], 'SEQUENCE')
# If name is not present with in update data then copy it
# from old data
for arg in required_args:
if arg not in data:
data[arg] = old_data[arg]
SQL = render_template("/".join([self.template_path, 'update.sql']),
data=data, o_data=old_data, conn=self.conn)
else:
# To format privileges coming from client
if 'relacl' in data:
data['relacl'] = parse_priv_to_db(data['relacl'], 'SEQUENCE')
SQL = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
SQL += render_template("/".join([self.template_path, 'grant.sql']), data=data, conn=self.conn)
return SQL
@check_precondition(action="sql")
def sql(self, gid, sid, did, scid, seid):
"""
This function will generate sql for sql panel
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
"""
SQL = render_template("/".join([self.template_path, 'properties.sql']), scid=scid, seid=seid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
for row in res['rows']:
SQL = render_template("/".join([self.template_path, 'get_def.sql']), data=row)
status, rset1 = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset1)
row['current_value'] = rset1['rows'][0]['last_value']
row['minimum'] = rset1['rows'][0]['min_value']
row['maximum'] = rset1['rows'][0]['max_value']
row['increment'] = rset1['rows'][0]['increment_by']
row['cache'] = rset1['rows'][0]['cache_value']
row['cycled'] = rset1['rows'][0]['is_cycled']
result = res['rows'][0]
result = self._formatter(result, scid, seid)
SQL = self.getSQL(gid, sid, did, result, scid)
SQL = SQL.strip('\n').strip(' ')
return ajax_response(response=SQL)
def _formatter(self, data, scid, seid):
"""
Args:
data: dict of query result
scid: Schema ID
seid: Sequence ID
Returns:
It will return formatted output of sequence
"""
# Need to format security labels according to client js collection
if 'securities' in data and data['securities'] is not None:
seclabels = []
for seclbls in data['securities']:
k, v = seclbls.split('=')
seclabels.append({'provider': k, 'security_label': v})
data['securities'] = seclabels
# We need to parse & convert ACL coming from database to json format
SQL = render_template("/".join([self.template_path, 'acl.sql']),
scid=scid, seid=seid)
status, acl = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=acl)
# We will set get privileges from acl sql so we don't need
# it from properties sql
data['relacl'] = []
for row in acl['rows']:
priv = parse_priv_from_db(row)
data.setdefault(row['deftype'], []).append(priv)
return data
@check_precondition(action="dependents")
def dependents(self, gid, sid, did, scid, seid):
"""
This function gets the dependents and returns an ajax response
for the sequence node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
"""
dependents_result = self.get_dependents(self.conn, seid)
return ajax_response(
response=dependents_result,
status=200
)
@check_precondition(action="dependencies")
def dependencies(self, gid, sid, did, scid, seid):
"""
This function gets the dependencies and returns an ajax response
for the sequence node.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
seid: Sequence ID
"""
dependencies_result = self.get_dependencies(self.conn, seid)
# Get missing dependencies.
# A Corner case, reported by Guillaume Lelarge, could be found at:
# http://archives.postgresql.org/pgadmin-hackers/2009-03/msg00026.php
sql = render_template("/".join([self.template_path,
'get_dependencies.sql']), seid=seid)
status, result = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=result)
for row in result['rows']:
ref_name = row['refname']
if ref_name is None:
continue
dep_type = ''
dep_str = row['deptype']
if dep_str == 'a':
dep_type = 'auto'
elif dep_str == 'n':
dep_type = 'normal'
elif dep_str == 'i':
dep_type = 'internal'
dependencies_result.append({'type': 'column',
'name': ref_name,
'field': dep_type})
return ajax_response(
response=dependencies_result,
status=200
)
SequenceView.register_node_view(blueprint)

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

View File

@ -0,0 +1,249 @@
define(
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
function($, _, S, pgAdmin, pgBrowser, alertify) {
// Extend the browser's node model class to create a security model
var SecurityModel = pgAdmin.Browser.Node.Model.extend({
defaults: {
provider: undefined,
securitylabel: undefined
},
// Define the schema for the Security Label
schema: [{
id: 'provider', label: '{{ _('Provider') }}',
type: 'text', editable: true
},{
id: 'security_label', label: '{{ _('Security Label') }}',
type: 'text', editable: true
}],
/* validate function is used to validate the input given by
* the user. In case of error, message will be displayed on
* the GUI for the respective control.
*/
validate: function() {
var err = {},
errmsg = null,
data = this.toJSON();
if (_.isUndefined(data.label) ||
_.isNull(data.label) ||
String(data.label).replace(/^\s+|\s+$/g, '') == '') {
return _("Please specify the value for all the security providers.");
}
return null;
}
});
// Extend the browser's collection class for sequence collection
if (!pgBrowser.Nodes['coll-sequence']) {
var databases = pgAdmin.Browser.Nodes['coll-sequence'] =
pgAdmin.Browser.Collection.extend({
node: 'sequence',
label: '{{ _('Sequences') }}',
type: 'coll-sequence',
columns: ['oid', 'name', 'seqowner', 'comment']
});
};
// Extend the browser's node class for sequence node
if (!pgBrowser.Nodes['sequence']) {
pgAdmin.Browser.Nodes['sequence'] = pgBrowser.Node.extend({
type: 'sequence',
label: '{{ _('Sequence') }}',
collection_type: 'coll-sequence',
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_sequence_on_coll', node: 'coll-sequence', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Sequence...') }}',
icon: 'wcTabIcon icon-sequence', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_sequence', node: 'sequence', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Sequence...') }}',
icon: 'wcTabIcon icon-sequence', data: {action: 'create', check: true},
enable: 'canCreate'
},{
name: 'create_sequence', node: 'schema', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: '{{ _('Sequence...') }}',
icon: 'wcTabIcon icon-sequence', data: {action: 'create', check: false},
enable: 'canCreate'
}
]);
},
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
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 collation
if (_.indexOf(['schema'], d._type) > -1)
return true;
if ('coll-sequence' == 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;
},
// Define the model for sequence node.
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
name: undefined,
oid: undefined,
seqowner: undefined,
schema: undefined,
comment: undefined,
increment: undefined,
start: undefined,
current_value: undefined,
minimum: undefined,
maximum: undefined,
cache: undefined,
cycled: undefined,
relacl: [],
securities: []
},
// 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({'seqowner': userInfo.name}, {silent: true});
this.set({'schema': schemaInfo.label}, {silent: true});
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
// Define the schema for sequence node.
schema: [{
id: 'name', label: '{{ _('Name') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit']
},{
id: 'oid', label:'{{ _('Oid') }}', cell: 'string',
type: 'text' , mode: ['properties']
},{
id: 'seqowner', label:'{{ _('Owner') }}', cell: 'string',
type: 'text', mode: ['properties', 'create', 'edit'], node: 'role',
control: Backform.NodeListByNameControl
},{
id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
control: 'node-list-by-name', node: 'schema',
type: 'text', mode: ['create', 'edit'], filter: function(d) {
// If schema name start with pg_* then we need to exclude them
if(d && d.label.match(/^pg_/))
{
return false;
}
return true;
}
},{
id: 'comment', label:'{{ _('Comment') }}', type: 'multiline',
mode: ['properties', 'create', 'edit']
},{
id: 'increment', label: '{{ _('Increment') }}', type: 'int',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'start', label: '{{ _('Start') }}', type: 'int',
mode: ['create'], group: '{{ _('Definition') }}'
},{
id: 'current_value', label: '{{ _('Current value') }}', type: 'int',
mode: ['properties', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'minimum', label: '{{ _('Minimum') }}', type: 'int',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'maximum', label: '{{ _('Maximum') }}', type: 'int',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'cache', label: '{{ _('Cache') }}', type: 'int',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'cycled', label: '{{ _('Cycled') }}', type: 'switch',
mode: ['properties', 'create', 'edit'], group: '{{ _('Definition') }}'
},{
id: 'acl', label: '{{ _('Privileges') }}', type: 'text',
mode: ['properties'], disabled: true
},{
id: 'relacl', label: '{{ _('Privileges') }}', model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend(
{privileges: ['r', 'w', 'U']}), uniqueCol : ['grantee', 'grantor'],
editable: false, type: 'collection', group: '{{ _('Security') }}', mode: ['edit', 'create'],
canAdd: true, canDelete: true, control: 'unique-col-collection',
},{
id: 'securities', label: '{{ _('Securitiy Labels') }}', model: SecurityModel,
editable: false, type: 'collection', canEdit: false,
group: '{{ _('Security') }}', canDelete: true,
mode: ['edit', 'create'], canAdd: true,
control: 'unique-col-collection', uniqueCol : ['provider'],
min_version: 90200
}],
/* validate function is used to validate the input given by
* the user. In case of error, message will be displayed on
* the GUI for the respective control.
*/
validate: function() {
var msg = undefined;
// Clear any existing error msg.
this.errorModel.unset('name');
this.errorModel.unset('seqowner');
this.errorModel.unset('schema');
if (_.isUndefined(this.get('name'))
|| String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Sequence name can not be empty!') }}';
this.errorModel.set('name', msg);
return msg;
}
if (_.isUndefined(this.get('seqowner'))
|| String(this.get('seqowner')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Sequence owner can not be empty!') }}';
this.errorModel.set('seqowner', msg);
return msg;
}
if (_.isUndefined(this.get('schema'))
|| String(this.get('schema')).replace(/^\s+|\s+$/g, '') == '') {
msg = '{{ _('Sequence schema can not be empty!') }}';
this.errorModel.set('schema', msg);
return msg;
}
return null;
}
})
});
}
return pgBrowser.Nodes['sequence'];
});

View File

@ -0,0 +1,30 @@
SELECT 'relacl' as deftype, COALESCE(gt.rolname, 'public') grantee, g.rolname grantor, array_agg(privilege_type) as privileges, array_agg(is_grantable) as grantable
FROM
(SELECT
d.grantee, d.grantor, d.is_grantable,
CASE d.privilege_type
WHEN 'CONNECT' THEN 'c'
WHEN 'CREATE' THEN 'C'
WHEN 'DELETE' THEN 'd'
WHEN 'EXECUTE' THEN 'X'
WHEN 'INSERT' THEN 'a'
WHEN 'REFERENCES' THEN 'x'
WHEN 'SELECT' THEN 'r'
WHEN 'TEMPORARY' THEN 'T'
WHEN 'TRIGGER' THEN 't'
WHEN 'TRUNCATE' THEN 'D'
WHEN 'UPDATE' THEN 'w'
WHEN 'USAGE' THEN 'U'
ELSE 'UNKNOWN'
END AS privilege_type
FROM
(SELECT relacl
FROM pg_class cl
LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid AND des.classoid='pg_class'::regclass)
WHERE relkind = 'S' AND relnamespace = {{scid}}::oid
AND cl.oid = {{seid}}::oid ) acl,
aclexplode(relacl) d
) d
LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
GROUP BY g.rolname, gt.rolname

View File

@ -0,0 +1,15 @@
SELECT
CASE WHEN nsp.nspname IN ('sys', 'dbo', 'information_schema') THEN true ELSE false END AS dbSupport
FROM pg_namespace nsp
WHERE nsp.oid={{scid}}::int AND (
(nspname = 'pg_catalog' AND EXISTS
(SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1))
OR (nspname = 'pgagent' AND EXISTS
(SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1))
OR (nspname = 'information_schema' AND EXISTS
(SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1))
OR (nspname LIKE '_%' AND EXISTS
(SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1))
) AND
nspname NOT LIKE E'pg\\temp\\%' AND
nspname NOT LIKE E'pg\\toast_temp\\%'

View File

@ -0,0 +1,16 @@
{% if data %}
CREATE SEQUENCE {{ conn|qtIdent(data.schema) }}.{{ conn|qtIdent(data.name) }}
{% if data.cycled and data.cycled == True %}
CYCLE
{% endif %}
{% if data.increment %}
INCREMENT {{data.increment}}
{% endif %}{% if data.start %}
START {{data.start}}
{% endif %}{% if data.minimum %}
MINVALUE {{data.minimum}}
{% endif %}{% if data.maximum %}
MAXVALUE {{data.maximum}}
{% endif %}{% if data.cache %}
CACHE {{data.cache}}{% endif %};
{% endif %}

View File

@ -0,0 +1 @@
DROP SEQUENCE {{ conn|qtIdent(data.schema) }}.{{ conn|qtIdent(data.name) }}{% if cascade%} CASCADE{% endif %};

View File

@ -0,0 +1,9 @@
SELECT
last_value,
min_value,
max_value,
cache_value,
is_cycled,
increment_by,
is_called
FROM {{ conn|qtIdent(data.schema) }}.{{ conn|qtIdent(data.name) }}

View File

@ -0,0 +1,12 @@
SELECT
CASE WHEN att.attname IS NOT NULL AND ref.relname IS NOT NULL THEN ref.relname || '.' || att.attname
ELSE ref.relname
END AS refname,
d2.refclassid, d1.deptype AS deptype
FROM pg_depend d1
LEFT JOIN pg_depend d2 ON d1.objid=d2.objid AND d1.refobjid != d2.refobjid
LEFT JOIN pg_class ref ON ref.oid = d2.refobjid
LEFT JOIN pg_attribute att ON d2.refobjid=att.attrelid AND d2.refobjsubid=att.attnum
WHERE d1.classid=(SELECT oid FROM pg_class WHERE relname='pg_attrdef')
AND d2.refobjid NOT IN (SELECT d3.refobjid FROM pg_depend d3 WHERE d3.objid=d1.refobjid)
AND d1.refobjid={{seid}}::oid

View File

@ -0,0 +1,5 @@
SELECT cl.oid as oid
FROM pg_class cl
LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid AND des.classoid='pg_class'::regclass)
WHERE relkind = 'S' AND relnamespace = {{scid}}::oid
AND relname = {{ name|qtLiteral }}

View File

@ -0,0 +1,26 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
{# Construct sequence name from name and schema #}
{% set seqname=conn|qtIdent(data.schema, data.name) %}
{% if data.seqowner %}
ALTER SEQUENCE {{ seqname }}
OWNER TO {{ conn|qtIdent(data.seqowner) }};
{% endif %}
{% if data.comment %}
COMMENT ON SEQUENCE {{ seqname }}
IS {{ data.comment|qtLiteral }};
{% endif %}
{% if data.securities %}
{% for r in data.securities %}
{{ SECLABLE.SET(conn, 'SEQUENCE', data.name, r.provider, r.security_label, data.schema) }}
{% endfor %}
{% endif %}
{% if data.relacl %}
{% for priv in data.relacl %}
{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,4 @@
SELECT cl.oid as oid, relname as name
FROM pg_class cl
WHERE relkind = 'S' AND relnamespace = {{scid}}::oid
ORDER BY relname

View File

@ -0,0 +1,17 @@
{% if scid %}
SELECT
cl.oid as oid,
relname as name,
nsp.nspname as schema,
pg_get_userbyid(relowner) AS seqowner,
description as comment,
array_to_string(relacl::text[], ', ') as acl,
(SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=cl.oid) AS securities
FROM pg_class cl
LEFT OUTER JOIN pg_namespace nsp ON cl.relnamespace = nsp.oid
LEFT OUTER JOIN pg_description des ON (des.objoid=cl.oid
AND des.classoid='pg_class'::regclass)
WHERE relkind = 'S' AND relnamespace = {{scid}}::oid
{% if seid %}AND cl.oid = {{seid}}::oid {% endif %}
ORDER BY relname
{% endif %}

View File

@ -0,0 +1,94 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
{% if data %}
{% if data.name != o_data.name %}
ALTER SEQUENCE {{ conn|qtIdent(o_data.schema, o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}
{% if data.seqowner and data.seqowner != o_data.seqowner %}
ALTER SEQUENCE {{ conn|qtIdent(o_data.schema, data.name) }}
OWNER TO {{ conn|qtIdent(data.seqowner) }};
{% endif %}
{% if data.current_value %}
{% set seqname = conn|qtIdent(o_data.schema, data.name) %}
SELECT setval({{ seqname|qtLiteral }}, {{ data.current_value }}, true);
{% endif %}
{% set defquery = '' %}
{% if data.increment %}
{% set defquery = defquery+'\n INCREMENT '+data.increment|string %}
{% endif %}
{% if data.minimum %}
{% set defquery = defquery+'\n MINVALUE '+data.minimum|string %}
{% endif %}
{% if data.maximum %}
{% set defquery = defquery+'\n MAXVALUE '+data.maximum|string %}
{% endif %}
{% if data.cache %}
{% set defquery = defquery+'\n CACHE '+data.cache|string %}
{% endif %}
{% if data.cycled == True %}
{% set defquery = defquery+'\n CYCLE' %}
{% elif data.cycled == False %}
{% set defquery = defquery+'\n NO CYCLE' %}
{% endif %}
{% if defquery and defquery != '' %}
ALTER SEQUENCE {{ conn|qtIdent(o_data.schema, data.name) }} {{ defquery }};
{% endif %}
{% if data.schema and data.schema != o_data.schema %}
ALTER SEQUENCE {{ conn|qtIdent(o_data.schema, data.name) }}
SET SCHEMA {{ conn|qtIdent(data.schema) }};
{% set seqname = conn|qtIdent(data.schema, data.name) %}
{% set schema = data.schema %}
{% else %}
{% set seqname = conn|qtIdent(o_data.schema, data.name) %}
{% set schema = o_data.schema %}
{% endif %}
{% if data.comment and data.comment != o_data.comment %}
COMMENT ON SEQUENCE {{ seqname }}
IS {{ data.comment|qtLiteral }};
{% endif %}
{% if data.securities and data.securities|length > 0 %}
{% set seclabels = data.securities %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABLE.UNSET(conn, 'SEQUENCE', data.name, r.provider, schema) }}
{% endfor %}
{% endif %}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABLE.SET(conn, 'SEQUENCE', data.name, r.provider, r.security_label, schema) }}
{% endfor %}
{% endif %}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABLE.SET(conn, 'SEQUENCE', data.name, r.provider, r.security_label, schema) }}
{% endfor %}
{% endif %}
{% endif %}
{% if data.relacl %}
{% if 'deleted' in data.relacl %}
{% for priv in data.relacl.deleted %}
{{ PRIVILEGE.UNSETALL(conn, 'SEQUENCE', priv.grantee, data.name, schema) }}
{% endfor %}
{% endif %}
{% if 'changed' in data.relacl %}
{% for priv in data.relacl.changed %}
{{ PRIVILEGE.UNSETALL(conn, 'SEQUENCE', priv.grantee, data.name, schema) }}
{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv.grantee, data.name, priv.without_grant, priv.with_grant, schema) }}
{% endfor %}
{% endif %}
{% if 'added' in data.relacl %}
{% for priv in data.relacl.added %}
{{ PRIVILEGE.SET(conn, 'SEQUENCE', priv.grantee, data.name, priv.without_grant, priv.with_grant, schema) }}
{% endfor %}
{% endif %}
{% endif %}
{% endif %}