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*
>
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
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers