diff OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp @ 4623:95ffe3b6ef7c db-changes

handling of revisions for metadata
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 16 Apr 2021 17:13:03 +0200
parents fda80844b920
children f7d5372b59b3
line wrap: on
line diff
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Fri Apr 16 10:48:57 2021 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Fri Apr 16 17:13:03 2021 +0200
@@ -1367,33 +1367,35 @@
 
 
   bool StatelessDatabaseOperations::LookupMetadata(std::string& target,
+                                                   int64_t& revision,
                                                    const std::string& publicId,
                                                    ResourceType expectedType,
                                                    MetadataType type)
   {
-    class Operations : public ReadOnlyOperationsT5<bool&, std::string&, const std::string&, ResourceType, MetadataType>
+    class Operations : public ReadOnlyOperationsT6<bool&, std::string&, int64_t&,
+                                                   const std::string&, ResourceType, MetadataType>
     {
     public:
       virtual void ApplyTuple(ReadOnlyTransaction& transaction,
                               const Tuple& tuple) ORTHANC_OVERRIDE
       {
-        ResourceType rtype;
+        ResourceType resourceType;
         int64_t id;
-        if (!transaction.LookupResource(id, rtype, tuple.get<2>()) ||
-            rtype != tuple.get<3>())
+        if (!transaction.LookupResource(id, resourceType, tuple.get<3>()) ||
+            resourceType != tuple.get<4>())
         {
           throw OrthancException(ErrorCode_UnknownResource);
         }
         else
         {
-          tuple.get<0>() = transaction.LookupMetadata(tuple.get<1>(), id, tuple.get<4>());
+          tuple.get<0>() = transaction.LookupMetadata(tuple.get<1>(), tuple.get<2>(), id, tuple.get<5>());
         }
       }
     };
 
     bool found;
     Operations operations;
-    operations.Apply(*this, found, target, publicId, expectedType, type);
+    operations.Apply(*this, found, target, revision, publicId, expectedType, type);
     return found;
   }
 
@@ -2188,91 +2190,165 @@
   }
 
 
-  void StatelessDatabaseOperations::SetMetadata(const std::string& publicId,
+  void StatelessDatabaseOperations::SetMetadata(int64_t& newRevision,
+                                                const std::string& publicId,
                                                 MetadataType type,
-                                                const std::string& value)
+                                                const std::string& value,
+                                                bool hasOldRevision,
+                                                int64_t oldRevision)
+  {
+    class Operations : public IReadWriteOperations
+    {
+    private:
+      int64_t&            newRevision_;
+      const std::string&  publicId_;
+      MetadataType        type_;
+      const std::string&  value_;
+      bool                hasOldRevision_;
+      int64_t             oldRevision_;
+
+    public:
+      Operations(int64_t& newRevision,
+                 const std::string& publicId,
+                 MetadataType type,
+                 const std::string& value,
+                 bool hasOldRevision,
+                 int64_t oldRevision) :
+        newRevision_(newRevision),
+        publicId_(publicId),
+        type_(type),
+        value_(value),
+        hasOldRevision_(hasOldRevision),
+        oldRevision_(oldRevision)
+      {
+      }
+
+      virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
+      {
+        ResourceType resourceType;
+        int64_t id;
+        if (!transaction.LookupResource(id, resourceType, publicId_))
+        {
+          throw OrthancException(ErrorCode_UnknownResource);
+        }
+        else
+        {
+          std::string oldValue;
+          int64_t expectedRevision;
+          if (transaction.LookupMetadata(oldValue, expectedRevision, id, type_))
+          {
+            if (hasOldRevision_ &&
+                expectedRevision != oldRevision_)
+            {
+              throw OrthancException(ErrorCode_Revision);
+            }
+            else
+            {
+              newRevision_ = oldRevision_ + 1;
+            }
+          }
+          else
+          {
+            // The metadata is not existing yet: Ignore "oldRevision"
+            // and initialize a new sequence of revisions
+            newRevision_ = 0;
+          }
+
+          transaction.SetMetadata(id, type_, value_, newRevision_);
+          
+          if (IsUserMetadata(type_))
+          {
+            transaction.LogChange(id, ChangeType_UpdatedMetadata, resourceType, publicId_);
+          }
+        }
+      }
+    };
+
+    Operations operations(newRevision, publicId, type, value, hasOldRevision, oldRevision);
+    Apply(operations);
+  }
+
+
+  void StatelessDatabaseOperations::OverwriteMetadata(const std::string& publicId,
+                                                      MetadataType type,
+                                                      const std::string& value)
+  {
+    int64_t newRevision;  // Unused
+    SetMetadata(newRevision, publicId, type, value, false /* no old revision */, -1 /* dummy */);
+  }
+
+
+  bool StatelessDatabaseOperations::DeleteMetadata(const std::string& publicId,
+                                                   MetadataType type,
+                                                   bool hasRevision,
+                                                   int64_t revision)
   {
     class Operations : public IReadWriteOperations
     {
     private:
       const std::string&  publicId_;
       MetadataType        type_;
-      const std::string&  value_;
+      bool                hasRevision_;
+      int64_t             revision_;
+      bool                found_;
 
     public:
       Operations(const std::string& publicId,
                  MetadataType type,
-                 const std::string& value) :
+                 bool hasRevision,
+                 int64_t revision) :
         publicId_(publicId),
         type_(type),
-        value_(value)
+        hasRevision_(hasRevision),
+        revision_(revision),
+        found_(false)
       {
       }
 
+      bool HasFound() const
+      {
+        return found_;
+      }
+
       virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
       {
-        ResourceType rtype;
+        ResourceType resourceType;
         int64_t id;
-        if (!transaction.LookupResource(id, rtype, publicId_))
+        if (!transaction.LookupResource(id, resourceType, publicId_))
         {
           throw OrthancException(ErrorCode_UnknownResource);
         }
         else
         {
-          transaction.SetMetadata(id, type_, value_);
-
-          if (IsUserMetadata(type_))
+          std::string s;
+          int64_t expectedRevision;
+          if (transaction.LookupMetadata(s, expectedRevision, id, type_))
           {
-            transaction.LogChange(id, ChangeType_UpdatedMetadata, rtype, publicId_);
+            if (hasRevision_ &&
+                expectedRevision != revision_)
+            {
+              throw OrthancException(ErrorCode_Revision);
+            }
+            
+            found_ = true;
+            transaction.DeleteMetadata(id, type_);
+
+            if (IsUserMetadata(type_))
+            {
+              transaction.LogChange(id, ChangeType_UpdatedMetadata, resourceType, publicId_);
+            }
+          }
+          else
+          {
+            found_ = false;
           }
         }
       }
     };
 
-    Operations operations(publicId, type, value);
+    Operations operations(publicId, type, hasRevision, revision);
     Apply(operations);
-  }
-
-
-  void StatelessDatabaseOperations::DeleteMetadata(const std::string& publicId,
-                                                   MetadataType type)
-  {
-    class Operations : public IReadWriteOperations
-    {
-    private:
-      const std::string&  publicId_;
-      MetadataType        type_;
-
-    public:
-      Operations(const std::string& publicId,
-                 MetadataType type) :
-        publicId_(publicId),
-        type_(type)
-      {
-      }
-
-      virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
-      {
-        ResourceType rtype;
-        int64_t id;
-        if (!transaction.LookupResource(id, rtype, publicId_))
-        {
-          throw OrthancException(ErrorCode_UnknownResource);
-        }
-        else
-        {
-          transaction.DeleteMetadata(id, type_);
-
-          if (IsUserMetadata(type_))
-          {
-            transaction.LogChange(id, ChangeType_UpdatedMetadata, rtype, publicId_);
-          }
-        }
-      }
-    };
-
-    Operations operations(publicId, type);
-    Apply(operations);
+    return operations.HasFound();
   }
 
 
@@ -2421,9 +2497,9 @@
         
       virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
       {
-        ResourceType rtype;
+        ResourceType resourceType;
         int64_t id;
-        if (!transaction.LookupResource(id, rtype, publicId_))
+        if (!transaction.LookupResource(id, resourceType, publicId_))
         {
           throw OrthancException(ErrorCode_UnknownResource);
         }
@@ -2433,7 +2509,7 @@
           
           if (IsUserContentType(type_))
           {
-            transaction.LogChange(id, ChangeType_UpdatedAttachment, rtype, publicId_);
+            transaction.LogChange(id, ChangeType_UpdatedAttachment, resourceType, publicId_);
           }
         }
       }
@@ -2512,6 +2588,24 @@
       std::unique_ptr<DicomInstanceHasher>  hasher_;
       bool                                  hasTransferSyntax_;
       DicomTransferSyntax                   transferSyntax_;
+
+      static void ReplaceMetadata(ReadWriteTransaction& transaction,
+                                  int64_t instance,
+                                  MetadataType metadata,
+                                  const std::string& value)
+      {
+        std::string oldValue;
+        int64_t oldRevision;
+        
+        if (transaction.LookupMetadata(oldValue, oldRevision, instance, metadata))
+        {
+          transaction.SetMetadata(instance, metadata, value, oldRevision + 1);
+        }
+        else
+        {
+          transaction.SetMetadata(instance, metadata, value, 0);
+        }
+      }
       
     public:
       explicit Operations(const ParsedDicomFile& dicom)
@@ -2548,7 +2642,7 @@
         transaction.ClearMainDicomTags(instance);
 
         {
-          ResourcesContent content;
+          ResourcesContent content(false /* prevent the setting of metadata */);
           content.AddResource(patient, ResourceType_Patient, summary_);
           content.AddResource(study, ResourceType_Study, summary_);
           content.AddResource(series, ResourceType_Series, summary_);
@@ -2558,7 +2652,7 @@
 
         if (hasTransferSyntax_)
         {
-          transaction.SetMetadata(instance, MetadataType_Instance_TransferSyntax, GetTransferSyntaxUid(transferSyntax_));
+          ReplaceMetadata(transaction, instance, MetadataType_Instance_TransferSyntax, GetTransferSyntaxUid(transferSyntax_));
         }
 
         const DicomValue* value;
@@ -2566,7 +2660,7 @@
             !value->IsNull() &&
             !value->IsBinary())
         {
-          transaction.SetMetadata(instance, MetadataType_Instance_SopClassUid, value->GetContent());
+          ReplaceMetadata(transaction, instance, MetadataType_Instance_SopClassUid, value->GetContent());
         }
       }
     };
@@ -2929,7 +3023,7 @@
 
       
           {
-            ResourcesContent content;
+            ResourcesContent content(true /* new resource, metadata can be set */);
       
             // Populate the tags of the newly-created resources