QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsvectorlayerjoinbuffer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerjoinbuffer.cpp
3  ----------------------------
4  begin : Feb 09, 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 
20 #include "qgsfeatureiterator.h"
21 #include "qgslogger.h"
22 #include "qgsproject.h"
23 #include "qgsvectordataprovider.h"
24 #include "qgsauxiliarystorage.h"
25 
26 #include <QDomElement>
27 
29  : mLayer( layer )
30 {
31 }
32 
33 static QList<QgsVectorLayer *> _outEdges( QgsVectorLayer *vl )
34 {
35  QList<QgsVectorLayer *> lst;
36  const auto constVectorJoins = vl->vectorJoins();
37  for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
38  {
39  if ( QgsVectorLayer *joinVl = info.joinLayer() )
40  lst << joinVl;
41  }
42  return lst;
43 }
44 
45 static bool _hasCycleDFS( QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
46 {
47  if ( mark.value( n ) == 1 ) // temporary
48  return true;
49  if ( mark.value( n ) == 0 ) // not visited
50  {
51  mark[n] = 1; // temporary
52  const auto outEdges { _outEdges( n ) };
53  for ( QgsVectorLayer *m : outEdges )
54  {
55  if ( _hasCycleDFS( m, mark ) )
56  return true;
57  }
58  mark[n] = 2; // permanent
59  }
60  return false;
61 }
62 
63 
65 {
66  QMutexLocker locker( &mMutex );
67  mVectorJoins.push_back( joinInfo );
68 
69  // run depth-first search to detect cycles in the graph of joins between layers.
70  // any cycle would cause infinite recursion when updating fields
71  QHash<QgsVectorLayer *, int> markDFS;
72  if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
73  {
74  // we have to reject this one
75  mVectorJoins.pop_back();
76  return false;
77  }
78 
79  // Wait for notifications about changed fields in joined layer to propagate them.
80  // During project load the joined layers possibly do not exist yet so the connection will not be created,
81  // but then QgsProject makes sure to call createJoinCaches() which will do the connection.
82  // Unique connection makes sure we do not respond to one layer's update more times (in case of multiple join)
83  if ( QgsVectorLayer *vl = joinInfo.joinLayer() )
84  {
85  connectJoinedLayer( vl );
86  }
87 
88  locker.unlock();
89  mLayer->updateFields();
90  locker.relock();
91 
92  //cache joined layer to virtual memory if specified by user
93  if ( joinInfo.isUsingMemoryCache() )
94  {
95  cacheJoinLayer( mVectorJoins.last() );
96  }
97 
98  locker.unlock();
99 
100  return true;
101 }
102 
103 
104 bool QgsVectorLayerJoinBuffer::removeJoin( const QString &joinLayerId )
105 {
106  bool res = false;
107  {
108  QMutexLocker locker( &mMutex );
109  for ( int i = 0; i < mVectorJoins.size(); ++i )
110  {
111  if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
112  {
113  if ( QgsVectorLayer *vl = mVectorJoins.at( i ).joinLayer() )
114  {
115  disconnect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields );
116  }
117 
118  mVectorJoins.removeAt( i );
119  res = true;
120  }
121  }
122  }
123 
124  emit joinedFieldsChanged();
125  return res;
126 }
127 
128 void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo )
129 {
130  //memory cache not required or already done
131  if ( !joinInfo.isUsingMemoryCache() || !joinInfo.cacheDirty )
132  {
133  return;
134  }
135 
136  QgsVectorLayer *cacheLayer = joinInfo.joinLayer();
137  if ( cacheLayer )
138  {
139  int joinFieldIndex = cacheLayer->fields().indexFromName( joinInfo.joinFieldName() );
140 
141  if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->fields().count() )
142  return;
143 
144  joinInfo.cachedAttributes.clear();
145 
146  QgsFeatureRequest request;
148  // maybe user requested just a subset of layer's attributes
149  // so we do not have to cache everything
150  QVector<int> subsetIndices;
151  if ( joinInfo.hasSubset() )
152  {
153  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( joinInfo );
154  subsetIndices = joinSubsetIndices( cacheLayer, subsetNames );
155 
156  // we need just subset of attributes - but make sure to include join field name
157  QgsAttributeList cacheLayerAttrs = subsetIndices.toList();
158  if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
159  cacheLayerAttrs.append( joinFieldIndex );
160  request.setSubsetOfAttributes( cacheLayerAttrs );
161  }
162 
163  QgsFeatureIterator fit = cacheLayer->getFeatures( request );
164  QgsFeature f;
165  while ( fit.nextFeature( f ) )
166  {
167  QgsAttributes attrs = f.attributes();
168  QString key = attrs.at( joinFieldIndex ).toString();
169  if ( joinInfo.hasSubset() )
170  {
171  QgsAttributes subsetAttrs( subsetIndices.count() );
172  for ( int i = 0; i < subsetIndices.count(); ++i )
173  subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
174  joinInfo.cachedAttributes.insert( key, subsetAttrs );
175  }
176  else
177  {
178  QgsAttributes attributesCache;
179  for ( int i = 0; i < attrs.size(); i++ )
180  {
181  if ( i == joinFieldIndex )
182  continue;
183 
184  QString joinInfoPrefix = joinInfo.prefix();
185  if ( joinInfoPrefix.isNull() ) // Default prefix 'layerName_' used
186  joinInfoPrefix = QString( "%1_" ).arg( cacheLayer->name() );
187 
188  // Joined field name
189  const QString joinFieldName = joinInfoPrefix + cacheLayer->fields().names().at( i );
190 
191  // Check for name collisions
192  int fieldIndex = mLayer->fields().indexFromName( joinFieldName );
193  if ( fieldIndex >= 0
194  && mLayer->fields().fieldOrigin( fieldIndex ) != QgsFields::OriginJoin )
195  continue;
196 
197  attributesCache.append( attrs.at( i ) );
198  }
199  joinInfo.cachedAttributes.insert( key, attributesCache );
200  }
201  }
202  joinInfo.cacheDirty = false;
203  }
204 }
205 
206 
207 QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset )
208 {
209  return joinSubsetIndices( joinLayer->fields(), joinFieldsSubset );
210 }
211 
212 QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( const QgsFields &joinLayerFields, const QStringList &joinFieldsSubset )
213 {
214  QVector<int> subsetIndices;
215  for ( int i = 0; i < joinFieldsSubset.count(); ++i )
216  {
217  QString joinedFieldName = joinFieldsSubset.at( i );
218  int index = joinLayerFields.lookupField( joinedFieldName );
219  if ( index != -1 )
220  {
221  subsetIndices.append( index );
222  }
223  else
224  {
225  QgsDebugMsg( "Join layer subset field not found: " + joinedFieldName );
226  }
227  }
228 
229  return subsetIndices;
230 }
231 
233 {
234  QString prefix;
235 
236  QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
237  for ( int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
238  {
239  QgsVectorLayer *joinLayer = joinIt->joinLayer();
240  if ( !joinLayer )
241  {
242  continue;
243  }
244 
245  const QgsFields &joinFields = joinLayer->fields();
246  QString joinFieldName = joinIt->joinFieldName();
247 
248  QSet<QString> subset;
249  if ( joinIt->hasSubset() )
250  {
251  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
252  subset = qgis::listToSet( subsetNames );
253  }
254 
255  if ( joinIt->prefix().isNull() )
256  {
257  prefix = joinLayer->name() + '_';
258  }
259  else
260  {
261  prefix = joinIt->prefix();
262  }
263 
264  for ( int idx = 0; idx < joinFields.count(); ++idx )
265  {
266  // if using just a subset of fields, filter some of them out
267  if ( joinIt->hasSubset() && !subset.contains( joinFields.at( idx ).name() ) )
268  continue;
269 
270  //skip the join field to avoid double field names (fields often have the same name)
271  // when using subset of field, use all the selected fields
272  if ( joinIt->hasSubset() || joinFields.at( idx ).name() != joinFieldName )
273  {
274  QgsField f = joinFields.at( idx );
275  f.setName( prefix + f.name() );
276  fields.append( f, QgsFields::OriginJoin, idx + ( joinIdx * 1000 ) );
277  }
278  }
279  }
280 }
281 
283 {
284  QMutexLocker locker( &mMutex );
285  QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
286  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
287  {
288  if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
289  cacheJoinLayer( *joinIt );
290  }
291 }
292 
293 
294 void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &document ) const
295 {
296  QDomElement vectorJoinsElem = document.createElement( QStringLiteral( "vectorjoins" ) );
297  layer_node.appendChild( vectorJoinsElem );
298  QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
299  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
300  {
301  if ( isAuxiliaryJoin( *joinIt ) )
302  continue;
303 
304  QDomElement joinElem = document.createElement( QStringLiteral( "join" ) );
305 
306  joinElem.setAttribute( QStringLiteral( "targetFieldName" ), joinIt->targetFieldName() );
307 
308  joinElem.setAttribute( QStringLiteral( "joinLayerId" ), joinIt->joinLayerId() );
309  joinElem.setAttribute( QStringLiteral( "joinFieldName" ), joinIt->joinFieldName() );
310 
311  joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() );
312  joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() );
313  joinElem.setAttribute( QStringLiteral( "editable" ), joinIt->isEditable() );
314  joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
315  joinElem.setAttribute( QStringLiteral( "cascadedDelete" ), joinIt->hasCascadedDelete() );
316 
317  if ( joinIt->hasSubset() )
318  {
319  QDomElement subsetElem = document.createElement( QStringLiteral( "joinFieldsSubset" ) );
320  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
321 
322  const auto constSubsetNames = subsetNames;
323  for ( const QString &fieldName : constSubsetNames )
324  {
325  QDomElement fieldElem = document.createElement( QStringLiteral( "field" ) );
326  fieldElem.setAttribute( QStringLiteral( "name" ), fieldName );
327  subsetElem.appendChild( fieldElem );
328  }
329 
330  joinElem.appendChild( subsetElem );
331  }
332 
333  if ( !joinIt->prefix().isNull() )
334  {
335  joinElem.setAttribute( QStringLiteral( "customPrefix" ), joinIt->prefix() );
336  joinElem.setAttribute( QStringLiteral( "hasCustomPrefix" ), 1 );
337  }
338 
339  vectorJoinsElem.appendChild( joinElem );
340  }
341 }
342 
343 void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
344 {
345  mVectorJoins.clear();
346  QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( "vectorjoins" ) );
347  if ( !vectorJoinsElem.isNull() )
348  {
349  QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( "join" ) );
350  for ( int i = 0; i < joinList.size(); ++i )
351  {
352  QDomElement infoElem = joinList.at( i ).toElement();
354  info.setJoinFieldName( infoElem.attribute( QStringLiteral( "joinFieldName" ) ) );
355  // read layer ID - to turn it into layer object, caller will need to call resolveReferences() later
356  info.setJoinLayerId( infoElem.attribute( QStringLiteral( "joinLayerId" ) ) );
357  info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) );
358  info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() );
359  info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() );
360  info.setEditable( infoElem.attribute( QStringLiteral( "editable" ) ).toInt() );
361  info.setUpsertOnEdit( infoElem.attribute( QStringLiteral( "upsertOnEdit" ) ).toInt() );
362  info.setCascadedDelete( infoElem.attribute( QStringLiteral( "cascadedDelete" ) ).toInt() );
363 
364  QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) );
365  if ( !subsetElem.isNull() )
366  {
367  QStringList *fieldNames = new QStringList;
368  QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( "field" ) );
369  fieldNames->reserve( fieldNodes.count() );
370  for ( int i = 0; i < fieldNodes.count(); ++i )
371  *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( "name" ) );
372  info.setJoinFieldNamesSubset( fieldNames );
373  }
374 
375  if ( infoElem.attribute( QStringLiteral( "hasCustomPrefix" ) ).toInt() )
376  info.setPrefix( infoElem.attribute( QStringLiteral( "customPrefix" ) ) );
377  else
378  info.setPrefix( QString() );
379 
380  addJoin( info );
381  }
382  }
383 }
384 
386 {
387  bool resolved = false;
388  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
389  {
390  if ( it->joinLayer() )
391  continue; // already resolved
392 
393  if ( QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->mapLayer( it->joinLayerId() ) ) )
394  {
395  it->setJoinLayer( joinedLayer );
396  connectJoinedLayer( joinedLayer );
397  resolved = true;
398  }
399  }
400 
401  if ( resolved )
402  emit joinedFieldsChanged();
403 }
404 
406 {
407  if ( !info )
408  return -1;
409 
410  int joinIndex = mVectorJoins.indexOf( *info );
411  if ( joinIndex == -1 )
412  return -1;
413 
414  for ( int i = 0; i < fields.count(); ++i )
415  {
416  if ( fields.fieldOrigin( i ) != QgsFields::OriginJoin )
417  continue;
418 
419  if ( fields.fieldOriginIndex( i ) / 1000 == joinIndex )
420  return i;
421  }
422  return -1;
423 }
424 
425 const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, const QgsFields &fields, int &sourceFieldIndex ) const
426 {
427  if ( fields.fieldOrigin( index ) != QgsFields::OriginJoin )
428  return nullptr;
429 
430  int originIndex = fields.fieldOriginIndex( index );
431  int sourceJoinIndex = originIndex / 1000;
432  sourceFieldIndex = originIndex % 1000;
433 
434  if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
435  return nullptr;
436 
437  return &( mVectorJoins[sourceJoinIndex] );
438 }
439 
440 QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
441 {
442  QList<const QgsVectorLayerJoinInfo *> infos;
443 
444  const auto constMVectorJoins = mVectorJoins;
445  for ( const QgsVectorLayerJoinInfo &info : constMVectorJoins )
446  {
447  if ( infos.contains( &info ) )
448  continue;
449 
450  if ( info.targetFieldName() == field.name() )
451  infos.append( &info );
452  }
453 
454  return infos;
455 }
456 
458 {
459  QgsFeature joinedFeature;
460 
461  if ( info->joinLayer() )
462  {
463  joinedFeature.initAttributes( info->joinLayer()->fields().count() );
464  joinedFeature.setFields( info->joinLayer()->fields() );
465 
466  QString joinFieldName = info->joinFieldName();
467  const QVariant targetValue = feature.attribute( info->targetFieldName() );
468  QString filter = QgsExpression::createFieldEqualityExpression( joinFieldName, targetValue );
469 
470  QgsFeatureRequest request;
471  request.setFilterExpression( filter );
472  request.setLimit( 1 );
473 
474  QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
475  it.nextFeature( joinedFeature );
476  }
477 
478  return joinedFeature;
479 }
480 
482 {
483  QgsFeature targetedFeature;
484 
485  if ( info->joinLayer() )
486  {
487  const QVariant targetValue = feature.attribute( info->joinFieldName() );
488  const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue );
489 
490  QgsFeatureRequest request;
491  request.setFilterExpression( filter );
492  request.setLimit( 1 );
493 
494  QgsFeatureIterator it = mLayer->getFeatures( request );
495  it.nextFeature( targetedFeature );
496  }
497 
498  return targetedFeature;
499 }
500 
502 {
503  QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );
504  cloned->mVectorJoins = mVectorJoins;
505  return cloned;
506 }
507 
508 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
509 {
510  // TODO - check - this whole method is probably not needed anymore,
511  // since the cache handling is covered by joinedLayerModified()
512 
513  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
514  Q_ASSERT( joinedLayer );
515 
516  // recache the joined layer
517  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
518  {
519  if ( joinedLayer == it->joinLayer() )
520  {
521  it->cachedAttributes.clear();
522  cacheJoinLayer( *it );
523  }
524  }
525 
526  emit joinedFieldsChanged();
527 }
528 
529 void QgsVectorLayerJoinBuffer::joinedLayerModified()
530 {
531  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
532  Q_ASSERT( joinedLayer );
533 
534  // recache the joined layer
535  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
536  {
537  if ( joinedLayer == it->joinLayer() )
538  {
539  it->cacheDirty = true;
540  }
541  }
542 }
543 
544 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
545 {
546  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
547  Q_ASSERT( joinedLayer );
548 
549  removeJoin( joinedLayer->id() );
550 }
551 
552 void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
553 {
554  connect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields, Qt::UniqueConnection );
555  connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
556  connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
557 }
558 
559 bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags )
560 {
561  if ( !containsJoins() )
562  return false;
563 
564  // try to add/update a feature in each joined layer
565  const QgsVectorJoinList joins = vectorJoins();
566  for ( const QgsVectorLayerJoinInfo &info : joins )
567  {
568  QgsVectorLayer *joinLayer = info.joinLayer();
569 
570  if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
571  {
572  QgsFeatureList joinFeatures;
573 
574  for ( const QgsFeature &feature : std::as_const( features ) )
575  {
576  const QgsFeature joinFeature = info.extractJoinedFeature( feature );
577 
578  // we don't want to add a new feature in joined layer when the id
579  // column value yet exist, we just want to update the existing one
580  const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
581  const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );
582 
583  QgsFeatureRequest request;
585  request.setNoAttributes();
586  request.setFilterExpression( filter );
587  request.setLimit( 1 );
588 
589  QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
590  QgsFeature existingFeature;
591  it.nextFeature( existingFeature );
592 
593  if ( existingFeature.isValid() )
594  {
595  if ( info.hasSubset() )
596  {
597  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( info );
598  const auto constSubsetNames = subsetNames;
599  for ( const QString &field : constSubsetNames )
600  {
601  QVariant newValue = joinFeature.attribute( field );
602  int fieldIndex = joinLayer->fields().indexOf( field );
603  joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
604  }
605  }
606  else
607  {
608  const QgsFields joinFields = joinFeature.fields();
609  for ( const auto &field : joinFields )
610  {
611  QVariant newValue = joinFeature.attribute( field.name() );
612  int fieldIndex = joinLayer->fields().indexOf( field.name() );
613  joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
614  }
615  }
616  }
617  else
618  {
619  // joined feature is added only if one of its field is not null
620  bool notNullFields = false;
621  const QgsFields joinFields = joinFeature.fields();
622  for ( const auto &field : joinFields )
623  {
624  if ( field.name() == info.joinFieldName() )
625  continue;
626 
627  if ( !joinFeature.attribute( field.name() ).isNull() )
628  {
629  notNullFields = true;
630  break;
631  }
632  }
633 
634  if ( notNullFields )
635  joinFeatures << joinFeature;
636  }
637  }
638 
639  joinLayer->addFeatures( joinFeatures );
640  }
641  }
642 
643  return true;
644 }
645 
646 bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
647 {
648  if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin )
649  return false;
650 
651  int srcFieldIndex;
652  const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex );
653  if ( info && info->joinLayer() && info->isEditable() )
654  {
655  QgsFeature feature = mLayer->getFeature( fid );
656 
657  if ( !feature.isValid() )
658  return false;
659 
660  const QgsFeature joinFeature = joinedFeatureOf( info, feature );
661 
662  if ( joinFeature.isValid() )
663  return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
664  else
665  {
666  feature.setAttribute( field, newValue );
667  return addFeatures( QgsFeatureList() << feature );
668  }
669  }
670  else
671  return false;
672 }
673 
675 {
676  bool success = true;
677 
678  for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
679  {
680  const int field = it.key();
681  const QVariant newValue = it.value();
682  QVariant oldValue;
683 
684  if ( oldValues.contains( field ) )
685  oldValue = oldValues[field];
686 
687  success &= changeAttributeValue( fid, field, newValue, oldValue );
688  }
689 
690  return success;
691 }
692 
694 {
695  return deleteFeatures( QgsFeatureIds() << fid, context );
696 }
697 
699 {
700  if ( !containsJoins() )
701  return false;
702 
703  const auto constFids = fids;
704  for ( const QgsFeatureId &fid : constFids )
705  {
706  const auto constVectorJoins = vectorJoins();
707  for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
708  {
709  if ( info.isEditable() && info.hasCascadedDelete() )
710  {
711  const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) );
712  if ( joinFeature.isValid() )
713  info.joinLayer()->deleteFeature( joinFeature.id(), context );
714  }
715  }
716  }
717 
718  return true;
719 }
720 
722 {
723  const QgsAuxiliaryLayer *al = mLayer->auxiliaryLayer();
724 
725  return al && al->id() == info.joinLayerId();
726 }
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:115
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsMapLayer::willBeDeleted
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
QgsVectorLayer::addFeatures
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a list of features to the sink.
Definition: qgsvectorlayer.cpp:3658
QgsVectorLayerJoinBuffer::joinSubsetIndices
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.
Definition: qgsvectorlayerjoinbuffer.cpp:207
QgsVectorLayer::updateFields
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
Definition: qgsvectorlayer.cpp:3963
QgsAuxiliaryLayer
Class allowing to manage the auxiliary storage for a vector layer.
Definition: qgsauxiliarystorage.h:61
QgsFeature::initAttributes
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:235
QgsVectorLayerJoinBuffer::writeXml
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
Definition: qgsvectorlayerjoinbuffer.cpp:294
QgsVectorLayerJoinBuffer::isAuxiliaryJoin
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.
Definition: qgsvectorlayerjoinbuffer.cpp:721
QgsVectorLayer::DeleteContext
Context for cascade delete features.
Definition: qgsvectorlayer.h:495
QgsVectorLayerJoinInfo::setEditable
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields.
Definition: qgsvectorlayerjoininfo.cpp:51
qgsfeatureiterator.h
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsVectorLayerJoinBuffer::deleteFeatures
bool deleteFeatures(const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a list of features from joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:698
QgsVectorLayerJoinBuffer::joinForFieldIndex
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Definition: qgsvectorlayerjoinbuffer.cpp:425
QgsVectorLayerJoinBuffer::readXml
void readXml(const QDomNode &layer_node)
Reads joins from project file.
Definition: qgsvectorlayerjoinbuffer.cpp:343
QgsVectorLayerJoinBuffer::targetedFeatureOf
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
Definition: qgsvectorlayerjoinbuffer.cpp:481
QgsVectorLayerJoinInfo::joinFieldNamesSubset
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
Definition: qgsvectorlayerjoininfo.h:236
QgsVectorLayer::auxiliaryLayer
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
Definition: qgsvectorlayer.cpp:5520
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:228
QgsVectorLayerJoinInfo::setUpsertOnEdit
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
Definition: qgsvectorlayerjoininfo.h:128
field
const QgsField & field
Definition: qgsfield.h:463
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsFields::append
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)
Definition: qgsfields.cpp:59
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3680
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsVectorLayerJoinBuffer::updateFields
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
Definition: qgsvectorlayerjoinbuffer.cpp:232
QgsVectorLayerJoinInfo::joinLayerId
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
Definition: qgsvectorlayerjoininfo.h:63
QgsField::name
QString name
Definition: qgsfield.h:60
QgsVectorLayer::changeAttributeValue
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).
Definition: qgsvectorlayer.cpp:3072
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3728
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsVectorLayerJoinBuffer::joinedFieldsOffset
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
Definition: qgsvectorlayerjoinbuffer.cpp:405
QgsVectorJoinList
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Definition: qgsvectorlayerjoinbuffer.h:30
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:167
qgsauxiliarystorage.h
QgsVectorLayerJoinInfo::targetFieldName
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:68
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
QgsVectorLayerJoinInfo::prefix
QString prefix() const
Returns prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
Definition: qgsvectorlayerjoininfo.h:78
QgsVectorLayerJoinBuffer::changeAttributeValues
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:674
qgsvectorlayerjoinbuffer.h
QgsFeature::isValid
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QgsVectorLayerJoinInfo::cachedAttributes
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)
Definition: qgsvectorlayerjoininfo.h:277
QgsFeature::setFields
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:195
QgsVectorLayer::vectorJoins
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
Definition: qgsvectorlayer.cpp:3921
QgsVectorLayerJoinInfo::setDynamicFormEnabled
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
Definition: qgsvectorlayerjoininfo.h:101
QgsVectorLayerJoinBuffer
Manages joined fields for a vector layer.
Definition: qgsvectorlayerjoinbuffer.h:37
QgsVectorLayerJoinBuffer::clone
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
Definition: qgsvectorlayerjoinbuffer.cpp:501
QgsFields::fieldOrigin
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
qgsvectordataprovider.h
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
QgsAttributeMap
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QgsVectorLayerJoinInfo::isEditable
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
Definition: qgsvectorlayerjoininfo.h:108
QgsVectorLayerJoinBuffer::addFeatures
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:559
QgsVectorLayerJoinBuffer::vectorJoins
const QgsVectorJoinList & vectorJoins() const
Definition: qgsvectorlayerjoinbuffer.h:83
QgsVectorLayerJoinInfo::joinFieldName
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:73
QgsVectorLayerJoinBuffer::joinsWhereFieldIsId
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
Definition: qgsvectorlayerjoinbuffer.cpp:440
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:327
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:169
QgsVectorLayerJoinBuffer::createJoinCaches
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
Definition: qgsvectorlayerjoinbuffer.cpp:282
QgsVectorLayerJoinInfo
Defines left outer join from our vector layer to some other vector layer. The join is done based on [...
Definition: qgsvectorlayerjoininfo.h:33
QgsMapLayer::layerModified
void layerModified()
Emitted when modifications has been done on layer.
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1126
QgsFeatureRequest::setNoAttributes
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
Definition: qgsfeaturerequest.cpp:235
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:69
QgsVectorLayerJoinInfo::setUsingMemoryCache
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
Definition: qgsvectorlayerjoininfo.cpp:38
QgsVectorLayerJoinInfo::cacheDirty
bool cacheDirty
True if the cached join attributes need to be updated.
Definition: qgsvectorlayerjoininfo.h:264
QgsVectorLayerJoinInfo::setTargetFieldName
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:66
QgsVectorLayerJoinInfo::setJoinFieldName
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:71
QgsFeature::setAttribute
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:262
QgsVectorLayerJoinInfo::setJoinFieldNamesSubset
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Sets the subset of fields to be used from joined layer.
Definition: qgsvectorlayerjoininfo.h:225
QgsVectorLayerJoinInfo::setPrefix
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
Definition: qgsvectorlayerjoininfo.h:76
QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
Definition: qgsvectorlayerjoinbuffer.cpp:28
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
QgsVectorLayerJoinBuffer::addJoin
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
Definition: qgsvectorlayerjoinbuffer.cpp:64
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsFeatureRequest::setLimit
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
Definition: qgsfeaturerequest.cpp:216
QgsVectorLayerJoinBuffer::joinedFeatureOf
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
Definition: qgsvectorlayerjoinbuffer.cpp:457
QgsVectorLayerJoinInfo::joinLayer
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
Definition: qgsvectorlayerjoininfo.h:58
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:76
QgsFields::fieldOriginIndex
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
Definition: qgsfields.cpp:197
QgsVectorLayerJoinBuffer::containsJoins
bool containsJoins() const
Quick way to test if there is any join at all.
Definition: qgsvectorlayerjoinbuffer.h:81
QgsVectorLayerJoinBuffer::deleteFeature
bool deleteFeature(QgsFeatureId fid, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a feature from joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:693
QgsAttributes
A vector of attributes. Mostly equal to QVector<QVariant>.
Definition: qgsattributes.h:57
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:70
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsVectorLayerJoinBuffer::changeAttributeValue
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:646
QgsVectorLayer::updatedFields
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsVectorLayer::getFeature
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
Definition: qgsvectorlayer.h:1187
QgsVectorLayerJoinInfo::isUsingMemoryCache
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups.
Definition: qgsvectorlayerjoininfo.cpp:43
qgslogger.h
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
QgsVectorLayerJoinBuffer::joinedFieldsChanged
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g.
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:222
QgsVectorLayerJoinBuffer::removeJoin
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
Definition: qgsvectorlayerjoinbuffer.cpp:104
QgsVectorLayerJoinInfo::hasSubset
bool hasSubset(bool blocklisted=true) const
Returns true if blocklisted fields is not empty or if a subset of names has been set.
Definition: qgsvectorlayerjoininfo.cpp:127
qgsproject.h
QgsFields::indexFromName
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsField::setName
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:175
QgsVectorLayerJoinInfo::setCascadedDelete
void setCascadedDelete(bool enabled)
Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the cor...
Definition: qgsvectorlayerjoininfo.h:142
QgsFields::OriginJoin
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
QgsVectorLayerJoinBuffer::resolveReferences
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project's available layers.
Definition: qgsvectorlayerjoinbuffer.cpp:385
QgsVectorLayerJoinInfo::setJoinLayerId
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
Definition: qgsvectorlayerjoininfo.h:61
QgsFields::names
QStringList names() const
Returns a list with field names.
Definition: qgsfields.cpp:143
QgsFields::indexOf
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50