26 #include <QDomElement> 
   35   QList<QgsVectorLayer *> lst;
 
   45 static bool _hasCycleDFS( 
QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
 
   47   if ( mark.value( n ) == 1 ) 
 
   49   if ( mark.value( n ) == 0 ) 
 
   52     const auto outEdges { _outEdges( n ) };
 
   55       if ( _hasCycleDFS( m, mark ) )
 
   66   QMutexLocker locker( &mMutex );
 
   67   mVectorJoins.push_back( joinInfo );
 
   71   QHash<QgsVectorLayer *, int> markDFS;
 
   72   if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
 
   75     mVectorJoins.pop_back();
 
   82     cacheJoinLayer( mVectorJoins.last() );
 
   91     connectJoinedLayer( vl );
 
  105     QMutexLocker locker( &mMutex );
 
  106     for ( 
int i = 0; i < mVectorJoins.size(); ++i )
 
  108       if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
 
  115         mVectorJoins.removeAt( i );
 
  138     if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->
fields().
count() )
 
  147     QVector<int> subsetIndices;
 
  155       if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
 
  156         cacheLayerAttrs.append( joinFieldIndex );
 
  165       QString key = attrs.at( joinFieldIndex ).toString();
 
  169         for ( 
int i = 0; i < subsetIndices.count(); ++i )
 
  170           subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
 
  176         attrs2.remove( joinFieldIndex );  
 
  192   QVector<int> subsetIndices;
 
  193   for ( 
int i = 0; i < joinFieldsSubset.count(); ++i )
 
  195     QString joinedFieldName = joinFieldsSubset.at( i );
 
  196     int index = joinLayerFields.
lookupField( joinedFieldName );
 
  199       subsetIndices.append( index );
 
  203       QgsDebugMsg( 
"Join layer subset field not found: " + joinedFieldName );
 
  207   return subsetIndices;
 
  214   QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
 
  215   for ( 
int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
 
  224     QString joinFieldName = joinIt->joinFieldName();
 
  226     QSet<QString> subset;
 
  227     if ( joinIt->hasSubset() )
 
  230       subset = qgis::listToSet( subsetNames );
 
  233     if ( joinIt->prefix().isNull() )
 
  235       prefix = joinLayer->
name() + 
'_';
 
  239       prefix = joinIt->prefix();
 
  242     for ( 
int idx = 0; idx < joinFields.
count(); ++idx )
 
  245       if ( joinIt->hasSubset() && !subset.contains( joinFields.
at( idx ).
name() ) )
 
  250       if ( joinIt->hasSubset() || joinFields.
at( idx ).
name() != joinFieldName )
 
  262   QMutexLocker locker( &mMutex );
 
  263   QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
 
  264   for ( ; joinIt != mVectorJoins.end(); ++joinIt )
 
  266     if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
 
  267       cacheJoinLayer( *joinIt );
 
  274   QDomElement vectorJoinsElem = document.createElement( QStringLiteral( 
"vectorjoins" ) );
 
  275   layer_node.appendChild( vectorJoinsElem );
 
  276   QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
 
  277   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
 
  282     QDomElement joinElem = document.createElement( QStringLiteral( 
"join" ) );
 
  284     joinElem.setAttribute( QStringLiteral( 
"targetFieldName" ), joinIt->targetFieldName() );
 
  286     joinElem.setAttribute( QStringLiteral( 
"joinLayerId" ), joinIt->joinLayerId() );
 
  287     joinElem.setAttribute( QStringLiteral( 
"joinFieldName" ), joinIt->joinFieldName() );
 
  289     joinElem.setAttribute( QStringLiteral( 
"memoryCache" ), joinIt->isUsingMemoryCache() );
 
  290     joinElem.setAttribute( QStringLiteral( 
"dynamicForm" ), joinIt->isDynamicFormEnabled() );
 
  291     joinElem.setAttribute( QStringLiteral( 
"editable" ), joinIt->isEditable() );
 
  292     joinElem.setAttribute( QStringLiteral( 
"upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
 
  293     joinElem.setAttribute( QStringLiteral( 
"cascadedDelete" ), joinIt->hasCascadedDelete() );
 
  295     if ( joinIt->hasSubset() )
 
  297       QDomElement subsetElem = document.createElement( QStringLiteral( 
"joinFieldsSubset" ) );
 
  300       const auto constSubsetNames = subsetNames;
 
  301       for ( 
const QString &fieldName : constSubsetNames )
 
  303         QDomElement fieldElem = document.createElement( QStringLiteral( 
"field" ) );
 
  304         fieldElem.setAttribute( QStringLiteral( 
"name" ), fieldName );
 
  305         subsetElem.appendChild( fieldElem );
 
  308       joinElem.appendChild( subsetElem );
 
  311     if ( !joinIt->prefix().isNull() )
 
  313       joinElem.setAttribute( QStringLiteral( 
"customPrefix" ), joinIt->prefix() );
 
  314       joinElem.setAttribute( QStringLiteral( 
"hasCustomPrefix" ), 1 );
 
  317     vectorJoinsElem.appendChild( joinElem );
 
  323   mVectorJoins.clear();
 
  324   QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( 
"vectorjoins" ) );
 
  325   if ( !vectorJoinsElem.isNull() )
 
  327     QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( 
"join" ) );
 
  328     for ( 
int i = 0; i < joinList.size(); ++i )
 
  330       QDomElement infoElem = joinList.at( i ).toElement();
 
  332       info.
setJoinFieldName( infoElem.attribute( QStringLiteral( 
"joinFieldName" ) ) );
 
  334       info.
setJoinLayerId( infoElem.attribute( QStringLiteral( 
"joinLayerId" ) ) );
 
  338       info.
setEditable( infoElem.attribute( QStringLiteral( 
"editable" ) ).toInt() );
 
  339       info.
setUpsertOnEdit( infoElem.attribute( QStringLiteral( 
"upsertOnEdit" ) ).toInt() );
 
  340       info.
setCascadedDelete( infoElem.attribute( QStringLiteral( 
"cascadedDelete" ) ).toInt() );
 
  342       QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( 
"joinFieldsSubset" ) );
 
  343       if ( !subsetElem.isNull() )
 
  345         QStringList *fieldNames = 
new QStringList;
 
  346         QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( 
"field" ) );
 
  347         fieldNames->reserve( fieldNodes.count() );
 
  348         for ( 
int i = 0; i < fieldNodes.count(); ++i )
 
  349           *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( 
"name" ) );
 
  353       if ( infoElem.attribute( QStringLiteral( 
"hasCustomPrefix" ) ).toInt() )
 
  354         info.
setPrefix( infoElem.attribute( QStringLiteral( 
"customPrefix" ) ) );
 
  365   bool resolved = 
false;
 
  366   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  368     if ( it->joinLayer() )
 
  371     if ( 
QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->
mapLayer( it->joinLayerId() ) ) )
 
  373       it->setJoinLayer( joinedLayer );
 
  374       connectJoinedLayer( joinedLayer );
 
  388   int joinIndex = mVectorJoins.indexOf( *info );
 
  389   if ( joinIndex == -1 )
 
  392   for ( 
int i = 0; i < fields.
count(); ++i )
 
  409   int sourceJoinIndex = originIndex / 1000;
 
  410   sourceFieldIndex = originIndex % 1000;
 
  412   if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
 
  415   return &( mVectorJoins[sourceJoinIndex] );
 
  420   QList<const QgsVectorLayerJoinInfo *> infos;
 
  422   const auto constMVectorJoins = mVectorJoins;
 
  425     if ( infos.contains( &info ) )
 
  428     if ( info.targetFieldName() == 
field.
name() )
 
  429       infos.append( &info );
 
  456   return joinedFeature;
 
  476   return targetedFeature;
 
  482   cloned->mVectorJoins = mVectorJoins;
 
  486 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
 
  491   QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  492   Q_ASSERT( joinedLayer );
 
  495   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  497     if ( joinedLayer == it->joinLayer() )
 
  499       it->cachedAttributes.clear();
 
  500       cacheJoinLayer( *it );
 
  507 void QgsVectorLayerJoinBuffer::joinedLayerModified()
 
  509   QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  510   Q_ASSERT( joinedLayer );
 
  513   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  515     if ( joinedLayer == it->joinLayer() )
 
  517       it->cacheDirty = 
true;
 
  522 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
 
  524   QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  525   Q_ASSERT( joinedLayer );
 
  530 void QgsVectorLayerJoinBuffer::connectJoinedLayer( 
QgsVectorLayer *vl )
 
  548     if ( joinLayer && joinLayer->
isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
 
  552       for ( 
const QgsFeature &feature : std::as_const( features ) )
 
  554         const QgsFeature joinFeature = info.extractJoinedFeature( feature );
 
  558         const QVariant idFieldValue = feature.
attribute( info.targetFieldName() );
 
  571         if ( existingFeature.
isValid() )
 
  573           if ( info.hasSubset() )
 
  576             const auto constSubsetNames = subsetNames;
 
  577             for ( 
const QString &
field : constSubsetNames )
 
  587             for ( 
const auto &
field : joinFields )
 
  598           bool notNullFields = 
false;
 
  600           for ( 
const auto &
field : joinFields )
 
  602             if ( 
field.
name() == info.joinFieldName() )
 
  607               notNullFields = 
true;
 
  613             joinFeatures << joinFeature;
 
  656   for ( 
auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
 
  658     const int field = it.key();
 
  659     const QVariant newValue = it.value();
 
  662     if ( oldValues.contains( 
field ) )
 
  663       oldValue = oldValues[
field];
 
  681   const auto constFids = fids;
 
  687       if ( info.isEditable() && info.hasCascadedDelete() )
 
  691           info.joinLayer()->deleteFeature( joinFeature.
id(), context );
 
Class allowing to manage the auxiliary storage for a vector layer.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
void setName(const QString &name)
Set the field name.
Container of fields for a vector layer.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Manages joined fields for a vector layer.
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project's available layers.
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
void readXml(const QDomNode &layer_node)
Reads joins from project file.
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
const QgsVectorJoinList & vectorJoins() const
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers.
bool deleteFeatures(const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a list of features from joined layers.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
bool containsJoins() const
Quick way to test if there is any join at all.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features in joined layers.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g.
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Returns a vector of indices for use in join based on field names from the layer.
bool deleteFeature(QgsFeatureId fid, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a feature from joined layers.
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.
Defines left outer join from our vector layer to some other vector layer.
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields.
bool cacheDirty
True if the cached join attributes need to be updated.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
void setCascadedDelete(bool enabled)
Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the cor...
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups.
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
bool hasSubset(bool blocklisted=true) const
Returns true if blocklisted fields is not empty or if a subset of names has been set.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Sets the subset of fields to be used from joined layer.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a list of features to the sink.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
void layerModified()
Emitted when modifications has been done on layer.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QMap< int, QVariant > QgsAttributeMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Context for cascade delete features.