Пример #1
0
void QgsPostgresConn::deduceEndian()
{
  // need to store the PostgreSQL endian format used in binary cursors
  // since it appears that starting with
  // version 7.4, binary cursors return data in XDR whereas previous versions
  // return data in the endian of the server

  QgsPostgresResult res = PQexec( "select regclass('pg_class')::oid" );
  QString oidValue = res.PQgetvalue( 0, 0 );

  QgsDebugMsg( "Creating binary cursor" );

  // get the same value using a binary cursor
  openCursor( "oidcursor", "select regclass('pg_class')::oid" );

  QgsDebugMsg( "Fetching a record and attempting to get check endian-ness" );

  res = PQexec( "fetch forward 1 from oidcursor" );

  mSwapEndian = true;
  if ( res.PQntuples() > 0 )
  {
    // get the oid value from the binary cursor
    qint64 oid = getBinaryInt( res, 0, 0 );

    QgsDebugMsg( QString( "Got oid of %1 from the binary cursor" ).arg( oid ) );
    QgsDebugMsg( QString( "First oid is %1" ).arg( oidValue ) );

    // compare the two oid values to determine if we need to do an endian swap
    if ( oid != oidValue.toLongLong() )
      mSwapEndian = false;
  }

  closeCursor( "oidcursor" );
}
Пример #2
0
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
{
  feature.setValid( false );

  if ( mClosed )
    return false;

  if ( mFeatureQueue.empty() )
  {
    QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
    QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
    if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
    {
      QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
    }

    QgsPostgresResult queryResult;
    for ( ;; )
    {
      queryResult = mConn->PQgetResult();
      if ( !queryResult.result() )
        break;

      if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK )
      {
        QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
        break;
      }

      int rows = queryResult.PQntuples();
      if ( rows == 0 )
        continue;

      for ( int row = 0; row < rows; row++ )
      {
        mFeatureQueue.enqueue( QgsFeature() );
        getFeature( queryResult, row, mFeatureQueue.back() );
      } // for each row in queue
    }
  }

  if ( mFeatureQueue.empty() )
  {
    QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) );
    close();

    mSource->mShared->ensureFeaturesCountedAtLeast( mFetched );

    return false;
  }

  feature = mFeatureQueue.dequeue();
  mFetched++;

  feature.setValid( true );
  feature.setFields( mSource->mFields ); // allow name-based attribute lookups

  return true;
}
Пример #3
0
bool QgsPostgresConn::PQexecNR( QString query, bool retry )
{
  QgsPostgresResult res = PQexec( query, false );

  ExecStatusType errorStatus = res.PQresultStatus();
  if ( errorStatus == PGRES_COMMAND_OK )
    return true;

  QgsMessageLog::logMessage( tr( "Query: %1 returned %2 [%3]" )
                             .arg( query )
                             .arg( errorStatus )
                             .arg( res.PQresultErrorMessage() ),
                             tr( "PostGIS" ) );

  if ( mOpenCursors )
  {
    QgsMessageLog::logMessage( tr( "%1 cursor states lost.\nSQL: %2\nResult: %3 (%4)" )
                               .arg( mOpenCursors ).arg( query ).arg( errorStatus )
                               .arg( res.PQresultErrorMessage() ), tr( "PostGIS" ) );
    mOpenCursors = 0;
  }

  if ( PQstatus() == CONNECTION_OK )
  {
    PQexecNR( "ROLLBACK" );
  }
  else if ( retry )
  {
    QgsMessageLog::logMessage( tr( "resetting bad connection." ), tr( "PostGIS" ) );
    ::PQreset( mConn );
    if ( PQstatus() == CONNECTION_OK )
    {
      if ( PQexecNR( query, false ) )
      {
        QgsMessageLog::logMessage( tr( "retry after reset succeeded." ), tr( "PostGIS" ) );
        return true;
      }
      else
      {
        QgsMessageLog::logMessage( tr( "retry after reset failed again." ), tr( "PostGIS" ) );
        return false;
      }
    }
    else
    {
      QgsMessageLog::logMessage( tr( "connection still bad after reset." ), tr( "PostGIS" ) );
    }
  }
  else
  {
    QgsMessageLog::logMessage( tr( "bad connection, not retrying." ), tr( "PostGIS" ) );
  }

  return false;
}
Пример #4
0
bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg )
{
  if ( !mConn )
  {
    return false;
  }

  QgsDebugMsg( QString( "Transaction sql: %1" ).arg( sql ) );
  mConn->lock();
  QgsPostgresResult r = mConn->PQexec( sql, true );
  mConn->unlock();
  if ( r.PQresultStatus() != PGRES_COMMAND_OK )
  {
    errorMsg = QString( "Status %1 (%2)" ).arg( r.PQresultStatus() ).arg( r.PQresultErrorMessage() );
    QgsDebugMsg( errorMsg );
    return false;
  }
  QgsDebugMsg( QString( "Status %1 (OK)" ).arg( r.PQresultStatus() ) );
  return true;
}
Пример #5
0
QStringList QgsPostgresConn::pkCandidates( QString schemaName, QString viewName )
{
  QStringList cols;

  QString sql = QString( "SELECT attname FROM pg_attribute JOIN pg_type ON atttypid=pg_type.oid WHERE pg_type.typname IN ('int2','int4','int8','oid','serial','serial8') AND attrelid=regclass('%1.%2')" )
                .arg( quotedIdentifier( schemaName ) )
                .arg( quotedIdentifier( viewName ) );
  QgsDebugMsg( sql );
  QgsPostgresResult colRes = PQexec( sql );

  if ( colRes.PQresultStatus() == PGRES_TUPLES_OK )
  {
    for ( int i = 0; i < colRes.PQntuples(); i++ )
    {
      QgsDebugMsg( colRes.PQgetvalue( i, 0 ) );
      cols << colRes.PQgetvalue( i, 0 );
    }
  }
  else
  {
    QgsMessageLog::logMessage( tr( "SQL:%1\nresult:%2\nerror:%3\n" ).arg( sql ).arg( colRes.PQresultStatus() ).arg( colRes.PQresultErrorMessage() ), tr( "PostGIS" ) );
  }

  return cols;
}
bool QgsPostgresFeatureIterator::nextFeature( QgsFeature& feature )
{
  feature.setValid( false );

  if ( mClosed )
    return false;

#if 0
  // featureAtId used to have some special checks - necessary?
  if ( !mUseQueue )
  {
    QgsPostgresResult queryResult = P->mConnectionRO->PQexec( QString( "FETCH FORWARD 1 FROM %1" ).arg( mCursorName ) );

    int rows = queryResult.PQntuples();
    if ( rows == 0 )
    {
      QgsMessageLog::logMessage( tr( "feature %1 not found" ).arg( featureId ), tr( "PostGIS" ) );
      P->mConnectionRO->closeCursor( cursorName );
      return false;
    }
    else if ( rows != 1 )
    {
      QgsMessageLog::logMessage( tr( "found %1 features instead of just one." ).arg( rows ), tr( "PostGIS" ) );
    }

    bool gotit = getFeature( queryResult, 0, feature );

    feature.setValid( gotit );
    feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups
    return gotit;
  }
#endif

  if ( mFeatureQueue.empty() )
  {
    QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
    QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
    if ( P->mConnectionRO->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
    {
      QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
    }

    QgsPostgresResult queryResult;
    for ( ;; )
    {
      queryResult = P->mConnectionRO->PQgetResult();
      if ( !queryResult.result() )
        break;

      if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK )
      {
        QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
        break;
      }

      int rows = queryResult.PQntuples();
      if ( rows == 0 )
        continue;

      for ( int row = 0; row < rows; row++ )
      {
        mFeatureQueue.enqueue( QgsFeature() );
        getFeature( queryResult, row, mFeatureQueue.back() );
      } // for each row in queue
    }
  }

  if ( mFeatureQueue.empty() )
  {
    QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) );
    close();

    if ( P->mFeaturesCounted < mFetched )
    {
      QgsDebugMsg( QString( "feature count adjusted from %1 to %2" ).arg( P->mFeaturesCounted ).arg( mFetched ) );
      P->mFeaturesCounted = mFetched;
    }
    return false;
  }

  // Now return the next feature from the queue
  if ( mRequest.flags() & QgsFeatureRequest::NoGeometry )
  {
    feature.setGeometryAndOwnership( 0, 0 );
  }
  else
  {
    QgsGeometry* featureGeom = mFeatureQueue.front().geometryAndOwnership();
    feature.setGeometry( featureGeom );
  }
  feature.setFeatureId( mFeatureQueue.front().id() );
  feature.setAttributes( mFeatureQueue.front().attributes() );

  mFeatureQueue.dequeue();
  mFetched++;

  feature.setValid( true );
  feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups
  return true;
}
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
  try
  {
    feature.initAttributes( P->fields().count() );

    int col = 0;

    if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
    {
      int returnedLength = ::PQgetlength( queryResult.result(), row, col );
      if ( returnedLength > 0 )
      {
        unsigned char *featureGeom = new unsigned char[returnedLength + 1];
        memset( featureGeom, 0, returnedLength + 1 );
        memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
        feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
      }
      else
      {
        feature.setGeometryAndOwnership( 0, 0 );
      }

      col++;
    }

    QgsFeatureId fid = 0;

    bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
    const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

    switch ( P->mPrimaryKeyType )
    {
      case QgsPostgresProvider::pktOid:
      case QgsPostgresProvider::pktTid:
      case QgsPostgresProvider::pktInt:
        fid = P->mConnectionRO->getBinaryInt( queryResult, row, col++ );
        if ( P->mPrimaryKeyType == QgsPostgresProvider::pktInt &&
             ( !subsetOfAttributes || fetchAttributes.contains( P->mPrimaryKeyAttrs[0] ) ) )
          feature.setAttribute( P->mPrimaryKeyAttrs[0], fid );
        break;

      case QgsPostgresProvider::pktFidMap:
      {
        QList<QVariant> primaryKeyVals;

        foreach ( int idx, P->mPrimaryKeyAttrs )
        {
          const QgsField &fld = P->field( idx );

          QVariant v = P->convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
          primaryKeyVals << v;

          if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
            feature.setAttribute( idx, v );

          col++;
        }

        fid = P->lookupFid( QVariant( primaryKeyVals ) );
      }
      break;

      case QgsPostgresProvider::pktUnknown:
        Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
        return false;
    }

    feature.setFeatureId( fid );
    QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

    // iterate attributes
    if ( subsetOfAttributes )
    {
      foreach ( int idx, fetchAttributes )
        getFeatureAttribute( idx, queryResult, row, col, feature );
    }
    else
    {
      for ( int idx = 0; idx < P->mAttributeFields.count(); ++idx )
        getFeatureAttribute( idx, queryResult, row, col, feature );
    }

    return true;
  }
Пример #8
0
qint64 QgsPostgresConn::getBinaryInt( QgsPostgresResult &queryResult, int row, int col )
{
  quint64 oid;
  char *p = PQgetvalue( queryResult.result(), row, col );
  size_t s = PQgetlength( queryResult.result(), row, col );

#ifdef QGISDEBUG
  if ( QgsLogger::debugLevel() >= 4 )
  {
    QString buf;
    for ( size_t i = 0; i < s; i++ )
    {
      buf += QString( "%1 " ).arg( *( unsigned char * )( p + i ), 0, 16, QLatin1Char( ' ' ) );
    }

    QgsDebugMsg( QString( "int in hex:%1" ).arg( buf ) );
  }
#endif

  switch ( s )
  {
    case 2:
      oid = *( quint16 * )p;
      if ( mSwapEndian )
        oid = ntohs( oid );
      break;

    case 6:
    {
      quint64 block  = *( quint32 * ) p;
      quint64 offset = *( quint16 * )( p + sizeof( quint32 ) );

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

      oid = ( block << 16 ) + offset;
    }
    break;

    case 8:
    {
      quint32 oid0 = *( quint32 * ) p;
      quint32 oid1 = *( quint32 * )( p + sizeof( quint32 ) );

      if ( mSwapEndian )
      {
        QgsDebugMsgLevel( QString( "swap oid0:%1 oid1:%2" ).arg( oid0 ).arg( oid1 ), 4 );
        oid0 = ntohl( oid0 );
        oid1 = ntohl( oid1 );
      }

      QgsDebugMsgLevel( QString( "oid0:%1 oid1:%2" ).arg( oid0 ).arg( oid1 ), 4 );
      oid   = oid0;
      QgsDebugMsgLevel( QString( "oid:%1" ).arg( oid ), 4 );
      oid <<= 32;
      QgsDebugMsgLevel( QString( "oid:%1" ).arg( oid ), 4 );
      oid  |= oid1;
      QgsDebugMsgLevel( QString( "oid:%1" ).arg( oid ), 4 );
    }
    break;

    default:
      QgsDebugMsg( QString( "unexpected size %1" ).arg( s ) );

    case 4:
      oid = *( quint32 * )p;
      if ( mSwapEndian )
        oid = ntohl( oid );
      break;
  }

  return oid;
}
Пример #9
0
/* Functions for determining available features in postGIS */
QString QgsPostgresConn::postgisVersion()
{
  if ( mGotPostgisVersion )
    return mPostgisVersionInfo;

  mPostgresqlVersion = PQserverVersion( mConn );

  QgsPostgresResult result = PQexec( "SELECT postgis_version()" );
  if ( result.PQntuples() != 1 )
  {
    QgsMessageLog::logMessage( tr( "Retrieval of postgis version failed" ), tr( "PostGIS" ) );
    return QString::null;
  }

  mPostgisVersionInfo = result.PQgetvalue( 0, 0 );

  QgsDebugMsg( "PostGIS version info: " + mPostgisVersionInfo );

  QStringList postgisParts = mPostgisVersionInfo.split( " ", QString::SkipEmptyParts );

  // Get major and minor version
  QStringList postgisVersionParts = postgisParts[0].split( ".", QString::SkipEmptyParts );
  if ( postgisVersionParts.size() < 2 )
  {
    QgsMessageLog::logMessage( tr( "Could not parse postgis version string '%1'" ).arg( mPostgisVersionInfo ), tr( "PostGIS" ) );
    return QString::null;
  }

  mPostgisVersionMajor = postgisVersionParts[0].toInt();
  mPostgisVersionMinor = postgisVersionParts[1].toInt();

  mUseWkbHex = mPostgisVersionMajor < 1;

  // apparently postgis 1.5.2 doesn't report capabilities in postgis_version() anymore
  if ( mPostgisVersionMajor > 1 || ( mPostgisVersionMajor == 1 && mPostgisVersionMinor >= 5 ) )
  {
    result = PQexec( "SELECT postgis_geos_version(),postgis_proj_version()" );
    mGeosAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 0 );
    mProjAvailable = result.PQntuples() == 1 && !result.PQgetisnull( 0, 1 );
    QgsDebugMsg( QString( "geos:%1 proj:%2" )
                 .arg( mGeosAvailable ? result.PQgetvalue( 0, 0 ) : "none" )
                 .arg( mProjAvailable ? result.PQgetvalue( 0, 1 ) : "none" ) );
    mGistAvailable = true;
  }
  else
  {
    // assume no capabilities
    mGeosAvailable = false;
    mGistAvailable = false;
    mProjAvailable = false;

    // parse out the capabilities and store them
    QStringList geos = postgisParts.filter( "GEOS" );
    if ( geos.size() == 1 )
    {
      mGeosAvailable = ( geos[0].indexOf( "=1" ) > -1 );
    }
    QStringList gist = postgisParts.filter( "STATS" );
    if ( gist.size() == 1 )
    {
      mGistAvailable = ( geos[0].indexOf( "=1" ) > -1 );
    }
    QStringList proj = postgisParts.filter( "PROJ" );
    if ( proj.size() == 1 )
    {
      mProjAvailable = ( proj[0].indexOf( "=1" ) > -1 );
    }
  }

  // checking for topology support
  QgsDebugMsg( "Checking for topology support" );
  mTopologyAvailable = false;
  if ( mPostgisVersionMajor > 1 )
  {
    QgsPostgresResult result = PQexec( "SELECT count(c.oid) FROM pg_class AS c JOIN pg_namespace AS n ON c.relnamespace=n.oid WHERE n.nspname='topology' AND c.relname='topology'" );
    if ( result.PQntuples() >= 1 )
    {
      mTopologyAvailable = true;
    }
  }

  mGotPostgisVersion = true;

  return mPostgisVersionInfo;
}
Пример #10
0
bool QgsPostgresConn::getTableInfo( bool searchGeometryColumnsOnly, bool searchPublicOnly, bool allowGeometrylessTables )
{
  int nColumns = 0;
  int foundInTables = 0;
  QgsPostgresResult result;
  QgsPostgresLayerProperty layerProperty;

  QgsDebugMsg( "Entering." );

  mLayersSupported.clear();

  for ( int i = 0; i < 3; i++ )
  {
    QString sql, tableName, schemaName, columnName, typeName, sridName, gtableName;
    QgsPostgresGeometryColumnType columnType = sctGeometry;

    if ( i == 0 )
    {
      tableName  = "l.f_table_name";
      schemaName = "l.f_table_schema";
      columnName = "l.f_geometry_column";
      typeName   = "upper(l.type)";
      sridName   = "l.srid";
      gtableName = "geometry_columns";
      columnType = sctGeometry;
    }
    else if ( i == 1 )
    {
      tableName  = "l.f_table_name";
      schemaName = "l.f_table_schema";
      columnName = "l.f_geography_column";
      typeName   = "upper(l.type)";
      sridName   = "l.srid";
      gtableName = "geography_columns";
      columnType = sctGeography;
    }
    else if ( i == 2 )
    {
      schemaName = "l.schema_name";
      tableName  = "l.table_name";
      columnName = "l.feature_column";
      typeName   = "CASE "
                   "WHEN l.feature_type = 1 THEN 'MULTIPOINT' "
                   "WHEN l.feature_type = 2 THEN 'MULTILINESTRING' "
                   "WHEN l.feature_type = 3 THEN 'MULTIPOLYGON' "
                   "WHEN l.feature_type = 4 THEN 'GEOMETRYCOLLECTION' "
                   "END AS type";
      sridName   = "(SELECT srid FROM topology.topology t WHERE l.topology_id=t.id)";
      gtableName = "topology.layer";
      columnType = sctTopoGeometry;
    }

    // The following query returns only tables that exist and the user has SELECT privilege on.
    // Can't use regclass here because table must exist, else error occurs.
    sql = QString( "SELECT %1,%2,%3,%4,%5,c.relkind"
                   " FROM %6 l,pg_class c,pg_namespace n"
                   " WHERE c.relname=%1"
                   " AND %2=n.nspname"
                   " AND n.oid=c.relnamespace"
                   " AND has_schema_privilege(n.nspname,'usage')"
                   " AND has_table_privilege('\"'||n.nspname||'\".\"'||c.relname||'\"','select')" // user has select privilege
                 )
          .arg( tableName ).arg( schemaName ).arg( columnName ).arg( typeName ).arg( sridName ).arg( gtableName );

    if ( searchPublicOnly )
      sql += " AND n.nspname='public'";

    sql += QString( " ORDER BY n.nspname,c.relname,%1" ).arg( columnName );

    QgsDebugMsg( "getting table info: " + sql );
    result = PQexec( sql, i == 0 );
    if ( result.PQresultStatus() != PGRES_TUPLES_OK )
    {
      PQexecNR( "COMMIT" );
      continue;
    }

    for ( int idx = 0; idx < result.PQntuples(); idx++ )
    {
      QString tableName = result.PQgetvalue( idx, 0 );
      QString schemaName = result.PQgetvalue( idx, 1 );
      QString column = result.PQgetvalue( idx, 2 );
      QString type = result.PQgetvalue( idx, 3 );
      QString srid = result.PQgetvalue( idx, 4 );
      QString relkind = result.PQgetvalue( idx, 5 );

      QgsDebugMsg( QString( "%1 : %2.%3.%4: %5 %6 %7" )
                   .arg( gtableName )
                   .arg( schemaName ).arg( tableName ).arg( column )
                   .arg( type )
                   .arg( srid )
                   .arg( relkind ) );

      layerProperty.pkCols.clear();
      layerProperty.type = type;
      layerProperty.schemaName = schemaName;
      layerProperty.tableName = tableName;
      layerProperty.geometryColName = column;
      layerProperty.geometryColType = columnType;

      if ( relkind == "v" )
      {
        layerProperty.pkCols = pkCandidates( schemaName, tableName );
        if ( layerProperty.pkCols.isEmpty() )
        {
          QgsDebugMsg( "no key columns found." );
          continue;
        }
      }

      layerProperty.srid = srid;
      layerProperty.sql = "";

      mLayersSupported << layerProperty;
      nColumns++;
    }

    foundInTables |= 1 << i;
  }

  if ( nColumns == 0 )
  {
    QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined." ), tr( "PostGIS" ) );
  }

  //search for geometry columns in tables that are not in the geometry_columns metatable
  if ( !searchGeometryColumnsOnly )
  {
    // Now have a look for geometry columns that aren't in the geometry_columns table.
    QString sql = "SELECT"
                  " c.relname"
                  ",n.nspname"
                  ",a.attname"
                  ",c.relkind"
                  ",CASE WHEN t.typname IN ('geometry','geography','topogeometry') THEN t.typname ELSE b.typname END AS coltype"
                  " FROM pg_attribute a"
                  " JOIN pg_class c ON c.oid=a.attrelid"
                  " JOIN pg_namespace n ON n.oid=c.relnamespace"
                  " JOIN pg_type t ON t.oid=a.atttypid"
                  " LEFT JOIN pg_type b ON b.oid=t.typbasetype"
                  " WHERE c.relkind IN ('v','r')"
                  " AND has_schema_privilege( n.nspname, 'usage' )"
                  " AND has_table_privilege( '\"' || n.nspname || '\".\"' || c.relname || '\"', 'select' )"
                  " AND (t.typname IN ('geometry','geography','topogeometry') OR b.typname IN ('geometry','geography','topogeometry'))";

    // user has select privilege
    if ( searchPublicOnly )
      sql += " AND n.nspname='public'";

    // skip columns of which we already derived information from the metadata tables
    if ( nColumns > 0 )
    {
      if ( foundInTables & 1 )
      {
        sql += " AND (n.nspname,c.relname,a.attname) NOT IN (SELECT f_table_schema,f_table_name,f_geometry_column FROM geometry_columns)";
      }

      if ( foundInTables & 2 )
      {
        sql += " AND (n.nspname,c.relname,a.attname) NOT IN (SELECT f_table_schema,f_table_name,f_geography_column FROM geography_columns)";
      }

      if ( foundInTables & 4 )
      {
        sql += " AND (n.nspname,c.relname,a.attname) NOT IN (SELECT schema_name,table_name,feature_column FROM topology.layer)";
      }
    }

    QgsDebugMsg( "sql: " + sql );

    result = PQexec( sql );

    if ( result.PQresultStatus() != PGRES_TUPLES_OK )
    {
      QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined. The error message from the database was:\n%1\n" )
                                 .arg( result.PQresultErrorMessage() ),
                                 tr( "PostGIS" ) );
      PQexecNR( "COMMIT" );
      return false;
    }

    for ( int i = 0; i < result.PQntuples(); i++ )
    {
      // Have the column name, schema name and the table name. The concept of a
      // catalog doesn't exist in postgresql so we ignore that, but we
      // do need to get the geometry type.

      // Make the assumption that the geometry type for the first
      // row is the same as for all other rows.

      QString tableName  = result.PQgetvalue( i, 0 ); // relname
      QString schemaName = result.PQgetvalue( i, 1 ); // nspname
      QString column     = result.PQgetvalue( i, 2 ); // attname
      QString relkind    = result.PQgetvalue( i, 3 ); // relation kind
      QString coltype    = result.PQgetvalue( i, 4 ); // column type

      QgsDebugMsg( QString( "%1.%2.%3: %4" ).arg( schemaName ).arg( tableName ).arg( column ).arg( relkind ) );

      layerProperty.type = QString::null;
      layerProperty.schemaName = schemaName;
      layerProperty.tableName = tableName;
      layerProperty.geometryColName = column;
      if ( coltype == "geometry" )
      {
        layerProperty.geometryColType = sctGeometry;
      }
      else if ( coltype == "geography" )
      {
        layerProperty.geometryColType = sctGeography;
      }
      else if ( coltype == "topogeometry" )
      {
        layerProperty.geometryColType = sctTopoGeometry;
      }
      else
      {
        Q_ASSERT( !"Unknown geometry type" );
      }

      if ( relkind == "v" )
      {
        layerProperty.pkCols = pkCandidates( schemaName, tableName );
        if ( layerProperty.pkCols.isEmpty() )
        {
          QgsDebugMsg( "no key columns found." );
          continue;
        }
      }

      layerProperty.sql = "";

      mLayersSupported << layerProperty;
      nColumns++;
    }
  }


  if ( allowGeometrylessTables )
  {
    QString sql = "SELECT "
                  "pg_class.relname"
                  ",pg_namespace.nspname"
                  ",pg_class.relkind"
                  " FROM "
                  " pg_class"
                  ",pg_namespace"
                  " WHERE pg_namespace.oid=pg_class.relnamespace"
                  " AND has_schema_privilege(pg_namespace.nspname,'usage')"
                  " AND has_table_privilege('\"' || pg_namespace.nspname || '\".\"' || pg_class.relname || '\"','select')"
                  " AND pg_class.relkind IN ('v','r')";

    // user has select privilege
    if ( searchPublicOnly )
      sql += " AND pg_namespace.nspname='public'";

    QgsDebugMsg( "sql: " + sql );

    result = PQexec( sql );

    if ( result.PQresultStatus() != PGRES_TUPLES_OK )
    {
      QgsMessageLog::logMessage( tr( "Database connection was successful, but the accessible tables could not be determined.\nThe error message from the database was:\n%1" )
                                 .arg( result.PQresultErrorMessage() ),
                                 tr( "PostGIS" ) );
      return false;
    }

    for ( int i = 0; i < result.PQntuples(); i++ )
    {
      QString table   = result.PQgetvalue( i, 0 ); // relname
      QString schema  = result.PQgetvalue( i, 1 ); // nspname
      QString relkind = result.PQgetvalue( i, 2 ); // relation kind

      QgsDebugMsg( QString( "%1.%2: %3" ).arg( schema ).arg( table ).arg( relkind ) );

      layerProperty.type = QString::null;
      layerProperty.schemaName = schema;
      layerProperty.tableName = table;
      layerProperty.geometryColName = QString::null;
      layerProperty.geometryColType = sctNone;
      layerProperty.pkCols = relkind == "v" ? pkCandidates( schema, table ) : QStringList();
      layerProperty.srid = "";
      layerProperty.sql = "";

      mLayersSupported << layerProperty;
      nColumns++;
    }
  }

  if ( nColumns == 0 )
  {
    QgsMessageLog::logMessage( tr( "Database connection was successful, but no accessible tables were found.  Please verify that you have SELECT privilege on a table carrying PostGIS geometry." ), tr( "PostGIS" ) );
  }

  return true;
}
Пример #11
0
void QgsPostgresConn::retrieveLayerTypes( QgsPostgresLayerProperty &layerProperty, bool useEstimatedMetadata )
{
  QString table;

  if ( !layerProperty.schemaName.isEmpty() )
  {
    table = QString( "%1.%2" )
            .arg( quotedIdentifier( layerProperty.schemaName ) )
            .arg( quotedIdentifier( layerProperty.tableName ) );
  }
  else
  {
    // Query
    table = layerProperty.tableName;
  }


  // our estimatation ignores that a where clause might restrict the feature type or srid
  if ( useEstimatedMetadata )
  {
    table = QString( "(SELECT %1 FROM %2 WHERE %1 IS NOT NULL%3 LIMIT %4) AS t" )
            .arg( quotedIdentifier( layerProperty.geometryColName ) )
            .arg( table )
            .arg( layerProperty.sql.isEmpty() ? "" : QString( " AND (%1)" ).arg( layerProperty.sql ) )
            .arg( sGeomTypeSelectLimit );
  }
  else if ( !layerProperty.sql.isEmpty() )
  {
    table += QString( " WHERE %1" ).arg( layerProperty.sql );
  }

  QString query = QString( "SELECT DISTINCT"
                           " CASE"
                           " WHEN %1 THEN 'POINT'"
                           " WHEN %2 THEN 'LINESTRING'"
                           " WHEN %3 THEN 'POLYGON'"
                           " END,"
                           " %4(%5%6)"
                           " FROM %7" )
                  .arg( postgisTypeFilter( layerProperty.geometryColName, QGis::WKBPoint, layerProperty.geometryColType == sctGeography ) )
                  .arg( postgisTypeFilter( layerProperty.geometryColName, QGis::WKBLineString, layerProperty.geometryColType == sctGeography ) )
                  .arg( postgisTypeFilter( layerProperty.geometryColName, QGis::WKBPolygon, layerProperty.geometryColType == sctGeography ) )
                  .arg( majorVersion() < 2 ? "srid" : "st_srid" )
                  .arg( quotedIdentifier( layerProperty.geometryColName ) )
                  .arg( layerProperty.geometryColType == sctGeography ? "::geometry" : "" )
                  .arg( table );

  QgsDebugMsg( "Retrieving geometry types: " + query );

  QgsPostgresResult gresult = PQexec( query );

  QString type;
  QString srid;
  if ( gresult.PQresultStatus() == PGRES_TUPLES_OK )
  {
    QStringList types;
    QStringList srids;

    for ( int i = 0; i < gresult.PQntuples(); i++ )
    {
      QString type = gresult.PQgetvalue( i, 0 );
      QString srid = gresult.PQgetvalue( i, 1 );
      if ( type.isEmpty() )
        continue;

      types << type;
      srids << srid;
    }

    type = types.join( "," );
    srid = srids.join( "," );
  }

  QgsDebugMsg( QString( "type:%1 srid:%2" ).arg( type ).arg( srid ) );
  layerProperty.type = type;
  layerProperty.srid = srid;
}
Пример #12
0
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
    feature.initAttributes( mSource->mFields.count() );

    int col = 0;

    if ( mFetchGeometry )
    {
        int returnedLength = ::PQgetlength( queryResult.result(), row, col );
        if ( returnedLength > 0 )
        {
            unsigned char *featureGeom = new unsigned char[returnedLength + 1];
            memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
            memset( featureGeom + returnedLength, 0, 1 );

            unsigned int wkbType;
            memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );
            QgsWKBTypes::Type newType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );

            if (( unsigned int )newType != wkbType )
            {
                // overwrite type
                unsigned int n = newType;
                memcpy( featureGeom + 1, &n, sizeof( n ) );
            }

            // PostGIS stores TIN as a collection of Triangles.
            // Since Triangles are not supported, they have to be converted to Polygons
            const int nDims = 2 + ( QgsWKBTypes::hasZ( newType ) ? 1 : 0 ) + ( QgsWKBTypes::hasM( newType ) ? 1 : 0 );
            if ( wkbType % 1000 == 16 )
            {
                unsigned int numGeoms;
                memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
                unsigned char *wkb = featureGeom + 9;
                for ( unsigned int i = 0; i < numGeoms; ++i )
                {
                    const unsigned int localType = QgsWKBTypes::singleType( newType ); // polygon(Z|M)
                    memcpy( wkb + 1, &localType, sizeof( localType ) );

                    // skip endian and type info
                    wkb += sizeof( unsigned int ) + 1;

                    // skip coordinates
                    unsigned int nRings;
                    memcpy( &nRings, wkb, sizeof( int ) );
                    wkb += sizeof( int );
                    for ( unsigned int j = 0; j < nRings; ++j )
                    {
                        unsigned int nPoints;
                        memcpy( &nPoints, wkb, sizeof( int ) );
                        wkb += sizeof( nPoints ) + sizeof( double ) * nDims * nPoints;
                    }
                }
            }

            feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
        }
        else
        {
            feature.setGeometryAndOwnership( 0, 0 );
        }

        col++;
    }

    QgsFeatureId fid = 0;

    bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
    const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

    switch ( mSource->mPrimaryKeyType )
    {
    case pktOid:
    case pktTid:
    case pktInt:
        fid = mConn->getBinaryInt( queryResult, row, col++ );
        if ( mSource->mPrimaryKeyType == pktInt &&
                ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) )
            feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid );
        break;

    case pktFidMap:
    {
        QList<QVariant> primaryKeyVals;

        Q_FOREACH ( int idx, mSource->mPrimaryKeyAttrs )
        {
            const QgsField &fld = mSource->mFields.at( idx );

            QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
            primaryKeyVals << v;

            if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
                feature.setAttribute( idx, v );

            col++;
        }

        fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) );

    }
    break;

    case pktUnknown:
        Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
        return false;
    }

    feature.setFeatureId( fid );
    QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

    // iterate attributes
    if ( subsetOfAttributes )
    {
        Q_FOREACH ( int idx, fetchAttributes )
            getFeatureAttribute( idx, queryResult, row, col, feature );
    }
    else
    {
        for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
Пример #13
0
bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
{
    feature.setValid( false );

    if ( mClosed )
        return false;

    if ( mFeatureQueue.empty() )
    {
        QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
        QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
        if ( P->mConnectionRO->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
        {
            QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
        }

        QgsPostgresResult queryResult;
        for ( ;; )
        {
            queryResult = P->mConnectionRO->PQgetResult();
            if ( !queryResult.result() )
                break;

            if ( queryResult.PQresultStatus() != PGRES_TUPLES_OK )
            {
                QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName ).arg( P->mConnectionRO->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
                break;
            }

            int rows = queryResult.PQntuples();
            if ( rows == 0 )
                continue;

            for ( int row = 0; row < rows; row++ )
            {
                mFeatureQueue.enqueue( QgsFeature() );
                getFeature( queryResult, row, mFeatureQueue.back() );
            } // for each row in queue
        }
    }

    if ( mFeatureQueue.empty() )
    {
        QgsDebugMsg( QString( "Finished after %1 features" ).arg( mFetched ) );
        close();

        /* only updates the feature count if it was already once.
         * Otherwise, this would lead to false feature count if
         * an existing project is open at a restrictive extent.
         */
        if ( P->mFeaturesCounted > 0 && P->mFeaturesCounted < mFetched )
        {
            QgsDebugMsg( QString( "feature count adjusted from %1 to %2" ).arg( P->mFeaturesCounted ).arg( mFetched ) );
            P->mFeaturesCounted = mFetched;
        }
        return false;
    }

    // Now return the next feature from the queue
    if ( mRequest.flags() & QgsFeatureRequest::NoGeometry )
    {
        feature.setGeometryAndOwnership( 0, 0 );
    }
    else
    {
        QgsGeometry* featureGeom = mFeatureQueue.front().geometryAndOwnership();
        feature.setGeometry( featureGeom );
    }
    feature.setFeatureId( mFeatureQueue.front().id() );
    feature.setAttributes( mFeatureQueue.front().attributes() );

    mFeatureQueue.dequeue();
    mFetched++;

    feature.setValid( true );
    feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups

    return true;
}
Пример #14
0
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
    try
    {
        feature.initAttributes( P->fields().count() );

        int col = 0;

        if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
        {
            int returnedLength = ::PQgetlength( queryResult.result(), row, col );
            if ( returnedLength > 0 )
            {
                unsigned char *featureGeom = new unsigned char[returnedLength + 1];
                memset( featureGeom, 0, returnedLength + 1 );
                memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );

                // modify 2.5D WKB types to make them compliant with OGR
                unsigned int wkbType;
                memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );

                // convert unsupported types to supported ones
                switch ( wkbType )
                {
                case 15:
                    // 2D polyhedral => multipolygon
                    wkbType = 6;
                    break;
                case 1015:
                    // 3D polyhedral => multipolygon
                    wkbType = 1006;
                    break;
                case 17:
                    // 2D triangle => polygon
                    wkbType = 3;
                    break;
                case 1017:
                    // 3D triangle => polygon
                    wkbType = 1003;
                    break;
                case 16:
                    // 2D TIN => multipolygon
                    wkbType = 6;
                    break;
                case 1016:
                    // TIN => multipolygon
                    wkbType = 1006;
                    break;
                }
                // convert from postgis types to qgis types
                if ( wkbType >= 1000 )
                {
                    wkbType = wkbType - 1000 + QGis::WKBPoint25D - 1;
                }
                memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) );

                // change wkb type of inner geometries
                if ( wkbType == QGis::WKBMultiPoint25D ||
                        wkbType == QGis::WKBMultiLineString25D ||
                        wkbType == QGis::WKBMultiPolygon25D )
                {
                    unsigned int numGeoms = *(( int* )( featureGeom + 5 ) );
                    unsigned char* wkb = featureGeom + 9;
                    for ( unsigned int i = 0; i < numGeoms; ++i )
                    {
                        unsigned int localType;
                        memcpy( &localType, wkb + 1, sizeof( localType ) );
                        switch ( localType )
                        {
                        case 15:
                            // 2D polyhedral => multipolygon
                            localType = 6;
                            break;
                        case 1015:
                            // 3D polyhedral => multipolygon
                            localType = 1006;
                            break;
                        case 17:
                            // 2D triangle => polygon
                            localType = 3;
                            break;
                        case 1017:
                            // 3D triangle => polygon
                            localType = 1003;
                            break;
                        case 16:
                            // 2D TIN => multipolygon
                            localType = 6;
                            break;
                        case 1016:
                            // TIN => multipolygon
                            localType = 1006;
                            break;
                        }
                        if ( localType >= 1000 )
                        {
                            localType = localType - 1000 + QGis::WKBPoint25D - 1;
                        }
                        memcpy( wkb + 1, &localType, sizeof( localType ) );

                        // skip endian and type info
                        wkb += sizeof( unsigned int ) + 1;

                        // skip coordinates
                        switch ( wkbType )
                        {
                        case QGis::WKBMultiPoint25D:
                            wkb += sizeof( double ) * 3;
                            break;
                        case QGis::WKBMultiLineString25D:
                        {
                            unsigned int nPoints = *(( int* ) wkb );
                            wkb += sizeof( nPoints );
                            wkb += sizeof( double ) * 3 * nPoints;
                        }
                        break;
                        default:
                        case QGis::WKBMultiPolygon25D:
                        {
                            unsigned int nRings = *(( int* ) wkb );
                            wkb += sizeof( nRings );
                            for ( unsigned int j = 0; j < nRings; ++j )
                            {
                                unsigned int nPoints = *(( int* ) wkb );
                                wkb += sizeof( nPoints );
                                wkb += sizeof( double ) * 3 * nPoints;
                            }
                        }
                        break;
                        }
                    }
                }

                feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
            }
            else
            {
                feature.setGeometryAndOwnership( 0, 0 );
            }

            col++;
        }

        QgsFeatureId fid = 0;

        bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
        const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

        switch ( P->mPrimaryKeyType )
        {
        case QgsPostgresProvider::pktOid:
        case QgsPostgresProvider::pktTid:
        case QgsPostgresProvider::pktInt:
            fid = P->mConnectionRO->getBinaryInt( queryResult, row, col++ );
            if ( P->mPrimaryKeyType == QgsPostgresProvider::pktInt &&
                    ( !subsetOfAttributes || fetchAttributes.contains( P->mPrimaryKeyAttrs[0] ) ) )
                feature.setAttribute( P->mPrimaryKeyAttrs[0], fid );
            break;

        case QgsPostgresProvider::pktFidMap:
        {
            QList<QVariant> primaryKeyVals;

            foreach ( int idx, P->mPrimaryKeyAttrs )
            {
                const QgsField &fld = P->field( idx );

                QVariant v = P->convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
                primaryKeyVals << v;

                if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
                    feature.setAttribute( idx, v );

                col++;
            }

            fid = P->lookupFid( QVariant( primaryKeyVals ) );
        }
        break;

        case QgsPostgresProvider::pktUnknown:
            Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
            return false;
        }

        feature.setFeatureId( fid );
        QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

        // iterate attributes
        if ( subsetOfAttributes )
        {
            foreach ( int idx, fetchAttributes )
                getFeatureAttribute( idx, queryResult, row, col, feature );
        }
        else
        {
            for ( int idx = 0; idx < P->mAttributeFields.count(); ++idx )
                getFeatureAttribute( idx, queryResult, row, col, feature );
        }

        return true;
    }
Пример #15
0
bool QgsPostgresFeatureIterator::getFeature( QgsPostgresResult &queryResult, int row, QgsFeature &feature )
{
  feature.initAttributes( mSource->mFields.count() );

  int col = 0;

  if ( mFetchGeometry )
  {
    int returnedLength = ::PQgetlength( queryResult.result(), row, col );
    if ( returnedLength > 0 )
    {
      unsigned char *featureGeom = new unsigned char[returnedLength + 1];
      memcpy( featureGeom, PQgetvalue( queryResult.result(), row, col ), returnedLength );
      memset( featureGeom + returnedLength, 0, 1 );

      // modify 2.5D WKB types to make them compliant with OGR
      unsigned int wkbType;
      memcpy( &wkbType, featureGeom + 1, sizeof( wkbType ) );
      wkbType = QgsPostgresConn::wkbTypeFromOgcWkbType( wkbType );
      memcpy( featureGeom + 1, &wkbType, sizeof( wkbType ) );

      // change wkb type of inner geometries
      if ( wkbType == QGis::WKBMultiPoint25D ||
           wkbType == QGis::WKBMultiLineString25D ||
           wkbType == QGis::WKBMultiPolygon25D )
      {
        unsigned int numGeoms;
        memcpy( &numGeoms, featureGeom + 5, sizeof( unsigned int ) );
        unsigned char *wkb = featureGeom + 9;
        for ( unsigned int i = 0; i < numGeoms; ++i )
        {
          unsigned int localType;
          memcpy( &localType, wkb + 1, sizeof( localType ) );
          localType = QgsPostgresConn::wkbTypeFromOgcWkbType( localType );
          memcpy( wkb + 1, &localType, sizeof( localType ) );

          // skip endian and type info
          wkb += sizeof( unsigned int ) + 1;

          // skip coordinates
          switch ( wkbType )
          {
            case QGis::WKBMultiPoint25D:
              wkb += sizeof( double ) * 3;
              break;
            case QGis::WKBMultiLineString25D:
            {
              unsigned int nPoints;
              memcpy( &nPoints, wkb, sizeof( int ) );
              wkb += sizeof( int ) + sizeof( double ) * 3 * nPoints;
            }
            break;
            default:
            case QGis::WKBMultiPolygon25D:
            {
              unsigned int nRings;
              memcpy( &nRings, wkb, sizeof( int ) );
              wkb += sizeof( int );
              for ( unsigned int j = 0; j < nRings; ++j )
              {
                unsigned int nPoints;
                memcpy( &nPoints, wkb, sizeof( int ) );
                wkb += sizeof( nPoints ) + sizeof( double ) * 3 * nPoints;
              }
            }
            break;
          }
        }
      }

      feature.setGeometryAndOwnership( featureGeom, returnedLength + 1 );
    }
    else
    {
      feature.setGeometryAndOwnership( 0, 0 );
    }

    col++;
  }

  QgsFeatureId fid = 0;

  bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes;
  const QgsAttributeList& fetchAttributes = mRequest.subsetOfAttributes();

  switch ( mSource->mPrimaryKeyType )
  {
    case pktOid:
    case pktTid:
    case pktInt:
      fid = mConn->getBinaryInt( queryResult, row, col++ );
      if ( mSource->mPrimaryKeyType == pktInt &&
           ( !subsetOfAttributes || fetchAttributes.contains( mSource->mPrimaryKeyAttrs[0] ) ) )
        feature.setAttribute( mSource->mPrimaryKeyAttrs[0], fid );
      break;

    case pktFidMap:
    {
      QList<QVariant> primaryKeyVals;

      Q_FOREACH ( int idx, mSource->mPrimaryKeyAttrs )
      {
        const QgsField &fld = mSource->mFields[idx];

        QVariant v = QgsPostgresProvider::convertValue( fld.type(), queryResult.PQgetvalue( row, col ) );
        primaryKeyVals << v;

        if ( !subsetOfAttributes || fetchAttributes.contains( idx ) )
          feature.setAttribute( idx, v );

        col++;
      }

      fid = mSource->mShared->lookupFid( QVariant( primaryKeyVals ) );

    }
    break;

    case pktUnknown:
      Q_ASSERT( !"FAILURE: cannot get feature with unknown primary key" );
      return false;
  }

  feature.setFeatureId( fid );
  QgsDebugMsgLevel( QString( "fid=%1" ).arg( fid ), 4 );

  // iterate attributes
  if ( subsetOfAttributes )
  {
    Q_FOREACH ( int idx, fetchAttributes )
      getFeatureAttribute( idx, queryResult, row, col, feature );
  }
  else
  {
    for ( int idx = 0; idx < mSource->mFields.count(); ++idx )