changeset 209:13a3863df7fa

cont
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 19 Mar 2021 17:01:38 +0100
parents 696bc0c9fddb
children a0c095a4ba7d
files Framework/Plugins/DatabaseBackendAdapterV2.cpp Framework/Plugins/IDatabaseBackend.h Framework/Plugins/IndexBackend.cpp Framework/Plugins/IndexBackend.h Framework/Plugins/IndexUnitTests.h PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/PostgreSQLIndex.h SQLite/Plugins/IndexPlugin.cpp
diffstat 8 files changed, 545 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Plugins/DatabaseBackendAdapterV2.cpp	Fri Mar 19 15:40:50 2021 +0100
+++ b/Framework/Plugins/DatabaseBackendAdapterV2.cpp	Fri Mar 19 17:01:38 2021 +0100
@@ -648,7 +648,7 @@
 
     try
     {
-      *target = backend->GetResourceCount(resourceType);
+      *target = backend->GetResourcesCount(resourceType);
       return OrthancPluginErrorCode_Success;
     }
     ORTHANC_PLUGINS_DATABASE_CATCH;
@@ -789,7 +789,18 @@
 
     try
     {
-      backend->LogChange(*change);
+      int64_t id;
+      OrthancPluginResourceType type;
+      if (!backend->LookupResource(id, type, change->publicId) ||
+          type != change->resourceType)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
+      }
+      else
+      {
+        backend->LogChange(change->changeType, id, type, change->date);
+      }
+      
       return OrthancPluginErrorCode_Success;
     }
     ORTHANC_PLUGINS_DATABASE_CATCH;
--- a/Framework/Plugins/IDatabaseBackend.h	Fri Mar 19 15:40:50 2021 +0100
+++ b/Framework/Plugins/IDatabaseBackend.h	Fri Mar 19 17:01:38 2021 +0100
@@ -110,7 +110,7 @@
 
     virtual std::string GetPublicId(int64_t resourceId) = 0;
 
-    virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) = 0;
+    virtual uint64_t GetResourcesCount(OrthancPluginResourceType resourceType) = 0;
 
     virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) = 0;
 
@@ -128,8 +128,11 @@
     virtual void ListAvailableAttachments(std::list<int32_t>& target /*out*/,
                                           int64_t id) = 0;
 
-    virtual void LogChange(const OrthancPluginChange& change) = 0;
-
+    virtual void LogChange(int32_t changeType,
+                           int64_t resourceId,
+                           OrthancPluginResourceType resourceType,
+                           const char* date) = 0;
+    
     virtual void LogExportedResource(const OrthancPluginExportedResource& resource) = 0;
     
     /* Use GetOutput().AnswerAttachment() */
--- a/Framework/Plugins/IndexBackend.cpp	Fri Mar 19 15:40:50 2021 +0100
+++ b/Framework/Plugins/IndexBackend.cpp	Fri Mar 19 17:01:38 2021 +0100
@@ -730,7 +730,7 @@
   }
 
     
-  uint64_t IndexBackend::GetResourceCount(OrthancPluginResourceType resourceType)
+  uint64_t IndexBackend::GetResourcesCount(OrthancPluginResourceType resourceType)
   {
     std::unique_ptr<DatabaseManager::CachedStatement> statement;
 
@@ -939,16 +939,11 @@
   }
 
     
-  void IndexBackend::LogChange(const OrthancPluginChange& change)
+  void IndexBackend::LogChange(int32_t changeType,
+                               int64_t resourceId,
+                               OrthancPluginResourceType resourceType,
+                               const char* date)
   {
-    int64_t id;
-    OrthancPluginResourceType type;
-    if (!LookupResource(id, type, change.publicId) ||
-        type != change.resourceType)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
-    }
-      
     DatabaseManager::CachedStatement statement(
       STATEMENT_FROM_HERE, manager_,
       "INSERT INTO Changes VALUES(${}, ${changeType}, ${id}, ${resourceType}, ${date})");
@@ -959,10 +954,10 @@
     statement.SetParameterType("date", ValueType_Utf8String);
 
     Dictionary args;
-    args.SetIntegerValue("changeType", change.changeType);
-    args.SetIntegerValue("id", id);
-    args.SetIntegerValue("resourceType", change.resourceType);
-    args.SetUtf8Value("date", change.date);
+    args.SetIntegerValue("changeType", changeType);
+    args.SetIntegerValue("id", resourceId);
+    args.SetIntegerValue("resourceType", resourceType);
+    args.SetUtf8Value("date", date);
 
     statement.Execute(args);
   }
@@ -1504,7 +1499,7 @@
 
 
   // For unit testing only!
-  uint64_t IndexBackend::GetResourcesCount()
+  uint64_t IndexBackend::GetAllResourcesCount()
   {
     std::unique_ptr<DatabaseManager::CachedStatement> statement;
 
--- a/Framework/Plugins/IndexBackend.h	Fri Mar 19 15:40:50 2021 +0100
+++ b/Framework/Plugins/IndexBackend.h	Fri Mar 19 17:01:38 2021 +0100
@@ -163,7 +163,7 @@
     
     virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE;
     
-    virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) ORTHANC_OVERRIDE;
+    virtual uint64_t GetResourcesCount(OrthancPluginResourceType resourceType) ORTHANC_OVERRIDE;
     
     virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE;
     
@@ -181,7 +181,10 @@
     virtual void ListAvailableAttachments(std::list<int32_t>& target /*out*/,
                                           int64_t id) ORTHANC_OVERRIDE;
     
-    virtual void LogChange(const OrthancPluginChange& change) ORTHANC_OVERRIDE;
+    virtual void LogChange(int32_t changeType,
+                           int64_t resourceId,
+                           OrthancPluginResourceType resourceType,
+                           const char* date) ORTHANC_OVERRIDE;
     
     virtual void LogExportedResource(const OrthancPluginExportedResource& resource) ORTHANC_OVERRIDE;
     
@@ -268,7 +271,7 @@
     virtual void ClearMainDicomTags(int64_t internalId) ORTHANC_OVERRIDE;
 
     // For unit testing only!
-    virtual uint64_t GetResourcesCount();
+    virtual uint64_t GetAllResourcesCount();
 
     // For unit testing only!
     virtual uint64_t GetUnprotectedPatientsCount();
--- a/Framework/Plugins/IndexUnitTests.h	Fri Mar 19 15:40:50 2021 +0100
+++ b/Framework/Plugins/IndexUnitTests.h	Fri Mar 19 17:01:38 2021 +0100
@@ -226,10 +226,10 @@
   c = db.CreateResource("series2", OrthancPluginResourceType_Series);
   db.AttachChild(a, c);
 
-  ASSERT_EQ(3u, db.GetResourcesCount());
-  ASSERT_EQ(0u, db.GetResourceCount(OrthancPluginResourceType_Patient));
-  ASSERT_EQ(1u, db.GetResourceCount(OrthancPluginResourceType_Study));
-  ASSERT_EQ(2u, db.GetResourceCount(OrthancPluginResourceType_Series));
+  ASSERT_EQ(3u, db.GetAllResourcesCount());
+  ASSERT_EQ(0u, db.GetResourcesCount(OrthancPluginResourceType_Patient));
+  ASSERT_EQ(1u, db.GetResourcesCount(OrthancPluginResourceType_Study));
+  ASSERT_EQ(2u, db.GetResourcesCount(OrthancPluginResourceType_Series));
 
   ASSERT_FALSE(db.GetParentPublicId(s, a));
   ASSERT_TRUE(db.GetParentPublicId(s, b));  ASSERT_EQ("study", s);
@@ -415,7 +415,7 @@
   db.GetAllPublicIds(pub, OrthancPluginResourceType_Study); ASSERT_EQ(1u, pub.size());
   db.GetAllPublicIds(pub, OrthancPluginResourceType_Series); ASSERT_EQ(2u, pub.size());
   db.GetAllPublicIds(pub, OrthancPluginResourceType_Instance); ASSERT_EQ(0u, pub.size());
-  ASSERT_EQ(3u, db.GetResourcesCount());
+  ASSERT_EQ(3u, db.GetAllResourcesCount());
 
   ASSERT_EQ(0u, db.GetUnprotectedPatientsCount());  // No patient was inserted
   ASSERT_TRUE(db.IsExistingResource(c));
@@ -432,14 +432,14 @@
   ASSERT_FALSE(db.IsExistingResource(c));
   ASSERT_TRUE(db.IsExistingResource(a));
   ASSERT_TRUE(db.IsExistingResource(b));
-  ASSERT_EQ(2u, db.GetResourcesCount());
+  ASSERT_EQ(2u, db.GetAllResourcesCount());
   db.DeleteResource(*output, a);
-  ASSERT_EQ(0u, db.GetResourcesCount());
+  ASSERT_EQ(0u, db.GetAllResourcesCount());
   ASSERT_FALSE(db.IsExistingResource(a));
   ASSERT_FALSE(db.IsExistingResource(b));
   ASSERT_FALSE(db.IsExistingResource(c));
 
-  ASSERT_EQ(0u, db.GetResourcesCount());
+  ASSERT_EQ(0u, db.GetAllResourcesCount());
   ASSERT_EQ(0u, db.GetUnprotectedPatientsCount());
   int64_t p1 = db.CreateResource("patient1", OrthancPluginResourceType_Patient);
   int64_t p2 = db.CreateResource("patient2", OrthancPluginResourceType_Patient);
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Fri Mar 19 15:40:50 2021 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Fri Mar 19 17:01:38 2021 +0100
@@ -379,7 +379,7 @@
 #endif
 
 
-  uint64_t PostgreSQLIndex::GetResourceCount(OrthancPluginResourceType resourceType)
+  uint64_t PostgreSQLIndex::GetResourcesCount(OrthancPluginResourceType resourceType)
   {
     // Optimized version thanks to the "FastCountResources.sql" extension
 
@@ -408,7 +408,7 @@
       result = static_cast<uint64_t>(ReadInteger64(statement, 0));
     }
       
-    assert(result == IndexBackend::GetResourceCount(resourceType));
+    assert(result == IndexBackend::GetResourcesCount(resourceType));
     return result;
   }
 
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h	Fri Mar 19 15:40:50 2021 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.h	Fri Mar 19 17:01:38 2021 +0100
@@ -93,8 +93,7 @@
       ORTHANC_OVERRIDE;
 #endif
 
-    virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType)
-      ORTHANC_OVERRIDE;
+    virtual uint64_t GetResourcesCount(OrthancPluginResourceType resourceType) ORTHANC_OVERRIDE;
 
     virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE;
 
--- a/SQLite/Plugins/IndexPlugin.cpp	Fri Mar 19 15:40:50 2021 +0100
+++ b/SQLite/Plugins/IndexPlugin.cpp	Fri Mar 19 17:01:38 2021 +0100
@@ -103,19 +103,67 @@
     
     void Clear()
     {
+      // We don't systematically clear all the vectors, in order to
+      // avoid spending unnecessary time
+      
+      switch (answerType_)
+      {
+        case _OrthancPluginDatabaseAnswerType_None:
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_Attachment:
+          attachments_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_Change:
+          changes_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_DicomTag:
+          tags_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_ExportedResource:
+          exported_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_Int32:
+          integers32_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_Int64:
+          integers64_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_MatchingResource:
+          matches_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_Metadata:
+          metadata_.clear();
+          break;
+        
+        case _OrthancPluginDatabaseAnswerType_String:
+          stringAnswers_.clear();
+          break;
+        
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      
       answerType_ = _OrthancPluginDatabaseAnswerType_None;
       stringsStore_.clear();
       
-      attachments_.clear();
-      changes_.clear();
-      tags_.clear();
-      exported_.clear();
-      events_.clear();
-      integers32_.clear();
-      integers64_.clear();
-      matches_.clear();
-      metadata_.clear();
-      stringAnswers_.clear();
+      assert(attachments_.empty());
+      assert(changes_.empty());
+      assert(tags_.empty());
+      assert(exported_.empty());
+      assert(events_.empty());
+      assert(integers32_.empty());
+      assert(integers64_.empty());
+      assert(matches_.empty());
+      assert(metadata_.empty());
+      assert(stringAnswers_.empty());
     }
 
 
@@ -196,8 +244,8 @@
 
 
     static OrthancPluginErrorCode ReadAnswerChange(OrthancPluginDatabaseTransaction* transaction,
-                                                       OrthancPluginChange* target /* out */,
-                                                       uint32_t index)
+                                                   OrthancPluginChange* target /* out */,
+                                                   uint32_t index)
     {
       const Output& that = *reinterpret_cast<const Output*>(transaction);
 
@@ -567,6 +615,21 @@
       stringAnswers_.reserve(values.size());
       std::copy(std::begin(values), std::end(values), std::back_inserter(stringAnswers_));
     }
+
+
+    void AnswerString(const std::string& value)
+    {
+      SetupAnswerType(_OrthancPluginDatabaseAnswerType_String);
+
+      if (stringAnswers_.empty())
+      {
+        stringAnswers_.push_back(value);
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+    }
   };
 
 
@@ -587,11 +650,19 @@
   class Transaction : public boost::noncopyable
   {
   private:
-    IDatabaseBackend&        backend_;
-    std::unique_ptr<Output>  output_;
+    boost::mutex::scoped_lock  lock_;    // TODO - REMOVE
+    IDatabaseBackend&          backend_;
+    std::unique_ptr<Output>    output_;
 
+    static boost::mutex& GetMutex()   // TODO - REMOVE
+    {
+      static boost::mutex mutex_;
+      return mutex_;
+    }
+    
   public:
     Transaction(IDatabaseBackend& backend) :
+      lock_(GetMutex()),
       backend_(backend),
       output_(new Output)
     {
@@ -965,6 +1036,398 @@
   }
 
   
+  static OrthancPluginErrorCode GetChildrenInternalId(OrthancPluginDatabaseTransaction* transaction,
+                                                      int64_t id)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::list<int64_t> values;
+      t->GetBackend().GetChildrenInternalId(values, id);
+      t->GetOutput().AnswerIntegers64(values);
+        
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetChildrenMetadata(OrthancPluginDatabaseTransaction* transaction,
+                                                    int64_t resourceId,
+                                                    int32_t metadata)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::list<std::string> values;
+      t->GetBackend().GetChildrenMetadata(values, resourceId, metadata);
+      t->GetOutput().AnswerStrings(values);
+        
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetChildrenPublicId(OrthancPluginDatabaseTransaction* transaction,
+                                                    int64_t id)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::list<std::string> values;
+      t->GetBackend().GetChildrenPublicId(values, id);
+      t->GetOutput().AnswerStrings(values);
+        
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetExportedResources(OrthancPluginDatabaseTransaction* transaction,
+                                                     uint8_t* targetDone /* out */,
+                                                     int64_t since,
+                                                     uint32_t maxResults)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      bool done;
+      t->GetBackend().GetExportedResources(t->GetOutput(), done, since, maxResults);
+      *targetDone = (done ? 1 : 0);
+        
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetLastChange(OrthancPluginDatabaseTransaction* transaction)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetBackend().GetLastChange(t->GetOutput());
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetLastChangeIndex(OrthancPluginDatabaseTransaction* transaction,
+                                                   int64_t* target)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      *target = t->GetBackend().GetLastChangeIndex();
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetLastExportedResource(OrthancPluginDatabaseTransaction* transaction)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetBackend().GetLastExportedResource(t->GetOutput());
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetMainDicomTags(OrthancPluginDatabaseTransaction* transaction,
+                                                 int64_t id)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetBackend().GetMainDicomTags(t->GetOutput(), id);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+  
+  static OrthancPluginErrorCode GetPublicId(OrthancPluginDatabaseTransaction* transaction,
+                                            int64_t id)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetOutput().AnswerString(t->GetBackend().GetPublicId(id));
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+
+  static OrthancPluginErrorCode GetResourcesCount(OrthancPluginDatabaseTransaction* transaction,
+                                                  uint64_t* target /* out */,
+                                                  OrthancPluginResourceType resourceType)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      *target = t->GetBackend().GetResourcesCount(resourceType);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode GetResourceType(OrthancPluginDatabaseTransaction* transaction,
+                                                OrthancPluginResourceType* target /* out */,
+                                                uint64_t resourceId)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      *target = t->GetBackend().GetResourceType(resourceId);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode GetTotalCompressedSize(OrthancPluginDatabaseTransaction* transaction,
+                                                       uint64_t* target /* out */)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      *target = t->GetBackend().GetTotalCompressedSize();
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode GetTotalUncompressedSize(OrthancPluginDatabaseTransaction* transaction,
+                                                         uint64_t* target /* out */)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      *target = t->GetBackend().GetTotalUncompressedSize();
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode IsDiskSizeAbove(OrthancPluginDatabaseTransaction* transaction,
+                                                uint8_t* target,
+                                                uint64_t threshold)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      bool above = (t->GetBackend().GetTotalCompressedSize() >= threshold);
+      *target = (above ? 1 : 0);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode IsExistingResource(OrthancPluginDatabaseTransaction* transaction,
+                                                   uint8_t* target,
+                                                   int64_t resourceId)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      bool exists = t->GetBackend().IsExistingResource(resourceId);
+      *target = (exists ? 1 : 0);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode IsProtectedPatient(OrthancPluginDatabaseTransaction* transaction,
+                                                   uint8_t* target,
+                                                   int64_t resourceId)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      bool isProtected = t->GetBackend().IsProtectedPatient(resourceId);
+      *target = (isProtected ? 1 : 0);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode ListAvailableAttachments(OrthancPluginDatabaseTransaction* transaction,
+                                                         int64_t resourceId)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::list<int32_t> values;
+      t->GetBackend().ListAvailableAttachments(values, resourceId);
+      t->GetOutput().AnswerIntegers32(values);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode LogChange(OrthancPluginDatabaseTransaction* transaction,
+                                          int32_t changeType,
+                                          int64_t resourceId,
+                                          OrthancPluginResourceType resourceType,
+                                          const char* date)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetBackend().LogChange(changeType, resourceId, resourceType, date);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+  
+
+  static OrthancPluginErrorCode LogExportedResource(OrthancPluginDatabaseTransaction* transaction,
+                                                    OrthancPluginResourceType resourceType,
+                                                    const char* publicId,
+                                                    const char* modality,
+                                                    const char* date,
+                                                    const char* patientId,
+                                                    const char* studyInstanceUid,
+                                                    const char* seriesInstanceUid,
+                                                    const char* sopInstanceUid)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      OrthancPluginExportedResource exported;
+      exported.seq = 0;
+      exported.resourceType = resourceType;
+      exported.publicId = publicId;
+      exported.modality = modality;
+      exported.date = date;
+      exported.patientId = patientId;
+      exported.studyInstanceUid = studyInstanceUid;
+      exported.seriesInstanceUid = seriesInstanceUid;
+      exported.sopInstanceUid = sopInstanceUid;
+        
+      t->GetOutput().Clear();
+      t->GetBackend().LogExportedResource(exported);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+
+  static OrthancPluginErrorCode LookupAttachment(OrthancPluginDatabaseTransaction* transaction,
+                                                 int64_t resourceId,
+                                                 int32_t contentType)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+      t->GetBackend().LookupAttachment(t->GetOutput(), resourceId, contentType);
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+
+  static OrthancPluginErrorCode LookupGlobalProperty(OrthancPluginDatabaseTransaction* transaction,
+                                                     int32_t property)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::string s;
+      if (t->GetBackend().LookupGlobalProperty(s, property))
+      {
+        t->GetOutput().AnswerString(s);
+      }
+      
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
+
+  static OrthancPluginErrorCode LookupMetadata(OrthancPluginDatabaseTransaction* transaction,
+                                               int64_t id,
+                                               int32_t metadata)
+  {
+    Transaction* t = reinterpret_cast<Transaction*>(transaction);
+
+    try
+    {
+      t->GetOutput().Clear();
+
+      std::string s;
+      if (t->GetBackend().LookupMetadata(s, id, metadata))
+      {
+        t->GetOutput().AnswerString(s);
+      }
+      
+      return OrthancPluginErrorCode_Success;
+    }
+    ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext());
+  }
+
 
   static void RegisterV3(IDatabaseBackend& database)
   {
@@ -1007,6 +1470,28 @@
     params.getAllPublicIds = GetAllPublicIds;
     params.getAllPublicIdsWithLimit = GetAllPublicIdsWithLimit;
     params.getChanges = GetChanges;
+    params.getChildrenInternalId = GetChildrenInternalId;
+    params.getChildrenMetadata = GetChildrenMetadata;
+    params.getChildrenPublicId = GetChildrenPublicId;
+    params.getExportedResources = GetExportedResources;
+    params.getLastChange = GetLastChange;
+    params.getLastChangeIndex = GetLastChangeIndex;
+    params.getLastExportedResource = GetLastExportedResource;
+    params.getMainDicomTags = GetMainDicomTags;
+    params.getPublicId = GetPublicId;
+    params.getResourcesCount = GetResourcesCount;
+    params.getResourceType = GetResourceType;
+    params.getTotalCompressedSize = GetTotalCompressedSize;
+    params.getTotalUncompressedSize = GetTotalUncompressedSize;
+    params.isDiskSizeAbove = IsDiskSizeAbove;
+    params.isExistingResource = IsExistingResource;
+    params.isProtectedPatient = IsProtectedPatient;
+    params.listAvailableAttachments = ListAvailableAttachments;
+    params.logChange = LogChange;
+    params.logExportedResource = LogExportedResource;
+    params.lookupAttachment = LookupAttachment;
+    params.lookupGlobalProperty = LookupGlobalProperty;
+    params.lookupMetadata = LookupMetadata;
 
     OrthancPluginContext* context = database.GetContext();