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