Explorar o código

Use thread-local database connections

Qt only allows a single database connection per thread.
Stefan Dinkelacker %!s(int64=7) %!d(string=hai) anos
pai
achega
0fb2b8d27b

+ 85 - 95
Libs/PluginFramework/ctkPluginStorageSQL.cpp

@@ -32,6 +32,7 @@
 
 #include <QFileInfo>
 #include <QUrl>
+#include <QThread>
 
 //database table names
 #define PLUGINS_TABLE "Plugins"
@@ -52,8 +53,7 @@ enum TBindIndexes
 
 //----------------------------------------------------------------------------
 ctkPluginStorageSQL::ctkPluginStorageSQL(ctkPluginFrameworkContext *framework)
-  : m_isDatabaseOpen(false)
-  , m_framework(framework)
+  : m_framework(framework)
   , m_nextFreeId(-1)
 {
   // See if we have a storage database
@@ -70,11 +70,52 @@ ctkPluginStorageSQL::~ctkPluginStorageSQL()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginStorageSQL::open()
+QSqlDatabase ctkPluginStorageSQL::getConnection(bool create) const
 {
-  if (m_isDatabaseOpen)
-    return;
+  if (m_connectionNames.hasLocalData() && QSqlDatabase::contains(m_connectionNames.localData()))
+  {
+    return QSqlDatabase::database(m_connectionNames.localData());
+  }
+
+  if (!create)
+  {
+    throw ctkPluginDatabaseException(QString("No database connection."),
+      ctkPluginDatabaseException::DB_NOT_OPEN_ERROR);
+  }
+
+  m_connectionNames.setLocalData(getConnectionName());
+
+  QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", m_connectionNames.localData());
+  database.setDatabaseName(getDatabasePath());
+
+  if (!database.isValid())
+  {
+    close();
+    throw ctkPluginDatabaseException(QString("Invalid database connection: %1").arg(m_connectionNames.localData()),
+      ctkPluginDatabaseException::DB_CONNECTION_INVALID);
+  }
+
+  if (!database.open())
+  {
+    close();
+    throw ctkPluginDatabaseException(QString("Could not open database connection: %1 (%2)").arg(m_connectionNames.localData()).arg(database.lastError().text()),
+      ctkPluginDatabaseException::DB_SQL_ERROR);
+  }
 
+  return database;
+}
+
+//----------------------------------------------------------------------------
+QString ctkPluginStorageSQL::getConnectionName() const
+{
+  QString connectionName = QFileInfo(getDatabasePath()).completeBaseName();
+  connectionName += QString("_0x%1").arg(reinterpret_cast<quintptr>(QThread::currentThread()), 2 * QT_POINTER_SIZE, 16, QLatin1Char('0'));
+  return connectionName;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::open()
+{
   QString path;
 
   //Create full path to database
@@ -93,36 +134,7 @@ void ctkPluginStorageSQL::open()
     }
   }
 
-  m_connectionName = dbFileInfo.completeBaseName();
-  QSqlDatabase database;
-  if (QSqlDatabase::contains(m_connectionName))
-  {
-    database = QSqlDatabase::database(m_connectionName);
-  }
-  else
-  {
-    database = QSqlDatabase::addDatabase("QSQLITE", m_connectionName);
-    database.setDatabaseName(path);
-  }
-
-  if (!database.isValid())
-  {
-    close();
-    throw ctkPluginDatabaseException(QString("Invalid database connection: %1").arg(m_connectionName),
-                                  ctkPluginDatabaseException::DB_CONNECTION_INVALID);
-  }
-
-  //Create or open database
-  if (!database.isOpen())
-  {
-    if (!database.open())
-    {
-      close();
-      throw ctkPluginDatabaseException(QString("Could not open database. ") + database.lastError().text(),
-                                    ctkPluginDatabaseException::DB_SQL_ERROR);
-    }
-  }
-  m_isDatabaseOpen = true;
+  QSqlDatabase database = getConnection();
 
   //Check if the sqlite version supports foreign key constraints
   QSqlQuery query(database);
@@ -181,9 +193,7 @@ void ctkPluginStorageSQL::open()
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::initNextFreeIds()
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   QString statement = "SELECT ID,MAX(Generation) FROM " PLUGINS_TABLE " GROUP BY ID";
@@ -214,9 +224,7 @@ void ctkPluginStorageSQL::initNextFreeIds()
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::cleanupDB()
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
@@ -243,9 +251,7 @@ void ctkPluginStorageSQL::cleanupDB()
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::updateDB()
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
@@ -387,9 +393,7 @@ QSharedPointer<ctkPluginArchive> ctkPluginStorageSQL::insertPlugin(const QUrl& l
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::insertArchive(QSharedPointer<ctkPluginArchiveSQL> pa)
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
@@ -515,9 +519,7 @@ void ctkPluginStorageSQL::replacePluginArchive(QSharedPointer<ctkPluginArchive>
     throw ctkRuntimeException(QString("replacePluginArchive: Old plugin archive not found, pos=").append(pos));
   }
 
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
@@ -546,9 +548,7 @@ void ctkPluginStorageSQL::replacePluginArchive(QSharedPointer<ctkPluginArchive>
 //----------------------------------------------------------------------------
 bool ctkPluginStorageSQL::removeArchive(ctkPluginArchiveSQL* pa)
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
@@ -617,9 +617,7 @@ QList<QString> ctkPluginStorageSQL::getStartOnLaunchPlugins() const
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::setStartLevel(int key, int startLevel)
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   QString statement = "UPDATE " PLUGINS_TABLE " SET StartLevel=? WHERE K=?";
@@ -633,9 +631,7 @@ void ctkPluginStorageSQL::setStartLevel(int key, int startLevel)
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::setLastModified(int key, const QDateTime& lastModified)
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   QString statement = "UPDATE " PLUGINS_TABLE " SET LastModified=? WHERE K=?";
@@ -649,9 +645,7 @@ void ctkPluginStorageSQL::setLastModified(int key, const QDateTime& lastModified
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::setAutostartSetting(int key, int autostart)
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   QString statement = "UPDATE " PLUGINS_TABLE " SET AutoStart=? WHERE K=?";
@@ -665,7 +659,8 @@ void ctkPluginStorageSQL::setAutostartSetting(int key, int autostart)
 //----------------------------------------------------------------------------
 QStringList ctkPluginStorageSQL::findResourcesPath(int archiveKey, const QString& path) const
 {
-  checkConnection();
+  QSqlDatabase database = getConnection();
+  QSqlQuery query(database);
 
   QString statement = "SELECT SUBSTR(ResourcePath,?) FROM PluginResources WHERE K=? AND SUBSTR(ResourcePath,1,?)=?";
 
@@ -679,9 +674,6 @@ QStringList ctkPluginStorageSQL::findResourcesPath(int archiveKey, const QString
   bindValues.append(resourcePath.size());
   bindValues.append(resourcePath);
 
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
-  QSqlQuery query(database);
-
   executeQuery(&query, statement, bindValues);
 
   QSet<QString> paths;
@@ -765,21 +757,26 @@ void ctkPluginStorageSQL::executeQuery(QSqlQuery *query, const QString &statemen
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::close()
 {
-  if (m_isDatabaseOpen)
+  const_cast<const ctkPluginStorageSQL*>(this)->close();
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::close() const
+{
+  if (isOpen())
   {
-    QSqlDatabase database = QSqlDatabase::database(m_connectionName, false);
+    QSqlDatabase database = getConnection(false);
     if (database.isValid())
     {
       if(database.isOpen())
       {
         database.close();
-        m_isDatabaseOpen = false;
         return;
       }
     }
     else
     {
-      throw ctkPluginDatabaseException(QString("Problem closing database: Invalid connection %1").arg(m_connectionName));
+      throw ctkPluginDatabaseException(QString("Problem closing database: Invalid connection %1").arg(m_connectionNames.localData()));
     }
   }
 }
@@ -808,9 +805,7 @@ QString ctkPluginStorageSQL::getDatabasePath() const
 //----------------------------------------------------------------------------
 QByteArray ctkPluginStorageSQL::getPluginResource(int key, const QString& res) const
 {
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
 
   QString statement = "SELECT Resource FROM PluginResources WHERE K=? AND ResourcePath=?";
@@ -833,7 +828,7 @@ QByteArray ctkPluginStorageSQL::getPluginResource(int key, const QString& res) c
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::createTables()
 {
-    QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+    QSqlDatabase database = getConnection();
     QSqlQuery query(database);
 
     //Begin Transaction
@@ -891,8 +886,10 @@ void ctkPluginStorageSQL::createTables()
 //----------------------------------------------------------------------------
 bool ctkPluginStorageSQL::checkTables() const
 {
+  QSqlDatabase database = getConnection();
+
   bool bTables(false);
-  QStringList tables = QSqlDatabase::database(m_connectionName).tables();
+  QStringList tables = database.tables();
   if (tables.contains(PLUGINS_TABLE) &&
       tables.contains(PLUGIN_RESOURCES_TABLE))
   {
@@ -905,7 +902,7 @@ bool ctkPluginStorageSQL::checkTables() const
 bool ctkPluginStorageSQL::dropTables()
 {
   //Execute transaction for deleting the database tables
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlDatabase database = getConnection();
   QSqlQuery query(database);
   QStringList expectedTables;
   expectedTables << PLUGINS_TABLE << PLUGIN_RESOURCES_TABLE;
@@ -946,7 +943,16 @@ bool ctkPluginStorageSQL::dropTables()
 //----------------------------------------------------------------------------
 bool ctkPluginStorageSQL::isOpen() const
 {
-  return m_isDatabaseOpen;
+  try
+  {
+    getConnection(false);
+  }
+  catch (const ctkPluginDatabaseException&)
+  {
+    return false;
+  }
+
+  return true;
 }
 
 int ctkPluginStorageSQL::find(long id) const
@@ -989,21 +995,6 @@ int ctkPluginStorageSQL::find(ctkPluginArchive *pa) const
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginStorageSQL::checkConnection() const
-{
-  if(!m_isDatabaseOpen)
-  {
-    throw ctkPluginDatabaseException("Database not open.", ctkPluginDatabaseException::DB_NOT_OPEN_ERROR);
-  }
-
-  if (!QSqlDatabase::database(m_connectionName).isValid())
-  {
-    throw ctkPluginDatabaseException(QString("Database connection invalid: %1").arg(m_connectionName),
-                                  ctkPluginDatabaseException::DB_CONNECTION_INVALID);
-  }
-}
-
-//----------------------------------------------------------------------------
 void ctkPluginStorageSQL::beginTransaction(QSqlQuery *query, TransactionType type)
 {
   bool success;
@@ -1061,9 +1052,8 @@ void ctkPluginStorageSQL::rollbackTransaction(QSqlQuery *query)
 //----------------------------------------------------------------------------
 void ctkPluginStorageSQL::restorePluginArchives()
 {
-  checkConnection();
-
-  QSqlQuery query(QSqlDatabase::database(m_connectionName));
+  QSqlDatabase database = getConnection();
+  QSqlQuery query(database);
   QString statement = "SELECT ID, Location, LocalPath, StartLevel, LastModified, AutoStart, K, MAX(Generation)"
                       " FROM " PLUGINS_TABLE " WHERE StartLevel != -2 GROUP BY ID"
                       " ORDER BY ID";

+ 15 - 5
Libs/PluginFramework/ctkPluginStorageSQL_p.h

@@ -31,6 +31,7 @@
 #include <QSqlError>
 #include <QPluginLoader>
 #include <QDirIterator>
+#include <QThreadStorage>
 
 // CTK class forward declarations
 class ctkPluginFrameworkContext;
@@ -117,7 +118,8 @@ public:
    *
    * @throws ctkPluginDatabaseException
    */
-  void close();
+  void close(); // Satisfy abstract interface
+  void close() const;
 
   // -------------------------------------------------------------
   // end ctkPluginStorage interface
@@ -263,11 +265,20 @@ private:
   bool checkTables() const;
 
   /**
-   * Checks the database connection.
+   * Creates or returns an existing, thread-local database connection.
    *
+   * @param open Create and open connection.
+   * @return Database connection.
    * @throws ctkPluginDatabaseException
    */
-  void checkConnection() const;
+  QSqlDatabase getConnection(bool create = true) const;
+
+  /**
+   * Creates a thread-unique database connection name.
+   *
+   * @return Database connection name.
+   */
+  QString getConnectionName() const;
 
   /**
    * Compares the persisted plugin modification time with the
@@ -330,8 +341,7 @@ private:
 
 
   QString m_databasePath;
-  QString m_connectionName;
-  bool m_isDatabaseOpen;
+  mutable QThreadStorage<QString> m_connectionNames;
 
   QMutex m_archivesLock;