Mercurial > hg > orthanc
changeset 4595:cc64385593ef db-changes
added OrthancPluginRegisterDatabaseBackendV3() to plugin sdk
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 16 Mar 2021 17:58:16 +0100 |
parents | d494b4f1103e |
children | da2e0a457eae |
files | OrthancServer/CMakeLists.txt OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabase.h OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.h OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h |
diffstat | 13 files changed, 1147 insertions(+), 34 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/CMakeLists.txt Tue Mar 16 17:58:16 2021 +0100 @@ -188,6 +188,7 @@ list(APPEND ORTHANC_SERVER_SOURCES ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabase.cpp + ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabaseV3.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPlugins.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsEnumerations.cpp ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsErrorDictionary.cpp
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Tue Mar 16 17:58:16 2021 +0100 @@ -35,12 +35,17 @@ #include "OrthancPluginDatabase.h" #if ORTHANC_ENABLE_PLUGINS != 1 -#error The plugin support is disabled +# error The plugin support is disabled #endif #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/Compatibility/ICreateInstance.h" +#include "../../Sources/Database/Compatibility/IGetChildrenMetadata.h" +#include "../../Sources/Database/Compatibility/ILookupResourceAndParent.h" +#include "../../Sources/Database/Compatibility/ILookupResources.h" +#include "../../Sources/Database/Compatibility/ISetResourcesContent.h" #include "../../Sources/Database/VoidDatabaseListener.h" #include "PluginsEnumerations.h" @@ -978,7 +983,7 @@ } - virtual uint64_t GetResourceCount(ResourceType resourceType) ORTHANC_OVERRIDE + virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE { uint64_t count; CheckSuccess(that_.backend_.getResourceCount(&count, that_.payload_, Plugins::Convert(resourceType)));
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.h Tue Mar 16 17:58:16 2021 +0100 @@ -36,11 +36,7 @@ #if ORTHANC_ENABLE_PLUGINS == 1 #include "../../../OrthancFramework/Sources/SharedLibrary.h" -#include "../../Sources/Database/Compatibility/ICreateInstance.h" -#include "../../Sources/Database/Compatibility/IGetChildrenMetadata.h" -#include "../../Sources/Database/Compatibility/ILookupResources.h" -#include "../../Sources/Database/Compatibility/ILookupResourceAndParent.h" -#include "../../Sources/Database/Compatibility/ISetResourcesContent.h" +#include "../../Sources/Database/IDatabaseWrapper.h" #include "../Include/orthanc/OrthancCDatabasePlugin.h" #include "PluginsErrorDictionary.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Tue Mar 16 17:58:16 2021 +0100 @@ -0,0 +1,746 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../../Sources/PrecompiledHeadersServer.h" +#include "OrthancPluginDatabaseV3.h" + +#if ORTHANC_ENABLE_PLUGINS != 1 +# error The plugin support is disabled +#endif + +#include "../../../OrthancFramework/Sources/Logging.h" +#include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/VoidDatabaseListener.h" +#include "PluginsEnumerations.h" + +#include <cassert> + +namespace Orthanc +{ + class OrthancPluginDatabaseV3::Transaction : public IDatabaseWrapper::ITransaction + { + private: + OrthancPluginDatabaseV3& that_; + IDatabaseListener& listener_; + OrthancPluginDatabaseTransaction* transaction_; + + + void CheckSuccess(OrthancPluginErrorCode code) const + { + that_.CheckSuccess(code); + } + + + void ReadStringAnswers(std::list<std::string>& target) + { + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + const char* value = NULL; + CheckSuccess(that_.backend_.readAnswerString(transaction_, &value, i)); + if (value == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + target.push_back(value); + } + } + } + + + std::string ReadOneStringAnswer() + { + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + if (count == 0) + { + throw OrthancException(ErrorCode_InexistentItem); + } + else if (count == 1) + { + const char* value = NULL; + CheckSuccess(that_.backend_.readAnswerString(transaction_, &value, 0)); + if (value == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + return value; + } + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + ExportedResource ReadAnswerExportedResource(uint32_t answerIndex) + { + OrthancPluginExportedResource exported; + CheckSuccess(that_.backend_.readAnswerExportedResource(transaction_, &exported, answerIndex)); + + if (exported.publicId == NULL || + exported.modality == NULL || + exported.date == NULL || + exported.patientId == NULL || + exported.studyInstanceUid == NULL || + exported.seriesInstanceUid == NULL || + exported.sopInstanceUid == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + return ExportedResource(exported.seq, + Plugins::Convert(exported.resourceType), + exported.publicId, + exported.modality, + exported.date, + exported.patientId, + exported.studyInstanceUid, + exported.seriesInstanceUid, + exported.sopInstanceUid); + } + } + + + ServerIndexChange ReadAnswerChange(uint32_t answerIndex) + { + OrthancPluginChange change; + CheckSuccess(that_.backend_.readAnswerChange(transaction_, &change, answerIndex)); + + if (change.publicId == NULL || + change.date == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + return ServerIndexChange(change.seq, + static_cast<ChangeType>(change.changeType), + Plugins::Convert(change.resourceType), + change.publicId, + change.date); + } + } + + + public: + Transaction(OrthancPluginDatabaseV3& that, + IDatabaseListener& listener, + _OrthancPluginDatabaseTransactionType type) : + that_(that), + listener_(listener) + { + CheckSuccess(that.backend_.startTransaction(that.database_, &transaction_, type)); + if (transaction_ == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + virtual ~Transaction() + { + OrthancPluginErrorCode code = that_.backend_.destructTransaction(transaction_); + if (code != OrthancPluginErrorCode_Success) + { + // Don't throw exception in destructors + that_.errorDictionary_.LogError(code, true); + } + } + + + virtual void Rollback() ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.rollback(transaction_)); + } + + + virtual void Commit(int64_t fileSizeDelta) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.commit(transaction_, fileSizeDelta)); + } + + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) ORTHANC_OVERRIDE + { + OrthancPluginAttachment tmp; + tmp.uuid = attachment.GetUuid().c_str(); + tmp.contentType = static_cast<int32_t>(attachment.GetContentType()); + tmp.uncompressedSize = attachment.GetUncompressedSize(); + tmp.uncompressedHash = attachment.GetUncompressedMD5().c_str(); + tmp.compressionType = static_cast<int32_t>(attachment.GetCompressionType()); + tmp.compressedSize = attachment.GetCompressedSize(); + tmp.compressedHash = attachment.GetCompressedMD5().c_str(); + + CheckSuccess(that_.backend_.addAttachment(transaction_, id, &tmp)); + } + + + virtual void ClearChanges() ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.clearChanges(transaction_)); + } + + + virtual void ClearExportedResources() ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.clearExportedResources(transaction_)); + } + + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteAttachment(transaction_, id, static_cast<int32_t>(attachment))); + } + + + virtual void DeleteMetadata(int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteMetadata(transaction_, id, static_cast<int32_t>(type))); + } + + + virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteResource(transaction_, id)); + } + + + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getAllMetadata(transaction_, id)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + int32_t metadata; + const char* value = NULL; + CheckSuccess(that_.backend_.readAnswerMetadata(transaction_, &metadata, &value, i)); + + if (value == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + target[static_cast<MetadataType>(metadata)] = value; + } + } + } + + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getAllPublicIds(transaction_, Plugins::Convert(resourceType))); + ReadStringAnswers(target); + } + + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType, + size_t since, + size_t limit) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getAllPublicIdsWithLimit( + transaction_, Plugins::Convert(resourceType), + static_cast<uint64_t>(since), static_cast<uint64_t>(limit))); + ReadStringAnswers(target); + } + + + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + uint8_t tmpDone = true; + CheckSuccess(that_.backend_.getChanges(transaction_, &tmpDone, since, maxResults)); + + done = (tmpDone != 0); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + target.push_back(ReadAnswerChange(i)); + } + } + + + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getChildrenInternalId(transaction_, id)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + int64_t value; + CheckSuccess(that_.backend_.readAnswerInt64(transaction_, &value, i)); + target.push_back(value); + } + } + + + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getChildrenPublicId(transaction_, id)); + ReadStringAnswers(target); + } + + + virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + uint8_t tmpDone = true; + CheckSuccess(that_.backend_.getExportedResources(transaction_, &tmpDone, since, maxResults)); + + done = (tmpDone != 0); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + target.push_back(ReadAnswerExportedResource(i)); + } + } + + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getLastChange(transaction_)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + if (count == 1) + { + target.push_back(ReadAnswerChange(0)); + } + else if (count > 1) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getLastExportedResource(transaction_)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + if (count == 1) + { + target.push_back(ReadAnswerExportedResource(0)); + } + else if (count > 1) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + virtual void GetMainDicomTags(DicomMap& target, + int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getMainDicomTags(transaction_, id)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.Clear(); + for (uint32_t i = 0; i < count; i++) + { + uint16_t group, element; + const char* value = NULL; + CheckSuccess(that_.backend_.readAnswerDicomTag(transaction_, &group, &element, &value, i)); + + if (value == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + else + { + target.SetValue(group, element, std::string(value), false); + } + } + } + + + virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.getPublicId(transaction_, resourceId)); + return ReadOneStringAnswer(); + } + + + virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE + { + uint64_t value; + CheckSuccess(that_.backend_.getResourcesCount(transaction_, &value, Plugins::Convert(resourceType))); + return value; + } + + + virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE + { + OrthancPluginResourceType type; + CheckSuccess(that_.backend_.getResourceType(transaction_, &type, resourceId)); + return Plugins::Convert(type); + } + + + virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE + { + uint64_t s; + CheckSuccess(that_.backend_.getTotalCompressedSize(transaction_, &s)); + return s; + } + + + virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE + { + uint64_t s; + CheckSuccess(that_.backend_.getTotalUncompressedSize(transaction_, &s)); + return s; + } + + + virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE + { + uint8_t b; + CheckSuccess(that_.backend_.isExistingResource(transaction_, &b, internalId)); + return (b != 0); + } + + + virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE + { + uint8_t b; + CheckSuccess(that_.backend_.isProtectedPatient(transaction_, &b, internalId)); + return (b != 0); + } + + + virtual void ListAvailableAttachments(std::set<FileContentType>& target, + int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.listAvailableAttachments(transaction_, id)); + + uint32_t count; + CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count)); + + target.clear(); + for (uint32_t i = 0; i < count; i++) + { + int32_t value; + CheckSuccess(that_.backend_.readAnswerInt32(transaction_, &value, i)); + target.insert(static_cast<FileContentType>(value)); + } + } + + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.logChange(transaction_, static_cast<int32_t>(change.GetChangeType()), + internalId, Plugins::Convert(change.GetResourceType()), + change.GetPublicId().c_str(), change.GetDate().c_str())); + } + + + virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE + { + } + + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) ORTHANC_OVERRIDE + { + } + + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property) ORTHANC_OVERRIDE + { + } + + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + } + + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) ORTHANC_OVERRIDE + { + } + + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) ORTHANC_OVERRIDE + { + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE + { + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) ORTHANC_OVERRIDE + { + } + + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value) ORTHANC_OVERRIDE + { + } + + + virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE + { + } + + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) ORTHANC_OVERRIDE + { + } + + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) ORTHANC_OVERRIDE + { + } + + + virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE + { + } + + + virtual void ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, // Can be NULL if not needed + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) ORTHANC_OVERRIDE + { + } + + + virtual bool CreateInstance(CreateInstanceResult& result, /* out */ + int64_t& instanceId, /* out */ + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance) ORTHANC_OVERRIDE + { + } + + + virtual void SetResourcesContent(const ResourcesContent& content) ORTHANC_OVERRIDE + { + } + + + virtual void GetChildrenMetadata(std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata) ORTHANC_OVERRIDE + { + } + + + virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE + { + + } + + + virtual bool LookupResourceAndParent(int64_t& id, + ResourceType& type, + std::string& parentPublicId, + const std::string& publicId) ORTHANC_OVERRIDE + { + } + }; + + + void OrthancPluginDatabaseV3::CheckSuccess(OrthancPluginErrorCode code) + { + if (code != OrthancPluginErrorCode_Success) + { + errorDictionary_.LogError(code, true); + throw OrthancException(static_cast<ErrorCode>(code)); + } + } + + + OrthancPluginDatabaseV3::OrthancPluginDatabaseV3(SharedLibrary& library, + PluginsErrorDictionary& errorDictionary, + const OrthancPluginDatabaseBackendV3* backend, + size_t backendSize, + OrthancPluginDatabaseContext* database) : + library_(library), + errorDictionary_(errorDictionary), + database_(database) + { + if (backendSize >= sizeof(backend_)) + { + memcpy(&backend_, backend, sizeof(backend_)); + } + else + { + // Not all the primitives are implemented by the plugin + memset(&backend_, 0, sizeof(backend_)); + memcpy(&backend_, backend, backendSize); + } + } + + + OrthancPluginDatabaseV3::~OrthancPluginDatabaseV3() + { + if (database_ != NULL) + { + OrthancPluginErrorCode code = backend_.destructDatabase(database_); + if (code != OrthancPluginErrorCode_Success) + { + // Don't throw exception in destructors + errorDictionary_.LogError(code, true); + } + } + } + + + void OrthancPluginDatabaseV3::Open() + { + CheckSuccess(backend_.open(database_)); + } + + + void OrthancPluginDatabaseV3::Close() + { + CheckSuccess(backend_.close(database_)); + } + + + IDatabaseWrapper::ITransaction* OrthancPluginDatabaseV3::StartTransaction(TransactionType type, + IDatabaseListener& listener) + { + switch (type) + { + case TransactionType_ReadOnly: + return new Transaction(*this, listener, _OrthancPluginDatabaseTransactionType_ReadOnly); + + case TransactionType_ReadWrite: + return new Transaction(*this, listener, _OrthancPluginDatabaseTransactionType_ReadWrite); + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + unsigned int OrthancPluginDatabaseV3::GetDatabaseVersion() + { + uint32_t version = 0; + CheckSuccess(backend_.getDatabaseVersion(database_, &version)); + return version; + } + + + void OrthancPluginDatabaseV3::Upgrade(unsigned int targetVersion, + IStorageArea& storageArea) + { + VoidDatabaseListener listener; + + if (backend_.upgradeDatabase != NULL) + { + Transaction transaction(*this, listener, _OrthancPluginDatabaseTransactionType_ReadWrite); + + OrthancPluginErrorCode code = backend_.upgradeDatabase( + database_, reinterpret_cast<OrthancPluginStorageArea*>(&storageArea), + static_cast<uint32_t>(targetVersion)); + + if (code == OrthancPluginErrorCode_Success) + { + transaction.Commit(0); + } + else + { + transaction.Rollback(); + errorDictionary_.LogError(code, true); + throw OrthancException(static_cast<ErrorCode>(code)); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.h Tue Mar 16 17:58:16 2021 +0100 @@ -0,0 +1,95 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_PLUGINS == 1 + +#include "../../../OrthancFramework/Sources/SharedLibrary.h" +#include "../../Sources/Database/IDatabaseWrapper.h" +#include "../Include/orthanc/OrthancCDatabasePlugin.h" +#include "PluginsErrorDictionary.h" + +namespace Orthanc +{ + class OrthancPluginDatabaseV3 : public IDatabaseWrapper + { + private: + class Transaction; + + SharedLibrary& library_; + PluginsErrorDictionary& errorDictionary_; + OrthancPluginDatabaseBackendV3 backend_; + OrthancPluginDatabaseContext* database_; + + void CheckSuccess(OrthancPluginErrorCode code); + + public: + OrthancPluginDatabaseV3(SharedLibrary& library, + PluginsErrorDictionary& errorDictionary, + const OrthancPluginDatabaseBackendV3* backend, + size_t backendSize, + OrthancPluginDatabaseContext* database); + + virtual ~OrthancPluginDatabaseV3(); + + virtual void Open() ORTHANC_OVERRIDE; + + virtual void Close() ORTHANC_OVERRIDE; + + const SharedLibrary& GetSharedLibrary() const + { + return library_; + } + + virtual void FlushToDisk() ORTHANC_OVERRIDE + { + } + + virtual bool HasFlushToDisk() const ORTHANC_OVERRIDE + { + return false; + } + + virtual IDatabaseWrapper::ITransaction* StartTransaction(TransactionType type, + IDatabaseListener& listener) + ORTHANC_OVERRIDE; + + virtual unsigned int GetDatabaseVersion() ORTHANC_OVERRIDE; + + virtual void Upgrade(unsigned int targetVersion, + IStorageArea& storageArea) ORTHANC_OVERRIDE; + }; +} + +#endif
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Tue Mar 16 17:58:16 2021 +0100 @@ -71,6 +71,8 @@ #include "../../Sources/Search/HierarchicalMatcher.h" #include "../../Sources/ServerContext.h" #include "../../Sources/ServerToolbox.h" +#include "OrthancPluginDatabase.h" +#include "OrthancPluginDatabaseV3.h" #include "PluginsEnumerations.h" #include "PluginsJob.h" @@ -1191,6 +1193,7 @@ int argc_; char** argv_; std::unique_ptr<OrthancPluginDatabase> database_; + std::unique_ptr<OrthancPluginDatabaseV3> databaseV3_; // New in Orthanc 1.10.0 PluginsErrorDictionary dictionary_; PImpl() : @@ -4886,12 +4889,15 @@ case _OrthancPluginService_RegisterDatabaseBackend: { + // TODO - WARN ABOUT PERFORMANCE + CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end"; const _OrthancPluginRegisterDatabaseBackend& p = *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackend*>(parameters); - if (pimpl_->database_.get() == NULL) + if (pimpl_->database_.get() == NULL && + pimpl_->databaseV3_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, NULL, 0, p.payload)); @@ -4908,12 +4914,15 @@ case _OrthancPluginService_RegisterDatabaseBackendV2: { + // TODO - WARN ABOUT PERFORMANCE + CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end"; const _OrthancPluginRegisterDatabaseBackendV2& p = *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV2*>(parameters); - if (pimpl_->database_.get() == NULL) + if (pimpl_->database_.get() == NULL && + pimpl_->databaseV3_.get() == NULL) { pimpl_->database_.reset(new OrthancPluginDatabase(plugin, GetErrorDictionary(), *p.backend, p.extensions, @@ -4929,6 +4938,27 @@ return true; } + case _OrthancPluginService_RegisterDatabaseBackendV3: + { + CLOG(INFO, PLUGINS) << "Plugin has registered a custom database back-end"; + + const _OrthancPluginRegisterDatabaseBackendV3& p = + *reinterpret_cast<const _OrthancPluginRegisterDatabaseBackendV3*>(parameters); + + if (pimpl_->database_.get() == NULL && + pimpl_->databaseV3_.get() == NULL) + { + pimpl_->databaseV3_.reset(new OrthancPluginDatabaseV3(plugin, GetErrorDictionary(), + p.backend, p.backendSize, p.database)); + } + else + { + throw OrthancException(ErrorCode_DatabaseBackendAlreadyRegistered); + } + + return true; + } + case _OrthancPluginService_DatabaseAnswer: throw OrthancException(ErrorCode_InternalError); // Implemented before locking (*) @@ -5048,7 +5078,8 @@ bool OrthancPlugins::HasDatabaseBackend() const { boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); - return pimpl_->database_.get() != NULL; + return (pimpl_->database_.get() != NULL || + pimpl_->databaseV3_.get() != NULL); } @@ -5080,27 +5111,35 @@ IDatabaseWrapper& OrthancPlugins::GetDatabaseBackend() { - if (!HasDatabaseBackend()) + if (pimpl_->database_.get() != NULL) + { + return *pimpl_->database_; + } + else if (pimpl_->databaseV3_.get() != NULL) + { + return *pimpl_->databaseV3_; + } + else { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else - { - return *pimpl_->database_; - } } const SharedLibrary& OrthancPlugins::GetDatabaseBackendLibrary() const { - if (!HasDatabaseBackend()) + if (pimpl_->database_.get() != NULL) + { + return pimpl_->database_->GetSharedLibrary(); + } + else if (pimpl_->databaseV3_.get() != NULL) + { + return pimpl_->databaseV3_->GetSharedLibrary(); + } + else { throw OrthancException(ErrorCode_BadSequenceOfCalls); } - else - { - return pimpl_->database_->GetSharedLibrary(); - } }
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h Tue Mar 16 17:58:16 2021 +0100 @@ -61,10 +61,10 @@ #include "../../../OrthancFramework/Sources/HttpServer/IHttpHandler.h" #include "../../../OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h" #include "../../../OrthancFramework/Sources/JobsEngine/IJob.h" +#include "../../Sources/Database/IDatabaseWrapper.h" #include "../../Sources/IDicomImageDecoder.h" #include "../../Sources/IServerListener.h" #include "../../Sources/ServerJobs/IStorageCommitmentFactory.h" -#include "OrthancPluginDatabase.h" #include "PluginsManager.h" #include <list>
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Tue Mar 16 17:58:16 2021 +0100 @@ -44,6 +44,14 @@ typedef struct _OrthancPluginDatabaseContext_t OrthancPluginDatabaseContext; + /** + * Opaque structure that represents a transaction of a custom database engine. + * New in Orthanc 1.10.0. + * @ingroup Callbacks + **/ + typedef struct _OrthancPluginDatabaseTransaction_t OrthancPluginDatabaseTransaction; + + /*<! @cond Doxygen_Suppress */ typedef enum { @@ -966,6 +974,228 @@ } + + /** + * New interface starting with Orthanc 1.10.0 + **/ + +/*<! @cond Doxygen_Suppress */ + typedef enum + { + _OrthancPluginDatabaseTransactionType_ReadOnly = 1, + _OrthancPluginDatabaseTransactionType_ReadWrite = 2, + _OrthancPluginDatabaseTransactionType_INTERNAL = 0x7fffffff + } _OrthancPluginDatabaseTransactionType; + + typedef struct + { + /** + * Functions to read the answers inside a transaction + **/ + + OrthancPluginErrorCode (*readAnswersCount) (OrthancPluginDatabaseTransaction* transaction, + uint32_t* target); + + OrthancPluginErrorCode (*readAnswerChange) (OrthancPluginDatabaseTransaction* transaction, + OrthancPluginChange* target, + uint32_t index); + + OrthancPluginErrorCode (*readAnswerDicomTag) (OrthancPluginDatabaseTransaction* transaction, + uint16_t* group, + uint16_t* element, + const char** value, + uint32_t index); + + OrthancPluginErrorCode (*readAnswerExportedResource) (OrthancPluginDatabaseTransaction* transaction, + OrthancPluginExportedResource* target, + uint32_t index); + + OrthancPluginErrorCode (*readAnswerInt32) (OrthancPluginDatabaseTransaction* transaction, + int32_t* target, + uint32_t index); + + OrthancPluginErrorCode (*readAnswerInt64) (OrthancPluginDatabaseTransaction* transaction, + int64_t* target, + uint32_t index); + + OrthancPluginErrorCode (*readAnswerMetadata) (OrthancPluginDatabaseTransaction* transaction, + int32_t* metadata, + const char** value, + uint32_t index); + OrthancPluginErrorCode (*readAnswerString) (OrthancPluginDatabaseTransaction* transaction, + const char** target, + uint32_t index); + + + /** + * Functions to access the global database object + * (cf. "IDatabaseWrapper" class in Orthanc) + **/ + + OrthancPluginErrorCode (*open) (OrthancPluginDatabaseContext* database); + + OrthancPluginErrorCode (*close) (OrthancPluginDatabaseContext* database); + + OrthancPluginErrorCode (*destructDatabase) (OrthancPluginDatabaseContext* database); + + OrthancPluginErrorCode (*getDatabaseVersion) (OrthancPluginDatabaseContext* database, + uint32_t* target); + + OrthancPluginErrorCode (*upgradeDatabase) (OrthancPluginDatabaseContext* database, + OrthancPluginStorageArea* storageArea, + uint32_t target); + + OrthancPluginErrorCode (*startTransaction) (OrthancPluginDatabaseContext* database, + OrthancPluginDatabaseTransaction** target, + _OrthancPluginDatabaseTransactionType type); + + OrthancPluginErrorCode (*destructTransaction) (OrthancPluginDatabaseTransaction* transaction); + + + /** + * Functions to run operations within a database transaction + * (cf. "IDatabaseWrapper::ITransaction" class in Orthanc) + **/ + + OrthancPluginErrorCode (*rollback) (OrthancPluginDatabaseTransaction* transaction); + + OrthancPluginErrorCode (*commit) (OrthancPluginDatabaseTransaction* transaction, + int64_t fileSizeDelta); + + OrthancPluginErrorCode (*addAttachment) (OrthancPluginDatabaseTransaction* transaction, + int64_t id, + const OrthancPluginAttachment* attachment); + + OrthancPluginErrorCode (*clearChanges) (OrthancPluginDatabaseTransaction* transaction); + + OrthancPluginErrorCode (*clearExportedResources) (OrthancPluginDatabaseTransaction* transaction); + + OrthancPluginErrorCode (*deleteAttachment) (OrthancPluginDatabaseTransaction* transaction, + int64_t id, + int32_t contentType); + + OrthancPluginErrorCode (*deleteMetadata) (OrthancPluginDatabaseTransaction* transaction, + int64_t id, + int32_t metadataType); + + OrthancPluginErrorCode (*deleteResource) (OrthancPluginDatabaseTransaction* transaction, + int64_t id); + + /* Answers are read using "readAnswerMetadata()" */ + OrthancPluginErrorCode (*getAllMetadata) (OrthancPluginDatabaseTransaction* transaction, + int64_t id); + + /* Answers are read using "readAnswerString()" */ + OrthancPluginErrorCode (*getAllPublicIds) (OrthancPluginDatabaseTransaction* transaction, + OrthancPluginResourceType resourceType); + + /* Answers are read using "readAnswerString()" */ + OrthancPluginErrorCode (*getAllPublicIdsWithLimit) (OrthancPluginDatabaseTransaction* transaction, + OrthancPluginResourceType resourceType, + uint64_t since, + uint64_t limit); + + /* Answers are read using "readAnswerChange()" */ + OrthancPluginErrorCode (*getChanges) (OrthancPluginDatabaseTransaction* transaction, + uint8_t* targetDone, + int64_t since, + uint32_t maxResults); + + /* Answers are read using "readAnswerInt64()" */ + OrthancPluginErrorCode (*getChildrenInternalId) (OrthancPluginDatabaseTransaction* transaction, + int64_t id); + + /* Answers are read using "readAnswerString()" */ + OrthancPluginErrorCode (*getChildrenPublicId) (OrthancPluginDatabaseTransaction* transaction, + int64_t id); + + /* Answers are read using "readAnswerExportedResource()" */ + OrthancPluginErrorCode (*getExportedResources) (OrthancPluginDatabaseTransaction* transaction, + uint8_t* targetDone, + int64_t since, + uint32_t maxResults); + + /* Answer is read using "readAnswerChange()" */ + OrthancPluginErrorCode (*getLastChange) (OrthancPluginDatabaseTransaction* transaction); + + /* Answer is read using "readAnswerExportedResource()" */ + OrthancPluginErrorCode (*getLastExportedResource) (OrthancPluginDatabaseTransaction* transaction); + + /* Answers are read using "readAnswerDicomTag()" */ + OrthancPluginErrorCode (*getMainDicomTags) (OrthancPluginDatabaseTransaction* transaction, + int64_t id); + + /* Answer is read using "readAnswerString()" */ + OrthancPluginErrorCode (*getPublicId) (OrthancPluginDatabaseTransaction* transaction, + int64_t internalId); + + OrthancPluginErrorCode (*getResourcesCount) (OrthancPluginDatabaseTransaction* transaction, + uint64_t* target, + OrthancPluginResourceType resourceType); + + OrthancPluginErrorCode (*getResourceType) (OrthancPluginDatabaseTransaction* transaction, + OrthancPluginResourceType* target, + uint64_t resourceId); + + OrthancPluginErrorCode (*getTotalCompressedSize) (OrthancPluginDatabaseTransaction* transaction, + uint64_t* target); + + OrthancPluginErrorCode (*getTotalUncompressedSize) (OrthancPluginDatabaseTransaction* transaction, + uint64_t* target); + + OrthancPluginErrorCode (*isExistingResource) (OrthancPluginDatabaseTransaction* transaction, + uint8_t* target, + int64_t resourceId); + + OrthancPluginErrorCode (*isProtectedPatient) (OrthancPluginDatabaseTransaction* transaction, + uint8_t* target, + int64_t resourceId); + + /* Answer is read using "readAnswerInt32()" */ + OrthancPluginErrorCode (*listAvailableAttachments) (OrthancPluginDatabaseTransaction* transaction, + int64_t internalId); + + OrthancPluginErrorCode (*logChange) (OrthancPluginDatabaseTransaction* transaction, + int32_t changeType, + int64_t internalId, + OrthancPluginResourceType resourceType, + const char* publicId, + const char* date); + + } OrthancPluginDatabaseBackendV3; + +/*<! @endcond */ + + + typedef struct + { + const OrthancPluginDatabaseBackendV3* backend; + uint32_t backendSize; + OrthancPluginDatabaseContext* database; + } _OrthancPluginRegisterDatabaseBackendV3; + + + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterDatabaseBackendV3( + OrthancPluginContext* context, + const OrthancPluginDatabaseBackendV3* backend, + uint32_t backendSize, + OrthancPluginDatabaseContext* database) + { + _OrthancPluginRegisterDatabaseBackendV3 params; + + if (sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType)) + { + return OrthancPluginErrorCode_Plugin; + } + + memset(¶ms, 0, sizeof(params)); + params.backend = backend; + params.backendSize = sizeof(OrthancPluginDatabaseBackendV3); + params.database = database; + + return context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackendV3, ¶ms); + } + #ifdef __cplusplus } #endif
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Mar 16 17:58:16 2021 +0100 @@ -17,7 +17,7 @@ * - Possibly register its callback for received DICOM instances using ::OrthancPluginRegisterOnStoredInstanceCallback(). * - Possibly register its callback for changes to the DICOM store using ::OrthancPluginRegisterOnChangeCallback(). * - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea2(). - * - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV2(). + * - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV3(). * - Possibly register a handler for C-Find SCP using OrthancPluginRegisterFindCallback(). * - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback(). * - Possibly register a handler for C-Move SCP using OrthancPluginRegisterMoveCallback(). @@ -116,7 +116,7 @@ #endif #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 -#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 9 +#define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 10 #define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 0 @@ -518,12 +518,13 @@ _OrthancPluginService_GetInstanceDicomWebXml = 4019, /* New in Orthanc 1.7.0 */ /* Services for plugins implementing a database back-end */ - _OrthancPluginService_RegisterDatabaseBackend = 5000, + _OrthancPluginService_RegisterDatabaseBackend = 5000, /* New in Orthanc 0.8.6 */ _OrthancPluginService_DatabaseAnswer = 5001, - _OrthancPluginService_RegisterDatabaseBackendV2 = 5002, + _OrthancPluginService_RegisterDatabaseBackendV2 = 5002, /* New in Orthanc 0.9.4 */ _OrthancPluginService_StorageAreaCreate = 5003, _OrthancPluginService_StorageAreaRead = 5004, _OrthancPluginService_StorageAreaRemove = 5005, + _OrthancPluginService_RegisterDatabaseBackendV3 = 5006, /* New in Orthanc 1.10.0 */ /* Primitives for handling images */ _OrthancPluginService_GetImagePixelFormat = 6000,
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Tue Mar 16 17:58:16 2021 +0100 @@ -131,7 +131,7 @@ virtual std::string GetPublicId(int64_t resourceId) = 0; - virtual uint64_t GetResourceCount(ResourceType resourceType) = 0; + virtual uint64_t GetResourcesCount(ResourceType resourceType) = 0; virtual ResourceType GetResourceType(int64_t resourceId) = 0;
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Tue Mar 16 17:58:16 2021 +0100 @@ -671,7 +671,7 @@ } - virtual uint64_t GetResourceCount(ResourceType resourceType) ORTHANC_OVERRIDE + virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE { SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT COUNT(*) FROM Resources WHERE resourceType=?");
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Tue Mar 16 17:58:16 2021 +0100 @@ -1110,10 +1110,10 @@ { tuple.get<0>() = transaction.GetTotalCompressedSize(); tuple.get<1>() = transaction.GetTotalUncompressedSize(); - tuple.get<2>() = transaction.GetResourceCount(ResourceType_Patient); - tuple.get<3>() = transaction.GetResourceCount(ResourceType_Study); - tuple.get<4>() = transaction.GetResourceCount(ResourceType_Series); - tuple.get<5>() = transaction.GetResourceCount(ResourceType_Instance); + tuple.get<2>() = transaction.GetResourcesCount(ResourceType_Patient); + tuple.get<3>() = transaction.GetResourcesCount(ResourceType_Study); + tuple.get<4>() = transaction.GetResourcesCount(ResourceType_Series); + tuple.get<5>() = transaction.GetResourcesCount(ResourceType_Instance); } }; @@ -2588,7 +2588,7 @@ if (maximumPatients != 0) { - uint64_t patientCount = transaction.GetResourceCount(ResourceType_Patient); + uint64_t patientCount = transaction.GetResourcesCount(ResourceType_Patient); if (patientCount > maximumPatients) { return true;
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Tue Mar 16 12:43:43 2021 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Tue Mar 16 17:58:16 2021 +0100 @@ -207,9 +207,9 @@ return transaction_.GetPublicId(resourceId); } - uint64_t GetResourceCount(ResourceType resourceType) + uint64_t GetResourcesCount(ResourceType resourceType) { - return transaction_.GetResourceCount(resourceType); + return transaction_.GetResourcesCount(resourceType); } ResourceType GetResourceType(int64_t resourceId)