QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
19#include "qgsdatasourceuri.h"
20#include "qgsexpression.h"
21#include "qgslogger.h"
22#include "qgsmessagelog.h"
23#include "qgsproviderregistry.h"
25#include "qgsvectorlayer.h"
26
27#include <QUuid>
28
29#include "moc_qgstransaction.cpp"
30
31QgsTransaction *QgsTransaction::create( const QString &connString, const QString &providerKey )
32{
33 return QgsProviderRegistry::instance()->createTransaction( providerKey, connString );
34}
35
36QgsTransaction *QgsTransaction::create( const QSet<QgsVectorLayer *> &layers )
37{
38 if ( layers.isEmpty() )
39 return nullptr;
40
41 QgsVectorLayer *firstLayer = *layers.constBegin();
42
43 const QString connStr = connectionString( firstLayer->source() );
44 const QString providerKey = firstLayer->providerType();
45 std::unique_ptr<QgsTransaction> transaction( QgsTransaction::create( connStr, providerKey ) );
46 if ( transaction )
47 {
48 for ( QgsVectorLayer *layer : layers )
49 {
50 if ( !transaction->addLayer( layer, false ) )
51 {
52 transaction.reset();
53 break;
54 }
55 }
56 }
57 return transaction.release();
58}
59
60
61QgsTransaction::QgsTransaction( const QString &connString )
62 : mConnString( connString )
63{
64}
65
67{
68 setLayerTransactionIds( nullptr );
69}
70
72{
73 return mConnString;
74}
75
76// For the needs of the OGR provider with GeoPackage datasources, remove
77// any reference to layers and filters in the connection string
78QString QgsTransaction::cleanupConnectionString( const QString &str )
79{
80 QString res( str );
81
82 static const QStringList toRemove
83 {
84 { QStringLiteral( "|layername=" )},
85 { QStringLiteral( "|layerid=" )},
86 { QStringLiteral( "|subset=" )},
87 };
88
89 for ( const auto &strToRm : std::as_const( toRemove ) )
90 {
91 const int pos = res.indexOf( strToRm );
92 if ( pos >= 0 )
93 {
94 const int end = res.indexOf( '|', pos + 1 );
95 if ( end >= 0 )
96 {
97 res = res.mid( 0, pos ) + res.mid( end );
98 }
99 else
100 {
101 res = res.mid( 0, pos );
102 }
103 }
104 }
105 return res;
106}
107
109QString QgsTransaction::connectionString( const QString &layerUri )
110{
111 QString connString = QgsDataSourceUri( layerUri ).connectionInfo( false );
112 // In the case of a OGR datasource, connectionInfo() will return an empty
113 // string. In that case, use the layer->source() itself, and strip any
114 // reference to layers from it.
115 if ( connString.isEmpty() )
116 {
117 connString = cleanupConnectionString( layerUri );
118 }
119 return connString;
120}
122
123bool QgsTransaction::addLayer( QgsVectorLayer *layer, bool addLayersInEditMode )
124{
125 if ( !layer )
126 return false;
127
128 if ( ! addLayersInEditMode
129 && layer->isEditable() )
130 return false;
131
132 //test if provider supports transactions
133 if ( !supportsTransaction( layer ) )
134 return false;
135
136 if ( layer->dataProvider()->transaction() )
137 return false;
138
139 //connection string not compatible
140
141 if ( connectionString( layer->source() ) != mConnString )
142 {
143 QgsDebugError( QStringLiteral( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
144 layer->id(), connectionString( layer->source() ), mConnString ) );
145 return false;
146 }
147
149 connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransaction::onLayerDeleted );
150 mLayers.insert( layer );
151
152 if ( mTransactionActive )
153 layer->dataProvider()->setTransaction( this );
154
155 return true;
156}
157
158bool QgsTransaction::begin( QString &errorMsg, int statementTimeout )
159{
160 if ( mTransactionActive )
161 return false;
162
163 //Set all layers to direct edit mode
164 if ( !beginTransaction( errorMsg, statementTimeout ) )
165 return false;
166
167 setLayerTransactionIds( this );
168 mTransactionActive = true;
169 mSavepoints.clear();
170 return true;
171}
172
173bool QgsTransaction::commit( QString &errorMsg )
174{
175 if ( !mTransactionActive )
176 return false;
177
178 if ( !commitTransaction( errorMsg ) )
179 return false;
180
181 setLayerTransactionIds( nullptr );
182 mTransactionActive = false;
183 mSavepoints.clear();
184 return true;
185}
186
187bool QgsTransaction::rollback( QString &errorMsg )
188{
189 if ( !mTransactionActive )
190 return false;
191
192 if ( !rollbackTransaction( errorMsg ) )
193 return false;
194
195 setLayerTransactionIds( nullptr );
196 mTransactionActive = false;
197 mSavepoints.clear();
198
199 emit afterRollback();
200
201 return true;
202}
203
205{
206 //test if provider supports transactions
208 return false;
209
210 return true;
211}
212
213void QgsTransaction::onLayerDeleted()
214{
215 mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
216}
217
218void QgsTransaction::setLayerTransactionIds( QgsTransaction *transaction )
219{
220 const auto constMLayers = mLayers;
221 for ( QgsVectorLayer *vl : constMLayers )
222 {
223 if ( vl->dataProvider() )
224 {
225 vl->dataProvider()->setTransaction( transaction );
226 }
227 }
228}
229
231{
232 if ( !mTransactionActive )
233 return QString();
234
235 if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
236 {
237 return mSavepoints.top();
238 }
239
240 const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) );
241 return createSavepoint( name, error );
242}
243
244QString QgsTransaction::createSavepoint( const QString &savePointId, QString &error SIP_OUT )
245{
246 if ( !mTransactionActive )
247 return QString();
248
249 if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( savePointId ) ), error ) )
250 {
251 QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
252 return QString();
253 }
254
255 mSavepoints.push( savePointId );
256 mLastSavePointIsDirty = false;
257 return savePointId;
258}
259
260bool QgsTransaction::rollbackToSavepoint( const QString &name, QString &error SIP_OUT )
261{
262 if ( !mTransactionActive )
263 return false;
264
265 const int idx = mSavepoints.indexOf( name );
266
267 if ( idx == -1 )
268 return false;
269
270 mSavepoints.resize( idx );
271 // Rolling back always dirties the previous savepoint because
272 // the status of the DB has changed between the previous savepoint and the
273 // one we are rolling back to.
275 if ( ! executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error ) )
276 {
277 return false;
278 }
279 emit afterRollbackToSavepoint( name );
280 return true;
281}
282
@ TransactionSupport
Supports transactions.
Definition qgis.h:513
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
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
Definition qgsmaplayer.h:83
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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.
Allows creation of a multi-layer database-side transaction.
QStack< QString > mSavepoints
void afterRollbackToSavepoint(const QString &savepointName)
Emitted after a rollback to savepoint.
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
virtual Q_INVOKABLE Qgis::VectorProviderCapabilities capabilities() const
Returns flags containing the supported capabilities.
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Represents a vector layer which manages a vector based dataset.
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 SIP_OUT
Definition qgis_sip.h:58
#define QgsDebugError(str)
Definition qgslogger.h:57