Fix Oracle connection reset in case of ORA-12170: Connect timeout occurred

Oracle connection is never reset in case of ORA-12170: Connect timeout occurred.

The execution of request is only retry in case of getfeature and error 12170.

A fix has been done in PostgreSQL provider #9107
This commit is contained in:
rldhont 2019-04-26 11:54:30 +02:00 committed by Jürgen Fischer
parent 4a5c5f2fff
commit acc94ee28b
6 changed files with 111 additions and 55 deletions

View File

@ -192,6 +192,15 @@ void QgsOracleConn::disconnect()
deleteLater(); deleteLater();
} }
void QgsOracleConn::reconnect()
{
if ( mDatabase.isOpen() )
{
mDatabase.close();
mDatabase.open();
}
}
bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList &params ) bool QgsOracleConn::exec( QSqlQuery &qry, const QString &sql, const QVariantList &params )
{ {
QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 ); QgsDebugMsgLevel( QStringLiteral( "SQL: %1" ).arg( sql ), 4 );

View File

@ -117,6 +117,11 @@ class QgsOracleConn : public QObject
static QgsOracleConn *connectDb( const QgsDataSourceUri &uri ); static QgsOracleConn *connectDb( const QgsDataSourceUri &uri );
void disconnect(); void disconnect();
/**
* Try to reconnect to database after timeout
*/
void reconnect();
/** /**
* Double quote a Oracle identifier for placement in a SQL string. * Double quote a Oracle identifier for placement in a SQL string.
*/ */

View File

@ -275,7 +275,7 @@ bool QgsOracleFeatureIterator::fetchFeature( QgsFeature &feature )
if ( mRewind ) if ( mRewind )
{ {
mRewind = false; mRewind = false;
if ( !QgsOracleProvider::exec( mQry, mSql, mArgs ) ) if ( !execQuery( mSql, mArgs, 1 ) )
{ {
QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" ) QgsMessageLog::logMessage( QObject::tr( "Fetching features failed.\nSQL: %1\nError: %2" )
.arg( mQry.lastQuery(), .arg( mQry.lastQuery(),
@ -508,7 +508,7 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar
QgsDebugMsg( QStringLiteral( "Fetch features: %1" ).arg( query ) ); QgsDebugMsg( QStringLiteral( "Fetch features: %1" ).arg( query ) );
mSql = query; mSql = query;
mArgs = args; mArgs = args;
if ( !QgsOracleProvider::exec( mQry, query, args ) ) if ( !execQuery( query, args, 1 ) )
{ {
if ( showLog ) if ( showLog )
{ {
@ -528,6 +528,28 @@ bool QgsOracleFeatureIterator::openQuery( const QString &whereClause, const QVar
return true; return true;
} }
bool QgsOracleFeatureIterator::execQuery( const QString &query, const QVariantList &args, int retryCount )
{
if ( !QgsOracleProvider::exec( mQry, query, args ) )
{
if ( retryCount != 0 )
{
// If the connection has been closed
// Try again N times in case of timeout
// ORA-12170: TNS:Connect timeout occurred
if ( mQry.lastError().number() == 12170 )
{
// restart connection
mConnection->reconnect();
// redo execute query
return execQuery( query, args, retryCount - 1 );
}
}
return false;
}
return true;
}
// ----------- // -----------
QgsOracleFeatureSource::QgsOracleFeatureSource( const QgsOracleProvider *p ) QgsOracleFeatureSource::QgsOracleFeatureSource( const QgsOracleProvider *p )

View File

@ -73,6 +73,8 @@ class QgsOracleFeatureIterator : public QgsAbstractFeatureIteratorFromSource<Qgs
bool openQuery( const QString &whereClause, const QVariantList &args, bool showLog = true ); bool openQuery( const QString &whereClause, const QVariantList &args, bool showLog = true );
bool execQuery( const QString &query, const QVariantList &args, int retryCount = 0 );
QgsOracleConn *mConnection = nullptr; QgsOracleConn *mConnection = nullptr;
QSqlQuery mQry; QSqlQuery mQry;
bool mRewind = false; bool mRewind = false;

View File

@ -75,8 +75,8 @@ QgsOracleProvider::QgsOracleProvider( QString const &uri, const ProviderOptions
mUseEstimatedMetadata = mUri.useEstimatedMetadata(); mUseEstimatedMetadata = mUri.useEstimatedMetadata();
mIncludeGeoAttributes = mUri.hasParam( "includegeoattributes" ) ? mUri.param( "includegeoattributes" ) == "true" : false; mIncludeGeoAttributes = mUri.hasParam( "includegeoattributes" ) ? mUri.param( "includegeoattributes" ) == "true" : false;
mConnection = QgsOracleConn::connectDb( mUri ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !mConnection ) if ( !conn )
{ {
return; return;
} }
@ -93,7 +93,7 @@ QgsOracleProvider::QgsOracleProvider( QString const &uri, const ProviderOptions
if ( mOwnerName.isEmpty() ) if ( mOwnerName.isEmpty() )
{ {
mOwnerName = mConnection->currentUser(); mOwnerName = conn->currentUser();
} }
if ( !mOwnerName.isEmpty() ) if ( !mOwnerName.isEmpty() )
@ -229,12 +229,12 @@ void QgsOracleProvider::setWorkspace( const QString &workspace )
else else
mUri.setParam( "dbworkspace", workspace ); mUri.setParam( "dbworkspace", workspace );
mConnection = QgsOracleConn::connectDb( mUri ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !mConnection ) if ( !conn )
{ {
mUri = prevUri; mUri = prevUri;
QgsDebugMsg( QStringLiteral( "restoring previous uri:%1" ).arg( mUri.uri() ) ); QgsDebugMsg( QStringLiteral( "restoring previous uri:%1" ).arg( mUri.uri() ) );
mConnection = QgsOracleConn::connectDb( mUri ); conn = QgsOracleConn::connectDb( mUri );
} }
else else
{ {
@ -249,9 +249,9 @@ QgsAbstractFeatureSource *QgsOracleProvider::featureSource() const
void QgsOracleProvider::disconnectDb() void QgsOracleProvider::disconnectDb()
{ {
if ( mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
mConnection->disconnect(); if ( conn )
mConnection = nullptr; conn->disconnect();
} }
bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &args ) bool QgsOracleProvider::exec( QSqlQuery &qry, QString sql, const QVariantList &args )
@ -288,6 +288,7 @@ QString QgsOracleProvider::storageType() const
QString QgsOracleProvider::pkParamWhereClause() const QString QgsOracleProvider::pkParamWhereClause() const
{ {
QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QString whereClause; QString whereClause;
switch ( mPrimaryKeyType ) switch ( mPrimaryKeyType )
@ -303,7 +304,7 @@ QString QgsOracleProvider::pkParamWhereClause() const
int idx = mPrimaryKeyAttrs[i]; int idx = mPrimaryKeyAttrs[i];
QgsField fld = field( idx ); QgsField fld = field( idx );
whereClause += delim + QString( "%1=?" ).arg( mConnection->fieldExpression( fld ) ); whereClause += delim + QString( "%1=?" ).arg( conn->fieldExpression( fld ) );
delim = " AND "; delim = " AND ";
} }
} }
@ -517,7 +518,8 @@ bool QgsOracleProvider::loadFields()
mAttributeFields.clear(); mAttributeFields.clear();
mDefaultValues.clear(); mDefaultValues.clear();
QSqlQuery qry( *mConnection ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *conn );
QMap<QString, QString> comments; QMap<QString, QString> comments;
QMap<QString, QString> types; QMap<QString, QString> types;
@ -752,10 +754,11 @@ bool QgsOracleProvider::hasSufficientPermsAndCapabilities()
mEnabledCapabilities = QgsVectorDataProvider::SelectAtId; mEnabledCapabilities = QgsVectorDataProvider::SelectAtId;
QSqlQuery qry( *mConnection ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *conn );
if ( !mIsQuery ) if ( !mIsQuery )
{ {
if ( mConnection->currentUser() == mOwnerName ) if ( conn->currentUser() == mOwnerName )
{ {
// full set of privileges for the owner // full set of privileges for the owner
mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures mEnabledCapabilities |= QgsVectorDataProvider::DeleteFeatures
@ -854,8 +857,8 @@ bool QgsOracleProvider::determinePrimaryKey()
// check to see if there is an unique index on the relation, which // check to see if there is an unique index on the relation, which
// can be used as a key into the table. Primary keys are always // can be used as a key into the table. Primary keys are always
// unique indices, so we catch them as well. // unique indices, so we catch them as well.
QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !mIsQuery ) if ( !mIsQuery )
{ {
@ -989,7 +992,8 @@ bool QgsOracleProvider::uniqueData( QString query, QString colName )
{ {
Q_UNUSED( query ) Q_UNUSED( query )
// Check to see if the given column contains unique data // Check to see if the given column contains unique data
QSqlQuery qry( *mConnection ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *conn );
QString table = mQuery; QString table = mQuery;
if ( !mSqlWhereClause.isEmpty() ) if ( !mSqlWhereClause.isEmpty() )
@ -1015,7 +1019,8 @@ bool QgsOracleProvider::uniqueData( QString query, QString colName )
// Returns the minimum value of an attribute // Returns the minimum value of an attribute
QVariant QgsOracleProvider::minimumValue( int index ) const QVariant QgsOracleProvider::minimumValue( int index ) const
{ {
if ( !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !conn )
return QVariant( QString() ); return QVariant( QString() );
try try
@ -1031,7 +1036,7 @@ QVariant QgsOracleProvider::minimumValue( int index ) const
sql += QString( " WHERE %1" ).arg( mSqlWhereClause ); sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
} }
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !exec( qry, sql, QVariantList() ) )
{ {
@ -1058,7 +1063,8 @@ QSet<QVariant> QgsOracleProvider::uniqueValues( int index, int limit ) const
{ {
QSet<QVariant> uniqueValues; QSet<QVariant> uniqueValues;
if ( !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !conn )
return uniqueValues; return uniqueValues;
try try
@ -1082,7 +1088,7 @@ QSet<QVariant> QgsOracleProvider::uniqueValues( int index, int limit ) const
sql = QString( "SELECT * FROM (%1) WHERE rownum<=%2" ).arg( sql ).arg( limit ); sql = QString( "SELECT * FROM (%1) WHERE rownum<=%2" ).arg( sql ).arg( limit );
} }
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !exec( qry, sql, QVariantList() ) )
{ {
@ -1108,7 +1114,8 @@ QSet<QVariant> QgsOracleProvider::uniqueValues( int index, int limit ) const
// Returns the maximum value of an attribute // Returns the maximum value of an attribute
QVariant QgsOracleProvider::maximumValue( int index ) const QVariant QgsOracleProvider::maximumValue( int index ) const
{ {
if ( !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !conn )
return QVariant(); return QVariant();
try try
@ -1124,7 +1131,7 @@ QVariant QgsOracleProvider::maximumValue( int index ) const
sql += QString( " WHERE %1" ).arg( mSqlWhereClause ); sql += QString( " WHERE %1" ).arg( mSqlWhereClause );
} }
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !exec( qry, sql, QVariantList() ) )
{ {
@ -1166,7 +1173,8 @@ QString QgsOracleProvider::paramValue( QString fieldValue, const QString &defaul
} }
else if ( fieldValue == defaultValue && !defaultValue.isNull() ) else if ( fieldValue == defaultValue && !defaultValue.isNull() )
{ {
QSqlQuery qry( *mConnection ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *conn );
if ( !exec( qry, QString( "SELECT %1 FROM dual" ).arg( defaultValue ), QVariantList() ) || !qry.next() ) if ( !exec( qry, QString( "SELECT %1 FROM dual" ).arg( defaultValue ), QVariantList() ) || !qry.next() )
{ {
throw OracleException( tr( "Evaluation of default value failed" ), qry ); throw OracleException( tr( "Evaluation of default value failed" ), qry );
@ -1186,7 +1194,8 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag
if ( flist.size() == 0 ) if ( flist.size() == 0 )
return true; return true;
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
bool returnvalue = true; bool returnvalue = true;
@ -1202,7 +1211,7 @@ bool QgsOracleProvider::addFeatures( QgsFeatureList &flist, QgsFeatureSink::Flag
flags |= QgsFeatureSink::FastInsert; flags |= QgsFeatureSink::FastInsert;
} }
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
try try
{ {
@ -1422,10 +1431,11 @@ bool QgsOracleProvider::deleteFeatures( const QgsFeatureIds &id )
{ {
bool returnvalue = true; bool returnvalue = true;
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
try try
{ {
@ -1473,10 +1483,11 @@ bool QgsOracleProvider::addAttributes( const QList<QgsField> &attributes )
{ {
bool returnvalue = true; bool returnvalue = true;
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
try try
{ {
@ -1553,10 +1564,11 @@ bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids )
{ {
bool returnvalue = true; bool returnvalue = true;
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
try try
{ {
@ -1615,7 +1627,8 @@ bool QgsOracleProvider::deleteAttributes( const QgsAttributeIds &ids )
bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes ) bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
{ {
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin(); QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
@ -1635,7 +1648,7 @@ bool QgsOracleProvider::renameAttributes( const QgsFieldNameMap &renamedAttribut
} }
} }
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
bool returnvalue = true; bool returnvalue = true;
@ -1695,13 +1708,14 @@ bool QgsOracleProvider::changeAttributeValues( const QgsChangedAttributesMap &at
{ {
bool returnvalue = true; bool returnvalue = true;
if ( mIsQuery || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || !conn )
return false; return false;
if ( attr_map.isEmpty() ) if ( attr_map.isEmpty() )
return true; return true;
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
try try
{ {
@ -2037,11 +2051,11 @@ void QgsOracleProvider::appendGeomParam( const QgsGeometry &geom, QSqlQuery &qry
bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map ) bool QgsOracleProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
{ {
QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mIsQuery || mGeometryColumn.isNull() || !mConnection ) if ( mIsQuery || mGeometryColumn.isNull() || !conn )
return false; return false;
QSqlDatabase db( *mConnection ); QSqlDatabase db( *conn );
bool returnvalue = true; bool returnvalue = true;
@ -2104,7 +2118,8 @@ QgsVectorDataProvider::Capabilities QgsOracleProvider::capabilities() const
bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount ) bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
{ {
if ( !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !conn )
return false; return false;
if ( theSQL.trimmed() == mSqlWhereClause ) if ( theSQL.trimmed() == mSqlWhereClause )
@ -2123,7 +2138,7 @@ bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatu
sql += "1=0"; sql += "1=0";
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !exec( qry, sql, QVariantList() ) ) if ( !exec( qry, sql, QVariantList() ) )
{ {
pushError( qry.lastError().text() ); pushError( qry.lastError().text() );
@ -2161,7 +2176,8 @@ bool QgsOracleProvider::setSubsetString( const QString &theSQL, bool updateFeatu
*/ */
long QgsOracleProvider::featureCount() const long QgsOracleProvider::featureCount() const
{ {
if ( mFeaturesCounted >= 0 || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mFeaturesCounted >= 0 || !conn )
return mFeaturesCounted; return mFeaturesCounted;
// get total number of features // get total number of features
@ -2186,7 +2202,7 @@ long QgsOracleProvider::featureCount() const
} }
} }
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( exec( qry, sql, args ) && qry.next() ) if ( exec( qry, sql, args ) && qry.next() )
{ {
mFeaturesCounted = qry.value( 0 ).toInt(); mFeaturesCounted = qry.value( 0 ).toInt();
@ -2200,13 +2216,14 @@ long QgsOracleProvider::featureCount() const
QgsRectangle QgsOracleProvider::extent() const QgsRectangle QgsOracleProvider::extent() const
{ {
if ( mGeometryColumn.isNull() || !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( mGeometryColumn.isNull() || !conn )
return QgsRectangle(); return QgsRectangle();
if ( mLayerExtent.isEmpty() ) if ( mLayerExtent.isEmpty() )
{ {
QString sql; QString sql;
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
bool ok = false; bool ok = false;
if ( !mIsQuery ) if ( !mIsQuery )
@ -2285,7 +2302,8 @@ bool QgsOracleProvider::getGeometryDetails()
QString tableName = mTableName; QString tableName = mTableName;
QString geomCol = mGeometryColumn; QString geomCol = mGeometryColumn;
QSqlQuery qry( *mConnection ); QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
QSqlQuery qry( *conn );
if ( mIsQuery ) if ( mIsQuery )
{ {
if ( !exec( qry, QString( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ), QVariantList() ) ) if ( !exec( qry, QString( "SELECT %1 FROM %2 WHERE 1=0" ).arg( quotedIdentifier( mGeometryColumn ) ).arg( mQuery ), QVariantList() ) )
@ -2385,7 +2403,7 @@ bool QgsOracleProvider::getGeometryDetails()
delim = " AND "; delim = " AND ";
} }
mConnection->retrieveLayerTypes( layerProperty, mUseEstimatedMetadata, false ); conn->retrieveLayerTypes( layerProperty, mUseEstimatedMetadata, false );
Q_ASSERT( layerProperty.types.size() == layerProperty.srids.size() ); Q_ASSERT( layerProperty.types.size() == layerProperty.srids.size() );
} }
@ -2461,10 +2479,11 @@ bool QgsOracleProvider::getGeometryDetails()
bool QgsOracleProvider::createSpatialIndex() bool QgsOracleProvider::createSpatialIndex()
{ {
if ( !mConnection ) QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !conn )
return false; return false;
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
if ( !crs().isGeographic() ) if ( !crs().isGeographic() )
{ {
@ -2971,11 +2990,11 @@ QgsVectorLayerExporter::ExportError QgsOracleProvider::createEmptyLayer(
QgsCoordinateReferenceSystem QgsOracleProvider::crs() const QgsCoordinateReferenceSystem QgsOracleProvider::crs() const
{ {
QgsCoordinateReferenceSystem srs; QgsCoordinateReferenceSystem srs;
QgsOracleConn *conn = QgsOracleConn::connectDb( mUri );
if ( !mConnection ) if ( !conn )
return srs; return srs;
QSqlQuery qry( *mConnection ); QSqlQuery qry( *conn );
// apparently some EPSG codes don't have the auth_name setup in cs_srs // apparently some EPSG codes don't have the auth_name setup in cs_srs
if ( exec( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << mSrid ) ) if ( exec( qry, QString( "SELECT coalesce(auth_name,'EPSG'),auth_srid,wktext FROM mdsys.cs_srs WHERE srid=?" ), QVariantList() << mSrid ) )

View File

@ -304,8 +304,7 @@ class QgsOracleProvider : public QgsVectorDataProvider
static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ) { return QgsOracleConn::quotedValue( value, type ); } static QString quotedValue( const QVariant &value, QVariant::Type type = QVariant::Invalid ) { return QgsOracleConn::quotedValue( value, type ); }
QMap<QVariant, QgsFeatureId> mKeyToFid; //!< Map key values to feature id QMap<QVariant, QgsFeatureId> mKeyToFid; //!< Map key values to feature id
QMap<QgsFeatureId, QVariant> mFidToKey; //!< Map feature back to fea QMap<QgsFeatureId, QVariant> mFidToKey; //!< Map feature back to feature id
QgsOracleConn *mConnection = nullptr;
bool mHasSpatialIndex; //!< Geometry column is indexed bool mHasSpatialIndex; //!< Geometry column is indexed
QString mSpatialIndexName; //!< Name of spatial index of geometry column QString mSpatialIndexName; //!< Name of spatial index of geometry column