QgsCachedFeatureWriterIterator::QgsCachedFeatureWriterIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest ) : QgsAbstractFeatureIterator( featureRequest ) , mVectorLayerCache( vlCache ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() ) { mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } mFeatIt = vlCache->layer()->getFeatures( mRequest ); }
QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsMemoryFeatureSource>( source, ownSource, request ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mSource->mSubsetString.isEmpty() ) { mSubsetExpression = qgis::make_unique< QgsExpression >( mSource->mSubsetString ); mSubsetExpression->prepare( &mSource->mExpressionContext ); } if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { mSelectRectGeom = QgsGeometry::fromRect( mFilterRect ); mSelectRectEngine.reset( QgsGeometry::createGeometryEngine( mSelectRectGeom.constGet() ) ); mSelectRectEngine->prepareGeometry(); } // if there's spatial index, use it! // (but don't use it when selection rect is not specified) if ( !mFilterRect.isNull() && mSource->mSpatialIndex ) { mUsingFeatureIdList = true; mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect ); QgsDebugMsg( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ) ); } else if ( mRequest.filterType() == QgsFeatureRequest::FilterFid ) { mUsingFeatureIdList = true; QgsFeatureMap::const_iterator it = mSource->mFeatures.constFind( mRequest.filterFid() ); if ( it != mSource->mFeatures.constEnd() ) mFeatureIdList.append( mRequest.filterFid() ); } else { mUsingFeatureIdList = false; } rewind(); }
QgsCachedFeatureIterator::QgsCachedFeatureIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest ) : QgsAbstractFeatureIterator( featureRequest ) , mVectorLayerCache( vlCache ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() ) { mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } switch ( featureRequest.filterType() ) { case QgsFeatureRequest::FilterFids: mFeatureIds = featureRequest.filterFids(); break; case QgsFeatureRequest::FilterFid: mFeatureIds = QgsFeatureIds() << featureRequest.filterFid(); break; default: mFeatureIds = mVectorLayerCache->mCache.keys().toSet(); break; } mFeatureIdIterator = mFeatureIds.constBegin(); if ( mFeatureIdIterator == mFeatureIds.constEnd() ) close(); }
QgsGPXFeatureIterator::QgsGPXFeatureIterator( QgsGPXFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsGPXFeatureSource>( source, ownSource, request ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } rewind(); }
QgsMssqlFeatureIterator::QgsMssqlFeatureIterator( QgsMssqlFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsMssqlFeatureSource>( source, ownSource, request ) { mClosed = false; mParser.IsGeography = mSource->mIsGeography; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } BuildStatement( request ); // connect to the database mDatabase = QgsMssqlProvider::GetDatabase( mSource->mService, mSource->mHost, mSource->mDatabaseName, mSource->mUserName, mSource->mPassword ); if ( !mDatabase.open() ) { QgsDebugMsg( "Failed to open database" ); QgsDebugMsg( mDatabase.lastError().text() ); return; } // create sql query mQuery.reset( new QSqlQuery( mDatabase ) ); // start selection rewind(); }
QgsDb2FeatureIterator::QgsDb2FeatureIterator( QgsDb2FeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsDb2FeatureSource>( source, ownSource, request ) { mClosed = false; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } BuildStatement( request ); // connect to the database QString errMsg; mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg ); if ( !errMsg.isEmpty() ) { QgsDebugMsg( "Failed to open database: " + errMsg ); return; } // create sql query mQuery.reset( new QSqlQuery( mDatabase ) ); // start selection rewind(); }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , mSharedDS( source->mSharedDS ) , mFirstFieldIsFid( source->mFirstFieldIsFid ) , mFieldsWithoutFid( source->mFieldsWithoutFid ) { for ( const auto &id : mRequest.filterFids() ) { mFilterFids.insert( id ); } mFilterFidsIt = mFilterFids.begin(); // Since connection timeout for OGR connections is problematic and can lead to crashes, disable for now. mRequest.setTimeout( -1 ); if ( mSharedDS ) { mOgrLayer = mSharedDS->getLayerFromNameOrIndex( mSource->mLayerName, mSource->mLayerIndex ); if ( !mOgrLayer ) { return; } } else { //QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection"); mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource, mSource->mShareSameDatasetAmongLayers ), mRequest.timeout(), mRequest.requestMayBeNested() ); if ( !mConn || !mConn->ds ) { return; } if ( mSource->mLayerName.isNull() ) { mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex ); } else { mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() ); } if ( !mOgrLayer ) { return; } if ( !mSource->mSubsetString.isEmpty() ) { mOgrLayerOri = mOgrLayer; mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); // If the mSubsetString was a full SELECT ...., then mOgrLayer will be a OGR SQL layer != mOgrLayerOri mFieldsWithoutFid.clear(); for ( int i = ( mFirstFieldIsFid ) ? 1 : 0; i < mSource->mFields.size(); i++ ) mFieldsWithoutFid.append( mSource->mFields.at( i ) ); if ( !mOgrLayer ) { close(); return; } } } QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr ); if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } mFetchGeometry = ( !mFilterRect.isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || ( mSource->mOgrGeometryTypeFilter != wkbUnknown ); QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { QSet<int> attributeIndexes; Q_FOREACH ( const QString &attr, mRequest.orderBy().usedAttributes() ) { attributeIndexes << mSource->mFields.lookupField( attr ); } attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); }
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsDelimitedTextFeatureSource>( source, ownSource, request ) , mTestSubset( mSource->mSubsetExpression ) { // Determine mode to use based on request... QgsDebugMsg( "Setting up QgsDelimitedTextIterator" ); // Does the layer have geometry - will revise later to determine if we actually need to // load it. bool hasGeometry = mSource->mGeomRep != QgsDelimitedTextProvider::GeomNone; if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect mClosed = true; return; } if ( !mFilterRect.isNull() && hasGeometry ) { QgsDebugMsg( "Configuring for rectangle select" ); mTestGeometry = true; // Exact intersection test only applies for WKT geometries mTestGeometryExact = mRequest.flags() & QgsFeatureRequest::ExactIntersect && mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt; // If request doesn't overlap extents, then nothing to return if ( ! mFilterRect.intersects( mSource->mExtent ) && !mTestSubset ) { QgsDebugMsg( "Rectangle outside layer extents - no features to return" ); mMode = FeatureIds; } // If the request extents include the entire layer, then revert to // a file scan else if ( mFilterRect.contains( mSource->mExtent ) && !mTestSubset ) { QgsDebugMsg( "Rectangle contains layer extents - bypass spatial filter" ); mTestGeometry = false; } // If we have a spatial index then use it. The spatial index already accounts // for the subset. Also means we don't have to test geometries unless doing exact // intersection else if ( mSource->mUseSpatialIndex ) { mFeatureIds = mSource->mSpatialIndex->intersects( mFilterRect ); // Sort for efficient sequential retrieval std::sort( mFeatureIds.begin(), mFeatureIds.end() ); QgsDebugMsg( QString( "Layer has spatial index - selected %1 features from index" ).arg( mFeatureIds.size() ) ); mMode = FeatureIds; mTestSubset = false; mTestGeometry = mTestGeometryExact; } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QgsDebugMsg( "Configuring for returning single id" ); if ( mFilterRect.isNull() || mFeatureIds.contains( request.filterFid() ) ) { mFeatureIds = QList<QgsFeatureId>() << request.filterFid(); } mMode = FeatureIds; mTestSubset = false; } // If have geometry and testing geometry then evaluate options... // If we don't have geometry then all records pass geometry filter. // CC: 2013-05-09 // Not sure about intended relationship between filtering on geometry and // requesting no geometry? Have preserved current logic of ignoring spatial filter // if not requesting geometry. else // If we have a subset index then use it.. if ( mMode == FileScan && mSource->mUseSubsetIndex ) { QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) ); mTestSubset = false; mMode = SubsetIndex; } // Otherwise just have to scan the file if ( mMode == FileScan ) { QgsDebugMsg( "File will be scanned for desired features" ); } // If the layer has geometry, do we really need to load it? // We need it if it is asked for explicitly in the request, // if we are testing geometry (ie spatial filter), or // if testing the subset expression. if ( hasGeometry && ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || mTestGeometry || ( mTestSubset && mSource->mSubsetExpression->needsGeometry() ) || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) ) ) { mLoadGeometry = true; } else { QgsDebugMsgLevel( "Feature geometries not required", 4 ); mLoadGeometry = false; } // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { QgsAttributeList attrs = request.subsetOfAttributes(); //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += attrs.toSet(); mRequest.setSubsetOfAttributes( attributeIndexes.toList() ); } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { QgsAttributeList attrs = request.subsetOfAttributes(); Q_FOREACH ( const QString &attr, mRequest.orderBy().usedAttributes() ) { int attrIndex = mSource->mFields.lookupField( attr ); if ( !attrs.contains( attrIndex ) ) attrs << attrIndex; } mRequest.setSubsetOfAttributes( attrs ); }
QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request ) , mFetchedFid( false ) { if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } if ( !mFilterRect.isNull() ) { // update request to be the unprojected filter rect mRequest.setFilterRect( mFilterRect ); } if ( mRequest.filterType() == QgsFeatureRequest::FilterExpression ) { mRequest.expressionContext()->setFields( mSource->mFields ); mRequest.filterExpression()->prepare( mRequest.expressionContext() ); if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = mRequest.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += mRequest.subsetOfAttributes().toSet(); mRequest.setSubsetOfAttributes( attributeIndexes.toList() ); } } prepareFields(); mHasVirtualAttributes = !mFetchJoinInfo.isEmpty() || !mExpressionFieldInfo.isEmpty(); // by default provider's request is the same mProviderRequest = mRequest; // but we remove any destination CRS parameter - that is handled in QgsVectorLayerFeatureIterator, // not at the provider level. Otherwise virtual fields depending on geometry would have incorrect // values if ( mRequest.destinationCrs().isValid() ) { mProviderRequest.setDestinationCrs( QgsCoordinateReferenceSystem(), mRequest.transformContext() ); } if ( mProviderRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // prepare list of attributes to match provider fields QSet<int> providerSubset; QgsAttributeList subset = mProviderRequest.subsetOfAttributes(); int nPendingFields = mSource->mFields.count(); Q_FOREACH ( int attrIndex, subset ) { if ( attrIndex < 0 || attrIndex >= nPendingFields ) continue; if ( mSource->mFields.fieldOrigin( attrIndex ) == QgsFields::OriginProvider ) providerSubset << mSource->mFields.fieldOriginIndex( attrIndex ); } // This is done in order to be prepared to do fallback order bys // and be sure we have the required columns. // TODO: // It would be nicer to first check if we can compile the order by // and only modify the subset if we cannot. if ( !mProviderRequest.orderBy().isEmpty() ) { Q_FOREACH ( const QString &attr, mProviderRequest.orderBy().usedAttributes() ) { providerSubset << mSource->mFields.lookupField( attr ); } }
QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsVirtualLayerFeatureSource>( source, ownSource, request ) { // NOTE: this is really bad and should be removed. // it's only here to guard mSource->mSqlite - because if the provider is removed // then mSqlite will be meaningless. // this needs to be totally reworked so that mSqlite no longer depends on the provider // and can be fully encapsulated in the source if ( !mSource->mProvider ) { close(); return; } if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } try { QString tableName = mSource->mTableName; QStringList wheres; QString offset; QString subset = mSource->mSubset; if ( !subset.isEmpty() ) { wheres << subset; } if ( !mSource->mDefinition.uid().isNull() ) { // filters are only available when a column with unique id exists if ( mSource->mDefinition.hasDefinedGeometry() && !mFilterRect.isNull() ) { bool do_exact = request.flags() & QgsFeatureRequest::ExactIntersect; QString mbr = QStringLiteral( "%1,%2,%3,%4" ).arg( mFilterRect.xMinimum() ).arg( mFilterRect.yMinimum() ).arg( mFilterRect.xMaximum() ).arg( mFilterRect.yMaximum() ); wheres << quotedColumn( mSource->mDefinition.geometryField() ) + " is not null"; wheres << QStringLiteral( "%1Intersects(%2,BuildMbr(%3))" ) .arg( do_exact ? "" : "Mbr", quotedColumn( mSource->mDefinition.geometryField() ), mbr ); } else if ( request.filterType() == QgsFeatureRequest::FilterFid ) { wheres << QStringLiteral( "%1=%2" ) .arg( quotedColumn( mSource->mDefinition.uid() ) ) .arg( request.filterFid() ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString values = quotedColumn( mSource->mDefinition.uid() ) + " IN ("; bool first = true; const auto constFilterFids = request.filterFids(); for ( QgsFeatureId v : constFilterFids ) { if ( !first ) { values += QLatin1String( "," ); } first = false; values += QString::number( v ); } values += QLatin1String( ")" ); wheres << values; } } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { if ( request.filterFid() >= 0 ) offset = QStringLiteral( " LIMIT 1 OFFSET %1" ).arg( request.filterFid() ); else // never return a feature if the id is negative offset = QStringLiteral( " LIMIT 0" ); } else if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { // if an exact intersection is requested, prepare the geometry to intersect QgsGeometry rectGeom = QgsGeometry::fromRect( mFilterRect ); mRectEngine.reset( QgsGeometry::createGeometryEngine( rectGeom.constGet() ) ); mRectEngine->prepareGeometry(); } } if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // copy only selected fields const auto subsetOfAttributes = request.subsetOfAttributes(); for ( int idx : subsetOfAttributes ) { mAttributes << idx; } // ensure that all attributes required for expression filter are being fetched if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { const auto constReferencedColumns = request.filterExpression()->referencedColumns(); for ( const QString &field : constReferencedColumns ) { int attrIdx = mSource->mFields.lookupField( field ); if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); for ( int attrIdx : usedAttributeIndices ) { if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } } else { mAttributes = mSource->mFields.allAttributesList(); } QString columns; { // the first column is always the uid (or 0) if ( !mSource->mDefinition.uid().isNull() ) { columns = quotedColumn( mSource->mDefinition.uid() ); } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { columns = QString::number( request.filterFid() ); } else { columns = QStringLiteral( "0" ); } } const auto constMAttributes = mAttributes; for ( int i : constMAttributes ) { columns += QLatin1String( "," ); QString cname = mSource->mFields.at( i ).name().toLower(); columns += quotedColumn( cname ); } } // the last column is the geometry, if any if ( ( !( request.flags() & QgsFeatureRequest::NoGeometry ) || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) ) && !mSource->mDefinition.geometryField().isNull() && mSource->mDefinition.geometryField() != QLatin1String( "*no*" ) ) { columns += "," + quotedColumn( mSource->mDefinition.geometryField() ); } mSqlQuery = "SELECT " + columns + " FROM " + tableName; if ( !wheres.isEmpty() ) { mSqlQuery += " WHERE " + wheres.join( QStringLiteral( " AND " ) ); } if ( !offset.isEmpty() ) { mSqlQuery += offset; } mQuery.reset( new Sqlite::Query( mSource->mSqlite, mSqlQuery ) ); mFid = 0; } catch ( std::runtime_error &e ) { QgsMessageLog::logMessage( e.what(), QObject::tr( "VLayer" ) ); close(); } }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , mSharedDS( source->mSharedDS ) , mFirstFieldIsFid( source->mFirstFieldIsFid ) , mFieldsWithoutFid( source->mFieldsWithoutFid ) { for ( const auto &id : mRequest.filterFids() ) { mFilterFids.insert( id ); } mFilterFidsIt = mFilterFids.begin(); // Since connection timeout for OGR connections is problematic and can lead to crashes, disable for now. mRequest.setTimeout( -1 ); if ( mSharedDS ) { mOgrLayer = mSharedDS->getLayerFromNameOrIndex( mSource->mLayerName, mSource->mLayerIndex ); if ( !mOgrLayer ) { return; } } else { //QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection"); mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource, mSource->mShareSameDatasetAmongLayers ), mRequest.timeout(), mRequest.requestMayBeNested() ); if ( !mConn || !mConn->ds ) { return; } if ( mSource->mLayerName.isNull() ) { mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex ); } else { mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() ); } if ( !mOgrLayer ) { return; } if ( !mSource->mSubsetString.isEmpty() ) { mOgrLayerOri = mOgrLayer; mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); // If the mSubsetString was a full SELECT ...., then mOgrLayer will be a OGR SQL layer != mOgrLayerOri mFieldsWithoutFid.clear(); for ( int i = ( mFirstFieldIsFid ) ? 1 : 0; i < mSource->mFields.size(); i++ ) mFieldsWithoutFid.append( mSource->mFields.at( i ) ); if ( !mOgrLayer ) { close(); return; } } } QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr ); if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } mFetchGeometry = ( !mFilterRect.isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) || ( mSource->mOgrGeometryTypeFilter != wkbUnknown ); QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { //ensure that all fields required for filter expressions are prepared QSet<int> attributeIndexes = request.filterExpression()->referencedAttributeIndexes( mSource->mFields ); attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { QSet<int> attributeIndexes; const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); for ( int attrIdx : usedAttributeIndices ) { attributeIndexes << attrIdx; } attributeIndexes += attrs.toSet(); attrs = attributeIndexes.toList(); mRequest.setSubsetOfAttributes( attrs ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) { mFetchGeometry = true; } // make sure we fetch just relevant fields // unless it's a VRT data source filtered by geometry as we don't know which // attributes make up the geometry and OGR won't fetch them to evaluate the // filter if we choose to ignore them (fixes #11223) if ( ( mSource->mDriverName != QLatin1String( "VRT" ) && mSource->mDriverName != QLatin1String( "OGR_VRT" ) ) || mFilterRect.isNull() ) { QgsOgrProviderUtils::setRelevantFields( mOgrLayer, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid, mSource->mSubsetString ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) QgsOgrProviderUtils::setRelevantFields( mOgrLayerOri, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid, mSource->mSubsetString ); } // spatial query to select features if ( !mFilterRect.isNull() ) { OGR_L_SetSpatialFilterRect( mOgrLayer, mFilterRect.xMinimum(), mFilterRect.yMinimum(), mFilterRect.xMaximum(), mFilterRect.yMaximum() ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) OGR_L_SetSpatialFilterRect( mOgrLayerOri, mFilterRect.xMinimum(), mFilterRect.yMinimum(), mFilterRect.xMaximum(), mFilterRect.yMaximum() ); } else { OGR_L_SetSpatialFilter( mOgrLayer, nullptr ); if ( mOgrLayerOri && mOgrLayerOri != mOgrLayer ) OGR_L_SetSpatialFilter( mOgrLayerOri, nullptr ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && QgsSettings().value( QStringLiteral( "qgis/compileExpressions" ), true ).toBool() ) { QgsSqlExpressionCompiler *compiler = nullptr; if ( source->mDriverName == QLatin1String( "SQLite" ) || source->mDriverName == QLatin1String( "GPKG" ) ) { compiler = new QgsSQLiteExpressionCompiler( source->mFields ); } else { compiler = new QgsOgrExpressionCompiler( source ); } QgsSqlExpressionCompiler::Result result = compiler->compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { QString whereClause = compiler->result(); if ( !mSource->mSubsetString.isEmpty() && mOgrLayer == mOgrLayerOri ) { whereClause = QStringLiteral( "(" ) + mSource->mSubsetString + QStringLiteral( ") AND (" ) + whereClause + QStringLiteral( ")" ); } if ( OGR_L_SetAttributeFilter( mOgrLayer, mSource->mEncoding->fromUnicode( whereClause ).constData() ) == OGRERR_NONE ) { //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); mCompileStatus = ( mExpressionCompiled ? Compiled : PartiallyCompiled ); } } else if ( mSource->mSubsetString.isEmpty() ) { OGR_L_SetAttributeFilter( mOgrLayer, nullptr ); } delete compiler; } else if ( mSource->mSubsetString.isEmpty() ) { OGR_L_SetAttributeFilter( mOgrLayer, nullptr ); } //start with first feature rewind(); }