Add support for collations.
parent
c0086b092f
commit
b6f7202448
|
@ -0,0 +1,664 @@
|
|||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Implements Collation Node """
|
||||
|
||||
import json
|
||||
from flask import render_template, 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.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 CollationModule(SchemaChildModule):
|
||||
"""
|
||||
class CollationModule(CollectionNodeModule)
|
||||
|
||||
A module class for Collation node derived from CollectionNodeModule.
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(*args, **kwargs)
|
||||
- Method is used to initialize the Collation and it's base module.
|
||||
|
||||
* get_nodes(gid, sid, did, scid, coid)
|
||||
- Method is used to generate the browser collection node.
|
||||
|
||||
* node_inode()
|
||||
- Method is overridden from its base class to make the node as leaf node.
|
||||
|
||||
* script_load()
|
||||
- Load the module script for schema, when any of the server node is
|
||||
initialized.
|
||||
"""
|
||||
|
||||
NODE_TYPE = 'collation'
|
||||
COLLECTION_LABEL = gettext("Collations")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Method is used to initialize the CollationModule and it's base module.
|
||||
|
||||
Args:
|
||||
*args:
|
||||
**kwargs:
|
||||
"""
|
||||
|
||||
super(CollationModule, self).__init__(*args, **kwargs)
|
||||
self.min_ver = 90100
|
||||
self.max_ver = None
|
||||
|
||||
def get_nodes(self, gid, sid, did, scid):
|
||||
"""
|
||||
Generate the collection node
|
||||
"""
|
||||
yield self.generate_browser_collection_node(scid)
|
||||
|
||||
@property
|
||||
def script_load(self):
|
||||
"""
|
||||
Load the module script for database, when any of the database node is
|
||||
initialized.
|
||||
"""
|
||||
return database.DatabaseModule.NODE_TYPE
|
||||
|
||||
@property
|
||||
def node_inode(self):
|
||||
return False
|
||||
|
||||
blueprint = CollationModule(__name__)
|
||||
|
||||
|
||||
class CollationView(PGChildNodeView):
|
||||
"""
|
||||
This class is responsible for generating routes for Collation node
|
||||
|
||||
Methods:
|
||||
-------
|
||||
* __init__(**kwargs)
|
||||
- Method is used to initialize the CollationView and it's base view.
|
||||
|
||||
* check_precondition()
|
||||
- This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
manager,conn & template_path properties to self
|
||||
|
||||
* list()
|
||||
- This function is used to list all the Collation nodes within that
|
||||
collection.
|
||||
|
||||
* nodes()
|
||||
- This function will used to create all the child node within that
|
||||
collection, Here it will create all the Collation node.
|
||||
|
||||
* properties(gid, sid, did, scid, coid)
|
||||
- This function will show the properties of the selected Collation node
|
||||
|
||||
* create(gid, sid, did, scid)
|
||||
- This function will create the new Collation object
|
||||
|
||||
* update(gid, sid, did, scid, coid)
|
||||
- This function will update the data for the selected Collation node
|
||||
|
||||
* delete(self, gid, sid, scid, coid):
|
||||
- This function will drop the Collation object
|
||||
|
||||
* msql(gid, sid, did, scid, coid)
|
||||
- This function is used to return modified SQL for the selected
|
||||
Collation node
|
||||
|
||||
* get_sql(data, scid, coid)
|
||||
- This function will generate sql from model data
|
||||
|
||||
* sql(gid, sid, did, scid):
|
||||
- This function will generate sql to show it in sql pane for the
|
||||
selected Collation node.
|
||||
|
||||
* dependency(gid, sid, did, scid):
|
||||
- This function will generate dependency list show it in dependency
|
||||
pane for the selected Collation node.
|
||||
|
||||
* dependent(gid, sid, did, scid):
|
||||
- This function will generate dependent list to show it in dependent
|
||||
pane for the selected Collation node.
|
||||
"""
|
||||
|
||||
node_type = blueprint.node_type
|
||||
|
||||
parent_ids = [
|
||||
{'type': 'int', 'id': 'gid'},
|
||||
{'type': 'int', 'id': 'sid'},
|
||||
{'type': 'int', 'id': 'did'},
|
||||
{'type': 'int', 'id': 'scid'}
|
||||
]
|
||||
ids = [
|
||||
{'type': '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'}],
|
||||
'get_collations': [{'get': 'get_collation'},
|
||||
{'get': 'get_collation'}]
|
||||
})
|
||||
|
||||
def check_precondition(f):
|
||||
"""
|
||||
This function will behave as a decorator which will checks
|
||||
database connection before running view, it will also attaches
|
||||
manager,conn & template_path properties to self
|
||||
"""
|
||||
@wraps(f)
|
||||
def wrap(*args, **kwargs):
|
||||
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||
self = args[0]
|
||||
self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
|
||||
kwargs['sid']
|
||||
)
|
||||
self.conn = self.manager.connection(did=kwargs['did'])
|
||||
# If DB not connected then return error to browser
|
||||
if not self.conn.connected():
|
||||
return precondition_required(
|
||||
gettext(
|
||||
"Connection to the server has been lost!"
|
||||
)
|
||||
)
|
||||
|
||||
# we will set template path for sql scripts
|
||||
self.template_path = 'collation/sql/9.1_plus'
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
@check_precondition
|
||||
def list(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function is used to list all the collation nodes within that collection.
|
||||
|
||||
Args:
|
||||
gid: Server group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
|
||||
Returns:
|
||||
JSON of available collation nodes
|
||||
"""
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']), scid=scid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def nodes(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function will used to create all the child node within that collection.
|
||||
Here it will create all the collation node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
|
||||
Returns:
|
||||
JSON of available collation child nodes
|
||||
"""
|
||||
|
||||
res = []
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'nodes.sql']), scid=scid)
|
||||
status, rset = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=rset)
|
||||
|
||||
for row in rset['rows']:
|
||||
res.append(
|
||||
self.blueprint.generate_browser_node(
|
||||
row['oid'],
|
||||
scid,
|
||||
row['name'],
|
||||
icon="icon-collation"
|
||||
))
|
||||
|
||||
return make_json_response(
|
||||
data=res,
|
||||
status=200
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def properties(self, gid, sid, did, scid, coid):
|
||||
"""
|
||||
This function will show the properties of the selected collation node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
|
||||
Returns:
|
||||
JSON of selected collation node
|
||||
"""
|
||||
|
||||
try:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
scid=scid, coid=coid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'][0],
|
||||
status=200
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def get_collation(self, gid, sid, did, scid, coid=None):
|
||||
"""
|
||||
This function will return list of collation available
|
||||
as AJAX response.
|
||||
"""
|
||||
|
||||
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))
|
||||
|
||||
def _check_definition(self, data):
|
||||
"""
|
||||
Args:
|
||||
data: request data received from client
|
||||
|
||||
Returns:
|
||||
True if defination is missing, False otherwise
|
||||
"""
|
||||
definition_args = [
|
||||
'locale',
|
||||
'copy_collation',
|
||||
'lc_collate',
|
||||
'lc_type'
|
||||
]
|
||||
|
||||
# Additional server side validation to check if
|
||||
# definition is sent properly from client side
|
||||
missing_definition_flag = False
|
||||
|
||||
for arg in definition_args:
|
||||
if arg == 'locale' and \
|
||||
(arg not in data or data[arg] == ''):
|
||||
if 'copy_collation' not in data and (
|
||||
'lc_collate' not in data and 'lc_type' not in data
|
||||
):
|
||||
missing_definition_flag = True
|
||||
|
||||
if arg == 'copy_collation' and \
|
||||
(arg not in data or data[arg] == ''):
|
||||
if 'locale' not in data and (
|
||||
'lc_collate' not in data and 'lc_type' not in data
|
||||
):
|
||||
missing_definition_flag = True
|
||||
|
||||
if (arg == 'lc_collate' or arg == 'lc_type') and \
|
||||
(arg not in data or data[arg] == ''):
|
||||
if 'copy_collation' not in data and 'locale' not in data:
|
||||
missing_definition_flag = True
|
||||
|
||||
return missing_definition_flag
|
||||
|
||||
|
||||
@check_precondition
|
||||
def create(self, gid, sid, did, scid):
|
||||
"""
|
||||
This function will creates new the collation object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data.decode()
|
||||
)
|
||||
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Couldn't find the required parameter (%s)." % arg
|
||||
)
|
||||
)
|
||||
if self._check_definition(data):
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
"Incomplete definition, Please provide Locale \
|
||||
OR Copy collation OR LC_TYPE/LC_COLLATE"
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'create.sql']),
|
||||
data=data, conn=self.conn)
|
||||
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
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'get_oid.sql']), data=data)
|
||||
status, coid = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=coid)
|
||||
|
||||
return jsonify(
|
||||
node=self.blueprint.generate_browser_node(
|
||||
coid,
|
||||
scid,
|
||||
data['name'],
|
||||
icon="icon-collation"
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, coid):
|
||||
"""
|
||||
This function will delete existing the collation object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
|
||||
# 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,
|
||||
'get_name.sql']),
|
||||
scid=scid, coid=coid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Collation dropped"),
|
||||
data={
|
||||
'id': coid,
|
||||
'scid': scid,
|
||||
'did': did
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def update(self, gid, sid, did, scid, coid):
|
||||
"""
|
||||
This function will updates existing the collation object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data.decode()
|
||||
)
|
||||
SQL = self.get_sql(gid, sid, data, scid, coid)
|
||||
try:
|
||||
if SQL and SQL.strip('\n') and SQL.strip(' '):
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Collation updated",
|
||||
data={
|
||||
'id': coid,
|
||||
'scid': scid,
|
||||
'did': did
|
||||
}
|
||||
)
|
||||
else:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info="Nothing to update",
|
||||
data={
|
||||
'id': coid,
|
||||
'scid': scid,
|
||||
'did': did
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, scid, coid=None):
|
||||
"""
|
||||
This function will generates modified sql for collation object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
data = dict()
|
||||
for k, v in request.args.items():
|
||||
try:
|
||||
data[k] = json.loads(v)
|
||||
except ValueError:
|
||||
data[k] = v
|
||||
|
||||
try:
|
||||
SQL = self.get_sql(gid, sid, data, scid, coid)
|
||||
if SQL and SQL.strip('\n') and SQL.strip(' '):
|
||||
return make_json_response(
|
||||
data=SQL,
|
||||
status=200
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
def get_sql(self, gid, sid, data, scid, coid=None):
|
||||
"""
|
||||
This function will genrate sql from model data
|
||||
"""
|
||||
if coid is not None:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
scid=scid, coid=coid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
if not status:
|
||||
return 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:
|
||||
required_args = [
|
||||
'name'
|
||||
]
|
||||
|
||||
for arg in required_args:
|
||||
if arg not in data:
|
||||
return "-- missing definition"
|
||||
|
||||
if self._check_definition(data):
|
||||
return "-- missing definition"
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'create.sql']),
|
||||
data=data, conn=self.conn)
|
||||
return SQL.strip('\n')
|
||||
|
||||
@check_precondition
|
||||
def sql(self, gid, sid, did, scid, coid):
|
||||
"""
|
||||
This function will generates reverse engineered sql for collation object
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
scid=scid, 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, conn=self.conn)
|
||||
|
||||
sql_header = "-- Collation: {0};\n\n-- ".format(data['name'])
|
||||
sql_header += render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=data['name'])
|
||||
SQL = sql_header + '\n\n' + SQL.strip('\n')
|
||||
|
||||
return ajax_response(response=SQL)
|
||||
|
||||
@check_precondition
|
||||
def dependents(self, gid, sid, did, scid, coid):
|
||||
"""
|
||||
This function get the dependents and return ajax response
|
||||
for the Collation node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation 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, coid):
|
||||
"""
|
||||
This function get the dependencies and return ajax response
|
||||
for the Collation node.
|
||||
|
||||
Args:
|
||||
gid: Server Group ID
|
||||
sid: Server ID
|
||||
did: Database ID
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
dependencies_result = self.get_dependencies(
|
||||
self.conn, coid
|
||||
)
|
||||
|
||||
return ajax_response(
|
||||
response=dependencies_result,
|
||||
status=200
|
||||
)
|
||||
|
||||
CollationView.register_node_view(blueprint)
|
Binary file not shown.
After Width: | Height: | Size: 178 B |
Binary file not shown.
After Width: | Height: | Size: 233 B |
|
@ -0,0 +1,264 @@
|
|||
define(
|
||||
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-collation']) {
|
||||
var databases = pgAdmin.Browser.Nodes['coll-collation'] =
|
||||
pgAdmin.Browser.Collection.extend({
|
||||
node: 'collation',
|
||||
label: '{{ _('Collations') }}',
|
||||
type: 'coll-collation',
|
||||
columns: ['name', 'owner', 'description']
|
||||
});
|
||||
};
|
||||
|
||||
if (!pgBrowser.Nodes['collation']) {
|
||||
pgAdmin.Browser.Nodes['collation'] = pgBrowser.Node.extend({
|
||||
type: 'collation',
|
||||
label: '{{ _('Collation') }}',
|
||||
collection_type: 'coll-collation',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
parent_type: ['schema', 'catalog'],
|
||||
Init: function() {
|
||||
/* Avoid mulitple registration of menus */
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_collation_on_coll', node: 'coll-collation', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Collation...') }}',
|
||||
icon: 'wcTabIcon icon-collation', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_collation', node: 'collation', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Collation...') }}',
|
||||
icon: 'wcTabIcon icon-collation', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_collation', node: 'schema', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: '{{ _('Collation...') }}',
|
||||
icon: 'wcTabIcon icon-collation', data: {action: 'create', check: false},
|
||||
enable: 'canCreate'
|
||||
}
|
||||
]);
|
||||
|
||||
},
|
||||
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
|
||||
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
defaults: {
|
||||
name: undefined,
|
||||
oid: undefined,
|
||||
owner: undefined,
|
||||
lc_type: undefined,
|
||||
lc_collate: undefined,
|
||||
description: undefined
|
||||
},
|
||||
|
||||
// Default values!
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
|
||||
if (isNew) {
|
||||
var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
var schemaInfo = args.node_info.schema;
|
||||
|
||||
this.set({'owner': userInfo.name}, {silent: true});
|
||||
this.set({'schema': schemaInfo.label}, {silent: true});
|
||||
}
|
||||
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
schema: [{
|
||||
id: 'name', label: '{{ _('Name') }}', cell: 'string',
|
||||
type: 'text', mode: ['properties', 'create', 'edit'],
|
||||
disabled: 'inSchema'
|
||||
},{
|
||||
id: 'oid', label:'{{ _('OID') }}', cell: 'string',
|
||||
type: 'text' , mode: ['properties']
|
||||
},{
|
||||
id: 'owner', label:'{{ _('Owner') }}', cell: 'string',
|
||||
type: 'text', mode: ['properties', 'create', 'edit'],
|
||||
disabled: 'inSchema', control: 'node-list-by-name', node: 'role'
|
||||
},{
|
||||
id: 'schema', label:'{{ _('Schema') }}', cell: 'string',
|
||||
type: 'text', mode: ['create', 'edit'], node: 'schema',
|
||||
disabled: 'inSchema', 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;
|
||||
},
|
||||
control: Backform.NodeListByNameControl.extend({
|
||||
render: function(){
|
||||
// Initialize parent's render method
|
||||
Backform.NodeListByNameControl.prototype.render.apply(this, arguments);
|
||||
|
||||
// Set schema default value to its parent Schema
|
||||
if(this.model.isNew()){
|
||||
this.model.set({'schema': this.model.node_info.schema.label});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
})
|
||||
},{
|
||||
id: 'copy_collation', label:'{{ _('Copy collation') }}', cell: 'string',
|
||||
control: 'node-ajax-options',
|
||||
type: 'text', mode: ['create', 'edit'], group: 'Definition',
|
||||
url: 'get_collations', disabled: 'inSchemaWithModelCheck',
|
||||
deps: ['locale', 'lc_collate', 'lc_type']
|
||||
},{
|
||||
id: 'locale', label:'{{ _('Locale') }}', cell: 'string',
|
||||
type: 'text', mode: ['create', 'edit'], group: 'Definition',
|
||||
disabled: 'inSchemaWithModelCheck',
|
||||
deps: ['lc_collate', 'lc_type', 'copy_collation']
|
||||
},{
|
||||
id: 'lc_collate', label:'{{ _('LC_COLLATE') }}', cell: 'string',
|
||||
type: 'text', mode: ['properties', 'create', 'edit'], group: 'Definition',
|
||||
deps: ['locale', 'copy_collation'], disabled: 'inSchemaWithModelCheck'
|
||||
},{
|
||||
id: 'lc_type', label:'{{ _('LC_TYPE') }}', cell: 'string',
|
||||
type: 'text', mode: ['properties', 'create', 'edit'], group: 'Definition',
|
||||
disabled: 'inSchemaWithModelCheck',
|
||||
deps: ['locale', 'copy_collation']
|
||||
},{
|
||||
id: 'description', label:'{{ _('Comment') }}', cell: 'string',
|
||||
type: 'multiline', mode: ['properties', 'create', 'edit'],
|
||||
disabled: 'inSchema'
|
||||
}
|
||||
],
|
||||
validate: function() {
|
||||
var err = {},
|
||||
msg = undefined,
|
||||
changedAttrs = this.changed,
|
||||
locale_flag = false,
|
||||
lc_type_flag = false,
|
||||
lc_coll_flag = false,
|
||||
copy_coll_flag = false,
|
||||
msg = undefined,
|
||||
data = this.toJSON();
|
||||
|
||||
this.errorModel.clear();
|
||||
|
||||
if (_.has(changedAttrs,data.name) && _.isUndefined(this.get('name'))
|
||||
|| String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
msg = '{{ _('Name can not be empty!') }}';
|
||||
this.errorModel.set('name', msg);
|
||||
}
|
||||
if (_.has(changedAttrs,data.locale) && (_.isUndefined(this.get('locale'))
|
||||
|| String(this.get('locale')).replace(/^\s+|\s+$/g, '') == '')) {
|
||||
locale_flag = true;
|
||||
}
|
||||
if (_.has(changedAttrs,data.lc_collate) && (_.isUndefined(this.get('lc_collate'))
|
||||
|| String(this.get('lc_collate')).replace(/^\s+|\s+$/g, '') == '')) {
|
||||
lc_coll_flag = true;
|
||||
}
|
||||
if (_.has(changedAttrs,data.lc_type) && (_.isUndefined(this.get('lc_type'))
|
||||
|| String(this.get('lc_type')).replace(/^\s+|\s+$/g, '') == '')) {
|
||||
lc_type_flag = true;
|
||||
}
|
||||
if (_.has(changedAttrs,data.copy_collation) && (_.isUndefined(this.get('copy_collation'))
|
||||
|| String(this.get('copy_collation')).replace(/^\s+|\s+$/g, '') == '')) {
|
||||
copy_coll_flag = true;
|
||||
}
|
||||
if (locale_flag && (lc_coll_flag || lc_coll_flag) && copy_coll_flag) {
|
||||
msg = '{{ _('Incomplete definition, Please provide Locale OR Copy collation OR LC_TYPE/LC_COLLATE!') }}';
|
||||
err['locale'] = msg
|
||||
}
|
||||
return null;
|
||||
},
|
||||
// We will disable everything if we are under catalog node
|
||||
inSchema: function() {
|
||||
if(this.node_info && 'catalog' in this.node_info)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// We will check if we are under schema node & in 'create' mode
|
||||
inSchemaWithModelCheck: function(m) {
|
||||
if(this.node_info && 'schema' in this.node_info)
|
||||
{
|
||||
// Enable copy_collation only if locale & lc_* is not provided
|
||||
if (m.isNew() && this.name == "copy_collation")
|
||||
{
|
||||
if(m.get('locale'))
|
||||
return true;
|
||||
if(m.get('lc_collate') || m.get('lc_type'))
|
||||
return true
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable lc_* only if copy_collation & locale is not provided
|
||||
if (m.isNew() && (this.name == 'lc_collate' || this.name == 'lc_type'))
|
||||
{
|
||||
if(m.get('locale'))
|
||||
return true;
|
||||
if(m.get('copy_collation'))
|
||||
return true
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable localy only if lc_* & copy_collation is not provided
|
||||
if (m.isNew() && this.name == 'locale')
|
||||
{
|
||||
if(m.get('lc_collate') || m.get('lc_type'))
|
||||
return true;
|
||||
if(m.get('copy_collation'))
|
||||
return true
|
||||
return false;
|
||||
}
|
||||
|
||||
// We will disbale control if it's in 'edit' mode
|
||||
if (m.isNew()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
canCreate: function(itemData, item, data) {
|
||||
//If check is false then , we will allow create menu
|
||||
if (data && data.check == false)
|
||||
return true;
|
||||
|
||||
var 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-collation' == 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;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['collation'];
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
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\\%'
|
|
@ -0,0 +1,25 @@
|
|||
{% if data %}
|
||||
CREATE COLLATION {{ conn|qtIdent(data.schema, data.name) }}
|
||||
{# if user has provided lc_collate & lc_type #}
|
||||
{% if data.lc_collate and data.lc_type %}
|
||||
(LC_COLLATE = {{ data.lc_collate|qtLiteral }}, LC_CTYPE = {{ data.lc_type|qtLiteral }});
|
||||
{% endif %}
|
||||
{# if user has provided locale only #}
|
||||
{% if data.locale %}
|
||||
(LOCALE = {{ data.locale|qtLiteral }});
|
||||
{% endif %}
|
||||
{# if user has choosed to copy from existing collation #}
|
||||
{% if data.copy_collation %}
|
||||
FROM {{ data.copy_collation }};
|
||||
{% endif %}
|
||||
{% if data.owner %}
|
||||
|
||||
ALTER COLLATION {{ conn|qtIdent(data.schema, data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.owner) }};
|
||||
{% endif %}
|
||||
{% if data.description %}
|
||||
|
||||
COMMENT ON COLLATION {{ conn|qtIdent(data.schema, data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
{% endif %}
|
||||
{% endif %}
|
|
@ -0,0 +1 @@
|
|||
DROP COLLATION {{name}}{% if cascade%} CASCADE{% endif %};
|
|
@ -0,0 +1,7 @@
|
|||
SELECT --nspname, collname,
|
||||
CASE WHEN length(nspname) > 0 AND length(collname) > 0 THEN
|
||||
concat(quote_ident(nspname), '.', quote_ident(collname))
|
||||
ELSE '' END AS copy_collation
|
||||
FROM pg_collation c, pg_namespace n
|
||||
WHERE c.collnamespace=n.oid
|
||||
ORDER BY nspname, collname;
|
|
@ -0,0 +1,5 @@
|
|||
SELECT concat(quote_ident(nspname), '.', quote_ident(collname)) AS name
|
||||
FROM pg_collation c, pg_namespace n
|
||||
WHERE c.collnamespace = n.oid AND
|
||||
n.oid = {{ scid }}::oid AND
|
||||
c.oid = {{ coid }}::oid;
|
|
@ -0,0 +1,8 @@
|
|||
{# Below will provide oid for newly created collation #}
|
||||
{% if data %}
|
||||
SELECT c.oid
|
||||
FROM pg_collation c, pg_namespace n
|
||||
WHERE c.collnamespace=n.oid AND
|
||||
n.nspname = {{ data.schema|qtLiteral }} AND
|
||||
c.collname = {{ data.name|qtLiteral }}
|
||||
{% endif %}
|
|
@ -0,0 +1,4 @@
|
|||
SELECT c.oid, c.collname AS name
|
||||
FROM pg_collation c
|
||||
WHERE c.collnamespace = {{scid}}::oid
|
||||
ORDER BY c.collname;
|
|
@ -0,0 +1,8 @@
|
|||
SELECT c.oid, c.collname AS name, c.collcollate AS lc_collate, c.collctype AS lc_type,
|
||||
pg_get_userbyid(c.collowner) AS owner, description, n.nspname AS schema
|
||||
FROM pg_collation c
|
||||
JOIN pg_namespace n ON n.oid=c.collnamespace
|
||||
LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid AND des.classoid='pg_collation'::regclass)
|
||||
WHERE c.collnamespace = {{scid}}::oid
|
||||
{% if coid %} AND c.oid = {{coid}}::oid {% endif %}
|
||||
ORDER BY c.collname;
|
|
@ -0,0 +1,26 @@
|
|||
{% if data %}
|
||||
{# Change object's owner #}
|
||||
{% if data.owner and data.owner != o_data.owner %}
|
||||
ALTER COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }}
|
||||
OWNER TO {{ conn|qtIdent(data.owner) }};
|
||||
|
||||
{% endif %}
|
||||
{# Change object's comment #}
|
||||
{% if data.description and data.description != o_data.description %}
|
||||
COMMENT ON COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }}
|
||||
IS {{ data.description|qtLiteral }};
|
||||
|
||||
{% endif %}
|
||||
{# Change object name #}
|
||||
{% if data.name and data.name != o_data.name %}
|
||||
ALTER COLLATION {{ conn|qtIdent(o_data.schema, o_data.name) }}
|
||||
RENAME TO {{ conn|qtIdent(data.name) }};
|
||||
|
||||
{% endif %}
|
||||
{# Change object schema #}
|
||||
{% if data.schema and data.schema != o_data.schema %}
|
||||
ALTER COLLATION {% if data.name and data.name != o_data.name %}{{ conn|qtIdent(o_data.schema, data.name) }}{% else %}{{ conn|qtIdent(o_data.schema, o_data.name) }}{% endif %}
|
||||
|
||||
SET SCHEMA {{ conn|qtIdent(data.schema) }};
|
||||
{% endif %}
|
||||
{% endif %}
|
Loading…
Reference in New Issue