Skip to content

Commit

Permalink
This refactoring job is related to qgis/QGIS-Enhancement-Proposals#272
Browse files Browse the repository at this point in the history
…and the original pull request for the AWS Redshift driver qgis#53018.

  - Move qgsConnectionPool_[connectionCreate|connectionDestroy|invalidateConnection|connectionIsValid|connectionToName] into their appropriate abstract classes and declare them as pure virual.
  - Implement the destructor and the functions above for all providers.
  - Refactor QgsPostgresConnPool[Group] to QgsSQLConnPool[Group], in order to reuse the template for future SQL based providers (AWS Redshift).
  • Loading branch information
Alexey Karandashev committed Jul 28, 2023
1 parent fbb9e08 commit 78ef09d
Show file tree
Hide file tree
Showing 12 changed files with 426 additions and 338 deletions.
43 changes: 43 additions & 0 deletions src/core/providers/ogr/qgsogrconnpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,43 @@

#include "qgslogger.h"


void QgsOgrConnPoolGroup::connectionCreate( const QString &connectionInfo, QgsOgrConn *&connection )
{
connection = new QgsOgrConn;

const QVariantMap parts = QgsOgrProviderMetadata().decodeUri( connectionInfo );
const QString fullPath = parts.value( QStringLiteral( "vsiPrefix" ) ).toString()
+ parts.value( QStringLiteral( "path" ) ).toString()
+ parts.value( QStringLiteral( "vsiSuffix" ) ).toString();
const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
char **papszOpenOptions = nullptr;
for ( const QString &option : std::as_const( openOptions ) )
{
papszOpenOptions = CSLAddString( papszOpenOptions,
option.toUtf8().constData() );
}
connection->ds = QgsOgrProviderUtils::GDALOpenWrapper( fullPath.toUtf8().constData(), false, papszOpenOptions, nullptr );
CSLDestroy( papszOpenOptions );
connection->path = connectionInfo;
connection->valid = true;
}

void QgsOgrConnPoolGroup::connectionDestroy( QgsOgrConn *connection )
{
destroyOgrConn( connection );
}

void QgsOgrConnPoolGroup::invalidateConnection( QgsOgrConn *connection )
{
connection->valid = false;
}

bool QgsOgrConnPoolGroup::connectionIsValid( QgsOgrConn *connection )
{
return connection->valid;
}

QgsOgrConnPool *QgsOgrConnPool::sInstance = nullptr;

// static public
Expand All @@ -44,4 +81,10 @@ QgsOgrConnPool::~QgsOgrConnPool()
QgsDebugCall;
}

QString QgsOgrConnPool::connectionToName( QgsOgrConn *connection )
{
return connection->path;
}


///@endcond
80 changes: 28 additions & 52 deletions src/core/providers/ogr/qgsogrconnpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,48 +33,6 @@ struct QgsOgrConn
bool valid;
};

inline QString qgsConnectionPool_ConnectionToName( QgsOgrConn *c )
{
return c->path;
}

inline void qgsConnectionPool_ConnectionCreate( const QString &connInfo, QgsOgrConn *&c )
{
c = new QgsOgrConn;

const QVariantMap parts = QgsOgrProviderMetadata().decodeUri( connInfo );
const QString fullPath = parts.value( QStringLiteral( "vsiPrefix" ) ).toString()
+ parts.value( QStringLiteral( "path" ) ).toString()
+ parts.value( QStringLiteral( "vsiSuffix" ) ).toString();
const QStringList openOptions = parts.value( QStringLiteral( "openOptions" ) ).toStringList();
char **papszOpenOptions = nullptr;
for ( const QString &option : openOptions )
{
papszOpenOptions = CSLAddString( papszOpenOptions,
option.toUtf8().constData() );
}
c->ds = QgsOgrProviderUtils::GDALOpenWrapper( fullPath.toUtf8().constData(), false, papszOpenOptions, nullptr );
CSLDestroy( papszOpenOptions );
c->path = connInfo;
c->valid = true;
}

inline void qgsConnectionPool_ConnectionDestroy( QgsOgrConn *c )
{
QgsOgrProviderUtils::GDALCloseWrapper( c->ds );
delete c;
}

inline void qgsConnectionPool_InvalidateConnection( QgsOgrConn *c )
{
c->valid = false;
}

inline bool qgsConnectionPool_ConnectionIsValid( QgsOgrConn *c )
{
return c->valid;
}

class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroup<QgsOgrConn *>
{
Q_OBJECT
Expand All @@ -92,17 +50,35 @@ class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroup<QgsOgr
//! QgsOgrConnPoolGroup cannot be copied
QgsOgrConnPoolGroup &operator=( const QgsOgrConnPoolGroup &other ) = delete;

~QgsOgrConnPoolGroup() override
{
for ( const Item &item : std::as_const( mConnections ) )
{
destroyOgrConn( item.connection );
}
}

void connectionCreate( const QString &connectionInfo, QgsOgrConn *&connection ) override;
void connectionDestroy( QgsOgrConn *connection ) override;
void invalidateConnection( QgsOgrConn *connection ) override;
bool connectionIsValid( QgsOgrConn *connection ) override;
void ref() { ++mRefCount; }
bool unref()
{
Q_ASSERT( mRefCount > 0 );
return --mRefCount == 0;
}

inline void destroyOgrConn( QgsOgrConn *connection )
{
QgsOgrProviderUtils::GDALCloseWrapper( connection->ds );
delete connection;
}

protected slots:
void handleConnectionExpired() { onConnectionExpired(); }
void startExpirationTimer() { expirationTimer->start(); }
void stopExpirationTimer() { expirationTimer->stop(); }
void startExpirationTimer() { mExpirationTimer->start(); }
void stopExpirationTimer() { mExpirationTimer->stop(); }

private:
int mRefCount = 0;
Expand All @@ -113,7 +89,7 @@ class QgsOgrConnPoolGroup : public QObject, public QgsConnectionPoolGroup<QgsOgr
class QgsOgrConnPool : public QgsConnectionPool<QgsOgrConn *, QgsOgrConnPoolGroup>
{
public:

QString connectionToName( QgsOgrConn *connection ) override;
// NOTE: first call to this function initializes the
// singleton.
// WARNING: concurrent call from multiple threads may result
Expand All @@ -140,31 +116,31 @@ class QgsOgrConnPool : public QgsConnectionPool<QgsOgrConn *, QgsOgrConnPoolGrou

/**
* \brief Increases the reference count on the connection pool for the specified connection.
* \param connInfo The connection string.
* \param connectionInfo The connection string.
* \note
* Any user of the connection pool needs to increase the reference count
* before it acquires any connections and decrease the reference count after
* releasing all acquired connections to ensure that all open OGR handles
* are freed when and only when no one is using the pool anymore.
*/
void ref( const QString &connInfo )
void ref( const QString &connectionInfo )
{
mMutex.lock();
T_Groups::iterator it = mGroups.find( connInfo );
T_Groups::iterator it = mGroups.find( connectionInfo );
if ( it == mGroups.end() )
it = mGroups.insert( connInfo, new QgsOgrConnPoolGroup( connInfo ) );
it = mGroups.insert( connectionInfo, new QgsOgrConnPoolGroup( connectionInfo ) );
it.value()->ref();
mMutex.unlock();
}

/**
* \brief Decrease the reference count on the connection pool for the specified connection.
* \param connInfo The connection string.
* \param connectionInfo The connection string.
*/
void unref( const QString &connInfo )
void unref( const QString &connectionInfo )
{
mMutex.lock();
T_Groups::iterator it = mGroups.find( connInfo );
T_Groups::iterator it = mGroups.find( connectionInfo );
if ( it == mGroups.end() )
{
mMutex.unlock();
Expand Down
124 changes: 124 additions & 0 deletions src/core/providers/qgssqlconnpool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/***************************************************************************
qgssqlconnpool.h
---------------------
begin : January 2014
copyright : (C) 2014 by Martin Dobias
email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSSQLCONNPOOL_H
#define QGSSQLCONNPOOL_H
#define SIP_NO_FILE

#include "qgsconnectionpool.h"
#include "qgslogger.h"

/**
* \ingroup core
* \brief Template class for SQL based provider connection pool group.
* \note not available in Python bindings.
* \since QGIS 3.32
*/
template<typename T>
class QgsSqlConnectionPoolGroup : public QgsConnectionPoolGroup<T *>
{
public:

/**
* The descendants inherit from QObject and pass a self reference.
*/
explicit QgsSqlConnectionPoolGroup( const QString &name, QObject *qobject ) :
QgsConnectionPoolGroup<T*>( name )
{
QgsConnectionPoolGroup<T *>::initTimer( qobject );
}

using typename QgsConnectionPoolGroup<T *>::Item;
using QgsConnectionPoolGroup<T *>::mConnections;
~QgsSqlConnectionPoolGroup() override
{
for ( const Item &item : std::as_const( mConnections ) )
{
connectionDestroy( item.connection );
}
}
void connectionCreate( const QString &connectionInfo, T *&connection ) override
{
connection = T::connectDb( connectionInfo, true, false );
}

void connectionDestroy( T *connection ) override
{
connection->unref(); // will delete itself
}

void invalidateConnection( T * ) override {}

bool connectionIsValid( T * ) override
{
return true;
}
};

/**
* \ingroup core
* \brief Template class for SQL based provider connection pool.
* \note not available in Python bindings.
* \since QGIS 3.32
*/
template<typename T, typename T_Group>
class QgsSqlConnectionPool : public QgsConnectionPool<T *, T_Group>
{
public:

/**
* \returns the instance singleton \a sInstance.
*/
static QgsSqlConnectionPool<T, T_Group> *instance()
{
if ( !sInstance )
sInstance = new QgsSqlConnectionPool<T, T_Group>();
return sInstance;
}

/**
* Reset the global instance of the connection pool.
*/
static void cleanupInstance()
{
delete sInstance;
sInstance = nullptr;
}

QString connectionToName( T *connection ) override
{
return connection->connInfo();
}

protected:

/**
* Constructor/Destructor implementation for the sole purpose of debugging.
*/
QgsSqlConnectionPool<T, T_Group>() : QgsConnectionPool<T *, T_Group>()
{
QgsDebugCall;
}

~QgsSqlConnectionPool<T, T_Group>() override
{
QgsDebugCall;
}
private:
static inline QgsSqlConnectionPool<T, T_Group> *sInstance = nullptr;
};


#endif // QGSSQLCONNPOOL_H
Loading

0 comments on commit 78ef09d

Please sign in to comment.