QGIS API Documentation  3.17.0-Master (8af46bc54f)
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  QString connStr = connectionString( firstLayer->source() );
40  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 ) )
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 
69 // For the needs of the OGR provider with GeoPackage datasources, remove
70 // any reference to layers in the connection string
71 QString QgsTransaction::removeLayerIdOrName( const QString &str )
72 {
73  QString res( str );
74 
75  for ( int i = 0; i < 2; i++ )
76  {
77  int pos = res.indexOf( i == 0 ? QLatin1String( "|layername=" ) : QLatin1String( "|layerid=" ) );
78  if ( pos >= 0 )
79  {
80  int end = res.indexOf( '|', pos + 1 );
81  if ( end >= 0 )
82  {
83  res = res.mid( 0, pos ) + res.mid( end );
84  }
85  else
86  {
87  res = res.mid( 0, pos );
88  }
89  }
90  }
91  return res;
92 }
93 
95 QString QgsTransaction::connectionString( const QString &layerName )
96 {
97  QString connString = QgsDataSourceUri( layerName ).connectionInfo( false );
98  // In the case of a OGR datasource, connectionInfo() will return an empty
99  // string. In that case, use the layer->source() itself, and strip any
100  // reference to layers from it.
101  if ( connString.isEmpty() )
102  {
103  connString = removeLayerIdOrName( layerName );
104  }
105  return connString;
106 }
108 
110 {
111  if ( !layer )
112  return false;
113 
114  if ( layer->isEditable() )
115  return false;
116 
117  //test if provider supports transactions
118  if ( !supportsTransaction( layer ) )
119  return false;
120 
121  if ( layer->dataProvider()->transaction() )
122  return false;
123 
124  //connection string not compatible
125 
126  if ( connectionString( layer->source() ) != mConnString )
127  {
128  QgsDebugMsg( QStringLiteral( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
129  layer->id(), connectionString( layer->source() ), mConnString ) );
130  return false;
131  }
132 
134  connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransaction::onLayerDeleted );
135  mLayers.insert( layer );
136 
137  if ( mTransactionActive )
138  layer->dataProvider()->setTransaction( this );
139 
140  return true;
141 }
142 
143 bool QgsTransaction::begin( QString &errorMsg, int statementTimeout )
144 {
145  if ( mTransactionActive )
146  return false;
147 
148  //Set all layers to direct edit mode
149  if ( !beginTransaction( errorMsg, statementTimeout ) )
150  return false;
151 
152  setLayerTransactionIds( this );
153  mTransactionActive = true;
154  mSavepoints.clear();
155  return true;
156 }
157 
158 bool QgsTransaction::commit( QString &errorMsg )
159 {
160  if ( !mTransactionActive )
161  return false;
162 
163  if ( !commitTransaction( errorMsg ) )
164  return false;
165 
166  setLayerTransactionIds( nullptr );
167  mTransactionActive = false;
168  mSavepoints.clear();
169  return true;
170 }
171 
172 bool QgsTransaction::rollback( QString &errorMsg )
173 {
174  if ( !mTransactionActive )
175  return false;
176 
177  if ( !rollbackTransaction( errorMsg ) )
178  return false;
179 
180  setLayerTransactionIds( nullptr );
181  mTransactionActive = false;
182  mSavepoints.clear();
183 
184  emit afterRollback();
185 
186  return true;
187 }
188 
190 {
191  //test if provider supports transactions
192  if ( !layer->dataProvider() || ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::TransactionSupport ) == 0 )
193  return false;
194 
195  return true;
196 }
197 
198 void QgsTransaction::onLayerDeleted()
199 {
200  mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
201 }
202 
203 void QgsTransaction::setLayerTransactionIds( QgsTransaction *transaction )
204 {
205  const auto constMLayers = mLayers;
206  for ( QgsVectorLayer *vl : constMLayers )
207  {
208  if ( vl->dataProvider() )
209  {
210  vl->dataProvider()->setTransaction( transaction );
211  }
212  }
213 }
214 
215 QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
216 {
217  if ( !mTransactionActive )
218  return QString();
219 
220  if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
221  return mSavepoints.top();
222 
223  const QString name( QStringLiteral( "qgis" ) + ( QUuid::createUuid().toString().mid( 1, 24 ).replace( '-', QString() ) ) );
224 
225  if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error ) )
226  {
227  QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
228  return QString();
229  }
230 
231  mSavepoints.push( name );
232  mLastSavePointIsDirty = false;
233  return name;
234 }
235 
236 QString QgsTransaction::createSavepoint( const QString &savePointId, QString &error SIP_OUT )
237 {
238  if ( !mTransactionActive )
239  return QString();
240 
241  if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( savePointId ) ), error ) )
242  {
243  QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
244  return QString();
245  }
246 
247  mSavepoints.push( savePointId );
248  mLastSavePointIsDirty = false;
249  return savePointId;
250 }
251 
252 bool QgsTransaction::rollbackToSavepoint( const QString &name, QString &error SIP_OUT )
253 {
254  if ( !mTransactionActive )
255  return false;
256 
257  const int idx = mSavepoints.indexOf( name );
258 
259  if ( idx == -1 )
260  return false;
261 
262  mSavepoints.resize( idx );
263  mLastSavePointIsDirty = false;
264  return executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error );
265 }
266 
268 {
269  mLastSavePointIsDirty = true;
270 }
static QgsTransaction * create(const QString &connString, const QString &providerKey)
Create a transaction for the specified connection string connString and provider with providerKey...
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString providerType() const
Returns the provider type (provider key) for this layer.
bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
QString connectionInfo(bool expandAuthConfig=true) const
Returns the connection part of the URI.
bool commit(QString &errorMsg)
Commit transaction.
virtual bool executeSql(const QString &sql, QString &error, bool isDirty=false, const QString &name=QString())=0
Execute the sql string.
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider&#39;...
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QgsTransaction * createTransaction(const QString &providerKey, const QString &connString)
Returns new instance of transaction.
~QgsTransaction() override
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
virtual QgsTransaction * transaction() const
Returns the transaction this data provider is included in, if any.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
void afterRollback()
Emitted after a rollback.
QgsTransaction(const QString &connString)
QString source() const
Returns the source for the layer.
This class allows including a set of layers in a database-side transaction, provided the layer data p...
#define SIP_OUT
Definition: qgis_sip.h:58
bool addLayer(QgsVectorLayer *layer)
Add the layer to the transaction.
bool rollback(QString &errorMsg)
Roll back transaction.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
Class for storing the component parts of a RDBMS data source URI (e.g.
QString createSavepoint(QString &error)
creates a save point returns empty string on error returns the last created savepoint if it&#39;s not dir...
Represents a vector layer which manages a vector based data sets.
void dirtyLastSavePoint()
dirty save point such that next call to createSavepoint will create a new one
bool begin(QString &errorMsg, int statementTimeout=20)
Begin transaction The statementTimeout (in seconds) specifies how long an sql statement is allowed to...