changeset 76:f6ca4c202c1a

Support version 6 of the database schema
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 20 Oct 2015 16:40:33 +0200
parents d8b0679333d4
children 913478499a72
files CMakeLists.txt IndexPlugin/Plugin.cpp IndexPlugin/PostgreSQLPrepare.sql IndexPlugin/PostgreSQLVersion5.sql IndexPlugin/PostgreSQLVersion6.sql IndexPlugin/PostgreSQLWrapper.cpp IndexPlugin/PostgreSQLWrapper.h NEWS StoragePlugin/Plugin.cpp
diffstat 9 files changed, 259 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sat Oct 17 12:05:41 2015 +0200
+++ b/CMakeLists.txt	Tue Oct 20 16:40:33 2015 +0200
@@ -107,6 +107,8 @@
   --system-exception 
   --namespace=OrthancPlugins
   POSTGRESQL_PREPARE ${CMAKE_CURRENT_SOURCE_DIR}/IndexPlugin/PostgreSQLPrepare.sql
+  POSTGRESQL_PREPARE_V5 ${CMAKE_CURRENT_SOURCE_DIR}/IndexPlugin/PostgreSQLVersion5.sql
+  POSTGRESQL_PREPARE_V6 ${CMAKE_CURRENT_SOURCE_DIR}/IndexPlugin/PostgreSQLVersion6.sql
   )
 
 set(CORE_SOURCES
--- a/IndexPlugin/Plugin.cpp	Sat Oct 17 12:05:41 2015 +0200
+++ b/IndexPlugin/Plugin.cpp	Tue Oct 20 16:40:33 2015 +0200
@@ -48,20 +48,8 @@
       return -1;
     }
 
-    /* Check the expected version of the database */
-    if (OrthancPluginGetExpectedDatabaseVersion(context_) != 5)
-    {
-      char info[1024];
-      sprintf(info, "This database plugin is incompatible with your version of Orthanc "
-              "expecting the DB schema version %d, but this plugin is compatible with version 5",
-              OrthancPluginGetExpectedDatabaseVersion(context_));
-      OrthancPluginLogError(context_, info);
-      return -1;      
-    }
-
     OrthancPluginSetDescription(context_, "Stores the Orthanc index into a PostgreSQL database.");
 
-
     Json::Value configuration;
     if (!OrthancPlugins::ReadConfiguration(configuration, context))
     {
--- a/IndexPlugin/PostgreSQLPrepare.sql	Sat Oct 17 12:05:41 2015 +0200
+++ b/IndexPlugin/PostgreSQLPrepare.sql	Tue Oct 20 16:40:33 2015 +0200
@@ -1,29 +1,6 @@
 -- Table "GlobalProperties" is created by the
 -- "OrthancPlugins::GlobalProperties" class
 
-CREATE TABLE Resources(
-       internalId BIGSERIAL NOT NULL PRIMARY KEY,
-       resourceType INTEGER NOT NULL,
-       publicId VARCHAR(64) NOT NULL,
-       parentId BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE
-       );
-
-CREATE TABLE MainDicomTags(
-       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
-       tagGroup INTEGER,
-       tagElement INTEGER,
-       value BYTEA,
-       PRIMARY KEY(id, tagGroup, tagElement)
-       );
-
-CREATE TABLE DicomIdentifiers(
-       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
-       tagGroup INTEGER,
-       tagElement INTEGER,
-       value BYTEA,
-       PRIMARY KEY(id, tagGroup, tagElement)
-       );
-
 CREATE TABLE Metadata(
        id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
        type INTEGER NOT NULL,
@@ -164,9 +141,3 @@
 AFTER INSERT ON Resources
 FOR EACH ROW
 EXECUTE PROCEDURE PatientAddedFunc();
-
-
-
--- Set the version of the database schema
--- The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration
-INSERT INTO GlobalProperties VALUES (1, '5');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IndexPlugin/PostgreSQLVersion5.sql	Tue Oct 20 16:40:33 2015 +0200
@@ -0,0 +1,22 @@
+CREATE TABLE Resources(
+       internalId BIGSERIAL NOT NULL PRIMARY KEY,
+       resourceType INTEGER NOT NULL,
+       publicId VARCHAR(64) NOT NULL,
+       parentId BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE
+       );
+
+CREATE TABLE MainDicomTags(
+       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value BYTEA,
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
+
+CREATE TABLE DicomIdentifiers(
+       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value BYTEA,
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IndexPlugin/PostgreSQLVersion6.sql	Tue Oct 20 16:40:33 2015 +0200
@@ -0,0 +1,22 @@
+CREATE TABLE Resources(
+       internalId BIGSERIAL NOT NULL PRIMARY KEY,
+       resourceType INTEGER NOT NULL,
+       publicId VARCHAR(64) NOT NULL,
+       parentId BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE
+       );
+
+CREATE TABLE MainDicomTags(
+       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value TEXT,
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
+
+CREATE TABLE DicomIdentifiers(
+       id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE,
+       tagGroup INTEGER,
+       tagElement INTEGER,
+       value TEXT,
+       PRIMARY KEY(id, tagGroup, tagElement)
+       );
--- a/IndexPlugin/PostgreSQLWrapper.cpp	Sat Oct 17 12:05:41 2015 +0200
+++ b/IndexPlugin/PostgreSQLWrapper.cpp	Tue Oct 20 16:40:33 2015 +0200
@@ -110,38 +110,56 @@
 
   void PostgreSQLWrapper::Prepare()
   {
+    uint32_t expectedVersion = OrthancPluginGetExpectedDatabaseVersion(context_);
+
+    /* Check the expected version of the database */
+    if (expectedVersion != 5 && expectedVersion != 6)
+    {
+      char info[1024];
+      sprintf(info, "This database plugin is incompatible with your version of Orthanc "
+              "expecting the DB schema version %d, but this plugin is compatible with versions 5 or 6",
+              expectedVersion);
+      OrthancPluginLogError(context_, info);
+      throw PostgreSQLException(info);
+    }
+
+
     PostgreSQLTransaction t(*connection_);
 
     if (!connection_->DoesTableExist("Resources"))
     {
       std::string query;
-      EmbeddedResources::GetFileResource(query, EmbeddedResources::POSTGRESQL_PREPARE);
+
+      if (expectedVersion == 5)
+      {
+        EmbeddedResources::GetFileResource(query, EmbeddedResources::POSTGRESQL_PREPARE_V5);
+      }
+      else
+      {
+        EmbeddedResources::GetFileResource(query, EmbeddedResources::POSTGRESQL_PREPARE_V6);
+      }
 
       connection_->Execute(query);
+
+      // Execute the common initialization between versions 5 and 6
+      EmbeddedResources::GetFileResource(query, EmbeddedResources::POSTGRESQL_PREPARE);
+      connection_->Execute(query);
+
+      // Set the version of the database schema
+      // The "1" corresponds to the "GlobalProperty_DatabaseSchemaVersion" enumeration
+      connection_->Execute("INSERT INTO GlobalProperties VALUES (1, '" + 
+                           boost::lexical_cast<std::string>(expectedVersion) + "')");
+
     }
-
     
     // Check the version of the database
-    std::string version = "unknown";
-    if (!LookupGlobalProperty(version, GlobalProperty_DatabaseSchemaVersion))
-    {
-      throw PostgreSQLException("The database is corrupted. Drop it manually for Orthanc to recreate it");
-    }
+    version_ = GetDatabaseVersion();
 
-    bool ok = false;
-
-    try
+    if (version_ != 5 &&
+        version_ != 6)
     {
-      unsigned int v = boost::lexical_cast<unsigned int>(version);
-      ok = (v == 5);
-    }
-    catch (boost::bad_lexical_cast&)
-    {
-    }
-   
-    if (!ok)
-    {
-      std::string message = "Incompatible version of the Orthanc PostgreSQL database: " + version;
+      std::string message = ("Incompatible version of the Orthanc PostgreSQL database: " + 
+                             boost::lexical_cast<std::string>(version_));
       throw PostgreSQLException(message);
     }
           
@@ -542,8 +560,6 @@
 
   void PostgreSQLWrapper::GetMainDicomTags(int64_t id)
   {
-    // TODO DB V6 => Remove "DicomIdentifiers"
-
     if (getMainDicomTags1_.get() == NULL ||
         getMainDicomTags2_.get() == NULL)
     {
@@ -571,6 +587,7 @@
       }
     }
 
+    if (version_ == 5)
     {
       getMainDicomTags2_->BindInteger64(0, id);
       PostgreSQLResult result(*getMainDicomTags2_);
@@ -874,6 +891,7 @@
   }
 
 
+  // Used only if Orthanc <= 0.9.4
   void PostgreSQLWrapper::LookupIdentifier(std::list<int64_t>& target,
                                            uint16_t group,
                                            uint16_t element,
@@ -886,7 +904,15 @@
          (*connection_, "SELECT id FROM DicomIdentifiers WHERE tagGroup=$1 AND tagElement=$2 and value=$3"));
       lookupIdentifier1_->DeclareInputInteger(0);
       lookupIdentifier1_->DeclareInputInteger(1);
-      lookupIdentifier1_->DeclareInputBinary(2);
+
+      if (version_ == 5)
+      {
+        lookupIdentifier1_->DeclareInputBinary(2);
+      }
+      else
+      {
+        lookupIdentifier1_->DeclareInputString(2);
+      }
     }
 
 
@@ -904,6 +930,8 @@
     }
   }
 
+
+  // Used only if Orthanc <= 0.9.4
   void PostgreSQLWrapper::LookupIdentifier(std::list<int64_t>& target,
                                            const char* value)
   {
@@ -912,7 +940,15 @@
       lookupIdentifier2_.reset
         (new PostgreSQLStatement
          (*connection_, "SELECT id FROM DicomIdentifiers WHERE value=$1"));
-      lookupIdentifier2_->DeclareInputBinary(0);
+
+      if (version_ == 5)
+      {
+        lookupIdentifier2_->DeclareInputBinary(0);
+      }
+      else
+      {
+        lookupIdentifier2_->DeclareInputString(0);
+      }
     }
 
     lookupIdentifier2_->BindString(0, value);
@@ -928,6 +964,53 @@
   }
 
 
+  // Used only if Orthanc >= 0.9.5
+  void PostgreSQLWrapper::LookupIdentifierExact(std::list<int64_t>& target,
+                                                OrthancPluginResourceType level,
+                                                uint16_t group,
+                                                uint16_t element,
+                                                const char* value)
+  {
+    if (lookupIdentifierExact_.get() == NULL)
+    {
+      lookupIdentifierExact_.reset
+        (new PostgreSQLStatement
+         (*connection_, 
+          "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE "
+          "d.id = r.internalId AND r.resourceType=$1 AND d.tagGroup=$2 AND d.tagElement=$3 AND d.value=$4"));
+      lookupIdentifierExact_->DeclareInputInteger(0);
+      lookupIdentifierExact_->DeclareInputInteger(1);
+      lookupIdentifierExact_->DeclareInputInteger(2);
+
+      if (version_ == 5)
+      {
+        lookupIdentifierExact_->DeclareInputBinary(3);
+      }
+      else
+      {
+        lookupIdentifierExact_->DeclareInputString(3);
+      }
+    }
+
+
+    lookupIdentifierExact_->BindInteger(0, level);
+    lookupIdentifierExact_->BindInteger(1, group);
+    lookupIdentifierExact_->BindInteger(2, element);
+    lookupIdentifierExact_->BindString(3, value);
+
+    PostgreSQLResult result(*lookupIdentifierExact_);
+    target.clear();
+
+    while (!result.IsDone())
+    {
+      target.push_back(result.GetInteger64(0));
+      result.Step();
+    }
+  }
+
+
+
+
   bool PostgreSQLWrapper::LookupMetadata(std::string& target,
                                          int64_t id,
                                          int32_t type)
@@ -1094,7 +1177,15 @@
       setMainDicomTags_->DeclareInputInteger64(0);
       setMainDicomTags_->DeclareInputInteger(1);
       setMainDicomTags_->DeclareInputInteger(2);
-      setMainDicomTags_->DeclareInputBinary(3);
+
+      if (version_ == 5)
+      {
+        setMainDicomTags_->DeclareInputBinary(3);
+      }
+      else
+      {
+        setMainDicomTags_->DeclareInputString(3);
+      }
     }
 
     SetTagInternal(*setMainDicomTags_, id, group, element, value);
@@ -1113,7 +1204,15 @@
       setIdentifierTag_->DeclareInputInteger64(0);
       setIdentifierTag_->DeclareInputInteger(1);
       setIdentifierTag_->DeclareInputInteger(2);
-      setIdentifierTag_->DeclareInputBinary(3);
+
+      if (version_ == 5)
+      {
+        setIdentifierTag_->DeclareInputBinary(3);
+      }
+      else
+      {
+        setIdentifierTag_->DeclareInputString(3);
+      }
     }
 
     SetTagInternal(*setIdentifierTag_, id, group, element, value);
@@ -1261,13 +1360,73 @@
 
   uint32_t PostgreSQLWrapper::GetDatabaseVersion()
   {
-    return 5;
+    // Check the version of the database
+    std::string version = "unknown";
+    if (!LookupGlobalProperty(version, GlobalProperty_DatabaseSchemaVersion))
+    {
+      throw PostgreSQLException("The database is corrupted. Drop it manually for Orthanc to recreate it");
+    }
+
+    try
+    {
+      return boost::lexical_cast<unsigned int>(version);
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      throw PostgreSQLException("The database is corrupted. Drop it manually for Orthanc to recreate it");
+    }
   }
 
 
   void PostgreSQLWrapper::UpgradeDatabase(uint32_t  targetVersion,
                                           OrthancPluginStorageArea* storageArea)
   {
-    throw PostgreSQLException("Unsupported call to upgrade");
+    unsigned int version = GetDatabaseVersion();
+
+#if !(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER <= 0 && ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER <= 9 && ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER <= 4)
+
+    if (version == 5 && targetVersion == 6)
+    {
+      connection_->Execute("ALTER TABLE MainDicomTags ALTER COLUMN value TYPE TEXT");
+      connection_->Execute("ALTER TABLE DicomIdentifiers ALTER COLUMN value TYPE TEXT");
+      OrthancPluginReconstructMainDicomTags(context_, storageArea, OrthancPluginResourceType_Patient);
+      OrthancPluginReconstructMainDicomTags(context_, storageArea, OrthancPluginResourceType_Study);
+      OrthancPluginReconstructMainDicomTags(context_, storageArea, OrthancPluginResourceType_Series);
+      OrthancPluginReconstructMainDicomTags(context_, storageArea, OrthancPluginResourceType_Instance);
+      connection_->Execute("DELETE FROM GlobalProperties WHERE property=1");      
+      connection_->Execute("INSERT INTO GlobalProperties VALUES (1, '6');");
+      version_ = 6;
+      return;  // Success
+    }
+
+#endif
+
+    std::string message = ("Unsupported call to upgrade from version " + boost::lexical_cast<std::string>(version) +
+                           " to version " + boost::lexical_cast<std::string>(targetVersion) + " of the database schema");
+    throw PostgreSQLException(message.c_str());
+  }
+
+
+  void PostgreSQLWrapper::ClearMainDicomTags(int64_t id)
+  {
+    if (clearMainDicomTags_.get() == NULL ||
+        clearDicomIdentifiers_.get() == NULL)
+    {
+      clearMainDicomTags_.reset
+        (new PostgreSQLStatement
+         (*connection_, "DELETE FROM MainDicomTags WHERE id=$1"));
+      clearMainDicomTags_->DeclareInputInteger64(0);
+    
+      clearDicomIdentifiers_.reset
+        (new PostgreSQLStatement
+         (*connection_, "DELETE FROM DicomIdentifiers WHERE id=$1"));
+      clearDicomIdentifiers_->DeclareInputInteger64(0);
+    }
+
+    clearMainDicomTags_->BindInteger64(0, id);
+    clearMainDicomTags_->Run();
+
+    clearDicomIdentifiers_->BindInteger64(0, id);
+    clearDicomIdentifiers_->Run();
   }
 }
--- a/IndexPlugin/PostgreSQLWrapper.h	Sat Oct 17 12:05:41 2015 +0200
+++ b/IndexPlugin/PostgreSQLWrapper.h	Tue Oct 20 16:40:33 2015 +0200
@@ -36,6 +36,7 @@
   {
   private:
     OrthancPluginContext*  context_;
+    uint32_t version_;
 
     std::auto_ptr<PostgreSQLConnection> connection_;
     std::auto_ptr<PostgreSQLTransaction>  transaction_;
@@ -71,6 +72,7 @@
     std::auto_ptr<PostgreSQLStatement> lookupAttachment_;
     std::auto_ptr<PostgreSQLStatement> lookupIdentifier1_;
     std::auto_ptr<PostgreSQLStatement> lookupIdentifier2_;
+    std::auto_ptr<PostgreSQLStatement> lookupIdentifierExact_;  // New in Orthanc 0.9.5
     std::auto_ptr<PostgreSQLStatement> lookupMetadata_;
     std::auto_ptr<PostgreSQLStatement> lookupParent_;
     std::auto_ptr<PostgreSQLStatement> lookupResource_;
@@ -86,9 +88,12 @@
     std::auto_ptr<PostgreSQLStatement> clearDeletedFiles_;
     std::auto_ptr<PostgreSQLStatement> clearDeletedResources_;
     std::auto_ptr<PostgreSQLStatement> clearRemainingAncestor_;
+    std::auto_ptr<PostgreSQLStatement> clearMainDicomTags_;
+    std::auto_ptr<PostgreSQLStatement> clearDicomIdentifiers_;
     std::auto_ptr<PostgreSQLStatement> getDeletedFiles_;
     std::auto_ptr<PostgreSQLStatement> getDeletedResources_;
     std::auto_ptr<PostgreSQLStatement> getRemainingAncestor_;
+
  
     void Prepare();
 
@@ -210,14 +215,23 @@
       return globalProperties_.LookupGlobalProperty(target, property);
     }
 
+    // Used only if Orthanc <= 0.9.4
     virtual void LookupIdentifier(std::list<int64_t>& result,
                                   uint16_t group,
                                   uint16_t element,
                                   const char* value);
 
+    // Used only if Orthanc <= 0.9.4
     virtual void LookupIdentifier(std::list<int64_t>& result,
                                   const char* value);
 
+    // Used only if Orthanc >= 0.9.5
+    virtual void LookupIdentifierExact(std::list<int64_t>& result,
+                                       OrthancPluginResourceType level,
+                                       uint16_t group,
+                                       uint16_t element,
+                                       const char* value);
+
     virtual bool LookupMetadata(std::string& target,
                                 int64_t id,
                                 int32_t type);
@@ -286,7 +300,11 @@
 
     virtual uint32_t GetDatabaseVersion();
 
+    // Used only if Orthanc >= 0.9.5
     virtual void UpgradeDatabase(uint32_t  targetVersion,
                                  OrthancPluginStorageArea* storageArea);
+
+    // Used only if Orthanc >= 0.9.5
+    virtual void ClearMainDicomTags(int64_t id);
   };
 }
--- a/NEWS	Sat Oct 17 12:05:41 2015 +0200
+++ b/NEWS	Tue Oct 20 16:40:33 2015 +0200
@@ -1,6 +1,9 @@
 Pending changes in the mainline
 ===============================
 
+* Support version 6 of the database schema
+* The "value" column of tables "MainDicomTags" and "DicomIdentifiers" are now TEXT instead of BYTEA
+
 
 Release 1.3 (2015/10/07)
 ========================
--- a/StoragePlugin/Plugin.cpp	Sat Oct 17 12:05:41 2015 +0200
+++ b/StoragePlugin/Plugin.cpp	Tue Oct 20 16:40:33 2015 +0200
@@ -29,14 +29,14 @@
 static OrthancPlugins::PostgreSQLStorageArea* storage_ = NULL;
 
 
-#if (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER >= 9 && ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= 5)
+#if (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER <= 0 && ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER <= 9 && ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER <= 4)
+#  define RETURN_TYPE     int32_t
+#  define RETURN_SUCCESS  0
+#  define RETURN_FAILURE  -1
+#else
 #  define RETURN_TYPE     OrthancPluginErrorCode
 #  define RETURN_SUCCESS  OrthancPluginErrorCode_Success
 #  define RETURN_FAILURE  OrthancPluginErrorCode_Plugin
-#else
-#  define RETURN_TYPE     int32_t
-#  define RETURN_SUCCESS  0
-#  define RETURN_FAILURE  -1
 #endif