Skip to content

Commit c2831cc

Browse files
committedOct 12, 2020
Don't crash on transaction save
Fixes #39265
1 parent 1d2bb41 commit c2831cc

File tree

3 files changed

+66
-9
lines changed

3 files changed

+66
-9
lines changed
 

‎src/core/qgstransactiongroup.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "qgsvectorlayer.h"
2020
#include "qgsdatasourceuri.h"
2121
#include "qgsvectordataprovider.h"
22+
#include "qgslogger.h"
2223

2324
#include <QTimer>
2425

@@ -102,25 +103,37 @@ void QgsTransactionGroup::onBeforeCommitChanges( bool stopEditing )
102103

103104
mEditingStopping = true;
104105

105-
QgsVectorLayer *triggeringLayer = qobject_cast<QgsVectorLayer *>( sender() );
106+
const QgsVectorLayer *triggeringLayer = qobject_cast<QgsVectorLayer *>( sender() );
106107

107108
QString errMsg;
108109
if ( mTransaction->commit( errMsg ) )
109110
{
110111
const auto constMLayers = mLayers;
111112
for ( QgsVectorLayer *layer : constMLayers )
112113
{
113-
if ( layer != sender() )
114+
if ( layer != triggeringLayer )
115+
{
114116
layer->commitChanges( stopEditing );
117+
}
118+
}
119+
120+
if ( stopEditing )
121+
{
122+
disableTransaction();
123+
}
124+
else
125+
{
126+
if ( ! mTransaction->begin( errMsg ) )
127+
{
128+
QgsDebugMsg( QStringLiteral( "Could not restart a transaction for %1: %2" ).arg( triggeringLayer->name() ).arg( errMsg ) );
129+
}
115130
}
116131

117-
disableTransaction();
118132
}
119133
else
120134
{
121135
emit commitError( errMsg );
122-
// Restart editing the calling layer in the next event loop cycle
123-
QTimer::singleShot( 0, triggeringLayer, &QgsVectorLayer::startEditing );
136+
restartTransaction( triggeringLayer );
124137
}
125138
mEditingStopping = false;
126139
}
@@ -147,8 +160,7 @@ void QgsTransactionGroup::onRollback()
147160
}
148161
else
149162
{
150-
// Restart editing the calling layer in the next event loop cycle
151-
QTimer::singleShot( 0, triggeringLayer, &QgsVectorLayer::startEditing );
163+
restartTransaction( triggeringLayer );
152164
}
153165
mEditingStopping = false;
154166
}
@@ -165,6 +177,12 @@ void QgsTransactionGroup::disableTransaction()
165177
}
166178
}
167179

180+
void QgsTransactionGroup::restartTransaction( const QgsVectorLayer *layer )
181+
{
182+
// Restart editing the calling layer in the next event loop cycle
183+
QTimer::singleShot( 0, layer, &QgsVectorLayer::startEditing );
184+
}
185+
168186
QString QgsTransactionGroup::providerKey() const
169187
{
170188
return mProviderKey;

‎src/core/qgstransactiongroup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class CORE_EXPORT QgsTransactionGroup : public QObject
9191

9292
void disableTransaction();
9393

94+
void restartTransaction( const QgsVectorLayer *layer );
95+
9496
QSet<QgsVectorLayer *> mLayers;
9597
//! Only set while a transaction is active
9698
std::unique_ptr<QgsTransaction> mTransaction;

‎tests/src/python/test_provider_ogr_gpkg.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,7 +1728,7 @@ def testExporterWithFIDColumn(self):
17281728
self.assertEqual(f.id(), 123)
17291729

17301730
def testTransactionGroup(self):
1731-
"""Issue https://github.com/qgis/QGIS/issues/36525"""
1731+
"""Test issue GH #36525"""
17321732

17331733
project = QgsProject()
17341734
project.setAutoTransaction(True)
@@ -1768,7 +1768,7 @@ def testTransactionGroup(self):
17681768
self.assertFalse(vl1_1.isEditable())
17691769
self.assertFalse(vl1_2.isEditable())
17701770

1771-
@unittest.skipIf(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 3, 0), "GDAL 2.3 required")
1771+
@unittest.skipIf(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 3, 0), "GDAL 2.3 required")
17721772
def testTransactionGroupIterator(self):
17731773
"""Test issue GH #39178: the bug is that this test hangs
17741774
forever in an endless loop"""
@@ -1800,6 +1800,43 @@ def testTransactionGroupIterator(self):
18001800
# Test that QGIS sees the new changes
18011801
self.assertEqual(next(vl.getFeatures()).attribute(1), 'new value')
18021802

1803+
def testTransactionGroupCrash(self):
1804+
"""Test issue GH #39265 segfault"""
1805+
1806+
project = QgsProject()
1807+
project.setAutoTransaction(True)
1808+
tmpfile = os.path.join(
1809+
self.basetestpath, 'tempGeoPackageTransactionCrash.gpkg')
1810+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile)
1811+
lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint)
1812+
lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString))
1813+
1814+
f = ogr.Feature(lyr.GetLayerDefn())
1815+
f.SetGeometry(ogr.CreateGeometryFromWkt('POINT (1 1)'))
1816+
f.SetField('str_field', 'one')
1817+
lyr.CreateFeature(f)
1818+
1819+
del lyr
1820+
del ds
1821+
1822+
vl = QgsVectorLayer(tmpfile + '|layername=test', 'test', 'ogr')
1823+
1824+
project.addMapLayers([vl])
1825+
1826+
feature = next(vl.getFeatures())
1827+
feature.setAttributes([None, 'two'])
1828+
1829+
self.assertTrue(vl.startEditing())
1830+
self.assertTrue(vl.addFeature(feature))
1831+
1832+
# Save without leaving editing
1833+
self.assertTrue(vl.commitChanges(False))
1834+
1835+
# Now add another one
1836+
feature.setAttributes([None, 'three'])
1837+
self.assertTrue(vl.addFeature(feature))
1838+
18031839

18041840
if __name__ == '__main__':
18051841
unittest.main()
1842+

0 commit comments

Comments
 (0)
Please sign in to comment.