public inbox for [email protected]  
help / color / mirror / Atom feed
patch for cast module
24+ messages / 4 participants
[nested] [flat]

* patch for cast module
@ 2016-01-18 13:46  Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-01-18 13:46 UTC (permalink / raw)
  To: pgadmin-hackers

Hi,

PFA patch for cast module.
Please do review it and let me know in case of any issue.


Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] cast.patch (33.8K, 3-cast.patch)
  download | inline diff:
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..edb937b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,470 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2015, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class CastModule(CollectionNodeModule):
+    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
+        """
+        yield self.generate_browser_collection_node(did)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for server, when any of the server-group node is
+        initialized.
+        """
+        return databases.DatabaseModule.NODE_TYPE
+
+
+blueprint = CastModule(__name__)
+
+
+class CastView(NodeView):
+    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'
+        }],
+        '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_type': [{'get': 'get_sourceTarget_type'}, {'get': 'get_sourceTarget_type'}],
+        'getfunctions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "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 >= 90000:
+                self.template_path = 'cast/sql/9.0_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        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)
+        return ajax_response(
+                response=res['rows'],
+                status=200
+                )
+
+    @check_precondition
+    def nodes(self, gid, sid, did):
+        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']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        row['name'],
+                        icon="icon-cast"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, cid):
+        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)
+
+        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
+        """
+
+        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
+                                )
+            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, 'grant.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,
+                    data['name'],
+                    icon="pg-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
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        SQL = self.getSQL(gid, sid, 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
+        """
+        # 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 and drop it
+            SQL = render_template("/".join([self.template_path, 'delete.sql']),
+                                  cid=cid,
+                                  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
+        """
+        data = request.args
+        SQL = self.getSQL(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 getSQL(self, gid, sid, did, data, cid=None):
+        """
+        This function will return SQL for model data
+        """
+        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)
+                    SQL += "\n"
+                    SQL += render_template("/".join([self.template_path, 'grant.sql']), data=data)
+                else:
+                    SQL = "-- incomplete definition"
+            return SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        res=[]
+        data = request.form if request.form else json.loads(request.data.decode())
+        #srcOid = data['srctyp'].split(":")[0]
+        #trgOid = data['trgtyp'].split(":")[0]
+        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)
+
+        # TODO: add schemaprefix to proname before adding it to value in res
+        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_sourceTarget_type(self, gid, sid, did, cid=None):
+        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']:
+            # TODO: Follow dlgTypeProperty::FillDataType() function before adding typename to res
+            res.append({'label': row['typname'],
+                        'value': row['typname']})
+
+
+        print(res)
+        return make_json_response(
+            data=res,
+            status=200
+            )
+
+
+    """def getDataType(self, resultRow, numdims=0, typmod = -1):
+
+        nspname = resultRow['nspname']
+        typname = resultRow['typname']
+        isdup = resultRow['isdup']
+        name = ""
+        array = ""
+        length = ""
+        prec = 0
+        len = 0
+        if str(nspname + '\".') in typname:
+            name = typname[nspname.len() + 3]    # "+2" because of the two double quotes
+        elif str(nspname + ".") in typname:
+            name = typname[nspname.len() + 1]
+        else:
+            name = typname
+
+        if name.startswith("_"):
+            if not numdims:
+                numdims = 1
+            name = name[1]
+        if name[-2:] == str("[]"):
+            if not numdims:
+                numdims = 1
+            name = name[name.len() - 2]
+
+        if name.startswith('\"') and name.endswith('\"'):
+            name = name[1, name.len() - 2]
+
+        if numdims > 0:
+            while numdims-- :
+                array += str("[]")
+
+        if typmod != -1:
+            length = str("(")
+            if name == str("numeric"):
+                len = (typmod - 4L) >> 16L
+                prec = (typmod - 4) & 0xffff
+                length += str(len)
+                if prec:
+                    length += str(",") + str(prec)
+
+            elif name == str("time") or name == str("timetz") \
+            or name == str("time without time zone") or name == str("time with time zone") \
+            or name == str("timestamp") or name == str("timestamptz") \
+            or name == str("timestamp without time zone") or name == str("timestamp with time zone") \
+            or name == str("bit") or name == str("bit varying") or name == str("varbit"):
+                prec = 0
+                len = typmod
+                length += str(len)
+
+            elif name == str("interval"):
+                prec = 0
+                len = (typmod & 0xffff)
+                length += str(len)
+
+            elif name == str("date"):
+                len = prec = 0
+                length = str("") #Clear Length
+
+            else:
+                prec = 0
+                len = typmod - 4L
+                length += str(len)
+
+            if length.len() > 0:
+                length += str(")")
+        else:
+            len = prec = 0"""
+CastView.register_node_view(blueprint)
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/cast-sm.png b/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/cast-sm.png
new file mode 100644
index 0000000000000000000000000000000000000000..52b775538d0fa24e280d595b8d08eccad868fc9f
GIT binary patch
literal 385
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}Y=BRQE0Es3wsvWsO@89kNdH}P
zr(Zj=J^k)|iTb?irAz-G-&J<+f#RxF|M#vbc=$+v<Hr9_p4#u)_5bMhgy$~;4<G*j
z>Q&0wv;W_|ExmT_|A+T&_wWDz^l9?5Xa7&_?Wrr6y|pSR3}^vkNswPKgTu2MX+REV
zfk$L9koEv$x0Bg+Kt{Bui(`ny<*9wA#T*O-oVD2&UYWh>`2YH4>$a5LxjT3A$<Cnr
z9uvI8D*Kw2vaDZV`nn(^p(jzg?n32_)54MrJ#$<Z#hr<<S;(fnc57kM;^L1&Z*1aD
z`^D)0KhAr=kvV?bYX*CuHL4}95hW>!C8<`)MX5lF!N|bSK-bVn*T68u$iT|P*viC2
z+rZSyz`$arsvL@j-29Zxv`UBu152<5kZLOfGl+&$(?iz)H86O(`njxgN@xNAxrdn&

literal 0
HcmV?d00001

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..501b878
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,183 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    if (!pgBrowser.Nodes['coll-cast']) {
+        var casts = pgAdmin.Browser.Nodes['coll-cast'] =
+        pgAdmin.Browser.Collection.extend({
+            node: 'cast',
+            label: '{{ _('Casts') }}',
+            type: 'coll-cast'
+        });
+    };
+
+    if (!pgBrowser.Nodes['cast']) {
+    pgAdmin.Browser.Nodes['cast'] = pgAdmin.Browser.Node.extend({
+      parent_type: 'database',
+      type: 'cast',
+      label: '{{ _('Cast') }}',
+      hasSQL: true,
+      Init: function() {
+        /* Avoid mulitple registration of menus */
+        if (this.initialized)
+            return;
+
+        this.initialized = true;
+
+        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'}
+        },{
+          name: 'drop_cast', node: 'cast', module: this,
+          applies: ['object', 'context'], callback: 'delete_obj',
+          category: 'drop', priority: 4, label: '{{ _('Delete/Drop Cast...') }}',
+          icon: 'fa fa-thrash'
+        },{
+          name: 'drop_cascaded_cast', node: 'cast', module: this,
+          applies: ['object', 'context'], callback: 'delete_obj_cascade',
+          category: 'drop', priority: 4, label: '{{ _('Drop Cascaded...') }}',
+          icon: 'fa fa-thrash'
+        }]);
+
+      },
+      model: pgAdmin.Browser.Node.Model.extend({
+        defaults: {
+          name: undefined,
+          comment: undefined,
+          encoding: 'UTF8',
+          srctyp: undefined,
+          trgtyp: undefined,
+          proname: undefined,
+          castcontext: undefined,
+          syscast: undefined,
+          description: undefined
+        },
+        schema: [{
+          id: 'name', label: '{{ _('Name') }}', cell: 'string', group: '{{ _('Definition') }}',
+          editable: false, type: 'text', disabled: true
+         },{
+          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()
+          },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+            control: Backform.NodeAjaxOptionsControl.extend({
+                onChange: function() {
+                console.log(this.$el.find("select").val());
+                Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+                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");
+            }
+          })
+        },{
+          id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+          type: 'text', group: 'Definition', disabled: function(m) {
+            return !m.isNew()
+            },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+          control: Backform.NodeAjaxOptionsControl.extend({
+              onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              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");
+          }
+          })
+        },{
+          id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+          editable: false, type: 'text', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition',
+          control: 'select', options: function() {
+            /*
+             * TODO::
+             * Make ajax call here and return it as an array of object
+             * {label: <LABEL>, value: <LABEL>}
+             */
+             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, 'getfunctions', this.field.get('node_data'), false,
+                            this.field.get('node_info')
+                    ]);
+                  $.ajax({
+                  type: 'POST',
+                  timeout: 30000,
+                  url: _url,
+                  cache: false,
+                  //data: this.model.toJSON(true, 0, 'POST'),
+                  data: {"srctyp" : srcTyp, "trgtyp" : trgtyp},
+                  success: function(res) {
+                    console.log(res);
+                    // append to res
+                    return res.data;
+                    //this.model.set('proname',res.data);
+                  },
+                  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;
+          }
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}', options:{'onText':'Implicit','offText':'Explicit'},
+          editable: false, type: 'switch', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition'
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}', group: '{{ _('Definition') }}',
+          type: 'text', group: 'Properties'
+        }
+        ]
+      })
+  });
+
+  }
+    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.0_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
new file mode 100644
index 0000000..160686a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
@@ -0,0 +1,11 @@
+{# ========== Below SQL will create cast =========== #}
+{% if data and data.srctyp and data.trgtyp %}
+CREATE CAST ({{data.srctyp}} AS {{data.trgtyp}})
+{% if data.proname %}
+WITH FUNCTION {{data.proname}}
+{% else %} WITHOUT FUNCTION
+{% endif %}
+{% if data.castcontext == True %}
+AS IMPLICIT
+{% endif %};
+{% endif %}
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
new file mode 100644
index 0000000..bb1635f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
@@ -0,0 +1,10 @@
+{% if cid %}
+SELECT
+    ca.castsource in castsource,
+    ca.casttarget in casttarget
+FROM
+    pg_cast ca
+WHERE
+    ca.oid = {{cid}}::OID
+{% endif %}
+DROP CAST (conn|qtIdent(castsource) AS conn|qtIdent(casttarget)) {% if cascade %}CASCADE{%endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
new file mode 100644
index 0000000..83a2e3d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
@@ -0,0 +1,11 @@
+SELECT
+    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 COALESCE(p.proargtypes[1], 0) = 0
+    AND prorettype = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{trgtyp|qtLiteral}})
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..3d5601c
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
@@ -0,0 +1,41 @@
+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.0_plus/grant.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/grant.sql
new file mode 100644
index 0000000..64fce3d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/grant.sql
@@ -0,0 +1,17 @@
+{# ========== Below creates description for cast =========== #}
+{% if data.description %}
+COMMENT ON CAST ({{ data.srctyp }} AS {{ data.trgtyp }})
+  IS {{ data.description|qtLiteral }};
+{% endif %}
+{# ======== Below SQL will fetch id for given cast ======== #}
+{% if srctyp and trgtyp %}
+SELECT
+    ca.oid
+FROM pg_cast ca
+WHERE pg_cast.castsource = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{srctyp|qtLiteral}})
+AND pg_cast.casttarget = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{trgtyp|qtLiteral}})
+ {% if datlastsysoid %}
+ AND ca.oid > {{datlastsysoid}}::OID
+ {% endif %}
+{% endif %}
+
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
new file mode 100644
index 0000000..edca24f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
@@ -0,0 +1,45 @@
+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'
+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
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
new file mode 100644
index 0000000..1a34693
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
@@ -0,0 +1,6 @@
+{# ===========Below SQL will update cast comments=================== #}
+
+{%  if data and data.description and data.description != o_data.description %}
+COMMENT ON CAST ({{o_data.srctyp}} AS {{o_data.trgtyp}})
+  IS {{ data.description }};
+{% endif %}
\ No newline at end of file


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-01-19 14:36  Sanket Mehta <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-01-19 14:36 UTC (permalink / raw)
  To: pgadmin-hackers

Hi,

PFA updated patch for cast module as per check list provided by Neel.
Please do review it and let me know in case of anything is missing.



Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <[email protected]
> wrote:

> Hi,
>
> PFA patch for cast module.
> Please do review it and let me know in case of any issue.
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv2.patch (31.6K, 3-castv2.patch)
  download | inline diff:
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..3ebc127
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,398 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class CastModule(CollectionNodeModule):
+    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
+        """
+        yield self.generate_browser_collection_node(did)
+
+    @property
+    def script_load(self):
+        """
+        Load the module script for server, when any of the server-group node is
+        initialized.
+        """
+        return databases.DatabaseModule.NODE_TYPE
+
+
+blueprint = CastModule(__name__)
+
+
+class CastView(NodeView):
+    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'
+        }],
+        '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_type': [{'get': 'get_sourceTarget_type'}, {'get': 'get_sourceTarget_type'}],
+        'getfunctions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "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 >= 90000:
+                self.template_path = 'cast/sql/9.0_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        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)
+        return ajax_response(
+                response=res['rows'],
+                status=200
+                )
+
+    @check_precondition
+    def nodes(self, gid, sid, did):
+        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']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        row['name'],
+                        icon="icon-cast"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, cid):
+        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)
+
+        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
+        """
+
+        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
+                                )
+            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,
+                    data['name'],
+                    icon="pg-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
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        SQL = self.getSQL(gid, sid, 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
+        """
+        # 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 and drop it
+            SQL = render_template("/".join([self.template_path, 'delete.sql']),
+                                  cid=cid,
+                                  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
+        """
+        data = request.args
+        SQL = self.getSQL(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 getSQL(self, gid, sid, did, data, cid=None):
+        """
+        This function will return SQL for model data
+        """
+        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)
+                else:
+                    SQL = "-- incomplete definition"
+            return SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        res=[]
+        data = request.form if request.form else json.loads(request.data.decode())
+        #srcOid = data['srctyp'].split(":")[0]
+        #trgOid = data['trgtyp'].split(":")[0]
+        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)
+
+        # TODO: add schemaprefix to proname before adding it to value in res
+        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_sourceTarget_type(self, gid, sid, did, cid=None):
+        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']:
+            # TODO: Follow dlgTypeProperty::FillDataType() function before adding typename to res
+            res.append({'label': row['typname'],
+                        'value': row['typname']})
+
+
+        print(res)
+        return make_json_response(
+            data=res,
+            status=200
+            )
+
+CastView.register_node_view(blueprint)
\ No newline at end of file
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..687aa89
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,204 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    if (!pgBrowser.Nodes['coll-cast']) {
+      var casts = pgAdmin.Browser.Nodes['coll-cast'] =
+        pgAdmin.Browser.Collection.extend({
+          node: 'cast',
+          label: '{{ _('Casts') }}',
+          type: 'coll-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,
+        Init: function() {
+          /* Avoid mulitple registration of menus */
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          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'}
+          }]);
+
+        },
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,
+            comment: undefined,
+            encoding: 'UTF8',
+            srctyp: undefined,
+            trgtyp: undefined,
+            proname: undefined,
+            castcontext: undefined,
+            syscast: undefined,
+            description: undefined
+          },
+          schema: [{
+            id: 'name', label: '{{ _('Name') }}', cell: 'string', group: '{{ _('Definition') }}',
+            editable: false, type: 'text', disabled: true
+          },{
+            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()
+          },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+          /*  As name is being generated from srctyp and trgtyp, a check has been put in
+           *  control field if both are changed or not and depending upon it, name has been set.
+          */
+            control: Backform.NodeAjaxOptionsControl.extend({
+                onChange: function() {
+                console.log(this.$el.find("select").val());
+                Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+                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");
+            }
+          })
+        },{
+          id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+          type: 'text', group: 'Definition', disabled: function(m) {
+            return !m.isNew()
+            },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+          /*  As name is being generated from srctyp and trgtyp, a check has been put in
+           *  control field if both are changed or not and depending upon it, name has been set.
+          */
+          control: Backform.NodeAjaxOptionsControl.extend({
+              onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              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");
+          }
+          })
+        },{
+          id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+          editable: false, type: 'text', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition',
+          control: 'select', options: function() {
+
+             var srcTyp = this.model.get('srctyp');
+             var trgtyp = this.model.get('trgtyp');
+             var res = [];
+             /*  On srctyp and trgtyp state change event an ajax call is made to
+             *   fetch list of related functions
+             */
+             if(srcTyp != undefined && srcTyp != '' && trgtyp != undefined && trgtyp != '')
+             {
+                    var node = this.field.get('schema_node'),
+                    _url = node.generate_url.apply(
+                    node, [
+                            null, 'getfunctions', this.field.get('node_data'), false,
+                            this.field.get('node_info')
+                    ]);
+                  $.ajax({
+                  type: 'POST',
+                  timeout: 30000,
+                  url: _url,
+                  cache: false,
+                  data: {"srctyp" : srcTyp, "trgtyp" : trgtyp},
+                  success: function(res) {
+                    return res.data;
+                  },
+                  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;
+          }
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}', options:{'onText':'Implicit','offText':'Explicit'},
+          editable: false, type: 'switch', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition'
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}', group: '{{ _('Definition') }}',
+          type: 'text', group: 'Properties'
+        }
+        ],
+        validate: function(keys){
+        /*
+            * Triggers specific error messages for srctyp and
+            * trgtyp if any one of them is not selected
+        */
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+          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');
+          }
+
+          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.0_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
new file mode 100644
index 0000000..cee46b3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
@@ -0,0 +1,19 @@
+{# ========== Below SQL will create cast =========== #}
+{% if data and data.srctyp and data.trgtyp %}
+  CREATE CAST ({{data.srctyp}} AS {{data.trgtyp}})
+  {% if data.proname %}
+    WITH FUNCTION {{data.proname}}
+  {% else %}
+    WITHOUT FUNCTION
+  {% endif %}
+  {% if data.castcontext == 'true' %}
+    AS IMPLICIT
+  {% endif %};
+
+{# ========== Below creates description for cast =========== #}
+  {% if data.description %}
+    COMMENT ON CAST ({{ data.srctyp }} AS {{ 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.0_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
new file mode 100644
index 0000000..28a1b0a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
@@ -0,0 +1,10 @@
+{% if cid %}
+  SELECT
+    ca.castsource in castsource,
+    ca.casttarget in casttarget
+  FROM
+    pg_cast ca
+  WHERE
+    ca.oid = {{cid}}::OID
+{% endif %}
+DROP CAST (conn|qtIdent(castsource) AS conn|qtIdent(casttarget)) {% if cascade %}CASCADE{%endif%};
\ No newline at end of file
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
new file mode 100644
index 0000000..84a1b15
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
@@ -0,0 +1,17 @@
+SELECT
+  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.0_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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.0_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
new file mode 100644
index 0000000..697479f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
@@ -0,0 +1,58 @@
+{# ======== Below SQL will fetch id for given cast ======== #}
+{% if srctyp and trgtyp %}
+  SELECT
+    ca.oid
+  FROM pg_cast ca
+  WHERE pg_cast.castsource = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{srctyp|qtLiteral}})
+  AND pg_cast.casttarget = (SELECT t.oid FROM pg_type t WHERE format_type(t.oid, NULL) = {{trgtyp|qtLiteral}})
+  {% if datlastsysoid %}
+   AND ca.oid > {{datlastsysoid}}::OID
+  {% endif %}
+
+{% 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'
+  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.0_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
new file mode 100644
index 0000000..cc141e7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
@@ -0,0 +1,6 @@
+{# ===========Below SQL will update cast comments=================== #}
+
+{%  if data and data.description and data.description != o_data.description %}
+  COMMENT ON CAST ({{o_data.srctyp}} AS {{o_data.trgtyp}})
+    IS {{ data.description }};
+{% endif %}
\ No newline at end of file


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-01-20 04:50  Neel Patel <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Neel Patel @ 2016-01-20 04:50 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Hi Sanket,

Below are the review comments.

- When we edit any existing cast node then it gives error "*Response object
has no attribute strip*". This error is coming because generated SQL is
  wrong.
- Unnecessary debug logs are coming on console. Please remove unnecessary
debug logs.
- In some of the sql file, 'qtIdent' and 'qtLiteral' is not used. Please
check all the SQL files.
- "Delete" cast functionality is not working. Error is getting displayed
saying *"syntax error at or near "castsource"*.
- "Delete cascade" functionality is not working - error is getting
displayed saying *"The requested URL not found".*
- Do the proper comments, in some of the function like "script_load" ,
comments are wrong.
- Is "configs" really required in __init__.py file ? We have not seen any
usage for this. Please remove it if it is not required.
- Remove commented code from the source file.

Please check all the generated SQL statements . Test the basic
functionality of "create", "Edit" and "Delete" node before sending patch
file.

Do let us know for any comments/issues.

Thanks,
Neel Patel

On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <[email protected]
> wrote:

> Hi,
>
> PFA updated patch for cast module as per check list provided by Neel.
> Please do review it and let me know in case of anything is missing.
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
> [email protected]> wrote:
>
>> Hi,
>>
>> PFA patch for cast module.
>> Please do review it and let me know in case of any issue.
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>
>
>
> --
> Sent via pgadmin-hackers mailing list ([email protected])
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgadmin-hackers
>
>


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-01-20 11:33  Sanket Mehta <[email protected]>
  parent: Neel Patel <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-01-20 11:33 UTC (permalink / raw)
  To: Neel Patel <[email protected]>; +Cc: pgadmin-hackers

Hi Neel.

PFA the revised patch which has changed according to your comments.
Please do review it and let me know in case anything is missing.



Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <[email protected]>
wrote:

> Hi Sanket,
>
> Below are the review comments.
>
> - When we edit any existing cast node then it gives error "*Response
> object has no attribute strip*". This error is coming because generated
> SQL is
>   wrong.
> - Unnecessary debug logs are coming on console. Please remove unnecessary
> debug logs.
> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used. Please
> check all the SQL files.
> - "Delete" cast functionality is not working. Error is getting displayed
> saying *"syntax error at or near "castsource"*.
> - "Delete cascade" functionality is not working - error is getting
> displayed saying *"The requested URL not found".*
> - Do the proper comments, in some of the function like "script_load" ,
> comments are wrong.
> - Is "configs" really required in __init__.py file ? We have not seen any
> usage for this. Please remove it if it is not required.
> - Remove commented code from the source file.
>
> Please check all the generated SQL statements . Test the basic
> functionality of "create", "Edit" and "Delete" node before sending patch
> file.
>
> Do let us know for any comments/issues.
>
> Thanks,
> Neel Patel
>
> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
> [email protected]> wrote:
>
>> Hi,
>>
>> PFA updated patch for cast module as per check list provided by Neel.
>> Please do review it and let me know in case of anything is missing.
>>
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>> [email protected]> wrote:
>>
>>> Hi,
>>>
>>> PFA patch for cast module.
>>> Please do review it and let me know in case of any issue.
>>>
>>>
>>> Regards,
>>> Sanket Mehta
>>> Sr Software engineer
>>> Enterprisedb
>>>
>>
>>
>>
>> --
>> Sent via pgadmin-hackers mailing list ([email protected])
>> To make changes to your subscription:
>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>
>>
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv3.patch (32.0K, 3-castv3.patch)
  download | inline diff:
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..37802b0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,402 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class CastModule(CollectionNodeModule):
+    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
+        """
+        yield self.generate_browser_collection_node(did)
+
+    @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(NodeView):
+    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_sourceTarget_type'}, {'get': 'get_sourceTarget_type'}],
+        'getfunctions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "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 >= 90000:
+                self.template_path = 'cast/sql/9.0_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        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)
+        return ajax_response(
+                response=res['rows'],
+                status=200
+                )
+
+    @check_precondition
+    def nodes(self, gid, sid, did):
+        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']:
+            res.append(
+                    self.blueprint.generate_browser_node(
+                        row['oid'],
+                        row['name'],
+                        icon="icon-cast"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, cid):
+        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)
+
+        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
+        """
+
+        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
+                                )
+            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,
+                    data['name'],
+                    icon="pg-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
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        SQL = self.getSQL(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
+        """
+        # 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
+        """
+        data = request.args
+        SQL = self.getSQL(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 getSQL(self, gid, sid, did, data, cid=None):
+        """
+        This function will return SQL for model data
+        """
+        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)
+                else:
+                    SQL = "-- incomplete definition"
+            return SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        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)
+
+        # TODO: add schemaprefix to proname before adding it to value in res
+        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_sourceTarget_type(self, gid, sid, did, cid=None):
+        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']:
+            # TODO: Follow dlgTypeProperty::FillDataType() function before adding typename to res
+            res.append({'label': row['typname'],
+                        'value': row['typname']})
+
+        return make_json_response(
+            data=res,
+            status=200
+            )
+
+CastView.register_node_view(blueprint)
\ No newline at end of file
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..81f3747
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,203 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    if (!pgBrowser.Nodes['coll-cast']) {
+      var casts = pgAdmin.Browser.Nodes['coll-cast'] =
+        pgAdmin.Browser.Collection.extend({
+          node: 'cast',
+          label: '{{ _('Casts') }}',
+          type: 'coll-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,
+        Init: function() {
+          /* Avoid mulitple registration of menus */
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          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'}
+          }]);
+
+        },
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,
+            comment: undefined,
+            encoding: 'UTF8',
+            srctyp: undefined,
+            trgtyp: undefined,
+            proname: undefined,
+            castcontext: undefined,
+            syscast: undefined,
+            description: undefined
+          },
+          schema: [{
+            id: 'name', label: '{{ _('Name') }}', cell: 'string', group: '{{ _('Definition') }}',
+            editable: false, type: 'text', disabled: true
+          },{
+            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()
+          },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+          /*  As name is being generated from srctyp and trgtyp, a check has been put in
+           *  control field if both are changed or not and depending upon it, name has been set.
+          */
+            control: Backform.NodeAjaxOptionsControl.extend({
+                onChange: function() {
+                Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+                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");
+            }
+          })
+        },{
+          id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+          type: 'text', group: 'Definition', disabled: function(m) {
+            return !m.isNew()
+            },
+          transform: function(rows) {
+            _.each(rows, function(r) {
+              r['image'] = 'icon-cast';
+            });
+            return rows;
+          },
+          /*  As name is being generated from srctyp and trgtyp, a check has been put in
+           *  control field if both are changed or not and depending upon it, name has been set.
+          */
+          control: Backform.NodeAjaxOptionsControl.extend({
+              onChange: function() {
+              Backform.NodeAjaxOptionsControl.prototype.onChange.apply(this, arguments);
+              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");
+          }
+          })
+        },{
+          id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+          editable: false, type: 'text', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition',
+          control: 'select', options: function() {
+
+             var srcTyp = this.model.get('srctyp');
+             var trgtyp = this.model.get('trgtyp');
+             var res = [];
+             /*  On srctyp and trgtyp state change event an ajax call is made to
+             *   fetch list of related functions
+             */
+             if(srcTyp != undefined && srcTyp != '' && trgtyp != undefined && trgtyp != '')
+             {
+                    var node = this.field.get('schema_node'),
+                    _url = node.generate_url.apply(
+                    node, [
+                            null, 'getfunctions', this.field.get('node_data'), false,
+                            this.field.get('node_info')
+                    ]);
+                  $.ajax({
+                  type: 'POST',
+                  timeout: 30000,
+                  url: _url,
+                  cache: false,
+                  data: {"srctyp" : srcTyp, "trgtyp" : trgtyp},
+                  success: function(res) {
+                    return res.data;
+                  },
+                  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;
+          }
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}', options:{'onText':'Implicit','offText':'Explicit'},
+          editable: false, type: 'switch', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition'
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}', group: '{{ _('Definition') }}',
+          type: 'text', group: 'Properties'
+        }
+        ],
+        validate: function(keys){
+        /*
+            * Triggers specific error messages for srctyp and
+            * trgtyp if any one of them is not selected
+        */
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+          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');
+          }
+
+          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.0_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
new file mode 100644
index 0000000..cee46b3
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
@@ -0,0 +1,19 @@
+{# ========== Below SQL will create cast =========== #}
+{% if data and data.srctyp and data.trgtyp %}
+  CREATE CAST ({{data.srctyp}} AS {{data.trgtyp}})
+  {% if data.proname %}
+    WITH FUNCTION {{data.proname}}
+  {% else %}
+    WITHOUT FUNCTION
+  {% endif %}
+  {% if data.castcontext == 'true' %}
+    AS IMPLICIT
+  {% endif %};
+
+{# ========== Below creates description for cast =========== #}
+  {% if data.description %}
+    COMMENT ON CAST ({{ data.srctyp }} AS {{ 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.0_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
new file mode 100644
index 0000000..cb5686f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
@@ -0,0 +1,12 @@
+{% 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 %}
+{% 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.0_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
new file mode 100644
index 0000000..84a1b15
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
@@ -0,0 +1,17 @@
+SELECT
+  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.0_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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.0_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
new file mode 100644
index 0000000..2445e2a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
@@ -0,0 +1,59 @@
+{# ======== Below SQL will fetch id for given 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 %}
+
+{# ===== Below SQL will fetch properties for particular cast if oid is provided or all user casts if not provided ==== #}
+{% 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'
+  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.0_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
new file mode 100644
index 0000000..a17b1e6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
@@ -0,0 +1,6 @@
+{# ===========Below SQL will update cast comments=================== #}
+
+{%  if data and data.description and data.description != o_data.description %}
+  COMMENT ON CAST ({{o_data.srctyp}} AS {{o_data.trgtyp}})
+    IS {{ data.description|qtLiteral }};
+{% endif %}
\ No newline at end of file


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-04 13:01  Sanket Mehta <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-04 13:01 UTC (permalink / raw)
  To: pgadmin-hackers

Hi Akshay,

PFA the latest patch for Cast module.
Please do review it and let me know if anything is missing.


Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Wed, Jan 20, 2016 at 5:03 PM, Sanket Mehta <[email protected]
> wrote:

> Hi Neel.
>
> PFA the revised patch which has changed according to your comments.
> Please do review it and let me know in case anything is missing.
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <[email protected]>
> wrote:
>
>> Hi Sanket,
>>
>> Below are the review comments.
>>
>> - When we edit any existing cast node then it gives error "*Response
>> object has no attribute strip*". This error is coming because generated
>> SQL is
>>   wrong.
>> - Unnecessary debug logs are coming on console. Please remove unnecessary
>> debug logs.
>> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used. Please
>> check all the SQL files.
>> - "Delete" cast functionality is not working. Error is getting displayed
>> saying *"syntax error at or near "castsource"*.
>> - "Delete cascade" functionality is not working - error is getting
>> displayed saying *"The requested URL not found".*
>> - Do the proper comments, in some of the function like "script_load" ,
>> comments are wrong.
>> - Is "configs" really required in __init__.py file ? We have not seen any
>> usage for this. Please remove it if it is not required.
>> - Remove commented code from the source file.
>>
>> Please check all the generated SQL statements . Test the basic
>> functionality of "create", "Edit" and "Delete" node before sending patch
>> file.
>>
>> Do let us know for any comments/issues.
>>
>> Thanks,
>> Neel Patel
>>
>> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
>> [email protected]> wrote:
>>
>>> Hi,
>>>
>>> PFA updated patch for cast module as per check list provided by Neel.
>>> Please do review it and let me know in case of anything is missing.
>>>
>>>
>>>
>>> Regards,
>>> Sanket Mehta
>>> Sr Software engineer
>>> Enterprisedb
>>>
>>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>>> [email protected]> wrote:
>>>
>>>> Hi,
>>>>
>>>> PFA patch for cast module.
>>>> Please do review it and let me know in case of any issue.
>>>>
>>>>
>>>> Regards,
>>>> Sanket Mehta
>>>> Sr Software engineer
>>>> Enterprisedb
>>>>
>>>
>>>
>>>
>>> --
>>> Sent via pgadmin-hackers mailing list ([email protected])
>>> To make changes to your subscription:
>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>
>>>
>>
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv5.patch (33.6K, 3-castv5.patch)
  download | inline diff:
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..5bba3d7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,436 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+from html import escape
+
+
+class CastModule(CollectionNodeModule):
+    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
+        """
+        yield self.generate_browser_collection_node(did)
+
+    @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(NodeView):
+    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_sourceTarget_type'}, {'get': 'get_sourceTarget_type'}],
+        'getfunctions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "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 >= 90000:
+                self.template_path = 'cast/sql/9.0_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        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):
+        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'],
+                        row['name'],
+                        icon="icon-cast"
+                    ))
+
+        return make_json_response(
+                data=res,
+                status=200
+                )
+
+    @check_precondition
+    def properties(self, gid, sid, did, cid):
+        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]
+        result['castcontext'] = True if result['castcontext'] == 'IMPLICIT' else False
+
+        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
+        """
+
+        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,
+                    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
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        SQL = self.getSQL(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
+        """
+        # 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
+        """
+        data = request.args
+        SQL = self.getSQL(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 getSQL(self, gid, sid, did, data, cid=None):
+        """
+        This function will return SQL for model data
+        """
+        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 SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        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)
+
+        # TODO: add schemaprefix to proname before adding it to value in res
+        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_sourceTarget_type(self, gid, sid, did, cid=None):
+        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']:
+            # TODO: Follow dlgTypeProperty::FillDataType() function before adding typename to res
+            res.append({
+                'label': row['typname'],
+                'value': escape(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
+        """
+        try:
+            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]
+            result['castcontext'] = False if result['castcontext'] == 'EXPLICIT' else True
+            SQL = render_template("/".join([self.template_path, 'create.sql']), data=result, conn=self.conn, is_sql=True)
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+CastView.register_node_view(blueprint)
\ No newline at end of file
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..8f9b806
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,209 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    if (!pgBrowser.Nodes['coll-cast']) {
+      var casts = pgAdmin.Browser.Nodes['coll-cast'] =
+        pgAdmin.Browser.Collection.extend({
+          node: 'cast',
+          label: '{{ _('Casts') }}',
+          type: 'coll-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,
+        Init: function() {
+          /* Avoid mulitple registration of menus */
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          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'}
+          }]);
+
+        },
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,
+            encoding: 'UTF8',
+            srctyp: undefined,
+            trgtyp: undefined,
+            proname: undefined,
+            castcontext: undefined,
+            syscast: undefined,
+            description: undefined
+          },
+          schema: [{
+            id: 'name', label: '{{ _('Name') }}', cell: 'string', group: '{{ _('Definition') }}',
+            editable: false, type: 'text', disabled: true
+          },{
+            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()
+            },
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+               onChange: function() {
+                 Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                    this, arguments
+                    );
+                 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");
+               }
+            })
+          },{
+            id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+            type: 'text', group: 'Definition', disabled: function(m) {
+              return !m.isNew()
+              },
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+             onChange: function() {
+               Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                 this, arguments
+                 );
+               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");
+             }
+             })
+          },{
+            id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+            type: 'text', disabled: function(m) { return !m.isNew(); },
+            group: 'Definition',
+            control: 'node-ajax-options', options: function() {
+
+              var srcTyp = this.model.get('srctyp');
+              var trgtyp = this.model.get('trgtyp');
+              var res = [];
+              /*  On srctyp and trgtyp state change event an ajax call is made to
+              *   fetch list of related functions
+              */
+              if(srcTyp != undefined && srcTyp != '' &&
+                 trgtyp != undefined && trgtyp != '')
+              {
+                 var node = this.field.get('schema_node'),
+                 _url = node.generate_url.apply(
+                 node, [
+                   null, 'getfunctions', this.field.get('node_data'), false,
+                   this.field.get('node_info')
+                 ]);
+                 $.ajax({
+                 type: 'POST',
+                 timeout: 30000,
+                 url: _url,
+                 cache: false,
+                 data: {"srctyp" : srcTyp, "trgtyp" : trgtyp},
+                 success: function(res) {
+                   return res.data;
+                 },
+                 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;
+          }
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}', options:{'onText':'IMPLICIT','offText':'EXPLICIT'},
+          editable: false, type: 'switch', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition'
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}',type: 'text', group: 'Properties',
+          type: 'multiline'
+        }
+        ],
+        validate: function(keys){
+        /*
+            * Triggers specific error messages for srctyp and
+            * trgtyp if any one of them is not selected
+        */
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+          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');
+          }
+
+          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.0_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
new file mode 100644
index 0000000..501aa1a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
@@ -0,0 +1,21 @@
+
+
+{# CREATE CAST Statement #}
+{% if is_sql %}
+-- 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 == True or data.castcontext == 'true' %}
+
+    AS IMPLICIT{% 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.0_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
new file mode 100644
index 0000000..1b8d8b6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_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.0_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
new file mode 100644
index 0000000..88c0e4b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
@@ -0,0 +1,18 @@
+{# FETCH FUNCTIONS depending upon SOURCE TYPE and TARGET TYPE IN CAST  #}
+SELECT
+  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.0_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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.0_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
new file mode 100644
index 0000000..e5e9188
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
@@ -0,0 +1,60 @@
+{# 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
+  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.0_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
new file mode 100644
index 0000000..4b8dcc6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/update.sql
@@ -0,0 +1,6 @@
+{# UPDATE Description for CAST #}
+
+{%  if data and data.description 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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-05 07:13  Akshay Joshi <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Akshay Joshi @ 2016-02-05 07:13 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Hi Sanket

Below are the review comments

   - As "Show System Object" is not implemented yet, we should show all the
   objects by default.
   - As in pgAdmin3 when click on Casts (Collection) node it should show
   only Name, Owner and Comments. With current code it is showing all the
   properties.
   - Properties Tab contains only one control "Comment" can that be a part
   of the Definition tab???
   - For some data type like "Character", "Integer", it is throwing error
   that data type doesn't exist.
   - If node is leaf node then it should not show (+) expand symbol.
   - Remove extra lines from create.sql and update.sql files as it shown in
   the SQL tab as well.
   - When select any system cast it is not showing function in the function
   control.
   - If comment is already exist and we remove the comments, sql query not
   generated in the SQL tab while it is generating in pgAdmin3.

*Question*: With current implementation in "pgAdmin3" to create "Cast" user
will have to select source type and target type and then click on OK
button. If source and target type is not physically compatible, server will
throw an error. I am not sure, but instead of that can we implement it like
when user select the source type from combo box, target type combo will
only show types which are physically compatible?



On Thu, Feb 4, 2016 at 6:31 PM, Sanket Mehta <[email protected]>
wrote:

> Hi Akshay,
>
> PFA the latest patch for Cast module.
> Please do review it and let me know if anything is missing.
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Wed, Jan 20, 2016 at 5:03 PM, Sanket Mehta <
> [email protected]> wrote:
>
>> Hi Neel.
>>
>> PFA the revised patch which has changed according to your comments.
>> Please do review it and let me know in case anything is missing.
>>
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <[email protected]
>> > wrote:
>>
>>> Hi Sanket,
>>>
>>> Below are the review comments.
>>>
>>> - When we edit any existing cast node then it gives error "*Response
>>> object has no attribute strip*". This error is coming because generated
>>> SQL is
>>>   wrong.
>>> - Unnecessary debug logs are coming on console. Please remove
>>> unnecessary debug logs.
>>> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used. Please
>>> check all the SQL files.
>>> - "Delete" cast functionality is not working. Error is getting displayed
>>> saying *"syntax error at or near "castsource"*.
>>> - "Delete cascade" functionality is not working - error is getting
>>> displayed saying *"The requested URL not found".*
>>> - Do the proper comments, in some of the function like "script_load" ,
>>> comments are wrong.
>>> - Is "configs" really required in __init__.py file ? We have not seen
>>> any usage for this. Please remove it if it is not required.
>>> - Remove commented code from the source file.
>>>
>>> Please check all the generated SQL statements . Test the basic
>>> functionality of "create", "Edit" and "Delete" node before sending patch
>>> file.
>>>
>>> Do let us know for any comments/issues.
>>>
>>> Thanks,
>>> Neel Patel
>>>
>>> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
>>> [email protected]> wrote:
>>>
>>>> Hi,
>>>>
>>>> PFA updated patch for cast module as per check list provided by Neel.
>>>> Please do review it and let me know in case of anything is missing.
>>>>
>>>>
>>>>
>>>> Regards,
>>>> Sanket Mehta
>>>> Sr Software engineer
>>>> Enterprisedb
>>>>
>>>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> PFA patch for cast module.
>>>>> Please do review it and let me know in case of any issue.
>>>>>
>>>>>
>>>>> Regards,
>>>>> Sanket Mehta
>>>>> Sr Software engineer
>>>>> Enterprisedb
>>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> Sent via pgadmin-hackers mailing list ([email protected])
>>>> To make changes to your subscription:
>>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>>
>>>>
>>>
>>
>
>
> --
> 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*


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-08 10:15  Sanket Mehta <[email protected]>
  parent: Akshay Joshi <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-08 10:15 UTC (permalink / raw)
  To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers

Hi Akshay,

PFA the revised patch.
All the comments are inline.


Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Fri, Feb 5, 2016 at 12:43 PM, Akshay Joshi <[email protected]
> wrote:

> Hi Sanket
>
> Below are the review comments
>
>    - As "Show System Object" is not implemented yet, we should show all
>    the objects by default.
>
> Done

>
>    - As in pgAdmin3 when click on Casts (Collection) node it should show
>    only Name, Owner and Comments. With current code it is showing all the
>    properties.
>
> Done.. Owner field is ignore as it is not a part of cast properties.

>
>    - Properties Tab contains only one control "Comment" can that be a
>    part of the Definition tab???
>    - For some data type like "Character", "Integer", it is throwing error
>    that data type doesn't exist.
>
> resolved

>
>    - If node is leaf node then it should not show (+) expand symbol.
>
> Done

>
>    - Remove extra lines from create.sql and update.sql files as it shown
>    in the SQL tab as well.
>
> Ignored as it was suggested by Ashesh.

>
>    - When select any system cast it is not showing function in the
>    function control.
>
> Resolved.

>
>    - If comment is already exist and we remove the comments, sql query
>    not generated in the SQL tab while it is generating in pgAdmin3.
>
> Done.


> *Question*: With current implementation in "pgAdmin3" to create "Cast"
> user will have to select source type and target type and then click on OK
> button. If source and target type is not physically compatible, server will
> throw an error. I am not sure, but instead of that can we implement it like
> when user select the source type from combo box, target type combo will
> only show types which are physically compatible?
>
After consulting with db server team, it is clear that they do not maintain
any mapping for compatible source and target types. in postgresql, they
pick selected source and target type and check them for compatibility. So
its not possible to filter out target type based on selected source type.

>
>
>
> On Thu, Feb 4, 2016 at 6:31 PM, Sanket Mehta <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> PFA the latest patch for Cast module.
>> Please do review it and let me know if anything is missing.
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Wed, Jan 20, 2016 at 5:03 PM, Sanket Mehta <
>> [email protected]> wrote:
>>
>>> Hi Neel.
>>>
>>> PFA the revised patch which has changed according to your comments.
>>> Please do review it and let me know in case anything is missing.
>>>
>>>
>>>
>>> Regards,
>>> Sanket Mehta
>>> Sr Software engineer
>>> Enterprisedb
>>>
>>> On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <
>>> [email protected]> wrote:
>>>
>>>> Hi Sanket,
>>>>
>>>> Below are the review comments.
>>>>
>>>> - When we edit any existing cast node then it gives error "*Response
>>>> object has no attribute strip*". This error is coming because
>>>> generated SQL is
>>>>   wrong.
>>>> - Unnecessary debug logs are coming on console. Please remove
>>>> unnecessary debug logs.
>>>> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used.
>>>> Please check all the SQL files.
>>>> - "Delete" cast functionality is not working. Error is getting
>>>> displayed saying *"syntax error at or near "castsource"*.
>>>> - "Delete cascade" functionality is not working - error is getting
>>>> displayed saying *"The requested URL not found".*
>>>> - Do the proper comments, in some of the function like "script_load" ,
>>>> comments are wrong.
>>>> - Is "configs" really required in __init__.py file ? We have not seen
>>>> any usage for this. Please remove it if it is not required.
>>>> - Remove commented code from the source file.
>>>>
>>>> Please check all the generated SQL statements . Test the basic
>>>> functionality of "create", "Edit" and "Delete" node before sending patch
>>>> file.
>>>>
>>>> Do let us know for any comments/issues.
>>>>
>>>> Thanks,
>>>> Neel Patel
>>>>
>>>> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> PFA updated patch for cast module as per check list provided by Neel.
>>>>> Please do review it and let me know in case of anything is missing.
>>>>>
>>>>>
>>>>>
>>>>> Regards,
>>>>> Sanket Mehta
>>>>> Sr Software engineer
>>>>> Enterprisedb
>>>>>
>>>>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> PFA patch for cast module.
>>>>>> Please do review it and let me know in case of any issue.
>>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Sanket Mehta
>>>>>> Sr Software engineer
>>>>>> Enterprisedb
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Sent via pgadmin-hackers mailing list ([email protected])
>>>>> To make changes to your subscription:
>>>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>>>
>>>>>
>>>>
>>>
>>
>>
>> --
>> 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*
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv6.patch (34.1K, 3-castv6.patch)
  download | inline diff:
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..2cec9e9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,445 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+from html import escape
+
+
+class CastModule(CollectionNodeModule):
+    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
+        """
+        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(NodeView):
+    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_sourceTarget_type'}, {'get': 'get_sourceTarget_type'}],
+        'getfunctions': [{'post': 'get_functions'}, {'post': 'get_functions'}]
+    })
+
+    def module_js(self):
+        """
+        This property defines (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        return make_response(
+                render_template(
+                    "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 >= 90000:
+                self.template_path = 'cast/sql/9.0_plus'
+
+            return f(*args, **kwargs)
+
+        return wrap
+
+
+    @check_precondition
+    def list(self, gid, sid, did):
+        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):
+        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):
+        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]
+        result['castcontext'] = True if result['castcontext'] == 'IMPLICIT' else False
+
+        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
+        """
+
+        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
+        """
+        data = request.form if request.form else json.loads(request.data.decode())
+        SQL = self.getSQL(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
+        """
+        # 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
+        """
+        data = request.args
+        SQL = self.getSQL(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 getSQL(self, gid, sid, did, data, cid=None):
+        """
+        This function will return SQL for model data
+        """
+        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 SQL
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+    @check_precondition
+    def get_functions(self, gid, sid, did, cid=None):
+        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)
+
+        # TODO: add schemaprefix to proname before adding it to value in res
+        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_sourceTarget_type(self, gid, sid, did, cid=None):
+        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']:
+            # TODO: Follow dlgTypeProperty::FillDataType() function before adding typename to res
+            res.append({
+                'label': row['typname'],
+                'value': escape(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
+        """
+        try:
+            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]
+            result['castcontext'] = False if result['castcontext'] == 'EXPLICIT' else True
+            SQL = render_template("/".join([self.template_path, 'create.sql']), data=result, conn=self.conn, is_sql=True)
+            return ajax_response(response=SQL)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+CastView.register_node_view(blueprint)
\ No newline at end of file
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..f0cdd21
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,213 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    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']
+        });
+    };
+
+    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,
+        Init: function() {
+          /* Avoid mulitple registration of menus */
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          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'}
+          }]);
+
+        },
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,
+            encoding: 'UTF8',
+            srctyp: undefined,
+            trgtyp: undefined,
+            proname: undefined,
+            castcontext: undefined,
+            syscast: undefined,
+            description: undefined
+          },
+          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()
+            },
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+               onChange: function() {
+                 Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                    this, arguments
+                    );
+                 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");
+               }
+            })
+          },{
+            id: 'trgtyp', label:'{{ _('Target type') }}', url: 'get_type',
+            type: 'text', group: 'Definition', disabled: function(m) {
+              return !m.isNew()
+              },
+            transform: function(rows) {
+              _.each(rows, function(r) {
+                r['image'] = 'icon-cast';
+              });
+              return rows;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+             onChange: function() {
+               Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                 this, arguments
+                 );
+               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");
+             }
+             })
+          },{
+            id: 'proname', label:'{{ _('Function') }}', deps:['srctyp', 'trgtyp'],
+            type: 'text', disabled: function(m) { return !m.isNew(); },
+            group: 'Definition',
+            control: 'node-ajax-options',
+            options: function() {
+
+              var srcTyp = this.model.get('srctyp');
+              var trgtyp = this.model.get('trgtyp');
+              var res = [];
+              /*  On srctyp and trgtyp state change event an ajax call is made to
+              *   fetch list of related functions
+              */
+              if(srcTyp != undefined && srcTyp != '' &&
+                 trgtyp != undefined && trgtyp != '')
+              {
+                 var node = this.field.get('schema_node'),
+                 _url = node.generate_url.apply(
+                 node, [
+                   null, 'getfunctions', 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},
+                 success: function(result) {
+                   res = result.data;
+                   return res;
+                 },
+                 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;
+          }
+        },{
+          id: 'castcontext', label:'{{ _('Context') }}', options:{'onText':'IMPLICIT','offText':'EXPLICIT'},
+          editable: false, type: 'switch', disabled: function(m) { return !m.isNew(); },
+          group: 'Definition'
+        },{
+          id: 'syscast', label:'{{ _('System Cast?') }}', mode: ['properties'],
+          editable: false, type: 'text'
+        },{
+          id: 'description', label:'{{ _('Comment') }}',type: 'text', group: 'Properties',
+          type: 'multiline', cellHeaderClasses: 'width_percent_50'
+        }
+        ],
+        validate: function(keys){
+        /*
+            * Triggers specific error messages for srctyp and
+            * trgtyp if any one of them is not selected
+        */
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+          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');
+          }
+
+          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.0_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
new file mode 100644
index 0000000..501aa1a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/create.sql
@@ -0,0 +1,21 @@
+
+
+{# CREATE CAST Statement #}
+{% if is_sql %}
+-- 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 == True or data.castcontext == 'true' %}
+
+    AS IMPLICIT{% 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.0_plus/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/delete.sql
new file mode 100644
index 0000000..1b8d8b6
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_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.0_plus/functions.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
new file mode 100644
index 0000000..88c0e4b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/functions.sql
@@ -0,0 +1,18 @@
+{# FETCH FUNCTIONS depending upon SOURCE TYPE and TARGET TYPE IN CAST  #}
+SELECT
+  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.0_plus/getsrcandtrgttype.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
new file mode 100644
index 0000000..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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.0_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
new file mode 100644
index 0000000..dd2d38d
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_plus/properties.sql
@@ -0,0 +1,61 @@
+{# 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
+  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.0_plus/update.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.0_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.0_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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-11 06:39  Akshay Joshi <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Akshay Joshi @ 2016-02-11 06:39 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Hi Sanket

Most of the review comments has been resolved but I found some issues with
this patch

   - When select some system cast it is showing wrong query, for example
   when select "abstime->date" in pgAdmin3 it is showing "AS ASSIGNMENT"
   and with your code it is showing "AS IMPLICIT".
   - For some of the system casts SQL query showing "WITH FUNCTION date"
   instead of "WITH FUNCTION date(abstime)" source type is not appended in
   query with new code.
   - For casts "bit->"bit"" function and target type is not listed.
   - When we create a new cast like "character->bytea" without selecting
   function, it creates successfully but when we select the newly created cast
   it shows the function "bpcharsend" in functions property. It may come
   for other combinations too, please verify.
   - Please fixed warnings in python file by using pep8 tool.


On Mon, Feb 8, 2016 at 3:45 PM, Sanket Mehta <[email protected]>
wrote:

> Hi Akshay,
>
> PFA the revised patch.
> All the comments are inline.
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Fri, Feb 5, 2016 at 12:43 PM, Akshay Joshi <
> [email protected]> wrote:
>
>> Hi Sanket
>>
>> Below are the review comments
>>
>>    - As "Show System Object" is not implemented yet, we should show all
>>    the objects by default.
>>
>> Done
>
>>
>>    - As in pgAdmin3 when click on Casts (Collection) node it should show
>>    only Name, Owner and Comments. With current code it is showing all the
>>    properties.
>>
>> Done.. Owner field is ignore as it is not a part of cast properties.
>
>>
>>    - Properties Tab contains only one control "Comment" can that be a
>>    part of the Definition tab???
>>    - For some data type like "Character", "Integer", it is throwing
>>    error that data type doesn't exist.
>>
>> resolved
>
>>
>>    - If node is leaf node then it should not show (+) expand symbol.
>>
>> Done
>
>>
>>    - Remove extra lines from create.sql and update.sql files as it shown
>>    in the SQL tab as well.
>>
>> Ignored as it was suggested by Ashesh.
>
>>
>>    - When select any system cast it is not showing function in the
>>    function control.
>>
>> Resolved.
>
>>
>>    - If comment is already exist and we remove the comments, sql query
>>    not generated in the SQL tab while it is generating in pgAdmin3.
>>
>> Done.
>
>
>> *Question*: With current implementation in "pgAdmin3" to create "Cast"
>> user will have to select source type and target type and then click on OK
>> button. If source and target type is not physically compatible, server will
>> throw an error. I am not sure, but instead of that can we implement it like
>> when user select the source type from combo box, target type combo will
>> only show types which are physically compatible?
>>
> After consulting with db server team, it is clear that they do not
> maintain any mapping for compatible source and target types. in postgresql,
> they pick selected source and target type and check them for compatibility.
> So its not possible to filter out target type based on selected source type.
>
>>
>>
>>
>> On Thu, Feb 4, 2016 at 6:31 PM, Sanket Mehta <
>> [email protected]> wrote:
>>
>>> Hi Akshay,
>>>
>>> PFA the latest patch for Cast module.
>>> Please do review it and let me know if anything is missing.
>>>
>>>
>>> Regards,
>>> Sanket Mehta
>>> Sr Software engineer
>>> Enterprisedb
>>>
>>> On Wed, Jan 20, 2016 at 5:03 PM, Sanket Mehta <
>>> [email protected]> wrote:
>>>
>>>> Hi Neel.
>>>>
>>>> PFA the revised patch which has changed according to your comments.
>>>> Please do review it and let me know in case anything is missing.
>>>>
>>>>
>>>>
>>>> Regards,
>>>> Sanket Mehta
>>>> Sr Software engineer
>>>> Enterprisedb
>>>>
>>>> On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Sanket,
>>>>>
>>>>> Below are the review comments.
>>>>>
>>>>> - When we edit any existing cast node then it gives error "*Response
>>>>> object has no attribute strip*". This error is coming because
>>>>> generated SQL is
>>>>>   wrong.
>>>>> - Unnecessary debug logs are coming on console. Please remove
>>>>> unnecessary debug logs.
>>>>> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used.
>>>>> Please check all the SQL files.
>>>>> - "Delete" cast functionality is not working. Error is getting
>>>>> displayed saying *"syntax error at or near "castsource"*.
>>>>> - "Delete cascade" functionality is not working - error is getting
>>>>> displayed saying *"The requested URL not found".*
>>>>> - Do the proper comments, in some of the function like "script_load" ,
>>>>> comments are wrong.
>>>>> - Is "configs" really required in __init__.py file ? We have not seen
>>>>> any usage for this. Please remove it if it is not required.
>>>>> - Remove commented code from the source file.
>>>>>
>>>>> Please check all the generated SQL statements . Test the basic
>>>>> functionality of "create", "Edit" and "Delete" node before sending patch
>>>>> file.
>>>>>
>>>>> Do let us know for any comments/issues.
>>>>>
>>>>> Thanks,
>>>>> Neel Patel
>>>>>
>>>>> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> PFA updated patch for cast module as per check list provided by Neel.
>>>>>> Please do review it and let me know in case of anything is missing.
>>>>>>
>>>>>>
>>>>>>
>>>>>> Regards,
>>>>>> Sanket Mehta
>>>>>> Sr Software engineer
>>>>>> Enterprisedb
>>>>>>
>>>>>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> PFA patch for cast module.
>>>>>>> Please do review it and let me know in case of any issue.
>>>>>>>
>>>>>>>
>>>>>>> Regards,
>>>>>>> Sanket Mehta
>>>>>>> Sr Software engineer
>>>>>>> Enterprisedb
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Sent via pgadmin-hackers mailing list ([email protected]
>>>>>> )
>>>>>> To make changes to your subscription:
>>>>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> 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*


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-12 11:57  Sanket Mehta <[email protected]>
  parent: Akshay Joshi <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-12 11:57 UTC (permalink / raw)
  To: pgadmin-hackers

Hi Akshay,

PFA the updated patch.

Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Thu, Feb 11, 2016 at 12:09 PM, Akshay Joshi <
[email protected]> wrote:

> Hi Sanket
>
> Most of the review comments has been resolved but I found some issues with
> this patch
>
>    - When select some system cast it is showing wrong query, for example
>    when select "abstime->date" in pgAdmin3 it is showing "AS ASSIGNMENT"
>    and with your code it is showing "AS IMPLICIT".
>    - For some of the system casts SQL query showing "WITH FUNCTION date"
>    instead of "WITH FUNCTION date(abstime)" source type is not appended
>    in query with new code.
>    - For casts "bit->"bit"" function and target type is not listed.
>    - When we create a new cast like "character->bytea" without selecting
>    function, it creates successfully but when we select the newly created cast
>    it shows the function "bpcharsend" in functions property. It may come
>    for other combinations too, please verify.
>    - Please fixed warnings in python file by using pep8 tool.
>
>
> On Mon, Feb 8, 2016 at 3:45 PM, Sanket Mehta <sanket.mehta@enterprisedb
> .com> wrote:
>
>> Hi Akshay,
>>
>> PFA the revised patch.
>> All the comments are inline.
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Fri, Feb 5, 2016 at 12:43 PM, Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Sanket
>>>
>>> Below are the review comments
>>>
>>>    - As "Show System Object" is not implemented yet, we should show all
>>>    the objects by default.
>>>
>>> Done
>>
>>>
>>>    - As in pgAdmin3 when click on Casts (Collection) node it should
>>>    show only Name, Owner and Comments. With current code it is showing all the
>>>    properties.
>>>
>>> Done.. Owner field is ignore as it is not a part of cast properties.
>>
>>>
>>>    - Properties Tab contains only one control "Comment" can that be a
>>>    part of the Definition tab???
>>>    - For some data type like "Character", "Integer", it is throwing
>>>    error that data type doesn't exist.
>>>
>>> resolved
>>
>>>
>>>    - If node is leaf node then it should not show (+) expand symbol.
>>>
>>> Done
>>
>>>
>>>    - Remove extra lines from create.sql and update.sql files as it
>>>    shown in the SQL tab as well.
>>>
>>> Ignored as it was suggested by Ashesh.
>>
>>>
>>>    - When select any system cast it is not showing function in the
>>>    function control.
>>>
>>> Resolved.
>>
>>>
>>>    - If comment is already exist and we remove the comments, sql query
>>>    not generated in the SQL tab while it is generating in pgAdmin3.
>>>
>>> Done.
>>
>>
>>> *Question*: With current implementation in "pgAdmin3" to create "Cast"
>>> user will have to select source type and target type and then click on OK
>>> button. If source and target type is not physically compatible, server will
>>> throw an error. I am not sure, but instead of that can we implement it like
>>> when user select the source type from combo box, target type combo will
>>> only show types which are physically compatible?
>>>
>> After consulting with db server team, it is clear that they do not
>> maintain any mapping for compatible source and target types. in postgresql,
>> they pick selected source and target type and check them for compatibility.
>> So its not possible to filter out target type based on selected source type.
>>
>>>
>>>
>>>
>>> On Thu, Feb 4, 2016 at 6:31 PM, Sanket Mehta <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>>
>>>> PFA the latest patch for Cast module.
>>>> Please do review it and let me know if anything is missing.
>>>>
>>>>
>>>> Regards,
>>>> Sanket Mehta
>>>> Sr Software engineer
>>>> Enterprisedb
>>>>
>>>> On Wed, Jan 20, 2016 at 5:03 PM, Sanket Mehta <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Neel.
>>>>>
>>>>> PFA the revised patch which has changed according to your comments.
>>>>> Please do review it and let me know in case anything is missing.
>>>>>
>>>>>
>>>>>
>>>>> Regards,
>>>>> Sanket Mehta
>>>>> Sr Software engineer
>>>>> Enterprisedb
>>>>>
>>>>> On Wed, Jan 20, 2016 at 10:20 AM, Neel Patel <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Sanket,
>>>>>>
>>>>>> Below are the review comments.
>>>>>>
>>>>>> - When we edit any existing cast node then it gives error "*Response
>>>>>> object has no attribute strip*". This error is coming because
>>>>>> generated SQL is
>>>>>>   wrong.
>>>>>> - Unnecessary debug logs are coming on console. Please remove
>>>>>> unnecessary debug logs.
>>>>>> - In some of the sql file, 'qtIdent' and 'qtLiteral' is not used.
>>>>>> Please check all the SQL files.
>>>>>> - "Delete" cast functionality is not working. Error is getting
>>>>>> displayed saying *"syntax error at or near "castsource"*.
>>>>>> - "Delete cascade" functionality is not working - error is getting
>>>>>> displayed saying *"The requested URL not found".*
>>>>>> - Do the proper comments, in some of the function like "script_load"
>>>>>> , comments are wrong.
>>>>>> - Is "configs" really required in __init__.py file ? We have not seen
>>>>>> any usage for this. Please remove it if it is not required.
>>>>>> - Remove commented code from the source file.
>>>>>>
>>>>>> Please check all the generated SQL statements . Test the basic
>>>>>> functionality of "create", "Edit" and "Delete" node before sending patch
>>>>>> file.
>>>>>>
>>>>>> Do let us know for any comments/issues.
>>>>>>
>>>>>> Thanks,
>>>>>> Neel Patel
>>>>>>
>>>>>> On Tue, Jan 19, 2016 at 8:06 PM, Sanket Mehta <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> PFA updated patch for cast module as per check list provided by Neel.
>>>>>>> Please do review it and let me know in case of anything is missing.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Regards,
>>>>>>> Sanket Mehta
>>>>>>> Sr Software engineer
>>>>>>> Enterprisedb
>>>>>>>
>>>>>>> On Mon, Jan 18, 2016 at 7:16 PM, Sanket Mehta <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> PFA patch for cast module.
>>>>>>>> Please do review it and let me know in case of any issue.
>>>>>>>>
>>>>>>>>
>>>>>>>> Regards,
>>>>>>>> Sanket Mehta
>>>>>>>> Sr Software engineer
>>>>>>>> Enterprisedb
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Sent via pgadmin-hackers mailing list (
>>>>>>> [email protected])
>>>>>>> To make changes to your subscription:
>>>>>>> http://www.postgresql.org/mailpref/pgadmin-hackers
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> 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:

  [text/x-patch] castv7.patch (38.5K, 3-castv7.patch)
  download | inline diff:
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..76696e0
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,497 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+from html import escape
+
+
+class CastModule(CollectionNodeModule):
+    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(NodeView):
+    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 (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        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):
+        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):
+        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):
+        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
+        """
+
+        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
+        """
+        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
+        """
+        # 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
+        """
+        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
+        """
+        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 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
+        """
+        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
+        """
+        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': escape(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
+        """
+        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 role/user!")
+                    )
+
+            return ajax_response(response=res)
+
+        except Exception as e:
+            return internal_server_error(errormsg=str(e))
+
+
+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..3095fa5
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,238 @@
+define(
+        ['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
+function($, _, S, pgAdmin, pgBrowser, alertify) {
+
+    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']
+        });
+    };
+
+    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,
+        Init: function() {
+          /* Avoid mulitple registration of menus */
+          if (this.initialized)
+            return;
+
+          this.initialized = true;
+
+          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'}
+          }]);
+
+        },
+        model: pgAdmin.Browser.Node.Model.extend({
+          defaults: {
+            name: undefined,
+            encoding: 'UTF8',
+            srctyp: undefined,
+            trgtyp: undefined,
+            proname: undefined,
+            castcontext: undefined,
+            syscast: undefined,
+            description: undefined
+          },
+          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;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+               onChange: function() {
+                 Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                    this, arguments
+                    );
+                 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");
+               }
+            })
+          },{
+            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;
+            },
+            /*  As name is being generated from srctyp and trgtyp, a check has been put in
+             *  control field if both are changed or not and depending upon it, name has been set.
+             */
+             control: Backform.NodeAjaxOptionsControl.extend({
+             onChange: function() {
+               Backform.NodeAjaxOptionsControl.prototype.onChange.apply(
+                 this, arguments
+                 );
+               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");
+             }
+             })
+          },{
+            id: 'trgtyp', label:'{{ _('Target type') }}', type: 'text',
+            group: 'Definition', disabled: true, mode:['properties','edit']
+          },{
+            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 = [];
+              /*  On srctyp and trgtyp state change event an ajax call is made to
+              *   fetch list of related functions
+              */
+              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},
+                 success: function(result) {
+                   res = result.data;
+                   return res;
+                 },
+                 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;
+          }
+        },{
+            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';
+            }
+          })
+        },{
+          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'
+        }
+        ],
+        validate: function(keys){
+        /*
+            * Triggers specific error messages for srctyp and
+            * trgtyp if any one of them is not selected
+        */
+          var srctype = this.get('srctyp');
+          var trgtype = this.get('trgtyp');
+          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');
+          }
+
+          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..70e8a18
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/create.sql
@@ -0,0 +1,22 @@
+
+
+{# 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..1b8d8b6
--- /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..bc6aca9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql
@@ -0,0 +1,18 @@
+{# 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..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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..3b4c330
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
@@ -0,0 +1,61 @@
+{# 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..29c28a7
--- /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, st.typtypmod)|| E' -> ' ||
+   format_type(tt.oid, tt.typtypmod) ||
+   E'\n\n-- DROP CAST (' || format_type(st.oid, st.typtypmod) ||
+   E' AS ' || format_type(tt.oid,tt.typtypmod) ||
+   E');\n\n  CREATE CAST (' || format_type(st.oid, st.typtypmod) ||
+   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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-12 16:24  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-12 16:24 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

On Fri, Feb 12, 2016 at 11:57 AM, Sanket Mehta <
[email protected]> wrote:

> Hi Akshay,
>
> PFA the updated patch.
>
>
Hi

When testing this patch, I get the following error:

Traceback (most recent call last):
  File "pgAdmin4.py", line 56, in <module>
    app = create_app()
  File "/Users/dpage/git/pgadmin4/web/pgadmin/__init__.py", line 194, in
create_app
    app.register_blueprint(module)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 62, in wrapper_func
    return f(self, *args, **kwargs)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 889, in register_blueprint
    blueprint.register(self, options, first_registration)
  File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
in register
    app.register_blueprint(module)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 62, in wrapper_func
    return f(self, *args, **kwargs)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 889, in register_blueprint
    blueprint.register(self, options, first_registration)
  File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
in register
    app.register_blueprint(module)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 62, in wrapper_func
    return f(self, *args, **kwargs)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 889, in register_blueprint
    blueprint.register(self, options, first_registration)
  File
"/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/__init__.py",
line 147, in register
    super(ServerModule, self).register(app, options, first_registration)
  File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
in register
    app.register_blueprint(module)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 62, in wrapper_func
    return f(self, *args, **kwargs)
  File
"/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
line 889, in register_blueprint
    blueprint.register(self, options, first_registration)
  File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 37,
in register
    self.submodules = list(app.find_submodules(self.import_name))
  File "/Users/dpage/git/pgadmin4/web/pgadmin/__init__.py", line 42, in
find_submodules
    module = import_module(module_name)
  File
"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py",
line 37, in import_module
    __import__(name)
  File
"/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py",
line 21, in <module>
    from html import escape
ImportError: No module named html

Other comments:

- Leave a blank line before the header block and the first line of Python
code please.
- There is no pydoc description for the module.
- The javascript is almost completely devoid of comments. There should be
at least a minimal amount to explain what each code block is doing.
- There should be no blank lines at the top of SQL templates (e.g.
create.sql).

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-15 08:10  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-15 08:10 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

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?


Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Fri, Feb 12, 2016 at 9:54 PM, Dave Page <[email protected]> wrote:

>
>
> On Fri, Feb 12, 2016 at 11:57 AM, Sanket Mehta <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> PFA the updated patch.
>>
>>
> Hi
>
> When testing this patch, I get the following error:
>
> Traceback (most recent call last):
>   File "pgAdmin4.py", line 56, in <module>
>     app = create_app()
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/__init__.py", line 194, in
> create_app
>     app.register_blueprint(module)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 62, in wrapper_func
>     return f(self, *args, **kwargs)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 889, in register_blueprint
>     blueprint.register(self, options, first_registration)
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
> in register
>     app.register_blueprint(module)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 62, in wrapper_func
>     return f(self, *args, **kwargs)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 889, in register_blueprint
>     blueprint.register(self, options, first_registration)
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
> in register
>     app.register_blueprint(module)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 62, in wrapper_func
>     return f(self, *args, **kwargs)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 889, in register_blueprint
>     blueprint.register(self, options, first_registration)
>   File
> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/__init__.py",
> line 147, in register
>     super(ServerModule, self).register(app, options, first_registration)
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 40,
> in register
>     app.register_blueprint(module)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 62, in wrapper_func
>     return f(self, *args, **kwargs)
>   File
> "/Users/dpage/.virtualenvs/pgadmin4/lib/python2.7/site-packages/flask/app.py",
> line 889, in register_blueprint
>     blueprint.register(self, options, first_registration)
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/utils/__init__.py", line 37,
> in register
>     self.submodules = list(app.find_submodules(self.import_name))
>   File "/Users/dpage/git/pgadmin4/web/pgadmin/__init__.py", line 42, in
> find_submodules
>     module = import_module(module_name)
>   File
> "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/importlib/__init__.py",
> line 37, in import_module
>     __import__(name)
>   File
> "/Users/dpage/git/pgadmin4/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py",
> line 21, in <module>
>     from html import escape
> ImportError: No module named html
>
> Other comments:
>
> - Leave a blank line before the header block and the first line of Python
> code please.
> - There is no pydoc description for the module.
> - The javascript is almost completely devoid of comments. There should be
> at least a minimal amount to explain what each code block is doing.
> - There should be no blank lines at the top of SQL templates (e.g.
> create.sql).
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-15 10:48  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-15 10:48 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <[email protected]
> 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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-15 13:28  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-15 13:28 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

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 <[email protected]> wrote:

>
>
> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
> [email protected]> 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
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv8.patch (40.0K, 3-castv8.patch)
  download | inline diff:
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..96caffc
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,498 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Defines views for management of 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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+
+class CastModule(CollectionNodeModule):
+    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(NodeView):
+    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 (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        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):
+        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):
+        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):
+        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
+        """
+
+        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
+        """
+        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
+        """
+        # 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
+        """
+        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
+        """
+        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 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
+        """
+        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
+        """
+        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
+        """
+        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))
+
+
+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..61b60aa
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/js/casts.js
@@ -0,0 +1,290 @@
+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,
+        Init: function() {
+
+          /* Avoid mulitple 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..1b8d8b6
--- /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..bc6aca9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql
@@ -0,0 +1,18 @@
+{# 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..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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..3b4c330
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
@@ -0,0 +1,61 @@
+{# 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..cdc52ad
--- /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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-15 16:53  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-15 16:53 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

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 <[email protected]
> 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 <[email protected]> wrote:
>
>>
>>
>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>> [email protected]> 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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-15 16:55  Dave Page <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-15 16:55 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

And this time with the attachment...

On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
> [email protected]> 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 <[email protected]> wrote:
>>
>>>
>>>
>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>> [email protected]> 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


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [image/png] Screen Shot 2016-02-15 at 16.50.06.png (17.8K, 3-Screen%20Shot%202016-02-15%20at%2016.50.06.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-19 11:03  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-19 11:03 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

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 <[email protected]> wrote:

> And this time with the attachment...
>
> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
>> [email protected]> 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 <[email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>> [email protected]> 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
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv9.patch (44.8K, 3-castv9.patch)
  download | inline diff:
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..ae7dbaf
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,646 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2016, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""
+Defines views for management of 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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+from pgadmin.browser.server_groups.servers.depends import get_dependencies, get_dependents
+
+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(NodeView):
+    """
+    class CastView(NodeView)
+
+        A view class for cast node derived from NodeView. 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.
+    """
+
+    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 (if javascript) exists for this node.
+        Override this property for your own logic.
+        """
+        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 = get_dependents(self.conn, cid, 'language')
+        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 = get_dependencies(self.conn, cid, 'language')
+        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..1b8d8b6
--- /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..bc6aca9
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/functions.sql
@@ -0,0 +1,18 @@
+{# 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..1e05ccb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/getsrcandtrgttype.sql
@@ -0,0 +1,43 @@
+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..3b4c330
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
@@ -0,0 +1,61 @@
+{# 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..cdc52ad
--- /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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-23 10:40  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-23 10:40 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

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 <
[email protected]> 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 <[email protected]> wrote:
>
>> And this time with the attachment...
>>
>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
>>> [email protected]> 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 <[email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>>> [email protected]> 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


-- 
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] castv9-dave.patch (43.7K, 3-castv9-dave.patch)
  download | inline diff:
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..e397a6e
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -0,0 +1,643 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 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 NodeView
+from pgadmin.browser.collection import CollectionNodeModule
+import pgadmin.browser.server_groups.servers.databases as databases
+from pgadmin.utils.ajax import precondition_required
+from pgadmin.utils.driver import get_driver
+from config import PG_DEFAULT_DRIVER
+from functools import wraps
+
+class 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(NodeView):
+    """
+    class CastView(NodeView)
+
+        A view class for cast node derived from NodeView. 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 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.
+    """
+
+    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 check the
+        database connection before running view. It will also attach
+        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 the 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 nodes within the 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 with 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 SQL for the cast!\n{0}").format(
+                        res
+                        )
+                    )
+
+            if res is None:
+                return gone(
+                    _("ERROR: Couldn't generate reversed engineered SQL 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 gets the dependents and returns an ajax response
+        for the cast node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            cid: Cast ID
+        """
+        dependents_result = get_dependents(self.conn, cid, 'language')
+        return ajax_response(
+                response=dependents_result,
+                status=200
+                )
+
+    @check_precondition
+    def dependencies(self, gid, sid, did, cid):
+        """
+        This function gets the dependencies and returns an ajax response
+        for the cast node.
+
+        Args:
+            gid: Server Group ID
+            sid: Server ID
+            did: Database ID
+            cid: Cast ID
+        """
+        dependencies_result = get_dependencies(self.conn, cid, 'language')
+        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 0000000..2be7f37
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/cast.png differ
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 0000000..09eb65a
Binary files /dev/null and b/web/pgadmin/browser/server_groups/servers/databases/casts/static/img/coll-cast.png differ
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..4354ee3
--- /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'}
+          }]);
+
+        },
+
+        // Define the 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
+          },
+
+          // Define the 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..cef76ca
--- /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..1195167
--- /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..4636f87
--- /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..15efa95
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/properties.sql
@@ -0,0 +1,61 @@
+{# 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..0554ad3
--- /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..e0bd4e8
--- /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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-23 13:34  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-02-23 13:34 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

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 <[email protected]> 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 <
> [email protected]> 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 <[email protected]> wrote:
>>
>>> And this time with the attachment...
>>>
>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
>>>> [email protected]> 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 <[email protected]> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>>>> [email protected]> 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
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] castv10.patch (44.7K, 3-castv10.patch)
  download | inline diff:
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


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-02-24 16:47  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-02-24 16:47 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Thanks - committed.

On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta <[email protected]
> wrote:

> 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 <[email protected]> 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 <
>> [email protected]> 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 <[email protected]> wrote:
>>>
>>>> And this time with the attachment...
>>>>
>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
>>>>> [email protected]> 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 <[email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>>>>> [email protected]> 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
>>
>
>


-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-03-01 07:20  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-03-01 07:20 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

Hi,

There was an error in cast module while fetching its dependency and
dependent.
Below is the patch which resolves this issue.
Please review and commit it.



Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Wed, Feb 24, 2016 at 10:17 PM, Dave Page <[email protected]> wrote:

> Thanks - committed.
>
> On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta <
> [email protected]> wrote:
>
>> 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 <[email protected]> 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 <
>>> [email protected]> 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 <[email protected]> wrote:
>>>>
>>>>> And this time with the attachment...
>>>>>
>>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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 <
>>>>>> [email protected]> 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 <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta <
>>>>>>>> [email protected]> 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
>>>
>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] cast_error_dependecy_dependent.patch (2.1K, 3-cast_error_dependecy_dependent.patch)
  download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
index e397a6e..cdec278 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -14,7 +14,7 @@ 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 NodeView
+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
@@ -78,11 +78,11 @@ class CastModule(CollectionNodeModule):
 blueprint = CastModule(__name__)
 
 
-class CastView(NodeView):
+class CastView(PGChildNodeView):
     """
-    class CastView(NodeView)
+    class CastView(PGChildNodeView)
 
-        A view class for cast node derived from NodeView. This class is
+        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.
 
@@ -614,7 +614,7 @@ class CastView(NodeView):
             did: Database ID
             cid: Cast ID
         """
-        dependents_result = get_dependents(self.conn, cid, 'language')
+        dependents_result = self.get_dependents(self.conn, cid, 'cast')
         return ajax_response(
                 response=dependents_result,
                 status=200
@@ -632,7 +632,7 @@ class CastView(NodeView):
             did: Database ID
             cid: Cast ID
         """
-        dependencies_result = get_dependencies(self.conn, cid, 'language')
+        dependencies_result = self.get_dependencies(self.conn, cid, 'cast')
         return ajax_response(
                 response=dependencies_result,
                 status=200


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-03-04 11:03  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Dave Page @ 2016-03-04 11:03 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Thanks, patch applied.

On Tue, Mar 1, 2016 at 7:20 AM, Sanket Mehta
<[email protected]> wrote:
> Hi,
>
> There was an error in cast module while fetching its dependency and
> dependent.
> Below is the patch which resolves this issue.
> Please review and commit it.
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Wed, Feb 24, 2016 at 10:17 PM, Dave Page <[email protected]> wrote:
>>
>> Thanks - committed.
>>
>> On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta
>> <[email protected]> wrote:
>>>
>>> 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 <[email protected]> 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
>>>> <[email protected]> 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 <[email protected]> wrote:
>>>>>>
>>>>>> And this time with the attachment...
>>>>>>
>>>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]> 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
>>>>>>> <[email protected]> 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 <[email protected]>
>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta
>>>>>>>>> <[email protected]> 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
>>>
>>>
>>
>>
>>
>> --
>> 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


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers



^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-03-16 07:13  Sanket Mehta <[email protected]>
  parent: Dave Page <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-03-16 07:13 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

Hi Dave,

PFA the patch for cast module incorporating changes regarding showing
system objects.
Apart from support for showing system object I have resolved few bugs in
that, unnecessary code and added nodes.sql file.

Please do review it and if it looks good, please commit.


Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Fri, Mar 4, 2016 at 4:33 PM, Dave Page <[email protected]> wrote:

> Thanks, patch applied.
>
> On Tue, Mar 1, 2016 at 7:20 AM, Sanket Mehta
> <[email protected]> wrote:
> > Hi,
> >
> > There was an error in cast module while fetching its dependency and
> > dependent.
> > Below is the patch which resolves this issue.
> > Please review and commit it.
> >
> >
> >
> > Regards,
> > Sanket Mehta
> > Sr Software engineer
> > Enterprisedb
> >
> > On Wed, Feb 24, 2016 at 10:17 PM, Dave Page <[email protected]> wrote:
> >>
> >> Thanks - committed.
> >>
> >> On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta
> >> <[email protected]> wrote:
> >>>
> >>> 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 <[email protected]> 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
> >>>> <[email protected]> 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 <[email protected]>
> wrote:
> >>>>>>
> >>>>>> And this time with the attachment...
> >>>>>>
> >>>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]>
> 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
> >>>>>>> <[email protected]> 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 <[email protected]>
> >>>>>>>> wrote:
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta
> >>>>>>>>> <[email protected]> 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
> >>>
> >>>
> >>
> >>
> >>
> >> --
> >> 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
>


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/x-patch] cast_support_for_system_obj.patch (8.2K, 3-cast_support_for_system_obj.patch)
  download | inline diff:
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
index cdec278..88477bb 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/__init__.py
@@ -225,7 +225,8 @@ class CastView(PGChildNodeView):
         """
         sql = render_template(
             "/".join([self.template_path, 'properties.sql']),
-            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
+            showsysobj=self.blueprint.show_system_objects
         )
         status, res = self.conn.execute_dict(sql)
 
@@ -252,15 +253,15 @@ class CastView(PGChildNodeView):
         """
         res = []
         sql = render_template(
-            "/".join([self.template_path, 'properties.sql']),
-            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+            "/".join([self.template_path, 'nodes.sql']),
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
+            showsysobj=self.blueprint.show_system_objects
         )
         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'],
@@ -275,6 +276,31 @@ class CastView(PGChildNodeView):
         )
 
     @check_precondition
+    def node(self, gid, sid, did, cid):
+        res = []
+        sql = render_template(
+            "/".join([self.template_path, 'nodes.sql']),
+            cid=cid
+        )
+        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'],
+                    did,
+                    row['name'],
+                    icon="icon-fts_template"
+                ))
+
+        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
@@ -287,7 +313,8 @@ class CastView(PGChildNodeView):
         sql = render_template(
             "/".join([self.template_path, 'properties.sql']),
             cid=cid,
-            datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+            datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
+            showsysobj=self.blueprint.show_system_objects
         )
         status, res = self.conn.execute_dict(sql)
 
@@ -338,7 +365,8 @@ class CastView(PGChildNodeView):
             sql = render_template("/".join([self.template_path, 'properties.sql']),
                                   srctyp=data['srctyp'],
                                   trgtyp=data['trgtyp'],
-                                  datlastsysoid=self.manager.db_info[did]['datlastsysoid']
+                                  datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
+                                  showsysobj=self.blueprint.show_system_objects
                                   )
             status, cid = self.conn.execute_scalar(sql)
             if not status:
@@ -485,7 +513,8 @@ class CastView(PGChildNodeView):
             if cid is not None:
                 sql = render_template("/".join([self.template_path, 'properties.sql']),
                                       cid=cid,
-                                      datlastsysoid=self.manager.db_info[did]['datlastsysoid'])
+                                      datlastsysoid=self.manager.db_info[did]['datlastsysoid'],
+                                      showsysobj=self.blueprint.show_system_objects)
                 status, res = self.conn.execute_dict(sql)
 
                 if not status:
@@ -614,7 +643,7 @@ class CastView(PGChildNodeView):
             did: Database ID
             cid: Cast ID
         """
-        dependents_result = self.get_dependents(self.conn, cid, 'cast')
+        dependents_result = self.get_dependents(self.conn, cid)
         return ajax_response(
                 response=dependents_result,
                 status=200
@@ -632,7 +661,7 @@ class CastView(PGChildNodeView):
             did: Database ID
             cid: Cast ID
         """
-        dependencies_result = self.get_dependencies(self.conn, cid, 'cast')
+        dependencies_result = self.get_dependencies(self.conn, cid)
         return ajax_response(
                 response=dependencies_result,
                 status=200
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
index cef76ca..edd2444 100644
--- 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
@@ -1,10 +1,5 @@
 {# 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'%}
diff --git a/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/nodes.sql
new file mode 100644
index 0000000..87f331f
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/databases/casts/templates/cast/sql/9.1_plus/nodes.sql
@@ -0,0 +1,23 @@
+    SELECT
+        ca.oid,
+        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 %}
+    {# Check for Show system object #}
+    {% if (not showsysobj) and datlastsysoid %}
+        {% if cid %}
+            AND
+        {% else %}
+            WHERE
+        {% endif %}
+        ca.oid > {{datlastsysoid}}::OID
+    {% endif %}
\ No newline at end of file
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
index 15efa95..255ee23 100644
--- 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
@@ -14,7 +14,7 @@
     SELECT
         ca.oid,
     CASE
-        WHEN {{datlastsysoid}}::OID > ca.oid then 'Yes' ELSE 'No'
+        WHEN {{datlastsysoid}}::OID > ca.oid then True ELSE False
     END AS syscast,
     CASE
         WHEN ca.castcontext = 'a' THEN 'ASSIGNMENT'
@@ -46,9 +46,8 @@
         WHERE ca.oid={{cid}}::int
     {% endif %}
 
---TODO: add check for showSystemObject(). currently assumed as false
-    {#
-    {% if datlastsysoid %}
+    {# Check for Show system object #}
+    {% if (not showsysobj) and datlastsysoid %}
         {% if cid %}
             AND
         {% else %}
@@ -56,6 +55,5 @@
         {% endif %}
         ca.oid > {{datlastsysoid}}::OID
     {% endif %}
-    #}
     ORDER BY st.typname, tt.typname
 {% endif %}
\ No newline at end of file


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-03-16 07:22  Sanket Mehta <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 1 reply; 24+ messages in thread

From: Sanket Mehta @ 2016-03-16 07:22 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

Hi,

I forgot tot mention bugs in previous mail.


1. System cast field was showing Yes even if the cast was not a system
cast, which I have resolved.
2. In Dependent and Dependency method in __init__.py  file, unnecessary
third parameter 'cast' was being passed which I have removed



Regards,
Sanket Mehta
Sr Software engineer
Enterprisedb

On Wed, Mar 16, 2016 at 12:43 PM, Sanket Mehta <
[email protected]> wrote:

> Hi Dave,
>
> PFA the patch for cast module incorporating changes regarding showing
> system objects.
> Apart from support for showing system object I have resolved few bugs in
> that, unnecessary code and added nodes.sql file.
>
> Please do review it and if it looks good, please commit.
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Fri, Mar 4, 2016 at 4:33 PM, Dave Page <[email protected]> wrote:
>
>> Thanks, patch applied.
>>
>> On Tue, Mar 1, 2016 at 7:20 AM, Sanket Mehta
>> <[email protected]> wrote:
>> > Hi,
>> >
>> > There was an error in cast module while fetching its dependency and
>> > dependent.
>> > Below is the patch which resolves this issue.
>> > Please review and commit it.
>> >
>> >
>> >
>> > Regards,
>> > Sanket Mehta
>> > Sr Software engineer
>> > Enterprisedb
>> >
>> > On Wed, Feb 24, 2016 at 10:17 PM, Dave Page <[email protected]> wrote:
>> >>
>> >> Thanks - committed.
>> >>
>> >> On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta
>> >> <[email protected]> wrote:
>> >>>
>> >>> 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 <[email protected]> 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
>> >>>> <[email protected]> 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 <[email protected]>
>> wrote:
>> >>>>>>
>> >>>>>> And this time with the attachment...
>> >>>>>>
>> >>>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]>
>> 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
>> >>>>>>> <[email protected]> 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 <[email protected]>
>> >>>>>>>> wrote:
>> >>>>>>>>>
>> >>>>>>>>>
>> >>>>>>>>>
>> >>>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta
>> >>>>>>>>> <[email protected]> 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
>> >>>
>> >>>
>> >>
>> >>
>> >>
>> >> --
>> >> 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
>>
>
>


^ permalink  raw  reply  [nested|flat] 24+ messages in thread

* Re: patch for cast module
@ 2016-03-17 11:49  Dave Page <[email protected]>
  parent: Sanket Mehta <[email protected]>
  0 siblings, 0 replies; 24+ messages in thread

From: Dave Page @ 2016-03-17 11:49 UTC (permalink / raw)
  To: Sanket Mehta <[email protected]>; +Cc: pgadmin-hackers

Thanks - committed.

On Wed, Mar 16, 2016 at 7:22 AM, Sanket Mehta
<[email protected]> wrote:
> Hi,
>
> I forgot tot mention bugs in previous mail.
>
>
> 1. System cast field was showing Yes even if the cast was not a system cast,
> which I have resolved.
> 2. In Dependent and Dependency method in __init__.py  file, unnecessary
> third parameter 'cast' was being passed which I have removed
>
>
>
> Regards,
> Sanket Mehta
> Sr Software engineer
> Enterprisedb
>
> On Wed, Mar 16, 2016 at 12:43 PM, Sanket Mehta
> <[email protected]> wrote:
>>
>> Hi Dave,
>>
>> PFA the patch for cast module incorporating changes regarding showing
>> system objects.
>> Apart from support for showing system object I have resolved few bugs in
>> that, unnecessary code and added nodes.sql file.
>>
>> Please do review it and if it looks good, please commit.
>>
>>
>> Regards,
>> Sanket Mehta
>> Sr Software engineer
>> Enterprisedb
>>
>> On Fri, Mar 4, 2016 at 4:33 PM, Dave Page <[email protected]> wrote:
>>>
>>> Thanks, patch applied.
>>>
>>> On Tue, Mar 1, 2016 at 7:20 AM, Sanket Mehta
>>> <[email protected]> wrote:
>>> > Hi,
>>> >
>>> > There was an error in cast module while fetching its dependency and
>>> > dependent.
>>> > Below is the patch which resolves this issue.
>>> > Please review and commit it.
>>> >
>>> >
>>> >
>>> > Regards,
>>> > Sanket Mehta
>>> > Sr Software engineer
>>> > Enterprisedb
>>> >
>>> > On Wed, Feb 24, 2016 at 10:17 PM, Dave Page <[email protected]> wrote:
>>> >>
>>> >> Thanks - committed.
>>> >>
>>> >> On Tue, Feb 23, 2016 at 1:34 PM, Sanket Mehta
>>> >> <[email protected]> wrote:
>>> >>>
>>> >>> 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 <[email protected]> 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
>>> >>>> <[email protected]> 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 <[email protected]>
>>> >>>>> wrote:
>>> >>>>>>
>>> >>>>>> And this time with the attachment...
>>> >>>>>>
>>> >>>>>> On Mon, Feb 15, 2016 at 4:53 PM, Dave Page <[email protected]>
>>> >>>>>> 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
>>> >>>>>>> <[email protected]> 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 <[email protected]>
>>> >>>>>>>> wrote:
>>> >>>>>>>>>
>>> >>>>>>>>>
>>> >>>>>>>>>
>>> >>>>>>>>> On Mon, Feb 15, 2016 at 8:10 AM, Sanket Mehta
>>> >>>>>>>>> <[email protected]> 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
>>> >>>
>>> >>>
>>> >>
>>> >>
>>> >>
>>> >> --
>>> >> 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


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers




^ permalink  raw  reply  [nested|flat] 24+ messages in thread


end of thread, other threads:[~2016-03-17 11:49 UTC | newest]

Thread overview: 24+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2016-01-18 13:46 patch for cast module Sanket Mehta <[email protected]>
2016-01-19 14:36 ` Sanket Mehta <[email protected]>
2016-01-20 04:50   ` Neel Patel <[email protected]>
2016-01-20 11:33     ` Sanket Mehta <[email protected]>
2016-02-04 13:01       ` Sanket Mehta <[email protected]>
2016-02-05 07:13         ` Akshay Joshi <[email protected]>
2016-02-08 10:15           ` Sanket Mehta <[email protected]>
2016-02-11 06:39             ` Akshay Joshi <[email protected]>
2016-02-12 11:57               ` Sanket Mehta <[email protected]>
2016-02-12 16:24                 ` Dave Page <[email protected]>
2016-02-15 08:10                   ` Sanket Mehta <[email protected]>
2016-02-15 10:48                     ` Dave Page <[email protected]>
2016-02-15 13:28                       ` Sanket Mehta <[email protected]>
2016-02-15 16:53                         ` Dave Page <[email protected]>
2016-02-15 16:55                           ` Dave Page <[email protected]>
2016-02-19 11:03                             ` Sanket Mehta <[email protected]>
2016-02-23 10:40                               ` Dave Page <[email protected]>
2016-02-23 13:34                                 ` Sanket Mehta <[email protected]>
2016-02-24 16:47                                   ` Dave Page <[email protected]>
2016-03-01 07:20                                     ` Sanket Mehta <[email protected]>
2016-03-04 11:03                                       ` Dave Page <[email protected]>
2016-03-16 07:13                                         ` Sanket Mehta <[email protected]>
2016-03-16 07:22                                           ` Sanket Mehta <[email protected]>
2016-03-17 11:49                                             ` Dave Page <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox