DB2 fixes for #20337

This commit is contained in:
David Adler 2018-11-08 17:19:16 -05:00 committed by Nyall Dawson
parent 152b1b09ff
commit c339dd49fb
3 changed files with 50 additions and 20 deletions

View File

@ -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.
}
@ -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" ) );
@ -402,7 +410,10 @@ bool QgsDb2FeatureIterator::fetchFeature( QgsFeature &feature )
bool QgsDb2FeatureIterator::rewind()
{
if ( mClosed )
{
QgsDebugMsg( QStringLiteral( "iterator closed" ) );
return false;
}
if ( mStatement.isEmpty() )
{

View File

@ -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() );
@ -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

View File

@ -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;