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