changeset 6161:c07e55229823 attach-custom-data

allow custom data to be binary
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 07 Jun 2025 18:50:42 +0200
parents 506fb1e40442
children 4cf3bfb29474
files OrthancFramework/Sources/FileStorage/FileInfo.cpp OrthancFramework/Sources/FileStorage/FileInfo.h OrthancFramework/Sources/SQLite/Statement.cpp OrthancFramework/Sources/SQLite/Statement.h OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql OrthancServer/Sources/Database/InstallRevisionAndCustomData.sql OrthancServer/Sources/Database/PrepareDatabase.sql OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/UnitTestsSources/ServerIndexTests.cpp
diffstat 18 files changed, 134 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/FileStorage/FileInfo.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancFramework/Sources/FileStorage/FileInfo.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -197,6 +197,19 @@
   }
 
 
+  void FileInfo::SwapCustomData(std::string& data)
+  {
+    if (valid_)
+    {
+      customData_.swap(data);
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
   const std::string& FileInfo::GetCustomData() const
   {
     if (valid_)
--- a/OrthancFramework/Sources/FileStorage/FileInfo.h	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancFramework/Sources/FileStorage/FileInfo.h	Sat Jun 07 18:50:42 2025 +0200
@@ -87,6 +87,8 @@
 
     void SetCustomData(const std::string& data);
 
+    void SwapCustomData(std::string& data);
+
     const std::string& GetCustomData() const;
   };
 }
--- a/OrthancFramework/Sources/SQLite/Statement.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancFramework/Sources/SQLite/Statement.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -249,6 +249,11 @@
       }
     }
 
+    void Statement::BindBlob(int col, const std::string& value)
+    {
+      BindBlob(col, value.empty() ? NULL : value.c_str(), value.size());
+    }
+
 
     int Statement::ColumnCount() const 
     {
--- a/OrthancFramework/Sources/SQLite/Statement.h	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancFramework/Sources/SQLite/Statement.h	Sat Jun 07 18:50:42 2025 +0200
@@ -131,6 +131,7 @@
       void BindString(int col, const std::string& val);
       //void BindString16(int col, const string16& value);
       void BindBlob(int col, const void* value, size_t value_len);
+      void BindBlob(int col, const std::string& value);
 
 
       // Retrieving ----------------------------------------------------------------
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -1507,7 +1507,8 @@
     }
 
     virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                            const std::string& customData) ORTHANC_OVERRIDE
+                                            const void* customData,
+                                            size_t customDataSize) ORTHANC_OVERRIDE
     {
       throw OrthancException(ErrorCode_NotImplemented);  // Not supported
     }
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -1119,7 +1119,8 @@
     }
 
     virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                            const std::string& customData) ORTHANC_OVERRIDE
+                                            const void* customData,
+                                            size_t customDataSize) ORTHANC_OVERRIDE
     {
       throw OrthancException(ErrorCode_NotImplemented);  // Not supported
     }
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -1055,13 +1055,14 @@
     }
 
     virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                            const std::string& customData) ORTHANC_OVERRIDE
+                                            const void* customData,
+                                            size_t customDataSize) ORTHANC_OVERRIDE
     {
       if (database_.GetDatabaseCapabilities().HasAttachmentCustomDataSupport())
       {
         DatabasePluginMessages::TransactionRequest request;
         request.mutable_update_attachment_custom_data()->set_uuid(attachmentUuid);
-        request.mutable_update_attachment_custom_data()->set_custom_data(customData);
+        request.mutable_update_attachment_custom_data()->set_custom_data(customData, customDataSize);
 
         DatabasePluginMessages::TransactionResponse response;
         ExecuteTransaction(response, DatabasePluginMessages::OPERATION_UPDATE_ATTACHMENT_CUSTOM_DATA, request);
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -4701,10 +4701,7 @@
   void OrthancPlugins::ApplyUpdateAttachmentCustomData(const _OrthancPluginUpdateAttachmentCustomData& parameters)
   {
     PImpl::ServerContextReference lock(*pimpl_);
-    FileInfo fileInfo;
-    std::string customData(parameters.customData, parameters.customDataSize);
-    
-    lock.GetContext().GetIndex().UpdateAttachmentCustomData(parameters.attachmentUuid, customData);
+    lock.GetContext().GetIndex().UpdateAttachmentCustomData(parameters.attachmentUuid, parameters.customData, parameters.customDataSize);
   }
 
   bool OrthancPlugins::HasKeyValueStoresSupport()
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Sat Jun 07 18:50:42 2025 +0200
@@ -1536,7 +1536,7 @@
     OrthancPluginContentType type,
     uint64_t rangeStart,
     const void* customData,
-    uint64_t customDataSize);
+    uint32_t customDataSize);
 
 
 
@@ -1557,7 +1557,7 @@
     const char* uuid,
     OrthancPluginContentType type,
     const void* customData,
-    uint64_t customDataSize);
+    uint32_t customDataSize);
 
 
   /**
@@ -9804,7 +9804,7 @@
     uint64_t    compressedSize;
     const char* compressedHash;
     const void* customData;
-    uint64_t    customDataSize;
+    uint32_t    customDataSize;
   } OrthancPluginAttachment2;
 
 
@@ -9880,8 +9880,8 @@
   typedef struct
   {
     const char*                   attachmentUuid; /* in */
-    const char*                   customData;     /* in */
-    int64_t                       customDataSize; /* in */
+    const void*                   customData;     /* in */
+    uint32_t                      customDataSize; /* in */
   } _OrthancPluginUpdateAttachmentCustomData;
 
 
@@ -9894,8 +9894,8 @@
   ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginUpdateAttachmentCustomData(
     OrthancPluginContext*         context,
     const char*                   attachmentUuid, /* in */
-    const char*                   customData,     /* in */
-    int64_t                       customDataSize  /* in */)
+    const void*                   customData,     /* in */
+    uint32_t                      customDataSize  /* in */)
   {
     _OrthancPluginUpdateAttachmentCustomData params;
     params.attachmentUuid = attachmentUuid;
--- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Sat Jun 07 18:50:42 2025 +0200
@@ -55,7 +55,7 @@
   int32   compression_type = 5;  // opaque "CompressionType" in Orthanc
   uint64  compressed_size = 6;
   string  compressed_hash = 7;
-  string  custom_data = 8;       // New in v 1.12.8
+  bytes   custom_data = 8;       // New in 1.12.8
 }
 
 enum ResourceType {
@@ -1090,7 +1090,7 @@
 message UpdateAttachmentCustomData {
   message Request {
     string uuid = 1;
-    string custom_data = 2;
+    bytes custom_data = 2;
   }
 
   message Response {
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h	Sat Jun 07 18:50:42 2025 +0200
@@ -291,7 +291,8 @@
                                  const std::string& attachmentUuid) = 0;
 
       virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                              const std::string& customData) = 0;
+                                              const void* customData,
+                                              size_t customDataSize) = 0;
 
       /**
        * If "shared" is "true", the property is shared by all the
--- a/OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql	Sat Jun 07 18:50:42 2025 +0200
@@ -29,7 +29,7 @@
 CREATE TABLE Queues (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        queueId TEXT NOT NULL,
-       value BLOB
+       value BLOB NOT NULL
 );
 
 CREATE INDEX QueuesIndex ON Queues (queueId, id);
--- a/OrthancServer/Sources/Database/InstallRevisionAndCustomData.sql	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/InstallRevisionAndCustomData.sql	Sat Jun 07 18:50:42 2025 +0200
@@ -27,7 +27,7 @@
 ALTER TABLE AttachedFiles ADD COLUMN revision INTEGER;
 
 -- Add new column for customData
-ALTER TABLE AttachedFiles ADD COLUMN customData TEXT;
+ALTER TABLE AttachedFiles ADD COLUMN customData BLOB;
 
 -- Record that this upgrade has been performed
 
--- a/OrthancServer/Sources/Database/PrepareDatabase.sql	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/PrepareDatabase.sql	Sat Jun 07 18:50:42 2025 +0200
@@ -69,7 +69,7 @@
        uncompressedMD5 TEXT,  -- New in Orthanc 0.7.3 (database v4)
        compressedMD5 TEXT,    -- New in Orthanc 0.7.3 (database v4)
        revision INTEGER,      -- New in Orthanc 1.12.8 (added in InstallRevisionAndCustomData.sql)
-       customData TEXT,       -- New in Orthanc 1.12.8 (added in InstallRevisionAndCustomData.sql)
+       customData BLOB,       -- New in Orthanc 1.12.8 (added in InstallRevisionAndCustomData.sql)
        PRIMARY KEY(id, fileType)
        );              
 
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -427,7 +427,7 @@
       s.BindString(6, attachment.GetUncompressedMD5());
       s.BindString(7, attachment.GetCompressedMD5());
       s.BindInt(8, revision);
-      s.BindString(9, attachment.GetCustomData());
+      s.BindBlob(9, attachment.GetCustomData());
       s.Run();
     }
 
@@ -1102,7 +1102,13 @@
                           s.ColumnInt64(C11_BIG_INT_2), s.ColumnString(C4_STRING_2),
                           static_cast<CompressionType>(s.ColumnInt(C8_INT_2)),
                           s.ColumnInt64(C10_BIG_INT_1), s.ColumnString(C5_STRING_3));
-            file.SetCustomData(s.ColumnString(C6_STRING_4));  // TODO_ATTACH_CUSTOM_DATA TODO TODO
+
+            std::string customData;
+            if (s.ColumnBlobAsString(C6_STRING_4, &customData))
+            {
+              file.SwapCustomData(customData);
+            }
+
             res.AddAttachment(file, s.ColumnInt(C9_INT_3));
           }; break;
 
@@ -1262,7 +1268,13 @@
                           s.ColumnInt64(C11_BIG_INT_2), s.ColumnString(C4_STRING_2),
                           static_cast<CompressionType>(s.ColumnInt(C8_INT_2)),
                           s.ColumnInt64(C10_BIG_INT_1), s.ColumnString(C5_STRING_3));
-            file.SetCustomData(s.ColumnString(C6_STRING_4));  // TODO_ATTACH_CUSTOM_DATA TODO TODO
+
+            std::string customData;
+            if (s.ColumnBlobAsString(C6_STRING_4, &customData))
+            {
+              file.SwapCustomData(customData);
+            }
+
             res.AddOneInstanceAttachment(file);
           }; break;
 
@@ -1384,7 +1396,10 @@
     
       if (s.Step())
       { 
-        customData = s.ColumnString(0);
+        if (!s.ColumnBlobAsString(C6_STRING_4, &customData))
+        {
+          customData.clear();
+        }
       }
       else
       {
@@ -1799,7 +1814,13 @@
                               static_cast<CompressionType>(s.ColumnInt(2)),
                               s.ColumnInt64(3),
                               s.ColumnString(5));
-        attachment.SetCustomData(s.ColumnString(7));  // TODO_ATTACH_CUSTOM_DATA TODO TODO
+
+        std::string customData;
+        if (s.ColumnBlobAsString(7, &customData))
+        {
+          attachment.SwapCustomData(customData);
+        }
+
         revision = s.ColumnInt(6);
         return true;
       }
@@ -1827,18 +1848,25 @@
                               static_cast<CompressionType>(s.ColumnInt(2)),
                               s.ColumnInt64(3),
                               s.ColumnString(5));
-        attachment.SetCustomData(s.ColumnString(7));  // TODO_ATTACH_CUSTOM_DATA TODO TODO
+
+        std::string customData;
+        if (s.ColumnBlobAsString(7, &customData))
+        {
+          attachment.SwapCustomData(customData);
+        }
+
         revision = s.ColumnInt(6);
         return true;
       }
     }
 
     virtual void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                            const std::string& customData) ORTHANC_OVERRIDE
+                                            const void* customData,
+                                            size_t customDataSize) ORTHANC_OVERRIDE
     {
       SQLite::Statement s(db_, SQLITE_FROM_HERE, 
                           "UPDATE AttachedFiles SET customData=? WHERE uuid=?");
-      s.BindString(0, customData);
+      s.BindBlob(0, customData, customDataSize);
       s.BindString(1, attachmentUuid);
       s.Run();
     }
@@ -2376,7 +2404,7 @@
                       static_cast<CompressionType>(context.GetIntValue(3)),
                       static_cast<uint64_t>(context.GetInt64Value(4)),
                       compressedMD5);
-        info.SetCustomData(customData);
+        info.SwapCustomData(customData);
 
         sqlite_.activeTransaction_->GetListener().SignalAttachmentDeleted(info);
         sqlite_.activeTransaction_->DeleteDeletedFile(id);
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -3590,29 +3590,33 @@
   }
 
   void StatelessDatabaseOperations::UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                                               const std::string& customData)
+                                                               const void* customData,
+                                                               size_t customDataSize)
   {
     class Operations : public IReadWriteOperations
     {
     private:
       const std::string& attachmentUuid_;
-      const std::string& customData_;
+      const void*        customData_;
+      size_t             customDataSize_;
 
     public:
       Operations(const std::string& attachmentUuid,
-                 const std::string& customData) :
+                 const void* customData,
+                 size_t customDataSize) :
         attachmentUuid_(attachmentUuid),
-        customData_(customData)
+        customData_(customData),
+        customDataSize_(customDataSize)
       {
       }
 
       virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
       {
-        transaction.UpdateAttachmentCustomData(attachmentUuid_, customData_);
+        transaction.UpdateAttachmentCustomData(attachmentUuid_, customData_, customDataSize_);
       }
     };
 
-    Operations operations(attachmentUuid, customData);
+    Operations operations(attachmentUuid, customData, customDataSize);
     Apply(operations);
   }
 
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Sat Jun 07 18:50:42 2025 +0200
@@ -487,9 +487,10 @@
       }
 
       void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                      const std::string& customData)
+                                      const void* customData,
+                                      size_t customDataSize)
       {
-        return transaction_.UpdateAttachmentCustomData(attachmentUuid, customData);
+        return transaction_.UpdateAttachmentCustomData(attachmentUuid, customData, customDataSize);
       }
 
     };
@@ -592,7 +593,8 @@
                        const std::string& attachmentUuid);
 
     void UpdateAttachmentCustomData(const std::string& attachmentUuid,
-                                    const std::string& customData);
+                                    const void* customData,
+                                    size_t customDataSize);
 
     bool LookupAttachment(FileInfo& attachment,
                           int64_t& revision,
--- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp	Sat Jun 07 17:40:48 2025 +0200
+++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp	Sat Jun 07 18:50:42 2025 +0200
@@ -1318,3 +1318,42 @@
 
   db.Close();
 }
+
+
+TEST_F(DatabaseWrapperTest, BinaryCustomData)
+{
+  int64_t patient = transaction_->CreateResource("Patient", ResourceType_Patient);
+
+  {
+    FileInfo info("hello", FileContentType_Dicom, 10, "md5");
+
+    {
+      std::string blob;
+      blob.push_back(0);
+      blob.push_back(1);
+      blob.push_back(0);
+      blob.push_back(2);
+      info.SetCustomData(blob);
+    }
+
+    transaction_->AddAttachment(patient, info, 43);
+  }
+
+  {
+    FileInfo info;
+    int64_t revision;
+    ASSERT_TRUE(transaction_->LookupAttachment(info, revision, patient, FileContentType_Dicom));
+    ASSERT_EQ(43u, revision);
+    ASSERT_EQ("hello", info.GetUuid());
+    ASSERT_EQ(CompressionType_None, info.GetCompressionType());
+    ASSERT_EQ(10u, info.GetCompressedSize());
+    ASSERT_EQ("md5", info.GetCompressedMD5());
+    ASSERT_EQ(4u, info.GetCustomData().size());
+    ASSERT_EQ(0, static_cast<uint8_t>(info.GetCustomData()[0]));
+    ASSERT_EQ(1, static_cast<uint8_t>(info.GetCustomData()[1]));
+    ASSERT_EQ(0, static_cast<uint8_t>(info.GetCustomData()[2]));
+    ASSERT_EQ(2, static_cast<uint8_t>(info.GetCustomData()[3]));
+  }
+
+  transaction_->DeleteResource(patient);
+}
\ No newline at end of file