changeset 85:1012fe77241c db-changes

new extension implemented for PostgreSQL and SQLite: GetLastChangeIndex
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 10 Jan 2019 18:04:12 +0100
parents 122f22550521
children d16e94157efe
files Framework/Plugins/IndexBackend.cpp Framework/Plugins/IndexBackend.h Framework/Plugins/OrthancCppDatabasePlugin.h PostgreSQL/CMakeLists.txt PostgreSQL/Plugins/GetLastChangeIndex.sql PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/PostgreSQLIndex.h SQLite/Plugins/SQLiteIndex.cpp SQLite/Plugins/SQLiteIndex.h
diffstat 9 files changed, 129 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Plugins/IndexBackend.cpp	Thu Jan 10 13:33:33 2019 +0100
+++ b/Framework/Plugins/IndexBackend.cpp	Thu Jan 10 18:04:12 2019 +0100
@@ -1862,7 +1862,6 @@
 #endif
 
 
-#if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
   // New primitive since Orthanc 1.5.2
   void IndexBackend::GetChildrenMetadata(std::list<std::string>& target,
                                          int64_t resourceId,
@@ -1883,5 +1882,4 @@
 
     ReadListOfStrings(target, statement, args);
   }
-#endif  
 }
--- a/Framework/Plugins/IndexBackend.h	Thu Jan 10 13:33:33 2019 +0100
+++ b/Framework/Plugins/IndexBackend.h	Thu Jan 10 18:04:12 2019 +0100
@@ -277,11 +277,9 @@
       const OrthancPluginResourcesContentMetadata* metadata);
 #endif
 
-#if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
     // New primitive since Orthanc 1.5.2
     virtual void GetChildrenMetadata(std::list<std::string>& target,
                                      int64_t resourceId,
                                      int32_t metadata);
-#endif
   };
 }
--- a/Framework/Plugins/OrthancCppDatabasePlugin.h	Thu Jan 10 13:33:33 2019 +0100
+++ b/Framework/Plugins/OrthancCppDatabasePlugin.h	Thu Jan 10 18:04:12 2019 +0100
@@ -492,7 +492,10 @@
 
     virtual void ClearMainDicomTags(int64_t internalId) = 0;
 
-    virtual bool HasCreateInstance() const = 0;
+    virtual bool HasCreateInstance() const
+    {
+      return false;
+    }
 
 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
     virtual void LookupResources(const std::vector<Orthanc::DatabaseConstraint>& lookup,
@@ -524,11 +527,11 @@
 #endif
 
     
-#if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
     virtual void GetChildrenMetadata(std::list<std::string>& target,
                                      int64_t resourceId,
                                      int32_t metadata) = 0;
-#endif
+
+    virtual int64_t GetLastChangeIndex() = 0;
   };
 
 
@@ -1585,8 +1588,6 @@
 #endif    
 
     
-
-#if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1
     // New primitive since Orthanc 1.5.2
     static OrthancPluginErrorCode GetChildrenMetadata(OrthancPluginDatabaseContext* context,
                                                       void* payload,
@@ -1613,8 +1614,23 @@
       }
       ORTHANC_PLUGINS_DATABASE_CATCH      
     }
-#endif
+
+
+    // New primitive since Orthanc 1.5.2
+    static OrthancPluginErrorCode GetLastChangeIndex(int64_t* result,
+                                                     void* payload)
+    {
+      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
+      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
 
+      try
+      {
+        *result = backend->GetLastChangeIndex();
+        return OrthancPluginErrorCode_Success;
+      }
+      ORTHANC_PLUGINS_DATABASE_CATCH      
+    }
+   
 
   public:
     /**
@@ -1700,6 +1716,7 @@
       extensions.lookupResources = LookupResources;          // Fast lookup
       extensions.setResourcesContent = SetResourcesContent;  // Fast setting tags/metadata
       extensions.getChildrenMetadata = GetChildrenMetadata;
+      extensions.getLastChangeIndex = GetLastChangeIndex;
 
       if (backend.HasCreateInstance())
       {
--- a/PostgreSQL/CMakeLists.txt	Thu Jan 10 13:33:33 2019 +0100
+++ b/PostgreSQL/CMakeLists.txt	Thu Jan 10 18:04:12 2019 +0100
@@ -52,10 +52,11 @@
 
 
 EmbedResources(
-  POSTGRESQL_PREPARE_INDEX         ${CMAKE_SOURCE_DIR}/Plugins/PrepareIndex.sql
-  POSTGRESQL_CREATE_INSTANCE       ${CMAKE_SOURCE_DIR}/Plugins/CreateInstance.sql
-  POSTGRESQL_FAST_TOTAL_SIZE       ${CMAKE_SOURCE_DIR}/Plugins/FastTotalSize.sql
-  POSTGRESQL_FAST_COUNT_RESOURCES  ${CMAKE_SOURCE_DIR}/Plugins/FastCountResources.sql
+  POSTGRESQL_PREPARE_INDEX          ${CMAKE_SOURCE_DIR}/Plugins/PrepareIndex.sql
+  POSTGRESQL_CREATE_INSTANCE        ${CMAKE_SOURCE_DIR}/Plugins/CreateInstance.sql
+  POSTGRESQL_FAST_TOTAL_SIZE        ${CMAKE_SOURCE_DIR}/Plugins/FastTotalSize.sql
+  POSTGRESQL_FAST_COUNT_RESOURCES   ${CMAKE_SOURCE_DIR}/Plugins/FastCountResources.sql
+  POSTGRESQL_GET_LAST_CHANGE_INDEX  ${CMAKE_SOURCE_DIR}/Plugins/GetLastChangeIndex.sql
   )
 
 add_library(OrthancPostgreSQLIndex SHARED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PostgreSQL/Plugins/GetLastChangeIndex.sql	Thu Jan 10 18:04:12 2019 +0100
@@ -0,0 +1,27 @@
+-- In PostgreSQL, the most straightforward query would be to run:
+
+--   SELECT currval(pg_get_serial_sequence('Changes', 'seq'))".
+
+-- Unfortunately, this raises the error message "currval of sequence
+-- "changes_seq_seq" is not yet defined in this session" if no change
+-- has been inserted before the SELECT. We thus track the sequence
+-- index with a trigger.
+-- http://www.neilconway.org/docs/sequences/
+
+INSERT INTO GlobalIntegers
+SELECT 6, CAST(COALESCE(MAX(seq), 0) AS BIGINT) FROM Changes;
+
+
+CREATE FUNCTION InsertedChangeFunc() 
+RETURNS TRIGGER AS $body$
+BEGIN
+  UPDATE GlobalIntegers SET value = new.seq WHERE key = 6;
+  RETURN NULL;
+END;
+$body$ LANGUAGE plpgsql;
+
+
+CREATE TRIGGER InsertedChange
+AFTER INSERT ON Changes
+FOR EACH ROW
+EXECUTE PROCEDURE InsertedChangeFunc();
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Thu Jan 10 13:33:33 2019 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp	Thu Jan 10 18:04:12 2019 +0100
@@ -37,6 +37,7 @@
   static const GlobalProperty GlobalProperty_HasTrigramIndex = GlobalProperty_DatabaseInternal0;
   static const GlobalProperty GlobalProperty_HasCreateInstance = GlobalProperty_DatabaseInternal1;
   static const GlobalProperty GlobalProperty_HasFastCountResources = GlobalProperty_DatabaseInternal2;
+  static const GlobalProperty GlobalProperty_GetLastChangeIndex = GlobalProperty_DatabaseInternal3;
 }
 
 
@@ -236,6 +237,29 @@
       t.Commit();
     }
 
+    {
+      PostgreSQLTransaction t(*db);
+
+      // Installing this extension requires the "GlobalIntegers" table
+      // created by the "GetLastChangeIndex" extension
+      int property = 0;
+      if (!LookupGlobalIntegerProperty(property, *db, t,
+                                       Orthanc::GlobalProperty_GetLastChangeIndex) ||
+          property != 1)
+      {
+        LOG(INFO) << "Installing the GetLastChangeIndex extension";
+
+        std::string query;
+        Orthanc::EmbeddedResources::GetFileResource
+          (query, Orthanc::EmbeddedResources::POSTGRESQL_GET_LAST_CHANGE_INDEX);
+        db->Execute(query);
+
+        SetGlobalIntegerProperty(*db, t, Orthanc::GlobalProperty_GetLastChangeIndex, 1);
+      }
+
+      t.Commit();
+    }
+
     return db.release();
   }
 
@@ -394,4 +418,17 @@
     assert(result == IndexBackend::GetResourceCount(resourceType));
     return result;
   }
+
+
+  int64_t PostgreSQLIndex::GetLastChangeIndex()
+  {
+    DatabaseManager::CachedStatement statement(
+      STATEMENT_FROM_HERE, GetManager(),
+      "SELECT value FROM GlobalIntegers WHERE key = 6");
+
+    statement.SetReadOnly(true);
+    statement.Execute();
+
+    return ReadInteger64(statement, 0);
+  }
 }
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h	Thu Jan 10 13:33:33 2019 +0100
+++ b/PostgreSQL/Plugins/PostgreSQLIndex.h	Thu Jan 10 18:04:12 2019 +0100
@@ -94,5 +94,7 @@
 
     virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType)
       ORTHANC_OVERRIDE;
+
+    virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE;
   };
 }
--- a/SQLite/Plugins/SQLiteIndex.cpp	Thu Jan 10 13:33:33 2019 +0100
+++ b/SQLite/Plugins/SQLiteIndex.cpp	Thu Jan 10 18:04:12 2019 +0100
@@ -21,6 +21,7 @@
 
 #include "SQLiteIndex.h"
 
+#include "../../Framework/Common/Integer64Value.h"
 #include "../../Framework/Plugins/GlobalProperties.h"
 #include "../../Framework/SQLite/SQLiteDatabase.h"
 #include "../../Framework/SQLite/SQLiteTransaction.h"
@@ -173,4 +174,35 @@
 
     return dynamic_cast<SQLiteDatabase&>(statement.GetDatabase()).GetLastInsertRowId();
   }
+
+
+  int64_t SQLiteIndex::GetLastChangeIndex()
+  {
+    DatabaseManager::CachedStatement statement(
+      STATEMENT_FROM_HERE, GetManager(),
+      "SELECT seq FROM sqlite_sequence WHERE name='Changes'");
+
+    statement.SetReadOnly(true);
+    statement.Execute();
+    
+    if (statement.IsDone())
+    {
+      // No change has been recorded so far in the database
+      return 0;
+    }
+    else
+    {
+      const IValue& value = statement.GetResultField(0);
+      
+      switch (value.GetType())
+      {
+        case ValueType_Integer64:
+          return dynamic_cast<const Integer64Value&>(value).GetValue();
+          
+        default:
+          //LOG(ERROR) << value.Format();
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+  }
 }
--- a/SQLite/Plugins/SQLiteIndex.h	Thu Jan 10 13:33:33 2019 +0100
+++ b/SQLite/Plugins/SQLiteIndex.h	Thu Jan 10 18:04:12 2019 +0100
@@ -73,5 +73,8 @@
 
     virtual int64_t CreateResource(const char* publicId,
                                    OrthancPluginResourceType type);
+
+    // New primitive since Orthanc 1.5.2
+    virtual int64_t GetLastChangeIndex();
   };
 }