# HG changeset patch # User Sebastien Jodogne # Date 1680019887 -7200 # Node ID 824d70ce85ff421d1b9bceb1e30232e889a002a5 # Parent 4a39850887232d6dbc240f4e64d92ac63b3f741f implemented database operations diff -r 4a3985088723 -r 824d70ce85ff Framework/Plugins/DatabaseBackendAdapterV3.h --- a/Framework/Plugins/DatabaseBackendAdapterV3.h Tue Mar 28 14:51:17 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV3.h Tue Mar 28 18:11:27 2023 +0200 @@ -49,7 +49,6 @@ } public: - class Adapter; class Transaction; class Factory : public IDatabaseBackendOutput::IFactory diff -r 4a3985088723 -r 824d70ce85ff Framework/Plugins/DatabaseBackendAdapterV4.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV4.cpp Tue Mar 28 14:51:17 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp Tue Mar 28 18:11:27 2023 +0200 @@ -25,10 +25,11 @@ #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) +#include "IndexConnectionsPool.h" + #include // Include protobuf messages #include -#include #include #include @@ -37,23 +38,190 @@ #include -#define ORTHANC_PLUGINS_DATABASE_CATCH(context) \ +#define ORTHANC_PLUGINS_DATABASE_CATCH(context) \ namespace OrthancDatabases { static bool isBackendInUse_ = false; // Only for sanity checks + + class Output : public IDatabaseBackendOutput + { + private: + Orthanc::DatabasePluginMessages::DeleteAttachment::Response* deleteAttachment_; + + void Clear() + { + deleteAttachment_ = NULL; + } + + public: + Output(Orthanc::DatabasePluginMessages::DeleteAttachment::Response& deleteAttachment) + { + Clear(); + deleteAttachment_ = &deleteAttachment; + } + + virtual void SignalDeletedAttachment(const std::string& uuid, + int32_t contentType, + uint64_t uncompressedSize, + const std::string& uncompressedHash, + int32_t compressionType, + uint64_t compressedSize, + const std::string& compressedHash) ORTHANC_OVERRIDE + { + Orthanc::DatabasePluginMessages::FileInfo* attachment; + if (deleteAttachment_ != NULL) + { + if (deleteAttachment_->has_deleted_attachment()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + attachment = deleteAttachment_->mutable_deleted_attachment(); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + attachment->set_uuid(uuid); + attachment->set_content_type(contentType); + attachment->set_uncompressed_size(uncompressedSize); + attachment->set_uncompressed_hash(uncompressedHash); + attachment->set_compression_type(compressionType); + attachment->set_compressed_size(compressedSize); + attachment->set_compressed_hash(compressedHash); + } + + virtual void SignalDeletedResource(const std::string& publicId, + OrthancPluginResourceType resourceType) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void SignalRemainingAncestor(const std::string& ancestorId, + OrthancPluginResourceType ancestorType) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerAttachment(const std::string& uuid, + int32_t contentType, + uint64_t uncompressedSize, + const std::string& uncompressedHash, + int32_t compressionType, + uint64_t compressedSize, + const std::string& compressedHash) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerChange(int64_t seq, + int32_t changeType, + OrthancPluginResourceType resourceType, + const std::string& publicId, + const std::string& date) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerDicomTag(uint16_t group, + uint16_t element, + const std::string& value) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerExportedResource(int64_t seq, + OrthancPluginResourceType resourceType, + const std::string& publicId, + const std::string& modality, + const std::string& date, + const std::string& patientId, + const std::string& studyInstanceUid, + const std::string& seriesInstanceUid, + const std::string& sopInstanceUid) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerMatchingResource(const std::string& resourceId) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + virtual void AnswerMatchingResource(const std::string& resourceId, + const std::string& someInstanceId) ORTHANC_OVERRIDE + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + }; + static void ProcessDatabaseOperation(Orthanc::DatabasePluginMessages::DatabaseResponse& response, const Orthanc::DatabasePluginMessages::DatabaseRequest& request, - IndexBackend& backend) + IndexConnectionsPool& pool) { switch (request.operation()) { case Orthanc::DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION: - response.mutable_get_system_information()->set_supports_revisions(backend.HasRevisionsSupport()); + { + IndexConnectionsPool::Accessor accessor(pool); + response.mutable_get_system_information()->set_database_version(accessor.GetBackend().GetDatabaseVersion(accessor.GetManager())); + response.mutable_get_system_information()->set_supports_flush_to_disk(false); + response.mutable_get_system_information()->set_supports_revisions(accessor.GetBackend().HasRevisionsSupport()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_OPEN: + { + pool.OpenConnections(); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_CLOSE: + { + pool.CloseConnections(); break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_FLUSH_TO_DISK: + { + // Raise an exception since "set_supports_flush_to_disk(false)" + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + case Orthanc::DatabasePluginMessages::OPERATION_START_TRANSACTION: + { + std::unique_ptr transaction(new IndexConnectionsPool::Accessor(pool)); + + switch (request.start_transaction().type()) + { + case Orthanc::DatabasePluginMessages::TRANSACTION_READ_ONLY: + transaction->GetManager().StartTransaction(TransactionType_ReadOnly); + break; + + case Orthanc::DatabasePluginMessages::TRANSACTION_READ_WRITE: + transaction->GetManager().StartTransaction(TransactionType_ReadWrite); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + response.mutable_start_transaction()->set_transaction(reinterpret_cast(transaction.release())); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_UPGRADE: + { + IndexConnectionsPool::Accessor accessor(pool); + OrthancPluginStorageArea* storageArea = reinterpret_cast(request.upgrade().storage_area()); + accessor.GetBackend().UpgradeDatabase(accessor.GetManager(), request.upgrade().target_version(), storageArea); + break; + } default: LOG(ERROR) << "Not implemented database operation from protobuf: " << request.operation(); @@ -64,10 +232,65 @@ static void ProcessTransactionOperation(Orthanc::DatabasePluginMessages::TransactionResponse& response, const Orthanc::DatabasePluginMessages::TransactionRequest& request, - IndexBackend& backend) + IndexConnectionsPool::Accessor& transaction) { switch (request.operation()) { + case Orthanc::DatabasePluginMessages::OPERATION_ROLLBACK: + { + transaction.GetManager().RollbackTransaction(); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_COMMIT: + { + transaction.GetManager().CommitTransaction(); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_ADD_ATTACHMENT: + { + OrthancPluginAttachment attachment; + attachment.uuid = request.add_attachment().attachment().uuid().c_str(); + attachment.contentType = request.add_attachment().attachment().content_type(); + attachment.uncompressedSize = request.add_attachment().attachment().uncompressed_size(); + attachment.uncompressedHash = request.add_attachment().attachment().uncompressed_hash().c_str(); + attachment.compressionType = request.add_attachment().attachment().compression_type(); + attachment.compressedSize = request.add_attachment().attachment().compressed_size(); + attachment.compressedHash = request.add_attachment().attachment().compressed_hash().c_str(); + + transaction.GetBackend().AddAttachment(transaction.GetManager(), request.add_attachment().id(), attachment, + request.add_attachment().revision()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_CLEAR_CHANGES: + { + transaction.GetBackend().ClearChanges(transaction.GetManager()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_CLEAR_EXPORTED_RESOURCES: + { + transaction.GetBackend().ClearExportedResources(transaction.GetManager()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_DELETE_ATTACHMENT: + { + Output output(*response.mutable_delete_attachment()); + transaction.GetBackend().DeleteAttachment( + output, transaction.GetManager(), request.delete_attachment().id(), request.delete_attachment().type()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_DELETE_METADATA: + { + transaction.GetBackend().DeleteMetadata( + transaction.GetManager(), request.delete_metadata().id(), request.delete_metadata().type()); + break; + } + default: LOG(ERROR) << "Not implemented transaction operation from protobuf: " << request.operation(); throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); @@ -76,7 +299,7 @@ static OrthancPluginErrorCode CallBackend(OrthancPluginMemoryBuffer64* serializedResponse, - void* rawBackend, + void* rawPool, const void* requestData, uint64_t requestSize) { @@ -87,13 +310,13 @@ return OrthancPluginErrorCode_InternalError; } - if (rawBackend == NULL) + if (rawPool == NULL) { LOG(ERROR) << "Received a NULL pointer from the database"; return OrthancPluginErrorCode_InternalError; } - IndexBackend& backend = *reinterpret_cast(rawBackend); + IndexConnectionsPool& pool = *reinterpret_cast(rawPool); try { @@ -102,12 +325,15 @@ switch (request.type()) { case Orthanc::DatabasePluginMessages::REQUEST_DATABASE: - ProcessDatabaseOperation(*response.mutable_database_response(), request.database_request(), backend); + ProcessDatabaseOperation(*response.mutable_database_response(), request.database_request(), pool); break; case Orthanc::DatabasePluginMessages::REQUEST_TRANSACTION: - ProcessTransactionOperation(*response.mutable_transaction_response(), request.transaction_request(), backend); + { + IndexConnectionsPool::Accessor& transaction = *reinterpret_cast(request.transaction_request().transaction()); + ProcessTransactionOperation(*response.mutable_transaction_response(), request.transaction_request(), transaction); break; + } default: LOG(ERROR) << "Not implemented request type from protobuf: " << request.type(); @@ -120,7 +346,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Cannot serialize to protobuf"); } - if (OrthancPluginCreateMemoryBuffer64(backend.GetContext(), serializedResponse, s.size()) != OrthancPluginErrorCode_Success) + if (OrthancPluginCreateMemoryBuffer64(pool.GetContext(), serializedResponse, s.size()) != OrthancPluginErrorCode_Success) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "Cannot allocate a memory buffer"); } @@ -150,11 +376,11 @@ } } - static void FinalizeBackend(void* rawBackend) + static void FinalizeBackend(void* rawPool) { - if (rawBackend != NULL) + if (rawPool != NULL) { - IndexBackend* backend = reinterpret_cast(rawBackend); + IndexConnectionsPool* pool = reinterpret_cast(rawPool); if (isBackendInUse_) { @@ -165,7 +391,7 @@ LOG(ERROR) << "More than one index backend was registered, internal error"; } - delete backend; + delete pool; } else { @@ -178,21 +404,16 @@ size_t countConnections, unsigned int maxDatabaseRetries) { - std::unique_ptr protection(backend); + std::unique_ptr pool(new IndexConnectionsPool(backend, countConnections)); if (isBackendInUse_) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - - if (backend == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } OrthancPluginContext* context = backend->GetContext(); - if (OrthancPluginRegisterDatabaseBackendV4(context, protection.release(), maxDatabaseRetries, + if (OrthancPluginRegisterDatabaseBackendV4(context, pool.release(), maxDatabaseRetries, CallBackend, FinalizeBackend) != OrthancPluginErrorCode_Success) { delete backend; diff -r 4a3985088723 -r 824d70ce85ff Framework/Plugins/IndexConnectionsPool.h --- a/Framework/Plugins/IndexConnectionsPool.h Tue Mar 28 14:51:17 2023 +0200 +++ b/Framework/Plugins/IndexConnectionsPool.h Tue Mar 28 18:11:27 2023 +0200 @@ -26,6 +26,8 @@ #include +#include + namespace OrthancDatabases { class IndexConnectionsPool : public boost::noncopyable @@ -41,7 +43,7 @@ Orthanc::SharedMessageQueue availableConnections_; public: - IndexConnectionsPool(IndexBackend* backend, + IndexConnectionsPool(IndexBackend* backend /* takes ownership */, size_t countConnections); ~IndexConnectionsPool();