Skip to content

Commit

Permalink
DB2 fixes for #20337
Browse files Browse the repository at this point in the history
  • Loading branch information
dwadler authored and nyalldawson committed Nov 20, 2018
1 parent 152b1b0 commit c339dd4
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
45 changes: 28 additions & 17 deletions src/providers/db2/qgsdb2featureiterator.cpp
Expand Up @@ -51,21 +51,11 @@ QgsDb2FeatureIterator::QgsDb2FeatureIterator( QgsDb2FeatureSource *source, bool

BuildStatement( request );

// connect to the database
QString errMsg;
mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg );

if ( !errMsg.isEmpty() )
{
QgsDebugMsg( "Failed to open database: " + errMsg );
return;
}

// create sql query
mQuery.reset( new QSqlQuery( mDatabase ) );

// start selection
rewind();
// WARNING - we can't obtain the database connection now, as this method should be
// run from the main thread, yet iteration can be done in a different thread.
// This would result in failure, because QSqlDatabase instances cannot be used
// from a different thread where they were created. Instead, we defer creation
// of the database until the first feature is fetched.
}


Expand Down Expand Up @@ -308,12 +298,30 @@ bool QgsDb2FeatureIterator::nextFeatureFilterExpression( QgsFeature &f )
bool QgsDb2FeatureIterator::fetchFeature( QgsFeature &feature )
{
feature.setValid( false );
if ( mClosed )

if ( !mDatabase.isValid() )
{
QgsDebugMsg( QStringLiteral( "iterator closed" ) );
// No existing connection, so set it up now. It's safe to do here as we're now in
// the thread were iteration is actually occurring.
// connect to the database
QString errMsg;
QgsDebugMsg( QStringLiteral( "fetchFeature getDatabase" ) );
mDatabase = QgsDb2Provider::getDatabase( mSource->mConnInfo, errMsg );
QgsDebugMsg( QStringLiteral( "fetchFeature back from getDatabase" ) );
if ( !errMsg.isEmpty() )
{
QgsDebugMsg( "Failed to open database: " + errMsg );
return false;
}

// create sql query
mQuery.reset( new QSqlQuery( mDatabase ) );

// start selection
if ( !rewind() )
return false;
}

if ( !mQuery )
{
QgsDebugMsg( QStringLiteral( "Read attempt on no query" ) );
Expand Down Expand Up @@ -402,7 +410,10 @@ bool QgsDb2FeatureIterator::fetchFeature( QgsFeature &feature )
bool QgsDb2FeatureIterator::rewind()
{
if ( mClosed )
{
QgsDebugMsg( QStringLiteral( "iterator closed" ) );
return false;
}

if ( mStatement.isEmpty() )
{
Expand Down
21 changes: 18 additions & 3 deletions src/providers/db2/qgsdb2provider.cpp
Expand Up @@ -186,16 +186,23 @@ QSqlDatabase QgsDb2Provider::getDatabase( const QString &connInfo, QString &errM
connectionName = service;
}
QgsDebugMsg( "connectionName: " + connectionName );

// Starting with Qt 5.11, sharing the same connection between threads is not allowed.
// We use a dedicated connection for each thread requiring access to the database,
// using the thread address as connection name.
const QString threadSafeConnectionName = dbConnectionName( connectionName );
QgsDebugMsg( "threadSafeConnectionName: " + threadSafeConnectionName );

/* if new database connection */
if ( !QSqlDatabase::contains( connectionName ) )
if ( !QSqlDatabase::contains( threadSafeConnectionName ) )
{
QgsDebugMsg( QStringLiteral( "new connection. create new QODBC mapping" ) );
db = QSqlDatabase::addDatabase( QStringLiteral( "QODBC3" ), connectionName );
db = QSqlDatabase::addDatabase( QStringLiteral( "QODBC3" ), threadSafeConnectionName );
}
else /* if existing database connection */
{
QgsDebugMsg( QStringLiteral( "found existing connection, use the existing one" ) );
db = QSqlDatabase::database( connectionName );
db = QSqlDatabase::database( threadSafeConnectionName );
}
db.setHostName( host );
db.setPort( port.toInt() );
Expand Down Expand Up @@ -1747,6 +1754,14 @@ QGISEXTERN QgsVectorLayerExporter::ExportError createEmptyLayer(
);
}

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

#ifdef HAVE_GUI

Expand Down
4 changes: 4 additions & 0 deletions src/providers/db2/qgsdb2provider.h
Expand Up @@ -118,6 +118,10 @@ class QgsDb2Provider : public QgsVectorDataProvider
private:
static void db2WkbTypeAndDimension( QgsWkbTypes::Type wkbType, QString &geometryType, int &dim );
static QString db2TypeName( int typeId );
/**
* Returns a thread-safe connection name for use with QSqlDatabase
*/
static QString dbConnectionName( const QString &name );

QgsFields mAttributeFields; //fields
QMap<int, QVariant> mDefaultValues;
Expand Down

0 comments on commit c339dd4

Please sign in to comment.