Skip to content

Commit

Permalink
Merge pull request #5376 from pblottiere/dirty
Browse files Browse the repository at this point in the history
[FEATURE] Add a flag to dirty edit buffer when using executeSql in transactions
  • Loading branch information
pblottiere committed Oct 26, 2017
2 parents 611dca4 + 8a4382a commit 861e05b
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 29 deletions.
13 changes: 12 additions & 1 deletion python/core/qgstransaction.sip
Expand Up @@ -97,10 +97,16 @@ class QgsTransaction : QObject /Abstract/
:rtype: bool
%End

virtual bool executeSql( const QString &sql, QString &error /Out/ ) = 0;
virtual bool executeSql( const QString &sql, QString &error /Out/, bool isDirty = false ) = 0;
%Docstring
Execute the ``sql`` string. The result must not be a tuple, so running a
``SELECT`` query will return an error.

\param sql The sql query to execute
\param error The error message
\param isDirty Flag to indicate if the underlying data will be modified

:return: true if everything is OK, false otherwise
:rtype: bool
%End

Expand Down Expand Up @@ -161,6 +167,11 @@ class QgsTransaction : QObject /Abstract/
Emitted after a rollback
%End

void dirtied( const QString &sql );
%Docstring
Emitted if a sql query is executed and the underlying data is modified
%End

protected:


Expand Down
13 changes: 13 additions & 0 deletions python/core/qgsvectorlayereditpassthrough.sip
Expand Up @@ -41,6 +41,19 @@ class QgsVectorLayerEditPassthrough : QgsVectorLayerEditBuffer
virtual void rollBack();


bool update( QgsTransaction *transaction, const QString &sql );
%Docstring
Update underlying data with a SQL query embedded in a transaction.

\param transaction Transaction in which the sql query has been run
\param sql The SQL query updating data

:return: true if the undo/redo command is well added to the stack, false otherwise

.. versionadded:: 3.0
:rtype: bool
%End

};

/************************************************************************
Expand Down
52 changes: 48 additions & 4 deletions python/core/qgsvectorlayerundopassthroughcommand.sip
Expand Up @@ -23,11 +23,12 @@ class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
%End
public:

QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text );
QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate = true );
%Docstring
Constructor for QgsVectorLayerUndoPassthroughCommand
\param buffer associated edit buffer
\param text text associated with command
\param autocreate flag allowing to automatically create a savepoint if necessary
%End

bool hasError() const;
Expand All @@ -46,10 +47,12 @@ class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
:rtype: bool
%End

bool setSavePoint();
bool setSavePoint( const QString &savePointId = QString() );
%Docstring
Set the command savepoint or set error status
error satus should be false prior to call
Set the command savepoint or set error status.
Error satus should be false prior to call. If the savepoint given in
parameter is empty, then a new one is created if none is currently
available in the transaction.
:rtype: bool
%End

Expand All @@ -58,6 +61,21 @@ class QgsVectorLayerUndoPassthroughCommand : QgsVectorLayerUndoCommand
Set error flag and append "failed" to text
%End

void setErrorMessage( const QString &errorMessage );
%Docstring
Sets the error message.

.. versionadded:: 3.0
%End

QString errorMessage() const;
%Docstring
Returns the error message or an empty string if there's none.

.. versionadded:: 3.0
:rtype: str
%End

};


Expand Down Expand Up @@ -246,6 +264,32 @@ class QgsVectorLayerUndoPassthroughCommandRenameAttribute : QgsVectorLayerUndoPa

};


class QgsVectorLayerUndoPassthroughCommandUpdate : QgsVectorLayerUndoPassthroughCommand
{
%Docstring
Undo command for running a specific sql query in transaction group.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvectorlayerundopassthroughcommand.h"
%End
public:

QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer /Transfer/, QgsTransaction *transaction, const QString &sql );
%Docstring
Constructor for QgsVectorLayerUndoCommandUpdate
\param buffer associated edit buffer
\param transaction transaction running the sql query
\param sql the query
%End

virtual void undo();
virtual void redo();

};

/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgstransaction.cpp
Expand Up @@ -201,7 +201,7 @@ QString QgsTransaction::createSavepoint( QString &error SIP_OUT )
if ( !mTransactionActive )
return QString();

if ( !mLastSavePointIsDirty )
if ( !mLastSavePointIsDirty && !mSavepoints.isEmpty() )
return mSavepoints.top();

const QString name( QUuid::createUuid().toString() );
Expand Down
13 changes: 12 additions & 1 deletion src/core/qgstransaction.h
Expand Up @@ -111,8 +111,14 @@ class CORE_EXPORT QgsTransaction : public QObject SIP_ABSTRACT
/**
* Execute the \a sql string. The result must not be a tuple, so running a
* ``SELECT`` query will return an error.
*
* \param sql The sql query to execute
* \param error The error message
* \param isDirty Flag to indicate if the underlying data will be modified
*
* \returns true if everything is OK, false otherwise
*/
virtual bool executeSql( const QString &sql, QString &error SIP_OUT ) = 0;
virtual bool executeSql( const QString &sql, QString &error SIP_OUT, bool isDirty = false ) = 0;

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

/**
* Emitted if a sql query is executed and the underlying data is modified
*/
void dirtied( const QString &sql );

protected:
QgsTransaction( const QString &connString ) SIP_SKIP;

Expand Down
10 changes: 10 additions & 0 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -1345,6 +1345,8 @@ bool QgsVectorLayer::startEditing()
if ( mDataProvider->transaction() )
{
mEditBuffer = new QgsVectorLayerEditPassthrough( this );

connect( mDataProvider->transaction(), &QgsTransaction::dirtied, this, &QgsVectorLayer::onDirtyTransaction, Qt::UniqueConnection );
}
else
{
Expand Down Expand Up @@ -4575,3 +4577,11 @@ bool QgsVectorLayer::readExtentFromXml() const
return mReadExtentFromXml;
}

void QgsVectorLayer::onDirtyTransaction( const QString &sql )
{
QgsTransaction *tr = dataProvider()->transaction();
if ( tr && mEditBuffer )
{
qobject_cast<QgsVectorLayerEditPassthrough *>( mEditBuffer )->update( tr, sql );
}
}
1 change: 1 addition & 0 deletions src/core/qgsvectorlayer.h
Expand Up @@ -2066,6 +2066,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
void onFeatureDeleted( QgsFeatureId fid );
void onRelationsLoaded();
void onSymbolsCounted();
void onDirtyTransaction( const QString &sql );

protected:
//! Set the extent
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorlayereditbuffer.h
Expand Up @@ -267,6 +267,7 @@ class CORE_EXPORT QgsVectorLayerEditBuffer : public QObject
friend class QgsVectorLayerUndoPassthroughCommandAddAttribute;
friend class QgsVectorLayerUndoPassthroughCommandDeleteAttribute;
friend class QgsVectorLayerUndoPassthroughCommandRenameAttribute;
friend class QgsVectorLayerUndoPassthroughCommandUpdate;

/**
* Deleted feature IDs which are not committed. Note a feature can be added and then deleted
Expand Down
13 changes: 12 additions & 1 deletion src/core/qgsvectorlayereditpassthrough.cpp
Expand Up @@ -17,6 +17,7 @@
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayerundopassthroughcommand.h"
#include "qgstransaction.h"

QgsVectorLayerEditPassthrough::QgsVectorLayerEditPassthrough( QgsVectorLayer *layer )
: mModified( false )
Expand All @@ -35,7 +36,12 @@ bool QgsVectorLayerEditPassthrough::modify( QgsVectorLayerUndoPassthroughCommand
if ( cmd->hasError() )
return false;

mModified = true;
if ( !mModified )
{
mModified = true;
emit layerModified();
}

return true;
}

Expand Down Expand Up @@ -105,3 +111,8 @@ void QgsVectorLayerEditPassthrough::rollBack()
{
mModified = false;
}

bool QgsVectorLayerEditPassthrough::update( QgsTransaction *tr, const QString &sql )
{
return modify( new QgsVectorLayerUndoPassthroughCommandUpdate( this, tr, sql ) );
}
13 changes: 13 additions & 0 deletions src/core/qgsvectorlayereditpassthrough.h
Expand Up @@ -20,6 +20,7 @@

class QgsVectorLayer;
class QgsVectorLayerUndoPassthroughCommand;
class QgsTransaction;

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

/**
* Update underlying data with a SQL query embedded in a transaction.
*
* \param transaction Transaction in which the sql query has been run
* \param sql The SQL query updating data
*
* \returns true if the undo/redo command is well added to the stack, false otherwise
*
* \since QGIS 3.0
*/
bool update( QgsTransaction *transaction, const QString &sql );

private:
bool mModified;

Expand Down
85 changes: 77 additions & 8 deletions src/core/qgsvectorlayerundopassthroughcommand.cpp
Expand Up @@ -29,9 +29,9 @@
//@todo use setObsolete instead of mHasError when upgrading qt version, this will allow auto removal of the command
// for the moment a errored command is left on the stack

QgsVectorLayerUndoPassthroughCommand::QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text )
QgsVectorLayerUndoPassthroughCommand::QgsVectorLayerUndoPassthroughCommand( QgsVectorLayerEditBuffer *buffer, const QString &text, bool autocreate )
: QgsVectorLayerUndoCommand( buffer )
, mSavePointId( mBuffer->L->isEditCommandActive()
, mSavePointId( mBuffer->L->isEditCommandActive() || !autocreate
? mBuffer->L->dataProvider()->transaction()->savePoints().last()
: mBuffer->L->dataProvider()->transaction()->createSavepoint( mError ) )
, mHasError( !mError.isEmpty() )
Expand All @@ -54,19 +54,36 @@ void QgsVectorLayerUndoPassthroughCommand::setError()
}
}

bool QgsVectorLayerUndoPassthroughCommand::setSavePoint()
void QgsVectorLayerUndoPassthroughCommand::setErrorMessage( const QString &errorMessage )
{
mError = errorMessage;
}

QString QgsVectorLayerUndoPassthroughCommand::errorMessage() const
{
return mError;
}

bool QgsVectorLayerUndoPassthroughCommand::setSavePoint( const QString &savePointId )
{
if ( !hasError() )
{
// re-create savepoint only if mRecreateSavePoint and rollBackToSavePoint as occurred
if ( mRecreateSavePoint && mBuffer->L->dataProvider()->transaction()->savePoints().indexOf( mSavePointId ) == -1 )
if ( savePointId.isEmpty() )
{
mSavePointId = mBuffer->L->dataProvider()->transaction()->createSavepoint( mSavePointId, mError );
if ( mSavePointId.isEmpty() )
// re-create savepoint only if mRecreateSavePoint and rollBackToSavePoint as occurred
if ( mRecreateSavePoint && mBuffer->L->dataProvider()->transaction()->savePoints().indexOf( mSavePointId ) == -1 )
{
setError();
mSavePointId = mBuffer->L->dataProvider()->transaction()->createSavepoint( mSavePointId, mError );
if ( mSavePointId.isEmpty() )
{
setError();
}
}
}
else
{
mSavePointId = savePointId;
}
}
return !hasError();
}
Expand Down Expand Up @@ -333,3 +350,55 @@ void QgsVectorLayerUndoPassthroughCommandRenameAttribute::redo()
setError();
}
}

QgsVectorLayerUndoPassthroughCommandUpdate::QgsVectorLayerUndoPassthroughCommandUpdate( QgsVectorLayerEditBuffer *buffer, QgsTransaction *transaction, const QString &sql )
: QgsVectorLayerUndoPassthroughCommand( buffer, QObject::tr( "custom transaction" ), false )
, mTransaction( transaction )
, mSql( sql )
{
}

void QgsVectorLayerUndoPassthroughCommandUpdate::undo()
{
if ( rollBackToSavePoint() )
{
mUndone = true;
emit mBuffer->L->layerModified();
}
else
{
setError();
}
}

void QgsVectorLayerUndoPassthroughCommandUpdate::redo()
{
// the first time that the sql query is execute is within QgsTransaction
// itself. So the redo has to be executed only after an undo action.
if ( mUndone )
{
QString errorMessage;

QString savePointId = mTransaction->createSavepoint( errorMessage );

if ( errorMessage.isEmpty() )
{
setSavePoint( savePointId );

if ( mTransaction->executeSql( mSql, errorMessage ) )
{
mUndone = false;
}
else
{
setErrorMessage( errorMessage );
setError();
}
}
else
{
setErrorMessage( errorMessage );
setError();
}
}
}

0 comments on commit 861e05b

Please sign in to comment.