Branch data Line data Source code
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 : :
18 : : #include "qgsvectorlayerjoinbuffer.h"
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 : :
28 : 277 : QgsVectorLayerJoinBuffer::QgsVectorLayerJoinBuffer( QgsVectorLayer *layer )
29 : 277 : : mLayer( layer )
30 : 831 : {
31 : 277 : }
32 : :
33 : 0 : static QList<QgsVectorLayer *> _outEdges( QgsVectorLayer *vl )
34 : : {
35 : 0 : QList<QgsVectorLayer *> lst;
36 : 0 : const auto constVectorJoins = vl->vectorJoins();
37 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
38 : : {
39 : 0 : if ( QgsVectorLayer *joinVl = info.joinLayer() )
40 : 0 : lst << joinVl;
41 : : }
42 : 0 : return lst;
43 : 0 : }
44 : :
45 : 0 : static bool _hasCycleDFS( QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
46 : : {
47 : 0 : if ( mark.value( n ) == 1 ) // temporary
48 : 0 : return true;
49 : 0 : if ( mark.value( n ) == 0 ) // not visited
50 : : {
51 : 0 : mark[n] = 1; // temporary
52 : 0 : const auto outEdges { _outEdges( n ) };
53 : 0 : for ( QgsVectorLayer *m : outEdges )
54 : : {
55 : 0 : if ( _hasCycleDFS( m, mark ) )
56 : 0 : return true;
57 : : }
58 : 0 : mark[n] = 2; // permanent
59 : 0 : }
60 : 0 : return false;
61 : 0 : }
62 : :
63 : :
64 : 0 : bool QgsVectorLayerJoinBuffer::addJoin( const QgsVectorLayerJoinInfo &joinInfo )
65 : : {
66 : 0 : QMutexLocker locker( &mMutex );
67 : 0 : 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 : 0 : QHash<QgsVectorLayer *, int> markDFS;
72 : 0 : if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
73 : : {
74 : : // we have to reject this one
75 : 0 : mVectorJoins.pop_back();
76 : 0 : return false;
77 : : }
78 : :
79 : : //cache joined layer to virtual memory if specified by user
80 : 0 : if ( joinInfo.isUsingMemoryCache() )
81 : : {
82 : 0 : cacheJoinLayer( mVectorJoins.last() );
83 : 0 : }
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 : 0 : if ( QgsVectorLayer *vl = joinInfo.joinLayer() )
90 : : {
91 : 0 : connectJoinedLayer( vl );
92 : 0 : }
93 : :
94 : 0 : locker.unlock();
95 : :
96 : 0 : emit joinedFieldsChanged();
97 : 0 : return true;
98 : 0 : }
99 : :
100 : :
101 : 0 : bool QgsVectorLayerJoinBuffer::removeJoin( const QString &joinLayerId )
102 : : {
103 : 0 : QMutexLocker locker( &mMutex );
104 : 0 : bool res = false;
105 : 0 : for ( int i = 0; i < mVectorJoins.size(); ++i )
106 : : {
107 : 0 : if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
108 : : {
109 : 0 : if ( QgsVectorLayer *vl = mVectorJoins.at( i ).joinLayer() )
110 : : {
111 : 0 : disconnect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields );
112 : 0 : }
113 : :
114 : 0 : mVectorJoins.removeAt( i );
115 : 0 : res = true;
116 : 0 : }
117 : 0 : }
118 : :
119 : 0 : emit joinedFieldsChanged();
120 : 0 : return res;
121 : 0 : }
122 : :
123 : 0 : void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo )
124 : : {
125 : : //memory cache not required or already done
126 : 0 : if ( !joinInfo.isUsingMemoryCache() || !joinInfo.cacheDirty )
127 : : {
128 : 0 : return;
129 : : }
130 : :
131 : 0 : QgsVectorLayer *cacheLayer = joinInfo.joinLayer();
132 : 0 : if ( cacheLayer )
133 : : {
134 : 0 : int joinFieldIndex = cacheLayer->fields().indexFromName( joinInfo.joinFieldName() );
135 : :
136 : 0 : if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->fields().count() )
137 : 0 : return;
138 : :
139 : 0 : joinInfo.cachedAttributes.clear();
140 : :
141 : 0 : QgsFeatureRequest request;
142 : 0 : request.setFlags( QgsFeatureRequest::NoGeometry );
143 : : // maybe user requested just a subset of layer's attributes
144 : : // so we do not have to cache everything
145 : 0 : QVector<int> subsetIndices;
146 : 0 : if ( joinInfo.hasSubset() )
147 : : {
148 : 0 : const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( joinInfo );
149 : 0 : subsetIndices = joinSubsetIndices( cacheLayer, subsetNames );
150 : :
151 : : // we need just subset of attributes - but make sure to include join field name
152 : 0 : QgsAttributeList cacheLayerAttrs = subsetIndices.toList();
153 : 0 : if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
154 : 0 : cacheLayerAttrs.append( joinFieldIndex );
155 : 0 : request.setSubsetOfAttributes( cacheLayerAttrs );
156 : 0 : }
157 : :
158 : 0 : QgsFeatureIterator fit = cacheLayer->getFeatures( request );
159 : 0 : QgsFeature f;
160 : 0 : while ( fit.nextFeature( f ) )
161 : : {
162 : 0 : QgsAttributes attrs = f.attributes();
163 : 0 : QString key = attrs.at( joinFieldIndex ).toString();
164 : 0 : if ( joinInfo.hasSubset() )
165 : : {
166 : 0 : QgsAttributes subsetAttrs( subsetIndices.count() );
167 : 0 : for ( int i = 0; i < subsetIndices.count(); ++i )
168 : 0 : subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
169 : 0 : joinInfo.cachedAttributes.insert( key, subsetAttrs );
170 : 0 : }
171 : : else
172 : : {
173 : 0 : QgsAttributes attrs2 = attrs;
174 : 0 : attrs2.remove( joinFieldIndex ); // skip the join field to avoid double field names (fields often have the same name)
175 : 0 : joinInfo.cachedAttributes.insert( key, attrs2 );
176 : 0 : }
177 : 0 : }
178 : 0 : joinInfo.cacheDirty = false;
179 : 0 : }
180 : 0 : }
181 : :
182 : :
183 : 0 : QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset )
184 : : {
185 : 0 : QVector<int> subsetIndices;
186 : 0 : const QgsFields &fields = joinLayer->fields();
187 : 0 : for ( int i = 0; i < joinFieldsSubset.count(); ++i )
188 : : {
189 : 0 : QString joinedFieldName = joinFieldsSubset.at( i );
190 : 0 : int index = fields.lookupField( joinedFieldName );
191 : 0 : if ( index != -1 )
192 : : {
193 : 0 : subsetIndices.append( index );
194 : 0 : }
195 : : else
196 : : {
197 : 0 : QgsDebugMsg( "Join layer subset field not found: " + joinedFieldName );
198 : : }
199 : 0 : }
200 : :
201 : 0 : return subsetIndices;
202 : 0 : }
203 : :
204 : 0 : void QgsVectorLayerJoinBuffer::updateFields( QgsFields &fields )
205 : : {
206 : 0 : QString prefix;
207 : :
208 : 0 : QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
209 : 0 : for ( int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
210 : : {
211 : 0 : QgsVectorLayer *joinLayer = joinIt->joinLayer();
212 : 0 : if ( !joinLayer )
213 : : {
214 : 0 : continue;
215 : : }
216 : :
217 : 0 : const QgsFields &joinFields = joinLayer->fields();
218 : 0 : QString joinFieldName = joinIt->joinFieldName();
219 : :
220 : 0 : QSet<QString> subset;
221 : 0 : if ( joinIt->hasSubset() )
222 : : {
223 : 0 : const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
224 : 0 : subset = qgis::listToSet( subsetNames );
225 : 0 : }
226 : :
227 : 0 : if ( joinIt->prefix().isNull() )
228 : : {
229 : 0 : prefix = joinLayer->name() + '_';
230 : 0 : }
231 : : else
232 : : {
233 : 0 : prefix = joinIt->prefix();
234 : : }
235 : :
236 : 0 : for ( int idx = 0; idx < joinFields.count(); ++idx )
237 : : {
238 : : // if using just a subset of fields, filter some of them out
239 : 0 : if ( joinIt->hasSubset() && !subset.contains( joinFields.at( idx ).name() ) )
240 : 0 : continue;
241 : :
242 : : //skip the join field to avoid double field names (fields often have the same name)
243 : : // when using subset of field, use all the selected fields
244 : 0 : if ( joinIt->hasSubset() || joinFields.at( idx ).name() != joinFieldName )
245 : : {
246 : 0 : QgsField f = joinFields.at( idx );
247 : 0 : f.setName( prefix + f.name() );
248 : 0 : fields.append( f, QgsFields::OriginJoin, idx + ( joinIdx * 1000 ) );
249 : 0 : }
250 : 0 : }
251 : 0 : }
252 : 0 : }
253 : :
254 : 0 : void QgsVectorLayerJoinBuffer::createJoinCaches()
255 : : {
256 : 0 : QMutexLocker locker( &mMutex );
257 : 0 : QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
258 : 0 : for ( ; joinIt != mVectorJoins.end(); ++joinIt )
259 : : {
260 : 0 : if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
261 : 0 : cacheJoinLayer( *joinIt );
262 : 0 : }
263 : 0 : }
264 : :
265 : :
266 : 0 : void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &document ) const
267 : : {
268 : 0 : QDomElement vectorJoinsElem = document.createElement( QStringLiteral( "vectorjoins" ) );
269 : 0 : layer_node.appendChild( vectorJoinsElem );
270 : 0 : QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
271 : 0 : for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
272 : : {
273 : 0 : if ( isAuxiliaryJoin( *joinIt ) )
274 : 0 : continue;
275 : :
276 : 0 : QDomElement joinElem = document.createElement( QStringLiteral( "join" ) );
277 : :
278 : 0 : joinElem.setAttribute( QStringLiteral( "targetFieldName" ), joinIt->targetFieldName() );
279 : :
280 : 0 : joinElem.setAttribute( QStringLiteral( "joinLayerId" ), joinIt->joinLayerId() );
281 : 0 : joinElem.setAttribute( QStringLiteral( "joinFieldName" ), joinIt->joinFieldName() );
282 : :
283 : 0 : joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() );
284 : 0 : joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() );
285 : 0 : joinElem.setAttribute( QStringLiteral( "editable" ), joinIt->isEditable() );
286 : 0 : joinElem.setAttribute( QStringLiteral( "upsertOnEdit" ), joinIt->hasUpsertOnEdit() );
287 : 0 : joinElem.setAttribute( QStringLiteral( "cascadedDelete" ), joinIt->hasCascadedDelete() );
288 : :
289 : 0 : if ( joinIt->hasSubset() )
290 : : {
291 : 0 : QDomElement subsetElem = document.createElement( QStringLiteral( "joinFieldsSubset" ) );
292 : 0 : const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
293 : :
294 : 0 : const auto constSubsetNames = subsetNames;
295 : 0 : for ( const QString &fieldName : constSubsetNames )
296 : : {
297 : 0 : QDomElement fieldElem = document.createElement( QStringLiteral( "field" ) );
298 : 0 : fieldElem.setAttribute( QStringLiteral( "name" ), fieldName );
299 : 0 : subsetElem.appendChild( fieldElem );
300 : 0 : }
301 : :
302 : 0 : joinElem.appendChild( subsetElem );
303 : 0 : }
304 : :
305 : 0 : if ( !joinIt->prefix().isNull() )
306 : : {
307 : 0 : joinElem.setAttribute( QStringLiteral( "customPrefix" ), joinIt->prefix() );
308 : 0 : joinElem.setAttribute( QStringLiteral( "hasCustomPrefix" ), 1 );
309 : 0 : }
310 : :
311 : 0 : vectorJoinsElem.appendChild( joinElem );
312 : 0 : }
313 : 0 : }
314 : :
315 : 0 : void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
316 : : {
317 : 0 : mVectorJoins.clear();
318 : 0 : QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( "vectorjoins" ) );
319 : 0 : if ( !vectorJoinsElem.isNull() )
320 : : {
321 : 0 : QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( "join" ) );
322 : 0 : for ( int i = 0; i < joinList.size(); ++i )
323 : : {
324 : 0 : QDomElement infoElem = joinList.at( i ).toElement();
325 : 0 : QgsVectorLayerJoinInfo info;
326 : 0 : info.setJoinFieldName( infoElem.attribute( QStringLiteral( "joinFieldName" ) ) );
327 : : // read layer ID - to turn it into layer object, caller will need to call resolveReferences() later
328 : 0 : info.setJoinLayerId( infoElem.attribute( QStringLiteral( "joinLayerId" ) ) );
329 : 0 : info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) );
330 : 0 : info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() );
331 : 0 : info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() );
332 : 0 : info.setEditable( infoElem.attribute( QStringLiteral( "editable" ) ).toInt() );
333 : 0 : info.setUpsertOnEdit( infoElem.attribute( QStringLiteral( "upsertOnEdit" ) ).toInt() );
334 : 0 : info.setCascadedDelete( infoElem.attribute( QStringLiteral( "cascadedDelete" ) ).toInt() );
335 : :
336 : 0 : QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) );
337 : 0 : if ( !subsetElem.isNull() )
338 : : {
339 : 0 : QStringList *fieldNames = new QStringList;
340 : 0 : QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( "field" ) );
341 : 0 : fieldNames->reserve( fieldNodes.count() );
342 : 0 : for ( int i = 0; i < fieldNodes.count(); ++i )
343 : 0 : *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( "name" ) );
344 : 0 : info.setJoinFieldNamesSubset( fieldNames );
345 : 0 : }
346 : :
347 : 0 : if ( infoElem.attribute( QStringLiteral( "hasCustomPrefix" ) ).toInt() )
348 : 0 : info.setPrefix( infoElem.attribute( QStringLiteral( "customPrefix" ) ) );
349 : : else
350 : 0 : info.setPrefix( QString() );
351 : :
352 : 0 : addJoin( info );
353 : 0 : }
354 : 0 : }
355 : 0 : }
356 : :
357 : 0 : void QgsVectorLayerJoinBuffer::resolveReferences( QgsProject *project )
358 : : {
359 : 0 : bool resolved = false;
360 : 0 : for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
361 : : {
362 : 0 : if ( it->joinLayer() )
363 : 0 : continue; // already resolved
364 : :
365 : 0 : if ( QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->mapLayer( it->joinLayerId() ) ) )
366 : : {
367 : 0 : it->setJoinLayer( joinedLayer );
368 : 0 : connectJoinedLayer( joinedLayer );
369 : 0 : resolved = true;
370 : 0 : }
371 : 0 : }
372 : :
373 : 0 : if ( resolved )
374 : 0 : emit joinedFieldsChanged();
375 : 0 : }
376 : :
377 : 0 : int QgsVectorLayerJoinBuffer::joinedFieldsOffset( const QgsVectorLayerJoinInfo *info, const QgsFields &fields )
378 : : {
379 : 0 : if ( !info )
380 : 0 : return -1;
381 : :
382 : 0 : int joinIndex = mVectorJoins.indexOf( *info );
383 : 0 : if ( joinIndex == -1 )
384 : 0 : return -1;
385 : :
386 : 0 : for ( int i = 0; i < fields.count(); ++i )
387 : : {
388 : 0 : if ( fields.fieldOrigin( i ) != QgsFields::OriginJoin )
389 : 0 : continue;
390 : :
391 : 0 : if ( fields.fieldOriginIndex( i ) / 1000 == joinIndex )
392 : 0 : return i;
393 : 0 : }
394 : 0 : return -1;
395 : 0 : }
396 : :
397 : 0 : const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, const QgsFields &fields, int &sourceFieldIndex ) const
398 : : {
399 : 0 : if ( fields.fieldOrigin( index ) != QgsFields::OriginJoin )
400 : 0 : return nullptr;
401 : :
402 : 0 : int originIndex = fields.fieldOriginIndex( index );
403 : 0 : int sourceJoinIndex = originIndex / 1000;
404 : 0 : sourceFieldIndex = originIndex % 1000;
405 : :
406 : 0 : if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
407 : 0 : return nullptr;
408 : :
409 : 0 : return &( mVectorJoins[sourceJoinIndex] );
410 : 0 : }
411 : :
412 : 0 : QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
413 : : {
414 : 0 : QList<const QgsVectorLayerJoinInfo *> infos;
415 : :
416 : 0 : const auto constMVectorJoins = mVectorJoins;
417 : 0 : for ( const QgsVectorLayerJoinInfo &info : constMVectorJoins )
418 : : {
419 : 0 : if ( infos.contains( &info ) )
420 : 0 : continue;
421 : :
422 : 0 : if ( info.targetFieldName() == field.name() )
423 : 0 : infos.append( &info );
424 : : }
425 : :
426 : 0 : return infos;
427 : 0 : }
428 : :
429 : 0 : QgsFeature QgsVectorLayerJoinBuffer::joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const
430 : : {
431 : 0 : QgsFeature joinedFeature;
432 : :
433 : 0 : if ( info->joinLayer() )
434 : : {
435 : 0 : joinedFeature.initAttributes( info->joinLayer()->fields().count() );
436 : 0 : joinedFeature.setFields( info->joinLayer()->fields() );
437 : :
438 : 0 : QString joinFieldName = info->joinFieldName();
439 : 0 : const QVariant targetValue = feature.attribute( info->targetFieldName() );
440 : 0 : QString filter = QgsExpression::createFieldEqualityExpression( joinFieldName, targetValue );
441 : :
442 : 0 : QgsFeatureRequest request;
443 : 0 : request.setFilterExpression( filter );
444 : 0 : request.setLimit( 1 );
445 : :
446 : 0 : QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
447 : 0 : it.nextFeature( joinedFeature );
448 : 0 : }
449 : :
450 : 0 : return joinedFeature;
451 : 0 : }
452 : :
453 : 0 : QgsFeature QgsVectorLayerJoinBuffer::targetedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const
454 : : {
455 : 0 : QgsFeature targetedFeature;
456 : :
457 : 0 : if ( info->joinLayer() )
458 : : {
459 : 0 : const QVariant targetValue = feature.attribute( info->joinFieldName() );
460 : 0 : const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue );
461 : :
462 : 0 : QgsFeatureRequest request;
463 : 0 : request.setFilterExpression( filter );
464 : 0 : request.setLimit( 1 );
465 : :
466 : 0 : QgsFeatureIterator it = mLayer->getFeatures( request );
467 : 0 : it.nextFeature( targetedFeature );
468 : 0 : }
469 : :
470 : 0 : return targetedFeature;
471 : 0 : }
472 : :
473 : 199 : QgsVectorLayerJoinBuffer *QgsVectorLayerJoinBuffer::clone() const
474 : : {
475 : 199 : QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );
476 : 199 : cloned->mVectorJoins = mVectorJoins;
477 : 199 : return cloned;
478 : 0 : }
479 : :
480 : 0 : void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
481 : : {
482 : : // TODO - check - this whole method is probably not needed anymore,
483 : : // since the cache handling is covered by joinedLayerModified()
484 : :
485 : 0 : QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
486 : : Q_ASSERT( joinedLayer );
487 : :
488 : : // recache the joined layer
489 : 0 : for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
490 : : {
491 : 0 : if ( joinedLayer == it->joinLayer() )
492 : : {
493 : 0 : it->cachedAttributes.clear();
494 : 0 : cacheJoinLayer( *it );
495 : 0 : }
496 : 0 : }
497 : :
498 : 0 : emit joinedFieldsChanged();
499 : 0 : }
500 : :
501 : 0 : void QgsVectorLayerJoinBuffer::joinedLayerModified()
502 : : {
503 : 0 : QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
504 : : Q_ASSERT( joinedLayer );
505 : :
506 : : // recache the joined layer
507 : 0 : for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
508 : : {
509 : 0 : if ( joinedLayer == it->joinLayer() )
510 : : {
511 : 0 : it->cacheDirty = true;
512 : 0 : }
513 : 0 : }
514 : 0 : }
515 : :
516 : 0 : void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
517 : : {
518 : 0 : QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
519 : : Q_ASSERT( joinedLayer );
520 : :
521 : 0 : removeJoin( joinedLayer->id() );
522 : 0 : }
523 : :
524 : 0 : void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
525 : : {
526 : 0 : connect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields, Qt::UniqueConnection );
527 : 0 : connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
528 : 0 : connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
529 : 0 : }
530 : :
531 : 0 : bool QgsVectorLayerJoinBuffer::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags )
532 : : {
533 : 0 : if ( !containsJoins() )
534 : 0 : return false;
535 : :
536 : : // try to add/update a feature in each joined layer
537 : 0 : const QgsVectorJoinList joins = vectorJoins();
538 : 0 : for ( const QgsVectorLayerJoinInfo &info : joins )
539 : : {
540 : 0 : QgsVectorLayer *joinLayer = info.joinLayer();
541 : :
542 : 0 : if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
543 : : {
544 : 0 : QgsFeatureList joinFeatures;
545 : :
546 : 0 : for ( const QgsFeature &feature : std::as_const( features ) )
547 : : {
548 : 0 : const QgsFeature joinFeature = info.extractJoinedFeature( feature );
549 : :
550 : : // we don't want to add a new feature in joined layer when the id
551 : : // column value yet exist, we just want to update the existing one
552 : 0 : const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
553 : 0 : const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );
554 : :
555 : 0 : QgsFeatureRequest request;
556 : 0 : request.setFlags( QgsFeatureRequest::NoGeometry );
557 : 0 : request.setNoAttributes();
558 : 0 : request.setFilterExpression( filter );
559 : 0 : request.setLimit( 1 );
560 : :
561 : 0 : QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
562 : 0 : QgsFeature existingFeature;
563 : 0 : it.nextFeature( existingFeature );
564 : :
565 : 0 : if ( existingFeature.isValid() )
566 : : {
567 : 0 : if ( info.hasSubset() )
568 : : {
569 : 0 : const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( info );
570 : 0 : const auto constSubsetNames = subsetNames;
571 : 0 : for ( const QString &field : constSubsetNames )
572 : : {
573 : 0 : QVariant newValue = joinFeature.attribute( field );
574 : 0 : int fieldIndex = joinLayer->fields().indexOf( field );
575 : 0 : joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
576 : 0 : }
577 : 0 : }
578 : : else
579 : : {
580 : 0 : const QgsFields joinFields = joinFeature.fields();
581 : 0 : for ( const auto &field : joinFields )
582 : : {
583 : 0 : QVariant newValue = joinFeature.attribute( field.name() );
584 : 0 : int fieldIndex = joinLayer->fields().indexOf( field.name() );
585 : 0 : joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
586 : 0 : }
587 : 0 : }
588 : 0 : }
589 : : else
590 : : {
591 : : // joined feature is added only if one of its field is not null
592 : 0 : bool notNullFields = false;
593 : 0 : const QgsFields joinFields = joinFeature.fields();
594 : 0 : for ( const auto &field : joinFields )
595 : : {
596 : 0 : if ( field.name() == info.joinFieldName() )
597 : 0 : continue;
598 : :
599 : 0 : if ( !joinFeature.attribute( field.name() ).isNull() )
600 : : {
601 : 0 : notNullFields = true;
602 : 0 : break;
603 : : }
604 : : }
605 : :
606 : 0 : if ( notNullFields )
607 : 0 : joinFeatures << joinFeature;
608 : 0 : }
609 : 0 : }
610 : :
611 : 0 : joinLayer->addFeatures( joinFeatures );
612 : 0 : }
613 : : }
614 : :
615 : 0 : return true;
616 : 0 : }
617 : :
618 : 0 : bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
619 : : {
620 : 0 : if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin )
621 : 0 : return false;
622 : :
623 : : int srcFieldIndex;
624 : 0 : const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex );
625 : 0 : if ( info && info->joinLayer() && info->isEditable() )
626 : : {
627 : 0 : QgsFeature feature = mLayer->getFeature( fid );
628 : :
629 : 0 : if ( !feature.isValid() )
630 : 0 : return false;
631 : :
632 : 0 : const QgsFeature joinFeature = joinedFeatureOf( info, feature );
633 : :
634 : 0 : if ( joinFeature.isValid() )
635 : 0 : return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
636 : : else
637 : : {
638 : 0 : feature.setAttribute( field, newValue );
639 : 0 : return addFeatures( QgsFeatureList() << feature );
640 : : }
641 : 0 : }
642 : : else
643 : 0 : return false;
644 : 0 : }
645 : :
646 : 0 : bool QgsVectorLayerJoinBuffer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues )
647 : : {
648 : 0 : bool success = true;
649 : :
650 : 0 : for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
651 : : {
652 : 0 : const int field = it.key();
653 : 0 : const QVariant newValue = it.value();
654 : 0 : QVariant oldValue;
655 : :
656 : 0 : if ( oldValues.contains( field ) )
657 : 0 : oldValue = oldValues[field];
658 : :
659 : 0 : success &= changeAttributeValue( fid, field, newValue, oldValue );
660 : 0 : }
661 : :
662 : 0 : return success;
663 : 0 : }
664 : :
665 : 0 : bool QgsVectorLayerJoinBuffer::deleteFeature( QgsFeatureId fid, QgsVectorLayer::DeleteContext *context ) const
666 : : {
667 : 0 : return deleteFeatures( QgsFeatureIds() << fid, context );
668 : 0 : }
669 : :
670 : 0 : bool QgsVectorLayerJoinBuffer::deleteFeatures( const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context ) const
671 : : {
672 : 0 : if ( !containsJoins() )
673 : 0 : return false;
674 : :
675 : 0 : const auto constFids = fids;
676 : 0 : for ( const QgsFeatureId &fid : constFids )
677 : : {
678 : 0 : const auto constVectorJoins = vectorJoins();
679 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
680 : : {
681 : 0 : if ( info.isEditable() && info.hasCascadedDelete() )
682 : : {
683 : 0 : const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) );
684 : 0 : if ( joinFeature.isValid() )
685 : 0 : info.joinLayer()->deleteFeature( joinFeature.id(), context );
686 : 0 : }
687 : : }
688 : 0 : }
689 : :
690 : 0 : return true;
691 : 0 : }
692 : :
693 : 0 : bool QgsVectorLayerJoinBuffer::isAuxiliaryJoin( const QgsVectorLayerJoinInfo &info ) const
694 : : {
695 : 0 : const QgsAuxiliaryLayer *al = mLayer->auxiliaryLayer();
696 : :
697 : 0 : return al && al->id() == info.joinLayerId();
698 : 0 : }
|