# HG changeset patch # User Sebastien Jodogne # Date 1617384216 -7200 # Node ID a4918d57435c6d69d26165601729db2631c92270 # Parent 94c9908e6acaf399a1502e0f6f7ade6a25c6b5c5 DatabaseManager doesn't IDatabaseFactory anymore diff -r 94c9908e6aca -r a4918d57435c Framework/Common/DatabaseManager.cpp --- 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(); } diff -r 94c9908e6aca -r a4918d57435c Framework/Common/DatabaseManager.h --- 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 // For std::unique_ptr<> @@ -38,14 +38,16 @@ private: typedef std::map CachedStatements; - boost::recursive_mutex mutex_; - std::unique_ptr factory_; - std::unique_ptr database_; - std::unique_ptr transaction_; - CachedStatements cachedStatements_; - Dialect dialect_; + boost::recursive_mutex mutex_; + std::unique_ptr database_; + std::unique_ptr 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); diff -r 94c9908e6aca -r a4918d57435c Framework/Common/IDatabaseFactory.h --- 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; }; } diff -r 94c9908e6aca -r a4918d57435c Framework/MySQL/MySQLDatabase.cpp --- 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 // For std::unique_ptr<> #include @@ -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 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(factory.Open()); + } } diff -r 94c9908e6aca -r a4918d57435c Framework/MySQL/MySQLDatabase.h --- 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); }; } diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/DatabaseBackendAdapterV2.cpp --- 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 #include @@ -78,8 +80,7 @@ if (manager_.get() == NULL) { - manager_.reset(new DatabaseManager(backend_->CreateDatabaseFactory())); - manager_->Open(); + manager_.reset(IndexBackend::CreateSingleDatabaseManager(*backend_)); } else { diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/DatabaseBackendAdapterV3.cpp --- 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 { diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/IDatabaseBackend.h --- 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; diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/IndexBackend.cpp --- 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 database(backend.OpenDatabaseConnection()); + backend.ConfigureDatabase(*database); + return new DatabaseManager(database.release()); + } } diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/IndexBackend.h --- 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); }; } diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/IndexUnitTests.h --- 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 manager(IndexBackend::CreateSingleDatabaseManager(db)); std::unique_ptr 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 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 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 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 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 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 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(); } diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/StorageBackend.cpp --- 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; diff -r 94c9908e6aca -r a4918d57435c Framework/Plugins/StorageBackend.h --- 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 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 diff -r 94c9908e6aca -r a4918d57435c Framework/PostgreSQL/PostgreSQLDatabase.cpp --- 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 #include @@ -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 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(factory.Open()); + } } diff -r 94c9908e6aca -r a4918d57435c Framework/PostgreSQL/PostgreSQLDatabase.h --- 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); }; } diff -r 94c9908e6aca -r a4918d57435c MySQL/Plugins/MySQLIndex.cpp --- 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(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 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) - { } diff -r 94c9908e6aca -r a4918d57435c MySQL/Plugins/MySQLIndex.h --- 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) diff -r 94c9908e6aca -r a4918d57435c MySQL/Plugins/MySQLStorageArea.cpp --- 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 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(size) / static_cast(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 database(MySQLDatabase::OpenDatabaseConnection(parameters)); + + if (database.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + ConfigureDatabase(*database, parameters, clearAll); + SetDatabase(database.release()); } } diff -r 94c9908e6aca -r a4918d57435c MySQL/Plugins/MySQLStorageArea.h --- 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); }; } diff -r 94c9908e6aca -r a4918d57435c MySQL/Plugins/StoragePlugin.cpp --- 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) { diff -r 94c9908e6aca -r a4918d57435c MySQL/UnitTests/UnitTestsMain.cpp --- 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 manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); { OrthancDatabases::MySQLIndex db2(NULL, lock); - OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory()); - manager2.Open(); + std::unique_ptr 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 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); diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/Plugins/PostgreSQLIndex.cpp --- 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 db(new PostgreSQLDatabase(parameters_)); - - db->Open(); + PostgreSQLDatabase& db = dynamic_cast(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) diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/Plugins/PostgreSQLIndex.h --- 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, diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/Plugins/PostgreSQLStorageArea.cpp --- 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 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 database(PostgreSQLDatabase::OpenDatabaseConnection(parameters)); + + if (database.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + ConfigureDatabase(*database, parameters, clearAll); + SetDatabase(database.release()); } } diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/Plugins/PostgreSQLStorageArea.h --- 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); }; } diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/Plugins/StoragePlugin.cpp --- 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) { diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/UnitTests/PostgreSQLTests.cpp --- 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 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 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 db2(CreateTestDatabase()); - db2->Open(); // The "db1" is still actively locking ASSERT_FALSE(db2->AcquireAdvisoryLock(43)); diff -r 94c9908e6aca -r a4918d57435c PostgreSQL/UnitTests/UnitTestsMain.cpp --- 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 manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); { OrthancDatabases::PostgreSQLIndex db2(NULL, lock); - OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory()); - manager2.Open(); + std::unique_ptr 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 manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4)); } diff -r 94c9908e6aca -r a4918d57435c Resources/CMake/DatabasesFrameworkConfiguration.cmake --- 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 ) diff -r 94c9908e6aca -r a4918d57435c SQLite/Plugins/SQLiteIndex.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 db(new SQLiteDatabase); + + if (path_.empty()) + { + db->OpenInMemory(); + } + else + { + db->Open(path_); + } + + return db.release(); + } + + + void SQLiteIndex::ConfigureDatabase(IDatabase& database) + { + SQLiteDatabase& db = dynamic_cast(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 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(); } diff -r 94c9908e6aca -r a4918d57435c SQLite/Plugins/SQLiteIndex.h --- 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, diff -r 94c9908e6aca -r a4918d57435c SQLite/UnitTests/UnitTestsMain.cpp --- 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 manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); OrthancDatabases::SQLiteIndex db2(NULL); - OrthancDatabases::DatabaseManager manager2(db2.CreateDatabaseFactory()); - - manager1.Open(); - manager2.Open(); + std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2)); } Orthanc::SystemToolbox::RemoveFile("index.db"); { OrthancDatabases::SQLiteIndex db1(NULL, "index.db"); - OrthancDatabases::DatabaseManager manager1(db1.CreateDatabaseFactory()); + std::unique_ptr 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 manager3(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3)); } }