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
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,39 @@ bool QgsPostgresProvider::getFeature( PGresult *queryResult, int row, bool fetch
{
try
{
int oid = *( int * )PQgetvalue( queryResult, row, 0 );
if ( swapEndian )
oid = ntohl( oid ); // convert oid to opposite endian
int oid;

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 );

Expand Down Expand Up @@ -627,10 +657,23 @@ bool QgsPostgresProvider::nextFeature( QgsFeature& feature )
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 )
{
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;

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 );
if ( rows == 0 )
{
QgsDebugMsg( "feature " + QString::number( featureId ) + " not found" );
QgsDebugMsg( QString( "feature %1 not found" ).arg( featureId ) );
connectionRO->closeCursor( cursorName );
return false;
}
Expand Down Expand Up @@ -916,12 +959,40 @@ QString QgsPostgresProvider::getPrimaryKey()
primaryKeyType = "int4";
}
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" ),
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"
"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
Expand Down Expand Up @@ -1739,14 +1810,25 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
connectionRW->PQexecNR( "BEGIN" );

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

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

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

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

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 )
throw PGException( stmt );
PQclear( stmt );
Expand All @@ -1846,17 +1928,20 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
QStringList params;
params << geomParam;

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

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

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

connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
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 )
{
QString sql = QString( "DELETE FROM %1 WHERE %2=%3" )
.arg( mSchemaTableName )
.arg( quotedIdentifier( primaryKey ) )
.arg( *it );
QString sql = QString( "DELETE FROM %1 WHERE %2" )
.arg( mSchemaTableName ).arg( whereClause( *it ) );
QgsDebugMsg( "delete sql: " + sql );

//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" )
.arg( quotedIdentifier( primaryKey ) )
.arg( fid );
sql += QString( " WHERE %1" ).arg( whereClause( fid ) );

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

QStringList params;
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 );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
Expand Down
2 changes: 2 additions & 0 deletions src/providers/postgres/qgspostgresprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class QgsPostgresProvider : public QgsVectorDataProvider
QgsFeature &feature,
const QgsAttributeList &fetchAttributes );

QString whereClause( int featureId ) const;

const QgsField &field( int index ) const;

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

0 comments on commit f8277d9

Please sign in to comment.