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 );
   103   QMutexLocker locker( &mMutex );
   105   for ( 
int i = 0; i < mVectorJoins.size(); ++i )
   107     if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
   114       mVectorJoins.removeAt( i );
   136     if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->
fields().
count() )
   145     QVector<int> subsetIndices;
   153       if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
   154         cacheLayerAttrs.append( joinFieldIndex );
   163       QString key = attrs.at( joinFieldIndex ).toString();
   167         for ( 
int i = 0; i < subsetIndices.count(); ++i )
   168           subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
   174         attrs2.remove( joinFieldIndex );  
   185   QVector<int> subsetIndices;
   187   for ( 
int i = 0; i < joinFieldsSubset.count(); ++i )
   189     QString joinedFieldName = joinFieldsSubset.
at( i );
   193       subsetIndices.append( index );
   197       QgsDebugMsg( 
"Join layer subset field not found: " + joinedFieldName );
   201   return subsetIndices;
   208   QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
   209   for ( 
int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
   218     QString joinFieldName = joinIt->joinFieldName();
   220     QSet<QString> subset;
   221     if ( joinIt->hasSubset() )
   224       subset = QSet<QString>::fromList( subsetNames );
   227     if ( joinIt->prefix().isNull() )
   229       prefix = joinLayer->
name() + 
'_';
   233       prefix = joinIt->prefix();
   236     for ( 
int idx = 0; idx < joinFields.
count(); ++idx )
   239       if ( joinIt->hasSubset() && !subset.contains( joinFields.
at( idx ).
name() ) )
   244       if ( joinIt->hasSubset() || joinFields.
at( idx ).
name() != joinFieldName )
   256   QMutexLocker locker( &mMutex );
   257   QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
   258   for ( ; joinIt != mVectorJoins.end(); ++joinIt )
   260     if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
   261       cacheJoinLayer( *joinIt );
   268   QDomElement vectorJoinsElem = document.createElement( QStringLiteral( 
"vectorjoins" ) );
   269   layer_node.appendChild( vectorJoinsElem );
   270   QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
   271   for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
   276     QDomElement joinElem = document.createElement( QStringLiteral( 
"join" ) );
   278     joinElem.setAttribute( QStringLiteral( 
"targetFieldName" ), joinIt->targetFieldName() );
   280     joinElem.setAttribute( QStringLiteral( 
"joinLayerId" ), joinIt->joinLayerId() );
   281     joinElem.setAttribute( QStringLiteral( 
"joinFieldName" ), joinIt->joinFieldName() );
   283     joinElem.setAttribute( QStringLiteral( 
"memoryCache" ), joinIt->isUsingMemoryCache() );
   284     joinElem.setAttribute( QStringLiteral( 
"dynamicForm" ), joinIt->isDynamicFormEnabled() );
   285     joinElem.setAttribute( QStringLiteral( 
"editable" ), joinIt->isEditable() );
   286     joinElem.setAttribute( QStringLiteral( 
"upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
   287     joinElem.setAttribute( QStringLiteral( 
"cascadedDelete" ), joinIt->hasCascadedDelete() );
   289     if ( joinIt->hasSubset() )
   291       QDomElement subsetElem = document.createElement( QStringLiteral( 
"joinFieldsSubset" ) );
   294       const auto constSubsetNames = subsetNames;
   295       for ( 
const QString &fieldName : constSubsetNames )
   297         QDomElement fieldElem = document.createElement( QStringLiteral( 
"field" ) );
   298         fieldElem.setAttribute( QStringLiteral( 
"name" ), fieldName );
   299         subsetElem.appendChild( fieldElem );
   302       joinElem.appendChild( subsetElem );
   305     if ( !joinIt->prefix().isNull() )
   307       joinElem.setAttribute( QStringLiteral( 
"customPrefix" ), joinIt->prefix() );
   308       joinElem.setAttribute( QStringLiteral( 
"hasCustomPrefix" ), 1 );
   311     vectorJoinsElem.appendChild( joinElem );
   317   mVectorJoins.clear();
   318   QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( 
"vectorjoins" ) );
   319   if ( !vectorJoinsElem.isNull() )
   321     QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( 
"join" ) );
   322     for ( 
int i = 0; i < joinList.size(); ++i )
   324       QDomElement infoElem = joinList.at( i ).toElement();
   326       info.
setJoinFieldName( infoElem.attribute( QStringLiteral( 
"joinFieldName" ) ) );
   328       info.
setJoinLayerId( infoElem.attribute( QStringLiteral( 
"joinLayerId" ) ) );
   332       info.
setEditable( infoElem.attribute( QStringLiteral( 
"editable" ) ).toInt() );
   333       info.
setUpsertOnEdit( infoElem.attribute( QStringLiteral( 
"upsertOnEdit" ) ).toInt() );
   334       info.
setCascadedDelete( infoElem.attribute( QStringLiteral( 
"cascadedDelete" ) ).toInt() );
   336       QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( 
"joinFieldsSubset" ) );
   337       if ( !subsetElem.isNull() )
   339         QStringList *fieldNames = 
new QStringList;
   340         QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( 
"field" ) );
   341         fieldNames->reserve( fieldNodes.count() );
   342         for ( 
int i = 0; i < fieldNodes.count(); ++i )
   343           *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( 
"name" ) );
   347       if ( infoElem.attribute( QStringLiteral( 
"hasCustomPrefix" ) ).toInt() )
   348         info.
setPrefix( infoElem.attribute( QStringLiteral( 
"customPrefix" ) ) );
   359   bool resolved = 
false;
   360   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
   362     if ( it->joinLayer() )
   365     if ( 
QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->
mapLayer( it->joinLayerId() ) ) )
   367       it->setJoinLayer( joinedLayer );
   368       connectJoinedLayer( joinedLayer );
   382   int joinIndex = mVectorJoins.indexOf( *info );
   383   if ( joinIndex == -1 )
   386   for ( 
int i = 0; i < fields.
count(); ++i )
   403   int sourceJoinIndex = originIndex / 1000;
   404   sourceFieldIndex = originIndex % 1000;
   406   if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
   409   return &( mVectorJoins[sourceJoinIndex] );
   414   QList<const QgsVectorLayerJoinInfo *> infos;
   416   const auto constMVectorJoins = mVectorJoins;
   419     if ( infos.contains( &info ) )
   422     if ( info.targetFieldName() == field.
name() )
   423       infos.append( &info );
   450   return joinedFeature;
   470   return targetedFeature;
   476   cloned->mVectorJoins = mVectorJoins;
   480 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
   486   Q_ASSERT( joinedLayer );
   489   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
   491     if ( joinedLayer == it->joinLayer() )
   493       it->cachedAttributes.clear();
   494       cacheJoinLayer( *it );
   501 void QgsVectorLayerJoinBuffer::joinedLayerModified()
   504   Q_ASSERT( joinedLayer );
   507   for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
   509     if ( joinedLayer == it->joinLayer() )
   511       it->cacheDirty = 
true;
   516 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
   519   Q_ASSERT( joinedLayer );
   524 void QgsVectorLayerJoinBuffer::connectJoinedLayer( 
QgsVectorLayer *vl )
   542     if ( joinLayer && joinLayer->
isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
   546       const auto constFeatures = features;
   547       for ( 
const QgsFeature &feature : constFeatures )
   549         const QgsFeature joinFeature = info.extractJoinedFeature( feature );
   553         const QVariant idFieldValue = feature.
attribute( info.targetFieldName() );
   566         if ( existingFeature.
isValid() )
   568           if ( info.hasSubset() )
   571             const auto constSubsetNames = subsetNames;
   572             for ( 
const QString &field : constSubsetNames )
   574               QVariant newValue = joinFeature.
attribute( field );
   582             for ( 
const auto &field : joinFields )
   584               QVariant newValue = joinFeature.
attribute( field.name() );
   585               int fieldIndex = joinLayer->
fields().
indexOf( field.name() );
   593           bool notNullFields = 
false;
   595           for ( 
const auto &field : joinFields )
   597             if ( field.name() == info.joinFieldName() )
   600             if ( !joinFeature.
attribute( field.name() ).isNull() )
   602               notNullFields = 
true;
   608             joinFeatures << joinFeature;
   651   for ( 
auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
   653     const int field = it.key();
   654     const QVariant newValue = it.value();
   657     if ( oldValues.contains( field ) )
   658       oldValue = oldValues[field];
   676   const auto constFids = fids;
   682       if ( info.isEditable() && info.hasCascadedDelete() )
   686           info.joinLayer()->deleteFeature( joinFeature.
id() );
 int lookupField(const QString &fieldName) const
Looks up field's index from the field name. 
 
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features in joined layers. 
 
bool hasSubset(bool blacklisted=true) const
Returns true if blacklisted fields is not empty or if a subset of names has been set. 
 
bool isValid() const
Returns the validity of this feature. 
 
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node. 
 
Wrapper for iterator of features from vector data provider or vector layer. 
 
QString targetFieldName() const
Returns name of the field of our layer that will be used for join. 
 
bool containsJoins() const
Quick way to test if there is any join at all. 
 
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
 
QSet< QgsFeatureId > QgsFeatureIds
 
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration) 
 
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name. 
 
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins. 
 
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields. 
 
Class allowing to manage the auxiliary storage for a vector layer. 
 
QList< QgsFeature > QgsFeatureList
 
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
 
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id. 
 
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched. 
 
void readXml(const QDomNode &layer_node)
Reads joins from project file. 
 
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join. 
 
Container of fields for a vector layer. 
 
void setName(const QString &name)
Set the field name. 
 
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index. 
 
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
 
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
 
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
 
int count() const
Returns number of items. 
 
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) ...
 
Manages joined fields for a vector layer. 
 
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1) 
 
int fieldOriginIndex(int fieldIdx) const
Gets field's origin index (its meaning is specific to each type of origin) 
 
bool isEditable() const FINAL
Returns true if the provider is in editing mode. 
 
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers. 
 
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
 
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression. 
 
int indexFromName(const QString &fieldName) const
Gets the field index from the field name. 
 
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
 
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject. 
 
QgsFields fields() const FINAL
Returns the list of fields of this layer. 
 
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet) ...
 
bool deleteFeature(QgsFeatureId fid) const
Deletes a feature from joined layers. 
 
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers. 
 
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched. 
 
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields. 
 
bool deleteFeatures(const QgsFeatureIds &fids) const
Deletes a list of features from joined layers. 
 
Defines left outer join from our vector layer to some other vector layer. 
 
QMap< int, QVariant > QgsAttributeMap
 
This class wraps a request for features to a vector layer (or directly its vector data provider)...
 
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join. 
 
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a list of features to the sink. 
 
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) ...
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc. 
 
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups. 
 
const QgsVectorJoinList & vectorJoins() const
 
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id. 
 
Encapsulate a field in an attribute table or data source. 
 
bool cacheDirty
True if the cached join attributes need to be updated. 
 
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join. 
 
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value. 
 
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer. 
 
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise. 
 
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If nullptr, joined layer's name will be used...
 
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. 
 
int indexOf(const QString &fieldName) const
Gets the field index from the field name. 
 
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. 
 
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index. 
 
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request. 
 
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature. 
 
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups...
 
void setCascadedDelete(bool enabled)
Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the cor...
 
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g. 
 
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID. 
 
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request. 
 
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). 
 
QList< int > QgsAttributeList
 
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer. ...
 
bool nextFeature(QgsFeature &f)
 
Geometry is not required. It may still be returned if e.g. required for a filter condition. 
 
Represents a vector layer which manages a vector based data sets. 
 
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name. 
 
void updatedFields()
Emitted whenever the fields available from this layer have been changed. 
 
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join. 
 
void layerModified()
Emitted when modifications has been done on layer. 
 
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Sets the subset of fields to be used from joined layer. 
 
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer. 
 
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project's available layers. 
 
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
 
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields. 
 
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer. 
 
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched. 
 
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.