changeset 226:a4918d57435c

DatabaseManager doesn't IDatabaseFactory anymore
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 02 Apr 2021 19:23:36 +0200
parents 94c9908e6aca
children 39cb02035f54
files Framework/Common/DatabaseManager.cpp Framework/Common/DatabaseManager.h Framework/Common/IDatabaseFactory.h Framework/MySQL/MySQLDatabase.cpp Framework/MySQL/MySQLDatabase.h Framework/Plugins/DatabaseBackendAdapterV2.cpp Framework/Plugins/DatabaseBackendAdapterV3.cpp Framework/Plugins/IDatabaseBackend.h Framework/Plugins/IndexBackend.cpp Framework/Plugins/IndexBackend.h Framework/Plugins/IndexUnitTests.h Framework/Plugins/StorageBackend.cpp Framework/Plugins/StorageBackend.h Framework/PostgreSQL/PostgreSQLDatabase.cpp Framework/PostgreSQL/PostgreSQLDatabase.h MySQL/Plugins/MySQLIndex.cpp MySQL/Plugins/MySQLIndex.h MySQL/Plugins/MySQLStorageArea.cpp MySQL/Plugins/MySQLStorageArea.h MySQL/Plugins/StoragePlugin.cpp MySQL/UnitTests/UnitTestsMain.cpp PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/PostgreSQLIndex.h PostgreSQL/Plugins/PostgreSQLStorageArea.cpp PostgreSQL/Plugins/PostgreSQLStorageArea.h PostgreSQL/Plugins/StoragePlugin.cpp PostgreSQL/UnitTests/PostgreSQLTests.cpp PostgreSQL/UnitTests/UnitTestsMain.cpp Resources/CMake/DatabasesFrameworkConfiguration.cmake SQLite/Plugins/SQLiteIndex.cpp SQLite/Plugins/SQLiteIndex.h SQLite/UnitTests/UnitTestsMain.cpp
diffstat 32 files changed, 482 insertions(+), 604 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Common/DatabaseManager.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Common/DatabaseManager.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -31,56 +31,6 @@
 
 namespace OrthancDatabases
 {
-  IDatabase& DatabaseManager::GetDatabase()
-  {
-    unsigned int maxConnectionRetries = 10;
-    unsigned int connectionRetryInterval = 5;
-    unsigned int count = 0;
-
-    factory_->GetConnectionRetriesParameters(maxConnectionRetries, connectionRetryInterval);
-      
-    while (database_.get() == NULL)
-    {
-      transaction_.reset(NULL);
-
-      try
-      {
-        database_.reset(factory_->Open());
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        if (e.GetErrorCode() == Orthanc::ErrorCode_DatabaseUnavailable)
-        {
-          count ++;
-
-          if (count <= maxConnectionRetries)
-          {
-            LOG(WARNING) << "Database is currently unavailable, retrying...";
-            boost::this_thread::sleep(boost::posix_time::seconds(connectionRetryInterval));
-            continue;
-          }
-          else
-          {
-            LOG(ERROR) << "Timeout when connecting to the database, giving up";
-          }
-        }
-
-        throw;
-      }
-    }
-
-    if (database_.get() == NULL ||
-        database_->GetDialect() != dialect_)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    else
-    {
-      return *database_;
-    }
-  }
-
-
   void DatabaseManager::Close()
   {
     LOG(TRACE) << "Closing the connection to the database";
@@ -200,15 +150,15 @@
   }
 
     
-  DatabaseManager::DatabaseManager(IDatabaseFactory* factory) :  // Takes ownership
-    factory_(factory)
+  DatabaseManager::DatabaseManager(IDatabase* database) :
+    database_(database)
   {
-    if (factory == NULL)
+    if (database == NULL)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
     }
 
-    dialect_ = factory->GetDialect();
+    dialect_ = database->GetDialect();
   }
 
   
--- a/Framework/Common/DatabaseManager.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Common/DatabaseManager.h	Fri Apr 02 19:23:36 2021 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "IDatabaseFactory.h"
+#include "IDatabase.h"
 #include "StatementLocation.h"
 
 #include <Compatibility.h>  // For std::unique_ptr<>
@@ -38,14 +38,16 @@
   private:
     typedef std::map<StatementLocation, IPrecompiledStatement*>  CachedStatements;
 
-    boost::recursive_mutex           mutex_;
-    std::unique_ptr<IDatabaseFactory>  factory_;
-    std::unique_ptr<IDatabase>         database_;
-    std::unique_ptr<ITransaction>      transaction_;
-    CachedStatements                 cachedStatements_;
-    Dialect                          dialect_;
+    boost::recursive_mutex         mutex_;
+    std::unique_ptr<IDatabase>     database_;
+    std::unique_ptr<ITransaction>  transaction_;
+    CachedStatements               cachedStatements_;
+    Dialect                        dialect_;
 
-    IDatabase& GetDatabase();
+    IDatabase& GetDatabase()
+    {
+      return *database_;
+    }
 
     void CloseIfUnavailable(Orthanc::ErrorCode e);
 
@@ -59,7 +61,7 @@
     void ReleaseImplicitTransaction();
 
   public:
-    explicit DatabaseManager(IDatabaseFactory* factory);  // Takes ownership
+    explicit DatabaseManager(IDatabase* database);  // Takes ownership
     
     ~DatabaseManager()
     {
@@ -71,11 +73,6 @@
       return dialect_;
     }
 
-    void Open()
-    {
-      GetDatabase();
-    }
-
     void Close();
     
     void StartTransaction(TransactionType type);
--- a/Framework/Common/IDatabaseFactory.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Common/IDatabaseFactory.h	Fri Apr 02 19:23:36 2021 +0200
@@ -32,10 +32,6 @@
     {
     }
 
-    virtual Dialect GetDialect() const = 0;
-
     virtual IDatabase* Open() = 0;
-
-    virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries, unsigned int& connectionRetryInterval) = 0;
   };
 }
--- a/Framework/MySQL/MySQLDatabase.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/MySQL/MySQLDatabase.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -21,11 +21,12 @@
 
 #include "MySQLDatabase.h"
 
+#include "../Common/ImplicitTransaction.h"
+#include "../Common/Integer64Value.h"
+#include "../Common/RetryDatabaseFactory.h"
 #include "MySQLResult.h"
 #include "MySQLStatement.h"
 #include "MySQLTransaction.h"
-#include "../Common/ImplicitTransaction.h"
-#include "../Common/Integer64Value.h"
 
 #include <Compatibility.h>  // For std::unique_ptr<>
 #include <Logging.h>
@@ -612,4 +613,33 @@
   {
     database_.ReleaseAdvisoryLock(lock_);
   }
+
+  
+  MySQLDatabase* MySQLDatabase::OpenDatabaseConnection(const MySQLParameters& parameters)
+  {
+    class Factory : public RetryDatabaseFactory
+    {
+    private:
+      const MySQLParameters&  parameters_;
+
+    protected:
+      virtual IDatabase* TryOpen()
+      {
+        std::unique_ptr<MySQLDatabase> db(new MySQLDatabase(parameters_));
+        db->Open();
+        return db.release();
+      }
+      
+    public:
+      Factory(const MySQLParameters& parameters) :
+        RetryDatabaseFactory(parameters.GetMaxConnectionRetries(),
+                             parameters.GetConnectionRetryInterval()),
+        parameters_(parameters)
+      {
+      }
+    };
+
+    Factory factory(parameters);
+    return dynamic_cast<MySQLDatabase*>(factory.Open());
+  }
 }
--- a/Framework/MySQL/MySQLDatabase.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/MySQL/MySQLDatabase.h	Fri Apr 02 19:23:36 2021 +0200
@@ -116,5 +116,7 @@
 
       ~TransientAdvisoryLock();
     };
+
+    static MySQLDatabase* OpenDatabaseConnection(const MySQLParameters& parameters);
   };
 }
--- a/Framework/Plugins/DatabaseBackendAdapterV2.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/DatabaseBackendAdapterV2.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -23,6 +23,8 @@
 #include "DatabaseBackendAdapterV2.h"
 #include "GlobalProperties.h"
 
+#include "IndexBackend.h"
+
 #include <OrthancException.h>
 
 #include <boost/thread/mutex.hpp>
@@ -78,8 +80,7 @@
 
       if (manager_.get() == NULL)
       {
-        manager_.reset(new DatabaseManager(backend_->CreateDatabaseFactory()));
-        manager_->Open();
+        manager_.reset(IndexBackend::CreateSingleDatabaseManager(*backend_));
       }
       else
       {
--- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -86,8 +86,7 @@
 
       if (manager_.get() == NULL)
       {
-        manager_.reset(new DatabaseManager(backend_->CreateDatabaseFactory()));
-        manager_->Open();
+        manager_.reset(IndexBackend::CreateSingleDatabaseManager(*backend_));
       }
       else
       {
--- a/Framework/Plugins/IDatabaseBackend.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/IDatabaseBackend.h	Fri Apr 02 19:23:36 2021 +0200
@@ -39,7 +39,10 @@
 
     virtual OrthancPluginContext* GetContext() = 0;
 
-    virtual IDatabaseFactory* CreateDatabaseFactory() = 0;
+    virtual IDatabase* OpenDatabaseConnection() = 0;
+
+    // This function is invoked once, even if multiple connections are open
+    virtual void ConfigureDatabase(IDatabase& database) = 0;
 
     virtual void SetOutputFactory(IDatabaseBackendOutput::IFactory* factory) = 0;
                         
--- a/Framework/Plugins/IndexBackend.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/IndexBackend.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -2308,4 +2308,12 @@
 #  endif
 #endif
   }
+
+
+  DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend)
+  {
+    std::unique_ptr<IDatabase> database(backend.OpenDatabaseConnection());
+    backend.ConfigureDatabase(*database);
+    return new DatabaseManager(database.release());
+  }
 }
--- a/Framework/Plugins/IndexBackend.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/IndexBackend.h	Fri Apr 02 19:23:36 2021 +0200
@@ -377,5 +377,7 @@
     static void Register(IndexBackend* backend);
 
     static void Finalize();
+
+    static DatabaseManager* CreateSingleDatabaseManager(IDatabaseBackend& backend);
   };
 }
--- a/Framework/Plugins/IndexUnitTests.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/IndexUnitTests.h	Fri Apr 02 19:23:36 2021 +0200
@@ -182,128 +182,127 @@
 
   db.SetOutputFactory(new DatabaseBackendAdapterV2::Factory(&context, NULL));
 
-  DatabaseManager manager(db.CreateDatabaseFactory());
-  manager.Open();
+  std::unique_ptr<DatabaseManager> manager(IndexBackend::CreateSingleDatabaseManager(db));
   
   std::unique_ptr<IDatabaseBackendOutput> output(db.CreateOutput());
 
   std::string s;
-  ASSERT_TRUE(db.LookupGlobalProperty(s, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion));
+  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion));
   ASSERT_EQ("6", s);
 
-  ASSERT_FALSE(db.LookupGlobalProperty(s, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
-  db.SetGlobalProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence, "Hello");
-  ASSERT_TRUE(db.LookupGlobalProperty(s, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
+  ASSERT_FALSE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
+  db.SetGlobalProperty(*manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence, "Hello");
+  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
   ASSERT_EQ("Hello", s);
-  db.SetGlobalProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence, "HelloWorld");
-  ASSERT_TRUE(db.LookupGlobalProperty(s, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
+  db.SetGlobalProperty(*manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence, "HelloWorld");
+  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_AnonymizationSequence));
   ASSERT_EQ("HelloWorld", s);
 
-  int64_t a = db.CreateResource(manager, "study", OrthancPluginResourceType_Study);
-  ASSERT_TRUE(db.IsExistingResource(manager, a));
-  ASSERT_FALSE(db.IsExistingResource(manager, a + 1));
+  int64_t a = db.CreateResource(*manager, "study", OrthancPluginResourceType_Study);
+  ASSERT_TRUE(db.IsExistingResource(*manager, a));
+  ASSERT_FALSE(db.IsExistingResource(*manager, a + 1));
 
   int64_t b;
   OrthancPluginResourceType t;
-  ASSERT_FALSE(db.LookupResource(b, t, manager, "world"));
-  ASSERT_TRUE(db.LookupResource(b, t, manager, "study"));
+  ASSERT_FALSE(db.LookupResource(b, t, *manager, "world"));
+  ASSERT_TRUE(db.LookupResource(b, t, *manager, "study"));
   ASSERT_EQ(a, b);
   ASSERT_EQ(OrthancPluginResourceType_Study, t);
   
-  b = db.CreateResource(manager, "series", OrthancPluginResourceType_Series);
+  b = db.CreateResource(*manager, "series", OrthancPluginResourceType_Series);
   ASSERT_NE(a, b);
 
-  ASSERT_EQ("study", db.GetPublicId(manager, a));
-  ASSERT_EQ("series", db.GetPublicId(manager, b));
-  ASSERT_EQ(OrthancPluginResourceType_Study, db.GetResourceType(manager, a));
-  ASSERT_EQ(OrthancPluginResourceType_Series, db.GetResourceType(manager, b));
+  ASSERT_EQ("study", db.GetPublicId(*manager, a));
+  ASSERT_EQ("series", db.GetPublicId(*manager, b));
+  ASSERT_EQ(OrthancPluginResourceType_Study, db.GetResourceType(*manager, a));
+  ASSERT_EQ(OrthancPluginResourceType_Series, db.GetResourceType(*manager, b));
 
-  db.AttachChild(manager, a, b);
+  db.AttachChild(*manager, a, b);
 
   int64_t c;
-  ASSERT_FALSE(db.LookupParent(c, manager, a));
-  ASSERT_TRUE(db.LookupParent(c, manager, b));
+  ASSERT_FALSE(db.LookupParent(c, *manager, a));
+  ASSERT_TRUE(db.LookupParent(c, *manager, b));
   ASSERT_EQ(a, c);
 
-  c = db.CreateResource(manager, "series2", OrthancPluginResourceType_Series);
-  db.AttachChild(manager, a, c);
+  c = db.CreateResource(*manager, "series2", OrthancPluginResourceType_Series);
+  db.AttachChild(*manager, a, c);
 
-  ASSERT_EQ(3u, db.GetAllResourcesCount(manager));
-  ASSERT_EQ(0u, db.GetResourcesCount(manager, OrthancPluginResourceType_Patient));
-  ASSERT_EQ(1u, db.GetResourcesCount(manager, OrthancPluginResourceType_Study));
-  ASSERT_EQ(2u, db.GetResourcesCount(manager, OrthancPluginResourceType_Series));
+  ASSERT_EQ(3u, db.GetAllResourcesCount(*manager));
+  ASSERT_EQ(0u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Patient));
+  ASSERT_EQ(1u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Study));
+  ASSERT_EQ(2u, db.GetResourcesCount(*manager, OrthancPluginResourceType_Series));
 
-  ASSERT_FALSE(db.GetParentPublicId(s, manager, a));
-  ASSERT_TRUE(db.GetParentPublicId(s, manager, b));  ASSERT_EQ("study", s);
-  ASSERT_TRUE(db.GetParentPublicId(s, manager, c));  ASSERT_EQ("study", s);
+  ASSERT_FALSE(db.GetParentPublicId(s, *manager, a));
+  ASSERT_TRUE(db.GetParentPublicId(s, *manager, b));  ASSERT_EQ("study", s);
+  ASSERT_TRUE(db.GetParentPublicId(s, *manager, c));  ASSERT_EQ("study", s);
 
   std::list<std::string> children;
-  db.GetChildren(children, manager, a);
+  db.GetChildren(children, *manager, a);
   ASSERT_EQ(2u, children.size());
-  db.GetChildren(children, manager, b);
+  db.GetChildren(children, *manager, b);
   ASSERT_EQ(0u, children.size());
-  db.GetChildren(children, manager, c);
+  db.GetChildren(children, *manager, c);
   ASSERT_EQ(0u, children.size());
 
   std::list<std::string> cp;
-  db.GetChildrenPublicId(cp, manager, a);
+  db.GetChildrenPublicId(cp, *manager, a);
   ASSERT_EQ(2u, cp.size());
   ASSERT_TRUE(cp.front() == "series" || cp.front() == "series2");
   ASSERT_TRUE(cp.back() == "series" || cp.back() == "series2");
   ASSERT_NE(cp.front(), cp.back());
 
   std::list<std::string> pub;
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Patient);
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Patient);
   ASSERT_EQ(0u, pub.size());
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Study);
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Study);
   ASSERT_EQ(1u, pub.size());
   ASSERT_EQ("study", pub.front());
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Series);
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Series);
   ASSERT_EQ(2u, pub.size());
   ASSERT_TRUE(pub.front() == "series" || pub.front() == "series2");
   ASSERT_TRUE(pub.back() == "series" || pub.back() == "series2");
   ASSERT_NE(pub.front(), pub.back());
 
   std::list<int64_t> ci;
-  db.GetChildrenInternalId(ci, manager, a);
+  db.GetChildrenInternalId(ci, *manager, a);
   ASSERT_EQ(2u, ci.size());
   ASSERT_TRUE(ci.front() == b || ci.front() == c);
   ASSERT_TRUE(ci.back() == b || ci.back() == c);
   ASSERT_NE(ci.front(), ci.back());
 
-  db.SetMetadata(manager, a, Orthanc::MetadataType_ModifiedFrom, "modified");
-  db.SetMetadata(manager, a, Orthanc::MetadataType_LastUpdate, "update2");
-  ASSERT_FALSE(db.LookupMetadata(s, manager, b, Orthanc::MetadataType_LastUpdate));
-  ASSERT_TRUE(db.LookupMetadata(s, manager, a, Orthanc::MetadataType_LastUpdate));
+  db.SetMetadata(*manager, a, Orthanc::MetadataType_ModifiedFrom, "modified");
+  db.SetMetadata(*manager, a, Orthanc::MetadataType_LastUpdate, "update2");
+  ASSERT_FALSE(db.LookupMetadata(s, *manager, b, Orthanc::MetadataType_LastUpdate));
+  ASSERT_TRUE(db.LookupMetadata(s, *manager, a, Orthanc::MetadataType_LastUpdate));
   ASSERT_EQ("update2", s);
-  db.SetMetadata(manager, a, Orthanc::MetadataType_LastUpdate, "update");
-  ASSERT_TRUE(db.LookupMetadata(s, manager, a, Orthanc::MetadataType_LastUpdate));
+  db.SetMetadata(*manager, a, Orthanc::MetadataType_LastUpdate, "update");
+  ASSERT_TRUE(db.LookupMetadata(s, *manager, a, Orthanc::MetadataType_LastUpdate));
   ASSERT_EQ("update", s);
 
   std::list<int32_t> md;
-  db.ListAvailableMetadata(md, manager, a);
+  db.ListAvailableMetadata(md, *manager, a);
   ASSERT_EQ(2u, md.size());
   ASSERT_TRUE(md.front() == Orthanc::MetadataType_ModifiedFrom || md.back() == Orthanc::MetadataType_ModifiedFrom);
   ASSERT_TRUE(md.front() == Orthanc::MetadataType_LastUpdate || md.back() == Orthanc::MetadataType_LastUpdate);
   std::string mdd;
-  ASSERT_TRUE(db.LookupMetadata(mdd, manager, a, Orthanc::MetadataType_ModifiedFrom));
+  ASSERT_TRUE(db.LookupMetadata(mdd, *manager, a, Orthanc::MetadataType_ModifiedFrom));
   ASSERT_EQ("modified", mdd);
-  ASSERT_TRUE(db.LookupMetadata(mdd, manager, a, Orthanc::MetadataType_LastUpdate));
+  ASSERT_TRUE(db.LookupMetadata(mdd, *manager, a, Orthanc::MetadataType_LastUpdate));
   ASSERT_EQ("update", mdd);
 
-  db.ListAvailableMetadata(md, manager, b);
+  db.ListAvailableMetadata(md, *manager, b);
   ASSERT_EQ(0u, md.size());
 
-  db.DeleteMetadata(manager, a, Orthanc::MetadataType_LastUpdate);
-  db.DeleteMetadata(manager, b, Orthanc::MetadataType_LastUpdate);
-  ASSERT_FALSE(db.LookupMetadata(s, manager, a, Orthanc::MetadataType_LastUpdate));
+  db.DeleteMetadata(*manager, a, Orthanc::MetadataType_LastUpdate);
+  db.DeleteMetadata(*manager, b, Orthanc::MetadataType_LastUpdate);
+  ASSERT_FALSE(db.LookupMetadata(s, *manager, a, Orthanc::MetadataType_LastUpdate));
 
-  db.ListAvailableMetadata(md, manager, a);
+  db.ListAvailableMetadata(md, *manager, a);
   ASSERT_EQ(1u, md.size());
   ASSERT_EQ(Orthanc::MetadataType_ModifiedFrom, md.front());
 
-  ASSERT_EQ(0u, db.GetTotalCompressedSize(manager));
-  ASSERT_EQ(0u, db.GetTotalUncompressedSize(manager));
+  ASSERT_EQ(0u, db.GetTotalCompressedSize(*manager));
+  ASSERT_EQ(0u, db.GetTotalUncompressedSize(*manager));
 
 
   std::list<int32_t> fc;
@@ -326,17 +325,17 @@
   a2.compressedSize = 4242;
   a2.compressedHash = "md5_2";
     
-  db.AddAttachment(manager, a, a1);
-  db.ListAvailableAttachments(fc, manager, a);
+  db.AddAttachment(*manager, a, a1);
+  db.ListAvailableAttachments(fc, *manager, a);
   ASSERT_EQ(1u, fc.size());
   ASSERT_EQ(Orthanc::FileContentType_Dicom, fc.front());
-  db.AddAttachment(manager, a, a2);
-  db.ListAvailableAttachments(fc, manager, a);
+  db.AddAttachment(*manager, a, a2);
+  db.ListAvailableAttachments(fc, *manager, a);
   ASSERT_EQ(2u, fc.size());
-  ASSERT_FALSE(db.LookupAttachment(*output, manager, b, Orthanc::FileContentType_Dicom));
+  ASSERT_FALSE(db.LookupAttachment(*output, *manager, b, Orthanc::FileContentType_Dicom));
 
-  ASSERT_EQ(4284u, db.GetTotalCompressedSize(manager));
-  ASSERT_EQ(4284u, db.GetTotalUncompressedSize(manager));
+  ASSERT_EQ(4284u, db.GetTotalCompressedSize(*manager));
+  ASSERT_EQ(4284u, db.GetTotalUncompressedSize(*manager));
 
   expectedAttachment.reset(new OrthancPluginAttachment);
   expectedAttachment->uuid = "uuid1";
@@ -346,7 +345,7 @@
   expectedAttachment->compressionType = Orthanc::CompressionType_None;
   expectedAttachment->compressedSize = 42;
   expectedAttachment->compressedHash = "md5_1";
-  ASSERT_TRUE(db.LookupAttachment(*output, manager, a, Orthanc::FileContentType_Dicom));
+  ASSERT_TRUE(db.LookupAttachment(*output, *manager, a, Orthanc::FileContentType_Dicom));
 
   expectedAttachment.reset(new OrthancPluginAttachment);
   expectedAttachment->uuid = "uuid2";
@@ -356,21 +355,21 @@
   expectedAttachment->compressionType = Orthanc::CompressionType_None;
   expectedAttachment->compressedSize = 4242;
   expectedAttachment->compressedHash = "md5_2";
-  ASSERT_TRUE(db.LookupAttachment(*output, manager, a, Orthanc::FileContentType_DicomAsJson));
+  ASSERT_TRUE(db.LookupAttachment(*output, *manager, a, Orthanc::FileContentType_DicomAsJson));
 
-  db.ListAvailableAttachments(fc, manager, b);
+  db.ListAvailableAttachments(fc, *manager, b);
   ASSERT_EQ(0u, fc.size());
-  db.DeleteAttachment(*output, manager, a, Orthanc::FileContentType_Dicom);
-  db.ListAvailableAttachments(fc, manager, a);
+  db.DeleteAttachment(*output, *manager, a, Orthanc::FileContentType_Dicom);
+  db.ListAvailableAttachments(fc, *manager, a);
   ASSERT_EQ(1u, fc.size());
   ASSERT_EQ(Orthanc::FileContentType_DicomAsJson, fc.front());
-  db.DeleteAttachment(*output, manager, a, Orthanc::FileContentType_DicomAsJson);
-  db.ListAvailableAttachments(fc, manager, a);
+  db.DeleteAttachment(*output, *manager, a, Orthanc::FileContentType_DicomAsJson);
+  db.ListAvailableAttachments(fc, *manager, a);
   ASSERT_EQ(0u, fc.size());
 
 
-  db.SetIdentifierTag(manager, a, 0x0010, 0x0020, "patient");
-  db.SetIdentifierTag(manager, a, 0x0020, 0x000d, "study");
+  db.SetIdentifierTag(*manager, a, 0x0010, 0x0020, "patient");
+  db.SetIdentifierTag(*manager, a, 0x0020, 0x000d, "study");
 
   expectedDicomTags.clear();
   expectedDicomTags.push_back(OrthancPluginDicomTag());
@@ -381,14 +380,14 @@
   expectedDicomTags.back().group = 0x0020;
   expectedDicomTags.back().element = 0x000d;
   expectedDicomTags.back().value = "study";
-  db.GetMainDicomTags(*output, manager, a);
+  db.GetMainDicomTags(*output, *manager, a);
 
 
-  db.LookupIdentifier(ci, manager, OrthancPluginResourceType_Study, 0x0010, 0x0020, 
+  db.LookupIdentifier(ci, *manager, OrthancPluginResourceType_Study, 0x0010, 0x0020, 
                       OrthancPluginIdentifierConstraint_Equal, "patient");
   ASSERT_EQ(1u, ci.size());
   ASSERT_EQ(a, ci.front());
-  db.LookupIdentifier(ci, manager, OrthancPluginResourceType_Study, 0x0010, 0x0020, 
+  db.LookupIdentifier(ci, *manager, OrthancPluginResourceType_Study, 0x0010, 0x0020, 
                       OrthancPluginIdentifierConstraint_Equal, "study");
   ASSERT_EQ(0u, ci.size());
 
@@ -403,67 +402,67 @@
   exp.studyInstanceUid = "study";
   exp.seriesInstanceUid = "series";
   exp.sopInstanceUid = "instance";
-  db.LogExportedResource(manager, exp);
+  db.LogExportedResource(*manager, exp);
 
   expectedExported.reset(new OrthancPluginExportedResource());
   *expectedExported = exp;
   expectedExported->seq = 1;
 
   bool done;
-  db.GetExportedResources(*output, done, manager, 0, 10);
+  db.GetExportedResources(*output, done, *manager, 0, 10);
   
 
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Patient); ASSERT_EQ(0u, pub.size());
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Study); ASSERT_EQ(1u, pub.size());
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Series); ASSERT_EQ(2u, pub.size());
-  db.GetAllPublicIds(pub, manager, OrthancPluginResourceType_Instance); ASSERT_EQ(0u, pub.size());
-  ASSERT_EQ(3u, db.GetAllResourcesCount(manager));
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Patient); ASSERT_EQ(0u, pub.size());
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Study); ASSERT_EQ(1u, pub.size());
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Series); ASSERT_EQ(2u, pub.size());
+  db.GetAllPublicIds(pub, *manager, OrthancPluginResourceType_Instance); ASSERT_EQ(0u, pub.size());
+  ASSERT_EQ(3u, db.GetAllResourcesCount(*manager));
 
-  ASSERT_EQ(0u, db.GetUnprotectedPatientsCount(manager));  // No patient was inserted
-  ASSERT_TRUE(db.IsExistingResource(manager, c));
+  ASSERT_EQ(0u, db.GetUnprotectedPatientsCount(*manager));  // No patient was inserted
+  ASSERT_TRUE(db.IsExistingResource(*manager, c));
 
   {
     // A transaction is needed here for MySQL, as it was not possible
     // to implement recursive deletion of resources using pure SQL
     // statements
-    manager.StartTransaction(TransactionType_ReadWrite);    
-    db.DeleteResource(*output, manager, c);
-    manager.CommitTransaction();
+    manager->StartTransaction(TransactionType_ReadWrite);    
+    db.DeleteResource(*output, *manager, c);
+    manager->CommitTransaction();
   }
   
-  ASSERT_FALSE(db.IsExistingResource(manager, c));
-  ASSERT_TRUE(db.IsExistingResource(manager, a));
-  ASSERT_TRUE(db.IsExistingResource(manager, b));
-  ASSERT_EQ(2u, db.GetAllResourcesCount(manager));
-  db.DeleteResource(*output, manager, a);
-  ASSERT_EQ(0u, db.GetAllResourcesCount(manager));
-  ASSERT_FALSE(db.IsExistingResource(manager, a));
-  ASSERT_FALSE(db.IsExistingResource(manager, b));
-  ASSERT_FALSE(db.IsExistingResource(manager, c));
+  ASSERT_FALSE(db.IsExistingResource(*manager, c));
+  ASSERT_TRUE(db.IsExistingResource(*manager, a));
+  ASSERT_TRUE(db.IsExistingResource(*manager, b));
+  ASSERT_EQ(2u, db.GetAllResourcesCount(*manager));
+  db.DeleteResource(*output, *manager, a);
+  ASSERT_EQ(0u, db.GetAllResourcesCount(*manager));
+  ASSERT_FALSE(db.IsExistingResource(*manager, a));
+  ASSERT_FALSE(db.IsExistingResource(*manager, b));
+  ASSERT_FALSE(db.IsExistingResource(*manager, c));
 
-  ASSERT_EQ(0u, db.GetAllResourcesCount(manager));
-  ASSERT_EQ(0u, db.GetUnprotectedPatientsCount(manager));
-  int64_t p1 = db.CreateResource(manager, "patient1", OrthancPluginResourceType_Patient);
-  int64_t p2 = db.CreateResource(manager, "patient2", OrthancPluginResourceType_Patient);
-  int64_t p3 = db.CreateResource(manager, "patient3", OrthancPluginResourceType_Patient);
-  ASSERT_EQ(3u, db.GetUnprotectedPatientsCount(manager));
+  ASSERT_EQ(0u, db.GetAllResourcesCount(*manager));
+  ASSERT_EQ(0u, db.GetUnprotectedPatientsCount(*manager));
+  int64_t p1 = db.CreateResource(*manager, "patient1", OrthancPluginResourceType_Patient);
+  int64_t p2 = db.CreateResource(*manager, "patient2", OrthancPluginResourceType_Patient);
+  int64_t p3 = db.CreateResource(*manager, "patient3", OrthancPluginResourceType_Patient);
+  ASSERT_EQ(3u, db.GetUnprotectedPatientsCount(*manager));
   int64_t r;
-  ASSERT_TRUE(db.SelectPatientToRecycle(r, manager));
+  ASSERT_TRUE(db.SelectPatientToRecycle(r, *manager));
   ASSERT_EQ(p1, r);
-  ASSERT_TRUE(db.SelectPatientToRecycle(r, manager, p1));
+  ASSERT_TRUE(db.SelectPatientToRecycle(r, *manager, p1));
   ASSERT_EQ(p2, r);
-  ASSERT_FALSE(db.IsProtectedPatient(manager, p1));
-  db.SetProtectedPatient(manager, p1, true);
-  ASSERT_TRUE(db.IsProtectedPatient(manager, p1));
-  ASSERT_TRUE(db.SelectPatientToRecycle(r, manager));
+  ASSERT_FALSE(db.IsProtectedPatient(*manager, p1));
+  db.SetProtectedPatient(*manager, p1, true);
+  ASSERT_TRUE(db.IsProtectedPatient(*manager, p1));
+  ASSERT_TRUE(db.SelectPatientToRecycle(r, *manager));
   ASSERT_EQ(p2, r);
-  db.SetProtectedPatient(manager, p1, false);
-  ASSERT_FALSE(db.IsProtectedPatient(manager, p1));
-  ASSERT_TRUE(db.SelectPatientToRecycle(r, manager));
+  db.SetProtectedPatient(*manager, p1, false);
+  ASSERT_FALSE(db.IsProtectedPatient(*manager, p1));
+  ASSERT_TRUE(db.SelectPatientToRecycle(r, *manager));
   ASSERT_EQ(p2, r);
-  db.DeleteResource(*output, manager, p2);
-  ASSERT_TRUE(db.SelectPatientToRecycle(r, manager, p3));
+  db.DeleteResource(*output, *manager, p2);
+  ASSERT_TRUE(db.SelectPatientToRecycle(r, *manager, p3));
   ASSERT_EQ(p1, r);
 
-  manager.Close();
+  manager->Close();
 }
--- a/Framework/Plugins/StorageBackend.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/StorageBackend.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -54,11 +54,35 @@
 
 namespace OrthancDatabases
 {
-  StorageBackend::StorageBackend(IDatabaseFactory* factory) :
-    manager_(factory)
+  void StorageBackend::SetDatabase(IDatabase* database)
   {
+    if (database == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else if (manager_.get() != NULL)
+    {
+      delete database;
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      manager_.reset(new DatabaseManager(database));
+    }
   }
-
+  
+  DatabaseManager& StorageBackend::GetManager()
+  {
+    if (manager_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *manager_;
+    }
+  }
+    
 
   void StorageBackend::Create(DatabaseManager::Transaction& transaction,
                               const std::string& uuid,
@@ -387,7 +411,6 @@
     {
       context_ = context;
       backend_.reset(backend);
-      backend_->GetManager().Open();
 
       bool hasLoadedV2 = false;
 
--- a/Framework/Plugins/StorageBackend.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/Plugins/StorageBackend.h	Fri Apr 02 19:23:36 2021 +0200
@@ -31,7 +31,7 @@
   class StorageBackend : public boost::noncopyable
   {
   private:
-    DatabaseManager   manager_;
+    std::unique_ptr<DatabaseManager>   manager_;
 
   public:
     class IFileContentVisitor : public boost::noncopyable
@@ -44,16 +44,13 @@
       virtual void Assign(const std::string& content) = 0;
     };
     
-    explicit StorageBackend(IDatabaseFactory* factory);
-
     virtual ~StorageBackend()
     {
     }
 
-    DatabaseManager& GetManager() 
-    {
-      return manager_;
-    }
+    void SetDatabase(IDatabase* database);  // Takes ownership
+
+    DatabaseManager& GetManager();
     
     // NB: These methods will always be invoked in mutual exclusion,
     // as having access to some "DatabaseManager::Transaction" implies
--- a/Framework/PostgreSQL/PostgreSQLDatabase.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/PostgreSQL/PostgreSQLDatabase.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -22,10 +22,11 @@
 #include "PostgreSQLIncludes.h"  // Must be the first
 #include "PostgreSQLDatabase.h"
 
+#include "../Common/ImplicitTransaction.h"
+#include "../Common/RetryDatabaseFactory.h"
 #include "PostgreSQLResult.h"
 #include "PostgreSQLStatement.h"
 #include "PostgreSQLTransaction.h"
-#include "../Common/ImplicitTransaction.h"
 
 #include <Logging.h>
 #include <OrthancException.h>
@@ -298,4 +299,33 @@
   {
     database_.ReleaseAdvisoryLock(lock_);
   }
+
+
+  PostgreSQLDatabase* PostgreSQLDatabase::OpenDatabaseConnection(const PostgreSQLParameters& parameters)
+  {
+    class Factory : public RetryDatabaseFactory
+    {
+    private:
+      const PostgreSQLParameters&  parameters_;
+
+    protected:
+      virtual IDatabase* TryOpen()
+      {
+        std::unique_ptr<PostgreSQLDatabase> db(new PostgreSQLDatabase(parameters_));
+        db->Open();
+        return db.release();
+      }
+      
+    public:
+      Factory(const PostgreSQLParameters& parameters) :
+        RetryDatabaseFactory(parameters.GetMaxConnectionRetries(),
+                             parameters.GetConnectionRetryInterval()),
+        parameters_(parameters)
+      {
+      }
+    };
+
+    Factory factory(parameters);
+    return dynamic_cast<PostgreSQLDatabase*>(factory.Open());
+  }
 }
--- a/Framework/PostgreSQL/PostgreSQLDatabase.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/Framework/PostgreSQL/PostgreSQLDatabase.h	Fri Apr 02 19:23:36 2021 +0200
@@ -89,5 +89,7 @@
 
       ~TransientAdvisoryLock();
     };
+
+    static PostgreSQLDatabase* OpenDatabaseConnection(const PostgreSQLParameters& parameters);
   };
 }
--- a/MySQL/Plugins/MySQLIndex.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/Plugins/MySQLIndex.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -36,6 +36,21 @@
 
 namespace OrthancDatabases
 {
+  MySQLIndex::MySQLIndex(OrthancPluginContext* context,
+                         const MySQLParameters& parameters) :
+    IndexBackend(context),
+    parameters_(parameters),
+    clearAll_(false)
+  {
+  }
+
+
+  IDatabase* MySQLIndex::OpenDatabaseConnection()
+  {
+    return MySQLDatabase::OpenDatabaseConnection(parameters_);
+  }
+
+
   static void ThrowCannotCreateTrigger()
   {
     LOG(ERROR) << "The MySQL user is not allowed to create triggers => 2 possible solutions:";
@@ -46,8 +61,11 @@
                                     "Need to fix the MySQL permissions for \"CREATE TRIGGER\"");
   }
   
-  IDatabase* MySQLIndex::OpenInternal()
+
+  void MySQLIndex::ConfigureDatabase(IDatabase& database)
   {
+    MySQLDatabase& db = dynamic_cast<MySQLDatabase&>(database);
+    
     uint32_t expectedVersion = 6;
 
     if (GetContext())   // "GetContext()" can possibly be NULL in the unit tests
@@ -74,13 +92,10 @@
       MySQLDatabase::ClearDatabase(parameters_);
     }
     
-    std::unique_ptr<MySQLDatabase> db(new MySQLDatabase(parameters_));
-
-    db->Open();
-    db->Execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", false);
+    db.Execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", false);
 
     {
-      MySQLDatabase::TransientAdvisoryLock lock(*db, MYSQL_LOCK_DATABASE_SETUP);
+      MySQLDatabase::TransientAdvisoryLock lock(db, MYSQL_LOCK_DATABASE_SETUP);
 
       /**
        * In a first transaction, we create the tables. Such a
@@ -97,19 +112,19 @@
        * https://groups.google.com/d/msg/orthanc-users/OCFFkm1qm0k/Mbroy8VWAQAJ
        **/      
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
         
-        db->Execute("ALTER DATABASE " + parameters_.GetDatabase() + 
+        db.Execute("ALTER DATABASE " + parameters_.GetDatabase() + 
                     " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", false);
 
         // This is the first table to be created
-        if (!db->DoesTableExist(t, "GlobalProperties"))
+        if (!db.DoesTableExist(t, "GlobalProperties"))
         {
           std::string query;
           
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::MYSQL_PREPARE_INDEX);
-          db->Execute(query, true);
+          db.Execute(query, true);
         }
 
         t.Commit();
@@ -126,25 +141,25 @@
       int version = 0;
 
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
 
         // This is the last table to be created
-        if (!db->DoesTableExist(t, "PatientRecyclingOrder"))
+        if (!db.DoesTableExist(t, "PatientRecyclingOrder"))
         {
           LOG(ERROR) << "Corrupted MySQL database";
           throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);        
         }
 
         // This is the last item to be created
-        if (!db->DoesTriggerExist(t, "PatientAdded"))
+        if (!db.DoesTriggerExist(t, "PatientAdded"))
         {
           ThrowCannotCreateTrigger();
         }
 
-        if (!LookupGlobalIntegerProperty(version, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion))
+        if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion))
         {
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
           version = expectedVersion;
         }
 
@@ -160,12 +175,12 @@
       int revision = 0;
 
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
 
-        if (!LookupGlobalIntegerProperty(revision, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
+        if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
         {
           revision = 1;
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
         }
 
         t.Commit();
@@ -173,72 +188,72 @@
 
       if (revision == 1)
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
         
         // The serialization of jobs as a global property can lead to
         // very long values => switch to the LONGTEXT type that can
         // store up to 4GB:
         // https://stackoverflow.com/a/13932834/881731
-        db->Execute("ALTER TABLE GlobalProperties MODIFY value LONGTEXT", false);
+        db.Execute("ALTER TABLE GlobalProperties MODIFY value LONGTEXT", false);
         
         revision = 2;
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
 
         t.Commit();
       }
 
       if (revision == 2)
       {        
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
 
         // Install the "GetLastChangeIndex" extension
         std::string query;
 
         Orthanc::EmbeddedResources::GetFileResource
           (query, Orthanc::EmbeddedResources::MYSQL_GET_LAST_CHANGE_INDEX);
-        db->Execute(query, true);
+        db.Execute(query, true);
 
-        if (!db->DoesTriggerExist(t, "ChangeAdded"))
+        if (!db.DoesTriggerExist(t, "ChangeAdded"))
         {
           ThrowCannotCreateTrigger();
         }
         
         revision = 3;
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
 
         t.Commit();
       }
       
       if (revision == 3)
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
 
         // Reconfiguration of "Metadata" from TEXT type (up to 64KB)
         // to the LONGTEXT type (up to 4GB). This might be important
         // for applications such as the Osimis Web viewer that stores
         // large amount of metadata.
         // http://book.orthanc-server.com/faq/features.html#central-registry-of-metadata-and-attachments
-        db->Execute("ALTER TABLE Metadata MODIFY value LONGTEXT", false);
+        db.Execute("ALTER TABLE Metadata MODIFY value LONGTEXT", false);
         
         revision = 4;
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
 
         t.Commit();
       }
       
       if (revision == 4)
       {
-        MySQLTransaction t(*db, TransactionType_ReadWrite);
+        MySQLTransaction t(db, TransactionType_ReadWrite);
         
         // Install the "CreateInstance" extension
         std::string query;
         
         Orthanc::EmbeddedResources::GetFileResource
           (query, Orthanc::EmbeddedResources::MYSQL_CREATE_INSTANCE);
-        db->Execute(query, true);
+        db.Execute(query, true);
         
         revision = 5;
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
 
         t.Commit();
       }
@@ -260,19 +275,8 @@
      **/
     if (parameters_.HasLock())
     {
-      db->AdvisoryLock(MYSQL_LOCK_INDEX);
+      db.AdvisoryLock(MYSQL_LOCK_INDEX);
     }
-          
-    return db.release();
-  }
-
-
-  MySQLIndex::MySQLIndex(OrthancPluginContext* context,
-                         const MySQLParameters& parameters) :
-    IndexBackend(context),
-    parameters_(parameters),
-    clearAll_(false)
-  {
   }
 
 
--- a/MySQL/Plugins/MySQLIndex.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/Plugins/MySQLIndex.h	Fri Apr 02 19:23:36 2021 +0200
@@ -29,39 +29,9 @@
   class MySQLIndex : public IndexBackend 
   {
   private:
-    class Factory : public IDatabaseFactory
-    {
-    private:
-      MySQLIndex&  that_;
-
-    public:
-      Factory(MySQLIndex& that) :
-      that_(that)
-      {
-      }
-
-      virtual Dialect GetDialect() const
-      {
-        return Dialect_MySQL;
-      }
-
-      virtual IDatabase* Open()
-      {
-        return that_.OpenInternal();
-      }
-
-      virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries, unsigned int& connectionRetryInterval)
-      {
-        maxConnectionRetries = that_.parameters_.GetMaxConnectionRetries();
-        connectionRetryInterval = that_.parameters_.GetConnectionRetryInterval();
-      }
-    };
-
     MySQLParameters        parameters_;
     bool                   clearAll_;
 
-    IDatabase* OpenInternal();
-
   public:
     MySQLIndex(OrthancPluginContext* context,
                const MySQLParameters& parameters);
@@ -71,11 +41,10 @@
       clearAll_ = clear;
     }
 
-    virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE
-    {
-      return new Factory(*this);
-    }
+    virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE;
 
+    virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE;
+ 
     virtual int64_t CreateResource(DatabaseManager& manager,
                                    const char* publicId,
                                    OrthancPluginResourceType type)
--- a/MySQL/Plugins/MySQLStorageArea.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/Plugins/MySQLStorageArea.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -33,18 +33,16 @@
 
 namespace OrthancDatabases
 {
-  IDatabase* MySQLStorageArea::OpenInternal()
+  void MySQLStorageArea::ConfigureDatabase(MySQLDatabase& db,
+                                           const MySQLParameters& parameters,
+                                           bool clearAll)
   {
-    std::unique_ptr<MySQLDatabase> db(new MySQLDatabase(parameters_));
-
-    db->Open();
-
     {
-      MySQLDatabase::TransientAdvisoryLock lock(*db, MYSQL_LOCK_DATABASE_SETUP);    
-      MySQLTransaction t(*db, TransactionType_ReadWrite);
+      MySQLDatabase::TransientAdvisoryLock lock(db, MYSQL_LOCK_DATABASE_SETUP);    
+      MySQLTransaction t(db, TransactionType_ReadWrite);
 
       int64_t size;
-      if (db->LookupGlobalIntegerVariable(size, "max_allowed_packet"))
+      if (db.LookupGlobalIntegerVariable(size, "max_allowed_packet"))
       {
         int mb = boost::math::iround(static_cast<double>(size) /
                                      static_cast<double>(1024 * 1024));
@@ -59,15 +57,15 @@
                      << "files that can be stored in this MySQL server";
       }
                
-      if (clearAll_)
+      if (clearAll)
       {
-        db->Execute("DROP TABLE IF EXISTS StorageArea", false);
+        db.Execute("DROP TABLE IF EXISTS StorageArea", false);
       }
 
-      db->Execute("CREATE TABLE IF NOT EXISTS StorageArea("
-                  "uuid VARCHAR(64) NOT NULL PRIMARY KEY,"
-                  "content LONGBLOB NOT NULL,"
-                  "type INTEGER NOT NULL)", false);
+      db.Execute("CREATE TABLE IF NOT EXISTS StorageArea("
+                 "uuid VARCHAR(64) NOT NULL PRIMARY KEY,"
+                 "content LONGBLOB NOT NULL,"
+                 "type INTEGER NOT NULL)", false);
 
       t.Commit();
     }
@@ -80,19 +78,24 @@
      * previously-acquired locks.
      * https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html
      **/
-    if (parameters_.HasLock())
+    if (parameters.HasLock())
     {
-      db->AdvisoryLock(MYSQL_LOCK_STORAGE);
+      db.AdvisoryLock(MYSQL_LOCK_STORAGE);
     }
-
-    return db.release();
   }
 
 
-  MySQLStorageArea::MySQLStorageArea(const MySQLParameters& parameters) :
-    StorageBackend(new Factory(*this)),
-    parameters_(parameters),
-    clearAll_(false)
+  MySQLStorageArea::MySQLStorageArea(const MySQLParameters& parameters,
+                                     bool clearAll)
   {
+    std::unique_ptr<MySQLDatabase> database(MySQLDatabase::OpenDatabaseConnection(parameters));
+    
+    if (database.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    
+    ConfigureDatabase(*database, parameters, clearAll);
+    SetDatabase(database.release());
   }
 }
--- a/MySQL/Plugins/MySQLStorageArea.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/Plugins/MySQLStorageArea.h	Fri Apr 02 19:23:36 2021 +0200
@@ -22,7 +22,7 @@
 #pragma once
 
 #include "../../Framework/Plugins/StorageBackend.h"
-#include "../../Framework/MySQL/MySQLParameters.h"
+#include "../../Framework/MySQL/MySQLDatabase.h"
 
 
 namespace OrthancDatabases
@@ -30,46 +30,12 @@
   class MySQLStorageArea : public StorageBackend
   {
   private:
-    class Factory : public IDatabaseFactory
-    {
-    private:
-      MySQLStorageArea&  that_;
-
-    public:
-      explicit Factory(MySQLStorageArea& that) :
-        that_(that)
-      {
-      }
-
-      virtual Dialect GetDialect() const ORTHANC_OVERRIDE
-      {
-        return Dialect_MySQL;
-      }
-
-      virtual IDatabase* Open() ORTHANC_OVERRIDE
-      {
-        return that_.OpenInternal();
-      }
-
-      virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries,
-                                                  unsigned int& connectionRetryInterval) ORTHANC_OVERRIDE
-      {
-        maxConnectionRetries = that_.parameters_.GetMaxConnectionRetries();
-        connectionRetryInterval = that_.parameters_.GetConnectionRetryInterval();
-      }
-    };
-
-    MySQLParameters        parameters_;
-    bool                   clearAll_;
-
-    IDatabase* OpenInternal();
-
+    void ConfigureDatabase(MySQLDatabase& db,
+                           const MySQLParameters& parameters,
+                           bool clearAll);
+    
   public:
-    explicit MySQLStorageArea(const MySQLParameters& parameters);
-
-    void SetClearAll(bool clear)
-    {
-      clearAll_ = clear;
-    }
+    MySQLStorageArea(const MySQLParameters& parameters,
+                     bool clearAll);
   };
 }
--- a/MySQL/Plugins/StoragePlugin.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/Plugins/StoragePlugin.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -64,7 +64,7 @@
     {
       OrthancDatabases::MySQLParameters parameters(mysql, configuration);
       OrthancDatabases::StorageBackend::Register
-        (context, new OrthancDatabases::MySQLStorageArea(parameters));
+        (context, new OrthancDatabases::MySQLStorageArea(parameters, false /* don't clear database */));
     }
     catch (Orthanc::OrthancException& e)
     {
--- a/MySQL/UnitTests/UnitTestsMain.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/MySQL/UnitTests/UnitTestsMain.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -50,22 +50,19 @@
   OrthancDatabases::MySQLIndex db1(NULL, noLock);
   db1.SetClearAll(true);
 
-  OrthancDatabases::DatabaseManager manager1(db1.CreateDatabaseFactory());
-  manager1.Open();
+  std::unique_ptr<OrthancDatabases::DatabaseManager> manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1));
 
   {
     OrthancDatabases::MySQLIndex db2(NULL, lock);
-    OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory());
-    manager2.Open();
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2));
 
     OrthancDatabases::MySQLIndex db3(NULL, lock);
-    OrthancDatabases::DatabaseManager manager3(db3.CreateDatabaseFactory());
-    ASSERT_THROW(manager3.Open(), Orthanc::OrthancException);
+    ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3), Orthanc::OrthancException);
+
   }
 
   OrthancDatabases::MySQLIndex db4(NULL, lock);
-  OrthancDatabases::DatabaseManager manager4(db4.CreateDatabaseFactory());
-  manager4.Open();
+  std::unique_ptr<OrthancDatabases::DatabaseManager> manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4));
 }
 
 
@@ -151,8 +148,7 @@
 
 TEST(MySQL, StorageArea)
 {
-  OrthancDatabases::MySQLStorageArea storageArea(globalParameters_);
-  storageArea.SetClearAll(true);
+  OrthancDatabases::MySQLStorageArea storageArea(globalParameters_, true);
 
   {
     OrthancDatabases::DatabaseManager::Transaction transaction(storageArea.GetManager(), OrthancDatabases::TransactionType_ReadWrite);
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -45,7 +45,22 @@
 
 namespace OrthancDatabases
 {
-  IDatabase* PostgreSQLIndex::OpenInternal()
+  PostgreSQLIndex::PostgreSQLIndex(OrthancPluginContext* context,
+                                   const PostgreSQLParameters& parameters) :
+    IndexBackend(context),
+    parameters_(parameters),
+    clearAll_(false)
+  {
+  }
+
+  
+  IDatabase* PostgreSQLIndex::OpenDatabaseConnection()
+  {
+    return PostgreSQLDatabase::OpenDatabaseConnection(parameters_);
+  }
+
+  
+  void PostgreSQLIndex::ConfigureDatabase(IDatabase& database)
   {
     uint32_t expectedVersion = 6;
 
@@ -63,47 +78,45 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
     }
 
-    std::unique_ptr<PostgreSQLDatabase> db(new PostgreSQLDatabase(parameters_));
-
-    db->Open();
+    PostgreSQLDatabase& db = dynamic_cast<PostgreSQLDatabase&>(database);
 
     if (parameters_.HasLock())
     {
-      db->AdvisoryLock(POSTGRESQL_LOCK_INDEX);
+      db.AdvisoryLock(POSTGRESQL_LOCK_INDEX);
     }
 
     {
-      PostgreSQLDatabase::TransientAdvisoryLock lock(*db, POSTGRESQL_LOCK_DATABASE_SETUP);
+      PostgreSQLDatabase::TransientAdvisoryLock lock(db, POSTGRESQL_LOCK_DATABASE_SETUP);
 
       if (clearAll_)
       {
-        db->ClearAll();
+        db.ClearAll();
       }
 
       {
-        PostgreSQLTransaction t(*db, TransactionType_ReadWrite);
+        PostgreSQLTransaction t(db, TransactionType_ReadWrite);
 
-        if (!db->DoesTableExist("Resources"))
+        if (!db.DoesTableExist("Resources"))
         {
           std::string query;
 
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::POSTGRESQL_PREPARE_INDEX);
-          db->Execute(query);
+          db.Execute(query);
 
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 0);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 0);
         }
           
-        if (!db->DoesTableExist("Resources"))
+        if (!db.DoesTableExist("Resources"))
         {
           LOG(ERROR) << "Corrupted PostgreSQL database";
           throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);        
         }
 
         int version = 0;
-        if (!LookupGlobalIntegerProperty(version, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) ||
+        if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) ||
             version != 6)
         {
           LOG(ERROR) << "PostgreSQL plugin is incompatible with database schema version: " << version;
@@ -111,10 +124,10 @@
         }
 
         int revision;
-        if (!LookupGlobalIntegerProperty(revision, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
+        if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
         {
           revision = 1;
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
         }
 
         if (revision != 1)
@@ -127,10 +140,10 @@
       }
 
       {
-        PostgreSQLTransaction t(*db, TransactionType_ReadWrite);
+        PostgreSQLTransaction t(db, TransactionType_ReadWrite);
 
         int hasTrigram = 0;
-        if (!LookupGlobalIntegerProperty(hasTrigram, *db, t, MISSING_SERVER_IDENTIFIER,
+        if (!LookupGlobalIntegerProperty(hasTrigram, db, t, MISSING_SERVER_IDENTIFIER,
                                          Orthanc::GlobalProperty_HasTrigramIndex) ||
             hasTrigram != 1)
         {
@@ -150,11 +163,11 @@
             LOG(WARNING) << "Trying to enable trigram matching on the PostgreSQL database "
                          << "to speed up wildcard searches. This may take several minutes";
 
-            db->Execute(
+            db.Execute(
               "CREATE EXTENSION IF NOT EXISTS pg_trgm; "
               "CREATE INDEX DicomIdentifiersIndexValues2 ON DicomIdentifiers USING gin(value gin_trgm_ops);");
 
-            SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 1);
+            SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasTrigramIndex, 1);
             LOG(WARNING) << "Trigram index has been created";
 
             t.Commit();
@@ -174,10 +187,10 @@
       }
 
       {
-        PostgreSQLTransaction t(*db, TransactionType_ReadWrite);
+        PostgreSQLTransaction t(db, TransactionType_ReadWrite);
 
         int property = 0;
-        if (!LookupGlobalIntegerProperty(property, *db, t, MISSING_SERVER_IDENTIFIER,
+        if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER,
                                          Orthanc::GlobalProperty_HasCreateInstance) ||
             property != 2)
         {
@@ -186,20 +199,20 @@
           if (property == 1)
           {
             // Drop older, experimental versions of this extension
-            db->Execute("DROP FUNCTION CreateInstance("
+            db.Execute("DROP FUNCTION CreateInstance("
                         "IN patient TEXT, IN study TEXT, IN series TEXT, in instance TEXT)");
           }
         
           std::string query;
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::POSTGRESQL_CREATE_INSTANCE);
-          db->Execute(query);
+          db.Execute(query);
 
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasCreateInstance, 2);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasCreateInstance, 2);
         }
 
       
-        if (!LookupGlobalIntegerProperty(property, *db, t, MISSING_SERVER_IDENTIFIER,
+        if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER,
                                          Orthanc::GlobalProperty_GetTotalSizeIsFast) ||
             property != 1)
         {
@@ -208,16 +221,16 @@
           std::string query;
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::POSTGRESQL_FAST_TOTAL_SIZE);
-          db->Execute(query);
+          db.Execute(query);
 
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetTotalSizeIsFast, 1);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetTotalSizeIsFast, 1);
         }
 
 
         // Installing this extension requires the "GlobalIntegers" table
         // created by the "FastTotalSize" extension
         property = 0;
-        if (!LookupGlobalIntegerProperty(property, *db, t, MISSING_SERVER_IDENTIFIER,
+        if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER,
                                          Orthanc::GlobalProperty_HasFastCountResources) ||
             property != 1)
         {
@@ -226,16 +239,16 @@
           std::string query;
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::POSTGRESQL_FAST_COUNT_RESOURCES);
-          db->Execute(query);
+          db.Execute(query);
 
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasFastCountResources, 1);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_HasFastCountResources, 1);
         }
 
 
         // Installing this extension requires the "GlobalIntegers" table
         // created by the "GetLastChangeIndex" extension
         property = 0;
-        if (!LookupGlobalIntegerProperty(property, *db, t, MISSING_SERVER_IDENTIFIER,
+        if (!LookupGlobalIntegerProperty(property, db, t, MISSING_SERVER_IDENTIFIER,
                                          Orthanc::GlobalProperty_GetLastChangeIndex) ||
             property != 1)
         {
@@ -244,28 +257,17 @@
           std::string query;
           Orthanc::EmbeddedResources::GetFileResource
             (query, Orthanc::EmbeddedResources::POSTGRESQL_GET_LAST_CHANGE_INDEX);
-          db->Execute(query);
+          db.Execute(query);
 
-          SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetLastChangeIndex, 1);
+          SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_GetLastChangeIndex, 1);
         }
 
         t.Commit();
       }
     }
-
-    return db.release();
   }
 
 
-  PostgreSQLIndex::PostgreSQLIndex(OrthancPluginContext* context,
-                                   const PostgreSQLParameters& parameters) :
-    IndexBackend(context),
-    parameters_(parameters),
-    clearAll_(false)
-  {
-  }
-
-  
   int64_t PostgreSQLIndex::CreateResource(DatabaseManager& manager,
                                           const char* publicId,
                                           OrthancPluginResourceType type)
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.h	Fri Apr 02 19:23:36 2021 +0200
@@ -29,39 +29,9 @@
   class PostgreSQLIndex : public IndexBackend 
   {
   private:
-    class Factory : public IDatabaseFactory
-    {
-    private:
-      PostgreSQLIndex&  that_;
-
-    public:
-      Factory(PostgreSQLIndex& that) :
-      that_(that)
-      {
-      }
-
-      virtual Dialect GetDialect() const
-      {
-        return Dialect_PostgreSQL;
-      }
-
-      virtual IDatabase* Open()
-      {
-        return that_.OpenInternal();
-      }
-
-      virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries, unsigned int& connectionRetryInterval)
-      {
-        maxConnectionRetries = that_.parameters_.GetMaxConnectionRetries();
-        connectionRetryInterval = that_.parameters_.GetConnectionRetryInterval();
-      }
-    };
-
     PostgreSQLParameters   parameters_;
     bool                   clearAll_;
 
-    IDatabase* OpenInternal();
-
   public:
     PostgreSQLIndex(OrthancPluginContext* context,
                     const PostgreSQLParameters& parameters);
@@ -71,10 +41,9 @@
       clearAll_ = clear;
     }
 
-    virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE
-    {
-      return new Factory(*this);
-    }
+    virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE;
+
+    virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE;
 
     virtual int64_t CreateResource(DatabaseManager& manager,
                                    const char* publicId,
--- a/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -31,52 +31,55 @@
 
 namespace OrthancDatabases
 {
-  IDatabase* PostgreSQLStorageArea::OpenInternal()
+  void PostgreSQLStorageArea::ConfigureDatabase(PostgreSQLDatabase& db,
+                                                const PostgreSQLParameters& parameters,
+                                                bool clearAll)
   {
-    std::unique_ptr<PostgreSQLDatabase> db(new PostgreSQLDatabase(parameters_));
-
-    db->Open();
-
-    if (parameters_.HasLock())
+    if (parameters.HasLock())
     {
-      db->AdvisoryLock(POSTGRESQL_LOCK_STORAGE);
+      db.AdvisoryLock(POSTGRESQL_LOCK_STORAGE);
     }
 
     {
-      PostgreSQLDatabase::TransientAdvisoryLock lock(*db, POSTGRESQL_LOCK_DATABASE_SETUP);
+      PostgreSQLDatabase::TransientAdvisoryLock lock(db, POSTGRESQL_LOCK_DATABASE_SETUP);
 
-      if (clearAll_)
+      if (clearAll)
       {
-        db->ClearAll();
+        db.ClearAll();
       }
 
       {
-        PostgreSQLTransaction t(*db, TransactionType_ReadWrite);
+        PostgreSQLTransaction t(db, TransactionType_ReadWrite);
 
-        if (!db->DoesTableExist("StorageArea"))
+        if (!db.DoesTableExist("StorageArea"))
         {
-          db->Execute("CREATE TABLE IF NOT EXISTS StorageArea("
-                      "uuid VARCHAR NOT NULL PRIMARY KEY,"
-                      "content OID NOT NULL,"
-                      "type INTEGER NOT NULL)");
-
+          db.Execute("CREATE TABLE IF NOT EXISTS StorageArea("
+                     "uuid VARCHAR NOT NULL PRIMARY KEY,"
+                     "content OID NOT NULL,"
+                     "type INTEGER NOT NULL)");
+          
           // Automatically remove the large objects associated with the table
-          db->Execute("CREATE OR REPLACE RULE StorageAreaDelete AS ON DELETE "
-                      "TO StorageArea DO SELECT lo_unlink(old.content);");
+          db.Execute("CREATE OR REPLACE RULE StorageAreaDelete AS ON DELETE "
+                     "TO StorageArea DO SELECT lo_unlink(old.content);");
         }
-
+        
         t.Commit();
       }
     }
-
-    return db.release();
   }
 
 
-  PostgreSQLStorageArea::PostgreSQLStorageArea(const PostgreSQLParameters& parameters) :
-    StorageBackend(new Factory(*this)),
-    parameters_(parameters),
-    clearAll_(false)
+  PostgreSQLStorageArea::PostgreSQLStorageArea(const PostgreSQLParameters& parameters,
+                                               bool clearAll)
   {
+    std::unique_ptr<PostgreSQLDatabase> database(PostgreSQLDatabase::OpenDatabaseConnection(parameters));
+    
+    if (database.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    
+    ConfigureDatabase(*database, parameters, clearAll);
+    SetDatabase(database.release());
   }
 }
--- a/PostgreSQL/Plugins/PostgreSQLStorageArea.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.h	Fri Apr 02 19:23:36 2021 +0200
@@ -22,53 +22,19 @@
 #pragma once
 
 #include "../../Framework/Plugins/StorageBackend.h"
-#include "../../Framework/PostgreSQL/PostgreSQLParameters.h"
+#include "../../Framework/PostgreSQL/PostgreSQLDatabase.h"
 
 namespace OrthancDatabases
 {
   class PostgreSQLStorageArea : public StorageBackend
   {
   private:
-    class Factory : public IDatabaseFactory
-    {
-    private:
-      PostgreSQLStorageArea&  that_;
-
-    public:
-      explicit Factory(PostgreSQLStorageArea& that) :
-        that_(that)
-      {
-      }
-
-      virtual Dialect GetDialect() const ORTHANC_OVERRIDE
-      {
-        return Dialect_PostgreSQL;
-      }
-
-      virtual IDatabase* Open() ORTHANC_OVERRIDE
-      {
-        return that_.OpenInternal();
-      }
-
-      virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries,
-                                                  unsigned int& connectionRetryInterval) ORTHANC_OVERRIDE
-      {
-        maxConnectionRetries = that_.parameters_.GetMaxConnectionRetries();
-        connectionRetryInterval = that_.parameters_.GetConnectionRetryInterval();
-      }
-    };
-
-    PostgreSQLParameters   parameters_;
-    bool                   clearAll_;
-
-    IDatabase* OpenInternal();
+    void ConfigureDatabase(PostgreSQLDatabase& db,
+                           const PostgreSQLParameters& parameters,
+                           bool clearAll);
 
   public:
-    explicit PostgreSQLStorageArea(const PostgreSQLParameters& parameters);
-
-    void SetClearAll(bool clear)
-    {
-      clearAll_ = clear;
-    }
+    PostgreSQLStorageArea(const PostgreSQLParameters& parameters,
+                          bool clearAll);
   };
 }
--- a/PostgreSQL/Plugins/StoragePlugin.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/Plugins/StoragePlugin.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -57,7 +57,7 @@
     {
       OrthancDatabases::PostgreSQLParameters parameters(postgresql);
       OrthancDatabases::StorageBackend::Register
-        (context, new OrthancDatabases::PostgreSQLStorageArea(parameters));
+        (context, new OrthancDatabases::PostgreSQLStorageArea(parameters, false /* don't clear database */));
     }
     catch (Orthanc::OrthancException& e)
     {
--- a/PostgreSQL/UnitTests/PostgreSQLTests.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/UnitTests/PostgreSQLTests.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -349,8 +349,7 @@
 
 TEST(PostgreSQL, StorageArea)
 {
-  PostgreSQLStorageArea storageArea(globalParameters_);
-  storageArea.SetClearAll(true);
+  PostgreSQLStorageArea storageArea(globalParameters_, true /* clear database */);
 
   {
     DatabaseManager::Transaction transaction(storageArea.GetManager(), TransactionType_ReadWrite);
@@ -456,33 +455,32 @@
   OrthancDatabases::PostgreSQLIndex db(NULL, globalParameters_);
   db.SetClearAll(true);
 
-  OrthancDatabases::DatabaseManager manager(db.CreateDatabaseFactory());
-  manager.Open();
+  std::unique_ptr<OrthancDatabases::DatabaseManager> manager(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db));
 
   std::string s;
-  ASSERT_TRUE(db.LookupGlobalProperty(s, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal1));
+  ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal1));
   ASSERT_EQ("2", s);
 
   OrthancPluginCreateInstanceResult r1, r2;
   
   memset(&r1, 0, sizeof(r1));
-  db.CreateInstance(r1, manager, "a", "b", "c", "d");
+  db.CreateInstance(r1, *manager, "a", "b", "c", "d");
   ASSERT_TRUE(r1.isNewInstance);
   ASSERT_TRUE(r1.isNewSeries);
   ASSERT_TRUE(r1.isNewStudy);
   ASSERT_TRUE(r1.isNewPatient);
 
   memset(&r2, 0, sizeof(r2));
-  db.CreateInstance(r2, manager, "a", "b", "c", "d");
+  db.CreateInstance(r2, *manager, "a", "b", "c", "d");
   ASSERT_FALSE(r2.isNewInstance);
   ASSERT_EQ(r1.instanceId, r2.instanceId);
 
   // Breaking the hierarchy
   memset(&r2, 0, sizeof(r2));
-  ASSERT_THROW(db.CreateInstance(r2, manager, "a", "e", "c", "f"), Orthanc::OrthancException);
+  ASSERT_THROW(db.CreateInstance(r2, *manager, "a", "e", "c", "f"), Orthanc::OrthancException);
 
   memset(&r2, 0, sizeof(r2));
-  db.CreateInstance(r2, manager, "a", "b", "c", "e");
+  db.CreateInstance(r2, *manager, "a", "b", "c", "e");
   ASSERT_TRUE(r2.isNewInstance);
   ASSERT_FALSE(r2.isNewSeries);
   ASSERT_FALSE(r2.isNewStudy);
@@ -493,7 +491,7 @@
   ASSERT_NE(r1.instanceId, r2.instanceId);
 
   memset(&r2, 0, sizeof(r2));
-  db.CreateInstance(r2, manager, "a", "b", "f", "g");
+  db.CreateInstance(r2, *manager, "a", "b", "f", "g");
   ASSERT_TRUE(r2.isNewInstance);
   ASSERT_TRUE(r2.isNewSeries);
   ASSERT_FALSE(r2.isNewStudy);
@@ -504,7 +502,7 @@
   ASSERT_NE(r1.instanceId, r2.instanceId);
 
   memset(&r2, 0, sizeof(r2));
-  db.CreateInstance(r2, manager, "a", "h", "i", "j");
+  db.CreateInstance(r2, *manager, "a", "h", "i", "j");
   ASSERT_TRUE(r2.isNewInstance);
   ASSERT_TRUE(r2.isNewSeries);
   ASSERT_TRUE(r2.isNewStudy);
@@ -515,7 +513,7 @@
   ASSERT_NE(r1.instanceId, r2.instanceId);
 
   memset(&r2, 0, sizeof(r2));
-  db.CreateInstance(r2, manager, "k", "l", "m", "n");
+  db.CreateInstance(r2, *manager, "k", "l", "m", "n");
   ASSERT_TRUE(r2.isNewInstance);
   ASSERT_TRUE(r2.isNewSeries);
   ASSERT_TRUE(r2.isNewStudy);
@@ -531,7 +529,6 @@
 TEST(PostgreSQL, Lock2)
 {
   std::unique_ptr<PostgreSQLDatabase> db1(CreateTestDatabase());
-  db1->Open();
 
   ASSERT_FALSE(db1->ReleaseAdvisoryLock(43)); // lock counter = 0
   ASSERT_TRUE(db1->AcquireAdvisoryLock(43));  // lock counter = 1
@@ -547,7 +544,6 @@
 
   {
     std::unique_ptr<PostgreSQLDatabase> db2(CreateTestDatabase());
-    db2->Open();
 
     // The "db1" is still actively locking
     ASSERT_FALSE(db2->AcquireAdvisoryLock(43));
--- a/PostgreSQL/UnitTests/UnitTestsMain.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/PostgreSQL/UnitTests/UnitTestsMain.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -27,6 +27,7 @@
 OrthancDatabases::PostgreSQLParameters  globalParameters_;
 
 #include "../../Framework/Plugins/IndexUnitTests.h"
+#include "../../Framework/PostgreSQL/PostgreSQLDatabase.h"
 
 
 #if ORTHANC_POSTGRESQL_STATIC == 1
@@ -92,22 +93,18 @@
   OrthancDatabases::PostgreSQLIndex db1(NULL, noLock);
   db1.SetClearAll(true);
 
-  OrthancDatabases::DatabaseManager manager1(db1.CreateDatabaseFactory());
-  manager1.Open();
+  std::unique_ptr<OrthancDatabases::DatabaseManager> manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1));
 
   {
     OrthancDatabases::PostgreSQLIndex db2(NULL, lock);
-    OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory());
-    manager2.Open();
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2));
 
     OrthancDatabases::PostgreSQLIndex db3(NULL, lock);
-    OrthancDatabases::DatabaseManager manager3(db3.CreateDatabaseFactory());
-    ASSERT_THROW(manager3.Open(), Orthanc::OrthancException);
+    ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3), Orthanc::OrthancException);
   }
 
   OrthancDatabases::PostgreSQLIndex db4(NULL, lock);
-  OrthancDatabases::DatabaseManager manager4(db4.CreateDatabaseFactory());
-  manager4.Open();
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4));
 }
 
 
--- a/Resources/CMake/DatabasesFrameworkConfiguration.cmake	Thu Apr 01 19:18:19 2021 +0200
+++ b/Resources/CMake/DatabasesFrameworkConfiguration.cmake	Fri Apr 02 19:23:36 2021 +0200
@@ -95,6 +95,8 @@
   ${ORTHANC_DATABASES_ROOT}/Framework/Common/NullValue.cpp
   ${ORTHANC_DATABASES_ROOT}/Framework/Common/Query.cpp
   ${ORTHANC_DATABASES_ROOT}/Framework/Common/ResultBase.cpp
+  ${ORTHANC_DATABASES_ROOT}/Framework/Common/RetryDatabaseFactory.cpp
+  ${ORTHANC_DATABASES_ROOT}/Framework/Common/RetryDatabaseFactory.cpp
   ${ORTHANC_DATABASES_ROOT}/Framework/Common/StatementLocation.cpp
   ${ORTHANC_DATABASES_ROOT}/Framework/Common/Utf8StringValue.cpp
   )
--- a/SQLite/Plugins/SQLiteIndex.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/SQLite/Plugins/SQLiteIndex.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -34,8 +34,27 @@
 
 namespace OrthancDatabases
 {
-  IDatabase* SQLiteIndex::OpenInternal()
+  IDatabase* SQLiteIndex::OpenDatabaseConnection()
   {
+    std::unique_ptr<SQLiteDatabase> db(new SQLiteDatabase);
+
+    if (path_.empty())
+    {
+      db->OpenInMemory();
+    }
+    else
+    {
+      db->Open(path_);
+    }
+
+    return db.release();
+  }
+
+
+  void SQLiteIndex::ConfigureDatabase(IDatabase& database)
+  {
+    SQLiteDatabase& db = dynamic_cast<SQLiteDatabase&>(database);
+    
     uint32_t expectedVersion = 6;
 
     if (GetContext())   // "GetContext()" can possibly be NULL in the unit tests
@@ -52,60 +71,48 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
     }
 
-
-    std::unique_ptr<SQLiteDatabase> db(new SQLiteDatabase);
+    {
+      SQLiteTransaction t(db);
 
-    if (path_.empty())
-    {
-      db->OpenInMemory();
-    }
-    else
-    {
-      db->Open(path_);
-    }
-
-    {
-      SQLiteTransaction t(*db);
-
-      if (!db->DoesTableExist("Resources"))
+      if (!db.DoesTableExist("Resources"))
       {
         std::string query;
 
         Orthanc::EmbeddedResources::GetFileResource
           (query, Orthanc::EmbeddedResources::SQLITE_PREPARE_INDEX);
-        db->Execute(query);
+        db.Execute(query);
  
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion, expectedVersion);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1);
      }
           
       t.Commit();
     }
 
-    db->Execute("PRAGMA ENCODING=\"UTF-8\";");
+    db.Execute("PRAGMA ENCODING=\"UTF-8\";");
 
     if (fast_)
     {
       // Performance tuning of SQLite with PRAGMAs
       // http://www.sqlite.org/pragma.html
-      db->Execute("PRAGMA SYNCHRONOUS=NORMAL;");
-      db->Execute("PRAGMA JOURNAL_MODE=WAL;");
-      db->Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;");
-      db->Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;");
-      //db->Execute("PRAGMA TEMP_STORE=memory");
+      db.Execute("PRAGMA SYNCHRONOUS=NORMAL;");
+      db.Execute("PRAGMA JOURNAL_MODE=WAL;");
+      db.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;");
+      db.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;");
+      //db.Execute("PRAGMA TEMP_STORE=memory");
     }
 
     {
-      SQLiteTransaction t(*db);
+      SQLiteTransaction t(db);
 
-      if (!db->DoesTableExist("Resources"))
+      if (!db.DoesTableExist("Resources"))
       {
         LOG(ERROR) << "Corrupted SQLite database";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);        
       }
 
       int version = 0;
-      if (!LookupGlobalIntegerProperty(version, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) ||
+      if (!LookupGlobalIntegerProperty(version, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseSchemaVersion) ||
           version != 6)
       {
         LOG(ERROR) << "SQLite plugin is incompatible with database schema version: " << version;
@@ -113,10 +120,10 @@
       }
 
       int revision;
-      if (!LookupGlobalIntegerProperty(revision, *db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
+      if (!LookupGlobalIntegerProperty(revision, db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel))
       {
         revision = 1;
-        SetGlobalIntegerProperty(*db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
+        SetGlobalIntegerProperty(db, t, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision);
       }
 
       if (revision != 1)
@@ -127,8 +134,6 @@
           
       t.Commit();
     }
-
-    return db.release();
   }
 
 
--- a/SQLite/Plugins/SQLiteIndex.h	Thu Apr 01 19:18:19 2021 +0200
+++ b/SQLite/Plugins/SQLiteIndex.h	Fri Apr 02 19:23:36 2021 +0200
@@ -21,6 +21,7 @@
 
 #pragma once
 
+#include "../../Framework/Common/IDatabaseFactory.h"
 #include "../../Framework/Plugins/IndexBackend.h"
 
 namespace OrthancDatabases
@@ -28,41 +29,9 @@
   class SQLiteIndex : public IndexBackend 
   {
   private:
-    class Factory : public IDatabaseFactory
-    {
-    private:
-      SQLiteIndex&  that_;
-
-    public:
-      Factory(SQLiteIndex& that) :
-      that_(that)
-      {
-      }
-
-      virtual Dialect GetDialect() const
-      {
-        return Dialect_SQLite;
-      }
-
-      virtual IDatabase* Open()
-      {
-        return that_.OpenInternal();
-      }
-
-      virtual void GetConnectionRetriesParameters(unsigned int& maxConnectionRetries,
-                                                  unsigned int& connectionRetryInterval)
-      {
-        // Dummy parameters
-        maxConnectionRetries = 0;
-        connectionRetryInterval = 1;
-      }
-    };
-
     std::string            path_;
     bool                   fast_;
 
-    IDatabase* OpenInternal();
-
   public:
     SQLiteIndex(OrthancPluginContext* context);  // Opens in memory
 
@@ -74,10 +43,9 @@
       fast_ = fast;
     }
 
-    virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE
-    {
-      return new Factory(*this);
-    }
+    virtual IDatabase* OpenDatabaseConnection() ORTHANC_OVERRIDE;
+
+    virtual void ConfigureDatabase(IDatabase& database) ORTHANC_OVERRIDE;
     
     virtual int64_t CreateResource(DatabaseManager& manager,
                                    const char* publicId,
--- a/SQLite/UnitTests/UnitTestsMain.cpp	Thu Apr 01 19:18:19 2021 +0200
+++ b/SQLite/UnitTests/UnitTestsMain.cpp	Fri Apr 02 19:23:36 2021 +0200
@@ -37,32 +37,25 @@
   {
     // No locking if using memory backend
     OrthancDatabases::SQLiteIndex db1(NULL);
-    OrthancDatabases::DatabaseManager manager1(db1.CreateDatabaseFactory());
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1));
 
     OrthancDatabases::SQLiteIndex db2(NULL);
-    OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory());
-    
-    manager1.Open();
-    manager2.Open();
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2));
   }
 
   Orthanc::SystemToolbox::RemoveFile("index.db");
 
   {
     OrthancDatabases::SQLiteIndex db1(NULL, "index.db");
-    OrthancDatabases::DatabaseManager manager1(db1.CreateDatabaseFactory());
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1));
 
     OrthancDatabases::SQLiteIndex db2(NULL, "index.db");
-    OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory());
-
-    manager1.Open();
-    ASSERT_THROW(manager2.Open(), Orthanc::OrthancException);
+    ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2), Orthanc::OrthancException);
   }
 
   {
     OrthancDatabases::SQLiteIndex db3(NULL, "index.db");
-    OrthancDatabases::DatabaseManager manager3(db3.CreateDatabaseFactory());
-    manager3.Open();
+    std::unique_ptr<OrthancDatabases::DatabaseManager> manager3(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3));
   }
 }