Fix various issues with Types, per discussion

This commit is contained in:
Murtuza Zabuawala 2016-04-14 12:37:19 +01:00 committed by Dave Page
parent 8a39b3a700
commit c047abd8a4
5 changed files with 151 additions and 41 deletions

View File

@ -16,7 +16,7 @@ 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.server_groups.servers.databases.schemas.utils \
import SchemaChildModule
import SchemaChildModule, DataTypeReader
import pgadmin.browser.server_groups.servers.databases as database
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
parse_priv_to_db
@ -87,7 +87,7 @@ class TypeModule(SchemaChildModule):
blueprint = TypeModule(__name__)
class TypeView(PGChildNodeView):
class TypeView(PGChildNodeView, DataTypeReader):
"""
This class is responsible for generating routes for Type node
@ -335,7 +335,13 @@ class TypeView(PGChildNodeView):
composite_lst = []
for row in rset['rows']:
typelist = ' '.join([row['attname'], row['typname']])
# We will fetch Full type name
fulltype = self.get_full_type(
row['collnspname'], row['typname'],
row['isdup'], row['attndims'], row['atttypmod']
)
typelist = ' '.join([row['attname'], fulltype])
if not row['collname'] or (row['collname'] == 'default'
and row['collnspname'] == 'pg_catalog'):
full_collate = ''
@ -349,19 +355,28 @@ class TypeView(PGChildNodeView):
# Below logic will allow us to split length, precision from type name for grid
import re
matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
# If we have length & precision both
matchObj = re.search(r'(\d+),(\d+)', fulltype)
if matchObj:
t_name = matchObj.group(1)
t_len = matchObj.group(2)
t_prec = matchObj.group(3)
t_len = matchObj.group(1)
t_prec = matchObj.group(2)
else:
t_name = row['typname']
t_len = None
t_prec = None
# If we have length only
matchObj = re.search(r'(\d+)', fulltype)
if matchObj:
t_len = matchObj.group(1)
t_prec = None
else:
t_len = None
t_prec = None
is_tlength = True if t_len else False
is_precision = True if t_prec else False
composite_lst.append({
'attnum':row['attnum'], 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
'tlength': t_len, 'precision': t_prec })
'attnum':row['attnum'], 'member_name': row['attname'], 'type': row['typname'], 'collation': full_collate,
'tlength': t_len, 'precision': t_prec,
'is_tlength': is_tlength, 'is_precision': is_precision })
# Adding both results
res['member_list'] = ', '.join(properties_list)
@ -442,7 +457,6 @@ class TypeView(PGChildNodeView):
# it from properties sql
copy_dict['typacl'] = []
for row in acl['rows']:
priv = parse_priv_from_db(row)
if row['deftype'] in copy_dict:
@ -1207,4 +1221,4 @@ class TypeView(PGChildNodeView):
status=200
)
TypeView.register_node_view(blueprint)
TypeView.register_node_view(blueprint)

View File

@ -308,23 +308,15 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
group: '{{ _('Definition') }}',
mode: ['edit', 'create'],
select2: { width: "50%" },
options: function() {
if(!this.model.isNew()) {
options: function(obj) {
return [
{label: "Composite", value: "c"},
{label: "Enumeration", value: "e"},
{label: "External", value: "b"},
{label: "Range", value: "r"},
{label: "Shell", value: "s"}
]
} else {
return [
{label: "Composite", value: "c"},
{label: "Enumeration", value: "e"},
{label: "Range", value: "r"},
]
}
},
},
disabled: 'inSchemaWithModelCheck',
// If create mode then by default open composite type
control: Backform.Select2Control.extend({
@ -345,10 +337,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
deps: ['typtype'], deps: ['typtype'],
visible: function(m) {
if (m.get('typtype') === 'c') {
return true;
}
return false;
return m.get('typtype') === 'c';
}
},{
id: 'enum', label: '{{ _('Enumeration Type') }}',
@ -524,7 +513,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
},{
type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
label: '{{ _('External Type') }}', deps: ['typtype'],
mode: ['edit'],
mode: ['create', 'edit'],
visible: function(m) {
return m.get('typtype') === 'b';
},
@ -731,14 +720,23 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
id: 'typacl', label: 'Privileges', type: 'collection',
group: '{{ _('Security') }}', control: 'unique-col-collection',
model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}),
mode: ['edit', 'create'], canAdd: true, canDelete: true,
uniqueCol : ['grantee']
mode: ['edit', 'create'], canDelete: true,
uniqueCol : ['grantee'], deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
return !(m.get('typtype') === 's');
}
},{
id: 'seclabels', label: '{{ _('Security Labels') }}',
model: SecurityModel, editable: false, type: 'collection',
group: '{{ _('Security') }}', mode: ['edit', 'create'],
min_version: 90100, canAdd: true,
canEdit: false, canDelete: true, control: 'unique-col-collection'
min_version: 90100, canEdit: false, canDelete: true,
control: 'unique-col-collection', deps: ['typtype'],
canAdd: function(m) {
// Do not allow to add when shell type is selected
return !(m.get('typtype') === 's');
}
}],
validate: function() {
// Validation code for required fields
@ -862,4 +860,4 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
});
}
return pgBrowser.Nodes['type'];
});
});

View File

@ -1,9 +1,13 @@
{% import 'macros/schemas/security.macros' as SECLABLE %}
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
{## If user selected shell type then just create type template ##}
{% if data and data.typtype == 's' %}
CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }};
{% endif %}
{### Composite Type ###}
{% if data and data.typtype == 'c' %}
CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
({% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %}, {% endif %}{{ conn|qtIdent(d.member_name) }} {{ d.type }}{% if d.is_tlength and d.tlength %}({{d.tlength}}{% if d.is_precision and d.precision %},{{d.precision}}{% endif %}){% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %});
({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {{ d.type }}{% if d.is_tlength and d.tlength %}({{d.tlength}}{% if d.is_precision and d.precision %},{{d.precision}}{% endif %}){% endif %}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}{{"\n"}});
{% endif %}
{### Enum Type ###}
{% if data and data.typtype == 'e' %}
@ -76,4 +80,4 @@ COMMENT ON TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{%
{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -1,16 +1,18 @@
{### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###}
{% if extfunc %}
SELECT proname, nspname,
CASE WHEN length(nspname) > 0 AND length(proname) > 0 THEN
CASE WHEN (length(nspname) > 0 AND nspname != 'public') and length(proname) > 0 THEN
concat(quote_ident(nspname), '.', quote_ident(proname))
WHEN length(proname) > 0 THEN
quote_ident(proname)
ELSE '' END AS func
FROM (
SELECT proname, nspname, max(proargtypes[0]) AS arg0, max(proargtypes[1]) AS arg1
FROM pg_proc p
JOIN pg_namespace n ON n.oid=pronamespace
GROUP BY proname, nspname
HAVING count(proname) = 1 ) AS uniquefunc
WHERE arg0 <> 0 AND arg1 IS NULL;
HAVING count(proname) = 1) AS uniquefunc
WHERE arg0 <> 0 AND (arg1 IS NULL OR arg1 <> 0);
{% endif %}
{### TypmodIN list ###}
{% if typemodin %}

View File

@ -13,6 +13,7 @@ from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.utils import PGChildNodeView
from flask import render_template
from pgadmin.utils.ajax import internal_server_error
import json
class SchemaChildModule(CollectionNodeModule):
"""
@ -141,6 +142,97 @@ class DataTypeReader:
return True, res
def get_full_type(self, nsp, typname, isDup, numdims, typmod):
"""
Returns full type name with Length and Precision.
Args:
conn: Connection Object
condition: condition to restrict SQL statement
"""
needSchema = isDup
schema = nsp if nsp is not None else ''
name = ''
array = ''
length = ''
# Above 7.4, format_type also sends the schema name if it's not included
# in the search_path, so we need to skip it in the typname
if typname.find(schema + '".') >= 0:
name = typname[len(schema)+3]
elif typname.find(schema + '.') >= 0:
name = typname[len(schema)+1]
else:
name = typname
if name.startswith('_'):
if not numdims:
numdims = 1
name = name[1:]
if name.endswith('[]'):
if not numdims:
numdims = 1
name = name[:-2]
if name.startswith('"') and name.endswith('"'):
name = name[1:-1]
if numdims > 0:
while numdims:
array += '[]'
numdims -= 1
if typmod != -1:
length = '('
if name == 'numeric':
_len = (typmod - 4) >> 16;
_prec = (typmod - 4) & 0xffff;
length += str(_len)
if(_prec):
length += ',' + str(_prec)
elif name == 'time' or \
name == 'timetz' or \
name == 'time without time zone' or \
name == 'time with time zone' or \
name == 'timestamp' or \
name == 'timestamptz'or \
name == 'timestamp without time zone' or \
name == 'timestamp with time zone' or \
name == 'bit' or \
name == 'bit varying' or \
name == 'varbit':
_prec = 0
_len = typmod
length += str(_len)
elif name == 'interval':
_prec = 0
_len = typmod & 0xffff
length += str(_len)
elif name == 'date':
# Clear length
length = ''
else:
_len = typmod - 4
_prec = 0
length += str(_len)
if len(length) > 0:
length += ')'
if name == 'char' and schema == 'pg_catalog':
return '"char"' + array
elif name == 'time with time zone':
return 'time' + length + ' with time zone' + array
elif name == 'time without time zone':
return 'time' + length + ' without time zone' + array
elif name == 'timestamp with time zone':
return 'timestamp' + length + ' with time zone' + array
elif name == 'timestamp without time zone':
return 'timestamp' + length + ' without time zone' + array
else:
return name + length + array
def trigger_definition(data):
"""
@ -246,4 +338,4 @@ def parse_rule_definition(res):
res_data['condition'] = condition
except Exception as e:
return internal_server_error(errormsg=str(e))
return res_data
return res_data