QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  //cache joined layer to virtual memory if specified by user
80  if ( joinInfo.isUsingMemoryCache() )
81  {
82  cacheJoinLayer( mVectorJoins.last() );
83  }
84 
85  // Wait for notifications about changed fields in joined layer to propagate them.
86  // During project load the joined layers possibly do not exist yet so the connection will not be created,
87  // but then QgsProject makes sure to call createJoinCaches() which will do the connection.
88  // Unique connection makes sure we do not respond to one layer's update more times (in case of multiple join)
89  if ( QgsVectorLayer *vl = joinInfo.joinLayer() )
90  {
91  connectJoinedLayer( vl );
92  }
93 
94  locker.unlock();
95 
96  emit joinedFieldsChanged();
97  return true;
98 }
99 
100 
101 bool QgsVectorLayerJoinBuffer::removeJoin( const QString &joinLayerId )
102 {
103  QMutexLocker locker( &mMutex );
104  bool res = false;
105  for ( int i = 0; i < mVectorJoins.size(); ++i )
106  {
107  if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
108  {
109  if ( QgsVectorLayer *vl = mVectorJoins.at( i ).joinLayer() )
110  {
111  disconnect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields );
112  }
113 
114  mVectorJoins.removeAt( i );
115  res = true;
116  }
117  }
118 
119  emit joinedFieldsChanged();
120  return res;
121 }
122 
123 void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo )
124 {
125  //memory cache not required or already done
126  if ( !joinInfo.isUsingMemoryCache() || !joinInfo.cacheDirty )
127  {
128  return;
129  }
130 
131  QgsVectorLayer *cacheLayer = joinInfo.joinLayer();
132  if ( cacheLayer )
133  {
134  int joinFieldIndex = cacheLayer->fields().indexFromName( joinInfo.joinFieldName() );
135 
136  if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->fields().count() )
137  return;
138 
139  joinInfo.cachedAttributes.clear();
140 
141  QgsFeatureRequest request;
143  // maybe user requested just a subset of layer's attributes
144  // so we do not have to cache everything
145  QVector<int> subsetIndices;
146  if ( joinInfo.hasSubset() )
147  {
148  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( joinInfo );
149  subsetIndices = joinSubsetIndices( cacheLayer, subsetNames );
150 
151  // we need just subset of attributes - but make sure to include join field name
152  QgsAttributeList cacheLayerAttrs = subsetIndices.toList();
153  if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
154  cacheLayerAttrs.append( joinFieldIndex );
155  request.setSubsetOfAttributes( cacheLayerAttrs );
156  }
157 
158  QgsFeatureIterator fit = cacheLayer->getFeatures( request );
159  QgsFeature f;
160  while ( fit.nextFeature( f ) )
161  {
162  QgsAttributes attrs = f.attributes();
163  QString key = attrs.at( joinFieldIndex ).toString();
164  if ( joinInfo.hasSubset() )
165  {
166  QgsAttributes subsetAttrs( subsetIndices.count() );
167  for ( int i = 0; i < subsetIndices.count(); ++i )
168  subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
169  joinInfo.cachedAttributes.insert( key, subsetAttrs );
170  }
171  else
172  {
173  QgsAttributes attrs2 = attrs;
174  attrs2.remove( joinFieldIndex ); // skip the join field to avoid double field names (fields often have the same name)
175  joinInfo.cachedAttributes.insert( key, attrs2 );
176  }
177  }
178  joinInfo.cacheDirty = false;
179  }
180 }
181 
182 
183 QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset )
184 {
185  QVector<int> subsetIndices;
186  const QgsFields &fields = joinLayer->fields();
187  for ( int i = 0; i < joinFieldsSubset.count(); ++i )
188  {
189  QString joinedFieldName = joinFieldsSubset.at( i );
190  int index = fields.lookupField( joinedFieldName );
191  if ( index != -1 )
192  {
193  subsetIndices.append( index );
194  }
195  else
196  {
197  QgsDebugMsg( "Join layer subset field not found: " + joinedFieldName );
198  }
199  }
200 
201  return subsetIndices;
202 }
203 
205 {
206  QString prefix;
207 
208  QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
209  for ( int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
210  {
211  QgsVectorLayer *joinLayer = joinIt->joinLayer();
212  if ( !joinLayer )
213  {
214  continue;
215  }
216 
217  const QgsFields &joinFields = joinLayer->fields();
218  QString joinFieldName = joinIt->joinFieldName();
219 
220  QSet<QString> subset;
221  if ( joinIt->hasSubset() )
222  {
223  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
224  subset = qgis::listToSet( subsetNames );
225  }
226 
227  if ( joinIt->prefix().isNull() )
228  {
229  prefix = joinLayer->name() + '_';
230  }
231  else
232  {
233  prefix = joinIt->prefix();
234  }
235 
236  for ( int idx = 0; idx < joinFields.count(); ++idx )
237  {
238  // if using just a subset of fields, filter some of them out
239  if ( joinIt->hasSubset() && !subset.contains( joinFields.at( idx ).name() ) )
240  continue;
241 
242  //skip the join field to avoid double field names (fields often have the same name)
243  // when using subset of field, use all the selected fields
244  if ( joinIt->hasSubset() || joinFields.at( idx ).name() != joinFieldName )
245  {
246  QgsField f = joinFields.at( idx );
247  f.setName( prefix + f.name() );
248  fields.append( f, QgsFields::OriginJoin, idx + ( joinIdx * 1000 ) );
249  }
250  }
251  }
252 }
253 
255 {
256  QMutexLocker locker( &mMutex );
257  QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
258  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
259  {
260  if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
261  cacheJoinLayer( *joinIt );
262  }
263 }
264 
265 
266 void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &document ) const
267 {
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 )
272  {
273  if ( isAuxiliaryJoin( *joinIt ) )
274  continue;
275 
276  QDomElement joinElem = document.createElement( QStringLiteral( "join" ) );
277 
278  joinElem.setAttribute( QStringLiteral( "targetFieldName" ), joinIt->targetFieldName() );
279 
280  joinElem.setAttribute( QStringLiteral( "joinLayerId" ), joinIt->joinLayerId() );
281  joinElem.setAttribute( QStringLiteral( "joinFieldName" ), joinIt->joinFieldName() );
282 
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() );
288 
289  if ( joinIt->hasSubset() )
290  {
291  QDomElement subsetElem = document.createElement( QStringLiteral( "joinFieldsSubset" ) );
292  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
293 
294  const auto constSubsetNames = subsetNames;
295  for ( const QString &fieldName : constSubsetNames )
296  {
297  QDomElement fieldElem = document.createElement( QStringLiteral( "field" ) );
298  fieldElem.setAttribute( QStringLiteral( "name" ), fieldName );
299  subsetElem.appendChild( fieldElem );
300  }
301 
302  joinElem.appendChild( subsetElem );
303  }
304 
305  if ( !joinIt->prefix().isNull() )
306  {
307  joinElem.setAttribute( QStringLiteral( "customPrefix" ), joinIt->prefix() );
308  joinElem.setAttribute( QStringLiteral( "hasCustomPrefix" ), 1 );
309  }
310 
311  vectorJoinsElem.appendChild( joinElem );
312  }
313 }
314 
315 void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
316 {
317  mVectorJoins.clear();
318  QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( "vectorjoins" ) );
319  if ( !vectorJoinsElem.isNull() )
320  {
321  QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( "join" ) );
322  for ( int i = 0; i < joinList.size(); ++i )
323  {
324  QDomElement infoElem = joinList.at( i ).toElement();
326  info.setJoinFieldName( infoElem.attribute( QStringLiteral( "joinFieldName" ) ) );
327  // read layer ID - to turn it into layer object, caller will need to call resolveReferences() later
328  info.setJoinLayerId( infoElem.attribute( QStringLiteral( "joinLayerId" ) ) );
329  info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) );
330  info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() );
331  info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() );
332  info.setEditable( infoElem.attribute( QStringLiteral( "editable" ) ).toInt() );
333  info.setUpsertOnEdit( infoElem.attribute( QStringLiteral( "upsertOnEdit" ) ).toInt() );
334  info.setCascadedDelete( infoElem.attribute( QStringLiteral( "cascadedDelete" ) ).toInt() );
335 
336  QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) );
337  if ( !subsetElem.isNull() )
338  {
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" ) );
344  info.setJoinFieldNamesSubset( fieldNames );
345  }
346 
347  if ( infoElem.attribute( QStringLiteral( "hasCustomPrefix" ) ).toInt() )
348  info.setPrefix( infoElem.attribute( QStringLiteral( "customPrefix" ) ) );
349  else
350  info.setPrefix( QString() );
351 
352  addJoin( info );
353  }
354  }
355 }
356 
358 {
359  bool resolved = false;
360  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
361  {
362  if ( it->joinLayer() )
363  continue; // already resolved
364 
365  if ( QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->mapLayer( it->joinLayerId() ) ) )
366  {
367  it->setJoinLayer( joinedLayer );
368  connectJoinedLayer( joinedLayer );
369  resolved = true;
370  }
371  }
372 
373  if ( resolved )
374  emit joinedFieldsChanged();
375 }
376 
378 {
379  if ( !info )
380  return -1;
381 
382  int joinIndex = mVectorJoins.indexOf( *info );
383  if ( joinIndex == -1 )
384  return -1;
385 
386  for ( int i = 0; i < fields.count(); ++i )
387  {
388  if ( fields.fieldOrigin( i ) != QgsFields::OriginJoin )
389  continue;
390 
391  if ( fields.fieldOriginIndex( i ) / 1000 == joinIndex )
392  return i;
393  }
394  return -1;
395 }
396 
397 const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, const QgsFields &fields, int &sourceFieldIndex ) const
398 {
399  if ( fields.fieldOrigin( index ) != QgsFields::OriginJoin )
400  return nullptr;
401 
402  int originIndex = fields.fieldOriginIndex( index );
403  int sourceJoinIndex = originIndex / 1000;
404  sourceFieldIndex = originIndex % 1000;
405 
406  if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
407  return nullptr;
408 
409  return &( mVectorJoins[sourceJoinIndex] );
410 }
411 
412 QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
413 {
414  QList<const QgsVectorLayerJoinInfo *> infos;
415 
416  const auto constMVectorJoins = mVectorJoins;
417  for ( const QgsVectorLayerJoinInfo &info : constMVectorJoins )
418  {
419  if ( infos.contains( &info ) )
420  continue;
421 
422  if ( info.targetFieldName() == field.name() )
423  infos.append( &info );
424  }
425 
426  return infos;
427 }
428 
430 {
431  QgsFeature joinedFeature;
432 
433  if ( info->joinLayer() )
434  {
435  joinedFeature.initAttributes( info->joinLayer()->fields().count() );
436  joinedFeature.setFields( info->joinLayer()->fields() );
437 
438  QString joinFieldName = info->joinFieldName();
439  const QVariant targetValue = feature.attribute( info->targetFieldName() );
440  QString filter = QgsExpression::createFieldEqualityExpression( joinFieldName, targetValue );
441 
442  QgsFeatureRequest request;
443  request.setFilterExpression( filter );
444  request.setLimit( 1 );
445 
446  QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
447  it.nextFeature( joinedFeature );
448  }
449 
450  return joinedFeature;
451 }
452 
454 {
455  QgsFeature targetedFeature;
456 
457  if ( info->joinLayer() )
458  {
459  const QVariant targetValue = feature.attribute( info->joinFieldName() );
460  const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue );
461 
462  QgsFeatureRequest request;
463  request.setFilterExpression( filter );
464  request.setLimit( 1 );
465 
466  QgsFeatureIterator it = mLayer->getFeatures( request );
467  it.nextFeature( targetedFeature );
468  }
469 
470  return targetedFeature;
471 }
472 
474 {
475  QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );
476  cloned->mVectorJoins = mVectorJoins;
477  return cloned;
478 }
479 
480 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
481 {
482  // TODO - check - this whole method is probably not needed anymore,
483  // since the cache handling is covered by joinedLayerModified()
484 
485  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
486  Q_ASSERT( joinedLayer );
487 
488  // recache the joined layer
489  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
490  {
491  if ( joinedLayer == it->joinLayer() )
492  {
493  it->cachedAttributes.clear();
494  cacheJoinLayer( *it );
495  }
496  }
497 
498  emit joinedFieldsChanged();
499 }
500 
501 void QgsVectorLayerJoinBuffer::joinedLayerModified()
502 {
503  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
504  Q_ASSERT( joinedLayer );
505 
506  // recache the joined layer
507  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
508  {
509  if ( joinedLayer == it->joinLayer() )
510  {
511  it->cacheDirty = true;
512  }
513  }
514 }
515 
516 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
517 {
518  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
519  Q_ASSERT( joinedLayer );
520 
521  removeJoin( joinedLayer->id() );
522 }
523 
524 void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
525 {
526  connect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields, Qt::UniqueConnection );
527  connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
528  connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
529 }
530 
531 bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags )
532 {
533  if ( !containsJoins() )
534  return false;
535 
536  // try to add/update a feature in each joined layer
537  const QgsVectorJoinList joins = vectorJoins();
538  for ( const QgsVectorLayerJoinInfo &info : joins )
539  {
540  QgsVectorLayer *joinLayer = info.joinLayer();
541 
542  if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
543  {
544  QgsFeatureList joinFeatures;
545 
546  for ( const QgsFeature &feature : qgis::as_const( features ) )
547  {
548  const QgsFeature joinFeature = info.extractJoinedFeature( feature );
549 
550  // we don't want to add a new feature in joined layer when the id
551  // column value yet exist, we just want to update the existing one
552  const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
553  const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );
554 
555  QgsFeatureRequest request;
557  request.setNoAttributes();
558  request.setFilterExpression( filter );
559  request.setLimit( 1 );
560 
561  QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
562  QgsFeature existingFeature;
563  it.nextFeature( existingFeature );
564 
565  if ( existingFeature.isValid() )
566  {
567  if ( info.hasSubset() )
568  {
569  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( info );
570  const auto constSubsetNames = subsetNames;
571  for ( const QString &field : constSubsetNames )
572  {
573  QVariant newValue = joinFeature.attribute( field );
574  int fieldIndex = joinLayer->fields().indexOf( field );
575  joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
576  }
577  }
578  else
579  {
580  const QgsFields joinFields = joinFeature.fields();
581  for ( const auto &field : joinFields )
582  {
583  QVariant newValue = joinFeature.attribute( field.name() );
584  int fieldIndex = joinLayer->fields().indexOf( field.name() );
585  joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
586  }
587  }
588  }
589  else
590  {
591  // joined feature is added only if one of its field is not null
592  bool notNullFields = false;
593  const QgsFields joinFields = joinFeature.fields();
594  for ( const auto &field : joinFields )
595  {
596  if ( field.name() == info.joinFieldName() )
597  continue;
598 
599  if ( !joinFeature.attribute( field.name() ).isNull() )
600  {
601  notNullFields = true;
602  break;
603  }
604  }
605 
606  if ( notNullFields )
607  joinFeatures << joinFeature;
608  }
609  }
610 
611  joinLayer->addFeatures( joinFeatures );
612  }
613  }
614 
615  return true;
616 }
617 
618 bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
619 {
620  if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin )
621  return false;
622 
623  int srcFieldIndex;
624  const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex );
625  if ( info && info->joinLayer() && info->isEditable() )
626  {
627  QgsFeature feature = mLayer->getFeature( fid );
628 
629  if ( !feature.isValid() )
630  return false;
631 
632  const QgsFeature joinFeature = joinedFeatureOf( info, feature );
633 
634  if ( joinFeature.isValid() )
635  return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
636  else
637  {
638  feature.setAttribute( field, newValue );
639  return addFeatures( QgsFeatureList() << feature );
640  }
641  }
642  else
643  return false;
644 }
645 
647 {
648  bool success = true;
649 
650  for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
651  {
652  const int field = it.key();
653  const QVariant newValue = it.value();
654  QVariant oldValue;
655 
656  if ( oldValues.contains( field ) )
657  oldValue = oldValues[field];
658 
659  success &= changeAttributeValue( fid, field, newValue, oldValue );
660  }
661 
662  return success;
663 }
664 
666 {
667  return deleteFeatures( QgsFeatureIds() << fid, context );
668 }
669 
671 {
672  if ( !containsJoins() )
673  return false;
674 
675  const auto constFids = fids;
676  for ( const QgsFeatureId &fid : constFids )
677  {
678  const auto constVectorJoins = vectorJoins();
679  for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
680  {
681  if ( info.isEditable() && info.hasCascadedDelete() )
682  {
683  const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) );
684  if ( joinFeature.isValid() )
685  info.joinLayer()->deleteFeature( joinFeature.id(), context );
686  }
687  }
688  }
689 
690  return true;
691 }
692 
694 {
695  const QgsAuxiliaryLayer *al = mLayer->auxiliaryLayer();
696 
697  return al && al->id() == info.joinLayerId();
698 }
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:81
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
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:3492
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:183
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsAuxiliaryLayer
Class allowing to manage the auxiliary storage for a vector layer.
Definition: qgsauxiliarystorage.h:61
QgsVectorLayer::layerModified
void layerModified()
Emitted when modifications has been done on layer.
QgsFeature::initAttributes
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:204
QgsVectorLayerJoinBuffer::writeXml
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
Definition: qgsvectorlayerjoinbuffer.cpp:266
QgsVectorLayerJoinBuffer::isAuxiliaryJoin
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.
Definition: qgsvectorlayerjoinbuffer.cpp:693
QgsVectorLayer::DeleteContext
Context for cascade delete features.
Definition: qgsvectorlayer.h:512
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:45
QgsVectorLayerJoinBuffer::deleteFeatures
bool deleteFeatures(const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a list of features from joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:670
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:397
QgsVectorLayerJoinBuffer::readXml
void readXml(const QDomNode &layer_node)
Reads joins from project file.
Definition: qgsvectorlayerjoinbuffer.cpp:315
QgsVectorLayerJoinBuffer::targetedFeatureOf
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
Definition: qgsvectorlayerjoinbuffer.cpp:453
QgsVectorLayerJoinInfo::joinFieldNamesSubset
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
Definition: qgsvectorlayerjoininfo.h:223
QgsVectorLayer::auxiliaryLayer
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
Definition: qgsvectorlayer.cpp:5293
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:185
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:115
field
const QgsField & field
Definition: qgsfield.h:456
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:3208
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsVectorLayerJoinBuffer::updateFields
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
Definition: qgsvectorlayerjoinbuffer.cpp:204
QgsVectorLayerJoinInfo::joinLayerId
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
Definition: qgsvectorlayerjoininfo.h:50
QgsField::name
QString name
Definition: qgsfield.h:59
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:2966
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3594
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:95
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:377
QgsVectorJoinList
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Definition: qgsvectorlayerjoinbuffer.h:30
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:124
qgsauxiliarystorage.h
QgsVectorLayerJoinInfo::targetFieldName
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:55
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsVectorLayerJoinBuffer::changeAttributeValues
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:646
qgsvectorlayerjoinbuffer.h
QgsFeature::isValid
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:185
QgsVectorLayerJoinInfo::cachedAttributes
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)
Definition: qgsvectorlayerjoininfo.h:264
QgsFeature::setFields
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:164
QgsVectorLayer::vectorJoins
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
Definition: qgsvectorlayer.cpp:3732
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:88
QgsVectorLayerJoinBuffer
Manages joined fields for a vector layer.
Definition: qgsvectorlayerjoinbuffer.h:38
QgsVectorLayerJoinBuffer::clone
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
Definition: qgsvectorlayerjoinbuffer.cpp:473
QgsFields::fieldOrigin
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
Definition: qgsfields.cpp:189
qgsvectordataprovider.h
QgsFeatureList
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:583
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:95
QgsVectorLayerJoinBuffer::addFeatures
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:531
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:60
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:412
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsVectorLayerJoinBuffer::createJoinCaches
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
Definition: qgsvectorlayerjoinbuffer.cpp:254
QgsVectorLayerJoinInfo
Defines left outer join from our vector layer to some other vector layer.
Definition: qgsvectorlayerjoininfo.h:34
QgsFeatureRequest::setNoAttributes
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
Definition: qgsfeaturerequest.cpp:192
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:65
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:251
QgsVectorLayerJoinInfo::setTargetFieldName
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:53
QgsVectorLayerJoinInfo::setJoinFieldName
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
Definition: qgsvectorlayerjoininfo.h:58
QgsFeature::setAttribute
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
Definition: qgsfeature.cpp:213
QgsVectorLayerJoinInfo::setJoinFieldNamesSubset
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Sets the subset of fields to be used from joined layer.
Definition: qgsvectorlayerjoininfo.h:212
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:63
QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
Definition: qgsvectorlayerjoinbuffer.cpp:28
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1079
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
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:387
QgsFeatureRequest::setLimit
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
Definition: qgsfeaturerequest.cpp:173
QgsVectorLayerJoinBuffer::joinedFeatureOf
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
Definition: qgsvectorlayerjoinbuffer.cpp:429
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:45
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:86
QgsFields::fieldOriginIndex
int fieldOriginIndex(int fieldIdx) const
Gets 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:665
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:66
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsVectorLayerJoinBuffer::changeAttributeValue
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers.
Definition: qgsvectorlayerjoinbuffer.cpp:618
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:1194
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:344
QgsFields::at
QgsField at(int i) const
Gets 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:265
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:179
QgsVectorLayerJoinBuffer::removeJoin
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
Definition: qgsvectorlayerjoinbuffer.cpp:101
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:174
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:129
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:357
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:48
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