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