mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fix various issues with Types, per discussion
This commit is contained in:
parent
8a39b3a700
commit
c047abd8a4
@ -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)
|
||||
|
@ -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'];
|
||||
});
|
||||
});
|
||||
|
@ -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 %}
|
@ -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 %}
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user