@@ -450,9 +450,39 @@ bool QgsPostgresProvider::getFeature( PGresult *queryResult, int row, bool fetch
450450{
451451 try
452452 {
453- int oid = *( int * )PQgetvalue ( queryResult, row, 0 );
454- if ( swapEndian )
455- oid = ntohl ( oid ); // convert oid to opposite endian
453+ int oid;
454+
455+ if ( primaryKeyType != " tid" )
456+ {
457+ oid = *( int * )PQgetvalue ( queryResult, row, 0 );
458+ if ( swapEndian )
459+ oid = ntohl ( oid ); // convert oid to opposite endian
460+ }
461+ else if ( PQgetlength ( queryResult, row, 0 ) == 6 )
462+ {
463+ char *data = PQgetvalue ( queryResult, row, 0 );
464+ int block = *( int * )data;
465+ int offset = *( short * )( data + sizeof ( int ) );
466+
467+ if ( swapEndian )
468+ {
469+ block = ntohl ( block );
470+ offset = ntohs ( offset );
471+ }
472+
473+ if ( block > 0xffff )
474+ {
475+ qWarning ( " block number %x exceed 16 bit" , block );
476+ return false ;
477+ }
478+
479+ oid = ( block << 16 ) + offset;
480+ }
481+ else
482+ {
483+ qWarning ( " expecting 6 bytes for tid (found %d bytes)" , PQgetlength ( queryResult, row, 0 ) );
484+ return false ;
485+ }
456486
457487 feature.setFeatureId ( oid );
458488
@@ -627,10 +657,23 @@ bool QgsPostgresProvider::nextFeature( QgsFeature& feature )
627657 return true ;
628658}
629659
660+ QString QgsPostgresProvider::whereClause ( int featureId ) const
661+ {
662+ if ( primaryKeyType != " tid" )
663+ {
664+ return QString ( " %1=%2" ).arg ( quotedIdentifier ( primaryKey ) ).arg ( featureId );
665+ }
666+ else
667+ {
668+ return QString ( " %1='(%2,%3)'" ).arg ( quotedIdentifier ( primaryKey ) ).arg ( featureId >> 16 ).arg ( featureId & 0xffff );
669+ }
670+ }
671+
630672bool QgsPostgresProvider::featureAtId ( int featureId, QgsFeature& feature, bool fetchGeometry, QgsAttributeList fetchAttributes )
631673{
632674 QString cursorName = QString ( " qgisfid%1" ).arg ( providerId );
633- if ( !declareCursor ( cursorName, fetchAttributes, fetchGeometry, QString ( " %2=%3" ).arg ( quotedIdentifier ( primaryKey ) ).arg ( featureId ) ) )
675+
676+ if ( !declareCursor ( cursorName, fetchAttributes, fetchGeometry, whereClause ( featureId ) ) )
634677 return false ;
635678
636679 Result queryResult = connectionRO->PQexec ( QString ( " fetch forward 1 from %1" ).arg ( cursorName ) );
@@ -640,7 +683,7 @@ bool QgsPostgresProvider::featureAtId( int featureId, QgsFeature& feature, bool
640683 int rows = PQntuples ( queryResult );
641684 if ( rows == 0 )
642685 {
643- QgsDebugMsg ( " feature " + QString::number ( featureId ) + " not found " );
686+ QgsDebugMsg ( QString ( " feature %1 not found " ). arg ( featureId ) );
644687 connectionRO->closeCursor ( cursorName );
645688 return false ;
646689 }
@@ -916,12 +959,40 @@ QString QgsPostgresProvider::getPrimaryKey()
916959 primaryKeyType = " int4" ;
917960 }
918961 else
962+ {
963+ sql = QString ( " SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" )
964+ .arg ( quotedValue ( mSchemaTableName ) );
965+
966+ Result ctidCheck = connectionRO->PQexec ( sql );
967+
968+ if ( PQntuples ( ctidCheck ) == 1 )
969+ {
970+ sql = QString ( " SELECT max(substring(ctid::text from E'\\\\ ((\\\\ d+),\\\\ d+\\\\ )')::integer) from %1" )
971+ .arg ( mSchemaTableName );
972+
973+ Result ctidCheck = connectionRO->PQexec ( sql );
974+ if ( PQntuples ( ctidCheck ) == 1 )
975+ {
976+ int id = QString ( PQgetvalue ( ctidCheck, 0 , 0 ) ).toInt ();
977+
978+ if ( id < 0x10000 )
979+ {
980+ // fallback to ctid
981+ primaryKey = " ctid" ;
982+ primaryKeyType = " tid" ;
983+ }
984+ }
985+ }
986+ }
987+
988+ if ( primaryKey.isEmpty () )
919989 {
920990 showMessageBox ( tr ( " No suitable key column in table" ),
921991 tr ( " The table has no column suitable for use as a key.\n\n "
922992 " Qgis requires that the table either has a column of type\n "
923993 " int4 with a unique constraint on it (which includes the\n "
924- " primary key) or has a PostgreSQL oid column.\n " ) );
994+ " primary key), has a PostgreSQL oid column or has a ctid\n "
995+ " column with a 16bit block number.\n " ) );
925996 }
926997 }
927998 else if ( type == " v" ) // the relation is a view
@@ -1739,14 +1810,25 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
17391810 connectionRW->PQexecNR ( " BEGIN" );
17401811
17411812 // Prepare the INSERT statement
1742- QString insert = QString ( " INSERT INTO %1(%2,%3 " )
1813+ QString insert = QString ( " INSERT INTO %1(%2" )
17431814 .arg ( mSchemaTableName )
1744- .arg ( quotedIdentifier ( geometryColumn ) )
1745- .arg ( quotedIdentifier ( primaryKey ) ),
1746- values = QString ( " ) VALUES (GeomFromWKB($1%1,%2),$2" )
1815+ .arg ( quotedIdentifier ( geometryColumn ) ),
1816+ values = QString ( " ) VALUES (GeomFromWKB($1%1,%2)" )
17471817 .arg ( connectionRW->useWkbHex () ? " " : " ::bytea" )
17481818 .arg ( srid );
17491819
1820+ int offset;
1821+ if ( primaryKeyType != " tid" )
1822+ {
1823+ insert += " ," + quotedIdentifier ( primaryKey );
1824+ values += " ,$2" ;
1825+ offset = 3 ;
1826+ }
1827+ else
1828+ {
1829+ offset = 2 ;
1830+ }
1831+
17501832 const QgsAttributeMap &attributevec = flist[0 ].attributeMap ();
17511833
17521834 QStringList defaultValues;
@@ -1811,11 +1893,11 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
18111893 // value is not unique => add parameter
18121894 if ( fit->typeName () == " geometry" )
18131895 {
1814- values += QString ( " ,geomfromewkt($%1)" ).arg ( defaultValues.size () + 3 );
1896+ values += QString ( " ,geomfromewkt($%1)" ).arg ( defaultValues.size () + offset );
18151897 }
18161898 else
18171899 {
1818- values += QString ( " ,$%1" ).arg ( defaultValues.size () + 3 );
1900+ values += QString ( " ,$%1" ).arg ( defaultValues.size () + offset );
18191901 }
18201902 defaultValues.append ( defVal );
18211903 fieldId.append ( it.key () );
@@ -1825,7 +1907,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
18251907 insert += values + " )" ;
18261908
18271909 QgsDebugMsg ( QString ( " prepare addfeatures: %1" ).arg ( insert ) );
1828- PGresult *stmt = connectionRW->PQprepare ( " addfeatures" , insert, fieldId.size () + 2 , NULL );
1910+ PGresult *stmt = connectionRW->PQprepare ( " addfeatures" , insert, fieldId.size () + offset - 1 , NULL );
18291911 if ( stmt == 0 || PQresultStatus ( stmt ) == PGRES_FATAL_ERROR )
18301912 throw PGException ( stmt );
18311913 PQclear ( stmt );
@@ -1846,17 +1928,20 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
18461928 QStringList params;
18471929 params << geomParam;
18481930
1849- if ( keyDefault. isNull () )
1931+ if ( primaryKeyType != " tid " )
18501932 {
1851- ++primaryKeyHighWater;
1852- params << QString::number ( primaryKeyHighWater );
1853- newIds << primaryKeyHighWater;
1854- }
1855- else
1856- {
1857- QByteArray key = paramValue ( keyDefault, keyDefault );
1858- params << key;
1859- newIds << key.toInt ();
1933+ if ( keyDefault.isNull () )
1934+ {
1935+ ++primaryKeyHighWater;
1936+ params << QString::number ( primaryKeyHighWater );
1937+ newIds << primaryKeyHighWater;
1938+ }
1939+ else
1940+ {
1941+ QByteArray key = paramValue ( keyDefault, keyDefault );
1942+ params << key;
1943+ newIds << key.toInt ();
1944+ }
18601945 }
18611946
18621947 for ( int i = 0 ; i < fieldId.size (); i++ )
@@ -1868,8 +1953,9 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList & flist )
18681953 PQclear ( result );
18691954 }
18701955
1871- for ( int i = 0 ; i < flist.size (); i++ )
1872- flist[i].setFeatureId ( newIds[i] );
1956+ if ( flist.size () == newIds.size () )
1957+ for ( int i = 0 ; i < flist.size (); i++ )
1958+ flist[i].setFeatureId ( newIds[i] );
18731959
18741960 connectionRW->PQexecNR ( " DEALLOCATE addfeatures" );
18751961 connectionRW->PQexecNR ( " COMMIT" );
@@ -1901,10 +1987,8 @@ bool QgsPostgresProvider::deleteFeatures( const QgsFeatureIds & id )
19011987
19021988 for ( QgsFeatureIds::const_iterator it = id.begin ();it != id.end ();++it )
19031989 {
1904- QString sql = QString ( " DELETE FROM %1 WHERE %2=%3" )
1905- .arg ( mSchemaTableName )
1906- .arg ( quotedIdentifier ( primaryKey ) )
1907- .arg ( *it );
1990+ QString sql = QString ( " DELETE FROM %1 WHERE %2" )
1991+ .arg ( mSchemaTableName ).arg ( whereClause ( *it ) );
19081992 QgsDebugMsg ( " delete sql: " + sql );
19091993
19101994 // send DELETE statement and do error handling
@@ -2059,9 +2143,7 @@ bool QgsPostgresProvider::changeAttributeValues( const QgsChangedAttributesMap &
20592143 }
20602144 }
20612145
2062- sql += QString ( " WHERE %1=%2" )
2063- .arg ( quotedIdentifier ( primaryKey ) )
2064- .arg ( fid );
2146+ sql += QString ( " WHERE %1" ).arg ( whereClause ( fid ) );
20652147
20662148 PGresult *result = connectionRW->PQexec ( sql );
20672149 if ( result == 0 || PQresultStatus ( result ) == PGRES_FATAL_ERROR )
@@ -2137,7 +2219,14 @@ bool QgsPostgresProvider::changeGeometryValues( QgsGeometryMap & geometry_map )
21372219
21382220 QStringList params;
21392221 params << geomParam;
2140- params << QString ( " %1" ).arg ( iter.key () );
2222+ if ( primaryKeyType != " tid" )
2223+ {
2224+ params << QString ( " %1" ).arg ( iter.key () );
2225+ }
2226+ else
2227+ {
2228+ params << QString ( " (%1,%2)" ).arg ( iter.key () >> 16 ).arg ( iter.key () & 0xffff );
2229+ }
21412230
21422231 PGresult *result = connectionRW->PQexecPrepared ( " updatefeatures" , params );
21432232 if ( result == 0 || PQresultStatus ( result ) == PGRES_FATAL_ERROR )
0 commit comments