Skip to content

Commit 638fc0c

Browse files
authored
Merge pull request #7245 from rouault/gpkg_transactions
[OGR provider] [FEATURE] Add support for transactions on GPKG databases
2 parents 1503459 + 6a987f9 commit 638fc0c

12 files changed

+562
-57
lines changed

python/core/auto_generated/qgstransaction.sip.in

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ returns the last created savepoint
146146
.. versionadded:: 3.0
147147
%End
148148

149+
149150
signals:
150151

151152
void afterRollback();

src/core/qgstransaction.cpp

+48-7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ QgsTransaction *QgsTransaction::create( const QSet<QgsVectorLayer *> &layers )
5151

5252
QgsVectorLayer *firstLayer = *layers.constBegin();
5353

54-
QString connStr = QgsDataSourceUri( firstLayer->source() ).connectionInfo( false );
54+
QString connStr = connectionString( firstLayer->source() );
5555
QString providerKey = firstLayer->dataProvider()->name();
5656
std::unique_ptr<QgsTransaction> transaction( QgsTransaction::create( connStr, providerKey ) );
5757
if ( transaction )
@@ -81,6 +81,46 @@ QgsTransaction::~QgsTransaction()
8181
setLayerTransactionIds( nullptr );
8282
}
8383

84+
// For the needs of the OGR provider with GeoPackage datasources, remove
85+
// any reference to layers in the connection string
86+
QString QgsTransaction::removeLayerIdOrName( const QString &str )
87+
{
88+
QString res( str );
89+
90+
for ( int i = 0; i < 2; i++ )
91+
{
92+
int pos = res.indexOf( i == 0 ? QLatin1String( "|layername=" ) : QLatin1String( "|layerid=" ) );
93+
if ( pos >= 0 )
94+
{
95+
int end = res.indexOf( '|', pos + 1 );
96+
if ( end >= 0 )
97+
{
98+
res = res.mid( 0, pos ) + res.mid( end );
99+
}
100+
else
101+
{
102+
res = res.mid( 0, pos );
103+
}
104+
}
105+
}
106+
return res;
107+
}
108+
109+
///@cond PRIVATE
110+
QString QgsTransaction::connectionString( const QString &layerName )
111+
{
112+
QString connString = QgsDataSourceUri( layerName ).connectionInfo( false );
113+
// In the case of a OGR datasource, connectionInfo() will return an empty
114+
// string. In that case, use the layer->source() itself, and strip any
115+
// reference to layers from it.
116+
if ( connString.isEmpty() )
117+
{
118+
connString = removeLayerIdOrName( layerName );
119+
}
120+
return connString;
121+
}
122+
///@endcond
123+
84124
bool QgsTransaction::addLayer( QgsVectorLayer *layer )
85125
{
86126
if ( !layer )
@@ -90,17 +130,18 @@ bool QgsTransaction::addLayer( QgsVectorLayer *layer )
90130
return false;
91131

92132
//test if provider supports transactions
93-
if ( !layer->dataProvider() || ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::TransactionSupport ) == 0 )
133+
if ( !supportsTransaction( layer ) )
94134
return false;
95135

96136
if ( layer->dataProvider()->transaction() )
97137
return false;
98138

99139
//connection string not compatible
100-
if ( QgsDataSourceUri( layer->source() ).connectionInfo( false ) != mConnString )
140+
141+
if ( connectionString( layer->source() ) != mConnString )
101142
{
102143
QgsDebugMsg( QString( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
103-
layer->id(), QgsDataSourceUri( layer->source() ).connectionInfo( false ), mConnString ) );
144+
layer->id(), connectionString( layer->source() ), mConnString ) );
104145
return false;
105146
}
106147

@@ -162,11 +203,11 @@ bool QgsTransaction::rollback( QString &errorMsg )
162203

163204
bool QgsTransaction::supportsTransaction( const QgsVectorLayer *layer )
164205
{
165-
std::unique_ptr< QLibrary > lib( QgsProviderRegistry::instance()->createProviderLibrary( layer->providerType() ) );
166-
if ( !lib )
206+
//test if provider supports transactions
207+
if ( !layer->dataProvider() || ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::TransactionSupport ) == 0 )
167208
return false;
168209

169-
return lib->resolve( "createTransaction" );
210+
return true;
170211
}
171212

172213
void QgsTransaction::onLayerDeleted()

src/core/qgstransaction.h

+7
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
158158
*/
159159
bool lastSavePointIsDirty() const { return mLastSavePointIsDirty; }
160160

161+
///@cond PRIVATE
162+
// For internal use only, or by QgsTransactionGroup
163+
static QString connectionString( const QString &layerName ) SIP_SKIP;
164+
///@endcond
165+
161166
signals:
162167

163168
/**
@@ -188,6 +193,8 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
188193

189194
void setLayerTransactionIds( QgsTransaction *transaction );
190195

196+
static QString removeLayerIdOrName( const QString &str );
197+
191198
virtual bool beginTransaction( QString &error, int statementTimeout ) = 0;
192199
virtual bool commitTransaction( QString &error ) = 0;
193200
virtual bool rollbackTransaction( QString &error ) = 0;

src/core/qgstransactiongroup.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ bool QgsTransactionGroup::addLayer( QgsVectorLayer *layer )
3333
if ( !QgsTransaction::supportsTransaction( layer ) )
3434
return false;
3535

36-
QString connString = QgsDataSourceUri( layer->source() ).connectionInfo();
37-
36+
QString connString = QgsTransaction::connectionString( layer->source() );
3837
if ( mConnString.isEmpty() )
3938
{
4039
mConnString = connString;
@@ -74,6 +73,8 @@ void QgsTransactionGroup::onEditingStarted()
7473
return;
7574

7675
mTransaction.reset( QgsTransaction::create( mConnString, mProviderKey ) );
76+
if ( !mTransaction )
77+
return;
7778

7879
QString errorMsg;
7980
mTransaction->begin( errorMsg );

src/providers/ogr/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SET (OGR_SRCS
99
qgsgeopackagerasterwritertask.cpp
1010
qgsogrdbconnection.cpp
1111
qgsogrdbtablemodel.cpp
12+
qgsogrtransaction.cpp
1213
)
1314

1415
SET(OGR_MOC_HDRS
@@ -19,6 +20,7 @@ SET(OGR_MOC_HDRS
1920
qgsgeopackagerasterwritertask.h
2021
qgsogrdbconnection.h
2122
qgsogrdbtablemodel.h
23+
qgsogrtransaction.h
2224
)
2325

2426
IF (WITH_GUI)

src/providers/ogr/qgsogrfeatureiterator.cpp

+58-21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "qgssettings.h"
2727
#include "qgsexception.h"
2828
#include "qgswkbtypes.h"
29+
#include "qgsogrtransaction.h"
2930

3031
#include <QTextCodec>
3132
#include <QFile>
@@ -42,38 +43,51 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool
4243
: QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request )
4344
, mFilterFids( mRequest.filterFids() )
4445
, mFilterFidsIt( mFilterFids.constBegin() )
46+
, mSharedDS( source->mSharedDS )
4547
{
46-
//QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection");
47-
mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource ) );
48-
if ( !mConn->ds )
48+
if ( mSharedDS )
4949
{
50-
return;
51-
}
52-
53-
if ( mSource->mLayerName.isNull() )
54-
{
55-
mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex );
50+
mOgrLayer = mSharedDS->createSQLResultLayer( mSource->mEncoding, mSource->mLayerName, mSource->mLayerIndex );
51+
if ( !mOgrLayer )
52+
{
53+
return;
54+
}
5655
}
5756
else
5857
{
59-
mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() );
60-
}
61-
if ( !mOgrLayer )
62-
{
63-
return;
64-
}
58+
//QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection");
59+
mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource ) );
60+
if ( !mConn->ds )
61+
{
62+
return;
63+
}
6564

66-
if ( !mSource->mSubsetString.isEmpty() )
67-
{
68-
mOgrOrigLayer = mOgrLayer;
69-
mOgrLayerWithFid = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, QString(), true, &mOrigFidAdded );
70-
mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString, true, &mOrigFidAdded );
65+
if ( mSource->mLayerName.isNull() )
66+
{
67+
mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex );
68+
}
69+
else
70+
{
71+
mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() );
72+
}
7173
if ( !mOgrLayer )
7274
{
73-
close();
7475
return;
7576
}
77+
78+
if ( !mSource->mSubsetString.isEmpty() )
79+
{
80+
mOgrOrigLayer = mOgrLayer;
81+
mOgrLayerWithFid = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, QString(), true, &mOrigFidAdded );
82+
mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString, true, &mOrigFidAdded );
83+
if ( !mOgrLayer )
84+
{
85+
close();
86+
return;
87+
}
88+
}
7689
}
90+
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );
7791

7892
if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs )
7993
{
@@ -237,6 +251,8 @@ bool QgsOgrFeatureIterator::fetchFeatureWithId( QgsFeatureId id, QgsFeature &fea
237251

238252
bool QgsOgrFeatureIterator::fetchFeature( QgsFeature &feature )
239253
{
254+
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );
255+
240256
feature.setValid( false );
241257

242258
if ( mClosed || !mOgrLayer )
@@ -286,6 +302,8 @@ bool QgsOgrFeatureIterator::fetchFeature( QgsFeature &feature )
286302

287303
bool QgsOgrFeatureIterator::rewind()
288304
{
305+
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );
306+
289307
if ( mClosed || !mOgrLayer )
290308
return false;
291309

@@ -299,6 +317,20 @@ bool QgsOgrFeatureIterator::rewind()
299317

300318
bool QgsOgrFeatureIterator::close()
301319
{
320+
if ( mSharedDS )
321+
{
322+
iteratorClosed();
323+
324+
if ( mOgrLayer )
325+
{
326+
mSharedDS->releaseResultSet( mOgrLayer );
327+
mOgrLayer = nullptr;
328+
}
329+
mSharedDS.reset();
330+
mClosed = true;
331+
return true;
332+
}
333+
302334
if ( !mConn )
303335
return false;
304336

@@ -444,7 +476,12 @@ QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider *p )
444476
, mDriverName( p->mGDALDriverName )
445477
, mCrs( p->crs() )
446478
, mWkbType( p->wkbType() )
479+
, mSharedDS( nullptr )
447480
{
481+
if ( p->mTransaction )
482+
{
483+
mSharedDS = p->mTransaction->sharedDS();
484+
}
448485
for ( int i = ( p->mFirstFieldIsFid ) ? 1 : 0; i < mFields.size(); i++ )
449486
mFieldsWithoutFid.append( mFields.at( i ) );
450487
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( mDataSource ) );

src/providers/ogr/qgsogrfeatureiterator.h

+6
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121

2222
#include <ogr_api.h>
2323

24+
#include <memory>
25+
2426
class QgsOgrFeatureIterator;
2527
class QgsOgrProvider;
28+
class QgsOgrDataset;
29+
using QgsOgrDatasetSharedPtr = std::shared_ptr< QgsOgrDataset>;
2630

2731
class QgsOgrFeatureSource : public QgsAbstractFeatureSource
2832
{
@@ -45,6 +49,7 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
4549
QString mDriverName;
4650
QgsCoordinateReferenceSystem mCrs;
4751
QgsWkbTypes::Type mWkbType = QgsWkbTypes::Unknown;
52+
QgsOgrDatasetSharedPtr mSharedDS = nullptr;
4853

4954
friend class QgsOgrFeatureIterator;
5055
friend class QgsOgrExpressionCompiler;
@@ -87,6 +92,7 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgr
8792

8893
QgsRectangle mFilterRect;
8994
QgsCoordinateTransform mTransform;
95+
QgsOgrDatasetSharedPtr mSharedDS = nullptr;
9096

9197
bool fetchFeatureWithId( QgsFeatureId id, QgsFeature &feature ) const;
9298
};

0 commit comments

Comments
 (0)