QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
21 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsexpression.h"
24 #include "qgsmessagelog.h"
25 #include <QUuid>
26 
27 QgsTransaction *QgsTransaction::create( const QString &connString, const QString &providerKey )
28 {
29  return QgsProviderRegistry::instance()->createTransaction( providerKey, connString );
30 }
31 
32 QgsTransaction *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 
57 QgsTransaction::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
76 QString 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 
100 QString 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 
114 bool 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 
149 bool 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 
164 bool 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 
178 bool 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
198  if ( !layer->dataProvider() || ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::TransactionSupport ) == 0 )
199  return false;
200 
201  return true;
202 }
203 
204 void QgsTransaction::onLayerDeleted()
205 {
206  mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
207 }
208 
209 void 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 
221 QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
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 
235 QString 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 
251 bool 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.
265  mLastSavePointIsDirty = true;
266  return executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error );
267 }
268 
270 {
271  mLastSavePointIsDirty = true;
272 }
QgsTransaction::mLastSavePointIsDirty
bool mLastSavePointIsDirty
Definition: qgstransaction.h:193
QgsDataSourceUri
Class for storing the component parts of a RDBMS data source URI (e.g. a Postgres data source).
Definition: qgsdatasourceuri.h:37
QgsTransaction::rollback
bool rollback(QString &errorMsg)
Roll back transaction.
Definition: qgstransaction.cpp:178
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:676
QgsVectorDataProvider::TransactionSupport
@ TransactionSupport
Supports transactions.
Definition: qgsvectordataprovider.h:87
QgsTransaction::mSavepoints
QStack< QString > mSavepoints
Definition: qgstransaction.h:192
qgsexpression.h
SIP_OUT
#define SIP_OUT
Definition: qgis_sip.h:58
QgsProviderRegistry::createTransaction
QgsTransaction * createTransaction(const QString &providerKey, const QString &connString)
Returns new instance of transaction.
Definition: qgsproviderregistry.cpp:733
QgsTransaction::executeSql
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
QgsVectorDataProvider::transaction
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
Definition: qgsvectordataprovider.cpp:653
QgsTransaction::begin
bool begin(QString &errorMsg, int statementTimeout=20)
Begin transaction The statementTimeout (in seconds) specifies how long an sql statement is allowed to...
Definition: qgstransaction.cpp:149
QgsTransaction
This class allows including a set of layers in a database-side transaction, provided the layer data p...
Definition: qgstransaction.h:56
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsDataProvider::dataChanged
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3728
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1864
QgsTransaction::mTransactionActive
bool mTransactionActive
Definition: qgstransaction.h:191
QgsVectorDataProvider::capabilities
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Definition: qgsvectordataprovider.cpp:208
qgsproviderregistry.h
QgsTransaction::rollbackToSavepoint
virtual bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
Definition: qgstransaction.cpp:251
qgsdatasourceuri.h
QgsTransaction::supportsTransaction
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
Definition: qgstransaction.cpp:195
QgsTransaction::~QgsTransaction
~QgsTransaction() override
Definition: qgstransaction.cpp:64
QgsTransaction::commit
bool commit(QString &errorMsg)
Commit transaction.
Definition: qgstransaction.cpp:164
qgsvectordataprovider.h
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:169
QgsTransaction::addLayer
bool addLayer(QgsVectorLayer *layer, bool addLayersInEditMode=false)
Add the layer to the transaction.
Definition: qgstransaction.cpp:114
qgsvectorlayer.h
qgstransaction.h
QgsMapLayer::source
QString source() const
Returns the source for the layer.
Definition: qgsmaplayer.cpp:300
QgsTransaction::connectionString
QString connectionString() const
Returns the connection string of the transaction.
Definition: qgstransaction.cpp:69
str
#define str(x)
Definition: qgis.cpp:37
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsTransaction::create
static QgsTransaction * create(const QString &connString, const QString &providerKey)
Create a transaction for the specified connection string connString and provider with providerKey.
Definition: qgstransaction.cpp:27
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:68
QgsTransaction::createSavepoint
QString createSavepoint(QString &error)
creates a save point returns empty string on error returns the last created savepoint if it's not dir...
Definition: qgstransaction.cpp:221
qgslogger.h
QgsTransaction::QgsTransaction
QgsTransaction(const QString &connString)
Definition: qgstransaction.cpp:57
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
QgsTransaction::dirtyLastSavePoint
void dirtyLastSavePoint()
dirty save point such that next call to createSavepoint will create a new one
Definition: qgstransaction.cpp:269
QgsDataSourceUri::connectionInfo
QString connectionInfo(bool expandAuthConfig=true) const
Returns the connection part of the URI.
Definition: qgsdatasourceuri.cpp:478
QgsTransaction::afterRollback
void afterRollback()
Emitted after a rollback.
qgsmessagelog.h
QgsTransaction::mConnString
QString mConnString
Definition: qgstransaction.h:190