Mercurial > hg > orthanc
view OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp @ 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 | |
children | da2e0a457eae |
line wrap: on
line source
/** * 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)); } } } }