QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
qgsvectorlayereditbuffergroup.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayereditbuffergroup.cpp - QgsVectorLayerEditBufferGroup
3
4 ---------------------
5 begin : 22.12.2021
6 copyright : (C) 2021 by Damiano Lombardi
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgsproject.h"
20#include "qgstransaction.h"
21#include "qgsvectorlayer.h"
23
24#include <QQueue>
25
26#include "moc_qgsvectorlayereditbuffergroup.cpp"
27
29 : QObject( parent )
30{}
31
33{
34 mLayers.insert( layer );
35}
36
38{
39 mLayers.remove( layer );
40}
41
43{
44 mLayers.clear();
45}
46
47QSet<QgsVectorLayer *> QgsVectorLayerEditBufferGroup::layers() const
48{
49 return mLayers;
50}
51
53{
54 QSet<QgsVectorLayer *> modifiedLayers;
55
56 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
57 if ( layer->isModified() )
58 modifiedLayers.insert( layer );
59
60 return modifiedLayers;
61}
62
64{
65 if ( mIsEditing )
66 return true;
67
68 bool editingStarted = true;
69 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
70 {
71 if ( !layer->isValid() )
72 {
73 editingStarted = false;
74 QgsLogger::debug( tr( "Can't start editing invalid layer '%1'." ).arg( layer->name() ) );
75 break;
76 }
77
78 if ( !layer->dataProvider() )
79 {
80 editingStarted = false;
81 QgsLogger::debug( tr( "Can't start editing layer '%1' with invalid data provider." ).arg( layer->name() ) );
82 break;
83 }
84
85 // allow editing if provider supports any of the capabilities
86 if ( !layer->supportsEditing() )
87 {
88 editingStarted = false;
89 QgsLogger::debug( tr( "Can't start editing. Layer '%1' doesn't support editing." ).arg( layer->name() ) );
90 break;
91 }
92
93 if ( layer->editBuffer() )
94 {
95 // editing already underway
96 layer->editBuffer()->setEditBufferGroup( this );
97 continue;
98 }
99
100
101 emit layer->beforeEditingStarted();
102 layer->dataProvider()->enterUpdateMode();
103 layer->createEditBuffer();
104 layer->editBuffer()->setEditBufferGroup( this );
105 layer->updateFields();
106 emit layer->editingStarted();
107 }
108
109 if ( !editingStarted )
110 {
111 QStringList rollbackErrors;
112 if ( !rollBack( rollbackErrors, true ) )
113 QgsLogger::debug( tr( "Can't rollback after start editing failure. Roll back detailed errors: %1" ).arg( rollbackErrors.join( " / " ) ) );
114 }
115
116 mIsEditing = editingStarted;
117 return mIsEditing;
118}
119
120bool QgsVectorLayerEditBufferGroup::commitChanges( QStringList &commitErrors, bool stopEditing )
121{
122 bool success = true;
123
124 const QSet<QgsVectorLayer *> constModifiedLayers = modifiedLayers();
125 if ( constModifiedLayers.isEmpty() )
126 {
127 editingFinished( stopEditing );
128 mIsEditing = !stopEditing;
129 return success;
130 }
131
132 QMap<QString, QSet<QgsVectorLayer *> > connectionStringsLayers;
133 for ( QgsVectorLayer *modifiedLayer : constModifiedLayers )
134 if ( QgsTransaction::supportsTransaction( modifiedLayer ) )
135 connectionStringsLayers[QgsTransaction::connectionString( modifiedLayer->source() )].insert( modifiedLayer );
136
137 QList<QgsVectorLayer *> transactionLayers;
138 QList<std::shared_ptr<QgsTransaction> > openTransactions;
139 const QStringList connectionStrings = connectionStringsLayers.keys();
140 for ( const QString &connectionString : connectionStrings )
141 {
142 const QString providerKey = ( *connectionStringsLayers.value( connectionString ).begin() )->providerType();
143
144 std::shared_ptr<QgsTransaction> transaction;
145 transaction.reset( QgsTransaction::create( connectionString, providerKey ) );
146 if ( !transaction )
147 {
148 commitErrors << tr( "ERROR: data source '%1', is not available for transactions." ).arg( connectionString );
149 success = false;
150 break;
151 }
152
153 QString errorMsg;
154 if ( !transaction->begin( errorMsg ) )
155 {
156 commitErrors << tr( "ERROR: could not start a transaction on data provider '%1', detailed error: '%2'." ).arg( providerKey, errorMsg );
157 success = false;
158 break;
159 }
160
161 const auto constLayers = connectionStringsLayers.value( connectionString );
162 for ( QgsVectorLayer *layer : constLayers )
163 {
164 if ( !transaction->addLayer( layer, true ) )
165 {
166 commitErrors << tr( "ERROR: could not add layer '%1' to transaction on data provider '%2'." ).arg( layer->name(), providerKey );
167 success = false;
168 break;
169 }
170
171 transactionLayers.append( layer );
172 }
173
174 openTransactions.append( transaction );
175
176 if ( !success )
177 break;
178 }
179
180 // Order layers children to parents
181 const QList<QgsVectorLayer *> orderedLayers = orderLayersParentsToChildren( constModifiedLayers );
182 QList<QgsVectorLayer *>::const_iterator orderedLayersIterator;
183
184 // Check geometry types
185 if ( success )
186 {
187 for ( orderedLayersIterator = orderedLayers.constBegin(); orderedLayersIterator != orderedLayers.constEnd(); ++orderedLayersIterator )
188 {
189 if ( !( *orderedLayersIterator )->editBuffer() )
190 {
191 commitErrors << tr( "ERROR: edit buffer of layer '%1' is not valid." ).arg( ( *orderedLayersIterator )->name() );
192 success = false;
193 break;
194 }
195
196 success = ( *orderedLayersIterator )->editBuffer()->commitChangesCheckGeometryTypeCompatibility( commitErrors );
197 if ( !success )
198 break;
199 }
200 }
201
202 QSet<QgsVectorLayer *> modifiedLayersOnProviderSide;
203
204 // Change fields (add new fields, delete fields)
205 if ( success )
206 {
207 for ( orderedLayersIterator = orderedLayers.constBegin(); orderedLayersIterator != orderedLayers.constEnd(); ++orderedLayersIterator )
208 {
209 QgsFields oldFields = ( *orderedLayersIterator )->fields();
210
211 bool attributesDeleted = false;
212 success = ( *orderedLayersIterator )->editBuffer()->commitChangesDeleteAttributes( attributesDeleted, commitErrors );
213 if ( !success )
214 break;
215
216 bool attributesRenamed = false;
217 success = ( *orderedLayersIterator )->editBuffer()->commitChangesRenameAttributes( attributesRenamed, commitErrors );
218 if ( !success )
219 break;
220
221 bool attributesAdded = false;
222 success = ( *orderedLayersIterator )->editBuffer()->commitChangesAddAttributes( attributesAdded, commitErrors );
223 if ( !success )
224 break;
225
226 if ( attributesDeleted || attributesRenamed || attributesAdded )
227 {
228 if ( !transactionLayers.contains( ( *orderedLayersIterator ) ) )
229 modifiedLayersOnProviderSide.insert( ( *orderedLayersIterator ) );
230
231 success = ( *orderedLayersIterator )->editBuffer()->commitChangesCheckAttributesModifications( oldFields, commitErrors );
232 if ( !success )
233 break;
234 }
235 }
236 }
237
238 // delete all features, in reverse dependency order (children first)
239 if ( success )
240 {
241 orderedLayersIterator = orderedLayers.constEnd();
242 while ( orderedLayersIterator != orderedLayers.constBegin() )
243 {
244 --orderedLayersIterator;
245 bool featuresDeleted;
246 success = ( *orderedLayersIterator )->editBuffer()->commitChangesDeleteFeatures( featuresDeleted, commitErrors );
247 if ( !success )
248 break;
249
250 if ( featuresDeleted && transactionLayers.contains( ( *orderedLayersIterator ) ) )
251 modifiedLayersOnProviderSide.insert( ( *orderedLayersIterator ) );
252 }
253 }
254
255 // add all features, in forward dependency order (parents first)
256 if ( success )
257 {
258 for ( orderedLayersIterator = orderedLayers.constBegin(); orderedLayersIterator != orderedLayers.constEnd(); ++orderedLayersIterator )
259 {
260 bool featuresAdded;
261 ( *orderedLayersIterator )->editBuffer()->commitChangesAddFeatures( featuresAdded, commitErrors );
262 if ( !success )
263 break;
264
265 if ( featuresAdded && transactionLayers.contains( ( *orderedLayersIterator ) ) )
266 modifiedLayersOnProviderSide.insert( ( *orderedLayersIterator ) );
267 }
268 }
269
270 // change all attributes and geometries in reverse dependency order (children first)
271 if ( success )
272 {
273 orderedLayersIterator = orderedLayers.constEnd();
274 while ( orderedLayersIterator != orderedLayers.constBegin() )
275 {
276 --orderedLayersIterator;
277
278 bool attributesChanged;
279 success = ( *orderedLayersIterator )->editBuffer()->commitChangesChangeAttributes( attributesChanged, commitErrors );
280 if ( !success )
281 break;
282
283 if ( attributesChanged && transactionLayers.contains( ( *orderedLayersIterator ) ) )
284 modifiedLayersOnProviderSide.insert( ( *orderedLayersIterator ) );
285 }
286 }
287
288 // if everything went well, commit
289 if ( success )
290 {
291 QList<std::shared_ptr<QgsTransaction> >::iterator openTransactionsIterator = openTransactions.begin();
292 while ( openTransactionsIterator != openTransactions.end() )
293 {
294 QString errorMsg;
295 if ( !( *openTransactionsIterator )->commit( errorMsg ) )
296 {
297 success = false;
298 commitErrors << tr( "ERROR: could not commit a transaction, detailed error: '%1'." ).arg( errorMsg );
299 break;
300 }
301
302 modifiedLayersOnProviderSide += connectionStringsLayers.value( ( *openTransactionsIterator )->connectionString() );
303 openTransactionsIterator = openTransactions.erase( openTransactionsIterator );
304 }
305 }
306
307 // Otherwise rollback
308 if ( !success )
309 {
310 // Append additional information about layer which can't be rollbacked
311 if ( !modifiedLayersOnProviderSide.isEmpty() )
312 {
313 if ( modifiedLayersOnProviderSide.size() == 1 )
314 commitErrors << tr( "WARNING: changes to layer '%1' were already sent to data provider and cannot be rolled back." ).arg( ( *modifiedLayersOnProviderSide.begin() )->name() );
315 else
316 {
317 commitErrors << tr( "WARNING: changes to following layers were already sent to data provider and cannot be rolled back:" );
318 for ( QgsVectorLayer *layer : std::as_const( modifiedLayersOnProviderSide ) )
319 commitErrors << tr( "- '%1'" ).arg( layer->name() );
320 }
321 }
322
323 QString rollbackError;
324 for ( const std::shared_ptr<QgsTransaction> &transaction : openTransactions )
325 transaction->rollback( rollbackError );
326 }
327
328 // Stop editing
329 if ( success )
330 editingFinished( stopEditing );
331
332 if ( success && stopEditing )
333 mIsEditing = false;
334
335 return success;
336}
337
338bool QgsVectorLayerEditBufferGroup::rollBack( QStringList &rollbackErrors, bool stopEditing )
339{
340 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
341 {
342 if ( !layer->editBuffer() )
343 continue;
344
345 if ( !layer->dataProvider() )
346 {
347 rollbackErrors << tr( "Layer '%1' doesn't have a valid data provider" ).arg( layer->name() );
348 return false;
349 }
350
351 bool rollbackExtent = !layer->editBuffer()->deletedFeatureIds().isEmpty() || !layer->editBuffer()->addedFeatures().isEmpty() || !layer->editBuffer()->changedGeometries().isEmpty();
352
353 emit layer->beforeRollBack();
354
355 layer->editBuffer()->rollBack();
356
357 emit layer->afterRollBack();
358
359 if ( layer->isModified() )
360 {
361 // new undo stack roll back method
362 // old method of calling every undo could cause many canvas refreshes
363 layer->undoStack()->setIndex( 0 );
364 }
365
366 layer->updateFields();
367
368 if ( stopEditing )
369 {
370 layer->clearEditBuffer();
371 layer->undoStack()->clear();
372 emit layer->editingStopped();
373 }
374
375 if ( rollbackExtent )
376 layer->updateExtents();
377
378 if ( stopEditing )
379 layer->dataProvider()->leaveUpdateMode();
380
381 layer->triggerRepaint();
382 }
383
384 mIsEditing = !stopEditing;
385 return true;
386}
387
389{
390 return mIsEditing;
391}
392
393QList<QgsVectorLayer *> QgsVectorLayerEditBufferGroup::orderLayersParentsToChildren( QSet<QgsVectorLayer *> layers )
394{
395 QList<QgsVectorLayer *> orderedLayers;
396 QSet<QgsVectorLayer *> unorderedLayers = layers;
397
398 bool layerOrdered = true;
399 while ( !unorderedLayers.isEmpty() && layerOrdered )
400 {
401 layerOrdered = false;
402 QSet<QgsVectorLayer *>::iterator unorderedLayerIterator = unorderedLayers.begin();
403 while ( unorderedLayerIterator != unorderedLayers.end() )
404 {
405 // Get referencing relation to find referenced layers
406 const QList<QgsRelation> referencingRelations = QgsProject::instance()->relationManager()->referencingRelations( *unorderedLayerIterator ); // skip-keyword-check
407
408 // If this layer references at least one modified layer continue
409 bool layerReferencingModifiedLayer = false;
410 for ( const QgsRelation &relation : referencingRelations )
411 {
412 if ( unorderedLayers.contains( relation.referencedLayer() ) )
413 {
414 layerReferencingModifiedLayer = true;
415 break;
416 }
417 }
418 if ( layerReferencingModifiedLayer )
419 {
420 ++unorderedLayerIterator;
421 continue;
422 }
423
424 // No modified layer is referencing this layer
425 orderedLayers.append( *unorderedLayerIterator );
426 unorderedLayerIterator = unorderedLayers.erase( unorderedLayerIterator );
427 layerOrdered = true;
428 }
429 }
430
431 if ( !unorderedLayers.isEmpty() )
432 {
433 QgsLogger::warning( tr( "Circular relation between some layers. Correct saving order of layers can't be guaranteed" ) );
434 orderedLayers.append( unorderedLayers.values() );
435 }
436
437 return orderedLayers;
438}
439
440void QgsVectorLayerEditBufferGroup::editingFinished( bool stopEditing )
441{
442 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
443 {
444 if ( !layer->mDeletedFids.empty() )
445 {
446 emit layer->featuresDeleted( layer->mDeletedFids );
447 layer->mDeletedFids.clear();
448 }
449
450 if ( stopEditing )
451 layer->clearEditBuffer();
452
453 layer->undoStack()->clear();
454 emit layer->afterCommitChanges();
455 if ( stopEditing )
456 emit layer->editingStopped();
457
458 layer->updateFields();
459
460 layer->dataProvider()->updateExtents();
461
462 if ( stopEditing )
463 {
464 layer->dataProvider()->leaveUpdateMode();
465 }
466
467 // This second call is required because OGR provider with JSON
468 // driver might have changed fields order after the call to
469 // leaveUpdateMode
470 if ( layer->fields().names() != layer->dataProvider()->fields().names() )
471 {
472 layer->updateFields();
473 }
474
475 layer->triggerRepaint();
476 }
477}
Container of fields for a vector layer.
Definition qgsfields.h:46
static void debug(const QString &msg, int debuglevel=1, const char *file=nullptr, const char *function=nullptr, int line=-1)
Goes to qDebug.
Definition qgslogger.cpp:62
static void warning(const QString &msg)
Goes to qWarning.
QgsRelationManager * relationManager
Definition qgsproject.h:124
static QgsProject * instance()
Returns the QgsProject singleton instance.
QList< QgsRelation > referencingRelations(const QgsVectorLayer *layer=nullptr, int fieldIdx=-2) const
Gets all relations where the specified layer (and field) is the referencing part (i....
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
QString connectionString() const
Returns the connection string of the transaction.
static QgsTransaction * create(const QString &connString, const QString &providerKey)
Create a transaction for the specified connection string connString and provider with providerKey.
bool commitChanges(QStringList &commitErrors, bool stopEditing=true)
Attempts to commit any changes to disk.
void clear()
Remove all layers from this edit buffer group.
bool rollBack(QStringList &rollbackErrors, bool stopEditing=true)
Stop editing and discard the edits.
void removeLayer(QgsVectorLayer *layer)
Remove a layer from this edit buffer group.
void addLayer(QgsVectorLayer *layer)
Add a layer to this edit buffer group.
QSet< QgsVectorLayer * > layers() const
Gets the set of layers currently managed by this edit buffer group.
QSet< QgsVectorLayer * > modifiedLayers() const
Gets the set of modified layers currently managed by this edit buffer group.
QgsVectorLayerEditBufferGroup(QObject *parent=nullptr)
Constructor for QgsEditBufferGroup.
bool isEditing() const
Returns true if the layers are in editing mode.
Represents a vector layer which manages a vector based dataset.