Skip to content

Commit c339dd4

Browse files
dwadlernyalldawson
authored andcommittedNov 20, 2018
DB2 fixes for #20337
1 parent 152b1b0 commit c339dd4

File tree

3 files changed

+50
-20
lines changed

3 files changed

+50
-20
lines changed
 

‎src/providers/db2/qgsdb2featureiterator.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,11 @@ QgsDb2FeatureIterator::QgsDb2FeatureIterator( QgsDb2FeatureSource *source, bool
5151

5252
BuildStatement( request );
5353

54-
// connect to the database
55-
QString errMsg;
56-
mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg );
57-
58-
if ( !errMsg.isEmpty() )
59-
{
60-
QgsDebugMsg( "Failed to open database: " + errMsg );
61-
return;
62-
}
63-
64-
// create sql query
65-
mQuery.reset( new QSqlQuery( mDatabase ) );
66-
67-
// start selection
68-
rewind();
54+
// WARNING - we can't obtain the database connection now, as this method should be
55+
// run from the main thread, yet iteration can be done in a different thread.
56+
// This would result in failure, because QSqlDatabase instances cannot be used
57+
// from a different thread where they were created. Instead, we defer creation
58+
// of the database until the first feature is fetched.
6959
}
7060

7161

@@ -308,12 +298,30 @@ bool QgsDb2FeatureIterator::nextFeatureFilterExpression( QgsFeature &f )
308298
bool QgsDb2FeatureIterator::fetchFeature( QgsFeature &feature )
309299
{
310300
feature.setValid( false );
311-
if ( mClosed )
301+
302+
if ( !mDatabase.isValid() )
312303
{
313-
QgsDebugMsg( QStringLiteral( "iterator closed" ) );
304+
// No existing connection, so set it up now. It's safe to do here as we're now in
305+
// the thread were iteration is actually occurring.
306+
// connect to the database
307+
QString errMsg;
308+
QgsDebugMsg( QStringLiteral( "fetchFeature getDatabase" ) );
309+
mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg );
310+
QgsDebugMsg( QStringLiteral( "fetchFeature back from getDatabase" ) );
311+
if ( !errMsg.isEmpty() )
312+
{
313+
QgsDebugMsg( "Failed to open database: " + errMsg );
314314
return false;
315315
}
316316

317+
// create sql query
318+
mQuery.reset( new QSqlQuery( mDatabase ) );
319+
320+
// start selection
321+
if ( !rewind() )
322+
return false;
323+
}
324+
317325
if ( !mQuery )
318326
{
319327
QgsDebugMsg( QStringLiteral( "Read attempt on no query" ) );
@@ -402,7 +410,10 @@ bool QgsDb2FeatureIterator::fetchFeature( QgsFeature &feature )
402410
bool QgsDb2FeatureIterator::rewind()
403411
{
404412
if ( mClosed )
413+
{
414+
QgsDebugMsg( QStringLiteral( "iterator closed" ) );
405415
return false;
416+
}
406417

407418
if ( mStatement.isEmpty() )
408419
{

‎src/providers/db2/qgsdb2provider.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,23 @@ QSqlDatabase QgsDb2Provider::getDatabase( const QString &connInfo, QString &errM
186186
connectionName = service;
187187
}
188188
QgsDebugMsg( "connectionName: " + connectionName );
189+
190+
// Starting with Qt 5.11, sharing the same connection between threads is not allowed.
191+
// We use a dedicated connection for each thread requiring access to the database,
192+
// using the thread address as connection name.
193+
const QString threadSafeConnectionName = dbConnectionName( connectionName );
194+
QgsDebugMsg( "threadSafeConnectionName: " + threadSafeConnectionName );
195+
189196
/* if new database connection */
190-
if ( !QSqlDatabase::contains( connectionName ) )
197+
if ( !QSqlDatabase::contains( threadSafeConnectionName ) )
191198
{
192199
QgsDebugMsg( QStringLiteral( "new connection. create new QODBC mapping" ) );
193-
db = QSqlDatabase::addDatabase( QStringLiteral( "QODBC3" ), connectionName );
200+
db = QSqlDatabase::addDatabase( QStringLiteral( "QODBC3" ), threadSafeConnectionName );
194201
}
195202
else /* if existing database connection */
196203
{
197204
QgsDebugMsg( QStringLiteral( "found existing connection, use the existing one" ) );
198-
db = QSqlDatabase::database( connectionName );
205+
db = QSqlDatabase::database( threadSafeConnectionName );
199206
}
200207
db.setHostName( host );
201208
db.setPort( port.toInt() );
@@ -1747,6 +1754,14 @@ QGISEXTERN QgsVectorLayerExporter::ExportError createEmptyLayer(
17471754
);
17481755
}
17491756

1757+
QString QgsDb2Provider::dbConnectionName( const QString &name )
1758+
{
1759+
// Starting with Qt 5.11, sharing the same connection between threads is not allowed.
1760+
// We use a dedicated connection for each thread requiring access to the database,
1761+
// using the thread address as connection name.
1762+
const QString threadAddress = QStringLiteral( ":0x%1" ).arg( QString::number( reinterpret_cast< quintptr >( QThread::currentThread() ), 16 ) );
1763+
return name + threadAddress;
1764+
}
17501765

17511766
#ifdef HAVE_GUI
17521767

‎src/providers/db2/qgsdb2provider.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ class QgsDb2Provider : public QgsVectorDataProvider
118118
private:
119119
static void db2WkbTypeAndDimension( QgsWkbTypes::Type wkbType, QString &geometryType, int &dim );
120120
static QString db2TypeName( int typeId );
121+
/**
122+
* Returns a thread-safe connection name for use with QSqlDatabase
123+
*/
124+
static QString dbConnectionName( const QString &name );
121125

122126
QgsFields mAttributeFields; //fields
123127
QMap<int, QVariant> mDefaultValues;

0 commit comments

Comments
 (0)
Please sign in to comment.