From 39992a817d86a5eb7f221f82d3c3e91bc1f4b950 Mon Sep 17 00:00:00 2001 From: Akshay Joshi Date: Fri, 29 Oct 2021 16:14:33 +0530 Subject: [PATCH] Added support of Aggregate and Operator node in view-only mode. Fixes #3834 --- docs/en_US/release_notes_6_2.rst | 2 +- .../databases/schemas/aggregates/__init__.py | 374 +++++++++++++++++ .../aggregates/static/img/aggregate.svg | 1 + .../aggregates/static/img/coll-aggregate.svg | 1 + .../schemas/aggregates/static/js/aggregate.js | 86 ++++ .../aggregates/static/js/aggregate.ui.js | 185 +++++++++ .../aggregates/sql/11_plus/create.sql | 24 ++ .../aggregates/sql/11_plus/properties.sql | 47 +++ .../aggregates/sql/12_plus/create.sql | 24 ++ .../aggregates/sql/default/create.sql | 22 + .../aggregates/sql/default/delete.sql | 3 + .../aggregates/sql/default/nodes.sql | 14 + .../aggregates/sql/default/properties.sql | 39 ++ .../databases/schemas/operators/__init__.py | 377 ++++++++++++++++++ .../operators/static/img/coll-operator.svg | 1 + .../schemas/operators/static/img/operator.svg | 1 + .../schemas/operators/static/js/operator.js | 86 ++++ .../operators/static/js/operator.ui.js | 125 ++++++ .../operators/sql/11_plus/create.sql | 13 + .../operators/sql/default/create.sql | 13 + .../operators/sql/default/delete.sql | 13 + .../templates/operators/sql/default/nodes.sql | 23 ++ .../operators/sql/default/properties.sql | 25 ++ .../servers/databases/schemas/utils.py | 3 +- .../static/js/SchemaView/MappedControl.jsx | 2 + .../static/js/components/FormComponents.jsx | 4 +- .../schema_ui_files/aggregate.ui.spec.js | 95 +++++ .../schema_ui_files/operator.ui.spec.js | 95 +++++ web/webpack.config.js | 2 + web/webpack.shim.js | 2 + 30 files changed, 1698 insertions(+), 4 deletions(-) create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/__init__.py create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/aggregate.svg create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/coll-aggregate.svg create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.ui.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/12_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/nodes.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/properties.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/__init__.py create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/coll-operator.svg create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/operator.svg create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.ui.js create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/11_plus/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/create.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/delete.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/nodes.sql create mode 100644 web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/properties.sql create mode 100644 web/regression/javascript/schema_ui_files/aggregate.ui.spec.js create mode 100644 web/regression/javascript/schema_ui_files/operator.ui.spec.js diff --git a/docs/en_US/release_notes_6_2.rst b/docs/en_US/release_notes_6_2.rst index 783f924cc..4836fe680 100644 --- a/docs/en_US/release_notes_6_2.rst +++ b/docs/en_US/release_notes_6_2.rst @@ -9,7 +9,7 @@ This release contains a number of bug fixes and new features since the release o New features ************ - +| `Issue #3834 `_ - Added support of Aggregate and Operator node in view-only mode. Housekeeping ************ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/__init__.py new file mode 100644 index 000000000..d6377a334 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/__init__.py @@ -0,0 +1,374 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2021, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +""" Implements Aggregate Node """ + +from functools import wraps + +from flask import render_template +from flask_babelex import gettext + +import pgadmin.browser.server_groups.servers.databases as database +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils \ + import SchemaChildModule +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, internal_server_error, \ + make_response as ajax_response, gone +from pgadmin.utils.compile_template_name import compile_template_path +from pgadmin.utils.driver import get_driver + + +class AggregateModule(SchemaChildModule): + """ + class AggregateModule(SchemaChildModule) + + A module class for Aggregate node derived from SchemaChildModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Aggregate and it's base module. + + * get_nodes(gid, sid, did, scid, agid) + - 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 = 'aggregate' + _COLLECTION_LABEL = gettext("Aggregates") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the AggregateModule and it's base module. + + Args: + *args: + **kwargs: + """ + + super(AggregateModule, 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 = AggregateModule(__name__) + + +class AggregateView(PGChildNodeView): + """ + This class is responsible for generating routes for Aggregate node + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the AggregateView 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 Aggregate nodes within that + collection. + + * nodes() + - This function will used to create all the child node within that + collection, Here it will create all the Aggregate node. + + * properties(gid, sid, did, scid, agid) + - This function will show the properties of the selected Aggregate node + + + * sql(gid, sid, did, scid): + - This function will generate sql to show it in sql pane for the + selected Aggregate node. + """ + + node_type = blueprint.node_type + node_label = "Aggregate" + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'agid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create', 'delete': 'delete'} + ], + 'delete': [{'delete': 'delete'}, {'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}] + }) + + 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']) + self.datlastsysoid = \ + self.manager.db_info[kwargs['did']]['datlastsysoid'] \ + if self.manager.db_info is not None and \ + kwargs['did'] in self.manager.db_info else 0 + + self.datistemplate = False + if ( + self.manager.db_info is not None and + kwargs['did'] in self.manager.db_info and + 'datistemplate' in self.manager.db_info[kwargs['did']] + ): + self.datistemplate = self.manager.db_info[ + kwargs['did']]['datistemplate'] + + # Set the template path for the SQL scripts + self.template_path = compile_template_path( + 'aggregates/sql/', + self.manager.server_type, + self.manager.version + ) + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + This function is used to list all the aggregate nodes within that + collection. + + Args: + gid: Server group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available aggregate nodes + """ + + SQL = render_template("/".join([self.template_path, + self._NODES_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 aggregate node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available aggregate child nodes + """ + + res = [] + SQL = render_template("/".join([self.template_path, + self._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-aggregate" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def node(self, gid, sid, did, scid, agid): + """ + This function will fetch properties of the aggregate node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + agid: Aggregate ID + + Returns: + JSON of given aggregate node + """ + + SQL = render_template("/".join([self.template_path, + self._NODES_SQL]), agid=agid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + return make_json_response( + data=self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-aggregate" + ), + status=200 + ) + + return gone(self.not_found_error_msg()) + + @check_precondition + def properties(self, gid, sid, did, scid, agid): + """ + This function will show the properties of the selected aggregate node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + scid: Schema ID + agid: Aggregate ID + + Returns: + JSON of selected aggregate node + """ + + status, res = self._fetch_properties(scid, agid) + if not status: + return res + + return ajax_response( + response=res, + status=200 + ) + + def _fetch_properties(self, scid, agid): + """ + This function fetch the properties for the specified object. + + :param scid: Schema ID + :param agid: Aggregate ID + """ + + SQL = render_template("/".join([self.template_path, + self._PROPERTIES_SQL]), + scid=scid, agid=agid, + datlastsysoid=self.datlastsysoid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return False, gone(self.not_found_error_msg()) + + res['rows'][0]['is_sys_obj'] = ( + res['rows'][0]['oid'] <= self.datlastsysoid or self.datistemplate) + + return True, res['rows'][0] + + @check_precondition + def sql(self, gid, sid, did, scid, agid, **kwargs): + """ + This function will generates reverse engineered sql for aggregate + object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + agid: Aggregate ID + json_resp: True then return json response + """ + + SQL = render_template("/".join([self.template_path, + self._PROPERTIES_SQL]), + scid=scid, agid=agid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + if len(res['rows']) == 0: + return gone(self.not_found_error_msg()) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + self._CREATE_SQL]), + data=data) + + sql_header = "-- Aggregate: {0};\n\n-- ".format(data['name']) + + sql_header += render_template("/".join([self.template_path, + self._DELETE_SQL]), + data=data) + SQL = sql_header + '\n' + SQL.strip('\n') + + return ajax_response(response=SQL) + + +AggregateView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/aggregate.svg b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/aggregate.svg new file mode 100644 index 000000000..cc7c8e568 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/aggregate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/coll-aggregate.svg b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/coll-aggregate.svg new file mode 100644 index 000000000..c320c2209 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/img/coll-aggregate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.js new file mode 100644 index 000000000..0d9a2f0ab --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.js @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import AggregateSchema from './aggregate.ui'; + +define('pgadmin.node.aggregate', [ + 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', + 'sources/pgadmin', 'pgadmin.browser', + 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection', +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) { + + if (!pgBrowser.Nodes['coll-aggregate']) { + pgAdmin.Browser.Nodes['coll-aggregate'] = + pgAdmin.Browser.Collection.extend({ + node: 'aggregate', + label: gettext('Aggregates'), + type: 'coll-aggregate', + columns: ['name', 'owner', 'description'], + canDrop: false, + canDropCascade: false, + }); + } + + if (!pgBrowser.Nodes['aggregate']) { + pgAdmin.Browser.Nodes['aggregate'] = schemaChild.SchemaChildNode.extend({ + type: 'aggregate', + sqlAlterHelp: 'sql-alteraggregate.html', + sqlCreateHelp: 'sql-createaggregate.html', + label: gettext('Aggregate'), + collection_type: 'coll-aggregate', + hasSQL: true, + hasDepends: false, + canDrop: false, + canDropCascade: false, + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + }, + model: pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'oid', + + // 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: gettext('Aggregate'), cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + },{ + id: 'owner', label: gettext('Owner'), cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + control: 'node-list-by-name', + node: 'role', + },{ + id: 'description', label: gettext('Comment'), cell: 'string', + type: 'multiline', mode: ['properties', 'create', 'edit'], + } + ], + }), + getSchema: ()=>{ + let schema = new AggregateSchema(); + return schema; + } + }); + } + return pgBrowser.Nodes['aggregate']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.ui.js new file mode 100644 index 000000000..ace2c86a9 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.ui.js @@ -0,0 +1,185 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import gettext from 'sources/gettext'; + +export default class AggregateSchema extends BaseUISchema { + constructor(fieldOptions = {},initValues) { + super({ + name: undefined, + oid: undefined, + owner: undefined, + description: undefined, + schema: null, + input_types: undefined, + state_type: undefined, + state_func: undefined, + state_data_size: 0, + final_type: undefined, + final_func: undefined, + final_func_modify: undefined, + final_extra_param: undefined, + initial_val: undefined, + moving_state_type: undefined, + moving_state_func: undefined, + moving_state_data_size: 0, + moving_final_type: undefined, + moving_final_func: undefined, + moving_final_func_modify: undefined, + moving_final_extra_param: undefined, + moving_initial_val: undefined, + moving_inverse_func: undefined, + combine_func: undefined, + serialization_func: undefined, + deserialization_func: undefined, + sort_oper: undefined, + ...initValues + }); + this.fieldOptions = fieldOptions; + } + + get idAttribute() { + return 'oid'; + } + + get baseFields() { + return [ + { + id: 'name', label: gettext('Name'), + type: 'text', readonly: true, + }, + { + id: 'oid', label: gettext('OID'), + type: 'text', mode: ['properties'], + }, + { + id: 'owner', label: gettext('Owner'), + type: 'text', readonly: true, + }, + { + id: 'schema', label: gettext('Schema'), + mode: ['create', 'edit'], + type: 'text', readonly: true, + }, + { + id: 'is_sys_obj', label: gettext('System aggregate?'), + cell: 'boolean', type: 'switch', mode: ['properties'], + }, + { + id: 'description', label: gettext('Comment'), + type: 'multiline', mode: ['properties', 'create', 'edit'], + readonly: true, + }, + { + id: 'input_types', label: gettext('Input types'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'state_type', label: gettext('State type'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'state_func', label: gettext('State function'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'state_data_size', label: gettext('State data size'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'final_type', label: gettext('Final type'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'final_func', label: gettext('Final function'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'final_func_modify', label: gettext('Final function modify'), + group: gettext('Options'), min_version: 110000, + type: 'text', readonly: true, + }, + { + id: 'final_extra_param', label: gettext('Pass extra arguments to final function'), + group: gettext('Options'), type: 'switch', readonly: true, + }, + { + id: 'initial_val', label: gettext('Initial condition'), + group: gettext('Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_state_type', label: gettext('State type'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_state_func', label: gettext('State function'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_state_data_size', label: gettext('State data size'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_final_func', label: gettext('Final function'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_final_func_modify', label: gettext('Final function modify'), + group: gettext('Moving Options'), min_version: 110000, + type: 'text', readonly: true, + }, + { + id: 'moving_final_extra_param', label: gettext('Pass extra arguments to final function'), + group: gettext('Moving Options'), type: 'switch', readonly: true, + }, + { + id: 'moving_inverse_func', label: gettext('Inverse function'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'moving_initial_val', label: gettext('Initial condition'), + group: gettext('Moving Options'), + type: 'text', readonly: true, + }, + { + id: 'sort_oper', label: gettext('Sort operator'), + group: gettext('Advanced'), + type: 'text', readonly: true, + }, + { + id: 'combine_func', label: gettext('Combine function'), + group: gettext('Advanced'), + type: 'text', readonly: true, + }, + { + id: 'serialization_func', label: gettext('Serialization function'), + group: gettext('Advanced'), + type: 'text', readonly: true, + }, + { + id: 'deserialization_func', label: gettext('Deserialization function'), + group: gettext('Advanced'), + type: 'text', readonly: true, + }, + ]; + } +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/create.sql new file mode 100644 index 000000000..758106a9b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/create.sql @@ -0,0 +1,24 @@ +{% if data %} +CREATE AGGREGATE {{conn|qtIdent(data.schema, data.name)}}({% if data.input_types %}{{data.input_types}}{% endif %}) ( + SFUNC = {{data.state_func}}, + STYPE = {{data.state_type}} {% if data.state_data_size %}, + SSPACE = {{data.state_data_size}}{% endif %}{% if data.final_func %}, + FINALFUNC = {{data.final_func}}{% endif %}{% if data.final_extra_param %}, + FINALFUNC_EXTRA{% endif %}{% if data.final_func_modify %}, + FINALFUNC_MODIFY = {{data.final_func_modify}}{% endif %}{% if data.combine_func %}, + COMBINEFUNC = {{data.combine_func}}{% endif %}{% if data.serialization_func %}, + SERIALFUNC = {{data.serialization_func}}{% endif %}{% if data.deserialization_func %}, + DESERIALFUNC = {{data.deserialization_func}}{% endif %}{% if data.initial_val %}, + INITCOND = '{{data.initial_val}}'::text{% endif %}{% if data.moving_state_func %}, + MSFUNC = {{data.moving_state_func}}{% endif %}{% if data.moving_inverse_func %}, + MINVFUNC = {{data.moving_inverse_func}}{% endif %}{% if data.moving_state_type %}, + MSTYPE = {{data.moving_state_type}}{% endif %}{% if data.moving_state_data_size %}, + MSSPACE = {{data.moving_state_data_size}}{% endif %}{% if data.moving_final_func %}, + MFINALFUNC = {{data.moving_final_func}}{% endif %}{% if data.moving_final_extra_param %}, + MFINALFUNC_EXTRA{% endif %}{% if data.moving_final_func_modify %}, + MFINALFUNC_MODIFY = {{data.moving_final_func_modify}}{% endif %}{% if data.moving_initial_val %}, + MINITCOND = '{{data.moving_initial_val}}'::text{% endif %}{% if data.sort_oper %}, + SORTOP = {{data.sort_oper}}{% endif %} + +); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/properties.sql new file mode 100644 index 000000000..cd0d4e8bc --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/11_plus/properties.sql @@ -0,0 +1,47 @@ +SELECT aggfnoid::oid as oid, proname as name, ns.nspname as schema, + pg_catalog.pg_get_userbyid(proowner) as owner, + COALESCE(pg_catalog.pg_get_function_arguments(aggfnoid::oid), '') as input_types, proacl, + CASE WHEN ag.aggkind = 'n' THEN 'normal' + WHEN ag.aggkind = 'o' THEN 'ordered-set' + WHEN ag.aggkind = 'h' THEN 'hypothetical-set' + ELSE 'unknown' END as kind, + CASE WHEN aggtransfn = '-'::regproc THEN null ELSE aggtransfn END as state_func, + CASE WHEN aggfinalfn = '-'::regproc THEN null ELSE aggfinalfn END as final_func, + CASE WHEN aggcombinefn = '-'::regproc THEN null ELSE aggcombinefn END as combine_func, + CASE WHEN aggserialfn = '-'::regproc THEN null ELSE aggserialfn END as serialization_func, + CASE WHEN aggdeserialfn = '-'::regproc THEN null ELSE aggdeserialfn END as deserialization_func, + CASE WHEN aggmtransfn = '-'::regproc THEN null ELSE aggmtransfn END as moving_state_func, + CASE WHEN aggmfinalfn = '-'::regproc THEN null ELSE aggmfinalfn END as moving_final_func, + CASE WHEN aggminvtransfn = '-'::regproc THEN null ELSE aggminvtransfn END as moving_inverse_func, + CASE WHEN ag.aggfinalmodify = 'r' THEN 'READ_ONLY' + WHEN ag.aggfinalmodify = 's' THEN 'SHAREABLE' + WHEN ag.aggfinalmodify = 'w' THEN 'READ_WRITE' + ELSE 'unknown' END as final_func_modify, + CASE WHEN ag.aggmfinalmodify = 'r' THEN 'READ_ONLY' + WHEN ag.aggmfinalmodify = 's' THEN 'SHAREABLE' + WHEN ag.aggmfinalmodify = 'w' THEN 'READ_WRITE' + ELSE 'unknown' END as moving_final_func_modify, + agginitval as initial_val, aggminitval as moving_initial_val, + op.oprname as sort_oper, aggfinalextra as final_extra_param, aggmfinalextra as moving_final_extra_param, + aggtransspace as state_data_size, aggmtransspace as moving_state_data_size, + CASE WHEN (tt.typlen = -1 AND tt.typelem != 0) THEN + (SELECT at.typname FROM pg_type at WHERE at.oid = tt.typelem) || '[]' + ELSE tt.typname END as state_type, + CASE WHEN (tf.typlen = -1 AND tf.typelem != 0) THEN + (SELECT at.typname FROM pg_catalog.pg_type at WHERE at.oid = tf.typelem) || '[]' + ELSE tf.typname END as final_type, + CASE WHEN (tm.typlen = -1 AND tm.typelem != 0) THEN + (SELECT at.typname FROM pg_type at WHERE at.oid = tm.typelem) || '[]' + ELSE tm.typname END as moving_state_type, + description +FROM pg_catalog.pg_aggregate ag + LEFT OUTER JOIN pg_catalog.pg_proc pr ON pr.oid = ag.aggfnoid + LEFT OUTER JOIN pg_catalog.pg_namespace ns ON ns.oid=pr.pronamespace + LEFT OUTER JOIN pg_catalog.pg_type tt on tt.oid=aggtranstype + LEFT OUTER JOIN pg_catalog.pg_type tf on tf.oid=prorettype + LEFT OUTER JOIN pg_catalog.pg_type tm on tm.oid=aggmtranstype + LEFT OUTER JOIN pg_catalog.pg_operator op on op.oid = ag.aggsortop + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=aggfnoid::oid AND des.classoid='pg_aggregate'::regclass) +WHERE pronamespace = {{scid}}::oid +{% if agid %} AND ag.aggfnoid = {{agid}}::oid {% endif %} +ORDER BY name; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/12_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/12_plus/create.sql new file mode 100644 index 000000000..750c23830 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/12_plus/create.sql @@ -0,0 +1,24 @@ +{% if data %} +CREATE OR REPLACE AGGREGATE {{conn|qtIdent(data.schema, data.name)}}({% if data.input_types %}{{data.input_types}}{% endif %}) ( + SFUNC = {{data.state_func}}, + STYPE = {{data.state_type}} {% if data.state_data_size %}, + SSPACE = {{data.state_data_size}}{% endif %}{% if data.final_func %}, + FINALFUNC = {{data.final_func}}{% endif %}{% if data.final_extra_param %}, + FINALFUNC_EXTRA{% endif %}{% if data.final_func_modify %}, + FINALFUNC_MODIFY = {{data.final_func_modify}}{% endif %}{% if data.combine_func %}, + COMBINEFUNC = {{data.combine_func}}{% endif %}{% if data.serialization_func %}, + SERIALFUNC = {{data.serialization_func}}{% endif %}{% if data.deserialization_func %}, + DESERIALFUNC = {{data.deserialization_func}}{% endif %}{% if data.initial_val %}, + INITCOND = '{{data.initial_val}}'::text{% endif %}{% if data.moving_state_func %}, + MSFUNC = {{data.moving_state_func}}{% endif %}{% if data.moving_inverse_func %}, + MINVFUNC = {{data.moving_inverse_func}}{% endif %}{% if data.moving_state_type %}, + MSTYPE = {{data.moving_state_type}}{% endif %}{% if data.moving_state_data_size %}, + MSSPACE = {{data.moving_state_data_size}}{% endif %}{% if data.moving_final_func %}, + MFINALFUNC = {{data.moving_final_func}}{% endif %}{% if data.moving_final_extra_param %}, + MFINALFUNC_EXTRA{% endif %}{% if data.moving_final_func_modify %}, + MFINALFUNC_MODIFY = {{data.moving_final_func_modify}}{% endif %}{% if data.moving_initial_val %}, + MINITCOND = '{{data.moving_initial_val}}'::text{% endif %}{% if data.sort_oper %}, + SORTOP = {{data.sort_oper}}{% endif %} + +); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/create.sql new file mode 100644 index 000000000..c59078c1e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/create.sql @@ -0,0 +1,22 @@ +{% if data %} +CREATE AGGREGATE {{conn|qtIdent(data.schema, data.name)}}({% if data.input_types %}{{data.input_types}}{% endif %}) ( + SFUNC = {{data.state_func}}, + STYPE = {{data.state_type}} {% if data.state_data_size %}, + SSPACE = {{data.state_data_size}}{% endif %}{% if data.final_func %}, + FINALFUNC = {{data.final_func}}{% endif %}{% if data.final_extra_param %}, + FINALFUNC_EXTRA{% endif %}{% if data.combine_func %}, + COMBINEFUNC = {{data.combine_func}}{% endif %}{% if data.serialization_func %}, + SERIALFUNC = {{data.serialization_func}}{% endif %}{% if data.deserialization_func %}, + DESERIALFUNC = {{data.deserialization_func}}{% endif %}{% if data.initial_val %}, + INITCOND = '{{data.initial_val}}'::text{% endif %}{% if data.moving_state_func %}, + MSFUNC = {{data.moving_state_func}}{% endif %}{% if data.moving_inverse_func %}, + MINVFUNC = {{data.moving_inverse_func}}{% endif %}{% if data.moving_state_type %}, + MSTYPE = {{data.moving_state_type}}{% endif %}{% if data.moving_state_data_size %}, + MSSPACE = {{data.moving_state_data_size}}{% endif %}{% if data.moving_final_func %}, + MFINALFUNC = {{data.moving_final_func}}{% endif %}{% if data.moving_final_extra_param %}, + MFINALFUNC_EXTRA{% endif %}{% if data.moving_initial_val %}, + MINITCOND = '{{data.moving_initial_val}}'::text{% endif %}{% if data.sort_oper %}, + SORTOP = {{data.sort_oper}}{% endif %} + +); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/delete.sql new file mode 100644 index 000000000..f3c7c966e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/delete.sql @@ -0,0 +1,3 @@ +{% if data %} +DROP AGGREGATE IF EXISTS {{conn|qtIdent(data.schema, data.name)}}({% if data.input_types %}{{data.input_types}}{% endif %}){% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/nodes.sql new file mode 100644 index 000000000..885b65e9a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/nodes.sql @@ -0,0 +1,14 @@ +SELECT aggfnoid::oid as oid, + proname || '(' || COALESCE(pg_catalog.pg_get_function_arguments(aggfnoid::oid), '') || ')' AS name, + pg_catalog.pg_get_userbyid(proowner) AS owner +FROM pg_aggregate ag + LEFT OUTER JOIN pg_catalog.pg_proc pr ON pr.oid = ag.aggfnoid + LEFT OUTER JOIN pg_catalog.pg_type tt on tt.oid=aggtranstype + LEFT OUTER JOIN pg_catalog.pg_type tf on tf.oid=prorettype + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=aggfnoid::oid AND des.classoid='pg_aggregate'::regclass) +{% if scid %} + WHERE pronamespace = {{scid}}::oid +{% elif agid %} + WHERE ag.aggfnoid = {{agid}}::oid +{% endif %} +ORDER BY name; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/properties.sql new file mode 100644 index 000000000..0b86bd478 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/aggregates/templates/aggregates/sql/default/properties.sql @@ -0,0 +1,39 @@ +SELECT aggfnoid::oid as oid, proname as name, ns.nspname as schema, + pg_catalog.pg_get_userbyid(proowner) as owner, + COALESCE(pg_catalog.pg_get_function_arguments(aggfnoid::oid), '') as input_types, proacl, + CASE WHEN ag.aggkind = 'n' THEN 'normal' + WHEN ag.aggkind = 'o' THEN 'ordered-set' + WHEN ag.aggkind = 'h' THEN 'hypothetical-set' + ELSE 'unknown' END as kind, + CASE WHEN aggtransfn = '-'::regproc THEN null ELSE aggtransfn END as state_func, + CASE WHEN aggfinalfn = '-'::regproc THEN null ELSE aggfinalfn END as final_func, + CASE WHEN aggcombinefn = '-'::regproc THEN null ELSE aggcombinefn END as combine_func, + CASE WHEN aggserialfn = '-'::regproc THEN null ELSE aggserialfn END as serialization_func, + CASE WHEN aggdeserialfn = '-'::regproc THEN null ELSE aggdeserialfn END as deserialization_func, + CASE WHEN aggmtransfn = '-'::regproc THEN null ELSE aggmtransfn END as moving_state_func, + CASE WHEN aggmfinalfn = '-'::regproc THEN null ELSE aggmfinalfn END as moving_final_func, + CASE WHEN aggminvtransfn = '-'::regproc THEN null ELSE aggminvtransfn END as moving_inverse_func, + agginitval as initial_val, aggminitval as moving_initial_val, + op.oprname as sort_oper, aggfinalextra as final_extra_param, aggmfinalextra as moving_final_extra_param, + aggtransspace as state_data_size, aggmtransspace as moving_state_data_size, + CASE WHEN (tt.typlen = -1 AND tt.typelem != 0) THEN + (SELECT at.typname FROM pg_type at WHERE at.oid = tt.typelem) || '[]' + ELSE tt.typname END as state_type, + CASE WHEN (tf.typlen = -1 AND tf.typelem != 0) THEN + (SELECT at.typname FROM pg_catalog.pg_type at WHERE at.oid = tf.typelem) || '[]' + ELSE tf.typname END as final_type, + CASE WHEN (tm.typlen = -1 AND tm.typelem != 0) THEN + (SELECT at.typname FROM pg_type at WHERE at.oid = tm.typelem) || '[]' + ELSE tm.typname END as moving_state_type, + description +FROM pg_catalog.pg_aggregate ag + LEFT OUTER JOIN pg_catalog.pg_proc pr ON pr.oid = ag.aggfnoid + LEFT OUTER JOIN pg_catalog.pg_namespace ns ON ns.oid=pr.pronamespace + LEFT OUTER JOIN pg_catalog.pg_type tt on tt.oid=aggtranstype + LEFT OUTER JOIN pg_catalog.pg_type tf on tf.oid=prorettype + LEFT OUTER JOIN pg_catalog.pg_type tm on tm.oid=aggmtranstype + LEFT OUTER JOIN pg_catalog.pg_operator op on op.oid = ag.aggsortop + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=aggfnoid::oid AND des.classoid='pg_aggregate'::regclass) +WHERE pronamespace = {{scid}}::oid +{% if agid %} AND ag.aggfnoid = {{agid}}::oid {% endif %} +ORDER BY name; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/__init__.py new file mode 100644 index 000000000..aa72b34d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/__init__.py @@ -0,0 +1,377 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2021, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +""" Implements Operator Node """ + +from functools import wraps + +from flask import render_template +from flask_babelex import gettext + +import pgadmin.browser.server_groups.servers.databases as database +from config import PG_DEFAULT_DRIVER +from pgadmin.browser.server_groups.servers.databases.schemas.utils \ + import SchemaChildModule +from pgadmin.browser.utils import PGChildNodeView +from pgadmin.utils.ajax import make_json_response, internal_server_error, \ + make_response as ajax_response, gone +from pgadmin.utils.compile_template_name import compile_template_path +from pgadmin.utils.driver import get_driver + + +class OperatorModule(SchemaChildModule): + """ + class OperatorModule(SchemaChildModule) + + A module class for Operator node derived from SchemaChildModule. + + Methods: + ------- + * __init__(*args, **kwargs) + - Method is used to initialize the Operator and it's base module. + + * get_nodes(gid, sid, did, scid, opid) + - 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 = 'operator' + _COLLECTION_LABEL = gettext("Operators") + + def __init__(self, *args, **kwargs): + """ + Method is used to initialize the OperatorModule and it's base module. + + Args: + *args: + **kwargs: + """ + + super(OperatorModule, 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 = OperatorModule(__name__) + + +class OperatorView(PGChildNodeView): + """ + This class is responsible for generating routes for Operator node + + Methods: + ------- + * __init__(**kwargs) + - Method is used to initialize the OperatorView 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 Operator nodes within that + collection. + + * nodes() + - This function will used to create all the child node within that + collection, Here it will create all the Operator node. + + * properties(gid, sid, did, scid, opid) + - This function will show the properties of the selected Operator node + + * sql(gid, sid, did, scid): + - This function will generate sql to show it in sql pane for the + selected Operator node. + """ + + node_type = blueprint.node_type + node_label = "Operator" + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'}, + {'type': 'int', 'id': 'scid'} + ] + ids = [ + {'type': 'int', 'id': 'opid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create', 'delete': 'delete'} + ], + 'delete': [{'delete': 'delete'}, {'delete': 'delete'}], + 'children': [{'get': 'children'}], + 'nodes': [{'get': 'node'}, {'get': 'nodes'}], + 'sql': [{'get': 'sql'}] + }) + + 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']) + self.datlastsysoid = \ + self.manager.db_info[kwargs['did']]['datlastsysoid'] \ + if self.manager.db_info is not None and \ + kwargs['did'] in self.manager.db_info else 0 + + self.datistemplate = False + if ( + self.manager.db_info is not None and + kwargs['did'] in self.manager.db_info and + 'datistemplate' in self.manager.db_info[kwargs['did']] + ): + self.datistemplate = self.manager.db_info[ + kwargs['did']]['datistemplate'] + + # Set the template path for the SQL scripts + self.template_path = compile_template_path( + 'operators/sql/', + self.manager.server_type, + self.manager.version + ) + + return f(*args, **kwargs) + + return wrap + + @check_precondition + def list(self, gid, sid, did, scid): + """ + This function is used to list all the operator nodes within that + collection. + + Args: + gid: Server group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available operator nodes + """ + + SQL = render_template("/".join([self.template_path, + self._NODES_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 operator node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + + Returns: + JSON of available operator child nodes + """ + + res = [] + SQL = render_template("/".join([self.template_path, + self._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-operator" + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def node(self, gid, sid, did, scid, opid): + """ + This function will fetch properties of the operator node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + opid: Operator ID + + Returns: + JSON of given operator node + """ + + SQL = render_template("/".join([self.template_path, + self._NODES_SQL]), opid=opid) + status, rset = self.conn.execute_2darray(SQL) + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + return make_json_response( + data=self.blueprint.generate_browser_node( + row['oid'], + scid, + row['name'], + icon="icon-operator" + ), + status=200 + ) + + return gone(self.not_found_error_msg()) + + @check_precondition + def properties(self, gid, sid, did, scid, opid): + """ + This function will show the properties of the selected operator node. + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + scid: Schema ID + opid: Operator ID + + Returns: + JSON of selected operator node + """ + + status, res = self._fetch_properties(scid, opid) + if not status: + return res + + return ajax_response( + response=res, + status=200 + ) + + def _fetch_properties(self, scid, opid): + """ + This function fetch the properties for the specified object. + + :param scid: Schema ID + :param opid: Operator ID + """ + + SQL = render_template("/".join([self.template_path, + self._PROPERTIES_SQL]), + scid=scid, opid=opid, + datlastsysoid=self.datlastsysoid) + status, res = self.conn.execute_dict(SQL) + + if not status: + return False, internal_server_error(errormsg=res) + + if len(res['rows']) == 0: + return False, gone(self.not_found_error_msg()) + + res['rows'][0]['is_sys_obj'] = ( + res['rows'][0]['oid'] <= self.datlastsysoid or self.datistemplate) + + return True, res['rows'][0] + + @check_precondition + def sql(self, gid, sid, did, scid, opid): + """ + This function will generates reverse engineered sql for operator + object + + Args: + gid: Server Group ID + sid: Server ID + did: Database ID + scid: Schema ID + opid: Operator ID + json_resp: True then return json response + """ + + SQL = render_template("/".join([self.template_path, + self._PROPERTIES_SQL]), + scid=scid, opid=opid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + if len(res['rows']) == 0: + return gone(self.not_found_error_msg()) + + data = res['rows'][0] + + SQL = render_template("/".join([self.template_path, + self._CREATE_SQL]), + data=data) + + sql_header = "-- Operator: {0};\n\n-- ".format(data['name']) + + sql_header += render_template("/".join([self.template_path, + self._DELETE_SQL]), + name=data['name'], + oprnamespace=data['schema'], + lefttype=data['lefttype'], + righttype=data['righttype'], + ) + SQL = sql_header + '\n' + SQL.strip('\n') + + return ajax_response(response=SQL) + + +OperatorView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/coll-operator.svg b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/coll-operator.svg new file mode 100644 index 000000000..5ad8f7235 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/coll-operator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/operator.svg b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/operator.svg new file mode 100644 index 000000000..80e5ae26f --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/img/operator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.js new file mode 100644 index 000000000..1cefcb5d2 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.js @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import OperatorSchema from './operator.ui'; + +define('pgadmin.node.operator', [ + 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', + 'sources/pgadmin', 'pgadmin.browser', + 'pgadmin.node.schema.dir/child', 'pgadmin.browser.collection', +], function(gettext, url_for, $, _, pgAdmin, pgBrowser, schemaChild) { + + if (!pgBrowser.Nodes['coll-operator']) { + pgAdmin.Browser.Nodes['coll-operator'] = + pgAdmin.Browser.Collection.extend({ + node: 'operator', + label: gettext('Operators'), + type: 'coll-operator', + columns: ['name', 'owner', 'description'], + canDrop: false, + canDropCascade: false, + }); + } + + if (!pgBrowser.Nodes['operator']) { + pgAdmin.Browser.Nodes['operator'] = schemaChild.SchemaChildNode.extend({ + type: 'operator', + sqlAlterHelp: 'sql-alteroperator.html', + sqlCreateHelp: 'sql-createoperator.html', + label: gettext('Operator'), + collection_type: 'coll-operator', + hasSQL: true, + hasDepends: false, + canDrop: false, + canDropCascade: false, + Init: function() { + /* Avoid mulitple registration of menus */ + if (this.initialized) + return; + + this.initialized = true; + }, + model: pgAdmin.Browser.Node.Model.extend({ + idAttribute: 'oid', + + // 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: gettext('Operator'), cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + },{ + id: 'owner', label: gettext('Owner'), cell: 'string', + type: 'text', mode: ['properties', 'create', 'edit'], + control: 'node-list-by-name', + node: 'role', + },{ + id: 'description', label: gettext('Comment'), cell: 'string', + type: 'multiline', mode: ['properties', 'create', 'edit'], + } + ], + }), + getSchema: ()=>{ + let schema = new OperatorSchema(); + return schema; + } + }); + } + return pgBrowser.Nodes['operator']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.ui.js new file mode 100644 index 000000000..e30fb6c43 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.ui.js @@ -0,0 +1,125 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import gettext from 'sources/gettext'; + +export default class OperatorSchema extends BaseUISchema { + constructor(fieldOptions = {},initValues) { + super({ + name: undefined, + oid: undefined, + owner: undefined, + description: undefined, + schema: null, + lefttype: undefined, + righttype: undefined, + operproc:undefined, + joinproc: undefined, + restrproc: undefined, + commutator: undefined, + negator:undefined, + support_hash: false, + support_merge: false, + ...initValues + }); + this.fieldOptions = fieldOptions; + } + + get idAttribute() { + return 'oid'; + } + + get baseFields() { + return [ + { + id: 'name', label: gettext('Name'), type: 'tel', + readonly: true, + }, + { + id: 'oid', label: gettext('OID'), + type: 'text', mode: ['properties'] + }, + { + id: 'owner', label: gettext('Owner'), + type: 'text', readonly: true, + }, + { + id: 'schema', label: gettext('Schema'), + mode: ['create', 'edit'], + type: 'text', readonly: true, + }, + { + id: 'is_sys_obj', label: gettext('System operator?'), + cell: 'boolean', type: 'switch', mode: ['properties'], + }, + { + id: 'description', label: gettext('Comment'), + type: 'multiline', mode: ['properties', 'create', 'edit'], + readonly: true, + }, + { + id: 'lefttype', label: gettext('Left type'), + group: gettext('Definition'), + type: 'text', readonly: true, + }, + { + id: 'righttype', label: gettext('Right type'), + group: gettext('Definition'), + type: 'text', readonly: true, + }, + { + id: 'resulttype', label: gettext('Result type'), + group: gettext('Definition'), + type: 'text', mode: ['properties'], + }, + { + id: 'oprkind', label: gettext('Kind'), + group: gettext('Definition'), + type: 'text', mode: ['properties'], + }, + { + id: 'operproc', label: gettext('Operator function'), + group: gettext('Definition'), + type: 'text', readonly: true, + }, + { + id: 'restrproc', label: gettext('Restrict function'), + group: gettext('Implementation'), + type: 'text', readonly: true, + }, + { + id: 'joinproc', label: gettext('Join function'), + group: gettext('Implementation'), + type: 'text', readonly: true, + }, + { + id: 'commutator', label: gettext('Commutator'), + group: gettext('Implementation'), + type: 'text', readonly: true, + }, + { + id: 'negator', label: gettext('Negator'), + group: gettext('Implementation'), + type: 'text', readonly: true, + }, + { + id: 'support_hash', label: gettext('Supports hash'), + group: gettext('Implementation'), + cell: 'boolean', type: 'switch', readonly: true, + }, + { + id: 'support_merge', label: gettext('Supports merge'), + group: gettext('Implementation'), + cell: 'boolean', type: 'switch', readonly: true, + } + ]; + } +} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/11_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/11_plus/create.sql new file mode 100644 index 000000000..c15382ce5 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/11_plus/create.sql @@ -0,0 +1,13 @@ +{% if data %} +CREATE OPERATOR {{data.schema}}.{{data.name}} ( + FUNCTION = {{data.operproc}}{% if data.lefttype %}, + LEFTARG = {{data.lefttype}}{% endif %}{% if data.righttype %}, + RIGHTARG = {{data.righttype}}{% endif %}{% if data.commutator %}, + COMMUTATOR = {{data.commutator}}{% endif %}{% if data.negator %}, + NEGATOR = {{data.negator}}{% endif %}{% if data.restrproc %}, + RESTRICT = {{data.restrproc}}{% endif %}{% if data.joinproc %}, + JOIN = {{data.joinproc}}{% endif %}{% if data.support_hash %}, + HASHES{% endif %}{% if data.support_merge %}, MERGES{% endif %} + +); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/create.sql new file mode 100644 index 000000000..6c3158074 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/create.sql @@ -0,0 +1,13 @@ +{% if data %} +CREATE OPERATOR {{data.schema}}.{{data.name}} ( + PROCEDURE = {{data.operproc}}{% if data.lefttype %}, + LEFTARG = {{data.lefttype}}{% endif %}{% if data.righttype %}, + RIGHTARG = {{data.righttype}}{% endif %}{% if data.commutator %}, + COMMUTATOR = {{data.commutator}}{% endif %}{% if data.negator %}, + NEGATOR = {{data.negator}}{% endif %}{% if data.restrproc %}, + RESTRICT = {{data.restrproc}}{% endif %}{% if data.joinproc %}, + JOIN = {{data.joinproc}}{% endif %}{% if data.support_hash %}, + HASHES{% endif %}{% if data.support_merge %}, MERGES{% endif %} + +); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/delete.sql new file mode 100644 index 000000000..63b93b772 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/delete.sql @@ -0,0 +1,13 @@ +{% if lefttype %} +{% set ltype = lefttype %} +{% else %} +{% set ltype = none %} +{% endif %} +{% if righttype %} +{% set rtype = righttype %} +{% else %} +{% set rtype = none %} +{% endif %} +{% if name %} +DROP OPERATOR IF EXISTS {{conn|qtIdent(oprnamespace)}}.{{name}} ({{ltype}} , {{rtype}}){% if cascade %} CASCADE{% endif %}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/nodes.sql new file mode 100644 index 000000000..ec8322c8e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/nodes.sql @@ -0,0 +1,23 @@ +SELECT op.oid, pg_catalog.pg_get_userbyid(op.oprowner) as owner, + CASE WHEN lt.typname IS NOT NULL AND rt.typname IS NOT NULL THEN + op.oprname || ' (' || pg_catalog.format_type(lt.oid, NULL) || ', ' || pg_catalog.format_type(rt.oid, NULL) || ')' + WHEN lt.typname IS NULL AND rt.typname IS NOT NULL THEN + op.oprname || ' (' || pg_catalog.format_type(rt.oid, NULL) || ')' + WHEN lt.typname IS NOT NULL AND rt.typname IS NULL THEN + op.oprname || ' (' || pg_catalog.format_type(lt.oid, NULL) || ')' + ELSE op.oprname || '()' +END as name, +lt.typname as lefttype, rt.typname as righttype +FROM pg_catalog.pg_operator op + LEFT OUTER JOIN pg_catalog.pg_type lt ON lt.oid=op.oprleft + LEFT OUTER JOIN pg_catalog.pg_type rt ON rt.oid=op.oprright + JOIN pg_catalog.pg_type et on et.oid=op.oprresult + LEFT OUTER JOIN pg_catalog.pg_operator co ON co.oid=op.oprcom + LEFT OUTER JOIN pg_catalog.pg_operator ne ON ne.oid=op.oprnegate + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=op.oid AND des.classoid='pg_operator'::regclass) +{% if scid %} + WHERE op.oprnamespace = {{scid}}::oid +{% elif opid %} + WHERE op.oid = {{opid}}::oid +{% endif %} +ORDER BY op.oprname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/properties.sql new file mode 100644 index 000000000..513e8621e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/operators/templates/operators/sql/default/properties.sql @@ -0,0 +1,25 @@ +SELECT op.oid, op.oprname as name, ns.nspname as schema, + pg_catalog.pg_get_userbyid(op.oprowner) as owner, + op.oprcanhash as support_hash, op.oprcanmerge as support_merge, + ns.nspname as schema, + CASE WHEN op.oprkind = 'b' THEN 'infix' + WHEN op.oprkind = 'l' THEN 'prefix' + WHEN op.oprkind = 'r' THEN 'postfix' + ELSE 'unknown' END as oprkind, et.typname as resulttype, + pg_catalog.format_type(lt.oid, NULL) as lefttype, + pg_catalog.format_type(rt.oid, NULL) as righttype, + co.oprname as commutator, op.oprcode as operproc, + ne.oprname as negator, description, + CASE WHEN op.oprrest = '-'::regproc THEN null ELSE op.oprrest END as restrproc, + CASE WHEN op.oprjoin = '-'::regproc THEN null ELSE op.oprjoin END as joinproc +FROM pg_catalog.pg_operator op + LEFT OUTER JOIN pg_catalog.pg_namespace ns ON ns.oid=op.oprnamespace + LEFT OUTER JOIN pg_catalog.pg_type lt ON lt.oid=op.oprleft + LEFT OUTER JOIN pg_catalog.pg_type rt ON rt.oid=op.oprright + JOIN pg_catalog.pg_type et on et.oid=op.oprresult + LEFT OUTER JOIN pg_catalog.pg_operator co ON co.oid=op.oprcom + LEFT OUTER JOIN pg_catalog.pg_operator ne ON ne.oid=op.oprnegate + LEFT OUTER JOIN pg_catalog.pg_description des ON (des.objoid=op.oid AND des.classoid='pg_operator'::regclass) +WHERE op.oprnamespace = {{scid}}::oid +{% if opid %} AND op.oid = {{opid}}::oid {% endif %} +ORDER BY op.oprname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py index e9581fbf6..f9a2943bb 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/utils.py @@ -182,7 +182,8 @@ class DataTypeReader: 'label': row['typname'], 'value': row['typname'], 'typval': typeval, 'precision': precision, 'length': length, 'min_val': min_val, 'max_val': max_val, - 'is_collatable': row['is_collatable'] + 'is_collatable': row['is_collatable'], + 'oid': row['oid'] }) except Exception as e: diff --git a/web/pgadmin/static/js/SchemaView/MappedControl.jsx b/web/pgadmin/static/js/SchemaView/MappedControl.jsx index 30da77f00..cbb9e9ab5 100644 --- a/web/pgadmin/static/js/SchemaView/MappedControl.jsx +++ b/web/pgadmin/static/js/SchemaView/MappedControl.jsx @@ -44,6 +44,8 @@ function MappedFormControlBase({type, value, id, onChange, className, visible, i return ; case 'numeric': return ; + case 'tel': + return ; case 'text': return ; case 'multiline': diff --git a/web/pgadmin/static/js/components/FormComponents.jsx b/web/pgadmin/static/js/components/FormComponents.jsx index 160a2b57c..9da3e0945 100644 --- a/web/pgadmin/static/js/components/FormComponents.jsx +++ b/web/pgadmin/static/js/components/FormComponents.jsx @@ -320,7 +320,7 @@ export const InputText = forwardRef(({ let changeVal = e.target.value; /* For type number, we set type as tel with number regex to get validity.*/ - if(['numeric', 'int'].indexOf(type) > -1) { + if(['numeric', 'int', 'tel'].indexOf(type) > -1) { if(!e.target.validity.valid && changeVal !== '' && changeVal !== '-') { return; } @@ -347,7 +347,7 @@ export const InputText = forwardRef(({ id: cid, maxLength: maxlength, 'aria-describedby': helpid, - ...(type ? {pattern: patterns[type]} : {}) + ...(type ? {pattern: !_.isUndefined(controlProps) && !_.isUndefined(controlProps.pattern) ? controlProps.pattern : patterns[type]} : {}) }} readOnly={Boolean(readonly)} disabled={Boolean(disabled)} diff --git a/web/regression/javascript/schema_ui_files/aggregate.ui.spec.js b/web/regression/javascript/schema_ui_files/aggregate.ui.spec.js new file mode 100644 index 000000000..ed83efc13 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/aggregate.ui.spec.js @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import AggregateSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate.ui'; + + +describe('AggregateSchema', ()=>{ + let mount; + let schemaObj = new AggregateSchema(); + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); +}); + diff --git a/web/regression/javascript/schema_ui_files/operator.ui.spec.js b/web/regression/javascript/schema_ui_files/operator.ui.spec.js new file mode 100644 index 000000000..eb1f451e4 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/operator.ui.spec.js @@ -0,0 +1,95 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import OperatorSchema from '../../../pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator.ui'; + + +describe('OperatorSchema', ()=>{ + let mount; + let schemaObj = new OperatorSchema(); + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); +}); + diff --git a/web/webpack.config.js b/web/webpack.config.js index 54b7af79f..40bf62696 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -506,6 +506,8 @@ module.exports = [{ 'pure|pgadmin.node.table', 'pure|pgadmin.node.partition', 'pure|pgadmin.node.compound_trigger', + 'pure|pgadmin.node.aggregate', + 'pure|pgadmin.node.operator', ], }, }, diff --git a/web/webpack.shim.js b/web/webpack.shim.js index 8b3fa8987..b49d9c144 100644 --- a/web/webpack.shim.js +++ b/web/webpack.shim.js @@ -226,6 +226,7 @@ var webpackShimConfig = { 'pgadmin.node.publication': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/publications/static/js/publication'), 'pgadmin.node.subscription': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/subscriptions/static/js/subscription'), 'pgadmin.node.catalog': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/static/js/catalog'), + 'pgadmin.node.aggregate': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/aggregates/static/js/aggregate'), 'pgadmin.node.catalog_object': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/static/js/catalog_object'), 'pgadmin.node.catalog_object_column': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/catalog_objects/columns/static/js/catalog_object_column'), 'pgadmin.node.check_constraint': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/js/check_constraint'), @@ -255,6 +256,7 @@ var webpackShimConfig = { 'pgadmin.node.index': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index'), 'pgadmin.node.language': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/languages/static/js/language'), 'pgadmin.node.mview': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview'), + 'pgadmin.node.operator': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/operators/static/js/operator'), 'pgadmin.node.package': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/packages/static/js/package'), 'pgadmin.node.partition': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/static/js/partition'), 'pgadmin.node.pga_job': path.join(__dirname, './pgadmin/browser/server_groups/servers/pgagent/static/js/pga_job'),