Mercurial > hg > orthanc
changeset 6145:055addebab2a attach-custom-data
support for blobs in key-value stores and queues
line wrap: on
line diff
--- a/OrthancFramework/Sources/SQLite/Statement.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancFramework/Sources/SQLite/Statement.cpp Fri May 30 15:38:47 2025 +0200 @@ -236,10 +236,17 @@ BindString(col, UTF16ToUTF8(value)); }*/ - void Statement::BindBlob(int col, const void* val, int val_len) + void Statement::BindBlob(int col, const void* val, size_t val_len) { - CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, val_len, SQLITE_TRANSIENT), - ErrorCode_BadParameterType); + if (static_cast<size_t>(static_cast<int>(val_len)) != val_len) + { + throw OrthancSQLiteException(ErrorCode_SQLiteBindOutOfRange); + } + else + { + CheckOk(sqlite3_bind_blob(GetStatement(), col + 1, val, static_cast<int>(val_len), SQLITE_TRANSIENT), + ErrorCode_BadParameterType); + } }
--- a/OrthancFramework/Sources/SQLite/Statement.h Fri May 30 14:28:41 2025 +0200 +++ b/OrthancFramework/Sources/SQLite/Statement.h Fri May 30 15:38:47 2025 +0200 @@ -130,7 +130,7 @@ void BindCString(int col, const char* val); void BindString(int col, const std::string& val); //void BindString16(int col, const string16& value); - void BindBlob(int col, const void* value, int value_len); + void BindBlob(int col, const void* value, size_t value_len); // Retrieving ----------------------------------------------------------------
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri May 30 15:38:47 2025 +0200 @@ -1453,7 +1453,8 @@ virtual void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { throw OrthancException(ErrorCode_InternalError); // Not supported } @@ -1482,7 +1483,8 @@ } virtual void EnqueueValue(const std::string& queueId, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { throw OrthancException(ErrorCode_InternalError); // Not supported }
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp Fri May 30 15:38:47 2025 +0200 @@ -1065,7 +1065,8 @@ virtual void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { throw OrthancException(ErrorCode_InternalError); // Not supported } @@ -1094,7 +1095,8 @@ } virtual void EnqueueValue(const std::string& queueId, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { throw OrthancException(ErrorCode_InternalError); // Not supported }
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Fri May 30 15:38:47 2025 +0200 @@ -41,7 +41,7 @@ #include "OrthancDatabasePlugin.pb.h" // Auto-generated file #include <cassert> - +#include <limits> namespace Orthanc { @@ -1860,14 +1860,22 @@ virtual void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { + // In protobuf, bytes "may contain any arbitrary sequence of bytes no longer than 2^32" + // https://protobuf.dev/programming-guides/proto3/ + if (valueSize > std::numeric_limits<uint32_t>::max()) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + if (database_.GetDatabaseCapabilities().HasKeyValueStoresSupport()) { DatabasePluginMessages::TransactionRequest request; request.mutable_store_key_value()->set_store_id(storeId); request.mutable_store_key_value()->set_key(key); - request.mutable_store_key_value()->set_value(value); + request.mutable_store_key_value()->set_value(value, valueSize); ExecuteTransaction(DatabasePluginMessages::OPERATION_STORE_KEY_VALUE, request); } @@ -1958,13 +1966,21 @@ } virtual void EnqueueValue(const std::string& queueId, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { + // In protobuf, bytes "may contain any arbitrary sequence of bytes no longer than 2^32" + // https://protobuf.dev/programming-guides/proto3/ + if (valueSize > std::numeric_limits<uint32_t>::max()) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + if (database_.GetDatabaseCapabilities().HasQueuesSupport()) { DatabasePluginMessages::TransactionRequest request; request.mutable_enqueue_value()->set_queue_id(queueId); - request.mutable_enqueue_value()->set_value(value); + request.mutable_enqueue_value()->set_value(value, valueSize); ExecuteTransaction(DatabasePluginMessages::OPERATION_ENQUEUE_VALUE, request); }
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri May 30 15:38:47 2025 +0200 @@ -1963,7 +1963,7 @@ std::string dicom; currentQuery_->SaveToMemoryBuffer(dicom); - CopyToMemoryBuffer(target, dicom.c_str(), dicom.size()); + CopyToMemoryBuffer(target, dicom); } bool IsMatch(const void* dicom, @@ -3951,7 +3951,7 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } - CopyToMemoryBuffer(*p.target, compressed.size() > 0 ? compressed.c_str() : NULL, compressed.size()); + CopyToMemoryBuffer(*p.target, compressed); } @@ -4675,8 +4675,8 @@ ServerContext::StoreResult result = lock.GetContext().AdoptAttachment(resultPublicId, *dicom, StoreInstanceMode_Default, adoptedFile); - CopyToMemoryBuffer(*parameters.attachmentUuid, adoptedFile.GetUuid().size() > 0 ? adoptedFile.GetUuid().c_str() : NULL, adoptedFile.GetUuid().size()); - CopyToMemoryBuffer(*parameters.createdResourceId, resultPublicId.size() > 0 ? resultPublicId.c_str() : NULL, resultPublicId.size()); + CopyToMemoryBuffer(*parameters.attachmentUuid, adoptedFile.GetUuid()); + CopyToMemoryBuffer(*parameters.createdResourceId, resultPublicId); *(parameters.storeStatus) = Plugins::Convert(result.GetStatus()); } } @@ -4689,7 +4689,7 @@ if (lock.GetContext().GetIndex().GetAttachment(fileInfo, revision, parameters.attachmentUuid)) { - CopyToMemoryBuffer(*parameters.customData, fileInfo.GetCustomData().size() > 0 ? fileInfo.GetCustomData().c_str() : NULL, fileInfo.GetCustomData().size()); + CopyToMemoryBuffer(*parameters.customData, fileInfo.GetCustomData()); } else { @@ -4709,22 +4709,18 @@ bool OrthancPlugins::HasKeyValueStoresSupport() { PImpl::ServerContextReference lock(*pimpl_); - return lock.GetContext().GetIndex().HasKeyValueStoresSupport(); } void OrthancPlugins::ApplyStoreKeyValue(const _OrthancPluginStoreKeyValue& parameters) { PImpl::ServerContextReference lock(*pimpl_); - std::string value(reinterpret_cast<const char*>(parameters.value), parameters.valueSize); - - lock.GetContext().GetIndex().StoreKeyValue(parameters.storeId, parameters.key, value); + lock.GetContext().GetIndex().StoreKeyValue(parameters.storeId, parameters.key, parameters.value, parameters.valueSize); } void OrthancPlugins::ApplyDeleteKeyValue(const _OrthancPluginDeleteKeyValue& parameters) { PImpl::ServerContextReference lock(*pimpl_); - lock.GetContext().GetIndex().DeleteKeyValue(parameters.storeId, parameters.key); } @@ -4736,7 +4732,7 @@ if (lock.GetContext().GetIndex().GetKeyValue(value, parameters.storeId, parameters.key)) { - CopyToMemoryBuffer(*parameters.target, value.size() > 0 ? value.c_str() : NULL, value.size()); + CopyToMemoryBuffer(*parameters.target, value); *parameters.found = true; } else @@ -4748,16 +4744,13 @@ bool OrthancPlugins::HasQueuesSupport() { PImpl::ServerContextReference lock(*pimpl_); - return lock.GetContext().GetIndex().HasQueuesSupport(); } void OrthancPlugins::ApplyEnqueueValue(const _OrthancPluginEnqueueValue& parameters) { PImpl::ServerContextReference lock(*pimpl_); - std::string value(reinterpret_cast<const char*>(parameters.value), parameters.valueSize); - - lock.GetContext().GetIndex().EnqueueValue(parameters.queueId, value); + lock.GetContext().GetIndex().EnqueueValue(parameters.queueId, parameters.value, parameters.valueSize); } void OrthancPlugins::ApplyDequeueValue(const _OrthancPluginDequeueValue& parameters) @@ -4768,7 +4761,7 @@ if (lock.GetContext().GetIndex().DequeueValue(value, parameters.queueId, Plugins::Convert(parameters.origin))) { - CopyToMemoryBuffer(*parameters.target, value.size() > 0 ? value.c_str() : NULL, value.size()); + CopyToMemoryBuffer(*parameters.target, value); *parameters.found = true; } else @@ -5232,7 +5225,7 @@ std::string content; SystemToolbox::ReadFile(content, p.path); - CopyToMemoryBuffer(*p.target, content.size() > 0 ? content.c_str() : NULL, content.size()); + CopyToMemoryBuffer(*p.target, content); return true; } @@ -5962,6 +5955,20 @@ } case _OrthancPluginService_KeysValuesIteratorGetKey: + if (!HasKeyValueStoresSupport()) + { + throw OrthancException(ErrorCode_NotImplemented, "The DB engine does not support key-value stores"); + } + else + { + const _OrthancPluginKeysValuesIteratorGetKey& p = + *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetKey*>(parameters); + + StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator); + *p.target = iterator.GetKey().c_str(); + return true; + } + case _OrthancPluginService_KeysValuesIteratorGetValue: if (!HasKeyValueStoresSupport()) { @@ -5969,25 +5976,12 @@ } else { - const _OrthancPluginKeysValuesIteratorGetString& p = - *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetString*>(parameters); + const _OrthancPluginKeysValuesIteratorGetValue& p = + *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetValue*>(parameters); StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator); - - if (service == _OrthancPluginService_KeysValuesIteratorGetKey) - { - *p.target = iterator.GetKey().c_str(); - return true; - } - else if (service == _OrthancPluginService_KeysValuesIteratorGetValue) - { - *p.target = iterator.GetValue().c_str(); - return true; - } - else - { - throw OrthancException(ErrorCode_InternalError); - } + CopyToMemoryBuffer(*p.target, iterator.GetValue()); + return true; } case _OrthancPluginService_EnqueueValue:
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri May 30 15:38:47 2025 +0200 @@ -484,7 +484,6 @@ _OrthancPluginService_DequeueValue = 58, /* New in Orthanc 1.12.8 */ _OrthancPluginService_GetQueueSize = 59, /* New in Orthanc 1.12.8 */ - /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, _OrthancPluginService_RegisterOnStoredInstanceCallback = 1001, @@ -10100,7 +10099,7 @@ { const char** target; OrthancPluginKeysValuesIterator* iterator; - } _OrthancPluginKeysValuesIteratorGetString; + } _OrthancPluginKeysValuesIteratorGetKey; /** * @brief Get the current key of an iterator over a key-value store. @@ -10118,7 +10117,7 @@ { const char* target = NULL; - _OrthancPluginKeysValuesIteratorGetString params; + _OrthancPluginKeysValuesIteratorGetKey params; params.target = ⌖ params.iterator = iterator; @@ -10133,6 +10132,12 @@ } + typedef struct + { + OrthancPluginMemoryBuffer* target; + OrthancPluginKeysValuesIterator* iterator; + } _OrthancPluginKeysValuesIteratorGetValue; + /** * @brief Get the current value of an iterator over a key-value store. * @@ -10140,27 +10145,21 @@ * must have been called at least once. * * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param target Memory buffer where to store the value that has been retrieved from the key-value store. + * It must be freed with OrthancPluginFreeMemoryBuffer(). * @param iterator The iterator of interest. * @return The current value, or NULL in the case of an error. **/ - ORTHANC_PLUGIN_INLINE const char* OrthancPluginKeysValuesIteratorGetValue( + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginKeysValuesIteratorGetValue( OrthancPluginContext* context, - OrthancPluginKeysValuesIterator* iterator) - { - const char* target = NULL; - - _OrthancPluginKeysValuesIteratorGetString params; - params.target = ⌖ + OrthancPluginMemoryBuffer* target /* out */, + OrthancPluginKeysValuesIterator* iterator /* in */) + { + _OrthancPluginKeysValuesIteratorGetValue params; + params.target = target; params.iterator = iterator; - if (context->InvokeService(context, _OrthancPluginService_KeysValuesIteratorGetValue, ¶ms) == OrthancPluginErrorCode_Success) - { - return target; - } - else - { - return NULL; - } + return context->InvokeService(context, _OrthancPluginService_KeysValuesIteratorGetValue, ¶ms); } @@ -10209,6 +10208,7 @@ * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). * @param found Pointer to a Boolean that is set to "true" iff. a value has been dequeued. * @param target Memory buffer where to store the value that has been retrieved from the queue. + * It must be freed with OrthancPluginFreeMemoryBuffer(). * @param queueId A unique identifier identifying both the plugin and the queue. * @param origin The position from where the value is dequeued (back for LIFO, front for FIFO). * @return 0 if success, other value if error.
--- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Fri May 30 15:38:47 2025 +0200 @@ -997,7 +997,7 @@ message Request { string store_id = 1; string key = 2; - string value = 3; + bytes value = 3; } message Response { @@ -1022,7 +1022,7 @@ message Response { bool found = 1; - string value = 2; + bytes value = 2; } } @@ -1037,7 +1037,7 @@ message Response { message KeyValue { string key = 1; - string value = 2; + bytes value = 2; } repeated KeyValue keys_values = 1; } @@ -1046,7 +1046,7 @@ message EnqueueValue { message Request { string queue_id = 1; - string value = 2; + bytes value = 2; } message Response { @@ -1061,7 +1061,7 @@ message Response { bool found = 1; - string value = 2; + bytes value = 2; } }
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Fri May 30 15:38:47 2025 +0200 @@ -4404,16 +4404,18 @@ #if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1 - std::string KeyValueStore::Iterator::GetValue() const - { - const char* s = OrthancPluginKeysValuesIteratorGetValue(OrthancPlugins::GetGlobalContext(), iterator_); - if (s == NULL) - { - ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError); + void KeyValueStore::Iterator::GetValue(std::string& value) const + { + OrthancPlugins::MemoryBuffer valueBuffer; + OrthancPluginErrorCode code = OrthancPluginKeysValuesIteratorGetValue(OrthancPlugins::GetGlobalContext(), *valueBuffer, iterator_); + + if (code != OrthancPluginErrorCode_Success) + { + ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code); } else { - return s; + valueBuffer.ToString(value); } } #endif
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Fri May 30 15:38:47 2025 +0200 @@ -1646,7 +1646,7 @@ std::string GetKey() const; - std::string GetValue() const; + void GetValue(std::string& target) const; }; private:
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri May 30 15:38:47 2025 +0200 @@ -437,7 +437,8 @@ // New in Orthanc 1.12.8 virtual void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) = 0; + const void* value, + size_t valueSize) = 0; // New in Orthanc 1.12.8 virtual void DeleteKeyValue(const std::string& storeId, @@ -453,12 +454,13 @@ std::list<std::string>& values /* out */, const std::string& storeId, bool first, - const std::string& from /* only used if "first == false" */, + const std::string& from /* exclusive bound, only used if "first == false" */, uint64_t limit /* maximum number of elements */) = 0; // New in Orthanc 1.12.8 virtual void EnqueueValue(const std::string& queueId, - const std::string& value) = 0; + const void* value, + size_t valueSize) = 0; // New in Orthanc 1.12.8 virtual bool DequeueValue(std::string& value,
--- a/OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/InstallKeyValueStoresAndQueues.sql Fri May 30 15:38:47 2025 +0200 @@ -22,14 +22,14 @@ CREATE TABLE KeyValueStores( storeId TEXT NOT NULL, key TEXT NOT NULL, - value TEXT NOT NULL, + value BLOB NOT NULL, PRIMARY KEY(storeId, key) -- Prevents duplicates ); CREATE TABLE Queues ( id INTEGER PRIMARY KEY AUTOINCREMENT, queueId TEXT NOT NULL, - value TEXT + value BLOB ); CREATE INDEX QueuesIndex ON Queues (queueId, id);
--- a/OrthancServer/Sources/Database/PrepareDatabase.sql Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/PrepareDatabase.sql Fri May 30 15:38:47 2025 +0200 @@ -207,14 +207,14 @@ CREATE TABLE KeyValueStores( storeId TEXT NOT NULL, key TEXT NOT NULL, - value TEXT NOT NULL, + value BLOB NOT NULL, PRIMARY KEY(storeId, key) -- Prevents duplicates ); CREATE TABLE Queues ( id INTEGER PRIMARY KEY AUTOINCREMENT, queueId TEXT NOT NULL, - value TEXT + value BLOB ); CREATE INDEX QueuesIndex ON Queues (queueId, id);
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri May 30 15:38:47 2025 +0200 @@ -2142,12 +2142,13 @@ virtual void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { 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); + s.BindBlob(2, value, valueSize); s.Run(); } @@ -2221,12 +2222,18 @@ // New in Orthanc 1.12.8 virtual void EnqueueValue(const std::string& queueId, - const std::string& value) ORTHANC_OVERRIDE + const void* value, + size_t valueSize) ORTHANC_OVERRIDE { - SQLite::Statement s(db_, SQLITE_FROM_HERE, + if (static_cast<size_t>(static_cast<int>(valueSize)) != valueSize) + { + throw OrthancException(ErrorCode_NotEnoughMemory, "Value is too large for a SQLite database"); + } + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Queues (queueId, value) VALUES (?, ?)"); s.BindString(0, queueId); - s.BindString(1, value); + s.BindBlob(1, value, valueSize); s.Run(); }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri May 30 15:38:47 2025 +0200 @@ -3335,32 +3335,42 @@ void StatelessDatabaseOperations::StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) + const void* value, + size_t valueSize) { + if (value == NULL && + valueSize > 0) + { + throw OrthancException(ErrorCode_NullPointer); + } + class Operations : public IReadWriteOperations { private: const std::string& storeId_; const std::string& key_; - const std::string& value_; + const void* value_; + size_t valueSize_; public: Operations(const std::string& storeId, const std::string& key, - const std::string& value) : + const void* value, + size_t valueSize) : storeId_(storeId), key_(key), - value_(value) + value_(value), + valueSize_(valueSize) { } virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE { - transaction.StoreKeyValue(storeId_, key_, value_); + transaction.StoreKeyValue(storeId_, key_, value_, valueSize_); } }; - Operations operations(storeId, key, value); + Operations operations(storeId, key, value, valueSize); Apply(operations); } @@ -3422,29 +3432,39 @@ } void StatelessDatabaseOperations::EnqueueValue(const std::string& queueId, - const std::string& value) + const void* value, + size_t valueSize) { + if (value == NULL && + valueSize > 0) + { + throw OrthancException(ErrorCode_NullPointer); + } + class Operations : public IReadWriteOperations { private: const std::string& queueId_; - const std::string& value_; + const void* value_; + size_t valueSize_; public: Operations(const std::string& queueId, - const std::string& value) : + const void* value, + size_t valueSize) : queueId_(queueId), - value_(value) + value_(value), + valueSize_(valueSize) { } virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE { - transaction.EnqueueValue(queueId_, value_); + transaction.EnqueueValue(queueId_, value_, valueSize_); } }; - Operations operations(queueId, value); + Operations operations(queueId, value, valueSize); Apply(operations); }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri May 30 15:38:47 2025 +0200 @@ -460,9 +460,10 @@ void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value) + const void* value, + size_t valueSize) { - transaction_.StoreKeyValue(storeId, key, value); + transaction_.StoreKeyValue(storeId, key, value, valueSize); } void DeleteKeyValue(const std::string& storeId, @@ -472,9 +473,10 @@ } void EnqueueValue(const std::string& queueId, - const std::string& value) + const void* value, + size_t valueSize) { - transaction_.EnqueueValue(queueId, value); + transaction_.EnqueueValue(queueId, value, valueSize); } bool DequeueValue(std::string& value, @@ -800,7 +802,15 @@ void StoreKeyValue(const std::string& storeId, const std::string& key, - const std::string& value); + const void* value, + size_t valueSize); + + void StoreKeyValue(const std::string& storeId, + const std::string& key, + const std::string& value) + { + StoreKeyValue(storeId, key, value.empty() ? NULL : value.c_str(), value.size()); + } void DeleteKeyValue(const std::string& storeId, const std::string& key); @@ -810,7 +820,14 @@ const std::string& key); void EnqueueValue(const std::string& queueId, - const std::string& value); + const void* value, + size_t valueSize); + + void EnqueueValue(const std::string& queueId, + const std::string& value) + { + EnqueueValue(queueId, value.empty() ? NULL : value.c_str(), value.size()); + } bool DequeueValue(std::string& value, const std::string& queueId,
--- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Fri May 30 14:28:41 2025 +0200 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Fri May 30 15:38:47 2025 +0200 @@ -1218,6 +1218,24 @@ it.SetLimit(limit); ASSERT_FALSE(it.Next()); } + + { + std::string blob; + blob.push_back(0); + blob.push_back(1); + blob.push_back(0); + blob.push_back(2); + op.StoreKeyValue("test", "blob", blob); // Storing binary values + } + + ASSERT_TRUE(op.GetKeyValue(s, "test", "blob")); + ASSERT_EQ(4u, s.size()); + ASSERT_EQ(0, static_cast<uint8_t>(s[0])); + ASSERT_EQ(1, static_cast<uint8_t>(s[1])); + ASSERT_EQ(0, static_cast<uint8_t>(s[2])); + ASSERT_EQ(2, static_cast<uint8_t>(s[3])); + op.DeleteKeyValue("test", "blob"); + ASSERT_FALSE(op.GetKeyValue(s, "test", "blob")); } db.Close(); @@ -1241,14 +1259,37 @@ std::string s; ASSERT_TRUE(op.DequeueValue(s, "test", QueueOrigin_Back)); ASSERT_EQ("world", s); + ASSERT_EQ(1u, op.GetQueueSize("test")); ASSERT_TRUE(op.DequeueValue(s, "test", QueueOrigin_Back)); ASSERT_EQ("hello", s); + ASSERT_EQ(0u, op.GetQueueSize("test")); ASSERT_FALSE(op.DequeueValue(s, "test", QueueOrigin_Back)); op.EnqueueValue("test", "hello"); op.EnqueueValue("test", "world"); + ASSERT_EQ(2u, op.GetQueueSize("test")); ASSERT_TRUE(op.DequeueValue(s, "test", QueueOrigin_Front)); ASSERT_EQ("hello", s); ASSERT_TRUE(op.DequeueValue(s, "test", QueueOrigin_Front)); ASSERT_EQ("world", s); + ASSERT_EQ(0u, op.GetQueueSize("test")); + ASSERT_FALSE(op.DequeueValue(s, "test", QueueOrigin_Front)); + + { + std::string blob; + blob.push_back(0); + blob.push_back(1); + blob.push_back(0); + blob.push_back(2); + op.EnqueueValue("test", blob); // Storing binary values + } + + ASSERT_EQ(1u, op.GetQueueSize("test")); + ASSERT_TRUE(op.DequeueValue(s, "test", QueueOrigin_Front)); + ASSERT_EQ(0u, op.GetQueueSize("test")); + ASSERT_EQ(4u, s.size()); + ASSERT_EQ(0, static_cast<uint8_t>(s[0])); + ASSERT_EQ(1, static_cast<uint8_t>(s[1])); + ASSERT_EQ(0, static_cast<uint8_t>(s[2])); + ASSERT_EQ(2, static_cast<uint8_t>(s[3])); ASSERT_FALSE(op.DequeueValue(s, "test", QueueOrigin_Front)); }