public inbox for [email protected]
help / color / mirror / Atom feedFrom: Surinder Kumar <[email protected]>
To: Akshay Joshi <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Subject: Re: [pgAdmin4][Patch]: View and Materialised View Nodes
Date: Tue, 17 May 2016 11:52:35 +0530
Message-ID: <CAM5-9D-gLAX+396BaQMC83F00gQREEMqrTuOMfKVx4dSY96VKw@mail.gmail.com> (raw)
In-Reply-To: <CANxoLDd-HE-wfgcfQhGFXG3cU4Sf1qosKxO9P84XDQLaNdq7zw@mail.gmail.com>
References: <CAM5-9D9t_ytZ5eA=Xb4pz8wKKt+-XhXX7L_kPEa=w=gkHa=tGg@mail.gmail.com>
<CANxoLDesxCagwR2DEBa5jRqRd755Z84iFnV2cHJM9tgfhHq7-w@mail.gmail.com>
<CAM5-9D_28KxDdr_FhWm7wjWwsCejudE2Q1FA4pX8DKFMReYaaA@mail.gmail.com>
<CANxoLDd-HE-wfgcfQhGFXG3cU4Sf1qosKxO9P84XDQLaNdq7zw@mail.gmail.com>
List-Unsubscribe: <mailto:[email protected]?body=unsub%20pgadmin-hackers>
Hi,
PFA updated patch.
Please find inline comments.
On Mon, May 16, 2016 at 1:56 PM, Akshay Joshi <[email protected]
> wrote:
> Hi
>
> Below are my review comments:
>
> - Definition box on the View dialogue not expanded as needed. It
> should expand on resize.
>
> removed the fixed height of definition box, but its height is dependent on
the number of sql code lines in it.
>
> -
> - "Do Instead" on Rule node under View/M-View node not working. Unable
> to generate proper SQL.
>
> Done
>
> - I am still able to drop columns under view/mview node.
>
> Done
>
> - "Save" button is enable by default when user opens Materialised View
> dialog.
>
> I checked it is not reproducible.
>
> On Fri, May 13, 2016 at 11:44 PM, Surinder Kumar <
> [email protected]> wrote:
>
>> Hi,
>>
>> Please find updated patch with fixed review comments:
>>
>> Most of the issues occurred because some code was missing in tables
>> subnodes patch.
>> Now I have shared the code related to table subnodes with harshal to
>> integrate in tables patch.
>>
>> This patch has dependency on tables patch.
>>
>> On Tue, May 10, 2016 at 7:46 PM, Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Surinder
>>>
>>> On Fri, Apr 29, 2016 at 8:07 PM, Surinder Kumar <surinder.kumar@
>>> enterprisedb.com> wrote:
>>>
>>>> Hi,
>>>>
>>>>
>>>> PFA patch for View and Materialised View Nodes.
>>>> This patch is dependent on *tables node* and its subnodes patch.
>>>> Please test this patch once latest tables node patch is submitted.
>>>>
>>>> I have merged one other patch:
>>>> *"Don't show Security group of node if it is under Catalogs"*
>>>>
>>>> http://www.postgresql.org/message-id/[email protected]....
>>>>
>>>> Below are the fix for the review comments given by Dave:
>>>>
>>>> *Views*:
>>>>
>>>> - Please add sqlCreateHelp and sqlAlterHelp properties to all nodes.
>>>> *I have added these in View and Materialised View. *
>>>>
>>>> - Some of the SQL templates have inconsistent indenting (i.e. not 4
>>>> chars), e.g.
>>>>
>>>> CREATE OR REPLACE VIEW pem.avail_agents
>>>> AS
>>>> ...
>>>> *Fixed*
>>>>
>>>> - When creating any object, the Owner, Schema and Tablespace should
>>>> have default values.
>>>> *Fixed*
>>>>
>>>> - Property labels should only have the first word capitalised, e.g.
>>>>
>>>> Security barrier
>>>> Check options
>>>> *Fixed*
>>>>
>>>> - The Definition box on the View dialogue should start at a single
>>>> line and expand as needed - see the Function dialogue
>>>> *Fixed*
>>>>
>>>
>>> Still reproducible.
>>>
>>>>
>>>>
>>>> - The Cancel button doesn't work on the View dialog. Please check them
>>>> all.
>>>> *In latest code pull, It **seems to be** fixed. not reproducible at my
>>>> end.*
>>>>
>>>> - The Save button doesn't close the View dialog, nor does it add the
>>>> tree node.
>>>> *In latest code pull, It **seems to be** fixed. not reproducible at my
>>>> end.*
>>>>
>>>> - Dependencies and Dependents don't show icons, and have very tall
>>>> rows. This issue likely comes from elsewhere as I'm seeing it on other
>>>> object types as well.
>>>> Yes, it is general, previously images were visible. It is regression of
>>>> some commit. I will check it.
>>>>
>>>> - Reverse engineered SQL for a column on a view is shown like:
>>>>
>>>> <html><head></head><body>-- Column: id -- ALTER TABLE pem.avail_agents
>>>> DROP COLUMN id; ALTER TABLE pem.avail_agents ADD COLUMN id
>>>> integer(4);</body></html>
>>>>
>>>> (yes, it's displaying the HTML tags)
>>>> *I pulled the latest code, but it is not reproducible.*
>>>>
>>>> - The ACL property should be called Privileges and be in the Security
>>>> group (see functions, sequences etc).
>>>> *Fixed*
>>>>
>>>> - The Schema should not be shown in "Properties" view.
>>>> *Fixed*
>>>>
>>>> - Fields in the General section should be in the order: Name, OID,
>>>> Owner, System xxx? (where appropriate), Comment
>>>> *I have checked that In view Fields (Name, Owner, Schema & Comment) are
>>>> in this order.*
>>>> *I didn't got your point. Can you please give and example, if possible.*
>>>>
>>>
>>> Not Fixed yet.
>>>
>> Now it is fixed.
>>
>>>
>>>>
>>>>
>>>> *Materialised Views:*
>>>> - Selecting an MV results in:
>>>>
>>>> 2016-04-14 09:34:58,863: ERROR pgadmin: Exception on
>>>> /browser/materialized_view/obj/1/1/24587/27424/107612 [GET]
>>>> Traceback (most recent call last):
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/app.py",
>>>> line 1817, in wsgi_app
>>>> response = self.full_dispatch_request()
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/app.py",
>>>> line 1477, in full_dispatch_request
>>>> rv = self.handle_user_exception(e)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/app.py",
>>>> line 1381, in handle_user_exception
>>>> reraise(exc_type, exc_value, tb)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/app.py",
>>>> line 1475, in full_dispatch_request
>>>> rv = self.dispatch_request()
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/app.py",
>>>> line 1461, in dispatch_request
>>>> return self.view_functions[rule.endpoint](**req.view_args)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/views.py",
>>>> line 84, in view
>>>> return self.dispatch_request(*args, **kwargs)
>>>> File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/utils.py", line
>>>> 233, in dispatch_request
>>>> return method(*args, **kwargs)
>>>> File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_
>>>> groups/servers/databases/schemas/views/__init__.py",
>>>> line 185, in wrap
>>>> return f(*args, **kwargs)
>>>> File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_
>>>> groups/servers/databases/schemas/views/__init__.py",
>>>> line 1226, in properties
>>>> self.conn, result, 'table')
>>>> File "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_
>>>> groups/servers/databases/schemas/utils.py",
>>>> line 357, in parse_vacuum_data
>>>> vacuum_fields = render_template("vacuum_
>>>> settings/vacuum_fields.json")
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/templating.py",
>>>> line 127, in render_template
>>>> return _render(ctx.app.jinja_env.get_or_select_template(template_
>>>> name_or_list),
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/jinja2/environment.py",
>>>> line 830, in get_or_select_template
>>>> return self.get_template(template_name_or_list, parent, globals)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/jinja2/environment.py",
>>>> line 791, in get_template
>>>> return self._load_template(name, self.make_globals(globals))
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/jinja2/environment.py",
>>>> line 765, in _load_template
>>>> template = self.loader.load(self, name, globals)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/jinja2/loaders.py",
>>>> line 113, in load
>>>> source, filename, uptodate = self.get_source(environment, name)
>>>> File "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-
>>>> packages/flask/templating.py",
>>>> line 64, in get_source
>>>> raise TemplateNotFound(template)
>>>> TemplateNotFound: vacuum_settings/vacuum_fields.json
>>>>
>>>> *It seems this patch*
>>>>
>>>> http://www.postgresql.org/message-id/[email protected]....
>>>> *wasn't applied properly. Now this patch is sent with tables patch.*
>>>>
>>>> - When creating an MV, an error is immediately shown telling me to
>>>> specify a name. We do not normally show such errors until the field
>>>> loses focus.
>>>> *Now its fixed.*
>>>>
>>>
>>> Not fixed. Issue is reproducible.
>>>
>>>>
>>>>
>>>> - No default owner is shown in create mode.
>>>> *Fixed.*
>>>>
>>>> - "Please enter function definition." is shown as an error on the
>>>> definition field.
>>>> *It triggers this error message as it is mandatory.*
>>>>
>>>> - The Definition field is missing a label. If relying on the tab title
>>>> to be the label, the control should fill the tab (as with Security
>>>> options etc).
>>>> *Fixed.*
>>>>
>>>> - No default tablespace is shown.
>>>> *Fixed*
>>>>
>>>> - If I enable custom auto-vacuum, there is no way to add a row to
>>>> specify options.
>>>> *You can enable custom auto vacuum field and add new values in the auto
>>>> vacuum settings.*
>>>>
>>>> - If I specify a comment on an MV, I get "ERROR: "mv_pg_tables" is not
>>>> a view"
>>>> *For some reason tablespace name was missing in generated sql.*
>>>>
>>>
>>> Not Fixed. Wrong sql generated "COMMENT ON VIEW ..." instead of
>>> "COMMENT ON MATERIALIZED VIEW ..."
>>>
>> Fixed
>>
>>>
>>>>
>>>> - Save/Cancel buttons not working as expected (like other objects)
>>>> *In latest code pull, It **seems to be** fixed. not reproducible at my
>>>> end.*
>>>>
>>>
>>>
>>> Apart from above below are my review comments
>>>
>>> *Views*:-
>>>
>>> - As per pgAdmin3 "indexes" node should not be listed under Views.
>>>
>>> Done
>>
>>>
>>> - SQL not generated when changing the value of "Event" and "Do
>>> Instead" for Rule node under View node.
>>>
>>> Done
>>
>>>
>>> - Changing the "Event" in Rule node not working.
>>>
>>> Done
>>
>>>
>>> - As per pgAdmin3 user can't be able to create columns inside View
>>> node.
>>>
>>> Done
>>
>>>
>>> - User can't be able to delete/drop columns and system generated
>>> Rule's, Trigger's etc..
>>>
>>>
>> Done
>>
>>>
>>> - Found one issue when changing value of "Security Barrier" from
>>> "Yes" to "No" it is not reflected on GUI when we open the dialog
>>> again while in backend value is updated, but on GUI it is showing
>>> "Yes".
>>>
>>> Done
>>
>>>
>>> - Create -> Trigger menu is missing when any view node is selected.
>>>
>>> Done
>>
>>>
>>> *Materialized View:-*
>>>
>>> - *As per pgAdmin3 user can't be able to create columns inside
>>> Materialized View node.*
>>>
>>> Done
>>
>>>
>>> -
>>> -
>>> *User can't be able to delete/drop columns and system generated Rule's,
>>> Trigger's etc. *
>>>
>>> I have checked in pgAdmin3 that delete/drop option specific to rules has
>> 2 cases:
>> 1. If rule is system rule, user can't delete/drop it.
>> 2. If not system rule, it can drop dropped.
>> Done
>>
>>>
>>> -
>>> - Create -> Trigger/Rule/Index menu is missing when any materialized
>>> view node is selected.
>>>
>>> Done
>>
>>>
>>> - In pgAdmin3 we have two more refresh options "Refresh data" and
>>> "Refresh data concurrently" which is missing.
>>>
>>> Done
>>
>>>
>>> - "Custom Auto Vaccum" for Table and Toast Table not working.
>>>
>>> Done
>>
>>>
>>>
>>>>
>>>> Thanks,
>>>> Surinder Kumar
>>>>
>>>>
>>>>
>>>> --
>>>> Sent via pgadmin-hackers mailing list ([email protected])
>>>> To make changes to your subscription:
>>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>>
>>>>
>>>
>>>
>>> --
>>> *Akshay Joshi*
>>> *Principal Software Engineer *
>>>
>>>
>>>
>>> *Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
>>>
>>
>>
>
>
> --
> *Akshay Joshi*
> *Principal Software Engineer *
>
>
>
> *Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
>
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers
Attachments:
[application/octet-stream] view_and_mat_view_nodes_v2.patch (214.4K, 3-view_and_mat_view_nodes_v2.patch)
download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py
new file mode 100644
index 0000000..adeb4b5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/__init__.py
@@ -0,0 +1,1418 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements View and Materialized View Node"""
+
+import simplejson as json
+from flask import render_template, request, jsonify
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_json_response, \
+ make_response as ajax_response, internal_server_error, \
+ bad_request
+from pgadmin.browser.utils import PGChildNodeView
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.browser.server_groups.servers.databases.schemas.utils import \
+ SchemaChildModule, parse_rule_definition, VacuumSettings
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+"""
+ This module is responsible for generating two nodes
+ 1) View
+ 2) Materialized View
+
+ We have created single file because view & material view has same
+ functionality. It allows us to share the same submodules for both
+ view, and material view modules.
+
+ This modules uses separate template paths for each respective node
+ - templates/view for View node
+ - templates/materialized_view for MaterializedView node
+
+ [Each path contains node specific js files as well as sql template files.]
+"""
+
+
+class ViewModule(SchemaChildModule):
+ """
+ class ViewModule(SchemaChildModule):
+
+ A module class for View node derived from SchemaChildModule.
+
+ Methods:
+ -------
+ * __init__(*args, **kwargs)
+ - Method is used to initialize the View and it's base module.
+
+ * get_nodes(gid, sid, did, scid)
+ - 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 View, when any of the server node is
+ initialized.
+ """
+ NODE_TYPE = 'view'
+ COLLECTION_LABEL = gettext("Views")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the ViewModule and it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(ViewModule, self).__init__(*args, **kwargs)
+ self.min_ver = None
+ 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 views, when any database node is
+ initialized, The reason is views are also listed under catalogs
+ which are loaded under database node.
+ """
+ return databases.DatabaseModule.NODE_TYPE
+
+ @property
+ def csssnippets(self):
+ """
+ Returns a snippet of css to include in the page
+ """
+ snippets = [
+ render_template(
+ "browser/css/collection.css",
+ node_type=self.node_type,
+ _=gettext
+ ),
+ render_template(
+ "view/css/view.css",
+ node_type=self.node_type,
+ _=gettext
+ ),
+ render_template(
+ "mview/css/mview.css",
+ node_type='mview',
+ _=gettext
+ )
+ ]
+
+ for submodule in self.submodules:
+ snippets.extend(submodule.csssnippets)
+
+ return snippets
+
+
+class MaterializedViewModule(ViewModule):
+ """
+ class MaterializedViewModule(ViewModule)
+ A module class for the materialized view and view node derived
+ from ViewModule.
+ """
+
+ NODE_TYPE = 'mview'
+ COLLECTION_LABEL = gettext("Materialized Views")
+
+ def __init__(self, *args, **kwargs):
+ """
+ Method is used to initialize the MaterializedViewModule and
+ it's base module.
+
+ Args:
+ *args:
+ **kwargs:
+ """
+ super(MaterializedViewModule, self).__init__(*args, **kwargs)
+ self.min_ver = 90300
+ self.max_ver = None
+
+view_blueprint = ViewModule(__name__)
+mview_blueprint = MaterializedViewModule(
+ __name__)
+
+
+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 instance of the method.
+
+ Assumptions:
+ This function will always be used as decorator of a class method.
+ """
+ @wraps(f)
+ def wrap(*args, **kwargs):
+
+ # Here args[0] will hold self & kwargs will hold gid,sid,did
+ self = args[0]
+ self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
+ kwargs['sid']
+ )
+ self.conn = self.manager.connection(did=kwargs['did'])
+ # If DB not connected then return error to browser
+ if not self.conn.connected():
+ return precondition_required(
+ gettext("Connection to the server has been lost!")
+ )
+ self.datlastsysoid = self.manager.db_info[
+ kwargs['did']]['datlastsysoid']
+
+ # Set template path for sql scripts
+ self.template_path = self.template_initial + '/' + (
+ self.ppas_template_path(self.manager.version)
+ if self.manager.server_type == 'ppas' else
+ self.pg_template_path(self.manager.version)
+ )
+
+ return f(*args, **kwargs)
+
+ return wrap
+
+
+class ViewNode(PGChildNodeView, VacuumSettings):
+ """
+ This class is responsible for generating routes for view node.
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the ViewNode and it's base view.
+
+ * module_js()
+ - Request handler for module.js routes for the view node module
+ javascript, which returns javscript for this module.
+
+ * list()
+ - This function is used to list all the view nodes within the
+ collection.
+
+ * nodes()
+ - This function will used to create all the child node within the
+ collection, Here it will create all the view node.
+
+ * properties(gid, sid, did, scid)
+ - This function will show the properties of the selected view node.
+
+ * create(gid, sid, did, scid)
+ - This function will create the new view object.
+
+ * update(gid, sid, did, scid)
+ - This function will update the data for the selected view node.
+
+ * delete(self, gid, sid, scid):
+ - This function will drop the view object
+
+ * msql(gid, sid, did, scid)
+ - This function is used to return modified SQL for the selected view
+ node.
+
+ * get_sql(data, scid)
+ - This function will generate sql from model data
+
+ * sql(gid, sid, did, scid):
+ - This function will generate sql to show it in sql pane for the view
+ node.
+
+ * dependency(gid, sid, did, scid):
+ - This function will generate dependency list show it in dependency
+ pane for the selected view node.
+
+ * dependent(gid, sid, did, scid):
+ - This function will generate dependent list to show it in dependent
+ pane for the selected view node.
+ """
+ node_type = view_blueprint.node_type
+
+ parent_ids = [
+ {'type': 'int', 'id': 'gid'},
+ {'type': 'int', 'id': 'sid'},
+ {'type': 'int', 'id': 'did'},
+ {'type': 'int', 'id': 'scid'}
+ ]
+ ids = [
+ {'type': 'int', 'id': 'vid'}
+ ]
+
+ operations = dict({
+ 'obj': [
+ {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+ {'get': 'list', 'post': 'create'}
+ ],
+ 'children': [{
+ 'get': 'children'
+ }],
+ 'delete': [{'delete': 'delete'}],
+ 'refresh_data': [{'put': 'refresh_data'}, {'put': 'refresh_data'}],
+ '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'}],
+ 'configs': [{'get': 'configs'}],
+ 'get_tblspc': [{'get': 'get_tblspc'}, {'get': 'get_tblspc'}],
+ 'get_table_vacuum': [
+ {'get': 'get_table_vacuum'},
+ {'get': 'get_table_vacuum'}],
+ 'get_toast_table_vacuum': [
+ {'get': 'get_toast_table_vacuum'},
+ {'get': 'get_toast_table_vacuum'}]
+ })
+
+ def __init__(self, *args, **kwargs):
+ """
+ Initialize the variables used by methods of ViewNode.
+ """
+
+ super(ViewNode, self).__init__(*args, **kwargs)
+
+ self.manager = None
+ self.conn = None
+ self.template_path = None
+ self.template_initial = 'view'
+
+ @staticmethod
+ def ppas_template_path(ver):
+ """
+ Returns the template path for PPAS servers.
+ """
+ return 'ppas/9.3_plus'
+
+ @staticmethod
+ def pg_template_path(ver):
+ """
+ Returns the template path for PostgreSQL servers.
+ """
+ if ver >= 90400:
+ return 'pg/9.4_plus'
+ elif ver >= 90300:
+ return 'pg/9.3_plus'
+ elif ver >= 90200:
+ return 'pg/9.2_plus'
+ elif ver >= 90100:
+ return 'pg/9.1_plus'
+ return 'pg/9.1_plus'
+
+ @check_precondition
+ def list(self, gid, sid, did, scid):
+ """
+ Fetches all views properties and render into properties tab
+ """
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']), scid=scid)
+ status, res = self.conn.execute_dict(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=res)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def nodes(self, gid, sid, did, scid):
+ """
+ Lists all views under the Views Collection node
+ """
+ res = []
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.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-view"
+ ))
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ def parse_views_privileges(self, db_privileges):
+ """
+ This function forms a separate list of grantable
+ and non grantable privileges.
+ """
+ acl = {'grantor': db_privileges['grantor'],
+ 'grantee': db_privileges['grantee'],
+ 'privileges': []
+ }
+
+ privileges = []
+ for idx, priv in enumerate(db_privileges['privileges']):
+ if db_privileges['grantable'][idx]:
+ privileges.append({"privilege_type": priv,
+ "privilege": True,
+ "with_grant": True
+ })
+ else:
+ privileges.append({"privilege_type": priv,
+ "privilege": True,
+ "with_grant": False
+ })
+
+ acl['privileges'] = privileges
+
+ return acl
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, vid):
+ """
+ Fetches the properties of an individual view
+ and render in the properties tab
+ """
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']
+ ), vid=vid, datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/acl.sql']), vid=vid)
+ status, dataclres = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in dataclres['rows']:
+ priv = self.parse_views_privileges(row)
+ if row['deftype'] in res['rows'][0]:
+ res['rows'][0][row['deftype']].append(priv)
+ else:
+ res['rows'][0][row['deftype']] = [priv]
+
+ result = res['rows'][0]
+
+ # sending result to formtter
+ frmtd_reslt = self.formatter(result)
+
+ # merging formated result with main result again
+ result.update(frmtd_reslt)
+
+ return ajax_response(
+ response=result,
+ status=200
+ )
+
+ @staticmethod
+ def formatter(result):
+ """ This function formats output for security label & variables"""
+ frmtd_result = dict()
+ sec_lbls = []
+ if 'seclabels' in result and result['seclabels'] is not None:
+ for sec in result['seclabels']:
+ import re
+ sec = re.search(r'([^=]+)=(.*$)', sec)
+ sec_lbls.append({
+ 'provider': sec.group(1),
+ 'security_label': sec.group(2)
+ })
+
+ frmtd_result.update({"seclabels": sec_lbls})
+ return frmtd_result
+
+ @check_precondition
+ def create(self, gid, sid, did, scid):
+ """
+ This function will create a new view object
+ """
+ required_args = [
+ 'name',
+ 'definition'
+ ]
+
+ 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
+ )
+ )
+
+ SQL = self.getSQL(gid, sid, data)
+ try:
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/view_id.sql']), data=data)
+ status, view_id = self.conn.execute_scalar(SQL)
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ view_id,
+ scid,
+ data['name'],
+ icon="icon-view"
+ )
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def update(self, gid, sid, did, scid, vid):
+ """
+ This function will update a view object
+ """
+ data = request.form if request.form else \
+ json.loads(request.data.decode())
+ SQL = self.getSQL(gid, sid, data, vid)
+ try:
+ status, res = self.conn.execute_scalar(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/view_id.sql']), data=data)
+ status, view_id = self.conn.execute_scalar(SQL)
+ return jsonify(
+ node=self.blueprint.generate_browser_node(
+ view_id,
+ scid,
+ data['name'],
+ icon="icon-view"
+ )
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def delete(self, gid, sid, did, scid, vid):
+ """
+ This function will drop a view object
+ """
+
+ # Below will decide if it's simple drop or drop with cascade call
+ cascade = True if self.cmd == 'delete' else False
+
+ try:
+
+ # Get name for view from did
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/delete.sql']), vid=vid)
+ status, res_data = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res_data)
+
+ # drop view
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/delete.sql']),
+ nspname=res_data['rows'][0]['nspname'],
+ name=res_data['rows'][0]['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("View dropped"),
+ data={
+ 'id': vid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ def _get_schema(self, scid):
+ """
+ Returns Schema Name from its OID.
+
+ Args:
+ scid: Schema Id
+ """
+ SQL = render_template("/".join([self.template_path,
+ 'sql/get_schema.sql']), scid=scid)
+
+ status, schema_name = self.conn.execute_scalar(SQL)
+
+ if not status:
+ return internal_server_error(errormsg=schema_name)
+
+ return schema_name
+
+ @check_precondition
+ def msql(self, gid, sid, did, scid, vid=None):
+ """
+ This function returns modified SQL
+ """
+ data = {}
+ for k, v in request.args.items():
+ try:
+ data[k] = json.loads(v)
+ except ValueError:
+ data[k] = v
+
+ SQL = self.getSQL(gid, sid, data, vid)
+ if(SQL):
+ return make_json_response(
+ data=SQL,
+ status=200
+ )
+ else:
+ return make_json_response(
+ data=gettext("-- Nothing changed"),
+ status=200
+ )
+
+ @staticmethod
+ def parse_privileges(str_privileges, object_type='VIEW'):
+ """Parse Privileges."""
+ db_privileges = {
+ 'a': 'INSERT',
+ 'r': 'SELECT',
+ 'w': 'UPDATE',
+ 'd': 'DELETE',
+ 'D': 'TRUNCATE',
+ 'x': 'REFERENCES',
+ 't': 'TRIGGER'
+ }
+ privileges = []
+ for priv in str_privileges:
+ priv_with_grant = []
+ priv_without_grant = []
+ for privilege in priv['privileges']:
+ if privilege['with_grant']:
+ priv_with_grant.append(
+ db_privileges[privilege['privilege_type']])
+ elif privilege['privilege']:
+ priv_without_grant.append(
+ db_privileges[privilege['privilege_type']])
+
+ # If we have all acl then just return all
+ if len(priv_with_grant) == len(db_privileges):
+ priv_with_grant = ['ALL']
+ if len(priv_without_grant) == len(db_privileges):
+ priv_without_grant = ['ALL']
+
+ # Server Level validation
+ if 'grantee' in priv:
+ privileges.append(
+ {
+ 'grantee': priv['grantee'] if 'grantee' in priv else '',
+ 'with_grant': priv_with_grant,
+ 'without_grant': priv_without_grant
+ }
+ )
+ else:
+ return ''
+ return privileges
+
+ def getSQL(self, gid, sid, data, vid=None):
+ """
+ This function will generate sql from model data
+ """
+ try:
+ if vid is not None:
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']),
+ vid=vid,
+ datlastsysoid=self.datlastsysoid
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ old_data = res['rows'][0]
+
+ if 'name' not in data:
+ data['name'] = res['rows'][0]['name']
+ if 'schema' not in data:
+ data['schema'] = res['rows'][0]['schema']
+
+ key = 'datacl'
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = self.parse_privileges(
+ data[key]['added'])
+ if 'changed' in data[key]:
+ data[key]['changed'] = self.parse_privileges(
+ data[key]['changed'])
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = self.parse_privileges(
+ data[key]['deleted'])
+ try:
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/update.sql']), data=data,
+ o_data=old_data, conn=self.conn)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+ else:
+ required_args = [
+ 'name',
+ 'schema',
+ 'definition'
+ ]
+ for arg in required_args:
+ if arg not in data:
+ return " -- definition incomplete"
+
+ # Get Schema Name from its OID.
+ if 'schema' in data:
+ data['schema'] = self._get_schema(data['schema'])
+
+ if 'datacl' in data and data['datacl'] is not None:
+ data['datacl'] = self.parse_privileges(data['datacl'])
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/create.sql']), data=data)
+ if data['definition']:
+ SQL += "\n"
+ SQL += render_template("/".join(
+ [self.template_path, 'sql/grant.sql']), data=data)
+ return SQL
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ def get_index_column_details(self, idx, data):
+ """
+ This functional will fetch list of column details for index
+
+ Args:
+ idx: Index OID
+ data: Properties data
+
+ Returns:
+ Updated properties data with column details
+ """
+
+ self.index_temp_path = 'index'
+ SQL = render_template("/".join([self.index_temp_path,
+ 'sql/9.1_plus/column_details.sql']), idx=idx)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+
+ # 'attdef' comes with quotes from query so we need to strip them
+ # 'options' we need true/false to render switch ASC(false)/DESC(true)
+ columns = []
+ cols = []
+ for row in rset['rows']:
+
+ # We need all data as collection for ColumnsModel
+ cols_data = {
+ 'colname': row['attdef'].strip('"'),
+ 'collspcname': row['collnspname'],
+ 'op_class': row['opcname'],
+ }
+ if row['options'][0] == 'DESC':
+ cols_data['sort_order'] = True
+ columns.append(cols_data)
+
+ # We need same data as string to display in properties window
+ # If multiple column then separate it by colon
+ cols_str = row['attdef']
+ if row['collnspname']:
+ cols_str += ' COLLATE ' + row['collnspname']
+ if row['opcname']:
+ cols_str += ' ' + row['opcname']
+ if row['options'][0] == 'DESC':
+ cols_str += ' DESC'
+ cols.append(cols_str)
+
+ # Push as collection
+ data['columns'] = columns
+ # Push as string
+ data['cols'] = ', '.join(cols)
+
+ return data
+
+ def get_trigger_column_details(self, tid, clist):
+ """
+ This function will fetch list of column for trigger
+
+ Args:
+ tid: Table OID
+ clist: List of columns
+
+ Returns:
+ Updated properties data with column
+ """
+
+ self.trigger_temp_path = 'schema/trigger'
+ SQL = render_template("/".join([self.trigger_temp_path,
+ 'get_columns.sql']),
+ tid=tid, clist=clist)
+ status, rset = self.conn.execute_2darray(SQL)
+ if not status:
+ return internal_server_error(errormsg=rset)
+ # 'tgattr' contains list of columns from table used in trigger
+ columns = []
+
+ for row in rset['rows']:
+ columns.append({'column': row['name']})
+
+ return columns
+
+ def get_rule_sql(self, vid):
+ """
+ Get all non system rules of view node,
+ generate their sql and render
+ into sql tab
+ """
+
+ self.rule_temp_path = 'rules'
+ SQL_data = ''
+ SQL = render_template("/".join(
+ [self.rule_temp_path, 'sql/properties.sql']), tid=vid)
+
+ status, data = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=data)
+
+ for rule in data['rows']:
+
+ # Generate SQL only for non system rule
+ if rule['name'] != '_RETURN':
+ res = []
+ SQL = render_template("/".join(
+ [self.rule_temp_path, 'sql/properties.sql']),
+ rid=rule['oid']
+ )
+ status, res = self.conn.execute_dict(SQL)
+ res = parse_rule_definition(res)
+ SQL = render_template("/".join(
+ [self.rule_temp_path, 'sql/create.sql']),
+ data=res, display_comments=True)
+ SQL_data += '\n'
+ SQL_data += SQL
+ return SQL_data
+
+ def get_trigger_sql(self, vid):
+ """
+ Get all trigger nodes associated with view node,
+ generate their sql and render
+ into sql tab
+ """
+
+ # Define template path
+ self.trigger_temp_path = 'trigger'
+
+ SQL_data = ''
+ SQL = render_template("/".join(
+ [self.trigger_temp_path, 'sql/9.1_plus/properties.sql']),
+ tid=vid)
+
+ status, data = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=data)
+
+ for trigger in data['rows']:
+ SQL = render_template("/".join(
+ [self.trigger_temp_path, 'sql/9.1_plus/properties.sql']),
+ tid=trigger['oid'],
+ tgrelid=vid
+ )
+
+ status, res = self.conn.execute_dict(SQL)
+
+ res_rows = dict(res['rows'][0])
+ if res_rows['tgnargs'] > 1:
+ # We know that trigger has more than 1
+ # arguments, let's join them
+ res_rows['tgargs'] = ', '.join(
+ res_rows['tgargs'])
+
+ if len(res_rows['tgattr']) > 1:
+ columns = ', '.join(res_rows['tgattr'].split(' '))
+ res_rows['columns'] = self.get_trigger_column_details(
+ trigger['oid'], columns)
+ res_rows = trigger_definition(res_rows)
+
+ SQL = render_template("/".join(
+ [self.trigger_temp_path, 'sql/9.1_plus/create.sql']),
+ data=res_rows, display_comments=True)
+ SQL_data += '\n'
+ SQL_data += SQL
+
+ return SQL_data
+
+ def get_index_sql(self, vid):
+ """
+ Get all index associated with view node,
+ generate their sql and render
+ into sql tab
+ """
+
+ self.index_temp_path = 'index'
+ SQL_data = ''
+ SQL = render_template("/".join(
+ [self.index_temp_path, 'sql/9.1_plus/properties.sql']), tid=vid)
+ status, data = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=data)
+
+ for index in data['rows']:
+ res = []
+ SQL = render_template("/".join(
+ [self.index_temp_path, 'sql/9.1_plus/properties.sql']),
+ idx=index['oid'],
+ tid=vid
+ )
+ status, res = self.conn.execute_dict(SQL)
+
+ data = dict(res['rows'][0])
+ # Adding parent into data dict, will be using it while creating sql
+ data['schema'] = data['nspname']
+ data['table'] = data['tabname']
+
+ # Add column details for current index
+ data = self.get_index_column_details(index['oid'], data)
+
+ SQL = render_template("/".join(
+ [self.index_temp_path, 'sql/9.1_plus/create.sql']),
+ data=data, display_comments=True)
+ SQL_data += '\n'
+ SQL_data += SQL
+ return SQL_data
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, vid):
+ """
+ This function will generate sql to render into the sql panel
+ """
+
+ SQL_data = ''
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']),
+ vid=vid,
+ datlastsysoid=self.datlastsysoid
+ )
+
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ result = res['rows'][0]
+
+ # Fetch all privileges for view
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/acl.sql']), vid=vid)
+ status, dataclres = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in dataclres['rows']:
+ priv = self.parse_views_privileges(row)
+ if row['deftype'] in res['rows'][0]:
+ res['rows'][0]['datacl'].append(priv)
+ else:
+ res['rows'][0]['datacl'] = [priv]
+
+ result.update(res['rows'][0])
+ if 'datacl' in result:
+ result['datacl'] = self.parse_privileges(result['datacl'])
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/create.sql']),
+ data=result,
+ conn=self.conn,
+ display_comments=True
+ )
+ SQL += "\n"
+ SQL += render_template("/".join(
+ [self.template_path, 'sql/grant.sql']), data=result)
+
+ SQL_data += SQL
+ SQL_data += self.get_rule_sql(vid)
+ SQL_data += self.get_trigger_sql(vid)
+ SQL_data += self.get_index_sql(vid)
+
+ return ajax_response(response=SQL_data)
+
+ @check_precondition
+ def get_tblspc(self, gid, sid, did, scid):
+ """
+ This function to return list of tablespaces
+ """
+ res = []
+ try:
+ SQL = render_template(
+ "/".join([self.template_path, 'sql/get_tblspc.sql'])
+ )
+ status, rset = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in rset['rows']:
+ res.append(
+ {'label': row['spcname'], 'value': row['spcname']}
+ )
+
+ return make_json_response(
+ data=res,
+ status=200
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def dependents(self, gid, sid, did, scid, vid):
+ """
+ This function gets the dependents and returns an ajax response
+ for the view node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ vid: View ID
+ """
+ dependents_result = self.get_dependents(self.conn, vid)
+ return ajax_response(
+ response=dependents_result,
+ status=200
+ )
+
+ @check_precondition
+ def dependencies(self, gid, sid, did, scid, vid):
+ """
+ This function gets the dependencies and returns an ajax response
+ for the view node.
+
+ Args:
+ gid: Server Group ID
+ sid: Server ID
+ did: Database ID
+ vid: View ID
+ """
+ dependencies_result = self.get_dependencies(self.conn, vid)
+ return ajax_response(
+ response=dependencies_result,
+ status=200
+ )
+
+
+class MaterializedViewNode(ViewNode, VacuumSettings):
+ """
+ This class is responsible for generating routes for
+ materialized view node.
+
+ Methods:
+ -------
+ * __init__(**kwargs)
+ - Method is used to initialize the MaterializedView
+ and it's base view.
+
+ * module_js()
+ - This property defines (if javascript) exists for this node.
+ Override this property for your own logic
+
+ * create(gid, sid, did, scid)
+ - Raise an error - we can not create a material view.
+
+ * update(gid, sid, did, scid)
+ - This function will update the data for the selected material node
+
+ * delete(self, gid, sid, scid):
+ - Raise an error - we can not delete a material view.
+
+ * get_sql(data, scid)
+ - This function will generate sql from model data
+
+ """
+
+ node_type = mview_blueprint.node_type
+
+ def __init__(self, *args, **kwargs):
+ """
+ Initialize the variables used by methods of ViewNode.
+ """
+
+ super(MaterializedViewNode, self).__init__(*args, **kwargs)
+
+ self.template_initial = 'mview'
+
+ @staticmethod
+ def pg_template_path(ver):
+ """
+ Returns the template path for PostgreSQL servers.
+ """
+ if ver >= 90300:
+ return 'pg/9.3_plus'
+ return 'pg/9.3_plus'
+
+ def get_sql(self, gid, sid, data, scid, vid=None):
+ """
+ This function will generate sql from model data
+ """
+ if scid is None:
+ return bad_request('Cannot create a View!')
+
+ return super(MaterializedViewNode, self).get_sql(
+ gid, sid, data, scid, vid)
+
+ def getSQL(self, gid, sid, data, vid=None):
+ """
+ This function will genrate sql from model data
+ """
+ try:
+ if vid is not None:
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']),
+ vid=vid,
+ datlastsysoid=self.datlastsysoid
+ )
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+ old_data = res['rows'][0]
+
+ if 'name' not in data:
+ data['name'] = res['rows'][0]['name']
+ if 'schema' not in data:
+ data['schema'] = res['rows'][0]['schema']
+
+ # merge vacuum lists into one
+ data['vacuum_data'] = {}
+ data['vacuum_data']['changed'] = []
+ data['vacuum_data']['reset'] = []
+
+ # table vacuum: separate list of changed and reset data for
+ if ('vacuum_table' in data):
+ if ('changed' in data['vacuum_table']):
+ for item in data['vacuum_table']['changed']:
+ if 'value' in item.keys():
+ if item['value'] is None:
+ if old_data[item['name']] != item['value']:
+ data['vacuum_data']['reset'].append(item)
+ else:
+ if (old_data[item['name']] is None or
+ (float(old_data[item['name']]) != float(item['value']))):
+ data['vacuum_data']['changed'].append(item)
+
+ if ('autovacuum_enabled' in data and
+ str(data['autovacuum_enabled']).lower() != str(old_data['autovacuum_enabled']).lower()):
+ data['vacuum_data']['changed'].append(
+ {'name': 'autovacuum_enabled',
+ 'value': data['autovacuum_enabled']})
+
+ # toast autovacuum: separate list of changed and reset data
+ if ('vacuum_toast' in data):
+ if ('changed' in data['vacuum_toast']):
+ for item in data['vacuum_toast']['changed']:
+ if 'value' in item.keys():
+ toast_key = 'toast_'+item['name']
+ item['name'] = 'toast.'+item['name']
+ if item['value'] is None:
+ if old_data[toast_key] != item['value']:
+ data['vacuum_data']['reset'].append(item)
+ else:
+ if (old_data[toast_key] is None or
+ (float(old_data[toast_key]) != float(item['value']))):
+ data['vacuum_data']['changed'].append(item)
+ if ('toast_autovacuum_enabled' in data and
+ str(data['toast_autovacuum_enabled']).lower() != str(old_data['toast_autovacuum_enabled']).lower()):
+ data['vacuum_data']['changed'].append(
+ {'name': 'toast.autovacuum_enabled',
+ 'value': data['toast_autovacuum_enabled']})
+
+ key = 'datacl'
+ if key in data and data[key] is not None:
+ if 'added' in data[key]:
+ data[key]['added'] = self.parse_privileges(
+ data[key]['added'])
+ if 'changed' in data[key]:
+ data[key]['changed'] = self.parse_privileges(
+ data[key]['changed'])
+ if 'deleted' in data[key]:
+ data[key]['deleted'] = self.parse_privileges(
+ data[key]['deleted'])
+
+ if (len(data['vacuum_data']['changed']) == 0 and
+ len(data['vacuum_data']['reset']) == 0):
+ data['settings'] = dict()
+ if ('autovacuum_custom' in data and data['autovacuum_custom'] is True and
+ (data['autovacuum_custom'] != old_data['autovacuum_custom']) and
+ ('autovacuum_enabled' in data and data['autovacuum_enabled'] is True)):
+ data['settings']['autovacuum_enabled'] = data['autovacuum_custom']
+ elif ('autovacuum_enabled' in data and old_data['autovacuum_enabled'] is not None and
+ str(data['autovacuum_enabled']).lower() != old_data['autovacuum_enabled'].lower()):
+ data['settings']['autovacuum_enabled'] = data['autovacuum_enabled']
+
+ try:
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/update.sql']), data=data,
+ o_data=old_data, conn=self.conn)
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+ else:
+ required_args = [
+ 'name',
+ 'schema',
+ 'definition'
+ ]
+ for arg in required_args:
+ if arg not in data:
+ return " -- definition incomplete"
+
+ # Get Schema Name from its OID.
+ if 'schema' in data:
+ data['schema'] = self._get_schema(data['schema'])
+
+ # merge vacuum lists into one
+ vacuum_table = [item for item in data['vacuum_table']
+ if 'value' in item.keys() and item['value'] is not None]
+ vacuum_toast = [
+ {'name': 'toast.'+item['name'], 'value':item['value']}
+ for item in data['vacuum_toast'] if 'value' in item.keys() and item['value'] is not None]
+
+ # add vacuum_toast dict to vacuum_data only if
+ # toast's autovacuum is enabled
+ if ('toast_autovacuum_enabled' in data and
+ data['toast_autovacuum_enabled'] is True):
+ data['vacuum_data'] = vacuum_table + vacuum_toast
+ else:
+ data['vacuum_data'] = vacuum_table
+
+ if 'datacl' in data and data['datacl'] is not None:
+ data['datacl'] = self.parse_privileges(data['datacl'])
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/create.sql']), data=data)
+ if data['definition']:
+ SQL += "\n"
+ SQL += render_template("/".join(
+ [self.template_path, 'sql/grant.sql']), data=data)
+ return SQL
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ @check_precondition
+ def sql(self, gid, sid, did, scid, vid):
+ """
+ This function will generate sql to render into the sql panel
+ """
+
+ SQL_data = ''
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']),
+ vid=vid,
+ datlastsysoid=self.datlastsysoid
+ )
+
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ result = res['rows'][0]
+
+ # sending result to formtter
+ frmtd_reslt = self.formatter(result)
+
+ # merging formated result with main result again
+ result.update(frmtd_reslt)
+ result['vacuum_table'] = self.parse_vacuum_data(
+ self.conn, result, 'table')
+ result['vacuum_toast'] = self.parse_vacuum_data(
+ self.conn, result, 'toast')
+
+ # merge vacuum lists into one
+ vacuum_table = [item for item in result['vacuum_table']
+ if 'value' in item.keys() and item['value'] is not None]
+ vacuum_toast = [
+ {'name': 'toast.'+item['name'], 'value':item['value']}
+ for item in result['vacuum_toast'] if 'value' in item.keys() and item['value'] is not None]
+
+ # add vacuum_toast dict to vacuum_data only if
+ # toast's autovacuum is enabled
+ if ('toast_autovacuum_enabled' in result and
+ result['toast_autovacuum_enabled'] is True):
+ result['vacuum_data'] = vacuum_table + vacuum_toast
+ else:
+ result['vacuum_data'] = vacuum_table
+
+ # Fetch all privileges for view
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/acl.sql']), vid=vid)
+ status, dataclres = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in dataclres['rows']:
+ priv = self.parse_views_privileges(row)
+ if row['deftype'] in res['rows'][0]:
+ res['rows'][0]['datacl'].append(priv)
+ else:
+ res['rows'][0]['datacl'] = [priv]
+
+ result.update(res['rows'][0])
+ if 'datacl' in result:
+ result['datacl'] = self.parse_privileges(result['datacl'])
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/create.sql']),
+ data=result,
+ conn=self.conn,
+ display_comments=True
+ )
+ SQL += "\n"
+ SQL += render_template("/".join(
+ [self.template_path, 'sql/grant.sql']), data=result)
+
+ SQL_data += SQL
+ SQL_data += self.get_rule_sql(vid)
+ SQL_data += self.get_trigger_sql(vid)
+ SQL_data += self.get_index_sql(vid)
+
+ return ajax_response(response=SQL_data)
+
+ @check_precondition
+ def get_table_vacuum(self, gid, sid, did, scid):
+ """
+ Fetch the default values for autovacuum
+ fields, return an array of
+ - label
+ - name
+ - setting
+ values
+ """
+
+ res = self.get_vacuum_table_settings(self.conn)
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def get_toast_table_vacuum(self, gid, sid, did, scid):
+ """
+ Fetch the default values for autovacuum
+ fields, return an array of
+ - label
+ - name
+ - setting
+ values
+ """
+ res = self.get_vacuum_toast_settings(self.conn)
+
+ return ajax_response(
+ response=res['rows'],
+ status=200
+ )
+
+ @check_precondition
+ def properties(self, gid, sid, did, scid, vid):
+ """
+ Fetches the properties of an individual view
+ and render in the properties tab
+ """
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/properties.sql']
+ ), vid=vid, datlastsysoid=self.datlastsysoid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/acl.sql']), vid=vid)
+ status, dataclres = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ for row in dataclres['rows']:
+ priv = self.parse_views_privileges(row)
+ if row['deftype'] in res['rows'][0]:
+ res['rows'][0][row['deftype']].append(priv)
+ else:
+ res['rows'][0][row['deftype']] = [priv]
+
+ result = res['rows'][0]
+
+ # sending result to formtter
+ frmtd_reslt = self.formatter(result)
+
+ # merging formated result with main result again
+ result.update(frmtd_reslt)
+
+ result['vacuum_table'] = self.parse_vacuum_data(
+ self.conn, result, 'table')
+ result['vacuum_toast'] = self.parse_vacuum_data(
+ self.conn, result, 'toast')
+
+ return ajax_response(
+ response=result,
+ status=200
+ )
+
+ @check_precondition
+ def refresh_data(self, gid, sid, did, scid, vid):
+ """
+ This function will refresh view object
+ """
+
+ # Below will decide if it's refresh data or refresh concurrently
+ data = request.form if request.form else json.loads(request.data.decode())
+ is_concurrent = json.loads(data['concurrent'])
+
+ try:
+
+ # Fetch view name by view id
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/get_view_name.sql']), vid=vid)
+ status, res = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res)
+
+ # Refresh view
+ SQL = render_template("/".join(
+ [self.template_path, 'sql/refresh.sql']),
+ name=res['rows'][0]['name'],
+ nspname=res['rows'][0]['schema'], is_concurrent=is_concurrent)
+ status, res_data = self.conn.execute_dict(SQL)
+ if not status:
+ return internal_server_error(errormsg=res_data)
+
+ return make_json_response(
+ success=1,
+ info=gettext("View refreshed"),
+ data={
+ 'id': vid,
+ 'sid': sid,
+ 'gid': gid,
+ 'did': did
+ }
+ )
+
+ except Exception as e:
+ return internal_server_error(errormsg=str(e))
+
+ViewNode.register_node_view(view_blueprint)
+MaterializedViewNode.register_node_view(mview_blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/children/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/children/__init__.py
new file mode 100644
index 0000000..3993ff3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/children/__init__.py
@@ -0,0 +1,12 @@
+"""
+We will use the exisiting modules for creating children module under
+materialize views.
+"""
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.column \
+ import blueprint as columns_module
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.indexes \
+ import blueprint as indexes_modules
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.triggers \
+ import blueprint as triggers_modules
+from pgadmin.browser.server_groups.servers.databases.schemas.tables.rules \
+ import blueprint as rules_modules
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-mview.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-mview.png
new file mode 100644
index 0000000000000000000000000000000000000000..40e65a96e18004104b041b0ae32579b26ae97058
GIT binary patch
literal 374
zcmV-+0g3*JP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU
z000XU0RWnu7ytkPC`m*?R5;6}lfO#CP!xy1+eV6l)y;y*AP73Rh@jQQr4BxYLpzBe
z;^H$1;tQlN;N&6>PA(>hLAOE$2b<YN5@VCx>!1V^ni|PnE{A)5-}!eBlsp2!8Jwv{
z_xxYN&Za98fNAM!r+Elb4K<vFpg}d7qay1FRZB!leDaRNo4Drr^^U#up3r_%Zyrty
zMW0`oB8+_iwCrQMIT$PM+D6LOia3pfYk85jS5BDpT2y2muX@kz%fU#$T)=Rmhd1pI
zN{>)_xh3TSloEv+sugkCnz_QX^u*q6^b<Q5U_V`EKMrm6egBz3w%Z*9N-2a8n3kS`
zXhlcZwT)C6BLS_AeIgkU%MT*y6UP3}1TI)wUL=ftA{hW~`DI$wk61H~McHlU3uk+8
Uv}-=(&;S4c07*qoM6N<$f<!@@p#T5?
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-view.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/coll-view.png
new file mode 100644
index 0000000000000000000000000000000000000000..078d533684b8bd7e185749c4f1c9620af23d114b
GIT binary patch
literal 418
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}Y=BRQE08|>xOdKRnRzD^9{rj2
z_}lVF@Af@;b@}P@d(WRgefI3xqo=p7KVP}!V)Uc`-T(gmd-%V5`%AU||Nn1)p?>Gh
zn(Z&tPJa$N{=xgfzqUJn>TdojJ^s=A@Oy`SZ_IYR((AHW{03+NV@Z%-FoVOh8)+a;
zlDE5yQ1zZ1Ae(`+z$3C4NPB>>+sSM@AS2(?#W6(Ua%g{~P=f-Gv+27P9V>QJ|4;U6
zbe!GKH+kPSMNh%0YD;wVPp!&p$=H2Qa>4_iC-Ws`4oVu`kXvDIe5l_eV|jwW;+5+S
zYgy6~PYdy+-cS45)~k2Z!$WAAUct+ok^krC+MlVs+ppfyc`@F`nC<>l-VMq?t5r)}
zBT7;dOH!?pi&B9UgOP!up{{|Eu910&p^25DrIo3vwt<<IfkFPhkew(Ra`RI%(<*Um
UkbC@w6{vy1)78&qol`;+0LQMW5dZ)H
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/mview.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/mview.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b72bdbb024d206a9ccccc307b980af7dc9cea88
GIT binary patch
literal 344
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wk0|O(M
zr;B5V#p&b(iHrjr$vHbE{{BC@z1bzxXo_Zv!8F~plmsAX<lOhGZpy*4Te=!QPFFbm
zU;Yt~PrZwz#Q!2=gMZH(vj3d_(fm;OaAw=?^=1c;K4AOv`+xgG`_JKRvEsKs+|pZn
z;^>D*x6OT+Cpd08n!IM#^LZ1F3M3y)72cH;)3)hB@w(<$ENtuI!+37X&3YKpe7)iO
zo7EB_E>059WTrQES9;xXdidD%!K10kJRqPs&0k{Df9H%vu?(M!xtNs?&v|Ub)6*j$
zbyq+v$yxg5%?jQE!}Cc$KEG!_e82v_q}|_|69*1#=yHs8ZfyjDhri#K1C8<Dp#F4o
lfA#<Q^_TpMg5)ZIft4K|E4KUpCSXu8c)I$ztaD0e0sxS)lm-9*
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/view.png b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/img/view.png
new file mode 100644
index 0000000000000000000000000000000000000000..44116db56496bb477c8ece7d3fdda666e1b3758b
GIT binary patch
literal 437
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}X@F0NE08|>xOdKRnaBToAO4>7
z;On9XZ+AU@dGYD<`_G?0efI3x<EOW7JYRF{Me@Q^nvec>Z-1%w|NsB(FVt^;p7P|y
zjoU9bT=|i6;cMdQ&tb<tdSCgTbKzU!@sHlyU#LC&-+lap_k(|JcmCAf{8f7ONA}_O
z4*TAi?Rus6RdrP&&|=1tAirP+hi5m^K%69RcNf7&TWxm%Ih+L^k;OpT1B~5HX4?T7
zS)MMAArhC96Am!+_;@g=95{03(5YkR4xT)E^z7l&>_QLRH3SlxI5?abofwq#PpGhn
z3y3`waupO1o<6}vaKfZ%6BvUwB`{h}XxPzW6T>ijp+R<{)&<#R3sx-A(NkQ!Xcb#n
z<0DHqpYzNNJzLm||5$yx0JK%L#5JNMC9x#cD!C{XNHG{07#ipr8tEDsh8P)GnHXD{
zm}(oCS{WG3d#ASwMMG|WN@iLmM1z4PSOZYCiIssFM8m1+p=*E|7(8A5T-G@yGywnx
Cm9(({
literal 0
HcmV?d00001
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/css/mview.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/css/mview.css
new file mode 100644
index 0000000..4782530
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/css/mview.css
@@ -0,0 +1,12 @@
+.icon-mview{
+ background-image: url('{{ url_for('NODE-mview.static', filename='img/mview.png') }}') !important;
+ border-radius: 10px;
+ background-repeat: no-repeat;
+ align-content: center;
+ vertical-align: middle;
+ height: 1.3em;
+}
+
+.sql_field_width_full {
+ width: 100%;
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/js/mview.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/js/mview.js
new file mode 100644
index 0000000..a6d26a4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/js/mview.js
@@ -0,0 +1,315 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'alertify',
+ 'pgadmin.browser', 'codemirror', 'pgadmin.browser.server.privilege'],
+
+function($, _, S, pgAdmin, alertify, pgBrowser, CodeMirror) {
+
+ /**
+ Create and add a view collection into nodes
+ @param {variable} label - Label for Node
+ @param {variable} type - Type of Node
+ @param {variable} columns - List of columns to
+ display under under properties.
+ */
+ if (!pgBrowser.Nodes['coll-mview']) {
+ var mviews= pgAdmin.Browser.Nodes['coll-mview'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'mview',
+ label: '{{ _("Materialized Views") }}',
+ type: 'coll-mview',
+ columns: ['name', 'owner']
+ });
+ }
+
+ /**
+ Create and Add a View Node into nodes
+ @param {variable} parent_type - The list of nodes
+ under which this node to display
+ @param {variable} type - Type of Node
+ @param {variable} hasSQL - To show SQL tab
+ @param {variable} canDrop - Adds drop view option
+ in the context menu
+ @param {variable} canDropCascade - Adds drop Cascade
+ view option in the context menu
+ */
+ if (!pgBrowser.Nodes['mview']) {
+ pgAdmin.Browser.Nodes['mview'] = pgAdmin.Browser.Node.extend({
+ parent_type: ['schema', 'catalog'],
+ type: 'mview',
+ sqlAlterHelp: 'sql-altermaterializedview.html',
+ sqlCreateHelp: 'sql-creatematerializedview.html',
+ label: '{{ _("Materialized View") }}',
+ hasSQL: true,
+ hasDepends: true,
+ collection_type: 'coll-mview',
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ Init: function() {
+
+ // Avoid mulitple registration of menus
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ /**
+ Add "create view" menu option into context and object menu
+ for the following nodes:
+ coll-mview, view and schema.
+ @property {data} - Allow create view option on schema node or
+ system view nodes.
+ */
+ pgBrowser.add_menus([{
+ name: 'create_mview_on_coll', node: 'coll-mview',
+ module: this, applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 1, label: '{{ _("Materialized View...") }}',
+ icon: 'wcTabIcon icon-mview', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_mview', node: 'mview', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 1, label: '{{ _("Materialized View...") }}',
+ icon: 'wcTabIcon icon-mview', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_mview', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 18, label: '{{ _("Materialized View...") }}',
+ icon: 'wcTabIcon icon-mview', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ },{
+ name: 'refresh_mview', node: 'mview', module: this, category: 'Refresh view',
+ applies: ['object', 'context'], callback: 'refresh_mview', icon: 'fa fa-refresh',
+ priority: 1, label: '{{ _("Refresh data") }}', data: {concurrent: false}
+ },{
+ name: 'refresh_mview_concurrent', node: 'mview', module: this,
+ category: 'Refresh view',
+ applies: ['object', 'context'], callback: 'refresh_mview', icon: 'fa fa-refresh',
+ priority: 2, label: '{{ _("Refresh concurrently") }}', data: {concurrent: true}
+ }
+ ]);
+ },
+
+ /**
+ Define model for the view node and specify the
+ properties of the model in schema.
+ */
+ model: pgAdmin.Browser.Node.Model.extend({
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+ if (isNew) {
+ // Set Selected Schema
+ schema_id = args.node_info.schema._id;
+ this.set({'schema': schema_id}, {silent: true});
+
+ // Set Current User
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ this.set({'owner': userInfo.name}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+ defaults: {
+ spcname: 'pg_default',
+ toast_autovacuum_enabled: false,
+ autovacuum_enabled: false
+ },
+ schema: [{
+ id: 'name', label: '{{ _("Name") }}', cell: 'string',
+ type: 'text', disabled: 'inSchema'
+ },
+ {
+ id: 'oid', label:'{{ _("OID") }}', cell: 'string',
+ type: 'text', disabled: true, mode: ['properties']
+ },
+ {
+ id: 'owner', label:'{{ _("Owner") }}', cell: 'string',
+ control: 'node-list-by-name',
+ node: 'role', disabled: 'inSchema'
+ },
+ {
+ id: 'schema', label:'{{ _("Schema") }}', cell: 'string', first_empty: false,
+ control: 'node-list-by-id', type: 'text', cache_level: 'database',
+ node: 'schema', mode: ['create', 'edit'], disabled: 'inSchema'
+ },
+ {
+ id: 'system_view', label:'{{ _("System view?") }}', cell: 'string',
+ type: 'switch', disabled: true, mode: ['properties'],
+ },
+ {
+ id: 'acl', label: '{{ _("ACL") }}',
+ mode: ['properties'], type: 'text'
+ },
+ {
+ id: 'comment', label:'{{ _("Comment") }}', cell: 'string',
+ type: 'multiline'
+ },
+ {
+ id: 'definition', label:'{{ _("") }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], group: 'Definition',
+ control: Backform.SqlFieldControl, extraClasses:['sql_field_width_full']
+ },
+ // Add Privilege Control
+ {
+ id: 'datacl', label: '{{ _("Privileges") }}',
+ model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend(
+ {privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't']}), uniqueCol : ['grantee'],
+ editable: false, type: 'collection', group: '{{ _("Security") }}',
+ mode: ['edit', 'create'], canAdd: true, canDelete: true,
+ control: 'unique-col-collection', priority: 3
+ },
+
+ // Add Security Labels Control
+ {
+ id: 'seclabels', label: '{{ _("Security Labels") }}',
+ model: Backform.SecurityModel, editable: false, type: 'collection',
+ canEdit: false, group: '{{ _("Security") }}', canDelete: true,
+ mode: ['edit', 'create'], canAdd: true,
+ control: 'unique-col-collection', uniqueCol : ['provider']
+ },
+ {
+ id: 'with_data', label: '{{ _("With Data") }}',
+ group: '{{ _("Storage") }}', mode: ['edit', 'create'],
+ type: 'switch',
+ },
+ {
+ id: 'spcname', label: '{{ _("Tablespace") }}', cell: 'string',
+ type: 'text', group: '{{ _("Storage") }}', first_empty: false,
+ control: 'node-list-by-name', node: 'tablespace',
+ filter: function(m) {
+ if (m.label == "pg_global") return false;
+ else return true;
+ }
+ },
+ {
+ id: 'fillfactor', label: '{{ _("Fill Factor") }}',
+ group: '{{ _("Storage") }}', mode: ['edit', 'create'],
+ type: 'integer', min: 10, max: 100
+ },
+ {
+ type: 'nested', control: 'tab', id: 'materialization',
+ label: '{{ _("Materialization") }}', mode: ['edit', 'create'],
+ group: '{{ _("Storage") }}',
+ schema: Backform.VacuumSettingsSchema
+ },
+ ],
+ validate: function(keys) {
+
+ // Triggers specific error messages for fields
+ var err = {},
+ errmsg,
+ field_name = this.get('name'),
+ field_def = this.get('definition');
+ if (_.indexOf(keys, 'autovacuum_enabled') != -1 ||
+ _.indexOf(keys, 'toast_autovacuum_enabled') != -1 )
+ return null;
+
+ if (_.isUndefined(field_name) || _.isNull(field_name) ||
+ String(field_name).replace(/^\s+|\s+$/g, '') == '') {
+ err['name'] = '{{ _("Please specify name.") }}';
+ errmsg = errmsg || err['name'];
+ this.errorModel.set('name', errmsg);
+ return errmsg;
+ }else{
+ this.errorModel.unset('name');
+ }
+ if(this.isNew()){
+ if (_.isUndefined(field_def) || _.isNull(field_def) ||
+ String(field_def).replace(/^\s+|\s+$/g, '') == '') {
+ err['definition'] = '{{ _("Please enter function definition.") }}';
+ errmsg = errmsg || err['definition'];
+ this.errorModel.set('definition', errmsg);
+ return errmsg;
+ }else{
+ this.errorModel.unset('definition');
+ }
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ }),
+
+ /**
+ Show or hide create view menu option on parent node
+ and hide for system view in catalogs.
+ */
+ canCreate: function(itemData, item, data) {
+
+ // If check is false then, we will allow create menu
+ if (data && data.check === false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+
+ // To iterate over tree to check parent node
+ while (i) {
+
+ // If it is schema then allow user to create view
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-mview' == d._type) {
+
+ // Check if we are not child of view
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+
+ // by default we do not want to allow create menu
+ return true;
+
+ },
+ refresh_mview: function(args) {
+ var input = args || {};
+ obj = this,
+ t = pgBrowser.tree,
+ i = input.item || t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined;
+
+ if (!d)
+ return false;
+
+ // Make ajax call to refresh mview data
+ $.ajax({
+ url: obj.generate_url(i, 'refresh_data' , d, true),
+ type: 'PUT',
+ data: {'concurrent': args.concurrent},
+ dataType: "json",
+ success: function(res) {
+ if (res.success == 1) {
+ alertify.success('View refreshed successfully');
+ }
+ else {
+ alertify.alert(
+ 'Error refreshing view',
+ res.data.result
+ );
+ }
+ },
+ error: function(e) {
+ var errmsg = $.parseJSON(e.responseText);
+ alertify.alert('Error refreshing view', errmsg.errormsg);
+ }
+ });
+
+ }
+ });
+ }
+
+ return pgBrowser.Nodes['coll-mview'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/acl.sql
new file mode 100644
index 0000000..0c1bd6c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/acl.sql
@@ -0,0 +1,41 @@
+{#============================Get ACLs=========================#}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee,
+ d.grantor,
+ d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON
+ (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'm'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/create.sql
new file mode 100644
index 0000000..29c9d1a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/create.sql
@@ -0,0 +1,42 @@
+{# ===================== Create new view ===================== #}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }}
+{% if(data.fillfactor or data['autovacuum_enabled'] or data['toast_autovacuum_enabled']) %}
+WITH (
+{% if data.fillfactor %}
+ FILLFACTOR = {{ data.fillfactor }}{% if data['autovacuum_enabled'] or data['toast_autovacuum_enabled'] or data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% if data['autovacuum_enabled'] %}
+ autovacuum_enabled = {{ data['autovacuum_enabled']|lower }}{% if data['toast_autovacuum_enabled'] or data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% if data['toast_autovacuum_enabled'] %}
+ {{ 'toast.autovacuum_enabled' }} = {{ data['toast_autovacuum_enabled']|lower }}{% if data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% for field in data['vacuum_data'] %}
+{% if field.value is defined and field.value != '' and field.value != none %}
+{% if loop.index > 1%},
+{% endif %} {{ field.name }} = {{ field.value|lower }}{% endif %}
+{% endfor %}
+
+)
+{% endif %}
+{% if data.spcname %}TABLESPACE {{ data.spcname }}
+{% endif %}AS
+{{ data.definition.rstrip(';') }}
+{% if data.with_data %}
+WITH DATA;
+{% else %}
+WITH NO DATA;
+{% endif %}
+{% if data.comment %}
+
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/delete.sql
new file mode 100644
index 0000000..cf667fc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# =================== Drop/Cascade materialized view by name ====================#}
+{% if vid %}
+SELECT
+ c.relname As name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP MATERIALIZED VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_schema.sql
new file mode 100644
index 0000000..bcc3d1c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_schema.sql
@@ -0,0 +1,7 @@
+{# ===== fetch schema name =====#}
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_view_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_view_name.sql
new file mode 100644
index 0000000..29ca638
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/get_view_name.sql
@@ -0,0 +1,11 @@
+{# ===== get view name against view id ==== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname AS schema
+FROM
+ pg_class c
+ LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+WHERE
+ c.oid = {{vid}}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/grant.sql
new file mode 100644
index 0000000..cff9d31
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ==== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# We will generate Security Label SQL using macro #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}
+{% if data.datacl %}{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/properties.sql
new file mode 100644
index 0000000..6428fc8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/properties.sql
@@ -0,0 +1,111 @@
+{# ========================== Fetch Materialized View Properties ========================= #}
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ c.relispopulated AS with_data,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relacl,
+ nsp.nspname as schema,
+ pg_get_userbyid(c.relowner) AS owner,
+ description AS comment,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {#=============Checks if it is system view================#}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ (SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS seclabels,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'fillfactor=([0-9]*)') AS fillfactor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+ c.reloptions AS reloptions, tst.reloptions AS toast_reloptions,
+ (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
+ (CASE WHEN (substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS autovacuum_custom,
+ (CASE WHEN (substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS toast_autovacuum
+FROM
+ pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'm'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/refresh.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/refresh.sql
new file mode 100644
index 0000000..c157036
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/refresh.sql
@@ -0,0 +1,4 @@
+{#=== refresh mat view [concurrenlty] ===#}
+{% if name and nspname %}
+REFRESH MATERIALIZED VIEW {% if is_concurrent %}CONCURRENTLY{% endif %} {{ conn|qtIdent(nspname, name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/update.sql
new file mode 100644
index 0000000..8264f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/update.sql
@@ -0,0 +1,162 @@
+{# ===================== Update View ===================#}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{%- if data -%}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{# ===== Rename mat view ===== #}
+{% if data.name and data.name != o_data.name %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+
+{% endif %}
+{# ===== Alter schema view ===== #}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+
+{% endif %}
+{# ===== Alter Table owner ===== #}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+
+{% endif %}
+{% if data['vacuum_data']['reset']|length == 0 and
+data['vacuum_data']['changed']|length == 0 and data['settings']|length > 0 %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+SET(
+{% for field in data['settings'] %}
+ {{ field }} = {{ data['settings'][field]|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+);
+
+{% endif %}
+{% if data['vacuum_data']['reset']|length > 0 %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+{% for field in data['vacuum_data']['reset'] %}
+ {{ field.name }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+);
+{% endif %}
+{# ===== First Drop and then create mat view ===== #}
+{% if def and def != o_data.definition.rstrip(';') %}
+{% if data.fillfactor or ( data['vacuum_data']['changed']|length > 0 ) %}
+DROP MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }};
+CREATE MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+WITH(
+{% if data.fillfactor %}
+ FILLFACTOR = {{ data.fillfactor }}{% if data['vacuum_data']['changed']|length > 0 %},{% endif %}{{ '\r' }}
+{% endif %}
+{% if data['vacuum_data']['changed']|length > 0 %}
+{% for field in data['vacuum_data']['changed'] %}
+ {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+{% endif %}
+)
+ AS
+{{ def }}
+{% if data.with_data %}
+ WITH DATA;
+
+{% else %}
+ WITH NO DATA;
+
+{% endif %}
+{% endif %}
+{% else %}
+{# ======= Alter Tablespace ========= #}
+{%- if data.spcoid and o_data.spcoid != data.spcoid -%}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET TABLESPACE {{ data.spcoid }};
+
+{% endif %}
+{# ======= SET/RESET Fillfactor ========= #}
+{% if data.fillfactor and o_data.fillfactor != data.fillfactor %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+SET(FILLFACTOR = {{ data.fillfactor }});
+
+{% elif data.fillfactor == '' %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+ FILLFACTOR
+);
+
+{% endif %}
+{# ===== Check for with_data property ===== #}
+{% if data.with_data is defined and o_data.with_data|lower != data.with_data|lower %}
+REFRESH MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }} WITH{{ ' NO' if data.with_data|lower == 'false' else '' }} DATA;
+
+{% endif %}
+{# ===== Check for Table tab properties ===== #}
+{% if data.autovacuum_custom is defined and data.autovacuum_custom|lower == 'false' %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+ autovacuum_vacuum_threshold,
+ autovacuum_analyze_threshold,
+ autovacuum_vacuum_scale_factor,
+ autovacuum_analyze_scale_factor,
+ autovacuum_vacuum_cost_delay,
+ autovacuum_vacuum_cost_limit,
+ autovacuum_freeze_min_age,
+ autovacuum_freeze_max_age,
+ autovacuum_freeze_table_age
+);
+
+{% elif(data['vacuum_data']['changed']|length > 0) %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }} SET(
+{% for field in data['vacuum_data']['changed'] %}
+{% if field.value != None %}
+ {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endif %}
+{% endfor %}
+);
+
+{% endif %}{# ===== End check for custom autovaccum ===== #}
+{% endif %}{# ===== End block for check data definition ===== #}
+{%- if data.comment and data.comment != o_data.comment -%}
+COMMENT ON MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+
+{% endif %}
+{# ============= The SQL generated below will change privileges ============= #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed -%}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{%- endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# ============== The SQL generated below will change Security Label ========= #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/view_id.sql
new file mode 100644
index 0000000..d192a80
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/pg/9.3_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# ===== Below will provide view id for last created view ==== #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/acl.sql
new file mode 100644
index 0000000..0c1bd6c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/acl.sql
@@ -0,0 +1,41 @@
+{#============================Get ACLs=========================#}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee,
+ d.grantor,
+ d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON
+ (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'm'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/create.sql
new file mode 100644
index 0000000..29c9d1a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/create.sql
@@ -0,0 +1,42 @@
+{# ===================== Create new view ===================== #}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }}
+{% if(data.fillfactor or data['autovacuum_enabled'] or data['toast_autovacuum_enabled']) %}
+WITH (
+{% if data.fillfactor %}
+ FILLFACTOR = {{ data.fillfactor }}{% if data['autovacuum_enabled'] or data['toast_autovacuum_enabled'] or data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% if data['autovacuum_enabled'] %}
+ autovacuum_enabled = {{ data['autovacuum_enabled']|lower }}{% if data['toast_autovacuum_enabled'] or data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% if data['toast_autovacuum_enabled'] %}
+ {{ 'toast.autovacuum_enabled' }} = {{ data['toast_autovacuum_enabled']|lower }}{% if data['vacuum_data']|length > 0 %},{{ '\r' }}{% endif %}
+{% endif %}
+{% for field in data['vacuum_data'] %}
+{% if field.value is defined and field.value != '' and field.value != none %}
+{% if loop.index > 1%},
+{% endif %} {{ field.name }} = {{ field.value|lower }}{% endif %}
+{% endfor %}
+
+)
+{% endif %}
+{% if data.spcname %}TABLESPACE {{ data.spcname }}
+{% endif %}AS
+{{ data.definition.rstrip(';') }}
+{% if data.with_data %}
+WITH DATA;
+{% else %}
+WITH NO DATA;
+{% endif %}
+{% if data.comment %}
+
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/delete.sql
new file mode 100644
index 0000000..cf667fc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# =================== Drop/Cascade materialized view by name ====================#}
+{% if vid %}
+SELECT
+ c.relname As name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP MATERIALIZED VIEW {{ conn|qtIdent(nspname, name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_schema.sql
new file mode 100644
index 0000000..7244ed3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_schema.sql
@@ -0,0 +1,7 @@
+{# ===== fetch schema name =====#}
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_view_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_view_name.sql
new file mode 100644
index 0000000..29ca638
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/get_view_name.sql
@@ -0,0 +1,11 @@
+{# ===== get view name against view id ==== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname AS schema
+FROM
+ pg_class c
+ LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+WHERE
+ c.oid = {{vid}}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/grant.sql
new file mode 100644
index 0000000..cff9d31
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ==== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# We will generate Security Label SQL using macro #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}
+{% if data.datacl %}{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/properties.sql
new file mode 100644
index 0000000..6428fc8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/properties.sql
@@ -0,0 +1,111 @@
+{# ========================== Fetch Materialized View Properties ========================= #}
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ c.relispopulated AS with_data,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relacl,
+ nsp.nspname as schema,
+ pg_get_userbyid(c.relowner) AS owner,
+ description AS comment,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {#=============Checks if it is system view================#}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ (SELECT array_agg(provider || '=' || label) FROM pg_seclabels sl1 WHERE sl1.objoid=c.oid AND sl1.objsubid=0) AS seclabels,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'fillfactor=([0-9]*)') AS fillfactor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)') AS autovacuum_enabled,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS autovacuum_vacuum_threshold,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_vacuum_scale_factor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_analyze_threshold=([0-9]*)') AS autovacuum_analyze_threshold,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS autovacuum_analyze_scale_factor,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS autovacuum_vacuum_cost_delay,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS autovacuum_vacuum_cost_limit,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_min_age=([0-9]*)') AS autovacuum_freeze_min_age,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_max_age=([0-9]*)') AS autovacuum_freeze_max_age,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_freeze_table_age=([0-9]*)') AS autovacuum_freeze_table_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)') AS toast_autovacuum_enabled,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_threshold=([0-9]*)') AS toast_autovacuum_vacuum_threshold,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_vacuum_scale_factor,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_analyze_threshold=([0-9]*)') AS toast_autovacuum_analyze_threshold,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_analyze_scale_factor=([0-9]*[.][0-9]*)') AS toast_autovacuum_analyze_scale_factor,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_delay=([0-9]*)') AS toast_autovacuum_vacuum_cost_delay,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_vacuum_cost_limit=([0-9]*)') AS toast_autovacuum_vacuum_cost_limit,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_min_age=([0-9]*)') AS toast_autovacuum_freeze_min_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_max_age=([0-9]*)') AS toast_autovacuum_freeze_max_age,
+ substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_freeze_table_age=([0-9]*)') AS toast_autovacuum_freeze_table_age,
+ c.reloptions AS reloptions, tst.reloptions AS toast_reloptions,
+ (CASE WHEN c.reltoastrelid = 0 THEN false ELSE true END) AS hastoasttable,
+ (CASE WHEN (substring(array_to_string(c.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS autovacuum_custom,
+ (CASE WHEN (substring(array_to_string(tst.reloptions, ',')
+ FROM 'autovacuum_enabled=([a-z|0-9]*)'))::boolean THEN true ELSE false END) AS toast_autovacuum
+FROM
+ pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'm'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/refresh.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/refresh.sql
new file mode 100644
index 0000000..c157036
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/refresh.sql
@@ -0,0 +1,4 @@
+{#=== refresh mat view [concurrenlty] ===#}
+{% if name and nspname %}
+REFRESH MATERIALIZED VIEW {% if is_concurrent %}CONCURRENTLY{% endif %} {{ conn|qtIdent(nspname, name) }};
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/update.sql
new file mode 100644
index 0000000..8264f0f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/update.sql
@@ -0,0 +1,162 @@
+{# ===================== Update View ===================#}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{%- if data -%}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{# ===== Rename mat view ===== #}
+{% if data.name and data.name != o_data.name %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+
+{% endif %}
+{# ===== Alter schema view ===== #}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+
+{% endif %}
+{# ===== Alter Table owner ===== #}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+
+{% endif %}
+{% if data['vacuum_data']['reset']|length == 0 and
+data['vacuum_data']['changed']|length == 0 and data['settings']|length > 0 %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+SET(
+{% for field in data['settings'] %}
+ {{ field }} = {{ data['settings'][field]|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+);
+
+{% endif %}
+{% if data['vacuum_data']['reset']|length > 0 %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+{% for field in data['vacuum_data']['reset'] %}
+ {{ field.name }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+);
+{% endif %}
+{# ===== First Drop and then create mat view ===== #}
+{% if def and def != o_data.definition.rstrip(';') %}
+{% if data.fillfactor or ( data['vacuum_data']['changed']|length > 0 ) %}
+DROP MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }};
+CREATE MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+WITH(
+{% if data.fillfactor %}
+ FILLFACTOR = {{ data.fillfactor }}{% if data['vacuum_data']['changed']|length > 0 %},{% endif %}{{ '\r' }}
+{% endif %}
+{% if data['vacuum_data']['changed']|length > 0 %}
+{% for field in data['vacuum_data']['changed'] %}
+ {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endfor %}
+{% endif %}
+)
+ AS
+{{ def }}
+{% if data.with_data %}
+ WITH DATA;
+
+{% else %}
+ WITH NO DATA;
+
+{% endif %}
+{% endif %}
+{% else %}
+{# ======= Alter Tablespace ========= #}
+{%- if data.spcoid and o_data.spcoid != data.spcoid -%}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET TABLESPACE {{ data.spcoid }};
+
+{% endif %}
+{# ======= SET/RESET Fillfactor ========= #}
+{% if data.fillfactor and o_data.fillfactor != data.fillfactor %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+SET(FILLFACTOR = {{ data.fillfactor }});
+
+{% elif data.fillfactor == '' %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+ FILLFACTOR
+);
+
+{% endif %}
+{# ===== Check for with_data property ===== #}
+{% if data.with_data is defined and o_data.with_data|lower != data.with_data|lower %}
+REFRESH MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }} WITH{{ ' NO' if data.with_data|lower == 'false' else '' }} DATA;
+
+{% endif %}
+{# ===== Check for Table tab properties ===== #}
+{% if data.autovacuum_custom is defined and data.autovacuum_custom|lower == 'false' %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+RESET(
+ autovacuum_vacuum_threshold,
+ autovacuum_analyze_threshold,
+ autovacuum_vacuum_scale_factor,
+ autovacuum_analyze_scale_factor,
+ autovacuum_vacuum_cost_delay,
+ autovacuum_vacuum_cost_limit,
+ autovacuum_freeze_min_age,
+ autovacuum_freeze_max_age,
+ autovacuum_freeze_table_age
+);
+
+{% elif(data['vacuum_data']['changed']|length > 0) %}
+ALTER MATERIALIZED VIEW {{ conn|qtIdent(data.schema, data.name) }} SET(
+{% for field in data['vacuum_data']['changed'] %}
+{% if field.value != None %}
+ {{ field.name }} = {{ field.value|lower }}{% if not loop.last %},{% endif %}{{ '\r' }}
+{% endif %}
+{% endfor %}
+);
+
+{% endif %}{# ===== End check for custom autovaccum ===== #}
+{% endif %}{# ===== End block for check data definition ===== #}
+{%- if data.comment and data.comment != o_data.comment -%}
+COMMENT ON MATERIALIZED VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+
+{% endif %}
+{# ============= The SQL generated below will change privileges ============= #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.RESETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed -%}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{%- endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# ============== The SQL generated below will change Security Label ========= #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/view_id.sql
new file mode 100644
index 0000000..d192a80
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/mview/ppas/9.3_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# ===== Below will provide view id for last created view ==== #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/css/view.css b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/css/view.css
new file mode 100644
index 0000000..89eae4e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/css/view.css
@@ -0,0 +1,12 @@
+.icon-view{
+ background-image: url('{{ url_for('NODE-view.static', filename='img/view.png') }}') !important;
+ border-radius: 10px;
+ background-repeat: no-repeat;
+ align-content: center;
+ vertical-align: middle;
+ height: 1.3em;
+}
+
+.sql_field_height_140 {
+ height: 140px;
+}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/js/view.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/js/view.js
new file mode 100644
index 0000000..e9d7b6b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/js/view.js
@@ -0,0 +1,268 @@
+define(
+ ['jquery', 'underscore', 'underscore.string', 'pgadmin',
+ 'pgadmin.browser', 'codemirror', 'pgadmin.browser.server.privilege', 'pgadmin.node.rule'],
+
+function($, _, S, pgAdmin, pgBrowser, CodeMirror) {
+
+ /**
+ Create and add a view collection into nodes
+ @param {variable} label - Label for Node
+ @param {variable} type - Type of Node
+ @param {variable} columns - List of columns to
+ display under under properties.
+ */
+ if (!pgBrowser.Nodes['coll-view']) {
+ var views= pgAdmin.Browser.Nodes['coll-view'] =
+ pgAdmin.Browser.Collection.extend({
+ node: 'view',
+ label: '{{ _("Views") }}',
+ type: 'coll-view',
+ columns: ["name", "owner"]
+ });
+ }
+
+ /**
+ Create and Add a View Node into nodes
+ @param {variable} parent_type - The list of nodes
+ under which this node to display
+ @param {variable} type - Type of Node
+ @param {variable} hasSQL - To show SQL tab
+ @param {variable} canDrop - Adds drop view option
+ in the context menu
+ @param {variable} canDropCascade - Adds drop Cascade
+ view option in the context menu
+ */
+ if (!pgBrowser.Nodes['view']) {
+ pgAdmin.Browser.Nodes['view'] = pgAdmin.Browser.Node.extend({
+ parent_type: ['schema', 'catalog'],
+ type: 'view',
+ sqlAlterHelp: 'sql-alterview.html',
+ sqlCreateHelp: 'sql-createview.html',
+ label: '{{ _("View") }}',
+ hasSQL: true,
+ hasDepends: true,
+ collection_type: 'coll-view',
+ canDrop: pgBrowser.Nodes['schema'].canChildDrop,
+ canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
+ Init: function() {
+
+ // Avoid mulitple registration of menus
+ if (this.initialized)
+ return;
+
+ this.initialized = true;
+
+ /**
+ Add "create view" menu option into context and object menu
+ for the following nodes:
+ coll-view, view and schema.
+ @property {data} - Allow create view option on schema node or
+ system view nodes.
+ */
+ pgBrowser.add_menus([{
+ name: 'create_view_on_coll', node: 'coll-view', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 1, label: '{{ _("View...") }}',
+ icon: 'wcTabIcon icon-view', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_view', node: 'view', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 1, label: '{{ _("View...") }}',
+ icon: 'wcTabIcon icon-view', data: {action: 'create', check: true},
+ enable: 'canCreate'
+ },{
+ name: 'create_view', node: 'schema', module: this,
+ applies: ['object', 'context'], callback: 'show_obj_properties',
+ category: 'create', priority: 17, label: '{{ _("View...") }}',
+ icon: 'wcTabIcon icon-view', data: {action: 'create', check: false},
+ enable: 'canCreate'
+ }
+ ]);
+ },
+
+ /**
+ Define model for the view node and specify the
+ properties of the model in schema.
+ */
+ model: pgAdmin.Browser.Node.Model.extend({
+ initialize: function(attrs, args) {
+ var isNew = (_.size(attrs) === 0);
+ if (isNew) {
+ // Set Selected Schema
+ schema_id = args.node_info.schema._id;
+ this.set({'schema': schema_id}, {silent: true});
+
+ // Set Current User
+ var userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
+ this.set({'owner': userInfo.name}, {silent: true});
+ }
+ pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
+ },
+ schema: [{
+ id: 'name', label: '{{ _("Name") }}', cell: 'string',
+ type: 'text', disabled: 'inSchema'
+ },
+ {
+ id: 'oid', label:'{{ _("OID") }}', cell: 'string',
+ type: 'text', disabled: true, mode: ['properties']
+ },
+ {
+ id: 'owner', label:'{{ _("Owner") }}', cell: 'string', control: 'node-list-by-name',
+ node: 'role', disabled: 'inSchema'
+ },
+ {
+ id: 'schema', label:'{{ _("Schema") }}', cell: 'string', first_empty: false,
+ control: 'node-list-by-id', type: 'text', cache_level: 'database',
+ node: 'schema', disabled: 'inSchema', mode: ['create', 'edit'],
+ },
+ {
+ id: 'system_view', label:'{{ _("System view?") }}', cell: 'string',
+ type: 'switch', disabled: true, mode: ['properties']
+ },
+ {
+ id: 'acl', label: '{{ _("Privileges") }}',
+ mode: ['properties'], type: 'text'
+ },
+ {
+ id: 'comment', label:'{{ _("Comment") }}', cell: 'string',
+ type: 'multiline', disabled: 'inSchema'
+ },
+ {
+ id: 'security_barrier', label:'{{ _("Security barrier") }}', cell: 'string',
+ type: 'switch', min_version: '90200', mode: ['create', 'edit'],
+ group: 'Definition', disabled: 'inSchema'
+ },
+ {
+ id: 'check_option', label:'{{ _("Check options") }}',
+ control: 'select2', group: 'Definition', type: 'text',
+ min_version: '90400', mode:['properties', 'create', 'edit'],
+ select2: {
+
+ // set select2 option width to 100%
+ width: '100%'
+ },
+ options:[
+ {label: "No", value: "no"},
+ {label: "Local", value: "local"},
+ {label: "Cascaded", value: "cascaded"}
+ ], disabled: 'inSchema'
+ },
+ {
+ id: 'definition', label:'{{ _("Definition") }}', cell: 'string',
+ type: 'text', mode: ['create', 'edit'], group: 'Definition',
+ control: Backform.SqlFieldControl,
+ disabled: 'inSchema'
+ },
+ {
+ id: 'security', label: '{{ _("Security") }}',
+ type: 'group',
+ visible: function(m) {
+ if (m.top && 'catalog' in m.top.node_info) {
+ return false;
+ }
+ return true;
+ }
+ },
+
+ // Add Privilege Control
+ {
+ id: 'datacl', label: '{{ _("Privileges") }}',
+ model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend(
+ {privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't']}), uniqueCol : ['grantee'],
+ editable: false, type: 'collection', group: 'security',
+ mode: ['edit', 'create'], canAdd: true, canDelete: true,
+ control: 'unique-col-collection', disabled: 'inSchema'
+ },
+
+ // Add Security Labels Control
+ {
+ id: 'seclabels', label: '{{ _("Security labels") }}',
+ model: Backform.SecurityModel, editable: false, type: 'collection',
+ canEdit: false, group: 'security', canDelete: true,
+ mode: ['edit', 'create'], canAdd: true, disabled: 'inSchema',
+ control: 'unique-col-collection', uniqueCol : ['provider']
+ },
+ ],
+ validate: function() {
+
+ // Triggers specific error messages for fields
+ var err = {},
+ errmsg,
+ field_name = this.get('name'),
+ field_def = this.get('definition');
+ if (_.isUndefined(field_name) || _.isNull(field_name) ||
+ String(field_name).replace(/^\s+|\s+$/g, '') == '') {
+ err['name'] = '{{ _("Please specify name.") }}';
+ errmsg = errmsg || err['name'];
+ this.errorModel.set('name', errmsg);
+ return errmsg;
+ }else{
+ this.errorModel.unset('name');
+ }
+ if(this.isNew()){
+ if (_.isUndefined(field_def) || _.isNull(field_def) ||
+ String(field_def).replace(/^\s+|\s+$/g, '') == '') {
+ err['definition'] = '{{ _("Please enter function definition.") }}';
+ errmsg = errmsg || err['definition'];
+ this.errorModel.set('definition', errmsg);
+ return errmsg;
+ }else{
+ this.errorModel.unset('definition');
+ }
+ }
+ return null;
+ },
+ // We will disable everything if we are under catalog node
+ inSchema: function() {
+ if(this.node_info && 'catalog' in this.node_info)
+ {
+ return true;
+ }
+ return false;
+ }
+ }),
+
+ /**
+ Show or hide create view menu option on parent node
+ and hide for system view in catalogs.
+ */
+ canCreate: function(itemData, item, data) {
+
+ // If check is false then, we will allow create menu
+ if (data && data.check == false)
+ return true;
+
+ var t = pgBrowser.tree, i = item, d = itemData;
+
+ // To iterate over tree to check parent node
+ while (i) {
+
+ // If it is schema then allow user to create view
+ if (_.indexOf(['schema'], d._type) > -1)
+ return true;
+
+ if ('coll-view' == d._type) {
+
+ // Check if we are not child of view
+ prev_i = t.hasParent(i) ? t.parent(i) : null;
+ prev_d = prev_i ? t.itemData(prev_i) : null;
+ if( prev_d._type == 'catalog') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ i = t.hasParent(i) ? t.parent(i) : null;
+ d = i ? t.itemData(i) : null;
+ }
+
+ // by default we do not want to allow create menu
+ return true;
+
+ }
+ });
+ }
+
+ return pgBrowser.Nodes['coll-view'];
+});
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/acl.sql
new file mode 100644
index 0000000..5850639
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/acl.sql
@@ -0,0 +1,51 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON (
+ cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ (SELECT
+ (d).grantee AS grantee,
+ (d).grantor AS grantor,
+ (d).is_grantable AS is_grantable,
+ (d).privilege_type AS privilege_type
+ FROM
+ (SELECT
+ aclexplode(relacl) AS d
+ FROM
+ pg_class cl1
+ WHERE
+ cl1.oid = {{ vid }}
+ ) a
+ ) d
+ ) d
+ LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+ LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname, gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/create.sql
new file mode 100644
index 0000000..b11bc8e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/create.sql
@@ -0,0 +1,20 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/properties.sql
new file mode 100644
index 0000000..ce1d801
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/properties.sql
@@ -0,0 +1,69 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ nsp.nspname AS schema,
+ description AS comment,
+ c.reltablespace AS spcoid,
+ pg_get_userbyid(c.relowner) AS owner,
+ pg_get_viewdef(c.oid, true) AS definition,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ {#=============Checks if it is system view================#}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+ (SELECT
+ array_agg(label)
+ FROM
+ pg_seclabels sl1
+ WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0
+ ) AS labels,
+ (SELECT
+ array_agg(provider)
+ FROM
+ pg_seclabels sl2
+ WHERE sl2.objoid=c.oid AND sl2.objsubid=0
+ ) AS providers
+FROM pg_class c
+ LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+ LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+ LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+WHERE ((c.relhasrules
+ AND
+ (EXISTS(
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid) AND (bpchar(r.ev_type) = '1'::bpchar))
+ ))
+ ) AND (c.relkind = 'v'::char))
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid ORDER BY c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/update.sql
new file mode 100644
index 0000000..2f7f82e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/update.sql
@@ -0,0 +1,66 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ AS{{ def }};
+{% endif %}
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/view_id.sql
new file mode 100644
index 0000000..6dff04e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.1_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# Below will provide view id for newly created view #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/acl.sql
new file mode 100644
index 0000000..5f2d0fb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/acl.sql
@@ -0,0 +1,52 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON (
+ cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ (SELECT
+ (d).grantee AS grantee,
+ (d).grantor AS grantor,
+ (d).is_grantable AS is_grantable,
+ (d).privilege_type AS privilege_type
+ FROM
+ (SELECT
+ aclexplode(relacl) AS d
+ FROM
+ pg_class cl1
+ WHERE
+ cl1.oid = {{ vid }}
+ ) a
+ ) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/create.sql
new file mode 100644
index 0000000..4ffb72f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/create.sql
@@ -0,0 +1,23 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if (data.security_barrier) %}{{'\r'}}
+WITH (
+{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/properties.sql
new file mode 100644
index 0000000..20eb3df
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/properties.sql
@@ -0,0 +1,70 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ pg_get_userbyid(c.relowner) AS owner,
+ description As comment,
+ pg_get_viewdef(c.oid, true) AS definition,
+ nsp.nspname AS schema,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ {#=============Checks if it is system view================#}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)
+ ) ))
+ ) AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid ORDER BY c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/update.sql
new file mode 100644
index 0000000..c37d4ba
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/update.sql
@@ -0,0 +1,74 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %}) {% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/view_id.sql
new file mode 100644
index 0000000..6dff04e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.2_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# Below will provide view id for newly created view #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/acl.sql
new file mode 100644
index 0000000..84300cf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/acl.sql
@@ -0,0 +1,38 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON (
+ cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/create.sql
new file mode 100644
index 0000000..4ffb72f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/create.sql
@@ -0,0 +1,23 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if (data.security_barrier) %}{{'\r'}}
+WITH (
+{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/properties.sql
new file mode 100644
index 0000000..7f03304
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/properties.sql
@@ -0,0 +1,74 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relkind,
+ description,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ nsp.nspname AS schema,
+ c.reltablespace AS spcoid,
+ c.relispopulated AS ispopulated,
+ pg_get_userbyid(c.relowner) AS owner,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {# ===== Checks if it is system view ===== #}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/update.sql
new file mode 100644
index 0000000..02d8f37
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/update.sql
@@ -0,0 +1,75 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %})
+{% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/view_id.sql
new file mode 100644
index 0000000..6dff04e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.3_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# Below will provide view id for newly created view #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/acl.sql
new file mode 100644
index 0000000..7d791f5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/acl.sql
@@ -0,0 +1,39 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON
+ (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/create.sql
new file mode 100644
index 0000000..b43b519
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/create.sql
@@ -0,0 +1,22 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %}{{ '\n' }}WITH (
+{% if data.check_option and data.check_option.lower() != 'no' %} check_option={{ data.check_option }}{% endif %}{{ ',\r' if data.check_option and data.check_option.lower() != 'no' and data.security_barrier }}{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner and data.m_view is undefined %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/properties.sql
new file mode 100644
index 0000000..ccd957f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/properties.sql
@@ -0,0 +1,77 @@
+{# ========================== Fetch View Properties ========================= #}
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relkind,
+ description,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ nsp.nspname AS schema,
+ c.relispopulated AS ispopulated,
+ pg_get_userbyid(c.relowner) AS owner,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {# ===== Checks if it is system view ===== #}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'check_option=([a-z]*)') AS check_option,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/update.sql
new file mode 100644
index 0000000..56183fd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/update.sql
@@ -0,0 +1,82 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.check_option and data.check_option != o_data.check_option)
+ or (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %})
+{% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (check_option={{ data.check_option }});
+{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option);
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/view_id.sql
new file mode 100644
index 0000000..ef27594
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/pg/9.4_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# ===== Below will provide view id for last created view ===== #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/acl.sql
new file mode 100644
index 0000000..7d791f5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/acl.sql
@@ -0,0 +1,39 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON
+ (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/create.sql
new file mode 100644
index 0000000..5720b14
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/create.sql
@@ -0,0 +1,20 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/properties.sql
new file mode 100644
index 0000000..4adbebb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/properties.sql
@@ -0,0 +1,76 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relkind,
+ description,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ nsp.nspname AS schema,
+ c.relispopulated AS ispopulated,
+ pg_get_userbyid(c.relowner) AS owner,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {# ===== Checks if it is system view ===== #}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'check_option=([a-z]*)') AS check_option,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/update.sql
new file mode 100644
index 0000000..56183fd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/update.sql
@@ -0,0 +1,82 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.check_option and data.check_option != o_data.check_option)
+ or (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %})
+{% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (check_option={{ data.check_option }});
+{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option);
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/view_id.sql
new file mode 100644
index 0000000..ef27594
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.1_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# ===== Below will provide view id for last created view ===== #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/acl.sql
new file mode 100644
index 0000000..5f2d0fb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/acl.sql
@@ -0,0 +1,52 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON (
+ cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ (SELECT
+ (d).grantee AS grantee,
+ (d).grantor AS grantor,
+ (d).is_grantable AS is_grantable,
+ (d).privilege_type AS privilege_type
+ FROM
+ (SELECT
+ aclexplode(relacl) AS d
+ FROM
+ pg_class cl1
+ WHERE
+ cl1.oid = {{ vid }}
+ ) a
+ ) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/create.sql
new file mode 100644
index 0000000..dc4a3ab
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/create.sql
@@ -0,0 +1,23 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if (data.security_barrier) %}{{'\r'}}
+WITH (
+{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/properties.sql
new file mode 100644
index 0000000..20eb3df
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/properties.sql
@@ -0,0 +1,70 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ pg_get_userbyid(c.relowner) AS owner,
+ description As comment,
+ pg_get_viewdef(c.oid, true) AS definition,
+ nsp.nspname AS schema,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ {#=============Checks if it is system view================#}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)
+ ) ))
+ ) AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid ORDER BY c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/update.sql
new file mode 100644
index 0000000..c37d4ba
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/update.sql
@@ -0,0 +1,74 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %}) {% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/view_id.sql
new file mode 100644
index 0000000..6dff04e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.2_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# Below will provide view id for newly created view #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/acl.sql
new file mode 100644
index 0000000..84300cf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/acl.sql
@@ -0,0 +1,38 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON (
+ cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/create.sql
new file mode 100644
index 0000000..dc4a3ab
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/create.sql
@@ -0,0 +1,23 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if (data.security_barrier) %}{{'\r'}}
+WITH (
+{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ data.owner }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/properties.sql
new file mode 100644
index 0000000..7f03304
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/properties.sql
@@ -0,0 +1,74 @@
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relkind,
+ description,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ nsp.nspname AS schema,
+ c.reltablespace AS spcoid,
+ c.relispopulated AS ispopulated,
+ pg_get_userbyid(c.relowner) AS owner,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {# ===== Checks if it is system view ===== #}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/update.sql
new file mode 100644
index 0000000..02d8f37
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/update.sql
@@ -0,0 +1,75 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %})
+{% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/view_id.sql
new file mode 100644
index 0000000..6dff04e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.3_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# Below will provide view id for newly created view #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/acl.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/acl.sql
new file mode 100644
index 0000000..7d791f5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/acl.sql
@@ -0,0 +1,39 @@
+{# ============================ Get ACLs ========================= #}
+{% if vid %}
+SELECT
+ 'datacl' as deftype,
+ COALESCE(gt.rolname, 'public') grantee,
+ g.rolname grantor,
+ array_agg(privilege_type) as privileges,
+ array_agg(is_grantable) as grantable
+FROM
+ (SELECT
+ d.grantee, d.grantor, d.is_grantable,
+ CASE d.privilege_type
+ WHEN 'DELETE' THEN 'd'
+ WHEN 'INSERT' THEN 'a'
+ WHEN 'REFERENCES' THEN 'x'
+ WHEN 'SELECT' THEN 'r'
+ WHEN 'TRIGGER' THEN 't'
+ WHEN 'UPDATE' THEN 'w'
+ WHEN 'TRUNCATE' THEN 'D'
+ ELSE 'UNKNOWN'
+ END AS privilege_type
+ FROM
+ (SELECT
+ relacl
+ FROM
+ pg_class cl
+ LEFT OUTER JOIN pg_shdescription descr ON
+ (cl.oid=descr.objoid AND descr.classoid='pg_class'::regclass)
+ WHERE
+ cl.oid = {{ vid }}::OID AND relkind = 'v'
+ ) acl,
+ aclexplode(relacl) d
+ ) d
+LEFT JOIN pg_catalog.pg_roles g ON (d.grantor = g.oid)
+LEFT JOIN pg_catalog.pg_roles gt ON (d.grantee = gt.oid)
+GROUP BY
+ g.rolname,
+ gt.rolname
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/create.sql
new file mode 100644
index 0000000..b43b519
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/create.sql
@@ -0,0 +1,22 @@
+{#============================Create new view=========================#}
+{% if display_comments %}
+-- View: {{ conn|qtIdent(data.schema, data.name) }}
+
+-- DROP VIEW {{ conn|qtIdent(data.schema, data.name) }};
+
+{% endif %}
+{% if data.name and data.schema and data.definition %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(data.schema, data.name) }}{% if ((data.check_option and data.check_option.lower() != 'no') or data.security_barrier) %}{{ '\n' }}WITH (
+{% if data.check_option and data.check_option.lower() != 'no' %} check_option={{ data.check_option }}{% endif %}{{ ',\r' if data.check_option and data.check_option.lower() != 'no' and data.security_barrier }}{% if data.security_barrier %} security_barrier={{ data.security_barrier|lower }}{% endif %}{{'\r'}}
+){% endif %} AS
+{{ data.definition.rstrip(';') }};
+{% if data.owner and data.m_view is undefined %}
+
+ALTER TABLE {{ conn|qtIdent(data.schema, data.name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(data.schema, data.name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/delete.sql
new file mode 100644
index 0000000..e50beec
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/delete.sql
@@ -0,0 +1,13 @@
+{# ====================== Drop/Cascade view by name ===================== #}
+{% if vid %}
+SELECT
+ c.relname AS name,
+ nsp.nspname
+FROM
+ pg_class c
+LEFT JOIN pg_namespace nsp ON c.relnamespace = nsp.oid
+WHERE
+ c.relfilenode = {{ vid }};
+{% elif (name and nspname) %}
+DROP VIEW {{nspname}}.{{ conn|qtIdent(name) }} {% if cascade %} CASCADE {% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/get_schema.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/get_schema.sql
new file mode 100644
index 0000000..127d4b9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/get_schema.sql
@@ -0,0 +1,6 @@
+SELECT
+ nspname
+FROM
+ pg_namespace
+WHERE
+ oid = {{ scid }}::oid;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/grant.sql
new file mode 100644
index 0000000..59a310f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/grant.sql
@@ -0,0 +1,6 @@
+{# ===== Grant Permissions to User Role on Views/Tables ===== #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{# ===== We will generate Security Label SQL using macro ===== #}
+{% if data.seclabels %}{% for r in data.seclabels %}{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}{% endfor %}{% endif %}{% if data.datacl %}
+{% for priv in data.datacl %}{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}{% endfor %}{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/properties.sql
new file mode 100644
index 0000000..ccd957f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/properties.sql
@@ -0,0 +1,77 @@
+{# ========================== Fetch View Properties ========================= #}
+{% if (vid and datlastsysoid) or scid %}
+SELECT
+ c.oid,
+ c.xmin,
+ c.relkind,
+ description,
+ (CASE WHEN length(spc.spcname) > 0 THEN spc.spcname ELSE 'pg_default' END) as spcname,
+ c.relname AS name,
+ c.reltablespace AS spcoid,
+ nsp.nspname AS schema,
+ c.relispopulated AS ispopulated,
+ pg_get_userbyid(c.relowner) AS owner,
+ array_to_string(c.relacl::text[], ', ') AS acl,
+ pg_get_viewdef(c.oid, true) AS definition,
+ {# ===== Checks if it is system view ===== #}
+ {% if vid and datlastsysoid %}
+ CASE WHEN {{vid}} <= {{datlastsysoid}} THEN True ELSE False END AS system_view,
+ {% endif %}
+(SELECT
+ array_agg(label)
+FROM
+ pg_seclabels sl1
+WHERE
+ sl1.objoid=c.oid AND sl1.objsubid=0) AS labels,
+(SELECT
+ array_agg(provider)
+FROM
+ pg_seclabels sl2
+WHERE
+ sl2.objoid=c.oid AND sl2.objsubid=0) AS providers,
+ substring(array_to_string(c.reloptions, ',')
+ FROM 'check_option=([a-z]*)') AS check_option,
+ (substring(array_to_string(c.reloptions, ',')
+ FROM 'security_barrier=([a-z|0-9]*)'))::boolean AS security_barrier
+FROM pg_class c
+LEFT OUTER JOIN pg_namespace nsp on nsp.oid = c.relnamespace
+LEFT OUTER JOIN pg_tablespace spc on spc.oid=c.reltablespace
+LEFT OUTER JOIN pg_description des ON (des.objoid=c.oid and des.objsubid=0 AND des.classoid='pg_class'::regclass)
+LEFT OUTER JOIN pg_class tst ON tst.oid = c.reltoastrelid
+ WHERE ((c.relhasrules AND (EXISTS (
+ SELECT
+ r.rulename
+ FROM
+ pg_rewrite r
+ WHERE
+ ((r.ev_class = c.oid)
+ AND (bpchar(r.ev_type) = '1'::bpchar)) )))
+ AND (c.relkind = 'v'::char)
+ )
+{% if (vid and datlastsysoid) %}
+ AND c.oid = {{vid}}::oid
+{% elif scid %}
+ AND c.relnamespace = {{scid}}::oid
+ORDER BY
+ c.relname
+{% endif %}
+
+{% elif type == 'roles' %}
+SELECT
+ pr.rolname
+FROM
+ pg_roles pr
+WHERE
+ pr.rolcanlogin
+ORDER BY
+ pr.rolname
+
+{% elif type == 'schemas' %}
+SELECT
+ nsp.nspname
+FROM
+ pg_namespace nsp
+WHERE
+ (nsp.nspname NOT LIKE E'pg\\_%'
+ AND nsp.nspname != 'information_schema')
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/update.sql
new file mode 100644
index 0000000..56183fd
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/update.sql
@@ -0,0 +1,82 @@
+{# ============================ Update View ========================= #}
+{% import 'macros/security.macros' as SECLABLE %}
+{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
+{% if data %}
+{% set view_name = data.name if data.name else o_data.name %}
+{% set view_schema = data.schema if data.schema else o_data.schema %}
+{% set def = data.definition.rstrip(';') if data.definition %}
+{% if data.name and data.name != o_data.name %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, o_data.name) }}
+ RENAME TO {{ conn|qtIdent(data.name) }};
+{% endif %}
+{% if data.schema and data.schema != o_data.schema %}
+ALTER VIEW {{ conn|qtIdent(o_data.schema, view_name ) }}
+ SET SCHEMA {{ conn|qtIdent(data.schema) }};
+{% endif %}
+{% if data.owner and data.owner != o_data.owner %}
+ALTER TABLE {{ conn|qtIdent(view_schema, view_name) }}
+ OWNER TO {{ conn|qtIdent(data.owner) }};
+{% endif %}
+{% if def and def != o_data.definition.rstrip(';') %}
+CREATE OR REPLACE VIEW {{ conn|qtIdent(view_schema, view_name) }}
+{% if (data.check_option and data.check_option != o_data.check_option)
+ or (data.security_barrier and data.security_barrier != o_data.security_barrier) %}
+ WITH ({% if (data.check_option and data.check_option != 'no') %}check_option={{ data.check_option }}{% endif %}{{', ' if data.check_option and data.check_option != 'no' and data.security_barrier }}{% if data.security_barrier %}security_barrier={{ data.security_barrier|lower }}{% endif %})
+{% endif %}
+ AS{{ def }};
+{% else %}
+{% if (data.security_barrier is defined and data.security_barrier|lower != o_data.security_barrier|lower) %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (security_barrier={{ data.security_barrier|lower }});
+{% endif %}
+{% if (data.check_option and data.check_option != o_data.check_option and data.check_option != 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ SET (check_option={{ data.check_option }});
+{% elif (data.check_option and data.check_option != o_data.check_option and data.check_option == 'no') %}
+ALTER VIEW {{ conn|qtIdent(view_schema, view_name) }} RESET (check_option);
+{% endif %}
+{% endif %}
+
+{% if data.comment and data.comment != o_data.comment %}
+COMMENT ON VIEW {{ conn|qtIdent(view_schema, view_name) }}
+ IS {{ data.comment|qtLiteral }};
+{% endif %}
+{# The SQL generated below will change privileges #}
+{% if data.datacl %}
+{% if 'deleted' in data.datacl %}
+{% for priv in data.datacl.deleted %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in data.datacl %}
+{% for priv in data.datacl.changed %}
+{{ PRIVILEGE.UNSETALL(conn, 'TABLE', priv.grantee, data.name, data.schema) }}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in data.datacl %}
+{% for priv in data.datacl.added %}
+{{ PRIVILEGE.SET(conn, 'TABLE', priv.grantee, data.name, priv.without_grant, priv.with_grant, data.schema) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{# The SQL generated below will change Security Label #}
+{% if data.seclabels is not none and data.seclabels|length > 0 %}
+{% set seclabels = data.seclabels %}
+{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
+{% for r in seclabels.deleted %}
+{{ SECLABLE.DROP(conn, 'VIEW', data.name, r.provider) }}
+{% endfor %}
+{% endif %}
+{% if 'added' in seclabels and seclabels.added|length > 0 %}
+{% for r in seclabels.added %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
+{% for r in seclabels.changed %}
+{{ SECLABLE.APPLY(conn, 'VIEW', data.name, r.provider, r.security_label) }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/view_id.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/view_id.sql
new file mode 100644
index 0000000..ef27594
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/templates/view/ppas/9.4_plus/sql/view_id.sql
@@ -0,0 +1,4 @@
+{# ===== Below will provide view id for last created view ===== #}
+{% if data %}
+SELECT c.oid FROM pg_class c WHERE c.relname = '{{ data.name }}';
+{% endif %}
diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js
index 4a81411..e005570 100644
--- a/web/pgadmin/static/js/backform.pgadmin.js
+++ b/web/pgadmin/static/js/backform.pgadmin.js
@@ -1665,13 +1665,18 @@
(_.isUndefined(s.min_version) ? true :
(server_info.version >= s.min_version)) &&
(_.isUndefined(s.max_version) ? true :
- (server_info.version <= s.max_version))));
+ (server_info.version <= s.max_version)))),
+ visible = true;
+
+ if (s.mode && _.isObject(s.mode))
+ visible = (_.indexOf(s.mode, mode) > -1);
+ if (visible)
+ visible = evalASFunc(s.visible);
+
groupInfo[s.id] = {
label: s.label || s.id,
version_compatible: ver_in_limit,
- visible: !s.mode || (
- s && s.mode && _.isObject(s.mode) &&
- _.indexOf(s.mode, mode) != -1) && evalASFunc(s.visible) || true
+ visible: visible
};
return;
}
@@ -1768,9 +1773,13 @@
// Create an array from the dictionary with proper required
// structure.
_.each(groups, function(val, key) {
- fields.push(_.extend({
- label: key, fields: val
- }, (groupInfo[key] || {version_compatible: true, visible: true})));
+ fields.push(
+ _.extend(
+ _.defaults(
+ groupInfo[key] || {label: key},
+ {version_compatible: true, visible: true}
+ ), {fields: val})
+ );
});
}
view thread (5+ messages)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: [pgAdmin4][Patch]: View and Materialised View Nodes
In-Reply-To: <CAM5-9D-gLAX+396BaQMC83F00gQREEMqrTuOMfKVx4dSY96VKw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox