Please find the updated patch with following changes: 1. corrected copyright. 2. Added proper comment for script_module function in __init__.py file. 3. Renamed collection Node's label to Extensions in extensions.js file.
On Tue, Jan 12, 2016 at 12:44 PM, Surinder Kumar < surinder.ku...@enterprisedb.com> wrote: > Hi, > > Please find attached patch for the extension module. > Please review it and Let me know for any comments. > > > Thanks, > Surinder Kumar >
diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/extensions/__init__.py new file mode 100644 index 0000000..33f3fba --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/__init__.py @@ -0,0 +1,356 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +import json +from flask import render_template, make_response, request, jsonify +from flask.ext.babel import gettext +from pgadmin.utils.ajax import make_json_response, \ + make_response as ajax_response, internal_server_error +from pgadmin.browser.utils import NodeView +from pgadmin.browser.collection import CollectionNodeModule +import pgadmin.browser.server_groups.servers.databases as databases +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 ExtensionModule(CollectionNodeModule): + NODE_TYPE = "extension" + COLLECTION_LABEL = gettext("Extensions") + + def __init__(self, *args, **kwargs): + super(ExtensionModule, self).__init__(*args, **kwargs) + + def get_nodes(self, gid, sid, did): + """ + Generate the collection node + """ + yield self.generate_browser_collection_node(did) + + @property + def script_load(self): + """ + Load the module script for extension, when any of the database node is + initialized. + """ + return databases.DatabaseModule.NODE_TYPE + + +blueprint = ExtensionModule(__name__) + + +class ExtensionView(NodeView): + node_type = blueprint.node_type + + parent_ids = [ + {'type': 'int', 'id': 'gid'}, + {'type': 'int', 'id': 'sid'}, + {'type': 'int', 'id': 'did'} + ] + ids = [ + {'type': 'int', 'id': 'eid'} + ] + + operations = dict({ + 'obj': [ + {'get': 'properties', 'delete': 'delete', 'put': 'update'}, + {'get': 'list', 'post': 'create'} + ], + 'delete': [{'delete': 'delete'}], + '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'}], + 'avails': [{}, {'get': 'avails'}], + 'schemas': [{}, {'get': 'schemas'}], + 'children': [{'get': 'children'}] + }) + + 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.template_path = 'extensions/sql' + + return f(*args, **kwargs) + return wrap + + @check_precondition + def list(self, gid, sid, did): + """ + It fetches all extensions properties and render into properties + tab + """ + SQL = render_template("/".join([self.template_path, 'properties.sql'])) + 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): + """ + It lists down the all extensions under the Extensions Collection node + """ + res = [] + SQL = render_template("/".join([self.template_path, 'properties.sql'])) + 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['eid'], + row['name'], + 'icon-extension' + )) + + return make_json_response( + data=res, + status=200 + ) + + @check_precondition + def properties(self, gid, sid, did, eid): + """ + It fetches the properties of a single extension + and render in properties tab + + """ + SQL = render_template("/".join([self.template_path, 'properties.sql']), eid=eid) + 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 + ) + + @check_precondition + def create(self, gid, sid, did): + """ + This function will creates new the extension object + """ + required_args = [ + 'name', + 'version' + ] + + data = request.form if request.form else json.loads(request.data.decode()) + for arg in required_args: + if arg not in data: + return make_json_response( + status=410, + success=0, + errormsg=gettext( + "Couldn't find the required parameter (%s)." % arg + ) + ) + + status, res = self.conn.execute_dict( + render_template( + "/".join([self.template_path, 'create.sql']), + data=data + ) + ) + + if not status: + return internal_server_error(errormsg=res) + + status, rset = self.conn.execute_dict( + render_template( + "/".join([self.template_path, 'properties.sql']), + ename=data['name'] + ) + ) + + if not status: + return internal_server_error(errormsg=rset) + + for row in rset['rows']: + return jsonify( + node=self.blueprint.generate_browser_node( + row['eid'], + row['name'], + 'icon-extension' + ) + ) + + @check_precondition + def update(self, gid, sid, did, eid): + """ + This function will update extension object + """ + data = request.form if request.form else json.loads(request.data.decode()) + SQL = self.getSQL(gid, sid, data, did, eid) + + try: + if SQL and SQL.strip('\n') and SQL.strip(' '): + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info="Extension updated", + data={ + 'id': eid, + 'sid': sid, + 'gid': gid + } + ) + else: + return make_json_response( + success=1, + info="Nothing to update", + data={ + 'id': did, + 'sid': sid, + 'gid': gid + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def delete(self, gid, sid, did, eid): + """ + This function will delete drop/drop cascade the extension object + """ + cascade = True if self.cmd == 'delete' else False + try: + # check if extension with eid exists + SQL = render_template("/".join([self.template_path, 'delete.sql']), eid=eid) + status, name = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=name) + # drop extension + SQL = render_template("/".join([self.template_path, 'delete.sql']), name=name, cascade=cascade) + status, res = self.conn.execute_scalar(SQL) + if not status: + return internal_server_error(errormsg=res) + + return make_json_response( + success=1, + info=gettext("Extension dropped"), + data={ + 'id': did, + 'sid': sid, + 'gid': gid, + } + ) + + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def msql(self, gid, sid, did, eid=None): + """ + This function to return modified SQL + """ + data = request.args.copy() + SQL = self.getSQL(gid, sid, data, did, eid) + if SQL.strip('\n') and SQL.strip(' '): + return make_json_response( + data=SQL, + status=200 + ) + else: + return make_json_response( + data='-- Modified SQL --', + status=200 + ) + + def getSQL(self, gid, sid, data, did, eid=None): + """ + This function will generate sql from model data + """ + required_args = [ + 'name', + 'schema', + 'version' + ] + try: + if eid is not None: + SQL = render_template("/".join([self.template_path, 'properties.sql']), eid=eid) + status, res = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=res) + old_data = res['rows'][0] + for arg in required_args: + if arg not in data: + data[arg] = old_data[arg] + SQL = render_template("/".join([self.template_path, 'update.sql']), data=data, o_data=old_data) + else: + SQL = render_template("/".join([self.template_path, 'create.sql']), data=data) + return SQL + except Exception as e: + return internal_server_error(errormsg=str(e)) + + @check_precondition + def avails(self, gid, sid, did): + """ + This function with fetch all the available extensions + """ + SQL = render_template("/".join([self.template_path, 'extensions.sql'])) + status, rset = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=rset) + return make_json_response( + data=rset['rows'], + status=200 + ) + + @check_precondition + def schemas(self, gid, sid, did): + """ + This function with fetch all the schemas + """ + SQL = render_template("/".join([self.template_path, 'schemas.sql'])) + status, rset = self.conn.execute_dict(SQL) + if not status: + return internal_server_error(errormsg=rset) + return make_json_response( + data=rset['rows'], + status=200 + ) + + def module_js(self): + """ + This property defines (if javascript) exists for this node. + Override this property for your own logic. + """ + return make_response( + render_template( + "extensions/js/extensions.js", + _=gettext + ), + 200, {'Content-Type': 'application/x-javascript'} + ) + +ExtensionView.register_node_view(blueprint) diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/static/img/coll-extension.png b/web/pgadmin/browser/server_groups/servers/databases/extensions/static/img/coll-extension.png new file mode 100644 index 0000000000000000000000000000000000000000..eed7ca97a33ef595f448b8621168d531f86d51d5 GIT binary patch literal 1017 zcmV<V0|xwwP)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px%_fSk!MS^xZ(#^N3p?R&Od+_Yu$ibj%T^6RorB#bmn!1_5x}2JiUH9|m=i$p{ zRt>7esZx(rkHMB&m|kp}YkF-i+S9qHoNTYCfx^|oK7c>H>Bx7neMEjjfO9vZnQgzg znaszo-`vc-ww7K_0rBeAwyS=gm1jPCJeJI$Gj=n&vyq*XV~~VSeQ+?GkzLu)v+(KC z?&Zpui%@D?5_o7K5L*te<Gd|$E&TiU`}p$t_U!fW;PdR+>*2!a-MZ%4vg6gM;L)L= zk53w479M3BDQ+lfsBo&|xoE3!8)F$jd_LXGn5dUmHEJcE$)m~b(xK3*v*EqB=fkk& zzN6u@o!_pI*P(X7g>k=tf76xP$dIj?SVe9ugSLpn>&}+lsF2#DiP)Wj)|h<Mlh449 zm4!uCPy}dL4Vs8UtCv!Hv4M5cjedPPC}$ujZ6{xNJBocXBx4(;)vk8Mhd_@=5nK-# zWf(t|O0=YEF=rrzvxn93+^yBLT$f*Ao@PIeN0+#kWR+urcQMcJ*SF-sT&rnflVFjw zkaKM%G=n;@$*_-qITl_M!syGG&7n||RE^1*D|;}$)4tfmhrz6AIbIZAnqq{yi*T%V zHhVWtV;&u18I#tdIE_NpzkbrYc9VfMLtPX<ZYx1&B5Z9Om3}8VZ7FiPes9o-TDx%1 zw{6X{Wx%FXorW{5k~_krMv{3VMsze|t!`+}gJR5mHj+XnY9(-;Zb*VhCx$c1uU*Hh zRmG=Gz@R^havV*1JY33nR>*Toyk;|+MLMZbK)_+XojHAH5m<yvR;FT5#&1i+YDL0k zJHK6_k4uhwGlp|1i+C)!pi;J+M~ZYIf@~RtZ5D4_30jF#R;gq*y;+2KJ*J&=$it|K zbtb^1PrI~}#=)h;zM@}|T^wm05?&D+Y8^J0MJ<m!6k`>&nL&4HC>d%SP=`*4d_$3X zCoGaZfT(|KS__tZB7UZQKD%8OXc&KL7J+LO8+Rm0f=C!{9T#mJ7UDFE00001bW%=J z06^y0W&i*H0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru+XEXB z6Cp(Rbcp}}0B%V{K~xyiU68R2z#t3+nS(tt!3~hY^vGg42FL=`EENNm0{M!6-y1;) zQxFazvL_cK*b<_okFg0d8Hc#V=Eh`)h+yZmplehnlNZEdtgQ@hX0CNeIWEpxw!oyN ndc~tsp9TlidOzOC&-)*|KH)3pD2ngr00000NkvXXu0mjf<KNUz literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/static/img/extension.png b/web/pgadmin/browser/server_groups/servers/databases/extensions/static/img/extension.png new file mode 100644 index 0000000000000000000000000000000000000000..e3c533347883fc1dec73bcb21b32fc54e3c31732 GIT binary patch literal 996 zcmV<A0~`E_P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0Itp) z=>Px%`cO<%MQ2zLf_6I6&9|zdd99>-@a*2m!Junh7N)|bRf|-bx|zVboSKha_w(lG z;mc-L4XVVcQjb)R!IoN>UTm6cdTlP+)48aeY_F(+!qvh)fIq$I$ak@QM1Demb2p-y zZNIpg%*U?Z+|0eUmR?Q)@#@yLtA3u9XFhv8mdv0tb~C!Ok)4xckc3Wsa4?*aUD?pH z@afX-<;s|gP-<HecxWLITMn+{ye)Dq{QLL&`11Mo?Dg>A^X%E{;lk+My5`xk<JGC) z(V?J^Pa0qr9%URUZYXJ}aH`|EXsd7=V;MkvKHbcisFznYY9*e@qsi^kq0p+c;k~!# z!?5MPqv5li->#6?p?1NAale3n)0NuDkgb|nMQ$vEwur;)&X(P%klLe(*qwsbn0(Ze z&%lqBg+*3S1ZY?dnutTImr{GNfpyZ2etkM9XCNqTCtr9wihVOAV;iN_u6D(TK#xcf zTn`v!7(bOtw4`b=XCQ>Lht=`it<|(#mtSF?W<QQcm$;T>m1BZ;G0*SUx8%WGt7&4B zV3D+tb8RIwgF3Luu#bN^7G4v==*yVRp-_@kjmeoSdoaG!zSzWv!K`UHUKC!MVuZSj zaIAJVdpAyF9vxyClh&g+jY8GGe$u;klYuouT@*iVD?w)>Y;7EsekVC?DRR1gZ_tQZ zyKv99ZOyY~z@}84hBK{_JHn(!l6fLVbTnhFZfMSfV$6Lul0qhGC2*Z?NP<TvhBL~q zUB|0c#ivccpg)Ro98G#WT*`P>$a6}(W;2>aI;l}Wz+t|fIeljlScFSfreaXWZ%f2# zMZ#t~zg?k^OOAUphI1*4cr3V}Qns8&igY1@Y#D@Y7H?b$T8UCtsbn_2S%i2!rk!)h z!>EaMCcvamyR?(W!KK8$qF<3+9BCaAUJ)8<9X6LmEss1DV->ZTL3e2=8EP9)hfaum zLy>wXERsEdsDEo(3zmE$ex`jsyImG&7=LOOfol~TcO*%INEmJ%7i}B|YRn1%0004W zQchC<K<3zH00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA00(qQO+^RW z0~-xG4!&HBF8}}lR!KxbR2b7^U?3j2xS;VvLXbtwTz~>0W=QhVA)yH_FkL{}1;huk zpaKCQKz@=7s`{i97i0@vl2TS8w1C7?R&G7y;)1Nm<<OZkC{A-h<6}A<CjbDaP8qDe SR_4e60000<MNUMnLSTaX-^L37 literal 0 HcmV?d00001 diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/js/extensions.js b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/js/extensions.js new file mode 100644 index 0000000..a21bf7b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/js/extensions.js @@ -0,0 +1,188 @@ +define( + ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'pgadmin.browser.collection'], +function($, _, S, pgAdmin, pgBrowser) { + + if (!pgBrowser.Nodes['coll-extension']) { + var extensions = pgAdmin.Browser.Nodes['coll-extension'] = + pgAdmin.Browser.Collection.extend({ + node: 'extension', + label: '{{ _('Extensions') }}', + type: 'coll-extension', + columns: ['name', 'owner', 'comment'] + }); + }; + + if (!pgBrowser.Nodes['extension']) { + pgAdmin.Browser.Nodes['extension'] = + pgAdmin.Browser.Node.extend({ + parent_type: 'database', + type: 'extension', + hasSQL: true, + canDrop: true, + canDropCascade: true, + label: '{{ _('Extension') }}', + + Init: function(){ + if(this.initialized) + return; + + this.initialized = true; + + pgBrowser.add_menus([{ + name: 'create_extension_on_coll', node: 'coll-extension', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Extension...') }}', + icon: 'wcTabIcon pg-icon-extension', data: {action: 'create'} + },{ + name: 'create_extension', node: 'extension', module: this, + applies: ['object', 'context'], callback: 'show_obj_properties', + category: 'create', priority: 4, label: '{{ _('Extension...') }}', + icon: 'wcTabIcon pg-icon-extension', data: {action: 'create'} + } + ]); + }, + model: pgAdmin.Browser.Node.Model.extend({ + schema: [ + { + id: 'name', label: '{{ _('Name')}}', + type: 'text', mode: ['properties', 'create', 'edit'], + visible: true, url:'avails', disabled: function(m) { + return !m.isNew(); + }, + transform: function(data) { + var res = []; + var label = this.model.get('name'); + if (!this.model.isNew()){ + res.push({label: label, value: label}); + } else { + if (data && _.isArray(data)) { + res.push({label: '', value: ''}); + _.each(data, function(d) { + if (d.installed_version == null) + /* d contains json data and sets into + * select's option control + */ + res.push({label: d.name, value: d}); + }) + } + } + return res; + }, + /* extends NodeAjaxOptionsControl to override the properties + * getValueFromDOM which takes stringified data from option of + * select control and parse it. And `onChange` takes the stringified + * data from select's option, thus convert it to json format and set the + * data into Model which is used to enable/disable the schema field. + */ + control: Backform.NodeAjaxOptionsControl.extend({ + getValueFromDOM: function() { + var data = this.formatter.toRaw(this.$el.find("select").val(), this.model); + return data.name; + }, + onChange: function() { + Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments); + var selectedValue = this.$el.find("select").val(); + if (selectedValue.trim() != ""){ + var d = this.formatter.toRaw(selectedValue, this.model); + var changes = { + 'version': (!_.isNull(d.version[0]) ? d.version[0]: ''), + 'relocatable': (!_.isNull(d.relocatable[0]) ? d.relocatable[0]: ''), + 'schema': (!_.isNull(d.schema[0]) ? d.schema[0]: '') + }; + this.model.set(changes); + }else{ + var changes = {'version': '', 'relocatable': true, 'schema': ''}; + this.model.set(changes); + } + }, + }) + },{ + id: 'eid', label: '{{ _('Oid')}}', cell: 'string', + type: 'text', disabled: true, mode: ['properties', 'edit', 'create'] + }, + { + id: 'owner', label: '{{ _('Owner')}}', cell: 'string', + type: 'text', mode: ['properties'] + }, + { + id: 'schema', label: '{{ _('Schema')}}', type: 'text', control: 'node-ajax-options', + mode: ['properties', 'create', 'edit'], group: 'Definition', deps: ['relocatable'], + url: 'schemas', disabled: function(m) { + /* enable or disable schema field if model's relocatable + * attribute is True or False + */ + return (m.has('relocatable') ? !m.get('relocatable') : false); + }, + transform: function(data) { + var res = []; + if (data && _.isArray(data)) { + _.each(data, function(d) { + res.push({label: d.schema, value: d.schema}); + }) + } + return res; + } + }, + { + id: 'relocatable', label: '{{ _('Relocatable?')}}', cell: 'switch', + type: 'switch', mode: ['properties'], 'options': { + 'onText': 'Yes', 'offText': 'No', 'onColor': 'success', + 'offColor': 'default', 'size': 'small' + } + }, + { + id: 'version', label: '{{ _('Version')}}', cell: 'string', + type: 'options', mode: ['properties', 'create', 'edit'], group: 'Definition', + disabled: false, control: 'node-ajax-options', url:'avails', + /* + * Transform the data into version for the selected extension. + */ + transform: function(data) { + res = []; + var extension = this.model.get('name'); + _.each(data, function(dt){ + if(dt.name == extension){ + if(dt.version && _.isArray(dt.version)){ + _.each(dt.version, function(v){ + res.push({ label: v, value: v }); + }); + } + } + }); + return res; + } + }, + { + id: 'comment', label: '{{ _('Comment')}}', cell: 'string', + type: 'multiline', disabled: true + }, + { + id: 'slony', label: '{{ _('Use Slony')}}', cell: 'string', + type: 'text', disabled: true, mode: ['create', 'edit'] + } + ], + validate: function() { + /* + * Triggers specific error messages for name and + * version if it is empty + */ + var changedAttrs = this.changed; + if (_.has(changedAttrs, this.get('name')) && _.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Name can not be empty!') }}'; + this.trigger('on-status',{msg:msg}); + return msg; + } + if (_.has(changedAttrs, this.get('version')) && _.isUndefined(this.get('version')) || String(this.get('version')).replace(/^\s+|\s+$/g, '') == '') { + var msg = '{{ _('Version can not be empty!') }}'; + this.trigger('on-status',{msg:msg}); + return msg; + } + this.trigger('on-status-clear'); + return true; + } + }) + }) + }; + + return pgBrowser.Nodes['coll-extension']; +}); diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/create.sql new file mode 100644 index 0000000..a68b7ce --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/create.sql @@ -0,0 +1,10 @@ +{#=========================Create new extension======================#} +{% if data.name %} +CREATE EXTENSION {{ data.name }} +{% if data.schema %} + SCHEMA {{ data.schema }} +{% endif %} +{% if data.version %} + VERSION '{{ data.version }}'; +{% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/delete.sql new file mode 100644 index 0000000..12741fb --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/delete.sql @@ -0,0 +1,8 @@ +{#============================Drop/Cascade Extension by name=========================#} +{% if eid %} +SELECT x.extname from pg_extension x + WHERE x.oid = {{ eid }}::int +{% endif %} +{% if name %} +DROP EXTENSION {{ name }} {% if cascade %} CASCADE {% endif %} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/extensions.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/extensions.sql new file mode 100644 index 0000000..bf3979d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/extensions.sql @@ -0,0 +1,12 @@ +{# ======================Fetch extensions names=====================#} +SELECT + a.name, a.installed_version, + array_agg(av.version) as version, + array_agg(av.schema) as schema, + array_agg(av.superuser) as superuser, + array_agg(av.relocatable) as relocatable +FROM + pg_available_extensions a + LEFT JOIN pg_available_extension_versions av ON (a.name = av.name) +GROUP BY a.name, a.installed_version +ORDER BY a.name diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/properties.sql new file mode 100644 index 0000000..b609883 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/properties.sql @@ -0,0 +1,17 @@ +{#===================Fetch properties of each extension by name or oid===================#} +SELECT + x.oid AS eid, pg_get_userbyid(extowner) AS owner, + x.extname AS name, n.nspname AS schema, + x.extrelocatable AS relocatable, x.extversion AS version, + e.comment +FROM + pg_extension x + LEFT JOIN pg_namespace n ON x.extnamespace=n.oid + JOIN pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{%- if eid %} + WHERE x.oid = {{eid}}::int +{% elif ename %} + WHERE x.extname = '{{ename}}'::text +{% else %} + ORDER BY x.extname +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/schemas.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/schemas.sql new file mode 100644 index 0000000..b8e3c97 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/schemas.sql @@ -0,0 +1,3 @@ +{#===================fetch all schemas==========================#} +SELECT nspname As schema FROM pg_namespace + ORDER BY nspname diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/update.sql new file mode 100644 index 0000000..cbdaa60 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/templates/extensions/sql/update.sql @@ -0,0 +1,10 @@ +{# =============Update extension schema============= #} +{% if data.schema != o_data.schema %} +ALTER EXTENSION {{ o_data.name }} + SET SCHEMA {{ data.schema|e }}; +{% endif %} +{# =============Update extension version============= #} +{% if data.version and data.version != o_data.version %} +ALTER EXTENSION {{ o_data.name }} + UPDATE TO '{{ data.version }}'; +{% endif %}
-- Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers