Fix the PostGIS Datatypes in SQL tab, Create / Update dialogues for Table, Column, Foreign Table and Type node. Fixes #2324

Note that this doesn't add full support for PostGIS types to the table/column dialogues, which still can't handle the typmod usage. It does bring support to parity with pgAdmin 3 though.
This commit is contained in:
Khushboo Vashi 2017-06-12 12:49:17 +01:00 committed by Dave Page
parent 59da69087c
commit 8bd8ae001b
14 changed files with 97 additions and 72 deletions

View File

@ -830,7 +830,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
data['columns'] = col_data data['columns'] = col_data
SQL = render_template("/".join([self.template_path, SQL = render_template("/".join([self.template_path,
'create.sql']), data=data) 'create.sql']), data=data, is_sql=True)
sql_header = u"""-- FOREIGN TABLE: {0} sql_header = u"""-- FOREIGN TABLE: {0}
@ -1093,10 +1093,10 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
substr = c['fulltype'][c['fulltype'].find("(") + 1:c['fulltype'].find(")")] substr = c['fulltype'][c['fulltype'].find("(") + 1:c['fulltype'].find(")")]
typlen = substr.split(",") typlen = substr.split(",")
if len(typlen) > 1: if len(typlen) > 1:
c['typlen'] = int(typlen[0]) c['typlen'] = int(typlen[0]) if typlen[0].isdigit() else typlen[0]
c['precision'] = int(typlen[1]) c['precision'] = int(typlen[1]) if typlen[1].isdigit() else typlen[1]
else: else:
c['typlen'] = int(typlen[0]) c['typlen'] = int(typlen[0]) if typlen[0].isdigit() else typlen[0]
c['precision'] = None c['precision'] = None
# Get formatted Column Options # Get formatted Column Options

View File

@ -4,7 +4,7 @@
CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}( CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
{% if data.columns %} {% if data.columns %}
{% for c in data.columns %} {% for c in data.columns %}
{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.coloptions %} {{conn|qtIdent(c.attname)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %}
{% for o in c.coloptions %}{% if o.option and o.value %} {% for o in c.coloptions %}{% if o.option and o.value %}
{% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %} {% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %}
{% endfor %}{% endif %}{% if c.attnotnull %} {% endfor %}{% endif %}{% if c.attnotnull %}

View File

@ -7,7 +7,7 @@ CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
{% for c in data.columns %} {% for c in data.columns %}
{% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %} {% if (not c.inheritedfrom or c.inheritedfrom =='' or c.inheritedfrom == None or c.inheritedfrom == 'None' ) %}
{% if is_columns.append('1') %}{% endif %} {% if is_columns.append('1') %}{% endif %}
{{conn|qtIdent(c.attname)}} {{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.coloptions %} {{conn|qtIdent(c.attname)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}}{% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.coloptions %}
{% for o in c.coloptions %}{% if o.option and o.value %} {% for o in c.coloptions %}{% if o.option and o.value %}
{% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %} {% if loop.first %} OPTIONS ({% endif %}{% if not loop.first %}, {% endif %}{{o.option}} {{o.value|qtLiteral}}{% if loop.last %}){% endif %}{% endif %}
{% endfor %}{% endif %} {% endfor %}{% endif %}

View File

@ -3,7 +3,7 @@
CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}( CREATE FOREIGN TABLE {{ conn|qtIdent(data.basensp, data.name) }}(
{% if data.columns %} {% if data.columns %}
{% for c in data.columns %} {% for c in data.columns %}
{{conn|qtIdent(c.attname)}} {{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% if c.attnotnull %} {{conn|qtIdent(c.attname)}} {% if is_sql %}{{ c.fulltype }}{% else %}{{ conn|qtTypeIdent(c.datatype) }}{% if c.typlen %}({{c.typlen}} {% if c.precision %}, {{c.precision}}{% endif %}){% endif %}{% if c.isArrayType %}[]{% endif %}{% endif %}{% if c.attnotnull %}
NOT NULL{% else %} NULL{% endif %} NOT NULL{% else %} NULL{% endif %}
{% if not loop.last %}, {% if not loop.last %},
{% endif %}{% endfor -%}{% endif %} {% endif %}{% endfor -%}{% endif %}

View File

@ -652,20 +652,24 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
column['isdup'], column['attndims'], column['atttypmod'] column['isdup'], column['attndims'], column['atttypmod']
) )
length = False
precision = False
if 'elemoid' in column:
length, precision, typeval = self.get_length_precision(column['elemoid'])
# If we have length & precision both # If we have length & precision both
matchObj = re.search(r'(\d+),(\d+)', fulltype) if length and precision:
if matchObj: matchObj = re.search(r'(\d+),(\d+)', fulltype)
column['attlen'] = matchObj.group(1) column['attlen'] = matchObj.group(1)
column['attprecision'] = matchObj.group(2) column['attprecision'] = matchObj.group(2)
else: elif length:
# If we have length only # If we have length only
matchObj = re.search(r'(\d+)', fulltype) matchObj = re.search(r'(\d+)', fulltype)
if matchObj: column['attlen'] = matchObj.group(1)
column['attlen'] = matchObj.group(1) column['attprecision'] = None
column['attprecision'] = None else:
else: column['attlen'] = None
column['attlen'] = None column['attprecision'] = None
column['attprecision'] = None
SQL = render_template("/".join([self.column_template_path, SQL = render_template("/".join([self.column_template_path,
'is_referenced.sql']), 'is_referenced.sql']),
@ -2540,7 +2544,7 @@ class TableView(PGChildNodeView, DataTypeReader, VacuumSettings):
# If the request for new object which do not have did # If the request for new object which do not have did
table_sql = render_template("/".join([self.template_path, table_sql = render_template("/".join([self.template_path,
'create.sql']), 'create.sql']),
data=data, conn=self.conn) data=data, conn=self.conn, is_sql=True)
# Add into main sql # Add into main sql
table_sql = re.sub('\n{2,}', '\n\n', table_sql) table_sql = re.sub('\n{2,}', '\n\n', table_sql)

View File

@ -343,21 +343,26 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
data['isdup'], data['attndims'], data['atttypmod'] data['isdup'], data['attndims'], data['atttypmod']
) )
length = False
precision = False
if 'elemoid' in data:
length, precision, typeval = self.get_length_precision(data['elemoid'])
import re import re
# If we have length & precision both # If we have length & precision both
matchObj = re.search(r'(\d+),(\d+)', fulltype)
if matchObj: if length and precision:
matchObj = re.search(r'(\d+),(\d+)', fulltype)
data['attlen'] = matchObj.group(1) data['attlen'] = matchObj.group(1)
data['attprecision'] = matchObj.group(2) data['attprecision'] = matchObj.group(2)
else: elif length:
# If we have length only # If we have length only
matchObj = re.search(r'(\d+)', fulltype) matchObj = re.search(r'(\d+)', fulltype)
if matchObj: data['attlen'] = matchObj.group(1)
data['attlen'] = matchObj.group(1) data['attprecision'] = None
data['attprecision'] = None else:
else: data['attlen'] = None
data['attlen'] = None data['attprecision'] = None
data['attprecision'] = None
# We need to fetch inherited tables for each table # We need to fetch inherited tables for each table
SQL = render_template("/".join([self.template_path, SQL = render_template("/".join([self.template_path,
@ -750,7 +755,7 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
except Exception as e: except Exception as e:
return internal_server_error(errormsg=str(e)) return internal_server_error(errormsg=str(e))
def get_sql(self, scid, tid, clid, data): def get_sql(self, scid, tid, clid, data, is_sql=False):
""" """
This function will genrate sql from model data This function will genrate sql from model data
""" """
@ -819,7 +824,7 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
self.acl) self.acl)
# If the request for new object which do not have did # If the request for new object which do not have did
SQL = render_template("/".join([self.template_path, 'create.sql']), SQL = render_template("/".join([self.template_path, 'create.sql']),
data=data, conn=self.conn) data=data, conn=self.conn, is_sql=is_sql)
return SQL, data['name'] if 'name' in data else old_data['name'] return SQL, data['name'] if 'name' in data else old_data['name']
@check_precondition @check_precondition
@ -863,7 +868,7 @@ class ColumnsView(PGChildNodeView, DataTypeReader):
# We will add table & schema as well # We will add table & schema as well
data = self._formatter(scid, tid, clid, data) data = self._formatter(scid, tid, clid, data)
SQL, name = self.get_sql(scid, tid, None, data) SQL, name = self.get_sql(scid, tid, None, data, is_sql=True)
if not isinstance(SQL, (str, unicode)): if not isinstance(SQL, (str, unicode)):
return SQL return SQL

View File

@ -2,6 +2,7 @@ SELECT att.attname as name, att.*, def.*, pg_catalog.pg_get_expr(def.adbin, def.
CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray, CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
format_type(ty.oid,NULL) AS typname, format_type(ty.oid,NULL) AS typname,
format_type(ty.oid,att.atttypmod) AS displaytypname, format_type(ty.oid,att.atttypmod) AS displaytypname,
CASE WHEN ty.typelem > 0 THEN ty.typelem ELSE ty.oid END as elemoid,
tn.nspname as typnspname, et.typname as elemtypname, tn.nspname as typnspname, et.typname as elemtypname,
ty.typstorage AS defaultstorage, cl.relname, na.nspname, ty.typstorage AS defaultstorage, cl.relname, na.nspname,
concat(quote_ident(na.nspname) ,'.', quote_ident(cl.relname)) AS parent_tbl, concat(quote_ident(na.nspname) ,'.', quote_ident(cl.relname)) AS parent_tbl,

View File

@ -5,7 +5,7 @@
{### Add column ###} {### Add column ###}
{% if data.name and data.cltype %} {% if data.name and data.cltype %}
ALTER TABLE {{conn|qtIdent(data.schema, data.table)}} ALTER TABLE {{conn|qtIdent(data.schema, data.table)}}
ADD COLUMN {{conn|qtIdent(data.name)}} {{ GET_TYPE.CREATE_TYPE_SQL(conn, data.cltype, data.attlen, data.attprecision, data.hasSqrBracket) }}{% if data.collspcname %} ADD COLUMN {{conn|qtIdent(data.name)}} {% if is_sql %}{{data.displaytypname}}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, data.cltype, data.attlen, data.attprecision, data.hasSqrBracket) }}{% endif %}{% if data.collspcname %}
COLLATE {{data.collspcname}}{% endif %}{% if data.attnotnull %} COLLATE {{data.collspcname}}{% endif %}{% if data.attnotnull %}
NOT NULL{% endif %}{% if data.defval %} NOT NULL{% endif %}{% if data.defval %}
DEFAULT {{data.defval}}{% endif %}; DEFAULT {{data.defval}}{% endif %};

View File

@ -2,6 +2,7 @@ SELECT att.attname as name, att.*, def.*, pg_catalog.pg_get_expr(def.adbin, def.
CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray, CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
format_type(ty.oid,NULL) AS typname, format_type(ty.oid,NULL) AS typname,
format_type(ty.oid,att.atttypmod) AS displaytypname, format_type(ty.oid,att.atttypmod) AS displaytypname,
CASE WHEN ty.typelem > 0 THEN ty.typelem ELSE ty.oid END as elemoid,
tn.nspname as typnspname, et.typname as elemtypname, tn.nspname as typnspname, et.typname as elemtypname,
ty.typstorage AS defaultstorage, cl.relname, na.nspname, ty.typstorage AS defaultstorage, cl.relname, na.nspname,
quote_ident(na.nspname) || '.' || quote_ident(cl.relname) AS parent_tbl, quote_ident(na.nspname) || '.' || quote_ident(cl.relname) AS parent_tbl,

View File

@ -44,7 +44,7 @@ CREATE {% if data.relpersistence %}UNLOGGED {% endif %}TABLE {{conn|qtIdent(data
{% if c.name and c.cltype %} {% if c.name and c.cltype %}
{% if loop.index != 1 %}, {% if loop.index != 1 %},
{% endif %} {% endif %}
{{conn|qtIdent(c.name)}} {{ GET_TYPE.CREATE_TYPE_SQL(conn, c.cltype, c.attlen, c.attprecision, c.hasSqrBracket) }}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %} {{conn|qtIdent(c.name)}} {% if is_sql %}{{c.displaytypname}}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, c.cltype, c.attlen, c.attprecision, c.hasSqrBracket) }}{% endif %}{% if c.collspcname %} COLLATE {{c.collspcname}}{% endif %}{% if c.attnotnull %} NOT NULL{% endif %}{% if c.defval %} DEFAULT {{c.defval}}{% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@ -414,12 +414,9 @@ class TypeView(PGChildNodeView, DataTypeReader):
for row in rset['rows']: for row in rset['rows']:
# We will fetch Full type name # 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])
typelist = ' '.join([row['attname'], row['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 = ''
@ -431,25 +428,26 @@ class TypeView(PGChildNodeView, DataTypeReader):
typelist += collate typelist += collate
properties_list.append(typelist) properties_list.append(typelist)
is_tlength = False
is_precision = False
if 'elemoid' in row:
is_tlength, is_precision, typeval = self.get_length_precision(row['elemoid'])
# 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
# If we have length & precision both # If we have length & precision both
matchObj = re.search(r'(\d+),(\d+)', fulltype) if is_tlength and is_precision:
if matchObj: matchObj = re.search(r'(\d+),(\d+)', row['fulltype'])
t_len = matchObj.group(1) t_len = matchObj.group(1)
t_prec = matchObj.group(2) t_prec = matchObj.group(2)
else: elif is_tlength:
# If we have length only # If we have length only
matchObj = re.search(r'(\d+)', fulltype) matchObj = re.search(r'(\d+)', row['fulltype'])
if matchObj: t_len = matchObj.group(1)
t_len = matchObj.group(1) t_prec = None
t_prec = None else:
else: t_len = None
t_len = None t_prec = None
t_prec = None
is_tlength = True if t_len else False
is_precision = True if t_prec else False
type_name = DataTypeReader.parse_type_name(row['typname']) type_name = DataTypeReader.parse_type_name(row['typname'])
@ -461,7 +459,7 @@ class TypeView(PGChildNodeView, DataTypeReader):
'collation': full_collate, 'cltype': row['type'], 'collation': full_collate, 'cltype': row['type'],
'tlength': t_len, 'precision': t_prec, 'tlength': t_len, 'precision': t_prec,
'is_tlength': is_tlength, 'is_precision': is_precision, 'is_tlength': is_tlength, 'is_precision': is_precision,
'hasSqrBracket': row['hasSqrBracket']}) 'hasSqrBracket': row['hasSqrBracket'], 'fulltype': row['fulltype']})
# Adding both results # Adding both results
res['member_list'] = ', '.join(properties_list) res['member_list'] = ', '.join(properties_list)
@ -1152,7 +1150,7 @@ class TypeView(PGChildNodeView, DataTypeReader):
return data return data
def get_sql(self, gid, sid, data, scid, tid=None): def get_sql(self, gid, sid, data, scid, tid=None, is_sql=False):
""" """
This function will genrate sql from model data This function will genrate sql from model data
""" """
@ -1262,7 +1260,7 @@ class TypeView(PGChildNodeView, DataTypeReader):
SQL = render_template("/".join([self.template_path, SQL = render_template("/".join([self.template_path,
'create.sql']), 'create.sql']),
data=data, conn=self.conn) data=data, conn=self.conn, is_sql=is_sql)
return SQL, data['name'] if 'name' in data else old_data['name'] return SQL, data['name'] if 'name' in data else old_data['name']
@ -1324,7 +1322,7 @@ class TypeView(PGChildNodeView, DataTypeReader):
if data[k] == '-': if data[k] == '-':
data[k] = None data[k] = None
SQL, name = self.get_sql(gid, sid, data, scid, tid=None) SQL, name = self.get_sql(gid, sid, data, scid, tid=None, is_sql=True)
# Most probably this is due to error # Most probably this is due to error
if not isinstance(SQL, (str, unicode)): if not isinstance(SQL, (str, unicode)):
return SQL return SQL

View File

@ -2,7 +2,9 @@
{% if type == 'c' %} {% if type == 'c' %}
SELECT attnum, attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname, SELECT attnum, attname, format_type(t.oid,NULL) AS typname, attndims, atttypmod, nsp.nspname,
(SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup, (SELECT COUNT(1) from pg_type t2 WHERE t2.typname=t.typname) > 1 AS isdup,
collname, nspc.nspname as collnspname, att.attrelid collname, nspc.nspname as collnspname, att.attrelid,
format_type(t.oid, att.atttypmod) AS fulltype,
CASE WHEN t.typelem > 0 THEN t.typelem ELSE t.oid END as elemoid
FROM pg_attribute att FROM pg_attribute att
JOIN pg_type t ON t.oid=atttypid JOIN pg_type t ON t.oid=atttypid
JOIN pg_namespace nsp ON t.typnamespace=nsp.oid JOIN pg_namespace nsp ON t.typnamespace=nsp.oid

View File

@ -8,7 +8,7 @@ CREATE TYPE {{ conn|qtIdent(data.schema, data.name) }};
{### 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
({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {{ GET_TYPE.CREATE_TYPE_SQL(conn, d.cltype, d.tlength, d.precision, d.hasSqrBracket) }}{% if d.collation %} COLLATE {{d.collation}}{% endif %}{% endfor %}{% endif %}{{"\n"}}); ({{"\n\t"}}{% if data.composite %}{% for d in data.composite %}{% if loop.index != 1 %},{{"\n\t"}}{% endif %}{{ conn|qtIdent(d.member_name) }} {% if is_sql %}{{ d.fulltype }}{% else %}{{ GET_TYPE.CREATE_TYPE_SQL(conn, d.cltype, d.tlength, d.precision, d.hasSqrBracket) }}{% 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' %}

View File

@ -103,25 +103,11 @@ class DataTypeReader:
min_val = 0 min_val = 0
max_val = 0 max_val = 0
# Check against PGOID for specific type # Check if the type will have length and precision or not
if row['elemoid']: if row['elemoid']:
if row['elemoid'] in (1560, 1561, 1562, 1563, 1042, 1043, length, precision, typeval = self.get_length_precision(row['elemoid'])
1014, 1015):
typeval = 'L'
elif row['elemoid'] in (1083, 1114, 1115, 1183, 1184, 1185,
1186, 1187, 1266, 1270):
typeval = 'D'
elif row['elemoid'] in (1231, 1700):
typeval = 'P'
else:
typeval = ' '
# Set precision & length/min/max values if length:
if typeval == 'P':
precision = True
if precision or typeval in ('L', 'D'):
length = True
min_val = 0 if typeval == 'D' else 1 min_val = 0 if typeval == 'D' else 1
if precision: if precision:
max_val = 1000 max_val = 1000
@ -143,6 +129,34 @@ class DataTypeReader:
return True, res return True, res
@staticmethod
def get_length_precision(elemoid):
precision = False
length = False
typeval = ''
# Check against PGOID for specific type
if elemoid:
if elemoid in (1560, 1561, 1562, 1563, 1042, 1043,
1014, 1015):
typeval = 'L'
elif elemoid in (1083, 1114, 1115, 1183, 1184, 1185,
1186, 1187, 1266, 1270):
typeval = 'D'
elif elemoid in (1231, 1700):
typeval = 'P'
else:
typeval = ' '
# Set precision & length/min/max values
if typeval == 'P':
precision = True
if precision or typeval in ('L', 'D'):
length = True
return length, precision, typeval
def get_full_type(self, nsp, typname, isDup, numdims, typmod): def get_full_type(self, nsp, typname, isDup, numdims, typmod):
""" """
Returns full type name with Length and Precision. Returns full type name with Length and Precision.