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
|
make_response as ajax_response, internal_server_error
|
||||||
from pgadmin.browser.utils import PGChildNodeView
|
from pgadmin.browser.utils import PGChildNodeView
|
||||||
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
from pgadmin.browser.server_groups.servers.databases.schemas.utils \
|
||||||
import SchemaChildModule
|
import SchemaChildModule, DataTypeReader
|
||||||
import pgadmin.browser.server_groups.servers.databases as database
|
import pgadmin.browser.server_groups.servers.databases as database
|
||||||
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
from pgadmin.browser.server_groups.servers.utils import parse_priv_from_db, \
|
||||||
parse_priv_to_db
|
parse_priv_to_db
|
||||||
@ -87,7 +87,7 @@ class TypeModule(SchemaChildModule):
|
|||||||
blueprint = TypeModule(__name__)
|
blueprint = TypeModule(__name__)
|
||||||
|
|
||||||
|
|
||||||
class TypeView(PGChildNodeView):
|
class TypeView(PGChildNodeView, DataTypeReader):
|
||||||
"""
|
"""
|
||||||
This class is responsible for generating routes for Type node
|
This class is responsible for generating routes for Type node
|
||||||
|
|
||||||
@ -335,7 +335,13 @@ class TypeView(PGChildNodeView):
|
|||||||
composite_lst = []
|
composite_lst = []
|
||||||
|
|
||||||
for row in rset['rows']:
|
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'
|
if not row['collname'] or (row['collname'] == 'default'
|
||||||
and row['collnspname'] == 'pg_catalog'):
|
and row['collnspname'] == 'pg_catalog'):
|
||||||
full_collate = ''
|
full_collate = ''
|
||||||
@ -349,19 +355,28 @@ class TypeView(PGChildNodeView):
|
|||||||
|
|
||||||
# Below logic will allow us to split length, precision from type name for grid
|
# Below logic will allow us to split length, precision from type name for grid
|
||||||
import re
|
import re
|
||||||
matchObj = re.match( r'(.*)\((.*?),(.*?)\)', row['typname'])
|
# If we have length & precision both
|
||||||
|
matchObj = re.search(r'(\d+),(\d+)', fulltype)
|
||||||
if matchObj:
|
if matchObj:
|
||||||
t_name = matchObj.group(1)
|
t_len = matchObj.group(1)
|
||||||
t_len = matchObj.group(2)
|
t_prec = matchObj.group(2)
|
||||||
t_prec = matchObj.group(3)
|
|
||||||
else:
|
else:
|
||||||
t_name = row['typname']
|
# If we have length only
|
||||||
t_len = None
|
matchObj = re.search(r'(\d+)', fulltype)
|
||||||
t_prec = None
|
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({
|
composite_lst.append({
|
||||||
'attnum':row['attnum'], 'member_name': row['attname'], 'type': t_name, 'collation': full_collate,
|
'attnum':row['attnum'], 'member_name': row['attname'], 'type': row['typname'], 'collation': full_collate,
|
||||||
'tlength': t_len, 'precision': t_prec })
|
'tlength': t_len, 'precision': t_prec,
|
||||||
|
'is_tlength': is_tlength, 'is_precision': is_precision })
|
||||||
|
|
||||||
# Adding both results
|
# Adding both results
|
||||||
res['member_list'] = ', '.join(properties_list)
|
res['member_list'] = ', '.join(properties_list)
|
||||||
@ -442,7 +457,6 @@ class TypeView(PGChildNodeView):
|
|||||||
# it from properties sql
|
# it from properties sql
|
||||||
copy_dict['typacl'] = []
|
copy_dict['typacl'] = []
|
||||||
|
|
||||||
|
|
||||||
for row in acl['rows']:
|
for row in acl['rows']:
|
||||||
priv = parse_priv_from_db(row)
|
priv = parse_priv_from_db(row)
|
||||||
if row['deftype'] in copy_dict:
|
if row['deftype'] in copy_dict:
|
||||||
@ -1207,4 +1221,4 @@ class TypeView(PGChildNodeView):
|
|||||||
status=200
|
status=200
|
||||||
)
|
)
|
||||||
|
|
||||||
TypeView.register_node_view(blueprint)
|
TypeView.register_node_view(blueprint)
|
||||||
|
@ -308,23 +308,15 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
|
|||||||
group: '{{ _('Definition') }}',
|
group: '{{ _('Definition') }}',
|
||||||
mode: ['edit', 'create'],
|
mode: ['edit', 'create'],
|
||||||
select2: { width: "50%" },
|
select2: { width: "50%" },
|
||||||
options: function() {
|
options: function(obj) {
|
||||||
if(!this.model.isNew()) {
|
|
||||||
return [
|
return [
|
||||||
{label: "Composite", value: "c"},
|
{label: "Composite", value: "c"},
|
||||||
{label: "Enumeration", value: "e"},
|
{label: "Enumeration", value: "e"},
|
||||||
{label: "External", value: "b"},
|
{label: "External", value: "b"},
|
||||||
{label: "Range", value: "r"},
|
{label: "Range", value: "r"},
|
||||||
|
{label: "Shell", value: "s"}
|
||||||
]
|
]
|
||||||
} else {
|
},
|
||||||
return [
|
|
||||||
{label: "Composite", value: "c"},
|
|
||||||
{label: "Enumeration", value: "e"},
|
|
||||||
{label: "Range", value: "r"},
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
disabled: 'inSchemaWithModelCheck',
|
disabled: 'inSchemaWithModelCheck',
|
||||||
// If create mode then by default open composite type
|
// If create mode then by default open composite type
|
||||||
control: Backform.Select2Control.extend({
|
control: Backform.Select2Control.extend({
|
||||||
@ -345,10 +337,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
|
|||||||
canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
|
canAdd: true, canEdit: true, canDelete: true, disabled: 'inSchema',
|
||||||
deps: ['typtype'], deps: ['typtype'],
|
deps: ['typtype'], deps: ['typtype'],
|
||||||
visible: function(m) {
|
visible: function(m) {
|
||||||
if (m.get('typtype') === 'c') {
|
return m.get('typtype') === 'c';
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
},{
|
},{
|
||||||
id: 'enum', label: '{{ _('Enumeration Type') }}',
|
id: 'enum', label: '{{ _('Enumeration Type') }}',
|
||||||
@ -524,7 +513,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
|
|||||||
},{
|
},{
|
||||||
type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
|
type: 'nested', control: 'tab', group: '{{ _('Definition') }}',
|
||||||
label: '{{ _('External Type') }}', deps: ['typtype'],
|
label: '{{ _('External Type') }}', deps: ['typtype'],
|
||||||
mode: ['edit'],
|
mode: ['create', 'edit'],
|
||||||
visible: function(m) {
|
visible: function(m) {
|
||||||
return m.get('typtype') === 'b';
|
return m.get('typtype') === 'b';
|
||||||
},
|
},
|
||||||
@ -731,14 +720,23 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
|
|||||||
id: 'typacl', label: 'Privileges', type: 'collection',
|
id: 'typacl', label: 'Privileges', type: 'collection',
|
||||||
group: '{{ _('Security') }}', control: 'unique-col-collection',
|
group: '{{ _('Security') }}', control: 'unique-col-collection',
|
||||||
model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}),
|
model: pgAdmin.Browser.Node.PrivilegeRoleModel.extend({privileges: ['U']}),
|
||||||
mode: ['edit', 'create'], canAdd: true, canDelete: true,
|
mode: ['edit', 'create'], canDelete: true,
|
||||||
uniqueCol : ['grantee']
|
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') }}',
|
id: 'seclabels', label: '{{ _('Security Labels') }}',
|
||||||
model: SecurityModel, editable: false, type: 'collection',
|
model: SecurityModel, editable: false, type: 'collection',
|
||||||
group: '{{ _('Security') }}', mode: ['edit', 'create'],
|
group: '{{ _('Security') }}', mode: ['edit', 'create'],
|
||||||
min_version: 90100, canAdd: true,
|
min_version: 90100, canEdit: false, canDelete: true,
|
||||||
canEdit: false, canDelete: true, control: 'unique-col-collection'
|
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() {
|
validate: function() {
|
||||||
// Validation code for required fields
|
// Validation code for required fields
|
||||||
@ -862,4 +860,4 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backgrid) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return pgBrowser.Nodes['type'];
|
return pgBrowser.Nodes['type'];
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
{% import 'macros/schemas/security.macros' as SECLABLE %}
|
{% import 'macros/schemas/security.macros' as SECLABLE %}
|
||||||
{% import 'macros/schemas/privilege.macros' as PRIVILEGE %}
|
{% 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 ###}
|
{### Composite Type ###}
|
||||||
{% if data and data.typtype == 'c' %}
|
{% if data and data.typtype == 'c' %}
|
||||||
CREATE TYPE {% if data.schema %}{{ conn|qtIdent(data.schema, data.name) }}{% else %}{{ conn|qtIdent(data.name) }}{% endif %} AS
|
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 %}
|
{% endif %}
|
||||||
{### Enum Type ###}
|
{### Enum Type ###}
|
||||||
{% if data and data.typtype == 'e' %}
|
{% 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) }}
|
{{ SECLABLE.SET(conn, 'TYPE', data.name, r.provider, r.security_label, data.schema) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
@ -1,16 +1,18 @@
|
|||||||
{### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###}
|
{### Input/Output/Send/Receive/Analyze function list also append into TypModeIN/TypModOUT ###}
|
||||||
{% if extfunc %}
|
{% if extfunc %}
|
||||||
SELECT proname, nspname,
|
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))
|
concat(quote_ident(nspname), '.', quote_ident(proname))
|
||||||
|
WHEN length(proname) > 0 THEN
|
||||||
|
quote_ident(proname)
|
||||||
ELSE '' END AS func
|
ELSE '' END AS func
|
||||||
FROM (
|
FROM (
|
||||||
SELECT proname, nspname, max(proargtypes[0]) AS arg0, max(proargtypes[1]) AS arg1
|
SELECT proname, nspname, max(proargtypes[0]) AS arg0, max(proargtypes[1]) AS arg1
|
||||||
FROM pg_proc p
|
FROM pg_proc p
|
||||||
JOIN pg_namespace n ON n.oid=pronamespace
|
JOIN pg_namespace n ON n.oid=pronamespace
|
||||||
GROUP BY proname, nspname
|
GROUP BY proname, nspname
|
||||||
HAVING count(proname) = 1 ) AS uniquefunc
|
HAVING count(proname) = 1) AS uniquefunc
|
||||||
WHERE arg0 <> 0 AND arg1 IS NULL;
|
WHERE arg0 <> 0 AND (arg1 IS NULL OR arg1 <> 0);
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{### TypmodIN list ###}
|
{### TypmodIN list ###}
|
||||||
{% if typemodin %}
|
{% if typemodin %}
|
||||||
|
@ -13,6 +13,7 @@ from pgadmin.browser.collection import CollectionNodeModule
|
|||||||
from pgadmin.browser.utils import PGChildNodeView
|
from pgadmin.browser.utils import PGChildNodeView
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from pgadmin.utils.ajax import internal_server_error
|
from pgadmin.utils.ajax import internal_server_error
|
||||||
|
import json
|
||||||
|
|
||||||
class SchemaChildModule(CollectionNodeModule):
|
class SchemaChildModule(CollectionNodeModule):
|
||||||
"""
|
"""
|
||||||
@ -141,6 +142,97 @@ class DataTypeReader:
|
|||||||
|
|
||||||
return True, res
|
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):
|
def trigger_definition(data):
|
||||||
"""
|
"""
|
||||||
@ -246,4 +338,4 @@ def parse_rule_definition(res):
|
|||||||
res_data['condition'] = condition
|
res_data['condition'] = condition
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return internal_server_error(errormsg=str(e))
|
return internal_server_error(errormsg=str(e))
|
||||||
return res_data
|
return res_data
|
Loading…
Reference in New Issue
Block a user