Skip to content

Commit 2e515a2

Browse files
committedApr 14, 2016
[postgres] Avoid deadlocks in transactional editing
Can currently be triggered by using the field calculator to update a selection. While an iterator is active and the connection is locked, an update statement waits unsuccessfully for the same (locked) connection. This commit only locks the connection while an iterator is actually fetching data and not for its entire lifetime.
1 parent 07fcf24 commit 2e515a2

File tree

2 files changed

+26
-6
lines changed

2 files changed

+26
-6
lines changed
 

‎src/providers/postgres/qgspostgresfeatureiterator.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ QgsPostgresFeatureIterator::QgsPostgresFeatureIterator( QgsPostgresFeatureSource
4646
else
4747
{
4848
mConn = source->mTransactionConnection;
49-
mConn->lock();
5049
mIsTransactionConnection = true;
5150
}
5251

@@ -227,6 +226,8 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
227226
{
228227
QString fetch = QString( "FETCH FORWARD %1 FROM %2" ).arg( mFeatureQueueSize ).arg( mCursorName );
229228
QgsDebugMsgLevel( QString( "fetching %1 features." ).arg( mFeatureQueueSize ), 4 );
229+
230+
lock();
230231
if ( mConn->PQsendQuery( fetch ) == 0 ) // fetch features asynchronously
231232
{
232233
QgsMessageLog::logMessage( QObject::tr( "Fetching from cursor %1 failed\nDatabase error: %2" ).arg( mCursorName, mConn->PQerrorMessage() ), QObject::tr( "PostGIS" ) );
@@ -257,6 +258,7 @@ bool QgsPostgresFeatureIterator::fetchFeature( QgsFeature& feature )
257258
getFeature( queryResult, row, mFeatureQueue.back() );
258259
} // for each row in queue
259260
}
261+
unlock();
260262
}
261263

262264
if ( mFeatureQueue.empty() )
@@ -319,13 +321,28 @@ bool QgsPostgresFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::
319321
return mOrderByCompiled;
320322
}
321323

324+
void QgsPostgresFeatureIterator::lock()
325+
{
326+
if ( mIsTransactionConnection )
327+
mConn->lock();
328+
}
329+
330+
void QgsPostgresFeatureIterator::unlock()
331+
{
332+
if ( mIsTransactionConnection )
333+
mConn->unlock();
334+
}
335+
322336
bool QgsPostgresFeatureIterator::rewind()
323337
{
324338
if ( mClosed )
325339
return false;
326340

327341
// move cursor to first record
342+
343+
lock();
328344
mConn->PQexecNR( QString( "move absolute 0 in %1" ).arg( mCursorName ) );
345+
unlock();
329346
mFeatureQueue.clear();
330347
mFetched = 0;
331348
mLastFetch = false;
@@ -338,16 +355,14 @@ bool QgsPostgresFeatureIterator::close()
338355
if ( !mConn )
339356
return false;
340357

358+
lock();
341359
mConn->closeCursor( mCursorName );
360+
unlock();
342361

343362
if ( !mIsTransactionConnection )
344363
{
345364
QgsPostgresConnPool::instance()->releaseConnection( mConn );
346365
}
347-
else
348-
{
349-
mConn->unlock();
350-
}
351366
mConn = nullptr;
352367

353368
while ( !mFeatureQueue.empty() )
@@ -598,15 +613,17 @@ bool QgsPostgresFeatureIterator::declareCursor( const QString& whereClause, long
598613
if ( !orderBy.isEmpty() )
599614
query += QString( " ORDER BY %1 " ).arg( orderBy );
600615

616+
lock();
601617
if ( !mConn->openCursor( mCursorName, query ) )
602618
{
603-
619+
unlock();
604620
// reloading the fields might help next time around
605621
// TODO how to cleanly force reload of fields? P->loadFields();
606622
if ( closeOnFail )
607623
close();
608624
return false;
609625
}
626+
unlock();
610627

611628
mLastFetch = false;
612629
return true;

‎src/providers/postgres/qgspostgresfeatureiterator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class QgsPostgresFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Q
126126

127127
virtual bool prepareOrderBy( const QList<QgsFeatureRequest::OrderByClause> &orderBys ) override;
128128

129+
inline void lock();
130+
inline void unlock();
131+
129132
bool mExpressionCompiled;
130133
bool mOrderByCompiled;
131134
bool mLastFetch;

0 commit comments

Comments
 (0)
Please sign in to comment.