diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py new file mode 100644 index 000000000..e147705db --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/__init__.py @@ -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) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/coll-collation.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/coll-collation.png new file mode 100644 index 000000000..fa46a49b3 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/coll-collation.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/collation.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/collation.png new file mode 100644 index 000000000..0bc8ff7d2 Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/static/img/collation.png differ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/js/collation.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/js/collation.js new file mode 100644 index 000000000..5a78bf3b6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/js/collation.js @@ -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']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/backend_support.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/backend_support.sql new file mode 100644 index 000000000..0d443d09a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/backend_support.sql @@ -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\\%' \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/create.sql new file mode 100644 index 000000000..c36189eff --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/create.sql @@ -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 %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/delete.sql new file mode 100644 index 000000000..5b1f8ce0b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/delete.sql @@ -0,0 +1 @@ +DROP COLLATION {{name}}{% if cascade%} CASCADE{% endif %}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_collations.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_collations.sql new file mode 100644 index 000000000..ff009b7b6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_collations.sql @@ -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; \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_name.sql new file mode 100644 index 000000000..f5dda0058 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_name.sql @@ -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; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_oid.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_oid.sql new file mode 100644 index 000000000..0abdfe6dc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/get_oid.sql @@ -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 %} \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/nodes.sql new file mode 100644 index 000000000..de932b29d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/nodes.sql @@ -0,0 +1,4 @@ +SELECT c.oid, c.collname AS name +FROM pg_collation c +WHERE c.collnamespace = {{scid}}::oid +ORDER BY c.collname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/properties.sql new file mode 100644 index 000000000..3bc9e6f45 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/properties.sql @@ -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; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/update.sql new file mode 100644 index 000000000..570e123ff --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/collations/templates/collation/sql/9.1_plus/update.sql @@ -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 %}