changeset 375:824d70ce85ff db-protobuf

implemented database operations
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 28 Mar 2023 18:11:27 +0200
parents 4a3985088723
children 59bba5fbb425
files Framework/Plugins/DatabaseBackendAdapterV3.h Framework/Plugins/DatabaseBackendAdapterV4.cpp Framework/Plugins/IndexConnectionsPool.h
diffstat 3 files changed, 246 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 <OrthancDatabasePlugin.pb.h>  // Include protobuf messages
 
 #include <Logging.h>
-#include <MultiThreading/SharedMessageQueue.h>
 #include <OrthancException.h>
 
 #include <stdexcept>
@@ -37,23 +38,190 @@
 #include <cassert>
 
 
-#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<IndexConnectionsPool::Accessor> 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<intptr_t>(transaction.release()));
+        break;
+      }
+        
+      case Orthanc::DatabasePluginMessages::OPERATION_UPGRADE:
+      {
+        IndexConnectionsPool::Accessor accessor(pool);
+        OrthancPluginStorageArea* storageArea = reinterpret_cast<OrthancPluginStorageArea*>(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<IndexBackend*>(rawBackend);
+    IndexConnectionsPool& pool = *reinterpret_cast<IndexConnectionsPool*>(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<IndexConnectionsPool::Accessor*>(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<IndexBackend*>(rawBackend);
+      IndexConnectionsPool* pool = reinterpret_cast<IndexConnectionsPool*>(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<IndexBackend> protection(backend);
+    std::unique_ptr<IndexConnectionsPool> 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;
--- 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 <MultiThreading/SharedMessageQueue.h>
 
+#include <list>
+
 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();