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