QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
18 #include <QLibrary>
19 
20 #include "qgstransaction.h"
21 #include "qgslogger.h"
22 #include "qgsdatasourceuri.h"
23 #include "qgsproviderregistry.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsexpression.h"
27 #include "qgsmessagelog.h"
28 #include <QUuid>
29 
30 typedef QgsTransaction *createTransaction_t( const QString &connString );
31 
32 QgsTransaction *QgsTransaction::create( const QString &connString, const QString &providerKey )
33 {
34  std::unique_ptr< QLibrary > lib( QgsProviderRegistry::instance()->createProviderLibrary( providerKey ) );
35  if ( !lib )
36  return nullptr;
37 
38  createTransaction_t *createTransaction = reinterpret_cast< createTransaction_t * >( cast_to_fptr( lib->resolve( "createTransaction" ) ) );
39  if ( !createTransaction )
40  return nullptr;
41 
42  QgsTransaction *ts = createTransaction( connString );
43 
44  return ts;
45 }
46 
47 QgsTransaction *QgsTransaction::create( const QSet<QgsVectorLayer *> &layers )
48 {
49  if ( layers.isEmpty() )
50  return nullptr;
51 
52  QgsVectorLayer *firstLayer = *layers.constBegin();
53 
54  QString connStr = QgsDataSourceUri( firstLayer->source() ).connectionInfo( false );
55  QString providerKey = firstLayer->dataProvider()->name();
56  std::unique_ptr<QgsTransaction> transaction( QgsTransaction::create( connStr, providerKey ) );
57  if ( transaction )
58  {
59  for ( QgsVectorLayer *layer : layers )
60  {
61  if ( !transaction->addLayer( layer ) )
62  {
63  transaction.reset();
64  break;
65  }
66  }
67  }
68  return transaction.release();
69 }
70 
71 
72 QgsTransaction::QgsTransaction( const QString &connString )
73  : mConnString( connString )
74  , mTransactionActive( false )
75  , mLastSavePointIsDirty( true )
76 {
77 }
78 
80 {
81  setLayerTransactionIds( nullptr );
82 }
83 
85 {
86  if ( !layer )
87  return false;
88 
89  if ( layer->isEditable() )
90  return false;
91 
92  //test if provider supports transactions
93  if ( !layer->dataProvider() || ( layer->dataProvider()->capabilities() & QgsVectorDataProvider::TransactionSupport ) == 0 )
94  return false;
95 
96  if ( layer->dataProvider()->transaction() )
97  return false;
98 
99  //connection string not compatible
100  if ( QgsDataSourceUri( layer->source() ).connectionInfo( false ) != mConnString )
101  {
102  QgsDebugMsg( QString( "Couldn't start transaction because connection string for layer %1 : '%2' does not match '%3'" ).arg(
103  layer->id(), QgsDataSourceUri( layer->source() ).connectionInfo( false ), mConnString ) );
104  return false;
105  }
106 
108  connect( layer, &QgsVectorLayer::destroyed, this, &QgsTransaction::onLayerDeleted );
109  mLayers.insert( layer );
110 
111  if ( mTransactionActive )
112  layer->dataProvider()->setTransaction( this );
113 
114  return true;
115 }
116 
117 bool QgsTransaction::begin( QString &errorMsg, int statementTimeout )
118 {
119  if ( mTransactionActive )
120  return false;
121 
122  //Set all layers to direct edit mode
123  if ( !beginTransaction( errorMsg, statementTimeout ) )
124  return false;
125 
126  setLayerTransactionIds( this );
127  mTransactionActive = true;
128  mSavepoints.clear();
129  return true;
130 }
131 
132 bool QgsTransaction::commit( QString &errorMsg )
133 {
134  if ( !mTransactionActive )
135  return false;
136 
137  if ( !commitTransaction( errorMsg ) )
138  return false;
139 
140  setLayerTransactionIds( nullptr );
141  mTransactionActive = false;
142  mSavepoints.clear();
143  return true;
144 }
145 
146 bool QgsTransaction::rollback( QString &errorMsg )
147 {
148  if ( !mTransactionActive )
149  return false;
150 
151  if ( !rollbackTransaction( errorMsg ) )
152  return false;
153 
154  setLayerTransactionIds( nullptr );
155  mTransactionActive = false;
156  mSavepoints.clear();
157 
158  emit afterRollback();
159 
160  return true;
161 }
162 
164 {
165  std::unique_ptr< QLibrary > lib( QgsProviderRegistry::instance()->createProviderLibrary( layer->providerType() ) );
166  if ( !lib )
167  return false;
168 
169  return lib->resolve( "createTransaction" );
170 }
171 
172 void QgsTransaction::onLayerDeleted()
173 {
174  mLayers.remove( static_cast<QgsVectorLayer *>( sender() ) );
175 }
176 
177 void QgsTransaction::setLayerTransactionIds( QgsTransaction *transaction )
178 {
179  Q_FOREACH ( QgsVectorLayer *vl, mLayers )
180  {
181  if ( vl->dataProvider() )
182  {
183  vl->dataProvider()->setTransaction( transaction );
184  }
185  }
186 }
187 
188 QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
189 {
190  if ( !mTransactionActive )
191  return QString();
192 
193  if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
194  return mSavepoints.top();
195 
196  const QString name( QUuid::createUuid().toString() );
197 
198  if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error ) )
199  {
200  QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
201  return QString();
202  }
203 
204  mSavepoints.push( name );
205  mLastSavePointIsDirty = false;
206  return name;
207 }
208 
209 QString QgsTransaction::createSavepoint( const QString &savePointId, QString &error SIP_OUT )
210 {
211  if ( !mTransactionActive )
212  return QString();
213 
214  if ( !executeSql( QStringLiteral( "SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( savePointId ) ), error ) )
215  {
216  QgsMessageLog::logMessage( tr( "Could not create savepoint (%1)" ).arg( error ) );
217  return QString();
218  }
219 
220  mSavepoints.push( savePointId );
221  mLastSavePointIsDirty = false;
222  return savePointId;
223 }
224 
225 bool QgsTransaction::rollbackToSavepoint( const QString &name, QString &error SIP_OUT )
226 {
227  if ( !mTransactionActive )
228  return false;
229 
230  const int idx = mSavepoints.indexOf( name );
231 
232  if ( idx == -1 )
233  return false;
234 
235  mSavepoints.resize( idx );
236  mLastSavePointIsDirty = false;
237  return executeSql( QStringLiteral( "ROLLBACK TO SAVEPOINT %1" ).arg( QgsExpression::quotedColumnRef( name ) ), error );
238 }
239 
241 {
242  mLastSavePointIsDirty = true;
243 }
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
QgsTransaction * createTransaction_t(const QString &connString)
bool rollbackToSavepoint(const QString &name, QString &error)
rollback to save point, the save point is maintained and is "undertied"
bool isEditable() const override
Returns true if the provider is in editing mode.
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.
virtual QString name() const =0
Returns a provider name.
void dataChanged()
This is emitted whenever an asynchronous operation has finished and the data should be redrawn...
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() 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.
#define cast_to_fptr(f)
Definition: qgis.h:170
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:51
bool addLayer(QgsVectorLayer *layer)
Add the layer to the transaction.
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
bool rollback(QString &errorMsg)
Roll back transaction.
QString providerType() const
Returns the provider type for this layer.
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
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...