QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 in the connection string
76QString QgsTransaction::removeLayerIdOrName( const QString &str )
77{
78 QString res( str );
79
80 for ( int i = 0; i < 2; i++ )
81 {
82 const int pos = res.indexOf( i == 0 ? QLatin1String( "|layername=" ) : QLatin1String( "|layerid=" ) );
83 if ( pos >= 0 )
84 {
85 const int end = res.indexOf( '|', pos + 1 );
86 if ( end >= 0 )
87 {
88 res = res.mid( 0, pos ) + res.mid( end );
89 }
90 else
91 {
92 res = res.mid( 0, pos );
93 }
94 }
95 }
96 return res;
97}
98
100QString QgsTransaction::connectionString( const QString &layerUri )
101{
102 QString connString = QgsDataSourceUri( layerUri ).connectionInfo( false );
103 // In the case of a OGR datasource, connectionInfo() will return an empty
104 // string. In that case, use the layer->source() itself, and strip any
105 // reference to layers from it.
106 if ( connString.isEmpty() )
107 {
108 connString = removeLayerIdOrName( layerUri );
109 }
110 return connString;
111}
113
114bool QgsTransaction::addLayer( QgsVectorLayer *layer, bool addLayersInEditMode )
115{
116 if ( !layer )
117 return false;
118
119 if ( ! addLayersInEditMode
120 && layer->isEditable() )
121 return false;
122
123 //test if provider supports transactions
124 if ( !supportsTransaction( layer ) )
125 return false;
126
127 if ( layer->dataProvider()->transaction() )
128 return false;
129
130 //connection string not compatible
131
132 if ( connectionString( layer->source() ) != mConnString )
133 {
134 QgsDebugMsg( QStringLiteral( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
135 layer->id(), connectionString( layer->source() ), mConnString ) );
136 return false;
137 }
138
140 connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransaction::onLayerDeleted );
141 mLayers.insert( layer );
142
143 if ( mTransactionActive )
144 layer->dataProvider()->setTransaction( this );
145
146 return true;
147}
148
149bool QgsTransaction::begin( QString &errorMsg, int statementTimeout )
150{
151 if ( mTransactionActive )
152 return false;
153
154 //Set all layers to direct edit mode
155 if ( !beginTransaction( errorMsg, statementTimeout ) )
156 return false;
157
158 setLayerTransactionIds( this );
159 mTransactionActive = true;
160 mSavepoints.clear();
161 return true;
162}
163
164bool QgsTransaction::commit( QString &errorMsg )
165{
166 if ( !mTransactionActive )
167 return false;
168
169 if ( !commitTransaction( errorMsg ) )
170 return false;
171
172 setLayerTransactionIds( nullptr );
173 mTransactionActive = false;
174 mSavepoints.clear();
175 return true;
176}
177
178bool QgsTransaction::rollback( QString &errorMsg )
179{
180 if ( !mTransactionActive )
181 return false;
182
183 if ( !rollbackTransaction( errorMsg ) )
184 return false;
185
186 setLayerTransactionIds( nullptr );
187 mTransactionActive = false;
188 mSavepoints.clear();
189
190 emit afterRollback();
191
192 return true;
193}
194
196{
197 //test if provider supports transactions
199 return false;
200
201 return true;
202}
203
204void QgsTransaction::onLayerDeleted()
205{
206 mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
207}
208
209void QgsTransaction::setLayerTransactionIds( QgsTransaction *transaction )
210{
211 const auto constMLayers = mLayers;
212 for ( QgsVectorLayer *vl : constMLayers )
213 {
214 if ( vl->dataProvider() )
215 {
216 vl->dataProvider()->setTransaction( transaction );
217 }
218 }
219}
220
222{
223 if ( !mTransactionActive )
224 return QString();
225
226 if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
227 {
228 return mSavepoints.top();
229 }
230
231 const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) );
232 return createSavepoint( name, error );
233}
234
235QString QgsTransaction::createSavepoint( const QString &savePointId, QString &error SIP_OUT )
236{
237 if ( !mTransactionActive )
238 return QString();
239
240 if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( savePointId ) ), error ) )
241 {
242 QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
243 return QString();
244 }
245
246 mSavepoints.push( savePointId );
247 mLastSavePointIsDirty = false;
248 return savePointId;
249}
250
251bool QgsTransaction::rollbackToSavepoint( const QString &name, QString &error SIP_OUT )
252{
253 if ( !mTransactionActive )
254 return false;
255
256 const int idx = mSavepoints.indexOf( name );
257
258 if ( idx == -1 )
259 return false;
260
261 mSavepoints.resize( idx );
262 // Rolling back always dirties the previous savepoint because
263 // the status of the DB has changed between the previous savepoint and the
264 // one we are rolling back to.
266 return executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error );
267}
268
270{
272}
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:37
#define SIP_OUT
Definition: qgis_sip.h:58
#define QgsDebugMsg(str)
Definition: qgslogger.h:38