19#include "moc_qgsvectorlayerjoinbuffer.cpp" 
   36  QList<QgsVectorLayer *> lst;
 
   46static bool _hasCycleDFS( 
QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
 
   48  if ( mark.value( n ) == 1 ) 
 
   50  if ( mark.value( n ) == 0 ) 
 
   53    const auto outEdges { _outEdges( n ) };
 
   56      if ( _hasCycleDFS( m, mark ) )
 
   67  QMutexLocker locker( &mMutex );
 
   68  mVectorJoins.push_back( joinInfo );
 
   72  QHash<QgsVectorLayer *, int> markDFS;
 
   73  if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
 
   76    mVectorJoins.pop_back();
 
   86    connectJoinedLayer( vl );
 
   96    cacheJoinLayer( mVectorJoins.last() );
 
 
  109    QMutexLocker locker( &mMutex );
 
  110    for ( 
int i = 0; i < mVectorJoins.size(); ++i )
 
  112      if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
 
  119        mVectorJoins.removeAt( i );
 
 
  142    if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->
fields().
count() )
 
  151    QVector<int> subsetIndices;
 
  159      if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
 
  160        cacheLayerAttrs.append( joinFieldIndex );
 
  169      QString key = attrs.at( joinFieldIndex ).toString();
 
  173        for ( 
int i = 0; i < subsetIndices.count(); ++i )
 
  174          subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
 
  180        for ( 
int i = 0; i < attrs.size(); i++ )
 
  182          if ( i == joinFieldIndex )
 
  185          QString joinInfoPrefix = joinInfo.
prefix();
 
  186          if ( joinInfoPrefix.isNull() ) 
 
  187            joinInfoPrefix = QString( 
"%1_" ).arg( cacheLayer->
name() );
 
  190          const QString joinFieldName = joinInfoPrefix + cacheLayer->
fields().
names().at( i );
 
  198          attributesCache.append( attrs.at( i ) );
 
  215  QVector<int> subsetIndices;
 
  216  for ( 
int i = 0; i < joinFieldsSubset.count(); ++i )
 
  218    QString joinedFieldName = joinFieldsSubset.at( i );
 
  219    int index = joinLayerFields.
lookupField( joinedFieldName );
 
  222      subsetIndices.append( index );
 
  226      QgsDebugError( 
"Join layer subset field not found: " + joinedFieldName );
 
  230  return subsetIndices;
 
 
  237  QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
 
  238  for ( 
int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
 
  247    QString joinFieldName = joinIt->joinFieldName();
 
  249    QSet<QString> subset;
 
  250    if ( joinIt->hasSubset() )
 
  253      subset = qgis::listToSet( subsetNames );
 
  256    if ( joinIt->prefix().isNull() )
 
  258      prefix = joinLayer->
name() + 
'_';
 
  262      prefix = joinIt->prefix();
 
  265    for ( 
int idx = 0; idx < joinFields.
count(); ++idx )
 
  268      if ( joinIt->hasSubset() && !subset.contains( joinFields.
at( idx ).
name() ) )
 
  273      if ( joinIt->hasSubset() || joinFields.
at( idx ).
name() != joinFieldName )
 
 
  285  QMutexLocker locker( &mMutex );
 
  286  QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
 
  287  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
 
  289    if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
 
  290      cacheJoinLayer( *joinIt );
 
 
  297  QDomElement vectorJoinsElem = document.createElement( QStringLiteral( 
"vectorjoins" ) );
 
  298  layer_node.appendChild( vectorJoinsElem );
 
  299  QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
 
  300  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
 
  305    QDomElement joinElem = document.createElement( QStringLiteral( 
"join" ) );
 
  307    joinElem.setAttribute( QStringLiteral( 
"targetFieldName" ), joinIt->targetFieldName() );
 
  309    joinElem.setAttribute( QStringLiteral( 
"joinLayerId" ), joinIt->joinLayerId() );
 
  310    joinElem.setAttribute( QStringLiteral( 
"joinFieldName" ), joinIt->joinFieldName() );
 
  312    joinElem.setAttribute( QStringLiteral( 
"memoryCache" ), joinIt->isUsingMemoryCache() );
 
  313    joinElem.setAttribute( QStringLiteral( 
"dynamicForm" ), joinIt->isDynamicFormEnabled() );
 
  314    joinElem.setAttribute( QStringLiteral( 
"editable" ), joinIt->isEditable() );
 
  315    joinElem.setAttribute( QStringLiteral( 
"upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
 
  316    joinElem.setAttribute( QStringLiteral( 
"cascadedDelete" ), joinIt->hasCascadedDelete() );
 
  318    if ( joinIt->hasSubset() )
 
  320      QDomElement subsetElem = document.createElement( QStringLiteral( 
"joinFieldsSubset" ) );
 
  323      const auto constSubsetNames = subsetNames;
 
  324      for ( 
const QString &fieldName : constSubsetNames )
 
  326        QDomElement fieldElem = document.createElement( QStringLiteral( 
"field" ) );
 
  327        fieldElem.setAttribute( QStringLiteral( 
"name" ), fieldName );
 
  328        subsetElem.appendChild( fieldElem );
 
  331      joinElem.appendChild( subsetElem );
 
  334    if ( !joinIt->prefix().isNull() )
 
  336      joinElem.setAttribute( QStringLiteral( 
"customPrefix" ), joinIt->prefix() );
 
  337      joinElem.setAttribute( QStringLiteral( 
"hasCustomPrefix" ), 1 );
 
  340    vectorJoinsElem.appendChild( joinElem );
 
 
  346  mVectorJoins.clear();
 
  347  QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( 
"vectorjoins" ) );
 
  348  if ( !vectorJoinsElem.isNull() )
 
  350    QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( 
"join" ) );
 
  351    for ( 
int i = 0; i < joinList.size(); ++i )
 
  353      QDomElement infoElem = joinList.at( i ).toElement();
 
  355      info.
setJoinFieldName( infoElem.attribute( QStringLiteral( 
"joinFieldName" ) ) );
 
  357      info.
setJoinLayerId( infoElem.attribute( QStringLiteral( 
"joinLayerId" ) ) );
 
  361      info.
setEditable( infoElem.attribute( QStringLiteral( 
"editable" ) ).toInt() );
 
  362      info.
setUpsertOnEdit( infoElem.attribute( QStringLiteral( 
"upsertOnEdit" ) ).toInt() );
 
  363      info.
setCascadedDelete( infoElem.attribute( QStringLiteral( 
"cascadedDelete" ) ).toInt() );
 
  365      QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( 
"joinFieldsSubset" ) );
 
  366      if ( !subsetElem.isNull() )
 
  368        QStringList *fieldNames = 
new QStringList;
 
  369        QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( 
"field" ) );
 
  370        fieldNames->reserve( fieldNodes.count() );
 
  371        for ( 
int i = 0; i < fieldNodes.count(); ++i )
 
  372          *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( 
"name" ) );
 
  376      if ( infoElem.attribute( QStringLiteral( 
"hasCustomPrefix" ) ).toInt() )
 
  377        info.
setPrefix( infoElem.attribute( QStringLiteral( 
"customPrefix" ) ) );
 
 
  388  bool resolved = 
false;
 
  389  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  391    if ( it->joinLayer() )
 
  394    if ( 
QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->
mapLayer( it->joinLayerId() ) ) )
 
  396      it->setJoinLayer( joinedLayer );
 
  397      connectJoinedLayer( joinedLayer );
 
 
  411  int joinIndex = mVectorJoins.indexOf( *info );
 
  412  if ( joinIndex == -1 )
 
  415  for ( 
int i = 0; i < fields.
count(); ++i )
 
 
  432  int sourceJoinIndex = originIndex / 1000;
 
  433  sourceFieldIndex = originIndex % 1000;
 
  435  if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
 
  438  return &( mVectorJoins[sourceJoinIndex] );
 
 
  443  QList<const QgsVectorLayerJoinInfo *> infos;
 
  445  const auto constMVectorJoins = mVectorJoins;
 
  448    if ( infos.contains( &info ) )
 
  451    if ( info.targetFieldName() == field.
name() )
 
  452      infos.append( &info );
 
 
  479  return joinedFeature;
 
 
  499  return targetedFeature;
 
 
  505  cloned->mVectorJoins = mVectorJoins;
 
 
  509void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
 
  514  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  515  Q_ASSERT( joinedLayer );
 
  518  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  520    if ( joinedLayer == it->joinLayer() )
 
  522      it->cachedAttributes.clear();
 
  523      cacheJoinLayer( *it );
 
  530void QgsVectorLayerJoinBuffer::joinedLayerModified()
 
  532  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  533  Q_ASSERT( joinedLayer );
 
  536  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
 
  538    if ( joinedLayer == it->joinLayer() )
 
  540      it->cacheDirty = 
true;
 
  545void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
 
  547  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
 
  548  Q_ASSERT( joinedLayer );
 
  553void QgsVectorLayerJoinBuffer::connectJoinedLayer( 
QgsVectorLayer *vl )
 
  571    if ( joinLayer && joinLayer->
isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
 
  575      for ( 
const QgsFeature &feature : std::as_const( features ) )
 
  577        const QgsFeature joinFeature = info.extractJoinedFeature( feature );
 
  581        const QVariant idFieldValue = feature.
attribute( info.targetFieldName() );
 
  594        if ( existingFeature.
isValid() )
 
  596          if ( info.hasSubset() )
 
  599            const auto constSubsetNames = subsetNames;
 
  600            for ( 
const QString &field : constSubsetNames )
 
  602              QVariant newValue = joinFeature.
attribute( field );
 
  610            for ( 
const auto &field : joinFields )
 
  612              QVariant newValue = joinFeature.
attribute( field.name() );
 
  613              int fieldIndex = joinLayer->
fields().
indexOf( field.name() );
 
  621          bool notNullFields = 
false;
 
  623          for ( 
const auto &field : joinFields )
 
  625            if ( field.name() == info.joinFieldName() )
 
  630              notNullFields = 
true;
 
  636            joinFeatures << joinFeature;
 
 
  679  for ( 
auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
 
  681    const int field = it.key();
 
  682    const QVariant newValue = it.value();
 
  685    if ( oldValues.contains( field ) )
 
  686      oldValue = oldValues[field];
 
 
  704  const auto constFids = fids;
 
  710      if ( info.isEditable() && info.hasCascadedDelete() )
 
  714          info.joinLayer()->deleteFeature( joinFeature.
id(), context );
 
 
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
 
@ Join
Field originates from a joined layer.
 
Allows managing the auxiliary storage for a vector layer.
 
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
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)
Fetch next feature and stores in f, returns true on success.
 
Wraps a request for features to a vector layer (or directly its vector data provider).
 
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
 
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
 
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
 
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
 
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...
 
Q_INVOKABLE 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.
 
Q_INVOKABLE 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, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
 
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
 
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
 
Qgis::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).
 
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
 
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
 
void layerModified()
Emitted when modifications has been done on layer.
 
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.
 
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
 
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 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.
 
const QgsVectorJoinList & vectorJoins() const
 
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.
 
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined 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.
 
QString prefix() const
Returns prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
 
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.
 
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.
 
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
 
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 dataset.
 
Q_INVOKABLE bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false, QgsVectorLayerToolsContext *context=nullptr)
Changes an attribute value for a feature (but does not immediately commit the changes).
 
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
 
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
 
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 updatedFields()
Emitted whenever the fields available from this layer have been changed.
 
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
 
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
 
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
 
#define QgsDebugError(str)
 
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
 
Context for cascade delete features.