Skip to content

Commit

Permalink
use ctid as temporary feature id in postgres provider if there is no …
Browse files Browse the repository at this point in the history
…other primary key available

git-svn-id: http://svn.osgeo.org/qgis/trunk@10474 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
jef committed Apr 5, 2009
1 parent 2bfb80b commit f8277d9
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 33 deletions.
155 changes: 122 additions & 33 deletions src/providers/postgres/qgspostgresprovider.cpp
Expand Up @@ -450,9 +450,39 @@ bool QgsPostgresProvider::getFeature( PGresult *queryResult, int row, bool fetch
{ {
try try
{ {
int oid = *( int * )PQgetvalue( queryResult, row, 0 ); int oid;
if ( swapEndian )
oid = ntohl( oid ); // convert oid to opposite endian if ( primaryKeyType != "tid" )
{
oid = *( int * )PQgetvalue( queryResult, row, 0 );
if ( swapEndian )
oid = ntohl( oid ); // convert oid to opposite endian
}
else if ( PQgetlength( queryResult, row, 0 ) == 6 )
{
char *data = PQgetvalue( queryResult, row, 0 );
int block = *( int * )data;
int offset = *( short * )( data + sizeof( int ) );

if ( swapEndian )
{
block = ntohl( block );
offset = ntohs( offset );
}

if ( block > 0xffff )
{
qWarning( "block number %x exceed 16 bit", block );
return false;
}

oid = ( block << 16 ) + offset;
}
else
{
qWarning( "expecting 6 bytes for tid (found %d bytes)", PQgetlength( queryResult, row, 0 ) );
return false;
}


feature.setFeatureId( oid ); feature.setFeatureId( oid );


Expand Down Expand Up @@ -627,10 +657,23 @@ bool QgsPostgresProvider::nextFeature( QgsFeature& feature )
return true; return true;
} }


QString QgsPostgresProvider::whereClause( int featureId ) const
{
if ( primaryKeyType != "tid" )
{
return QString( "%1=%2" ).arg( quotedIdentifier( primaryKey ) ).arg( featureId );
}
else
{
return QString( "%1='(%2,%3)'" ).arg( quotedIdentifier( primaryKey ) ).arg( featureId >> 16 ).arg( featureId & 0xffff );
}
}

bool QgsPostgresProvider::featureAtId( int featureId, QgsFeature& feature, bool fetchGeometry, QgsAttributeList fetchAttributes ) bool QgsPostgresProvider::featureAtId( int featureId, QgsFeature& feature, bool fetchGeometry, QgsAttributeList fetchAttributes )
{ {
QString cursorName = QString( "qgisfid%1" ).arg( providerId ); QString cursorName = QString( "qgisfid%1" ).arg( providerId );
if ( !declareCursor( cursorName, fetchAttributes, fetchGeometry, QString( "%2=%3" ).arg( quotedIdentifier( primaryKey ) ).arg( featureId ) ) )
if ( !declareCursor( cursorName, fetchAttributes, fetchGeometry, whereClause( featureId ) ) )
return false; return false;


Result queryResult = connectionRO->PQexec( QString( "fetch forward 1 from %1" ).arg( cursorName ) ); Result queryResult = connectionRO->PQexec( QString( "fetch forward 1 from %1" ).arg( cursorName ) );
Expand All @@ -640,7 +683,7 @@ bool QgsPostgresProvider::featureAtId( int featureId, QgsFeature& feature, bool
int rows = PQntuples( queryResult ); int rows = PQntuples( queryResult );
if ( rows == 0 ) if ( rows == 0 )
{ {
QgsDebugMsg( "feature " + QString::number( featureId ) + " not found" ); QgsDebugMsg( QString( "feature %1 not found" ).arg( featureId ) );
connectionRO->closeCursor( cursorName ); connectionRO->closeCursor( cursorName );
return false; return false;
} }
Expand Down Expand Up @@ -916,12 +959,40 @@ QString QgsPostgresProvider::getPrimaryKey()
primaryKeyType = "int4"; primaryKeyType = "int4";
} }
else else
{
sql = QString( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" )
.arg( quotedValue( mSchemaTableName ) );

Result ctidCheck = connectionRO->PQexec( sql );

if ( PQntuples( ctidCheck ) == 1 )
{
sql = QString( "SELECT max(substring(ctid::text from E'\\\\((\\\\d+),\\\\d+\\\\)')::integer) from %1" )
.arg( mSchemaTableName );

Result ctidCheck = connectionRO->PQexec( sql );
if ( PQntuples( ctidCheck ) == 1 )
{
int id = QString( PQgetvalue( ctidCheck, 0, 0 ) ).toInt();

if ( id < 0x10000 )
{
// fallback to ctid
primaryKey = "ctid";
primaryKeyType = "tid";
}
}
}
}

if ( primaryKey.isEmpty() )
{ {
showMessageBox( tr( "No suitable key column in table" ), showMessageBox( tr( "No suitable key column in table" ),
tr( "The table has no column suitable for use as a key.\n\n" tr( "The table has no column suitable for use as a key.\n\n"
"Qgis requires that the table either has a column of type\n" "Qgis requires that the table either has a column of type\n"
"int4 with a unique constraint on it (which includes the\n" "int4 with a unique constraint on it (which includes the\n"
"primary key) or has a PostgreSQL oid column.\n" ) ); "primary key), has a PostgreSQL oid column or has a ctid\n"
"column with a 16bit block number.\n" ) );
} }
} }
else if ( type == "v" ) // the relation is a view else if ( type == "v" ) // the relation is a view
Expand Down Expand Up @@ -1739,14 +1810,25 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
connectionRW->PQexecNR( "BEGIN" ); connectionRW->PQexecNR( "BEGIN" );


// Prepare the INSERT statement // Prepare the INSERT statement
QString insert = QString( "INSERT INTO %1(%2,%3" ) QString insert = QString( "INSERT INTO %1(%2" )
.arg( mSchemaTableName ) .arg( mSchemaTableName )
.arg( quotedIdentifier( geometryColumn ) ) .arg( quotedIdentifier( geometryColumn ) ),
.arg( quotedIdentifier( primaryKey ) ), values = QString( ") VALUES (GeomFromWKB($1%1,%2)" )
values = QString( ") VALUES (GeomFromWKB($1%1,%2),$2" )
.arg( connectionRW->useWkbHex() ? "" : "::bytea" ) .arg( connectionRW->useWkbHex() ? "" : "::bytea" )
.arg( srid ); .arg( srid );


int offset;
if ( primaryKeyType != "tid" )
{
insert += "," + quotedIdentifier( primaryKey );
values += ",$2";
offset = 3;
}
else
{
offset = 2;
}

const QgsAttributeMap &attributevec = flist[0].attributeMap(); const QgsAttributeMap &attributevec = flist[0].attributeMap();


QStringList defaultValues; QStringList defaultValues;
Expand Down Expand Up @@ -1811,11 +1893,11 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
// value is not unique => add parameter // value is not unique => add parameter
if ( fit->typeName() == "geometry" ) if ( fit->typeName() == "geometry" )
{ {
values += QString( ",geomfromewkt($%1)" ).arg( defaultValues.size() + 3 ); values += QString( ",geomfromewkt($%1)" ).arg( defaultValues.size() + offset );
} }
else else
{ {
values += QString( ",$%1" ).arg( defaultValues.size() + 3 ); values += QString( ",$%1" ).arg( defaultValues.size() + offset );
} }
defaultValues.append( defVal ); defaultValues.append( defVal );
fieldId.append( it.key() ); fieldId.append( it.key() );
Expand All @@ -1825,7 +1907,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
insert += values + ")"; insert += values + ")";


QgsDebugMsg( QString( "prepare addfeatures: %1" ).arg( insert ) ); QgsDebugMsg( QString( "prepare addfeatures: %1" ).arg( insert ) );
PGresult *stmt = connectionRW->PQprepare( "addfeatures", insert, fieldId.size() + 2, NULL ); PGresult *stmt = connectionRW->PQprepare( "addfeatures", insert, fieldId.size() + offset - 1, NULL );
if ( stmt == 0 || PQresultStatus( stmt ) == PGRES_FATAL_ERROR ) if ( stmt == 0 || PQresultStatus( stmt ) == PGRES_FATAL_ERROR )
throw PGException( stmt ); throw PGException( stmt );
PQclear( stmt ); PQclear( stmt );
Expand All @@ -1846,17 +1928,20 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
QStringList params; QStringList params;
params << geomParam; params << geomParam;


if ( keyDefault.isNull() ) if ( primaryKeyType != "tid" )
{ {
++primaryKeyHighWater; if ( keyDefault.isNull() )
params << QString::number( primaryKeyHighWater ); {
newIds << primaryKeyHighWater; ++primaryKeyHighWater;
} params << QString::number( primaryKeyHighWater );
else newIds << primaryKeyHighWater;
{ }
QByteArray key = paramValue( keyDefault, keyDefault ); else
params << key; {
newIds << key.toInt(); QByteArray key = paramValue( keyDefault, keyDefault );
params << key;
newIds << key.toInt();
}
} }


for ( int i = 0; i < fieldId.size(); i++ ) for ( int i = 0; i < fieldId.size(); i++ )
Expand All @@ -1868,8 +1953,9 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
PQclear( result ); PQclear( result );
} }


for ( int i = 0; i < flist.size(); i++ ) if ( flist.size() == newIds.size() )
flist[i].setFeatureId( newIds[i] ); for ( int i = 0; i < flist.size(); i++ )
flist[i].setFeatureId( newIds[i] );


connectionRW->PQexecNR( "DEALLOCATE addfeatures" ); connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
connectionRW->PQexecNR( "COMMIT" ); connectionRW->PQexecNR( "COMMIT" );
Expand Down Expand Up @@ -1901,10 +1987,8 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds & id )


for ( QgsFeatureIds::const_iterator it = id.begin();it != id.end();++it ) for ( QgsFeatureIds::const_iterator it = id.begin();it != id.end();++it )
{ {
QString sql = QString( "DELETE FROM %1 WHERE %2=%3" ) QString sql = QString( "DELETE FROM %1 WHERE %2" )
.arg( mSchemaTableName ) .arg( mSchemaTableName ).arg( whereClause( *it ) );
.arg( quotedIdentifier( primaryKey ) )
.arg( *it );
QgsDebugMsg( "delete sql: " + sql ); QgsDebugMsg( "delete sql: " + sql );


//send DELETE statement and do error handling //send DELETE statement and do error handling
Expand Down Expand Up @@ -2059,9 +2143,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &
} }
} }


sql += QString( " WHERE %1=%2" ) sql += QString( " WHERE %1" ).arg( whereClause( fid ) );
.arg( quotedIdentifier( primaryKey ) )
.arg( fid );


PGresult *result = connectionRW->PQexec( sql ); PGresult *result = connectionRW->PQexec( sql );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR ) if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
Expand Down Expand Up @@ -2137,7 +2219,14 @@ bool QgsPostgresProvider::changeGeometryValues( QgsGeometryMap & geometry_map )


QStringList params; QStringList params;
params << geomParam; params << geomParam;
params << QString( "%1" ).arg( iter.key() ); if ( primaryKeyType != "tid" )
{
params << QString( "%1" ).arg( iter.key() );
}
else
{
params << QString( "(%1,%2)" ).arg( iter.key() >> 16 ).arg( iter.key() & 0xffff );
}


PGresult *result = connectionRW->PQexecPrepared( "updatefeatures", params ); PGresult *result = connectionRW->PQexecPrepared( "updatefeatures", params );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR ) if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
Expand Down
2 changes: 2 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Expand Up @@ -322,6 +322,8 @@ class QgsPostgresProvider : public QgsVectorDataProvider
QgsFeature &feature, QgsFeature &feature,
const QgsAttributeList &fetchAttributes ); const QgsAttributeList &fetchAttributes );


QString whereClause( int featureId ) const;

const QgsField &field( int index ) const; const QgsField &field( int index ) const;


/** Double quote a PostgreSQL identifier for placement in a SQL string. /** Double quote a PostgreSQL identifier for placement in a SQL string.
Expand Down

0 comments on commit f8277d9

Please sign in to comment.