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