diff OrthancServer/DatabaseWrapper.cpp @ 183:baada606da3c

databasewrapper
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2012 14:52:30 +0100
parents
children 8e673a65564d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/DatabaseWrapper.cpp	Mon Nov 12 14:52:30 2012 +0100
@@ -0,0 +1,476 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012 Medical Physics Department, CHU of Liege,
+ * Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DatabaseWrapper.h"
+
+#include "../Core/DicomFormat/DicomArray.h"
+#include "EmbeddedResources.h"
+
+#include <glog/logging.h>
+#include <stdio.h>
+
+namespace Orthanc
+{
+
+  namespace Internals
+  {
+    class SignalFileDeleted : public SQLite::IScalarFunction
+    {
+    private:
+      IServerIndexListener& listener_;
+
+    public:
+      SignalFileDeleted(IServerIndexListener& listener) :
+        listener_(listener)
+      {
+      }
+
+      virtual const char* GetName() const
+      {
+        return "SignalFileDeleted";
+      }
+
+      virtual unsigned int GetCardinality() const
+      {
+        return 1;
+      }
+
+      virtual void Compute(SQLite::FunctionContext& context)
+      {
+        listener_.SignalFileDeleted(context.GetStringValue(0));
+      }
+    };
+
+    class SignalRemainingAncestor : public SQLite::IScalarFunction
+    {
+    private:
+      bool hasRemainingAncestor_;
+      std::string remainingPublicId_;
+      ResourceType remainingType_;
+
+    public:
+      void Reset()
+      {
+        hasRemainingAncestor_ = false;
+      }
+
+      virtual const char* GetName() const
+      {
+        return "SignalRemainingAncestor";
+      }
+
+      virtual unsigned int GetCardinality() const
+      {
+        return 2;
+      }
+
+      virtual void Compute(SQLite::FunctionContext& context)
+      {
+        VLOG(1) << "There exists a remaining ancestor with public ID \""
+                << context.GetStringValue(0)
+                << "\" of type "
+                << context.GetIntValue(1);
+
+        if (!hasRemainingAncestor_ ||
+            remainingType_ >= context.GetIntValue(1))
+        {
+          hasRemainingAncestor_ = true;
+          remainingPublicId_ = context.GetStringValue(0);
+          remainingType_ = static_cast<ResourceType>(context.GetIntValue(1));
+        }
+      }
+
+      bool HasRemainingAncestor() const
+      {
+        return hasRemainingAncestor_;
+      }
+
+      const std::string& GetRemainingAncestorId() const
+      {
+        assert(hasRemainingAncestor_);
+        return remainingPublicId_;
+      }
+
+      ResourceType GetRemainingAncestorType() const
+      {
+        assert(hasRemainingAncestor_);
+        return remainingType_;
+      }
+    };
+  }
+
+
+  
+  void DatabaseWrapper::SetGlobalProperty(const std::string& name,
+                                          const std::string& value)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)");
+    s.BindString(0, name);
+    s.BindString(1, value);
+    s.Run();
+  }
+
+  bool DatabaseWrapper::FindGlobalProperty(std::string& target,
+                                           const std::string& name)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                        "SELECT value FROM GlobalProperties WHERE name=?");
+    s.BindString(0, name);
+
+    if (!s.Step())
+    {
+      return false;
+    }
+    else
+    {
+      target = s.ColumnString(0);
+      return true;
+    }
+  }
+
+  std::string DatabaseWrapper::GetGlobalProperty(const std::string& name,
+                                                 const std::string& defaultValue)
+  {
+    std::string s;
+    if (FindGlobalProperty(s, name))
+    {
+      return s;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+  int64_t DatabaseWrapper::CreateResource(const std::string& publicId,
+                                          ResourceType type)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)");
+    s.BindInt(0, type);
+    s.BindString(1, publicId);
+    s.Run();
+    return db_.GetLastInsertRowId();
+  }
+
+  bool DatabaseWrapper::FindResource(const std::string& publicId,
+                                     int64_t& id,
+                                     ResourceType& type)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                        "SELECT internalId, resourceType FROM Resources WHERE publicId=?");
+    s.BindString(0, publicId);
+
+    if (!s.Step())
+    {
+      return false;
+    }
+    else
+    {
+      id = s.ColumnInt(0);
+      type = static_cast<ResourceType>(s.ColumnInt(1));
+
+      // Check whether there is a single resource with this public id
+      assert(!s.Step());
+
+      return true;
+    }
+  }
+
+  void DatabaseWrapper::AttachChild(int64_t parent,
+                                    int64_t child)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?");
+    s.BindInt(0, parent);
+    s.BindInt(1, child);
+    s.Run();
+  }
+
+  void DatabaseWrapper::DeleteResource(int64_t id)
+  {
+    signalRemainingAncestor_->Reset();
+
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?");
+    s.BindInt(0, id);
+    s.Run();
+
+    if (signalRemainingAncestor_->HasRemainingAncestor())
+    {
+      listener_.SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(),
+                                        signalRemainingAncestor_->GetRemainingAncestorId());
+    }
+  }
+
+  void DatabaseWrapper::SetMetadata(int64_t id,
+                                    MetadataType type,
+                                    const std::string& value)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)");
+    s.BindInt(0, id);
+    s.BindInt(1, type);
+    s.BindString(2, value);
+    s.Run();
+  }
+
+  bool DatabaseWrapper::FindMetadata(std::string& target,
+                                     int64_t id,
+                                     MetadataType type)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                        "SELECT value FROM Metadata WHERE id=? AND type=?");
+    s.BindInt(0, id);
+    s.BindInt(1, type);
+
+    if (!s.Step())
+    {
+      return false;
+    }
+    else
+    {
+      target = s.ColumnString(0);
+      return true;
+    }
+  }
+
+  std::string DatabaseWrapper::GetMetadata(int64_t id,
+                                           MetadataType type,
+                                           const std::string& defaultValue)
+  {
+    std::string s;
+    if (FindMetadata(s, id, type))
+    {
+      return s;
+    }
+    else
+    {
+      return defaultValue;
+    }
+  }
+
+  void DatabaseWrapper::AttachFile(int64_t id,
+                                   const std::string& name,
+                                   const std::string& fileUuid,
+                                   size_t compressedSize,
+                                   size_t uncompressedSize,
+                                   CompressionType compressionType)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?)");
+    s.BindInt(0, id);
+    s.BindString(1, name);
+    s.BindString(2, fileUuid);
+    s.BindInt(3, compressedSize);
+    s.BindInt(4, uncompressedSize);
+    s.BindInt(5, compressionType);
+    s.Run();
+  }
+
+  bool DatabaseWrapper::FindFile(int64_t id,
+                                 const std::string& name,
+                                 std::string& fileUuid,
+                                 size_t& compressedSize,
+                                 size_t& uncompressedSize,
+                                 CompressionType& compressionType)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
+                        "SELECT uuid, compressedSize, uncompressedSize, compressionType FROM AttachedFiles WHERE id=? AND name=?");
+    s.BindInt(0, id);
+    s.BindString(1, name);
+
+    if (!s.Step())
+    {
+      return false;
+    }
+    else
+    {
+      fileUuid = s.ColumnString(0);
+      compressedSize = s.ColumnInt(1);
+      uncompressedSize = s.ColumnInt(2);
+      compressionType = static_cast<CompressionType>(s.ColumnInt(3));
+      return true;
+    }
+  }
+
+  void DatabaseWrapper::SetMainDicomTags(int64_t id,
+                                         const DicomMap& tags)
+  {
+    DicomArray flattened(tags);
+    for (size_t i = 0; i < flattened.GetSize(); i++)
+    {
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)");
+      s.BindInt(0, id);
+      s.BindInt(1, flattened.GetElement(i).GetTag().GetGroup());
+      s.BindInt(2, flattened.GetElement(i).GetTag().GetElement());
+      s.BindString(3, flattened.GetElement(i).GetValue().AsString());
+      s.Run();
+    }
+  }
+
+  void DatabaseWrapper::GetMainDicomTags(DicomMap& map,
+                                         int64_t id)
+  {
+    map.Clear();
+
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?");
+    s.BindInt(0, id);
+    while (s.Step())
+    {
+      map.SetValue(s.ColumnInt(1),
+                   s.ColumnInt(2),
+                   s.ColumnString(3));
+    }
+  }
+
+
+  bool DatabaseWrapper::GetParentPublicId(std::string& result,
+                                          int64_t id)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b "
+                        "WHERE a.internalId = b.parentId AND b.internalId = ?");     
+    s.BindInt(0, id);
+
+    if (s.Step())
+    {
+      result = s.ColumnString(0);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  void DatabaseWrapper::GetChildrenPublicId(std::list<std::string>& result,
+                                            int64_t id)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b  "
+                        "WHERE a.parentId = b.internalId AND b.internalId = ?");     
+    s.BindInt(0, id);
+
+    result.clear();
+
+    while (s.Step())
+    {
+      result.push_back(s.ColumnString(0));
+    }
+  }
+
+
+  void DatabaseWrapper::LogChange(ChangeType changeType,
+                                  const std::string& publicId,
+                                  ResourceType resourceType,
+                                  const boost::posix_time::ptime& date)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)");
+    s.BindInt(0, changeType);
+    s.BindString(1, publicId);
+    s.BindInt(2, resourceType);
+    s.BindString(3, boost::posix_time::to_iso_string(date));
+    s.Run();      
+  }
+
+
+  void DatabaseWrapper::LogExportedInstance(const std::string& remoteModality,
+                                            DicomInstanceHasher& hasher,
+                                            const boost::posix_time::ptime& date)
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO ExportedInstances VALUES(NULL, ?, ?, ?, ?, ?, ?)");
+    s.BindString(0, remoteModality);
+    s.BindString(1, hasher.HashInstance());
+    s.BindString(2, hasher.GetPatientId());
+    s.BindString(3, hasher.GetStudyUid());
+    s.BindString(4, hasher.GetSeriesUid());
+    s.BindString(5, hasher.GetInstanceUid());
+    s.BindString(6, boost::posix_time::to_iso_string(date));
+    s.Run();      
+  }
+    
+
+  int64_t DatabaseWrapper::GetTableRecordCount(const std::string& table)
+  {
+    char buf[128];
+    sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str());
+    SQLite::Statement s(db_, buf);
+
+    assert(s.Step());
+    int64_t c = s.ColumnInt(0);
+    assert(!s.Step());
+
+    return c;
+  }
+
+    
+  uint64_t DatabaseWrapper::GetTotalCompressedSize()
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles");
+    s.Run();
+    return static_cast<uint64_t>(s.ColumnInt64(0));
+  }
+
+    
+  uint64_t DatabaseWrapper::GetTotalUncompressedSize()
+  {
+    SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles");
+    s.Run();
+    return static_cast<uint64_t>(s.ColumnInt64(0));
+  }
+
+
+  DatabaseWrapper::DatabaseWrapper(const std::string& path,
+                                   IServerIndexListener& listener) :
+    listener_(listener)
+  {
+    db_.Open(path);
+    Open();
+  }
+
+  DatabaseWrapper::DatabaseWrapper(IServerIndexListener& listener) :
+    listener_(listener)
+  {
+    db_.OpenInMemory();
+    Open();
+  }
+
+  void DatabaseWrapper::Open()
+  {
+    if (!db_.DoesTableExist("GlobalProperties"))
+    {
+      LOG(INFO) << "Creating the database";
+      std::string query;
+      EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE_2);
+      db_.Execute(query);
+    }
+
+    signalRemainingAncestor_ = new Internals::SignalRemainingAncestor;
+    db_.Register(signalRemainingAncestor_);
+    db_.Register(new Internals::SignalFileDeleted(listener_));
+  }
+}