Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #7245 from rouault/gpkg_transactions
[OGR provider] [FEATURE] Add support for transactions on GPKG databases
  • Loading branch information
m-kuhn committed Jun 22, 2018
2 parents 1503459 + 6a987f9 commit 638fc0c
Show file tree
Hide file tree
Showing 12 changed files with 562 additions and 57 deletions.
1 change: 1 addition & 0 deletions python/core/auto_generated/qgstransaction.sip.in
Expand Up @@ -146,6 +146,7 @@ returns the last created savepoint
.. versionadded:: 3.0
%End


signals:

void afterRollback();
Expand Down
55 changes: 48 additions & 7 deletions src/core/qgstransaction.cpp
Expand Up @@ -51,7 +51,7 @@ QgsTransaction *QgsTransaction::create( const QSet<QgsVectorLayer *> &layers )

QgsVectorLayer *firstLayer = *layers.constBegin();

QString connStr = QgsDataSourceUri( firstLayer->source() ).connectionInfo( false );
QString connStr = connectionString( firstLayer->source() );
QString providerKey = firstLayer->dataProvider()->name();
std::unique_ptr<QgsTransaction> transaction( QgsTransaction::create( connStr, providerKey ) );
if ( transaction )
Expand Down Expand Up @@ -81,6 +81,46 @@ QgsTransaction::~QgsTransaction()
setLayerTransactionIds( nullptr );
}

// For the needs of the OGR provider with GeoPackage datasources, remove
// any reference to layers in the connection string
QString QgsTransaction::removeLayerIdOrName( const QString &str )
{
QString res( str );

for ( int i = 0; i < 2; i++ )
{
int pos = res.indexOf( i == 0 ? QLatin1String( "|layername=" ) : QLatin1String( "|layerid=" ) );
if ( pos >= 0 )
{
int end = res.indexOf( '|', pos + 1 );
if ( end >= 0 )
{
res = res.mid( 0, pos ) + res.mid( end );
}
else
{
res = res.mid( 0, pos );
}
}
}
return res;
}

///@cond PRIVATE
QString QgsTransaction::connectionString( const QString &layerName )
{
QString connString = QgsDataSourceUri( layerName ).connectionInfo( false );
// In the case of a OGR datasource, connectionInfo() will return an empty
// string. In that case, use the layer->source() itself, and strip any
// reference to layers from it.
if ( connString.isEmpty() )
{
connString = removeLayerIdOrName( layerName );
}
return connString;
}
///@endcond

bool QgsTransaction::addLayer( QgsVectorLayer *layer )
{
if ( !layer )
Expand All @@ -90,17 +130,18 @@ bool QgsTransaction::addLayer( QgsVectorLayer *layer )
return false;

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

if ( layer->dataProvider()->transaction() )
return false;

//connection string not compatible
if ( QgsDataSourceUri( layer->source() ).connectionInfo( false ) != mConnString )

if ( connectionString( layer->source() ) != mConnString )
{
QgsDebugMsg( QString( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
layer->id(), QgsDataSourceUri( layer->source() ).connectionInfo( false ), mConnString ) );
layer->id(), connectionString( layer->source() ), mConnString ) );
return false;
}

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

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

return lib->resolve( "createTransaction" );
return true;
}

void QgsTransaction::onLayerDeleted()
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgstransaction.h
Expand Up @@ -158,6 +158,11 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
*/
bool lastSavePointIsDirty() const { return mLastSavePointIsDirty; }

///@cond PRIVATE
// For internal use only, or by QgsTransactionGroup
static QString connectionString( const QString &layerName ) SIP_SKIP;
///@endcond

signals:

/**
Expand Down Expand Up @@ -188,6 +193,8 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT

void setLayerTransactionIds( QgsTransaction *transaction );

static QString removeLayerIdOrName( const QString &str );

virtual bool beginTransaction( QString &error, int statementTimeout ) = 0;
virtual bool commitTransaction( QString &error ) = 0;
virtual bool rollbackTransaction( QString &error ) = 0;
Expand Down
5 changes: 3 additions & 2 deletions src/core/qgstransactiongroup.cpp
Expand Up @@ -33,8 +33,7 @@ bool QgsTransactionGroup::addLayer( QgsVectorLayer *layer )
if ( !QgsTransaction::supportsTransaction( layer ) )
return false;

QString connString = QgsDataSourceUri( layer->source() ).connectionInfo();

QString connString = QgsTransaction::connectionString( layer->source() );
if ( mConnString.isEmpty() )
{
mConnString = connString;
Expand Down Expand Up @@ -74,6 +73,8 @@ void QgsTransactionGroup::onEditingStarted()
return;

mTransaction.reset( QgsTransaction::create( mConnString, mProviderKey ) );
if ( !mTransaction )
return;

QString errorMsg;
mTransaction->begin( errorMsg );
Expand Down
2 changes: 2 additions & 0 deletions src/providers/ogr/CMakeLists.txt
Expand Up @@ -9,6 +9,7 @@ SET (OGR_SRCS
qgsgeopackagerasterwritertask.cpp
qgsogrdbconnection.cpp
qgsogrdbtablemodel.cpp
qgsogrtransaction.cpp
)

SET(OGR_MOC_HDRS
Expand All @@ -19,6 +20,7 @@ SET(OGR_MOC_HDRS
qgsgeopackagerasterwritertask.h
qgsogrdbconnection.h
qgsogrdbtablemodel.h
qgsogrtransaction.h
)

IF (WITH_GUI)
Expand Down
79 changes: 58 additions & 21 deletions src/providers/ogr/qgsogrfeatureiterator.cpp
Expand Up @@ -26,6 +26,7 @@
#include "qgssettings.h"
#include "qgsexception.h"
#include "qgswkbtypes.h"
#include "qgsogrtransaction.h"

#include <QTextCodec>
#include <QFile>
Expand All @@ -42,38 +43,51 @@ QgsOgrFeatureIterator::QgsOgrFeatureIterator( QgsOgrFeatureSource *source, bool
: QgsAbstractFeatureIteratorFromSource<QgsOgrFeatureSource>( source, ownSource, request )
, mFilterFids( mRequest.filterFids() )
, mFilterFidsIt( mFilterFids.constBegin() )
, mSharedDS( source->mSharedDS )
{
//QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection");
mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource ) );
if ( !mConn->ds )
if ( mSharedDS )
{
return;
}

if ( mSource->mLayerName.isNull() )
{
mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex );
mOgrLayer = mSharedDS->createSQLResultLayer( mSource->mEncoding, mSource->mLayerName, mSource->mLayerIndex );
if ( !mOgrLayer )
{
return;
}
}
else
{
mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() );
}
if ( !mOgrLayer )
{
return;
}
//QgsDebugMsg( "Feature iterator of " + mSource->mLayerName + ": acquiring connection");
mConn = QgsOgrConnPool::instance()->acquireConnection( QgsOgrProviderUtils::connectionPoolId( mSource->mDataSource ) );
if ( !mConn->ds )
{
return;
}

if ( !mSource->mSubsetString.isEmpty() )
{
mOgrOrigLayer = mOgrLayer;
mOgrLayerWithFid = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, QString(), true, &mOrigFidAdded );
mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString, true, &mOrigFidAdded );
if ( mSource->mLayerName.isNull() )
{
mOgrLayer = GDALDatasetGetLayer( mConn->ds, mSource->mLayerIndex );
}
else
{
mOgrLayer = GDALDatasetGetLayerByName( mConn->ds, mSource->mLayerName.toUtf8().constData() );
}
if ( !mOgrLayer )
{
close();
return;
}

if ( !mSource->mSubsetString.isEmpty() )
{
mOgrOrigLayer = mOgrLayer;
mOgrLayerWithFid = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, QString(), true, &mOrigFidAdded );
mOgrLayer = QgsOgrProviderUtils::setSubsetString( mOgrLayer, mConn->ds, mSource->mEncoding, mSource->mSubsetString, true, &mOrigFidAdded );
if ( !mOgrLayer )
{
close();
return;
}
}
}
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );

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

bool QgsOgrFeatureIterator::fetchFeature( QgsFeature &feature )
{
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );

feature.setValid( false );

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

bool QgsOgrFeatureIterator::rewind()
{
QMutexLocker locker( mSharedDS ? &mSharedDS->mutex() : nullptr );

if ( mClosed || !mOgrLayer )
return false;

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

bool QgsOgrFeatureIterator::close()
{
if ( mSharedDS )
{
iteratorClosed();

if ( mOgrLayer )
{
mSharedDS->releaseResultSet( mOgrLayer );
mOgrLayer = nullptr;
}
mSharedDS.reset();
mClosed = true;
return true;
}

if ( !mConn )
return false;

Expand Down Expand Up @@ -444,7 +476,12 @@ QgsOgrFeatureSource::QgsOgrFeatureSource( const QgsOgrProvider *p )
, mDriverName( p->mGDALDriverName )
, mCrs( p->crs() )
, mWkbType( p->wkbType() )
, mSharedDS( nullptr )
{
if ( p->mTransaction )
{
mSharedDS = p->mTransaction->sharedDS();
}
for ( int i = ( p->mFirstFieldIsFid ) ? 1 : 0; i < mFields.size(); i++ )
mFieldsWithoutFid.append( mFields.at( i ) );
QgsOgrConnPool::instance()->ref( QgsOgrProviderUtils::connectionPoolId( mDataSource ) );
Expand Down
6 changes: 6 additions & 0 deletions src/providers/ogr/qgsogrfeatureiterator.h
Expand Up @@ -21,8 +21,12 @@

#include <ogr_api.h>

#include <memory>

class QgsOgrFeatureIterator;
class QgsOgrProvider;
class QgsOgrDataset;
using QgsOgrDatasetSharedPtr = std::shared_ptr< QgsOgrDataset>;

class QgsOgrFeatureSource : public QgsAbstractFeatureSource
{
Expand All @@ -45,6 +49,7 @@ class QgsOgrFeatureSource : public QgsAbstractFeatureSource
QString mDriverName;
QgsCoordinateReferenceSystem mCrs;
QgsWkbTypes::Type mWkbType = QgsWkbTypes::Unknown;
QgsOgrDatasetSharedPtr mSharedDS = nullptr;

friend class QgsOgrFeatureIterator;
friend class QgsOgrExpressionCompiler;
Expand Down Expand Up @@ -87,6 +92,7 @@ class QgsOgrFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsOgr

QgsRectangle mFilterRect;
QgsCoordinateTransform mTransform;
QgsOgrDatasetSharedPtr mSharedDS = nullptr;

bool fetchFeatureWithId( QgsFeatureId id, QgsFeature &feature ) const;
};
Expand Down

0 comments on commit 638fc0c

Please sign in to comment.