# HG changeset patch # User Alain Mazy # Date 1747317717 -7200 # Node ID 370479295564998c1f676d64743a7e7b2833b2e7 # Parent 21370b265a86f1a1d523562f434d3ab905a6e70f KeyValueStore: ListKeys diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp --- 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& 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 { diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp --- 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& 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 { diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- 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& 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 { diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Engine/OrthancPlugins.cpp --- 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& list) + { + Json::Value json = Json::arrayValue; + + for (std::list::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 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(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(parameters); + ApplyListKeys(p); + return true; } case _OrthancPluginService_EnqueueValue: diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Engine/OrthancPlugins.h --- 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(); diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h --- 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, ¶ms); } + 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, ¶ms); + } + typedef struct { diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp --- 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& 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 + } diff -r 21370b265a86 -r 370479295564 OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h --- 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& keys, uint64_t since, uint64_t limit); + }; +#endif + } diff -r 21370b265a86 -r 370479295564 OrthancServer/Sources/Database/IDatabaseWrapper.h --- 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& 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; diff -r 21370b265a86 -r 370479295564 OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp --- 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& 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); diff -r 21370b265a86 -r 370479295564 OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- 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& keys, + const std::string& storeId, + uint64_t since, + uint64_t limit) + { + class Operations : public ReadOnlyOperationsT4&, 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) { diff -r 21370b265a86 -r 370479295564 OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- 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& 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& keys, + const std::string& storeId, + uint64_t since, + uint64_t limit); + void EnqueueValue(const std::string& queueId, const std::string& value);