Skip to content

Commit

Permalink
Rename attributes support for postgres provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 2, 2016
1 parent 251474a commit ccc67ef
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 8 deletions.
62 changes: 61 additions & 1 deletion src/providers/postgres/qgspostgresprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ bool QgsPostgresProvider::hasSufficientPermsAndCapabilities()
testAccess = connectionRO()->PQexec( sql );
if ( testAccess.PQresultStatus() == PGRES_TUPLES_OK && testAccess.PQntuples() == 1 )
{
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes;
mEnabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes | QgsVectorDataProvider::RenameAttributes;
}
}
}
Expand Down Expand Up @@ -2251,6 +2251,66 @@ bool QgsPostgresProvider::deleteAttributes( const QgsAttributeIds& ids )
return returnvalue;
}

bool QgsPostgresProvider::renameAttributes( const QgsFieldNameMap& renamedAttributes )
{
if ( mIsQuery )
return false;


QString sql = "BEGIN;";

QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
bool returnvalue = true;
for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
{
int fieldIndex = renameIt.key();
if ( fieldIndex < 0 || fieldIndex >= mAttributeFields.count() )
{
pushError( tr( "Invalid attribute index: %1" ).arg( fieldIndex ) );
return false;
}
if ( mAttributeFields.indexFromName( renameIt.value() ) >= 0 )
{
//field name already in use
pushError( tr( "Error renaming field %1: name '%2' already exists" ).arg( fieldIndex ).arg( renameIt.value() ) );
return false;
}

sql += QString( "ALTER TABLE %1 RENAME COLUMN %2 TO %3;" )
.arg( mQuery,
quotedIdentifier( mAttributeFields.at( fieldIndex ).name() ),
quotedIdentifier( renameIt.value() ) );
}
sql += "COMMIT;";

QgsPostgresConn* conn = connectionRW();
if ( !conn )
{
return false;
}
conn->lock();

try
{
conn->begin();
//send sql statement and do error handling
QgsPostgresResult result( conn->PQexec( sql ) );
if ( result.PQresultStatus() != PGRES_COMMAND_OK )
throw PGException( result );
returnvalue = conn->commit();
}
catch ( PGException &e )
{
pushError( tr( "PostGIS error while renaming attributes: %1" ).arg( e.errorMessage() ) );
conn->rollback();
returnvalue = false;
}

loadFields();
conn->unlock();
return returnvalue;
}

bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{
bool returnvalue = true;
Expand Down
8 changes: 1 addition & 7 deletions src/providers/postgres/qgspostgresprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,9 @@ class QgsPostgresProvider : public QgsVectorDataProvider
@return true in case of success and false in case of failure*/
bool deleteFeatures( const QgsFeatureIds & id ) override;

/** Adds new attributes
@param name map with attribute name as key and type as value
@return true in case of success and false in case of failure*/
bool addAttributes( const QList<QgsField> &attributes ) override;

/** Deletes existing attributes
@param names of the attributes to delete
@return true in case of success and false in case of failure*/
bool deleteAttributes( const QgsAttributeIds &name ) override;
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes ) override;

/** Changes attribute values of existing features
@param attr_map a map containing the new attributes. The integer is the feature id,
Expand Down
38 changes: 38 additions & 0 deletions tests/src/python/test_provider_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,44 @@ def testDomainTypes(self):
if 'precision' in e:
self.assertEqual(fields.at(fields.indexFromName(f)).precision(), e['precision'])

def testRenameAttributes(self):
''' Test renameAttributes() '''
vl = QgsVectorLayer('%s table="qgis_test"."rename_table" sql=' % (self.dbconn), "renames", "postgres")
provider = vl.dataProvider()
provider.renameAttributes({1: 'field1', 2: 'field2'})

# bad rename
self.assertFalse(provider.renameAttributes({-1: 'not_a_field'}))
self.assertFalse(provider.renameAttributes({100: 'not_a_field'}))
# already exists
self.assertFalse(provider.renameAttributes({1: 'field2'}))

# rename one field
self.assertTrue(provider.renameAttributes({1: 'newname'}))
self.assertEqual(provider.fields().at(1).name(), 'newname')
vl.updateFields()
fet = next(vl.getFeatures())
self.assertEqual(fet.fields()[1].name(), 'newname')

# rename two fields
self.assertTrue(provider.renameAttributes({1: 'newname2', 2: 'another'}))
self.assertEqual(provider.fields().at(1).name(), 'newname2')
self.assertEqual(provider.fields().at(2).name(), 'another')
vl.updateFields()
fet = next(vl.getFeatures())
self.assertEqual(fet.fields()[1].name(), 'newname2')
self.assertEqual(fet.fields()[2].name(), 'another')

# close layer and reopen, then recheck to confirm that changes were saved to db
del vl
vl = None
vl = QgsVectorLayer('%s table="qgis_test"."rename_table" sql=' % (self.dbconn), "renames", "postgres")
provider = vl.dataProvider()
self.assertEqual(provider.fields().at(1).name(), 'newname2')
self.assertEqual(provider.fields().at(2).name(), 'another')
fet = next(vl.getFeatures())
self.assertEqual(fet.fields()[1].name(), 'newname2')
self.assertEqual(fet.fields()[2].name(), 'another')

if __name__ == '__main__':
unittest.main()
14 changes: 14 additions & 0 deletions tests/testdata/provider/testdata_pg.sql
Original file line number Diff line number Diff line change
Expand Up @@ -398,3 +398,17 @@ CREATE TABLE qgis_test.domains
fld_text_domain qgis_test.text_domain,
fld_numeric_domain qgis_test.numeric_domain
);


--------------------------------------
-- Temporary table for testing renaming fields
--

CREATE TABLE qgis_test.rename_table
(
gid serial NOT NULL,
field1 text,
field2 text
);

INSERT INTO qgis_test.rename_table (field1,field2) VALUES ('a','b');

0 comments on commit ccc67ef

Please sign in to comment.