QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgstransaction.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstransaction.cpp
3 ------------------
4 begin : May 5, 2014
5 copyright : (C) 2014 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#include "qgstransaction.h"
18#include "qgslogger.h"
19#include "qgsdatasourceuri.h"
20#include "qgsproviderregistry.h"
22#include "qgsvectorlayer.h"
23#include "qgsexpression.h"
24#include "qgsmessagelog.h"
25#include <QUuid>
26
27QgsTransaction *QgsTransaction::create( const QString &connString, const QString &providerKey )
28{
29 return QgsProviderRegistry::instance()->createTransaction( providerKey, connString );
30}
31
32QgsTransaction *QgsTransaction::create( const QSet<QgsVectorLayer *> &layers )
33{
34 if ( layers.isEmpty() )
35 return nullptr;
36
37 QgsVectorLayer *firstLayer = *layers.constBegin();
38
39 const QString connStr = connectionString( firstLayer->source() );
40 const QString providerKey = firstLayer->providerType();
41 std::unique_ptr<QgsTransaction> transaction( QgsTransaction::create( connStr, providerKey ) );
42 if ( transaction )
43 {
44 for ( QgsVectorLayer *layer : layers )
45 {
46 if ( !transaction->addLayer( layer, false ) )
47 {
48 transaction.reset();
49 break;
50 }
51 }
52 }
53 return transaction.release();
54}
55
56
57QgsTransaction::QgsTransaction( const QString &connString )
58 : mConnString( connString )
59 , mTransactionActive( false )
60 , mLastSavePointIsDirty( true )
61{
62}
63
65{
66 setLayerTransactionIds( nullptr );
67}
68
70{
71 return mConnString;
72}
73
74// For the needs of the OGR provider with GeoPackage datasources, remove
75// any reference to layers and filters in the connection string
76QString QgsTransaction::cleanupConnectionString( const QString &str )
77{
78 QString res( str );
79
80 static const QStringList toRemove
81 {
82 { QStringLiteral( "|layername=" )},
83 { QStringLiteral( "|layerid=" )},
84 { QStringLiteral( "|subset=" )},
85 };
86
87 for ( const auto &strToRm : std::as_const( toRemove ) )
88 {
89 const int pos = res.indexOf( strToRm );
90 if ( pos >= 0 )
91 {
92 const int end = res.indexOf( '|', pos + 1 );
93 if ( end >= 0 )
94 {
95 res = res.mid( 0, pos ) + res.mid( end );
96 }
97 else
98 {
99 res = res.mid( 0, pos );
100 }
101 }
102 }
103 return res;
104}
105
107QString QgsTransaction::connectionString( const QString &layerUri )
108{
109 QString connString = QgsDataSourceUri( layerUri ).connectionInfo( false );
110 // In the case of a OGR datasource, connectionInfo() will return an empty
111 // string. In that case, use the layer->source() itself, and strip any
112 // reference to layers from it.
113 if ( connString.isEmpty() )
114 {
115 connString = cleanupConnectionString( layerUri );
116 }
117 return connString;
118}
120
121bool QgsTransaction::addLayer( QgsVectorLayer *layer, bool addLayersInEditMode )
122{
123 if ( !layer )
124 return false;
125
126 if ( ! addLayersInEditMode
127 && layer->isEditable() )
128 return false;
129
130 //test if provider supports transactions
131 if ( !supportsTransaction( layer ) )
132 return false;
133
134 if ( layer->dataProvider()->transaction() )
135 return false;
136
137 //connection string not compatible
138
139 if ( connectionString( layer->source() ) != mConnString )
140 {
141 QgsDebugError( QStringLiteral( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
142 layer->id(), connectionString( layer->source() ), mConnString ) );
143 return false;
144 }
145
147 connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransaction::onLayerDeleted );
148 mLayers.insert( layer );
149
150 if ( mTransactionActive )
151 layer->dataProvider()->setTransaction( this );
152
153 return true;
154}
155
156bool QgsTransaction::begin( QString &errorMsg, int statementTimeout )
157{
158 if ( mTransactionActive )
159 return false;
160
161 //Set all layers to direct edit mode
162 if ( !beginTransaction( errorMsg, statementTimeout ) )
163 return false;
164
165 setLayerTransactionIds( this );
166 mTransactionActive = true;
167 mSavepoints.clear();
168 return true;
169}
170
171bool QgsTransaction::commit( QString &errorMsg )
172{
173 if ( !mTransactionActive )
174 return false;
175
176 if ( !commitTransaction( errorMsg ) )
177 return false;
178
179 setLayerTransactionIds( nullptr );
180 mTransactionActive = false;
181 mSavepoints.clear();
182 return true;
183}
184
185bool QgsTransaction::rollback( QString &errorMsg )
186{
187 if ( !mTransactionActive )
188 return false;
189
190 if ( !rollbackTransaction( errorMsg ) )
191 return false;
192
193 setLayerTransactionIds( nullptr );
194 mTransactionActive = false;
195 mSavepoints.clear();
196
197 emit afterRollback();
198
199 return true;
200}
201
203{
204 //test if provider supports transactions
206 return false;
207
208 return true;
209}
210
211void QgsTransaction::onLayerDeleted()
212{
213 mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
214}
215
216void QgsTransaction::setLayerTransactionIds( QgsTransaction *transaction )
217{
218 const auto constMLayers = mLayers;
219 for ( QgsVectorLayer *vl : constMLayers )
220 {
221 if ( vl->dataProvider() )
222 {
223 vl->dataProvider()->setTransaction( transaction );
224 }
225 }
226}
227
229{
230 if ( !mTransactionActive )
231 return QString();
232
233 if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
234 {
235 return mSavepoints.top();
236 }
237
238 const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) );
239 return createSavepoint( name, error );
240}
241
242QString QgsTransaction::createSavepoint( const QString &savePointId, QString &error SIP_OUT )
243{
244 if ( !mTransactionActive )
245 return QString();
246
247 if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( savePointId ) ), error ) )
248 {
249 QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
250 return QString();
251 }
252
253 mSavepoints.push( savePointId );
254 mLastSavePointIsDirty = false;
255 return savePointId;
256}
257
258bool QgsTransaction::rollbackToSavepoint( const QString &name, QString &error SIP_OUT )
259{
260 if ( !mTransactionActive )
261 return false;
262
263 const int idx = mSavepoints.indexOf( name );
264
265 if ( idx == -1 )
266 return false;
267
268 mSavepoints.resize( idx );
269 // Rolling back always dirties the previous savepoint because
270 // the status of the DB has changed between the previous savepoint and the
271 // one we are rolling back to.
273 return executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error );
274}
275
277{
279}
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
Class for storing the component parts of a RDBMS data source URI (e.g.
QString connectionInfo(bool expandAuthConfig=true) const
Returns the connection part of the URI.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QgsTransaction * createTransaction(const QString &providerKey, const QString &connString)
Returns new instance of transaction.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
QStack< QString > mSavepoints
bool mLastSavePointIsDirty
void afterRollback()
Emitted after a rollback.
bool rollback(QString &errorMsg)
Roll back transaction.
bool addLayer(QgsVectorLayer *layer, bool addLayersInEditMode=false)
Add the layer to the transaction.
bool begin(QString &errorMsg, int statementTimeout=20)
Begin transaction The statementTimeout (in seconds) specifies how long an sql statement is allowed to...
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
virtual bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
QgsTransaction(const QString &connString)
QString connectionString() const
Returns the connection string of the transaction.
bool commit(QString &errorMsg)
Commit transaction.
~QgsTransaction() override
static QgsTransaction * create(const QString &connString, const QString &providerKey)
Create a transaction for the specified connection string connString and provider with providerKey.
QString createSavepoint(QString &error)
creates a save point returns empty string on error returns the last created savepoint if it's not dir...
void dirtyLastSavePoint()
dirty save point such that next call to createSavepoint will create a new one
@ TransactionSupport
Supports transactions.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Represents a vector layer which manages a vector based data sets.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
#define str(x)
Definition: qgis.cpp:38
#define SIP_OUT
Definition: qgis_sip.h:58
#define QgsDebugError(str)
Definition: qgslogger.h:38