changeset 246:483af3f35a4b

turning ResultFileValue into a base class, and implement its primitives for PostgreSQL
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 14 Apr 2021 08:42:31 +0200
parents 9d00e5e073e8
children e57a5313ffe5
files Framework/Common/ResultFileValue.cpp Framework/Common/ResultFileValue.h Framework/Plugins/StorageBackend.cpp Framework/Plugins/StorageBackend.h Framework/PostgreSQL/PostgreSQLResult.cpp Framework/PostgreSQL/PostgreSQLResult.h PostgreSQL/UnitTests/PostgreSQLTests.cpp
diffstat 7 files changed, 117 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Common/ResultFileValue.cpp	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/Common/ResultFileValue.cpp	Wed Apr 14 08:42:31 2021 +0200
@@ -35,7 +35,11 @@
     switch (target)
     {
       case ValueType_BinaryString:
-        return new BinaryStringValue(content_);
+      {
+        std::string content;
+        ReadWhole(content);
+        return new BinaryStringValue(content);
+      }
 
       default:
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType);
--- a/Framework/Common/ResultFileValue.h	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/Common/ResultFileValue.h	Wed Apr 14 08:42:31 2021 +0200
@@ -27,36 +27,19 @@
 
 namespace OrthancDatabases
 {
+  /**
+   * This class is not used for MySQL, as MySQL uses BLOB columns to
+   * store files.
+   **/
   class ResultFileValue : public IValue
   {
-  private:
-    std::string  content_;
-
   public:
-    ResultFileValue()
-    {
-    }
-
-    std::string& GetContent()
-    {
-      return content_;
-    }
-
-    const std::string& GetContent() const
-    {
-      return content_;
-    }
-
-    const void* GetBuffer() const
-    {
-      return (content_.empty() ? NULL : content_.c_str());
-    }
-
-    size_t GetSize() const
-    {
-      return content_.size();
-    }
-
+    virtual void ReadWhole(std::string& target) const = 0;
+    
+    virtual void ReadRange(std::string& target,
+                           uint64_t start,
+                           size_t length) const = 0;
+    
     virtual ValueType GetType() const ORTHANC_OVERRIDE
     {
       return ValueType_ResultFile;
--- a/Framework/Plugins/StorageBackend.cpp	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/Plugins/StorageBackend.cpp	Wed Apr 14 08:42:31 2021 +0200
@@ -148,8 +148,12 @@
         switch (value.GetType())
         {
           case ValueType_ResultFile:
-            visitor.Assign(dynamic_cast<const ResultFileValue&>(value).GetContent());
+          {
+            std::string content;
+            dynamic_cast<const ResultFileValue&>(value).ReadWhole(content);
+            visitor.Assign(content);
             break;
+          }
 
           case ValueType_BinaryString:
             visitor.Assign(dynamic_cast<const BinaryStringValue&>(value).GetContent());
@@ -174,9 +178,60 @@
                                                const std::string& uuid,
                                                OrthancPluginContentType type,
                                                uint64_t start,
-                                               uint64_t length)
+                                               size_t length)
   {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    /**
+     * This is a generic implementation, that will only work if
+     * "ResultFileValue" is implemented by the database backend. For
+     * instance, this will *not* work with MySQL, as the latter uses
+     * BLOB columns to store files.
+     **/
+    DatabaseManager::Transaction transaction(manager_, TransactionType_ReadOnly);
+
+    {
+      DatabaseManager::CachedStatement statement(
+        STATEMENT_FROM_HERE, manager_,
+        "SELECT content FROM StorageArea WHERE uuid=${uuid} AND type=${type}");
+     
+      statement.SetParameterType("uuid", ValueType_Utf8String);
+      statement.SetParameterType("type", ValueType_Integer64);
+
+      Dictionary args;
+      args.SetUtf8Value("uuid", uuid);
+      args.SetIntegerValue("type", type);
+     
+      statement.Execute(args);
+
+      if (statement.IsDone())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
+      }
+      else if (statement.GetResultFieldsCount() != 1)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);        
+      }
+      else
+      {
+        const IValue& value = statement.GetResultField(0);
+        if (value.GetType() == ValueType_ResultFile)
+        {
+          std::string content;
+          dynamic_cast<const ResultFileValue&>(value).ReadRange(content, start, length);
+          visitor.Assign(content);
+        }
+        else
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);        
+        }
+      }
+    }
+
+    transaction.Commit();
+
+    if (!visitor.IsSuccess())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_Database, "Could not read attachment from the storage area");
+    }
   }
 
 
@@ -598,7 +653,7 @@
                                          const std::string& uuid,
                                          OrthancPluginContentType type,
                                          uint64_t start,
-                                         uint64_t length)
+                                         size_t length)
   {
     StringVisitor visitor(target);
     accessor.ReadRange(visitor, uuid, type, start, length);
--- a/Framework/Plugins/StorageBackend.h	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/Plugins/StorageBackend.h	Wed Apr 14 08:42:31 2021 +0200
@@ -65,7 +65,7 @@
                              const std::string& uuid,
                              OrthancPluginContentType type,
                              uint64_t start,
-                             uint64_t length) = 0;
+                             size_t length) = 0;
       
       virtual void Remove(const std::string& uuid,
                           OrthancPluginContentType type) = 0;
@@ -111,7 +111,7 @@
                              const std::string& uuid,
                              OrthancPluginContentType type,
                              uint64_t start,
-                             uint64_t length);
+                             size_t length);
       
       virtual void Remove(const std::string& uuid,
                           OrthancPluginContentType type);
@@ -148,6 +148,6 @@
                                   const std::string& uuid,
                                   OrthancPluginContentType type,
                                   uint64_t start,
-                                  uint64_t length);
+                                  size_t length);
   };
 }
--- a/Framework/PostgreSQL/PostgreSQLResult.cpp	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/PostgreSQL/PostgreSQLResult.cpp	Wed Apr 14 08:42:31 2021 +0200
@@ -170,8 +170,7 @@
   }
 
 
-  void PostgreSQLResult::GetLargeObject(std::string& result,
-                                        unsigned int column) const
+  std::string PostgreSQLResult::GetLargeObjectOid(unsigned int column) const
   {
     CheckColumn(column, OIDOID);
 
@@ -185,24 +184,44 @@
     oid = *(const Oid*) PQgetvalue(reinterpret_cast<PGresult*>(result_), position_, column);
     oid = ntohl(oid);
 
-    PostgreSQLLargeObject::Read(result, database_, boost::lexical_cast<std::string>(oid));
+    return boost::lexical_cast<std::string>(oid);
+  }
+
+
+  void PostgreSQLResult::GetLargeObjectContent(std::string& content,
+                                               unsigned int column) const
+  {
+    PostgreSQLLargeObject::Read(content, database_, GetLargeObjectOid(column));
   }
 
 
-  void PostgreSQLResult::GetLargeObject(void*& result,
-                                           size_t& size,
-                                           unsigned int column) const
+  class PostgreSQLResult::LargeObjectResult : public ResultFileValue
   {
-    CheckColumn(column, OIDOID);
+  private:
+    PostgreSQLDatabase&  database_;
+    std::string          oid_;
+
+  public:
+    LargeObjectResult(PostgreSQLDatabase& database,
+                      const std::string& oid) :
+      database_(database),
+      oid_(oid)
+    {
+    }
 
-    Oid oid;
-    assert(PQfsize(reinterpret_cast<PGresult*>(result_), column) == sizeof(oid));
+    virtual void ReadWhole(std::string& target) const ORTHANC_OVERRIDE
+    {
+      PostgreSQLLargeObject::Read(target, database_, oid_);
 
-    oid = *(const Oid*) PQgetvalue(reinterpret_cast<PGresult*>(result_), position_, column);
-    oid = ntohl(oid);
-
-    PostgreSQLLargeObject::Read(result, size, database_, boost::lexical_cast<std::string>(oid));
-  }
+    }
+    
+    virtual void ReadRange(std::string& target,
+                           uint64_t start,
+                           size_t length) const ORTHANC_OVERRIDE
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  };
 
 
   IValue* PostgreSQLResult::GetValue(unsigned int column) const
@@ -234,11 +253,7 @@
         return new BinaryStringValue(GetString(column));
 
       case OIDOID:
-      {
-        std::unique_ptr<ResultFileValue> value(new ResultFileValue);
-        GetLargeObject(value->GetContent(), column);
-        return value.release();
-      }
+        return new LargeObjectResult(database_, GetLargeObjectOid(column));
 
       default:
         throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
--- a/Framework/PostgreSQL/PostgreSQLResult.h	Tue Apr 13 18:50:44 2021 +0200
+++ b/Framework/PostgreSQL/PostgreSQLResult.h	Wed Apr 14 08:42:31 2021 +0200
@@ -32,6 +32,8 @@
   class PostgreSQLResult : public boost::noncopyable
   {
   private:
+    class LargeObjectResult;
+    
     void                *result_;  /* Object of type "PGresult*" */
     int                  position_;
     PostgreSQLDatabase&  database_;
@@ -70,12 +72,10 @@
 
     std::string GetString(unsigned int column) const;
 
-    void GetLargeObject(std::string& result,
-                        unsigned int column) const;
+    std::string GetLargeObjectOid(unsigned int column) const;
 
-    void GetLargeObject(void*& result,
-                        size_t& size,
-                        unsigned int column) const;
+    void GetLargeObjectContent(std::string& content,
+                               unsigned int column) const;
 
     IValue* GetValue(unsigned int column) const;
   };
--- a/PostgreSQL/UnitTests/PostgreSQLTests.cpp	Tue Apr 13 18:50:44 2021 +0200
+++ b/PostgreSQL/UnitTests/PostgreSQLTests.cpp	Wed Apr 14 08:42:31 2021 +0200
@@ -326,7 +326,7 @@
     ASSERT_EQ("Index 9", r.GetString(0));
 
     std::string data;
-    r.GetLargeObject(data, 1);
+    r.GetLargeObjectContent(data, 1);
     ASSERT_EQ("Value 18", data);    
 
     r.Next();