changeset 6109:370479295564 attach-custom-data

KeyValueStore: ListKeys
author Alain Mazy <am@orthanc.team>
date Thu, 15 May 2025 16:01:57 +0200
parents 21370b265a86
children e08274ea166e
files OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h
diffstat 12 files changed, 270 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Thu May 15 16:01:57 2025 +0200
@@ -1471,6 +1471,14 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual void ListKeys(std::list<std::string>& keys,
+                          const std::string& storeId,
+                          uint64_t since,
+                          uint64_t limit) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // Not supported
+    }
+
     virtual void EnqueueValue(const std::string& queueId,
                               const std::string& value) ORTHANC_OVERRIDE
     {
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Thu May 15 16:01:57 2025 +0200
@@ -1083,6 +1083,14 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual void ListKeys(std::list<std::string>& keys,
+                          const std::string& storeId,
+                          uint64_t since,
+                          uint64_t limit) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // Not supported
+    }
+
     virtual void EnqueueValue(const std::string& queueId,
                               const std::string& value) ORTHANC_OVERRIDE
     {
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Thu May 15 16:01:57 2025 +0200
@@ -1842,6 +1842,14 @@
       throw OrthancException(ErrorCode_NotImplemented);  // TODO_ATTACH_CUSTOM_DATA
     }
 
+    virtual void ListKeys(std::list<std::string>& keys,
+                          const std::string& storeId,
+                          uint64_t since,
+                          uint64_t limit) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // TODO_ATTACH_CUSTOM_DATA
+    }
+
     virtual void EnqueueValue(const std::string& queueId,
                               const std::string& value) ORTHANC_OVERRIDE
     {
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Thu May 15 16:01:57 2025 +0200
@@ -623,6 +623,22 @@
   }
 
 
+  static void CopyStringList(OrthancPluginMemoryBuffer& target,
+                             const std::list<std::string>& list)
+  {
+    Json::Value json = Json::arrayValue;
+
+    for (std::list<std::string>::const_iterator 
+           it = list.begin(); it != list.end(); ++it)
+    {
+      json.append(*it);
+    }
+        
+    std::string s;
+    Toolbox::WriteFastJson(s, json);
+    CopyToMemoryBuffer(target, s);
+  }
+
   namespace
   {
     class MemoryBufferRaii : public boost::noncopyable
@@ -4728,7 +4744,7 @@
     lock.GetContext().GetIndex().DeleteKeyValue(parameters.storeId, parameters.key);
   }
 
-  bool OrthancPlugins::ApplyGetKeyValue(const _OrthancPluginGetKeyValue& parameters)
+  void OrthancPlugins::ApplyGetKeyValue(const _OrthancPluginGetKeyValue& parameters)
   {
     PImpl::ServerContextReference lock(*pimpl_);
 
@@ -4737,12 +4753,17 @@
     if (lock.GetContext().GetIndex().GetKeyValue(value, parameters.storeId, parameters.key))
     {
       CopyToMemoryBuffer(*parameters.value, value.size() > 0 ? value.c_str() : NULL, value.size());
-      return true;
-    }
-    else
-    {
-      return false;
-    }
+    }
+  }
+
+  void OrthancPlugins::ApplyListKeys(const _OrthancPluginListKeys& parameters)
+  {
+    PImpl::ServerContextReference lock(*pimpl_);
+
+    std::list<std::string> keys;
+
+    lock.GetContext().GetIndex().ListKeys(keys, parameters.storeId, parameters.since, parameters.limit);
+    CopyStringList(*(parameters.keys), keys);
   }
 
   bool OrthancPlugins::HasQueue()
@@ -5907,14 +5928,22 @@
 
         const _OrthancPluginGetKeyValue& p =
           *reinterpret_cast<const _OrthancPluginGetKeyValue*>(parameters);
-        if (ApplyGetKeyValue(p))
-        {
-          return true;
-        }
-        else
-        {
-          throw OrthancException(ErrorCode_UnknownResource);
-        }
+        ApplyGetKeyValue(p);
+        return true;
+      }
+
+      case _OrthancPluginService_ListKeys:
+      {
+        if (!HasKeyValueStore())
+        {
+          LOG(ERROR) << "The DB engine does not support Key Value Store";
+          return false;
+        }
+
+        const _OrthancPluginListKeys& p =
+          *reinterpret_cast<const _OrthancPluginListKeys*>(parameters);
+        ApplyListKeys(p);
+        return true;
       }
 
       case _OrthancPluginService_EnqueueValue:
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Thu May 15 16:01:57 2025 +0200
@@ -235,7 +235,9 @@
 
     void ApplyDeleteKeyValue(const _OrthancPluginDeleteKeyValue& parameters);
 
-    bool ApplyGetKeyValue(const _OrthancPluginGetKeyValue& parameters);
+    void ApplyGetKeyValue(const _OrthancPluginGetKeyValue& parameters);
+
+    void ApplyListKeys(const _OrthancPluginListKeys& parameters);
 
     bool HasQueue();
 
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Thu May 15 16:01:57 2025 +0200
@@ -473,10 +473,11 @@
     _OrthancPluginService_StoreKeyValue = 47,                       /* New in Orthanc 1.12.99 */
     _OrthancPluginService_DeleteKeyValue = 48,                      /* New in Orthanc 1.12.99 */
     _OrthancPluginService_GetKeyValue = 49,                         /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_EnqueueValue = 50,                        /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_DequeueValue = 51,                        /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_GetAttachmentCustomData = 52,             /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_UpdateAttachmentCustomData = 53,          /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_ListKeys = 50,                            /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_EnqueueValue = 51,                        /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_DequeueValue = 52,                        /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_GetAttachmentCustomData = 53,             /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_UpdateAttachmentCustomData = 54,          /* New in Orthanc 1.12.99 */
 
 
     /* Registration of callbacks */
@@ -9984,6 +9985,40 @@
     return context->InvokeService(context, _OrthancPluginService_GetKeyValue, &params);
   }
 
+  typedef struct
+  {
+    const char*                   storeId;
+    uint64_t                      since;
+    uint64_t                      limit;
+    OrthancPluginMemoryBuffer*    keys;
+  } _OrthancPluginListKeys;
+
+
+  /**
+   * @brief List the keys from a key-value store.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param storeId A unique identifier identifying both the plugin and the store
+   * @param since The index of the first key to return when sorted alphabetically
+   * @param limit The number of keys to return (0 for no limit)
+   * @param keys The keys serialized in a json string
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginListKeys(
+    OrthancPluginContext*         context,
+    const char*                   storeId, /* in */
+    uint64_t                      since, /* in */
+    uint64_t                      limit, /* in */
+    OrthancPluginMemoryBuffer*    keys /* out */)
+  {
+    _OrthancPluginListKeys params;
+    params.storeId = storeId;
+    params.since = since;
+    params.limit = limit;
+    params.keys = keys;
+
+    return context->InvokeService(context, _OrthancPluginService_ListKeys, &params);
+  }
+
 
   typedef struct
   {
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Thu May 15 16:01:57 2025 +0200
@@ -4316,4 +4316,79 @@
     }
   }
 #endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 99)
+
+  KeyValueStore::KeyValueStore(const std::string& storeId)
+  : storeId_(storeId)
+  {
+  }
+
+  void KeyValueStore::Store(const std::string& key, const std::string& value)
+  {
+    OrthancPluginStoreKeyValue(OrthancPlugins::GetGlobalContext(),
+                               storeId_.c_str(),
+                               key.c_str(),
+                               value.c_str(),
+                               value.size());
+  }
+
+  bool KeyValueStore::Get(std::string& value, const std::string& key)
+  {
+    OrthancPlugins::MemoryBuffer valueBuffer;
+    OrthancPluginErrorCode ret = OrthancPluginGetKeyValue(OrthancPlugins::GetGlobalContext(),
+                                                          storeId_.c_str(),
+                                                          key.c_str(),
+                                                          *valueBuffer);
+
+    if (ret == OrthancPluginErrorCode_Success)
+    {
+      if (!valueBuffer.IsEmpty())
+      {
+        value.assign(valueBuffer.GetData(), valueBuffer.GetSize());
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+    
+    return false;
+  }
+
+  void KeyValueStore::Delete(const std::string& key)
+  {
+    OrthancPluginDeleteKeyValue(OrthancPlugins::GetGlobalContext(),
+                                storeId_.c_str(),
+                                key.c_str());
+  }
+
+  bool KeyValueStore::GetAllKeys(std::list<std::string>& keys, uint64_t since, uint64_t limit)
+  {
+    OrthancPlugins::MemoryBuffer keysListBuffer;
+    OrthancPluginErrorCode ret = OrthancPluginListKeys(OrthancPlugins::GetGlobalContext(),
+                                                       storeId_.c_str(),
+                                                       since,
+                                                       limit,
+                                                       *keysListBuffer);
+
+    if (ret == OrthancPluginErrorCode_Success)
+    {
+      Json::Value jsonKeys;
+      keysListBuffer.ToJson(jsonKeys);
+
+      for (Json::ArrayIndex i = 0; i < jsonKeys.size(); ++i)
+      {
+        keys.push_back(jsonKeys[i].asString());
+      }
+
+      // return true if all values have been read
+      return limit == 0 || (jsonKeys.size() < limit);
+    }
+    
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to list keys");
+  }
+#endif
+
 }
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Thu May 15 16:01:57 2025 +0200
@@ -1604,4 +1604,25 @@
     bool GetAnswerJson(Json::Value& output) const;
   };
 #endif
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 99)
+
+  class KeyValueStore : public boost::noncopyable
+  {
+  private:
+    std::string storeId_;
+
+  public:
+    KeyValueStore(const std::string& storeId);
+
+    void Store(const std::string& key, const std::string& value);
+
+    bool Get(std::string& value, const std::string& key);
+
+    void Delete(const std::string& key);
+
+    bool GetAllKeys(std::list<std::string>& keys, uint64_t since, uint64_t limit);
+  };
+#endif
+
 }
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h	Thu May 15 16:01:57 2025 +0200
@@ -449,6 +449,12 @@
                                const std::string& key) = 0;
 
       // New in Orthanc 1.12.99
+      virtual void ListKeys(std::list<std::string>& keys,
+                            const std::string& storeId,
+                            uint64_t since,
+                            uint64_t limit) = 0;
+
+      // New in Orthanc 1.12.99
       virtual void EnqueueValue(const std::string& queueId,
                                 const std::string& value) = 0;
 
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu May 15 16:01:57 2025 +0200
@@ -2144,7 +2144,7 @@
                                const std::string& key,
                                const std::string& value) ORTHANC_OVERRIDE
     {
-      SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO KeyValueStore (storeId, key, value) VALUES(?, ?, ?)");
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO KeyValueStores (storeId, key, value) VALUES(?, ?, ?)");
       s.BindString(0, storeId);
       s.BindString(1, key);
       s.BindString(2, value);
@@ -2154,7 +2154,7 @@
     virtual void DeleteKeyValue(const std::string& storeId,
                                 const std::string& key) ORTHANC_OVERRIDE
     {
-      SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM KeyValueStore WHERE storeId = ? AND key = ?");
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM KeyValueStores WHERE storeId = ? AND key = ?");
       s.BindString(0, storeId);
       s.BindString(1, key);
       s.Run();
@@ -2165,7 +2165,7 @@
                              const std::string& key) ORTHANC_OVERRIDE
     {
       SQLite::Statement s(db_, SQLITE_FROM_HERE, 
-                          "SELECT value FROM KeyValueStore WHERE storeId=? AND key=?");
+                          "SELECT value FROM KeyValueStores WHERE storeId=? AND key=?");
       s.BindString(0, storeId);
       s.BindString(1, key);
 
@@ -2182,6 +2182,25 @@
     }
 
     // New in Orthanc 1.12.99
+    virtual void ListKeys(std::list<std::string>& keys,
+                          const std::string& storeId,
+                          uint64_t since,
+                          uint64_t limit) ORTHANC_OVERRIDE
+    {
+      LookupFormatter formatter;
+
+      std::string sql = "SELECT key FROM KeyValueStores WHERE storeId=? ORDER BY key ASC " + formatter.FormatLimits(since, limit);
+      SQLite::Statement s(db_, SQLITE_FROM_HERE_DYNAMIC(sql), sql);
+      s.BindString(0, storeId);
+
+      while (s.Step())
+      {
+        keys.push_back(s.ColumnString(0));
+      }
+    }
+
+
+    // New in Orthanc 1.12.99
     virtual void EnqueueValue(const std::string& queueId,
                               const std::string& value) ORTHANC_OVERRIDE
     {
@@ -2203,10 +2222,10 @@
       switch (origin)
       {
         case QueueOrigin_Front:
-          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStore WHERE queueId=? ORDER BY id ASC LIMIT 1"));
+          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStores WHERE queueId=? ORDER BY id ASC LIMIT 1"));
           break;
         case QueueOrigin_Back:
-          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStore WHERE queueId=? ORDER BY id DESC LIMIT 1"));
+          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStores WHERE queueId=? ORDER BY id DESC LIMIT 1"));
           break;
         default:
           throw OrthancException(ErrorCode_InternalError);
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu May 15 16:01:57 2025 +0200
@@ -3421,6 +3421,28 @@
     return operations.HasFound();
   }
 
+  void StatelessDatabaseOperations::ListKeys(std::list<std::string>& keys,
+                                             const std::string& storeId,
+                                             uint64_t since,
+                                             uint64_t limit)
+  {
+    class Operations : public ReadOnlyOperationsT4<std::list<std::string>&, const std::string&, uint64_t, uint64_t>
+    {
+    public:
+      Operations()
+      {}
+
+      virtual void ApplyTuple(ReadOnlyTransaction& transaction,
+                              const Tuple& tuple) ORTHANC_OVERRIDE
+      {
+        transaction.ListKeys(tuple.get<0>(), tuple.get<1>(), tuple.get<2>(), tuple.get<3>());
+      }
+    };
+
+    Operations operations;
+    operations.Apply(*this, keys, storeId, since, limit);
+  }
+
   void StatelessDatabaseOperations::EnqueueValue(const std::string& queueId,
                                                  const std::string& value)
   {
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Wed May 14 13:11:38 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu May 15 16:01:57 2025 +0200
@@ -308,6 +308,13 @@
         return transaction_.GetKeyValue(value, storeId, key);
       }
 
+      void ListKeys(std::list<std::string>& keys,
+                    const std::string& storeId,
+                    uint64_t since,
+                    uint64_t limit)
+      {
+        return transaction_.ListKeys(keys, storeId, since, limit);
+      }
     };
 
 
@@ -796,6 +803,11 @@
                      const std::string& storeId,
                      const std::string& key);
 
+    void ListKeys(std::list<std::string>& keys,
+                  const std::string& storeId,
+                  uint64_t since,
+                  uint64_t limit);
+
     void EnqueueValue(const std::string& queueId,
                       const std::string& value);