Hi,

PFA the revised patch as per your comments.
Please review it and let me know the feedback.

Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Tue, Feb 23, 2016 at 4:10 PM, Dave Page <dp...@pgadmin.org> wrote:

> Hi
>
> I've attached an update to this patch, in which I've done some
> word-smithing on various comments, and adjusted the SQL templates to
> improve the formatting.
>
> However, it looks like it's bit-rotted, as the dependents/dependencies
> display is throwing Python errors. Please fix and then I think it's just
> about ready to commit.
>
> Thanks.
>
>
> On Fri, Feb 19, 2016 at 11:03 AM, Sanket Mehta <
> sanket.me...@enterprisedb.com> wrote:
>
>> Hi Dave,
>>
>> PFA the revise patch.
>>
>> It includes changes according to your review comments as well as
>> dependency/dependent part also.
>>
>> Let me know in case anything is missing.
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Mon, Feb 15, 2016 at 10:25 PM, Dave Page <dp...@pgadmin.org> wrote:
>>
>>> And this time with the attachment...
>>>
>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <dp...@pgadmin.org> wrote:
>>>
>>>> That's much better. Just a couple of comments now, partly based on an
>>>> email I wrote earlier:
>>>>
>>>> - There is still inconsistency in comment style. Please see the
>>>> attachment for an example. Note that there is *always* a space between the
>>>> comment marker and text.
>>>>
>>>> - If I try to edit a cast, I can change the description - but no SQL is
>>>> shown on the SQL tab, despite the comment being correctly applied when I
>>>> hit save. The properties pane of the main window is also not updated.
>>>>
>>>> Otherwise, it looks fine.
>>>>
>>>> Thanks.
>>>>
>>>> On Mon, Feb 15, 2016 at 1:28 PM, Sanket Mehta <
>>>> sanket.me...@enterprisedb.com> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> PFA the revised patch with all the required comments.
>>>>>
>>>>>
>>>>>
>>>>> Regards,
>>>>> Sanket Mehta
>>>>> Sr Software engineer
>>>>> Enterprisedb
>>>>>
>>>>> On Mon, Feb 15, 2016 at 4:18 PM, Dave Page <dp...@pgadmin.org> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>>>> sanket.me...@enterprisedb.com> wrote:
>>>>>>
>>>>>>> Hi Dave,
>>>>>>>
>>>>>>> Regarding your suggestion of putting some comments in javascript, I
>>>>>>> think I have already put some comments regarding model data and their
>>>>>>> controls if any extended.
>>>>>>>
>>>>>>> Can you please let me know where exactly you think more comments are
>>>>>>> required?
>>>>>>>
>>>>>>
>>>>>> Hi
>>>>>>
>>>>>> The issue for me is that jQuery code isn't the easiest to read at the
>>>>>> best of times, with nested/anonymous functions and inline JSON etc. As I
>>>>>> look through the code for the various nodes in isolation, it's extremely
>>>>>> difficult to get a sense of what exactly each part of the code is doing. 
>>>>>> In
>>>>>> this example, what I see by reading the code is:
>>>>>>
>>>>>> - Define the required libraries (require.js stuff)
>>>>>> - Extend the collection class
>>>>>> - Extend the node class
>>>>>>   - Define an init function inline
>>>>>>   - Add the menu options
>>>>>>
>>>>>> That part is fairly easy to figure out (easier because there are
>>>>>> blank lines between the logical sections). From there though, it becomes
>>>>>> much harder;
>>>>>>
>>>>>> - There are no blank lines to separate logical code sections at all
>>>>>> between line 48 and 235 (there is one blank line, but it doesn't separate
>>>>>> code sections).
>>>>>> - There are 4 comments that I can see. The first two are identical,
>>>>>> and appear to have identical code blocks following them for reasons that
>>>>>> are not even remotely obvious.
>>>>>> - As a newcomer to this code, I'm wondering if it's purpose is to
>>>>>> define the backform model. If so, why is it not broken up into sections
>>>>>> with a comment to tell me what field each block handles, and any other
>>>>>> useful information I may need to know? If it's not, then what is it for?
>>>>>>
>>>>>> So... I'm not going to tell you exactly where to put comments,
>>>>>> because the point is that without spending a couple of hours 
>>>>>> understanding
>>>>>> this, I simply don't know. The point of the comments (and separation of
>>>>>> logical sections of code with blank lines) is to make it easy for another
>>>>>> developer (especially one as rusty as me) to read and understand, then 
>>>>>> fix
>>>>>> and improve. Be generous with comments, but don't use them unnecessarily
>>>>>> (e.g. "a = 1 // Set a to one").
>>>>>>
>>>>>> Of course, this is not just directed at you Sanket - it's something
>>>>>> all of us working on pgAdmin need to keep in mind.
>>>>>>
>>>>>> Thanks.
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
new file mode 100644
index 0000000..40e11d4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,647 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Implements Cast Node"""
+import json
+from flask import render_template, make_response, current_app, request, jsonify
+from flask.ext.babel import gettext
+from pgadmin.utils.ajax import make_json_response, \
+    make_response as ajax_response, internal_server_error
+from pgadmin.browser.utils import PGChildNodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+class CastModule(CollectionNodeModule):
+    """
+     class CastModule(CollectionNodeModule)
+
+        A module class for Cast node derived from CollectionNodeModule.
+
+    Methods:
+    -------
+    * __init__(*args, **kwargs)
+      - Method is used to initialize the CastModule and it's base module.
+
+    * get_nodes(gid, sid, did)
+      - 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 cast, when any of the database node is
+        initialized.
+    """
+
+    NODE_TYPE = 'cast'
+    COLLECTION_LABEL = 'Casts'
+
+    def __init__(self, *args, **kwargs):
+        super(CastModule, self).__init__(*args, **kwargs)
+
+    def get_nodes(self, gid, sid, did):
+        """
+        Generate the collection node
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        """
+        yield self.generate_browser_collection_node(did)
+
+    @property
+    def node_inode(self):
+        """
+        Override the property to make the node as leaf node
+        """
+        return False
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for cast, when any of the database node is
+        initialized.
+        """
+        return databases.DatabaseModule.NODE_TYPE
+
+
+blueprint = CastModule(__name__)
+
+
+class CastView(PGChildNodeView):
+    """
+    class CastView(PGChildNodeView)
+
+        A view class for cast node derived from PGChildNodeView. This class is
+        responsible for all the stuff related to view like create/update/delete cast,
+        showing properties of cast node, showing sql in sql pane.
+
+    Methods:
+    -------
+    * __init__(**kwargs)
+      - Method is used to initialize the CastView and it's base view.
+
+    * module_js()
+      - This property defines (if javascript) exists for this node.
+        Override this property for your own logic
+
+    * check_precondition()
+      - This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+
+    * list()
+      - This function is used to list all the cast nodes within that collection.
+
+    * nodes()
+      - This function will used to create all the child node within that collection.
+        Here it will create all the cast nodes.
+
+    * properties(gid, sid, did, rg_id)
+      - This function will show the properties of the selected cast node
+
+    * create(gid, sid, did, rg_id)
+      - This function will create the new cast object
+
+    * update(gid, sid, did, rg_id)
+      - This function will update the data for the selected cast node
+
+    * delete(self, gid, sid, rg_id):
+      - This function will drop the cast object
+
+    * msql(gid, sid, did, rg_id)
+      - This function is used to return modified SQL for the selected cast node
+
+    * get_sql(data, rg_id)
+      - This function will generate sql from model data
+
+    * sql(gid, sid, did, rg_id):
+      - This function will generate sql to show it in sql pane for the selected cast node.
+
+    * get_type():
+      - This function will fetch all the types for source and target types select control.
+
+    * get_functions():
+      - This function will fetch associated functions list depending on selected source
+        and target types while creating a new cast node.
+
+    * dependents(gid, sid, did, lid):
+      - This function get the dependents and return ajax response for the cast node.
+
+    * dependencies(self, gid, sid, did, lid):
+      - This function get the dependencies and return ajax response for the cast node.
+
+    """
+
+    node_type = blueprint.node_type
+
+    parent_ids = [
+        {'type': 'int', 'id': 'gid'},
+        {'type': 'int', 'id': 'sid'},
+        {'type': 'int', 'id': 'did'}
+    ]
+    ids = [
+        {'type': 'int', 'id': 'cid'}
+    ]
+
+    operations = dict({
+        'obj': [
+            {'get': 'properties', 'delete': 'delete', 'put': 'update'},
+            {'get': 'list', 'post': 'create'}
+        ],
+        'children': [{
+            'get': 'children'
+        }],
+        'delete': [{'delete': 'delete'}],
+        'nodes': [{'get': 'node'}, {'get': 'nodes'}],
+        'sql': [{'get': 'sql'}],
+        'msql': [{'get': 'msql'}, {'get': 'msql'}],
+        'stats': [{'get': 'statistics'}],
+        'dependency': [{'get': 'dependencies'}],
+        'dependent': [{'get': 'dependents'}],
+        'module.js': [{}, {}, {'get': 'module_js'}],
+        'get_type': [{'get': 'get_src_and_trg_type'}, {'get': 'get_src_and_trg_type'}],
+        'get_functions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def _init_(self, **kwargs):
+        self.conn = None
+        self.template_path = None
+        self.manager = None
+        super(CastView, self).__init__(**kwargs)
+
+    def module_js(self):
+        """
+        This property defines whether javascript exists for this node.
+        """
+        return make_response(
+            render_template(
+                "cast/js/casts.js",
+                _=gettext
+            ),
+            200, {'Content-Type': 'application/x-javascript'}
+        )
+
+    def check_precondition(f):
+        """
+        This function will behave as a decorator which will checks
+        database connection before running view, it will also attaches
+        manager,conn & template_path properties to self
+        """
+
+        @wraps(f)
+        def wrap(*args, **kwargs):
+            # Here args[0] will hold self & kwargs will hold gid,sid,did
+            self = args[0]
+            self.manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(kwargs['sid'])
+            self.conn = self.manager.connection(did=kwargs['did'])
+            # If DB not connected then return error to browser
+            if not self.conn.connected():
+                return precondition_required(
+                    gettext(
+                        "Connection to the server has been lost!"
+                    )
+                )
+            ver = self.manager.version
+            # we will set template path for sql scripts
+            if ver >= 90100:
+                self.template_path = 'cast/sql/9.1_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        """
+        This function is used to list all the cast nodes within that collection.
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :return:
+        """
+        sql = render_template(
+            "/".join([self.template_path, 'properties.sql']),
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+        )
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+
+        for row in res['rows']:
+            row['castcontext'] = True if row['castcontext'] == 'IMPLICIT' else False
+
+        return ajax_response(
+            response=res['rows'],
+            status=200
+        )
+
+    @check_precondition
+    def nodes(self, gid, sid, did):
+        """
+        This function will used to create all the child node within that collection.
+        Here it will create all the cast nodes.
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :return:
+        """
+        res = []
+        sql = render_template(
+            "/".join([self.template_path, 'properties.sql']),
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+        )
+        status, rset = self.conn.execute_2darray(sql)
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        for row in rset['rows']:
+            row['castcontext'] = True if row['castcontext'] == 'IMPLICIT' else False
+            res.append(
+                self.blueprint.generate_browser_node(
+                    row['oid'],
+                    did,
+                    row['name'],
+                    icon="icon-cast"
+                ))
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def properties(self, gid, sid, did, cid):
+        """
+        This function will show the properties of the selected cast node
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param cid: cast id
+        :return:
+        """
+        sql = render_template(
+            "/".join([self.template_path, 'properties.sql']),
+            cid=cid,
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+        )
+        status, res = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=res)
+        result = res['rows'][0]
+
+        return ajax_response(
+            response=res['rows'][0],
+            status=200
+        )
+
+    @check_precondition
+    def create(self, gid, sid, did):
+        """
+        This function will creates new the cast object
+        :param did: database id
+        :param sid: server id
+        :param gid: group id
+        :return:
+        """
+
+        required_args = [
+            'srctyp',
+            'trgtyp'
+        ]
+
+        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
+                    )
+                )
+        try:
+            sql = render_template("/".join([self.template_path, 'create.sql']),
+                                  data=data,
+                                  conn=self.conn,
+                                  )
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # we need oid to to add object in tree at browser, below sql will gives the same
+            sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                  srctyp=data['srctyp'],
+                                  trgtyp=data['trgtyp'],
+                                  datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+                                  )
+            status, cid = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(errormsg=cid)
+
+            return jsonify(
+                node=self.blueprint.generate_browser_node(
+                    cid,
+                    did,
+                    data['name'],
+                    icon="icon-cast"
+                )
+            )
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def update(self, gid, sid, did, cid):
+        """
+        This function will update cast object
+        :param cid: cast id
+        :param did: database id
+        :param sid: server id
+        :param gid: group id
+        :return:
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        sql = self.get_sql(gid, sid, did, data, cid)
+        try:
+            if sql and sql.strip('\n') and sql.strip(' '):
+                status, res = self.conn.execute_scalar(sql)
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                return make_json_response(
+                    success=1,
+                    info="Cast updated",
+                    data={
+                        'id': cid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+            else:
+                return make_json_response(
+                    success=1,
+                    info="Nothing to update",
+                    data={
+                        'id': cid,
+                        'sid': sid,
+                        'gid': gid,
+                        'did': did
+                    }
+                )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def delete(self, gid, sid, did, cid):
+        """
+        This function will drop the cast object
+        :param cid: cast id
+        :param did: database id
+        :param sid: server id
+        :param gid: group id
+        :return:
+        """
+        # Below will decide if it's simple drop or drop with cascade call
+        if self.cmd == 'delete':
+            # This is a cascade operation
+            cascade = True
+        else:
+            cascade = False
+
+        try:
+            # Get name for cast from cid
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  cid=cid)
+            status, res = self.conn.execute_dict(sql)
+            if not status:
+                return internal_server_error(errormsg=res)
+
+            # drop cast
+            result = res['rows'][0]
+            sql = render_template("/".join([self.template_path, 'delete.sql']),
+                                  castsource=result['castsource'],
+                                  casttarget=result['casttarget'],
+                                  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("Cast dropped"),
+                data={
+                    'id': cid,
+                    'sid': sid,
+                    'gid': gid,
+                    'did': did
+                }
+            )
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def msql(self, gid, sid, did, cid=None):
+        """
+         This function returns modified SQL
+         :param cid: cast id
+         :param did: database id
+         :param sid: server id
+         :param gid: group id
+         :return:
+        """
+        data = request.args
+        sql = self.get_sql(gid, sid, did, data, cid)
+        if isinstance(sql, str) and sql and sql.strip('\n') and sql.strip(' '):
+            return make_json_response(
+                data=sql,
+                status=200
+            )
+        else:
+            return make_json_response(
+                data="--modified SQL",
+                status=200
+            )
+
+    def get_sql(self, gid, sid, did, data, cid=None):
+        """
+        This function will return sql for model data
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param cid: cast id
+        :param data: model data
+        :return:
+        """
+        try:
+            if cid is not None:
+                sql = render_template("/".join([self.template_path, 'properties.sql']),
+                                      cid=cid,
+                                      datlastsysoid=self.manager.db_info[did]['datlastsysoid'])
+                status, res = self.conn.execute_dict(sql)
+
+                if not status:
+                    return internal_server_error(errormsg=res)
+
+                old_data = res['rows'][0]
+                sql = render_template(
+                    "/".join([self.template_path, 'update.sql']),
+                    data=data, o_data=old_data
+                )
+            else:
+                if 'srctyp' in data and 'trgtyp' in data:
+                    sql = render_template("/".join([self.template_path, 'create.sql']), data=data, conn=self.conn)
+                else:
+                    sql = "-- incomplete definition"
+            return str(sql)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        """
+        This function will return functions list associated to a cast
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param cid: cast id
+        :return:
+        """
+        res = []
+        data = request.form if request.form else json.loads(request.data.decode())
+        sql = render_template("/".join([self.template_path, 'functions.sql']),
+                              srctyp=data['srctyp'],
+                              trgtyp=data['trgtyp'])
+        status, rset = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rset)
+        res.append({'label': '',
+                    'value': ''})
+
+        for row in rset['rows']:
+            res.append({'label': row['proname'],
+                        'value': row['proname']})
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def get_src_and_trg_type(self, gid, sid, did, cid=None):
+        """
+        This function will return type list
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param cid: cast id
+        :return:
+        """
+        res = []
+        sql = render_template(
+            "/".join([self.template_path, 'getsrcandtrgttype.sql']),
+            cid=cid
+        )
+        status, rset = self.conn.execute_dict(sql)
+
+        if not status:
+            return internal_server_error(errormsg=rset)
+
+        res = [{'label': '', 'value': ''}]
+        for row in rset['rows']:
+            res.append({
+                'label': row['typname'],
+                'value': row['typname']
+            })
+
+        return make_json_response(
+            data=res,
+            status=200
+        )
+
+    @check_precondition
+    def sql(self, gid, sid, did, cid):
+        """
+        This function will generate sql for sql panel
+        :param gid: group id
+        :param sid: server id
+        :param did: database id
+        :param cid: cast id
+        :return:
+        """
+        try:
+            sql = render_template(
+                "/".join([self.template_path, 'sql.sql']),
+                cid=cid,
+                conn=self.conn
+            )
+            status, res = self.conn.execute_scalar(sql)
+            if not status:
+                return internal_server_error(
+                    _("ERROR: Couldn't generate reversed engineered Query for the cast!\n{0}").format(
+                        res
+                        )
+                    )
+
+            if res is None:
+                return gone(
+                    _("ERROR: Couldn't generate reversed engineered Query for the cast node!")
+                    )
+
+            return ajax_response(response=res)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def dependents(self, gid, sid, did, cid):
+        """
+        This function get the dependents and return ajax response
+        for the cast node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            cid: Cast ID
+        """
+        dependents_result = self.get_dependents(self.conn, cid)
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, cid):
+        """
+        This function get the dependencies and return ajax response
+        for the cast node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            cid: Cast ID
+        """
+        dependencies_result = self.get_dependencies(self.conn, cid)
+        return ajax_response(
+                response=dependencies_result,
+                status=200
+                )
+
+CastView.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/cast.png b/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/cast.png
new file mode 100644
index 0000000000000000000000000000000000000000..2be7f3742a760faa7709052669f444ba8949c330
GIT binary patch
literal 426
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}a)3{WE0A8=XLIY~`bhs>bEjXs
zdtc(<#)_V{^GlchKee~#-UGQ6EB@bqsIhL{|A&u_H*fy`<f;9xUH_jw_c?g*|BIJV
zCr<o-{W{~qh5v8gmR`H||NZ->d-wi-{Mh^W@&C`CroDLaf6w;E(Q{(sfz~jV1o;Is
zI6S+N2IO!SctjQhX%8@VJDF_<WYl@OIEF}E&OLuysL4Qp`NFZQywjDGIy`p%_#dBn
zQ0(`EO7CUHXQe*N`?)=t^Q7D8w8}9V*M&OXmYcSQMTTxXa5eZ^sYbzVjoHtlAM6X>
zw)Vt7;XUhu?aCg8-q)T#djsp?1vQn0(#HZ{&avK>G;7M|Kezi*1J|9@wM@A8GIu5a
z7k@SvKA%lLfi6)kag8WRNi0dVN-jzTQVd20h6cKZM!E)uAw~vPCdO7KCfWw3Rt5$Z
sGgakKH00)|WTsU@G#FTdHGouG8JIydoSGiG2B?9-)78&qol`;+06z@3hyVZp

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/coll-cast.png b/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/coll-cast.png
new file mode 100644
index 0000000000000000000000000000000000000000..09eb65af02c66bd64ab3405c592efe4d90d41c98
GIT binary patch
literal 402
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}RDe&2E08|5w`XaeO<lq4NdH}P
zr(e5!U!p#*dg;>t_Z}!tZ4Ap#oci#Q{=SXoPoCQE-(<UL)&Gqf|L@xM|LFFF=Pv>e
zAO8R0ecP*7DQD0A|MY3{+qb3HuKmA%|NpaR|1ZhRj0c*|SQ6wH%;50sMjDXAS>O>_
z45U54*zIJt9gval>Eak7ak=#TZN6p&0hSA?yGp&5X6Q%he*0e^ToZZWNTsr+b)t)l
zj9%4Lq4qZOwfFDczc{Oq<6)rmksEJ&nEiOFw@y3DVz*ZO=8+TAZ=JQC&Ch=IlJ%q0
zn#a%d$On5}eQ!_{`O0<1PkWs^`g)O!!JAm-u{;Xg4zyae#5JNMC9x#cD!C{XNHG{0
z7#ipr8tEDsh8P)GnHXD{m}ncAS{WEv%v6;_(U6;;l9^Ts(O_T+)&Np%Wnc!;aB6z!
Q8lVOSPgg&ebxsLQ09Xf~fdBvi

literal 0
HcmV?d00001

diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
new file mode 100644
index 0000000..532936b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,302 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    // Extend the collection class for cast
+    if (!pgBrowser.Nodes['coll-cast']) {
+      var casts = pgAdmin.Browser.Nodes['coll-cast'] =
+        pgAdmin.Browser.Collection.extend({
+          node: 'cast',
+          label: '{{ _('Casts') }}',
+          type: 'coll-cast',
+          columns: ['name', 'description']
+        });
+    };
+
+    // Extend the node class for cast
+    if (!pgBrowser.Nodes['cast']) {
+      pgAdmin.Browser.Nodes['cast'] = pgAdmin.Browser.Node.extend({
+        parent_type: 'database',
+        type: 'cast',
+        canDrop: true,
+        canDropCascade: true,
+        label: '{{ _('Cast') }}',
+        hasSQL: true,
+        hasDepends: true,
+        Init: function() {
+
+          // Avoid multiple registration of menus
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          // Add context menus for cast
+          pgBrowser.add_menus([{
+            name: 'create_cast_on_database', node: 'database', module: this,
+            applies: ['object', 'context'], callback: 'show_obj_properties',
+            category: 'create', priority: 4, label: '{{ _('Cast...') }}',
+            icon: 'wcTabIcon icon-cast', data: {action: 'create'}
+          },{
+            name: 'create_cast_on_coll', node: 'coll-cast', module: this,
+            applies: ['object', 'context'], callback: 'show_obj_properties',
+            category: 'create', priority: 4, label: '{{ _('Cast...') }}',
+            icon: 'wcTabIcon icon-cast', data: {action: 'create'}
+          },{
+            name: 'create_cast', node: 'cast', module: this,
+            applies: ['object', 'context'], callback: 'show_obj_properties',
+            category: 'create', priority: 4, label: '{{ _('Cast...') }}',
+            icon: 'wcTabIcon icon-cast', data: {action: 'create'}
+          }]);
+
+        },
+
+        // Defining backform model for cast node
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,            // Name of the cast
+            encoding: 'UTF8',
+            srctyp: undefined,          // Source type
+            trgtyp: undefined,          // Target type
+            proname: undefined,         // Function
+            castcontext: undefined,     // Context (IMPLICIT/EXPLICIT/ASSIGNMENT)
+            syscast: undefined,         // Is this cast is system object? Yes/No
+            description: undefined      // Comment on the cast
+          },
+
+          // Defining schema for cast
+          schema: [{
+            id: 'name', label: '{{ _('Name') }}', cell: 'string', group: '{{ _('Definition') }}',
+            editable: false, type: 'text', disabled: true, cellHeaderClasses: 'width_percent_50'
+          },{
+            id: 'oid', label:'{{ _('Oid') }}', cell: 'string', group: '{{ _('Definition') }}',
+            editable: false, type: 'text', disabled: true
+          },{
+            id: 'srctyp', label:'{{ _('Source type') }}', url: 'get_type',
+            type: 'text', group: 'Definition', disabled: function(m) {
+            return !m.isNew()
+            }, mode: ['create'],
+
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+
+            /*
+             * Control is extended to create cast name from source type and destination type
+             * once their values are changed
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+
+               onChange: function() {
+                 Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                    this, arguments
+                    );
+
+                 /*
+                  * On source type change, check if both source type and
+                  * target type are set, if yes then fetch values from both
+                  * controls and generate cast name
+                  */
+                 var srctype = this.model.get('srctyp');
+                 var trgtype = this.model.get('trgtyp');
+                 if(srctype != undefined && srctype != '' &&
+                    trgtype != undefined && trgtype != '')
+                   this.model.set("name", srctype+"->"+trgtype);
+                 else
+                   this.model.unset("name");
+               }
+            })
+          },
+
+          /*
+           * Text control for viewing source type in properties and
+           * edit mode only
+           */
+          {
+            id: 'srctyp', label:'{{ _('Source type') }}', type: 'text',
+            group: 'Definition', disabled: true, mode:['properties','edit']
+          },{
+            id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+            type: 'text', group: 'Definition', disabled: function(m) {
+              return !m.isNew()
+              }, mode: ['create'],
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+
+            /*
+             * Control is extended to create cast name from source type and destination type
+             * once their values are changed
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+
+             onChange: function() {
+               Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                 this, arguments
+                 );
+
+                 /*
+                  * on target type change, check if both source type and
+                  * target type are set, if yes then fetch values from both
+                  * controls and generate cast name
+                  */
+               var srcType = this.model.get('srctyp');
+               var trgtype = this.model.get('trgtyp');
+               if(srcType != undefined && srcType != '' &&
+                  trgtype != undefined && trgtype != '')
+                 this.model.set("name", srcType+"->"+trgtype);
+               else
+                 this.model.unset("name");
+             }
+             })
+          },
+          /*
+           * Text control for viewing target type in properties and
+           * edit mode only
+           */
+          {
+            id: 'trgtyp', label:'{{ _('Target type') }}', type: 'text',
+            group: 'Definition', disabled: true, mode:['properties','edit']
+          },
+
+          /*
+           * Proname field is dependent on source type and target type.
+           * On source and target type changed event,
+           * associated functions will be fetch using ajax call
+           */
+          {
+            id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+            type: 'text', disabled: function(m) { return !m.isNew(); },
+            group: 'Definition', mode: ['create'],
+            control: 'node-ajax-options',
+            options: function() {
+
+              var srcTyp = this.model.get('srctyp');
+              var trgtyp = this.model.get('trgtyp');
+              var res = [];
+
+              if(srcTyp != undefined && srcTyp != '' &&
+                 trgtyp != undefined && trgtyp != '')
+              {
+                 var node = this.field.get('schema_node'),
+                 _url = node.generate_url.apply(
+                 node, [
+                   null, 'get_functions', this.field.get('node_data'), false,
+                   this.field.get('node_info')
+                 ]);
+                 $.ajax({
+                 type: 'POST',
+                 timeout: 30000,
+                 url: _url,
+                 cache: false,
+                 async: false,
+                 data: {"srctyp" : srcTyp, "trgtyp" : trgtyp},
+
+                 // On success return function list from server
+                 success: function(result) {
+                   res = result.data;
+                   return res;
+                 },
+
+                 // On failure show error appropriate error message to user
+                 error: function(xhr, status, error) {
+                   try {
+                     var err = $.parseJSON(xhr.responseText);
+                     if (err.success == 0) {
+                       msg = S('{{ _(' + err.errormsg + ')}}').value();
+                       alertify.error("{{ _('" + err.errormsg + "') }}");
+                     }
+                   } catch (e) {}
+                 }
+                });
+              }
+            return res;
+          }
+        },
+        /*
+         * Text type control for viewing function name in properties and
+         * edit mode only
+         */
+        {
+          id: 'proname', label:'{{ _('Function') }}', type: 'text',
+          group: 'Definition', disabled: true, mode:['properties','edit']
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}',
+          options:{'onText':'IMPLICIT','offText':'EXPLICIT'},
+          editable: false, type: 'string', group: 'Definition',
+          mode:['create'],
+          control: Backform.SwitchControl.extend({
+            getValueFromDOM: function() {
+              return this.$input.prop('checked') ? 'IMPLICIT' : 'EXPLICIT';
+            }
+          })
+        },
+        /*
+         * Text control for viewing context in properties and
+         * edit mode
+         */
+        {
+          id: 'castcontext', label:'{{ _('Context') }}', disabled: true,
+          options:[{
+            label: 'IMPLICIT', value: 'IMPLICIT'
+          },{
+            label: 'EXPLICIT', value: 'EXPLICIT'
+          },{
+            label: 'ASSIGNMENT', value: 'ASSIGNMENT'
+          }], editable: false, type: 'select2', group: 'Definition',
+          mode:['properties', 'edit']
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}',type: 'text', group: 'Definition',
+          type: 'multiline', cellHeaderClasses: 'width_percent_50'
+        }
+        ],
+
+        /*
+         * Triggers control specific error messages for source type and
+         * target type if any one of them is not selected while creating
+         * new cast
+         */
+        validate: function(keys){
+
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+
+          // validate source type control
+          if (_.isUndefined(srctype) || _.isNull(srctype) || String(srctype).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = '{{ _('Source type must be selected!') }}';
+            this.errorModel.set('srctyp', msg);
+            return msg;
+          }
+          else
+          {
+            this.errorModel.unset('srctyp');
+          }
+
+          // validate target type control
+          if (_.isUndefined(trgtype) || _.isNull(trgtype) || String(trgtype).replace(/^\s+|\s+$/g, '') == '') {
+            var msg = '{{ _('Target type must be selected!') }}';
+            this.errorModel.set('trgtyp', msg);
+            return msg;
+          }
+          else
+          {
+            this.errorModel.unset('trgtyp');
+          }
+          this.trigger('on-status-clear');
+          return null;
+        }
+      })
+  });
+
+  }
+    return pgBrowser.Nodes['coll-cast'];
+});
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/create.sql
new file mode 100644
index 0000000..a303164
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/create.sql
@@ -0,0 +1,20 @@
+{# CREATE CAST Statement #}
+{% if is_sql %}
+-- Cast: {{conn|qtTypeIdent(data.srctyp)}}->{{ conn|qtTypeIdent(data.trgtyp) }};
+
+-- DROP CAST ({{ conn|qtTypeIdent(data.srctyp) }} AS {{ conn|qtTypeIdent(data.trgtyp) }});
+
+{% endif %}
+{% if data and data.srctyp and data.trgtyp %}
+CREATE CAST ({{ conn|qtTypeIdent(data.srctyp) }} AS {{ conn|qtTypeIdent(data.trgtyp) }})
+{% if data.proname and data.proname != 'binary compatible'%}
+    WITH FUNCTION {{data.proname}}{% else %}
+    WITHOUT FUNCTION{% endif %}
+{% if data.castcontext and data.castcontext != 'EXPLICIT' %}
+
+    AS {{data.castcontext}}{% endif %};
+{# Description for CAST #}
+{% if data.description %}
+COMMENT ON CAST ({{ conn|qtTypeIdent(data.srctyp) }} AS {{ conn|qtTypeIdent(data.trgtyp) }})
+      IS {{ data.description|qtLiteral }};
+{% endif %}{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/delete.sql
new file mode 100644
index 0000000..1ea2343
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/delete.sql
@@ -0,0 +1,14 @@
+{# FETCH CAST SOURCE TYPE AND TARGET TYPE Statement #}
+{% if cid %}
+SELECT
+    format_type(ca.castsource, null) as castsource,
+    format_type(ca.casttarget, null) as casttarget
+FROM
+    pg_cast ca
+WHERE
+    ca.oid = {{cid}}::OID;
+{% endif %}
+{# DROP CAST Statement #}
+{% if castsource and casttarget %}
+DROP CAST ({{castsource}} AS {{casttarget}}) {% if cascade %}CASCADE{%endif%};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql
new file mode 100644
index 0000000..69c041a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql
@@ -0,0 +1,17 @@
+{# FETCH FUNCTIONS depending upon SOURCE TYPE and TARGET TYPE IN CAST  #}
+SELECT
+    proname || '(' || pg_catalog.pg_get_function_identity_arguments(p.oid) || ')' as proname,
+    nspname,
+    proargtypes
+FROM
+    pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
+WHERE
+    proargtypes[0] = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{srctyp|qtLiteral}})
+    AND prorettype = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{trgtyp|qtLiteral}})
+    AND CASE
+        WHEN array_length(proargtypes,1)  = 2 THEN
+            proargtypes[1] = 23
+        WHEN array_length(proargtypes,1)  >= 3 THEN
+            proargtypes[1] = 23 AND proargtypes[2] = 16
+        ELSE TRUE
+    END
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..fc78ff0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql
@@ -0,0 +1,46 @@
+SELECT
+    *
+FROM (
+	SELECT
+	    format_type(t.oid,NULL) AS typname,
+		CASE
+		    WHEN typelem > 0 THEN typelem
+	        ELSE t.oid
+		END as elemoid,
+		typlen,
+		typtype,
+		t.oid,
+	    nspname,
+		(SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname = t.typname) > 1 AS isdup
+	FROM
+	    pg_type t
+	    JOIN pg_namespace nsp ON typnamespace=nsp.oid
+    WHERE
+        (NOT (typname = 'unknown'
+		AND nspname = 'pg_catalog'))
+		AND typisdefined
+		AND typtype IN ('b', 'c', 'e', 'r')
+		AND NOT EXISTS (
+		    SELECT
+		        1
+		    FROM
+		        pg_class
+		    WHERE
+		        relnamespace = typnamespace
+		        AND relname = typname
+		        AND relkind != 'c')
+		        AND (typname NOT LIKE '_%'
+		            OR NOT EXISTS (
+		               SELECT
+		                   1
+		               FROM
+		                   pg_class
+		               WHERE
+		                   relnamespace = typnamespace
+		                   AND relname = SUBSTRING(typname FROM 2)::name
+		                   AND relkind != 'c'
+		            )
+		        )
+		        AND nsp.nspname != 'information_schema' ) AS dummy
+            ORDER BY
+                nspname <> 'pg_catalog', nspname <> 'public', nspname, 1
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
new file mode 100644
index 0000000..bc866e0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
@@ -0,0 +1,63 @@
+{# Get OID for CAST #}
+{% if srctyp and trgtyp %}
+  SELECT
+      ca.oid
+  FROM
+      pg_cast ca
+  WHERE
+      ca.castsource = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{srctyp|qtLiteral}})
+  AND ca.casttarget = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{trgtyp|qtLiteral}})
+  {% if datlastsysoid %}
+      AND ca.oid > {{datlastsysoid}}::OID
+  {% endif %}
+
+{# FETCH properties for CAST #}
+{% else %}
+    SELECT
+        ca.oid,
+    CASE
+        WHEN {{datlastsysoid}}::OID > ca.oid then 'Yes' ELSE 'No'
+    END AS syscast,
+    CASE
+        WHEN ca.castcontext = 'a' THEN 'ASSIGNMENT'
+        WHEN ca.castcontext = 'i' THEN 'IMPLICIT'
+        WHEN ca.castcontext = 'e' THEN 'EXPLICIT'
+    END AS castcontext,
+    CASE
+        WHEN proname IS NULL THEN 'binary compatible'
+        ELSE proname || '(' || pg_catalog.pg_get_function_identity_arguments(pr.oid) || ')'
+    END AS proname,
+        ca.castfunc,
+        format_type(st.oid,NULL) AS srctyp,
+        format_type(tt.oid,tt.typtypmod) AS trgtyp,
+        ns.nspname AS srcnspname,
+        nt.nspname AS trgnspname,
+        np.nspname AS pronspname,
+        description,
+        concat(format_type(st.oid,NULL),'->',format_type(tt.oid,tt.typtypmod)) as name
+  FROM pg_cast ca
+  JOIN pg_type st ON st.oid=castsource
+  JOIN pg_namespace ns ON ns.oid=st.typnamespace
+  JOIN pg_type tt ON tt.oid=casttarget
+  JOIN pg_namespace nt ON nt.oid=tt.typnamespace
+  LEFT JOIN pg_proc pr ON pr.oid=castfunc
+  LEFT JOIN pg_namespace np ON np.oid=pr.pronamespace
+  LEFT OUTER JOIN pg_description des ON (des.objoid=ca.oid AND des.objsubid=0 AND des.classoid='pg_cast'::regclass)
+
+  {% if cid %}
+      WHERE ca.oid={{cid}}::int
+  {% endif %}
+
+--TODO: add check for showSystemObject(). currently assumed as false
+    {#
+    {% if datlastsysoid %}
+        {% if cid %}
+            AND
+        {% else %}
+            WHERE
+        {% endif %}
+        ca.oid > {{datlastsysoid}}::OID
+    {% endif %}
+    #}
+    ORDER BY st.typname, tt.typname
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/sql.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/sql.sql
new file mode 100644
index 0000000..64ed166
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/sql.sql
@@ -0,0 +1,44 @@
+SELECT
+    array_to_string(array_agg(sql), E'\n\n') as sql
+FROM
+(SELECT
+    E'-- Cast: ' ||
+    format_type(st.oid, null)|| E' -> ' ||
+    format_type(tt.oid, tt.typtypmod) ||
+    E'\n\n-- DROP CAST (' || format_type(st.oid, null) ||
+    E' AS ' || format_type(tt.oid,tt.typtypmod) ||
+    E');\n\n  CREATE CAST (' || format_type(st.oid, null) ||
+    E' AS ' || format_type(tt.oid,tt.typtypmod) || E')\n' ||
+    CASE WHEN ca.castfunc != 0 THEN
+    E'\tWITH FUNCTION ' ||
+    pr.proname || '(' || COALESCE(pg_catalog.pg_get_function_identity_arguments(pr.oid), '') || E')'
+    WHEN ca.castfunc = 0 AND ca.castmethod = 'i' THEN
+    E'\tWITH INOUT'
+    ELSE E'\tWITHOUT FUNCTION' END ||
+    CASE WHEN ca.castcontext = 'a' THEN E'\n\tAS ASSIGNMENT;'
+    WHEN ca.castcontext = 'i' THEN E'\n\tAS IMPLICIT;'
+    ELSE E';' END ||
+    CASE WHEN a.description IS NOT NULL THEN
+        E'\n\nCOMMENT ON CAST (' || (format_type(st.oid,NULL)) ||
+        E' AS ' || (format_type(tt.oid,tt.typtypmod)) ||
+        E') IS ' || pg_catalog.quote_literal(description) || E';'
+    ELSE ''  END as sql
+FROM
+    pg_cast ca
+    JOIN pg_type st ON st.oid=ca.castsource
+    JOIN pg_namespace ns ON ns.oid=st.typnamespace
+    JOIN pg_type tt ON tt.oid=ca.casttarget
+    JOIN pg_namespace nt ON nt.oid=tt.typnamespace
+    LEFT JOIN pg_proc pr ON pr.oid=ca.castfunc
+    LEFT JOIN (
+        SELECT
+            des.description as description,
+            des.objoid as descoid
+        FROM
+            pg_description des
+        WHERE
+            des.objoid={{cid}}::OID AND des.objsubid=0 AND des.classoid='pg_cast'::regclass
+    ) a ON (a.descoid = ca.oid)
+WHERE
+    ca.oid={{cid}}::OID
+) c;
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/update.sql
new file mode 100644
index 0000000..8b90a23
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/update.sql
@@ -0,0 +1,6 @@
+{# UPDATE Description for CAST #}
+
+{%  if data and 'description' in data and data.description != o_data.description %}
+  COMMENT ON CAST ({{ conn|qtTypeIdent(o_data.srctyp) }} AS {{ conn|qtTypeIdent(o_data.trgtyp) }})
+    IS {{ data.description|qtLiteral }};
+{% endif %}
\ No newline at end of file
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to