void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest& request ) { mFallbackStatement.clear(); mStatement.clear(); bool limitAtProvider = ( mRequest.limit() >= 0 ); // build sql statement // note: 'SELECT ' is added later, to account for 'SELECT TOP...' type queries mStatement += QString( "[%1]" ).arg( mSource->mFidColName ); mFidCol = mSource->mFields.indexFromName( mSource->mFidColName ); mAttributesToFetch.append( mFidCol ); bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; QgsAttributeList attrs = subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( subsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , mFeatureFetched( false ) , mConn( nullptr ) , ogrLayer( nullptr ) , mSubsetStringSet( false ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mFilterFids( mRequest.filterFids() ) , mFilterFidsIt( mFilterFids.constBegin() ) { mConn = QgsOgrConnPool::instance()->acquireConnection( mSource->mProvider->dataSourceUri() ); if ( !mConn->ds ) { return; } if ( mSource->mLayerName.isNull() ) { ogrLayer = OGR_DS_GetLayer( mConn->ds, mSource->mLayerIndex ); } else { ogrLayer = OGR_DS_GetLayerByName( mConn->ds, TO8( mSource->mLayerName ) ); } if ( !ogrLayer ) { return; } if ( !mSource->mSubsetString.isEmpty() ) { ogrLayer = QgsOgrProviderUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); if ( !ogrLayer ) { return; } mSubsetStringSet = true; } mFetchGeometry = ( !mRequest.filterRect().isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); 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 ) { Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); }
void QgsDb2FeatureIterator::BuildStatement( const QgsFeatureRequest &request ) { bool limitAtProvider = ( mRequest.limit() >= 0 ); QString delim; // build sql statement mStatement = QStringLiteral( "SELECT " ); if ( !mSource->mFidColName.isEmpty() ) { mStatement += mSource->mFidColName; mFidCol = mSource->mFields.indexFromName( mSource->mFidColName ); mAttributesToFetch.append( mFidCol ); delim = QStringLiteral( "," ); } bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; QgsAttributeList attrs = subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( 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(); } Q_FOREACH ( int i, attrs ) { QString fieldname = mSource->mFields.at( i ).name(); if ( mSource->mFidColName == fieldname ) continue; mStatement += delim + fieldname; delim = QStringLiteral( "," ); mAttributesToFetch.append( i ); QgsDebugMsg( QString( "i: %1; name: %2" ).arg( i ).arg( fieldname ) ); }
void QgsMssqlFeatureIterator::BuildStatement( const QgsFeatureRequest &request ) { mFallbackStatement.clear(); mStatement.clear(); bool limitAtProvider = ( mRequest.limit() >= 0 ); // build sql statement // note: 'SELECT ' is added later, to account for 'SELECT TOP...' type queries mStatement += QStringLiteral( "[%1]" ).arg( mSource->mFidColName ); mFidCol = mSource->mFields.indexFromName( mSource->mFidColName ); mAttributesToFetch.append( mFidCol ); bool subsetOfAttributes = mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes; QgsAttributeList attrs = subsetOfAttributes ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // ensure that all attributes required for expression filter are being fetched if ( 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(); } Q_FOREACH ( int i, attrs ) { QString fieldname = mSource->mFields.at( i ).name(); if ( mSource->mFidColName == fieldname ) continue; mStatement += QStringLiteral( ",[%1]" ).arg( fieldname ); mAttributesToFetch.append( i ); }
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 ); }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , ogrLayer( 0 ) , mSubsetStringSet( false ) , mGeometrySimplifier( NULL ) , mExpressionCompiled( false ) { mFeatureFetched = false; mConn = QgsOgrConnPool::instance()->acquireConnection( mSource->mFilePath ); if ( mSource->mLayerName.isNull() ) { ogrLayer = OGR_DS_GetLayer( mConn->ds, mSource->mLayerIndex ); } else { ogrLayer = OGR_DS_GetLayerByName( mConn->ds, TO8( mSource->mLayerName ) ); } if ( !mSource->mSubsetString.isEmpty() ) { ogrLayer = QgsOgrUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); mSubsetStringSet = true; } mFetchGeometry = ( !mRequest.filterRect().isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); QgsAttributeList attrs = ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) ? mRequest.subsetOfAttributes() : mSource->mFields.allAttributesList(); // 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 != "VRT" && mSource->mDriverName != "OGR_VRT" ) || mRequest.filterRect().isNull() ) { QgsOgrUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs ); } // spatial query to select features if ( !mRequest.filterRect().isNull() ) { const QgsRectangle& rect = mRequest.filterRect(); OGR_L_SetSpatialFilterRect( ogrLayer, rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() ); } else { OGR_L_SetSpatialFilter( ogrLayer, 0 ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsOgrExpressionCompiler compiler = QgsOgrExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { QString whereClause = compiler.result(); if ( OGR_L_SetAttributeFilter( ogrLayer, whereClause.toLocal8Bit().data() ) == OGRERR_NONE ) { //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } else { OGR_L_SetAttributeFilter( ogrLayer, 0 ); } } else { OGR_L_SetAttributeFilter( ogrLayer, 0 ); } //start with first feature rewind(); }
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 ); }
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(); }
QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>( source, ownSource, request ) , mFeatureQueueSize( sFeatureQueueSize ) , mFetched( 0 ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mLastFetch( false ) { if ( !source->mTransactionConnection ) { mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo ); mIsTransactionConnection = false; } else { mConn = source->mTransactionConnection; mConn->lock(); mIsTransactionConnection = true; } if ( !mConn ) { mClosed = true; iteratorClosed(); return; } mCursorName = mConn->uniqueCursorName(); QString whereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { whereClause = whereClauseRect(); } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString fidsWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source ); if ( compiler.compile( request.filterExpression() ) == QgsSqlExpressionCompiler::Complete ) { whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() ); mExpressionCompiled = true; } } if ( !mSource->mSqlWhereClause.isEmpty() ) { if ( !whereClause.isEmpty() ) whereClause += " AND "; whereClause += '(' + mSource->mSqlWhereClause + ')'; } if ( !declareCursor( whereClause ) ) { mClosed = true; iteratorClosed(); return; } mFetched = 0; }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( nullptr ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; bool useFallbackWhereClause = false; QString fallbackWhereClause; QString whereClause; //beware - limitAtProvider needs to be set to false if the request cannot be completely handled //by the provider (eg utilising QGIS expression filters) bool limitAtProvider = ( mRequest.limit() >= 0 ); if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } //IMPORTANT - this MUST be the last clause added! else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { useFallbackWhereClause = true; fallbackWhereClause = whereClauses.join( " AND " ); whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } if ( result != QgsSqlExpressionCompiler::Complete ) { //can't apply limit at provider side as we need to check all results using QGIS expressions limitAtProvider = false; } } else { limitAtProvider = false; } } whereClause = whereClauses.join( " AND " ); // Setup the order by QStringList orderByParts; mOrderByCompiled = true; if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { Q_FOREACH ( const QgsFeatureRequest::OrderByClause& clause, request.orderBy() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsExpression expression = clause.expression(); if ( compiler.compile( &expression ) == QgsSqlExpressionCompiler::Complete ) { QString part; part = compiler.result(); if ( clause.nullsFirst() ) orderByParts << QString( "%1 IS NOT NULL" ).arg( part ); else orderByParts << QString( "%1 IS NULL" ).arg( part ); part += clause.ascending() ? " COLLATE NOCASE ASC" : " COLLATE NOCASE DESC"; orderByParts << part; } else { // Bail out on first non-complete compilation. // Most important clauses at the beginning of the list // will still be sent and used to pre-sort so the local // CPU can use its cycles for fine-tuning. mOrderByCompiled = false; break; } } }
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(); } }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( nullptr ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; bool useFallbackWhereClause = false; QString fallbackWhereClause; QString whereClause; //beware - limitAtProvider needs to be set to false if the request cannot be completely handled //by the provider (eg utilising QGIS expression filters) bool limitAtProvider = ( mRequest.limit() >= 0 ); if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } //IMPORTANT - this MUST be the last clause added! else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && request.filterType() == QgsFeatureRequest::FilterExpression ) { QgsAttributeList attrs = request.subsetOfAttributes(); Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); } if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { useFallbackWhereClause = true; fallbackWhereClause = whereClauses.join( " AND " ); whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } if ( result != QgsSqlExpressionCompiler::Complete ) { //can't apply limit at provider side as we need to check all results using QGIS expressions limitAtProvider = false; } } else { limitAtProvider = false; } }
QgsFeatureRequest parseFilterElement( const QString &typeName, QDomElement &filterElem, const QgsProject *project ) { QgsFeatureRequest request; QDomNodeList fidNodes = filterElem.elementsByTagName( QStringLiteral( "FeatureId" ) ); QDomNodeList goidNodes = filterElem.elementsByTagName( QStringLiteral( "GmlObjectId" ) ); if ( !fidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement fidElem; for ( int f = 0; f < fidNodes.size(); f++ ) { fidElem = fidNodes.at( f ).toElement(); if ( !fidElem.hasAttribute( QStringLiteral( "fid" ) ) ) { throw QgsRequestNotWellFormedException( "FeatureId element without fid attribute" ); } QString fid = fidElem.attribute( QStringLiteral( "fid" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No FeatureId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( !goidNodes.isEmpty() ) { QgsFeatureIds fids; QDomElement goidElem; for ( int f = 0; f < goidNodes.size(); f++ ) { goidElem = goidNodes.at( f ).toElement(); if ( !goidElem.hasAttribute( QStringLiteral( "id" ) ) && !goidElem.hasAttribute( QStringLiteral( "gml:id" ) ) ) { throw QgsRequestNotWellFormedException( "GmlObjectId element without gml:id attribute" ); } QString fid = goidElem.attribute( QStringLiteral( "id" ) ); if ( fid.isEmpty() ) fid = goidElem.attribute( QStringLiteral( "gml:id" ) ); if ( fid.contains( QLatin1String( "." ) ) ) { if ( fid.section( QStringLiteral( "." ), 0, 0 ) != typeName ) continue; fid = fid.section( QStringLiteral( "." ), 1, 1 ); } fids.insert( fid.toInt() ); } if ( !fids.isEmpty() ) { request.setFilterFids( fids ); } else { throw QgsRequestNotWellFormedException( QStringLiteral( "No GmlObjectId element correctly parse against typeName '%1'" ).arg( typeName ) ); } request.setFlags( QgsFeatureRequest::NoFlags ); return request; } else if ( filterElem.firstChildElement().tagName() == QLatin1String( "BBOX" ) ) { QDomElement bboxElem = filterElem.firstChildElement(); QDomElement childElem = bboxElem.firstChildElement(); while ( !childElem.isNull() ) { if ( childElem.tagName() == QLatin1String( "Box" ) ) { request.setFilterRect( QgsOgcUtils::rectangleFromGMLBox( childElem ) ); } else if ( childElem.tagName() != QLatin1String( "PropertyName" ) ) { QgsGeometry geom = QgsOgcUtils::geometryFromGML( childElem ); request.setFilterRect( geom.boundingBox() ); } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } // Apply BBOX through filterRect even inside an And to use spatial index else if ( filterElem.firstChildElement().tagName() == QLatin1String( "And" ) && !filterElem.firstChildElement().firstChildElement( QLatin1String( "BBOX" ) ).isNull() ) { QDomElement childElem = filterElem.firstChildElement().firstChildElement(); while ( !childElem.isNull() ) { QDomElement childFilterElement = filterElem.ownerDocument().createElement( QLatin1String( "Filter" ) ); childFilterElement.appendChild( childElem.cloneNode( true ) ); QgsFeatureRequest childRequest = parseFilterElement( typeName, childFilterElement ); if ( childElem.tagName() == QLatin1String( "BBOX" ) ) { if ( request.filterRect().isEmpty() ) { request.setFilterRect( childRequest.filterRect() ); } else { request.setFilterRect( request.filterRect().intersect( childRequest.filterRect() ) ); } } else { if ( !request.filterExpression() ) { request.setFilterExpression( childRequest.filterExpression()->expression() ); } else { QgsExpressionNode *opLeft = request.filterExpression()->rootNode()->clone(); QgsExpressionNode *opRight = childRequest.filterExpression()->rootNode()->clone(); std::unique_ptr<QgsExpressionNodeBinaryOperator> node = qgis::make_unique<QgsExpressionNodeBinaryOperator>( QgsExpressionNodeBinaryOperator::boAnd, opLeft, opRight ); QgsExpression expr( node->dump() ); request.setFilterExpression( expr ); } } childElem = childElem.nextSiblingElement(); } request.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoFlags ); return request; } else { QgsVectorLayer *layer = nullptr; if ( project != nullptr ) { layer = layerByTypeName( project, typeName ); } std::shared_ptr<QgsExpression> filter( QgsOgcUtils::expressionFromOgcFilter( filterElem, layer ) ); if ( filter ) { if ( filter->hasParserError() ) { throw QgsRequestNotWellFormedException( filter->parserErrorString() ); } if ( filter->needsGeometry() ) { request.setFlags( QgsFeatureRequest::NoFlags ); } request.setFilterExpression( filter->expression() ); return request; } } return request; }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( NULL ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; QString whereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } whereClause = whereClauses.join( " AND " ); // preparing the SQL statement if ( !prepareStatement( whereClause ) ) { // some error occurred sqliteStatement = NULL; close(); return; } }
QgsSpatiaLiteFeatureIterator::QgsSpatiaLiteFeatureIterator( QgsSpatiaLiteFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsSpatiaLiteFeatureSource>( source, ownSource, request ) , sqliteStatement( NULL ) , mExpressionCompiled( false ) { mHandle = QgsSpatiaLiteConnPool::instance()->acquireConnection( mSource->mSqlitePath ); mFetchGeometry = !mSource->mGeometryColumn.isNull() && !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); mHasPrimaryKey = !mSource->mPrimaryKey.isEmpty(); mRowNumber = 0; QStringList whereClauses; bool useFallbackWhereClause = false; QString fallbackWhereClause; QString whereClause; //beware - limitAtProvider needs to be set to false if the request cannot be completely handled //by the provider (eg utilising QGIS expression filters) bool limitAtProvider = ( mRequest.limit() >= 0 ); if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { // some kind of MBR spatial filtering is required whereClause = whereClauseRect(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( !mSource->mSubsetString.isEmpty() ) { whereClause = "( " + mSource->mSubsetString + ')'; if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { whereClause = whereClauseFid(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { whereClause = whereClauseFids(); if ( ! whereClause.isEmpty() ) { whereClauses.append( whereClause ); } } //IMPORTANT - this MUST be the last clause added! else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSpatiaLiteExpressionCompiler compiler = QgsSpatiaLiteExpressionCompiler( source ); QgsSqlExpressionCompiler::Result result = compiler.compile( request.filterExpression() ); if ( result == QgsSqlExpressionCompiler::Complete || result == QgsSqlExpressionCompiler::Partial ) { whereClause = compiler.result(); if ( !whereClause.isEmpty() ) { useFallbackWhereClause = true; fallbackWhereClause = whereClauses.join( " AND " ); whereClauses.append( whereClause ); //if only partial success when compiling expression, we need to double-check results using QGIS' expressions mExpressionCompiled = ( result == QgsSqlExpressionCompiler::Complete ); } } if ( result != QgsSqlExpressionCompiler::Complete ) { //can't apply limit at provider side as we need to check all results using QGIS expressions limitAtProvider = false; } } else { limitAtProvider = false; } } whereClause = whereClauses.join( " AND " ); // preparing the SQL statement bool success = prepareStatement( whereClause, limitAtProvider ? mRequest.limit() : -1 ); if ( !success && useFallbackWhereClause ) { //try with the fallback where clause, eg for cases when using compiled expression failed to prepare mExpressionCompiled = false; success = prepareStatement( fallbackWhereClause, -1 ); } if ( !success ) { // some error occurred sqliteStatement = NULL; close(); } }
QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request ) , mFeatureFetched( false ) , mConn( nullptr ) , ogrLayer( nullptr ) , mSubsetStringSet( false ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mFilterFids( mRequest.filterFids() ) , mFilterFidsIt( mFilterFids.constBegin() ) { mConn = QgsOgrConnPool::instance()->acquireConnection( mSource->mProvider->dataSourceUri() ); if ( !mConn->ds ) { return; } if ( mSource->mLayerName.isNull() ) { ogrLayer = OGR_DS_GetLayer( mConn->ds, mSource->mLayerIndex ); } else { ogrLayer = OGR_DS_GetLayerByName( mConn->ds, TO8( mSource->mLayerName ) ); } if ( !ogrLayer ) { return; } if ( !mSource->mSubsetString.isEmpty() ) { ogrLayer = QgsOgrProviderUtils::setSubsetString( ogrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString ); if ( !ogrLayer ) { return; } mSubsetStringSet = true; } mFetchGeometry = ( !mRequest.filterRect().isNull() ) || !( mRequest.flags() & QgsFeatureRequest::NoGeometry ); 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 ); } 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 != "VRT" && mSource->mDriverName != "OGR_VRT" ) || mRequest.filterRect().isNull() ) { QgsOgrProviderUtils::setRelevantFields( ogrLayer, mSource->mFields.count(), mFetchGeometry, attrs, mSource->mFirstFieldIsFid ); } // spatial query to select features if ( !mRequest.filterRect().isNull() ) { const QgsRectangle& rect = mRequest.filterRect(); OGR_L_SetSpatialFilterRect( ogrLayer, rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() ); } else { OGR_L_SetSpatialFilter( ogrLayer, nullptr ); } if ( request.filterType() == QgsFeatureRequest::FilterExpression && QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { QgsSqlExpressionCompiler* compiler; if ( source->mDriverName == "SQLite" || source->mDriverName == "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 ( OGR_L_SetAttributeFilter( ogrLayer, 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 { OGR_L_SetAttributeFilter( ogrLayer, nullptr ); } delete compiler; } else { OGR_L_SetAttributeFilter( ogrLayer, nullptr ); } //start with first feature rewind(); }
QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource* source, bool ownSource, const QgsFeatureRequest& request ) : QgsAbstractFeatureIteratorFromSource<QgsPostgresFeatureSource>( source, ownSource, request ) , mFeatureQueueSize( sFeatureQueueSize ) , mFetched( 0 ) , mFetchGeometry( false ) , mExpressionCompiled( false ) , mOrderByCompiled( false ) , mLastFetch( false ) , mFilterRequiresGeometry( false ) { if ( !source->mTransactionConnection ) { mConn = QgsPostgresConnPool::instance()->acquireConnection( mSource->mConnInfo ); mIsTransactionConnection = false; } else { mConn = source->mTransactionConnection; mIsTransactionConnection = true; } if ( !mConn ) { mClosed = true; iteratorClosed(); return; } mCursorName = mConn->uniqueCursorName(); QString whereClause; bool limitAtProvider = ( mRequest.limit() >= 0 ); bool useFallbackWhereClause = false; QString fallbackWhereClause; if ( !request.filterRect().isNull() && !mSource->mGeometryColumn.isNull() ) { whereClause = whereClauseRect(); } if ( !mSource->mSqlWhereClause.isEmpty() ) { whereClause = QgsPostgresUtils::andWhereClauses( whereClause, '(' + mSource->mSqlWhereClause + ')' ); } if ( request.filterType() == QgsFeatureRequest::FilterFid ) { QString fidWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFid(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString fidsWhereClause = QgsPostgresUtils::whereClause( mRequest.filterFids(), mSource->mFields, mConn, mSource->mPrimaryKeyType, mSource->mPrimaryKeyAttrs, mSource->mShared ); whereClause = QgsPostgresUtils::andWhereClauses( whereClause, fidsWhereClause ); } else if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { // ensure that all attributes required for expression filter are being fetched if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) { QgsAttributeList attrs = mRequest.subsetOfAttributes(); Q_FOREACH ( const QString& field, request.filterExpression()->referencedColumns() ) { int attrIdx = mSource->mFields.fieldNameIndex( field ); if ( !attrs.contains( attrIdx ) ) attrs << attrIdx; } mRequest.setSubsetOfAttributes( attrs ); } mFilterRequiresGeometry = request.filterExpression()->needsGeometry(); if ( QSettings().value( "/qgis/compileExpressions", true ).toBool() ) { //IMPORTANT - this MUST be the last clause added! QgsPostgresExpressionCompiler compiler = QgsPostgresExpressionCompiler( source ); if ( compiler.compile( request.filterExpression() ) == QgsSqlExpressionCompiler::Complete ) { useFallbackWhereClause = true; fallbackWhereClause = whereClause; whereClause = QgsPostgresUtils::andWhereClauses( whereClause, compiler.result() ); mExpressionCompiled = true; mCompileStatus = Compiled; } else { limitAtProvider = false; } } else { limitAtProvider = false; } }