Skip to content

Commit 32ff78b

Browse files
committedOct 25, 2017
[FEATURE] Add a flag to dirty edit buffer when using executeSql in transactions
1 parent 6297d19 commit 32ff78b

14 files changed

+213
-16
lines changed
 

‎python/core/qgstransaction.sip

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,16 @@ class QgsTransaction : QObject /Abstract/
9797
:rtype: bool
9898
%End
9999

100-
virtual bool executeSql( const QString &sql, QString &error /Out/ ) = 0;
100+
virtual bool executeSql( const QString &sql, QString &error /Out/, bool isDirty = false ) = 0;
101101
%Docstring
102102
Execute the ``sql`` string. The result must not be a tuple, so running a
103103
``SELECT`` query will return an error.
104+
105+
\param sql The sql query to execute
106+
\param error The error message
107+
\param isDirty Flag to indicate if the underlying data will be modified
108+
109+
:return: true if everything is OK, false otherwise
104110
:rtype: bool
105111
%End
106112

@@ -161,6 +167,11 @@ class QgsTransaction : QObject /Abstract/
161167
Emitted after a rollback
162168
%End
163169

170+
void dirty( const QString &sql );
171+
%Docstring
172+
Emitted if a sql query is executed and the underlying data is modified
173+
%End
174+
164175
protected:
165176

166177

‎python/core/qgsvectorlayereditpassthrough.sip

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ class QgsVectorLayerEditPassthrough : QgsVectorLayerEditBuffer
4141
virtual void rollBack();
4242

4343

44+
bool update( QgsTransaction *transaction, const QString &sql );
45+
%Docstring
46+
Update underlying data with a SQL query embedded in a transaction.
47+
48+
\param transaction Transaction in which the sql query has been run
49+
\param sql The SQL query updating data
50+
51+
:return: true if the undo/redo command is well added to the stack, false otherwise
52+
53+
.. versionadded:: 3.0
54+
:rtype: bool
55+
%End
56+
4457
};
4558

4659
/************************************************************************

‎python/core/qgsvectorlayerundopassthroughcommand.sip

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
2323
%End
2424
public:
2525

26-
QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text );
26+
QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate = true );
2727
%Docstring
2828
Constructor for QgsVectorLayerUndoPassthroughCommand
2929
\param buffer associated edit buffer
3030
\param text text associated with command
31+
\param autocreate flag allowing to automatically create a savepoint if necessary
3132
%End
3233

3334
bool hasError() const;
@@ -58,6 +59,9 @@ class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
5859
Set error flag and append "failed" to text
5960
%End
6061

62+
63+
protected:
64+
6165
};
6266

6367

@@ -246,6 +250,32 @@ class QgsVectorLayerUndoPassthroughCommandRenameAttribute : QgsVectorLayerUndoPa
246250

247251
};
248252

253+
254+
class QgsVectorLayerUndoPassthroughCommandUpdate : QgsVectorLayerUndoPassthroughCommand
255+
{
256+
%Docstring
257+
Undo command for running a specific sql query in transaction group.
258+
.. versionadded:: 3.0
259+
%End
260+
261+
%TypeHeaderCode
262+
#include "qgsvectorlayerundopassthroughcommand.h"
263+
%End
264+
public:
265+
266+
QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsTransaction *transaction, const QString &sql );
267+
%Docstring
268+
Constructor for QgsVectorLayerUndoCommandUpdate
269+
\param buffer associated edit buffer
270+
\param transaction transaction running the sql query
271+
\param sql the query
272+
%End
273+
274+
virtual void undo();
275+
virtual void redo();
276+
277+
};
278+
249279
/************************************************************************
250280
* This file has been generated automatically from *
251281
* *

‎src/core/qgstransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
201201
if ( !mTransactionActive )
202202
return QString();
203203

204-
if ( !mLastSavePointIsDirty )
204+
if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
205205
return mSavepoints.top();
206206

207207
const QString name( QUuid::createUuid().toString() );

‎src/core/qgstransaction.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,14 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
111111
/**
112112
* Execute the \a sql string. The result must not be a tuple, so running a
113113
* ``SELECT`` query will return an error.
114+
*
115+
* \param sql The sql query to execute
116+
* \param error The error message
117+
* \param isDirty Flag to indicate if the underlying data will be modified
118+
*
119+
* \returns true if everything is OK, false otherwise
114120
*/
115-
virtual bool executeSql( const QString &sql, QString &error SIP_OUT ) = 0;
121+
virtual bool executeSql( const QString &sql, QString &error SIP_OUT, bool isDirty = false ) = 0;
116122

117123
/**
118124
* Checks if a the provider of a given \a layer supports transactions.
@@ -165,6 +171,11 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
165171
*/
166172
void afterRollback();
167173

174+
/**
175+
* Emitted if a sql query is executed and the underlying data is modified
176+
*/
177+
void dirty( const QString &sql );
178+
168179
protected:
169180
QgsTransaction( const QString &connString ) SIP_SKIP;
170181

‎src/core/qgsvectorlayer.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,8 @@ bool QgsVectorLayer::startEditing()
13451345
if ( mDataProvider->transaction() )
13461346
{
13471347
mEditBuffer = new QgsVectorLayerEditPassthrough( this );
1348+
1349+
connect( mDataProvider->transaction(), &QgsTransaction::dirty, this, &QgsVectorLayer::onDirtyTransaction, Qt::UniqueConnection );
13481350
}
13491351
else
13501352
{
@@ -4575,3 +4577,11 @@ bool QgsVectorLayer::readExtentFromXml() const
45754577
return mReadExtentFromXml;
45764578
}
45774579

4580+
void QgsVectorLayer::onDirtyTransaction( const QString &sql )
4581+
{
4582+
QgsTransaction *tr = dataProvider()->transaction();
4583+
if ( tr && mEditBuffer )
4584+
{
4585+
dynamic_cast<QgsVectorLayerEditPassthrough *>( mEditBuffer )->update( tr, sql );
4586+
}
4587+
}

‎src/core/qgsvectorlayer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
20662066
void onFeatureDeleted( QgsFeatureId fid );
20672067
void onRelationsLoaded();
20682068
void onSymbolsCounted();
2069+
void onDirtyTransaction( const QString &sql );
20692070

20702071
protected:
20712072
//! Set the extent

‎src/core/qgsvectorlayereditbuffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
267267
friend class QgsVectorLayerUndoPassthroughCommandAddAttribute;
268268
friend class QgsVectorLayerUndoPassthroughCommandDeleteAttribute;
269269
friend class QgsVectorLayerUndoPassthroughCommandRenameAttribute;
270+
friend class QgsVectorLayerUndoPassthroughCommandUpdate;
270271

271272
/**
272273
* Deleted feature IDs which are not committed. Note a feature can be added and then deleted

‎src/core/qgsvectorlayereditpassthrough.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "qgsvectorlayer.h"
1818
#include "qgsvectordataprovider.h"
1919
#include "qgsvectorlayerundopassthroughcommand.h"
20+
#include "qgstransaction.h"
2021

2122
QgsVectorLayerEditPassthrough::QgsVectorLayerEditPassthrough( QgsVectorLayer *layer )
2223
: mModified( false )
@@ -35,7 +36,12 @@ bool QgsVectorLayerEditPassthrough::modify( QgsVectorLayerUndoPassthroughCommand
3536
if ( cmd->hasError() )
3637
return false;
3738

38-
mModified = true;
39+
if ( !mModified )
40+
{
41+
mModified = true;
42+
emit layerModified();
43+
}
44+
3945
return true;
4046
}
4147

@@ -105,3 +111,8 @@ void QgsVectorLayerEditPassthrough::rollBack()
105111
{
106112
mModified = false;
107113
}
114+
115+
bool QgsVectorLayerEditPassthrough::update( QgsTransaction *tr, const QString &sql )
116+
{
117+
return modify( new QgsVectorLayerUndoPassthroughCommandUpdate( this, tr, sql ) );
118+
}

‎src/core/qgsvectorlayereditpassthrough.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
class QgsVectorLayer;
2222
class QgsVectorLayerUndoPassthroughCommand;
23+
class QgsTransaction;
2324

2425
/**
2526
* \ingroup core
@@ -43,6 +44,18 @@ class CORE_EXPORT QgsVectorLayerEditPassthrough : public QgsVectorLayerEditBuffe
4344
bool commitChanges( QStringList &commitErrors ) override;
4445
void rollBack() override;
4546

47+
/**
48+
* Update underlying data with a SQL query embedded in a transaction.
49+
*
50+
* \param transaction Transaction in which the sql query has been run
51+
* \param sql The SQL query updating data
52+
*
53+
* \returns true if the undo/redo command is well added to the stack, false otherwise
54+
*
55+
* \since QGIS 3.0
56+
*/
57+
bool update( QgsTransaction *transaction, const QString &sql );
58+
4659
private:
4760
bool mModified;
4861

‎src/core/qgsvectorlayerundopassthroughcommand.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
//@todo use setObsolete instead of mHasError when upgrading qt version, this will allow auto removal of the command
3030
// for the moment a errored command is left on the stack
3131

32-
QgsVectorLayerUndoPassthroughCommand::QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text )
32+
QgsVectorLayerUndoPassthroughCommand::QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate )
3333
: QgsVectorLayerUndoCommand( buffer )
34-
, mSavePointId( mBuffer->L->isEditCommandActive()
34+
, mSavePointId( mBuffer->L->isEditCommandActive() || !autocreate
3535
? mBuffer->L->dataProvider()->transaction()->savePoints().last()
3636
: mBuffer->L->dataProvider()->transaction()->createSavepoint( mError ) )
3737
, mHasError( !mError.isEmpty() )
@@ -333,3 +333,49 @@ void QgsVectorLayerUndoPassthroughCommandRenameAttribute::redo()
333333
setError();
334334
}
335335
}
336+
337+
QgsVectorLayerUndoPassthroughCommandUpdate::QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer, QgsTransaction *transaction, const QString &sql )
338+
: QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "custom transaction" ), false )
339+
, mTransaction( transaction )
340+
, mSql( sql )
341+
{
342+
}
343+
344+
void QgsVectorLayerUndoPassthroughCommandUpdate::undo()
345+
{
346+
if ( rollBackToSavePoint() )
347+
{
348+
mUndone = true;
349+
emit mBuffer->L->layerModified();
350+
}
351+
else
352+
{
353+
setError();
354+
}
355+
}
356+
357+
void QgsVectorLayerUndoPassthroughCommandUpdate::redo()
358+
{
359+
// the first time that the sql query is execute is within QgsTransaction
360+
// itself. So the redo has to be executed only after an undo action.
361+
if ( mUndone )
362+
{
363+
mSavePointId = mTransaction->createSavepoint( mError );
364+
365+
if ( mError.isEmpty() )
366+
{
367+
if ( mTransaction->executeSql( mSql, mError ) )
368+
{
369+
mUndone = false;
370+
}
371+
else
372+
{
373+
setError();
374+
}
375+
}
376+
else
377+
{
378+
setError();
379+
}
380+
}
381+
}

‎src/core/qgsvectorlayerundopassthroughcommand.h

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,15 @@ class CORE_EXPORT QgsVectorLayerUndoPassthroughCommand : public QgsVectorLayerUn
3636
* Constructor for QgsVectorLayerUndoPassthroughCommand
3737
* \param buffer associated edit buffer
3838
* \param text text associated with command
39+
* \param autocreate flag allowing to automatically create a savepoint if necessary
3940
*/
40-
QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text );
41+
QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate = true );
4142

4243
/**
4344
* Returns error status
4445
*/
4546
bool hasError() const { return mHasError; }
4647

47-
private:
48-
QString mError;
49-
QString mSavePointId;
50-
bool mHasError;
51-
bool mRecreateSavePoint;
52-
5348
protected:
5449

5550
/**
@@ -70,6 +65,14 @@ class CORE_EXPORT QgsVectorLayerUndoPassthroughCommand : public QgsVectorLayerUn
7065
*/
7166
void setError();
7267

68+
QString mSavePointId;
69+
70+
protected:
71+
QString mError;
72+
73+
private:
74+
bool mHasError;
75+
bool mRecreateSavePoint;
7376
};
7477

7578
/**
@@ -265,4 +268,32 @@ class CORE_EXPORT QgsVectorLayerUndoPassthroughCommandRenameAttribute : public Q
265268
const QString mOldName;
266269
};
267270

271+
/**
272+
* \ingroup core
273+
* \class QgsVectorLayerUndoPassthroughCommandUpdate
274+
* \brief Undo command for running a specific sql query in transaction group.
275+
* \since QGIS 3.0
276+
*/
277+
278+
class CORE_EXPORT QgsVectorLayerUndoPassthroughCommandUpdate : public QgsVectorLayerUndoPassthroughCommand
279+
{
280+
public:
281+
282+
/**
283+
* Constructor for QgsVectorLayerUndoCommandUpdate
284+
* \param buffer associated edit buffer
285+
* \param transaction transaction running the sql query
286+
* \param sql the query
287+
*/
288+
QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer SIP_TRANSFER, QgsTransaction *transaction, const QString &sql );
289+
290+
virtual void undo() override;
291+
virtual void redo() override;
292+
293+
private:
294+
QgsTransaction *mTransaction = nullptr;
295+
QString mSql;
296+
bool mUndone = false;
297+
};
298+
268299
#endif

‎src/providers/postgres/qgspostgrestransaction.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ bool QgsPostgresTransaction::rollbackTransaction( QString &error )
5757
return false;
5858
}
5959

60-
bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg )
60+
bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg, bool isDirty )
6161
{
6262
if ( !mConn )
6363
{
6464
return false;
6565
}
6666

67+
QString err;
68+
if ( isDirty )
69+
{
70+
createSavepoint( err );
71+
}
72+
6773
QgsDebugMsg( QString( "Transaction sql: %1" ).arg( sql ) );
6874
mConn->lock();
6975
QgsPostgresResult r( mConn->PQexec( sql, true ) );
@@ -72,8 +78,21 @@ bool QgsPostgresTransaction::executeSql( const QString &sql, QString &errorMsg )
7278
{
7379
errorMsg = QStringLiteral( "Status %1 (%2)" ).arg( r.PQresultStatus() ).arg( r.PQresultErrorMessage() );
7480
QgsDebugMsg( errorMsg );
81+
82+
if ( isDirty )
83+
{
84+
rollbackToSavepoint( savePoints().last(), err );
85+
}
86+
7587
return false;
7688
}
89+
90+
if ( isDirty )
91+
{
92+
dirtyLastSavePoint();
93+
emit dirty( sql );
94+
}
95+
7796
QgsDebugMsg( QString( "Status %1 (OK)" ).arg( r.PQresultStatus() ) );
7897
return true;
7998
}

‎src/providers/postgres/qgspostgrestransaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class QgsPostgresTransaction : public QgsTransaction
2929

3030
public:
3131
explicit QgsPostgresTransaction( const QString &connString );
32-
bool executeSql( const QString &sql, QString &error ) override;
32+
bool executeSql( const QString &sql, QString &error, bool isDirty = false ) override;
3333
QgsPostgresConn *connection() const { return mConn; }
3434

3535

0 commit comments

Comments
 (0)
Please sign in to comment.