Skip to content
Permalink
Browse files

[postgres] Improved handling of DOMAIN type fields

- show correct domain type as field type name
- correctly determine length and precision of domain types
- expose bpchar field type as 'character' to users, as postgres only
uses 'bpchar' internally and refers to bpchar as character in the
front end
  • Loading branch information
nyalldawson committed Apr 22, 2016
1 parent 647f322 commit fdd68963c717148dab1d1310110d84c855837c88
@@ -737,6 +737,7 @@ bool QgsPostgresProvider::loadFields()


QMap<int, QMap<int, QString> > fmtFieldTypeMap, descrMap, defValMap;
QMap<int, QMap<int, int> > attTypeIdMap;
if ( result.PQnfields() > 0 )
{
// Collect table oids
@@ -761,7 +762,7 @@ bool QgsPostgresProvider::loadFields()
QString tableoidsFilter = '(' + tableoidsList.join( "," ) + ')';

// Collect formatted field types
sql = "SELECT attrelid, attnum, pg_catalog.format_type(atttypid,atttypmod), pg_catalog.col_description(attrelid,attnum), pg_catalog.pg_get_expr(adbin,adrelid)"
sql = "SELECT attrelid, attnum, pg_catalog.format_type(atttypid,atttypmod), pg_catalog.col_description(attrelid,attnum), pg_catalog.pg_get_expr(adbin,adrelid), atttypid"
" FROM pg_attribute"
" LEFT OUTER JOIN pg_attrdef ON attrelid=adrelid AND attnum=adnum"
" WHERE attrelid IN " + tableoidsFilter;
@@ -773,9 +774,11 @@ bool QgsPostgresProvider::loadFields()
QString formatType = fmtFieldTypeResult.PQgetvalue( i, 2 );
QString descr = fmtFieldTypeResult.PQgetvalue( i, 3 );
QString defVal = fmtFieldTypeResult.PQgetvalue( i, 4 );
int attType = fmtFieldTypeResult.PQgetvalue( i, 5 ).toInt();
fmtFieldTypeMap[attrelid][attnum] = formatType;
descrMap[attrelid][attnum] = descr;
defValMap[attrelid][attnum] = defVal;
attTypeIdMap[attrelid][attnum] = attType;
}
}
}
@@ -789,16 +792,32 @@ bool QgsPostgresProvider::loadFields()
continue;

int fldtyp = result.PQftype( i );
int fldMod = result.PQfmod( i );
int fieldPrec = -1;
int tableoid = result.PQftable( i );
int attnum = result.PQftablecol( i );
int atttypid = attTypeIdMap[tableoid][attnum];

const PGTypeInfo& typeInfo = typeMap.value( fldtyp );
QString fieldTypeName = typeInfo.typeName;
QString fieldTType = typeInfo.typeType;
int fieldSize = typeInfo.typeLen;

bool isDomain = ( typeMap.value( atttypid ).typeType == "d" );

QString formattedFieldType = fmtFieldTypeMap[tableoid][attnum];
QString originalFormattedFieldType = formattedFieldType;
if ( isDomain )
{
// get correct formatted field type for domain
sql = QString( "SELECT format_type(%1, %2)" ).arg( fldtyp ).arg( fldMod );
QgsPostgresResult fmtFieldModResult( connectionRO()->PQexec( sql ) );
if ( fmtFieldModResult.PQntuples() > 0 )
{
formattedFieldType = fmtFieldModResult.PQgetvalue( 0, 0 );
}
}

QString fieldComment = descrMap[tableoid][attnum];

QVariant::Type fieldType;
@@ -904,6 +923,9 @@ bool QgsPostgresProvider::loadFields()
}
else if ( fieldTypeName == "bpchar" )
{
// although postgres internally uses "bpchar", this is exposed to users as character in postgres
fieldTypeName = "character";

fieldType = QVariant::String;

QRegExp re( "character\\((\\d+)\\)" );
@@ -971,6 +993,12 @@ bool QgsPostgresProvider::loadFields()

fields << fieldName;

if ( isDomain )
{
//field was defined using domain, so use domain type name for fieldTypeName
fieldTypeName = originalFormattedFieldType;
}

mAttrPalIndexName.insert( i, fieldName );
mDefaultValues.insert( mAttributeFields.size(), defValMap[tableoid][attnum] );
mAttributeFields.append( QgsField( fieldName, fieldType, fieldTypeName, fieldSize, fieldPrec, fieldComment ) );
@@ -206,5 +206,31 @@ def testNestedInsert(self):
self.vl.addFeature(f) # Should not deadlock during an active iteration
f = next(it)

def testDomainTypes(self):
"""Test that domain types are correctly mapped"""

vl = QgsVectorLayer('%s table="qgis_test"."domains" sql=' % (self.dbconn), "domains", "postgres")
self.assertTrue(vl.isValid())

fields = vl.dataProvider().fields()

expected = {}
expected['fld_var_char_domain'] = {'type': QVariant.String, 'typeName': 'qgis_test.var_char_domain', 'length': -1}
expected['fld_var_char_domain_6'] = {'type': QVariant.String, 'typeName': 'qgis_test.var_char_domain_6', 'length': 6}
expected['fld_character_domain'] = {'type': QVariant.String, 'typeName': 'qgis_test.character_domain', 'length': 1}
expected['fld_character_domain_6'] = {'type': QVariant.String, 'typeName': 'qgis_test.character_domain_6', 'length': 6}
expected['fld_char_domain'] = {'type': QVariant.String, 'typeName': 'qgis_test.char_domain', 'length': 1}
expected['fld_char_domain_6'] = {'type': QVariant.String, 'typeName': 'qgis_test.char_domain_6', 'length': 6}
expected['fld_text_domain'] = {'type': QVariant.String, 'typeName': 'qgis_test.text_domain', 'length': -1}
expected['fld_numeric_domain'] = {'type': QVariant.Double, 'typeName': 'qgis_test.numeric_domain', 'length': 10, 'precision': 4}

for f, e in expected.iteritems():
self.assertEqual(fields.at(fields.indexFromName(f)).type(), e['type'])
self.assertEqual(fields.at(fields.indexFromName(f)).typeName(), e['typeName'])
self.assertEqual(fields.at(fields.indexFromName(f)).length(), e['length'])
if 'precision' in e:
self.assertEqual(fields.at(fields.indexFromName(f)).precision(), e['precision'])


if __name__ == '__main__':
unittest.main()
@@ -357,3 +357,44 @@ CREATE TABLE qgis_test.oid_serial_table
);

ALTER TABLE qgis_test.oid_serial_table ALTER COLUMN obj_id SET DEFAULT 'prf_' || nextval('qgis_test.oid_serial');


--------------------------------------
-- Test use of domain types
--

CREATE DOMAIN qgis_test.var_char_domain
AS character varying;

CREATE DOMAIN qgis_test.var_char_domain_6
AS character varying(6);

CREATE DOMAIN qgis_test.character_domain
AS character;

CREATE DOMAIN qgis_test.character_domain_6
AS character(6);

CREATE DOMAIN qgis_test.char_domain
AS char;

CREATE DOMAIN qgis_test.char_domain_6
AS char(6);

CREATE DOMAIN qgis_test.text_domain
AS text;

CREATE DOMAIN qgis_test.numeric_domain
AS numeric(10,4);

CREATE TABLE qgis_test.domains
(
fld_var_char_domain qgis_test.var_char_domain,
fld_var_char_domain_6 qgis_test.var_char_domain_6,
fld_character_domain qgis_test.character_domain,
fld_character_domain_6 qgis_test.character_domain_6,
fld_char_domain qgis_test.char_domain,
fld_char_domain_6 qgis_test.char_domain_6,
fld_text_domain qgis_test.text_domain,
fld_numeric_domain qgis_test.numeric_domain
);

0 comments on commit fdd6896

Please sign in to comment.
You can’t perform that action at this time.