[QGIS Commit] r10474 - trunk/qgis/src/providers/postgres

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Apr 5 06:51:36 EDT 2009


Author: jef
Date: 2009-04-05 06:51:35 -0400 (Sun, 05 Apr 2009)
New Revision: 10474

Modified:
   trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
   trunk/qgis/src/providers/postgres/qgspostgresprovider.h
Log:
use ctid as temporary feature id in postgres provider if there is no other primary key available

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2009-04-05 10:46:08 UTC (rev 10473)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.cpp	2009-04-05 10:51:35 UTC (rev 10474)
@@ -450,10 +450,40 @@
 {
   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 );
 
     int col;  // first attribute column after geometry
@@ -627,10 +657,23 @@
   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 ) );
@@ -640,7 +683,7 @@
   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;
   }
@@ -917,11 +960,39 @@
       }
       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
@@ -1739,14 +1810,25 @@
     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;
@@ -1811,11 +1893,11 @@
         // 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() );
@@ -1825,7 +1907,7 @@
     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 );
@@ -1846,18 +1928,21 @@
       QStringList params;
       params << geomParam;
 
-      if ( keyDefault.isNull() )
+      if ( primaryKeyType != "tid" )
       {
-        ++primaryKeyHighWater;
-        params << QString::number( primaryKeyHighWater );
-        newIds << primaryKeyHighWater;
+        if ( keyDefault.isNull() )
+        {
+          ++primaryKeyHighWater;
+          params << QString::number( primaryKeyHighWater );
+          newIds << primaryKeyHighWater;
+        }
+        else
+        {
+          QByteArray key = paramValue( keyDefault, keyDefault );
+          params << key;
+          newIds << key.toInt();
+        }
       }
-      else
-      {
-        QByteArray key = paramValue( keyDefault, keyDefault );
-        params << key;
-        newIds << key.toInt();
-      }
 
       for ( int i = 0; i < fieldId.size(); i++ )
         params << paramValue( attributevec[ fieldId[i] ].toString(), defaultValues[i] );
@@ -1868,8 +1953,9 @@
       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" );
@@ -1901,10 +1987,8 @@
 
     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
@@ -2059,9 +2143,7 @@
         }
       }
 
-      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 )
@@ -2137,7 +2219,14 @@
 
         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 )

Modified: trunk/qgis/src/providers/postgres/qgspostgresprovider.h
===================================================================
--- trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2009-04-05 10:46:08 UTC (rev 10473)
+++ trunk/qgis/src/providers/postgres/qgspostgresprovider.h	2009-04-05 10:51:35 UTC (rev 10474)
@@ -322,6 +322,8 @@
                      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.



More information about the QGIS-commit mailing list