Mercurial > hg > orthanc-databases
changeset 242:b97a537f4613
MySQL: Support of range reads for the storage area
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 13 Apr 2021 17:00:02 +0200 |
parents | a063bbf10a3e |
children | f5dc59c56e65 |
files | Framework/Common/DatabaseManager.cpp Framework/MySQL/MySQLStatement.cpp Framework/Plugins/StorageBackend.cpp Framework/Plugins/StorageBackend.h Framework/PostgreSQL/PostgreSQLResult.cpp MySQL/NEWS MySQL/Plugins/MySQLStorageArea.cpp MySQL/Plugins/MySQLStorageArea.h MySQL/UnitTests/UnitTestsMain.cpp PostgreSQL/Plugins/PostgreSQLStorageArea.h PostgreSQL/UnitTests/PostgreSQLTests.cpp |
diffstat | 11 files changed, 450 insertions(+), 97 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Common/DatabaseManager.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/Framework/Common/DatabaseManager.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -269,7 +269,7 @@ if (active_) { manager_.CommitTransaction(); - active_ = true; + active_ = false; } else { @@ -283,7 +283,7 @@ if (active_) { manager_.RollbackTransaction(); - active_ = true; + active_ = false; } else {
--- a/Framework/MySQL/MySQLStatement.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/Framework/MySQL/MySQLStatement.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -153,6 +153,7 @@ case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_LONG_BLOB: // https://medium.com/@adamhooper/in-mysql-never-use-utf8-use-utf8mb4-11761243e434 switch (field.charsetnr) {
--- a/Framework/Plugins/StorageBackend.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/Framework/Plugins/StorageBackend.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -85,10 +85,10 @@ } - void StorageBackend::Accessor::Create(const std::string& uuid, - const void* content, - size_t size, - OrthancPluginContentType type) + void StorageBackend::AccessorBase::Create(const std::string& uuid, + const void* content, + size_t size, + OrthancPluginContentType type) { DatabaseManager::Transaction transaction(manager_, TransactionType_ReadWrite); @@ -113,9 +113,9 @@ } - void StorageBackend::Accessor::Read(StorageBackend::IFileContentVisitor& visitor, - const std::string& uuid, - OrthancPluginContentType type) + void StorageBackend::AccessorBase::ReadWhole(StorageBackend::IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type) { DatabaseManager::Transaction transaction(manager_, TransactionType_ReadOnly); @@ -170,8 +170,18 @@ } - void StorageBackend::Accessor::Remove(const std::string& uuid, - OrthancPluginContentType type) + void StorageBackend::AccessorBase::ReadRange(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + + void StorageBackend::AccessorBase::Remove(const std::string& uuid, + OrthancPluginContentType type) { DatabaseManager::Transaction transaction(manager_, TransactionType_ReadWrite); @@ -212,8 +222,8 @@ } else { - StorageBackend::Accessor accessor(*backend_); - accessor.Create(uuid, content, static_cast<size_t>(size), type); + std::unique_ptr<StorageBackend::IAccessor> accessor(backend_->CreateAccessor()); + accessor->Create(uuid, content, static_cast<size_t>(size), type); return OrthancPluginErrorCode_Success; } } @@ -237,6 +247,10 @@ target_(target), success_(false) { + if (target == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } } virtual bool IsSuccess() const ORTHANC_OVERRIDE @@ -269,24 +283,94 @@ } } }; - + + try { if (backend_.get() == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - else if (target == NULL) + else + { + Visitor visitor(target); + + { + std::unique_ptr<StorageBackend::IAccessor> accessor(backend_->CreateAccessor()); + accessor->ReadWhole(visitor, uuid, type); + } + + return OrthancPluginErrorCode_Success; + } + } + ORTHANC_PLUGINS_DATABASE_CATCH; + } + + + static OrthancPluginErrorCode StorageReadRange(OrthancPluginMemoryBuffer64* target, + const char* uuid, + OrthancPluginContentType type, + uint64_t start) + { + class Visitor : public StorageBackend::IFileContentVisitor + { + private: + OrthancPluginMemoryBuffer64* target_; // This buffer is already allocated by the Orthanc core + bool success_; + + public: + Visitor(OrthancPluginMemoryBuffer64* target) : + target_(target), + success_(false) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + if (target == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + virtual bool IsSuccess() const ORTHANC_OVERRIDE + { + return success_; + } + + virtual void Assign(const std::string& content) ORTHANC_OVERRIDE + { + if (success_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + if (content.size() != target_->size) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + if (!content.empty()) + { + memcpy(target_->data, content.c_str(), content.size()); + } + + success_ = true; + } + } + }; + + + try + { + if (backend_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } else { Visitor visitor(target); { - StorageBackend::Accessor accessor(*backend_); - accessor.Read(visitor, uuid, type); + std::unique_ptr<StorageBackend::IAccessor> accessor(backend_->CreateAccessor()); + accessor->ReadRange(visitor, uuid, type, start, target->size); } return OrthancPluginErrorCode_Success; @@ -377,8 +461,8 @@ Visitor visitor(data, size); { - StorageBackend::Accessor accessor(*backend_); - accessor.Read(visitor, uuid, type); + std::unique_ptr<StorageBackend::IAccessor> accessor(backend_->CreateAccessor()); + accessor->ReadWhole(visitor, uuid, type); } return OrthancPluginErrorCode_Success; @@ -399,8 +483,8 @@ } else { - StorageBackend::Accessor accessor(*backend_); - accessor.Remove(uuid, type); + std::unique_ptr<StorageBackend::IAccessor> accessor(backend_->CreateAccessor()); + accessor->Remove(uuid, type); return OrthancPluginErrorCode_Success; } } @@ -434,8 +518,13 @@ # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 0) if (OrthancPluginCheckVersionAdvanced(context, 1, 9, 0) == 1) { - OrthancPluginRegisterStorageArea2(context_, StorageCreate, StorageReadWhole, - NULL /* TODO - StorageReadRange */, StorageRemove); + OrthancPluginStorageReadRange readRange = NULL; + if (backend_->HasReadRange()) + { + readRange = StorageReadRange; + } + + OrthancPluginRegisterStorageArea2(context_, StorageCreate, StorageReadWhole, readRange, StorageRemove); hasLoadedV2 = true; } # endif @@ -456,44 +545,67 @@ } - void StorageBackend::Accessor::ReadToString(std::string& target, - const std::string& uuid, - OrthancPluginContentType type) + class StorageBackend::StringVisitor : public StorageBackend::IFileContentVisitor { - class Visitor : public StorageBackend::IFileContentVisitor + private: + std::string& target_; + bool success_; + + public: + StringVisitor(std::string& target) : + target_(target), + success_(false) { - private: - std::string& target_; - bool success_; - - public: - Visitor(std::string& target) : - target_(target), - success_(false) - { - } + } - virtual bool IsSuccess() const ORTHANC_OVERRIDE - { - return success_; - } + virtual bool IsSuccess() const ORTHANC_OVERRIDE + { + return success_; + } - virtual void Assign(const std::string& content) ORTHANC_OVERRIDE + virtual void Assign(const std::string& content) ORTHANC_OVERRIDE + { + if (success_) { - if (success_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - else - { - target_.assign(content); - success_ = true; - } + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - }; + else + { + target_.assign(content); + success_ = true; + } + } + }; - Visitor visitor(target); - Read(visitor, uuid, type); + void StorageBackend::ReadWholeToString(std::string& target, + IAccessor& accessor, + const std::string& uuid, + OrthancPluginContentType type) + { + StringVisitor visitor(target); + accessor.ReadWhole(visitor, uuid, type); + + if (!visitor.IsSuccess()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + void StorageBackend::ReadRangeToString(std::string& target, + IAccessor& accessor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length) + { + StringVisitor visitor(target); + accessor.ReadRange(visitor, uuid, type, start, length); + + if (!visitor.IsSuccess()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } } }
--- a/Framework/Plugins/StorageBackend.h Tue Apr 13 12:07:30 2021 +0200 +++ b/Framework/Plugins/StorageBackend.h Tue Apr 13 17:00:02 2021 +0200 @@ -32,15 +32,6 @@ { class StorageBackend : public boost::noncopyable { - private: - boost::mutex mutex_; - std::unique_ptr<DatabaseManager> manager_; - - DatabaseManager& GetManager(); - - protected: - void SetDatabase(IDatabase* database); // Takes ownership - public: class IFileContentVisitor : public boost::noncopyable { @@ -54,44 +45,109 @@ virtual bool IsSuccess() const = 0; }; - class Accessor : public boost::noncopyable + class IAccessor : public boost::noncopyable + { + public: + virtual ~IAccessor() + { + } + + virtual void Create(const std::string& uuid, + const void* content, + size_t size, + OrthancPluginContentType type) = 0; + + virtual void ReadWhole(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type) = 0; + + virtual void ReadRange(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length) = 0; + + virtual void Remove(const std::string& uuid, + OrthancPluginContentType type) = 0; + }; + + private: + class StringVisitor; + + boost::mutex mutex_; + std::unique_ptr<DatabaseManager> manager_; + + DatabaseManager& GetManager(); + + protected: + class AccessorBase : public IAccessor { private: boost::mutex::scoped_lock lock_; DatabaseManager& manager_; public: - Accessor(StorageBackend& backend) : + AccessorBase(StorageBackend& backend) : lock_(backend.mutex_), manager_(backend.GetManager()) { } - - void Create(const std::string& uuid, - const void* content, - size_t size, - OrthancPluginContentType type); + + DatabaseManager& GetManager() const + { + return manager_; + } + + virtual void Create(const std::string& uuid, + const void* content, + size_t size, + OrthancPluginContentType type); - void Read(IFileContentVisitor& visitor, - const std::string& uuid, - OrthancPluginContentType type); + virtual void ReadWhole(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type); - void Remove(const std::string& uuid, - OrthancPluginContentType type); - - // For unit tests - void ReadToString(std::string& target, - const std::string& uuid, - OrthancPluginContentType type); + virtual void ReadRange(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length); + + virtual void Remove(const std::string& uuid, + OrthancPluginContentType type); }; + void SetDatabase(IDatabase* database); // Takes ownership + + virtual bool HasReadRange() const = 0; + + public: virtual ~StorageBackend() { } + virtual IAccessor* CreateAccessor() + { + return new AccessorBase(*this); + } + static void Register(OrthancPluginContext* context, StorageBackend* backend); // Takes ownership static void Finalize(); + + // For unit tests + static void ReadWholeToString(std::string& target, + IAccessor& accessor, + const std::string& uuid, + OrthancPluginContentType type); + + // For unit tests + static void ReadRangeToString(std::string& target, + IAccessor& accessor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length); }; }
--- a/Framework/PostgreSQL/PostgreSQLResult.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/Framework/PostgreSQL/PostgreSQLResult.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -175,6 +175,10 @@ { CheckColumn(column, OIDOID); + /** + * In PostgreSQL, the type "Oid" corresponds to "unsigned int", cf. + * /usr/include/postgresql/10/server/postgres_ext.h + **/ Oid oid; assert(PQfsize(reinterpret_cast<PGresult*>(result_), column) == sizeof(oid));
--- a/MySQL/NEWS Tue Apr 13 12:07:30 2021 +0200 +++ b/MySQL/NEWS Tue Apr 13 17:00:02 2021 +0200 @@ -1,7 +1,8 @@ Pending changes in the mainline =============================== -* Support of "OrthancPluginRegisterStorageArea2()" from Orthanc SDK 1.9.0 +* Support of retries for collisions between multiple writers, from Orthanc SDK 1.9.2 +* Support of range reads for the storage area, from Orthanc SDK 1.9.0 Release 3.0 (2020-12-16)
--- a/MySQL/Plugins/MySQLStorageArea.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/MySQL/Plugins/MySQLStorageArea.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -21,6 +21,7 @@ #include "MySQLStorageArea.h" +#include "../../Framework/Common/BinaryStringValue.h" #include "../../Framework/MySQL/MySQLDatabase.h" #include "../../Framework/MySQL/MySQLTransaction.h" #include "MySQLDefinitions.h" @@ -98,4 +99,94 @@ ConfigureDatabase(*database, parameters, clearAll); SetDatabase(database.release()); } + + + class MySQLStorageArea::Accessor : public StorageBackend::AccessorBase + { + public: + Accessor(MySQLStorageArea& backend) : + AccessorBase(backend) + { + } + + virtual void ReadRange(IFileContentVisitor& visitor, + const std::string& uuid, + OrthancPluginContentType type, + uint64_t start, + uint64_t length) ORTHANC_OVERRIDE + { + DatabaseManager::Transaction transaction(GetManager(), TransactionType_ReadOnly); + + { + // https://stackoverflow.com/a/6545557/881731 + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, GetManager(), + "SELECT SUBSTRING(content, ${start}, ${length}) FROM StorageArea WHERE uuid=${uuid} AND type=${type}"); + + statement.SetParameterType("uuid", ValueType_Utf8String); + statement.SetParameterType("type", ValueType_Integer64); + statement.SetParameterType("start", ValueType_Integer64); + statement.SetParameterType("length", ValueType_Integer64); + + Dictionary args; + args.SetUtf8Value("uuid", uuid); + args.SetIntegerValue("type", type); + args.SetIntegerValue("length", length); + + /** + * "For all forms of SUBSTRING(), the position of the first + * character in the string from which the substring is to be + * extracted is reckoned as 1." => hence the "+ 1" + * https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_substring + **/ + args.SetIntegerValue("start", start + 1); + + statement.Execute(args); + + if (statement.IsDone()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + else if (statement.GetResultFieldsCount() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + else + { + const IValue& value = statement.GetResultField(0); + + if (value.GetType() == ValueType_BinaryString) + { + const std::string& content = dynamic_cast<const BinaryStringValue&>(value).GetContent(); + + if (static_cast<uint64_t>(content.size()) == length) + { + visitor.Assign(content); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRange); + } + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); + } + } + } + + transaction.Commit(); + + if (!visitor.IsSuccess()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Database, "Could not read range from the storage area"); + } + } + }; + + + StorageBackend::IAccessor* MySQLStorageArea::CreateAccessor() + { + return new Accessor(*this); + } }
--- a/MySQL/Plugins/MySQLStorageArea.h Tue Apr 13 12:07:30 2021 +0200 +++ b/MySQL/Plugins/MySQLStorageArea.h Tue Apr 13 17:00:02 2021 +0200 @@ -30,12 +30,22 @@ class MySQLStorageArea : public StorageBackend { private: + class Accessor; + void ConfigureDatabase(MySQLDatabase& db, const MySQLParameters& parameters, bool clearAll); - + + protected: + virtual bool HasReadRange() const + { + return true; + } + public: MySQLStorageArea(const MySQLParameters& parameters, bool clearAll); + + virtual IAccessor* CreateAccessor(); }; }
--- a/MySQL/UnitTests/UnitTestsMain.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/MySQL/UnitTests/UnitTestsMain.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -162,7 +162,7 @@ OrthancDatabases::MySQLStorageArea storageArea(globalParameters_, true /* clear database */); { - OrthancDatabases::MySQLStorageArea::Accessor accessor(storageArea); + std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor()); ASSERT_EQ(0, CountFiles(*database)); @@ -170,15 +170,16 @@ { std::string uuid = boost::lexical_cast<std::string>(i); std::string value = "Value " + boost::lexical_cast<std::string>(i * 2); - accessor.Create(uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown); + accessor->Create(uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown); } std::string buffer; - ASSERT_THROW(accessor.ReadToString(buffer, "nope", OrthancPluginContentType_Unknown), + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString( + buffer, *accessor, "nope", OrthancPluginContentType_Unknown), Orthanc::OrthancException); ASSERT_EQ(10, CountFiles(*database)); - accessor.Remove("5", OrthancPluginContentType_Unknown); + accessor->Remove("5", OrthancPluginContentType_Unknown); ASSERT_EQ(9, CountFiles(*database)); @@ -190,19 +191,20 @@ if (i == 5) { - ASSERT_THROW(accessor.ReadToString(buffer, uuid, OrthancPluginContentType_Unknown), + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString( + buffer, *accessor, uuid, OrthancPluginContentType_Unknown), Orthanc::OrthancException); } else { - accessor.ReadToString(buffer, uuid, OrthancPluginContentType_Unknown); + OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, uuid, OrthancPluginContentType_Unknown); ASSERT_EQ(expected, buffer); } } for (int i = 0; i < 10; i++) { - accessor.Remove(boost::lexical_cast<std::string>(i), OrthancPluginContentType_Unknown); + accessor->Remove(boost::lexical_cast<std::string>(i), OrthancPluginContentType_Unknown); } ASSERT_EQ(0, CountFiles(*database)); @@ -210,6 +212,76 @@ } +TEST(MySQL, StorageReadRange) +{ + std::unique_ptr<OrthancDatabases::MySQLDatabase> database( + OrthancDatabases::MySQLDatabase::OpenDatabaseConnection(globalParameters_)); + + OrthancDatabases::MySQLStorageArea storageArea(globalParameters_, true /* clear database */); + + { + std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor()); + ASSERT_EQ(0, CountFiles(*database)); + accessor->Create("uuid", "abcd\0\1\2\3\4\5", 10, OrthancPluginContentType_Unknown); + ASSERT_EQ(1u, CountFiles(*database)); + } + + { + std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor()); + ASSERT_EQ(1u, CountFiles(*database)); + + std::string s; + OrthancDatabases::StorageBackend::ReadWholeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown); + ASSERT_EQ(10u, s.size()); + ASSERT_EQ('a', s[0]); + ASSERT_EQ('d', s[3]); + ASSERT_EQ('\0', s[4]); + ASSERT_EQ('\5', s[9]); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 0); + ASSERT_TRUE(s.empty()); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 1); + ASSERT_EQ(1u, s.size()); + ASSERT_EQ('a', s[0]); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 1); + ASSERT_EQ(1u, s.size()); + ASSERT_EQ('\0', s[0]); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 9, 1); + ASSERT_EQ(1u, s.size()); + ASSERT_EQ('\5', s[0]); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 10, 0); + ASSERT_TRUE(s.empty()); + + // Cannot read non-empty range after the end of the string + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadRangeToString( + s, *accessor, "uuid", OrthancPluginContentType_Unknown, 10, 1), Orthanc::OrthancException); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 0, 4); + ASSERT_EQ(4u, s.size()); + ASSERT_EQ('a', s[0]); + ASSERT_EQ('b', s[1]); + ASSERT_EQ('c', s[2]); + ASSERT_EQ('d', s[3]); + + OrthancDatabases::StorageBackend::ReadRangeToString(s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 6); + ASSERT_EQ(6u, s.size()); + ASSERT_EQ('\0', s[0]); + ASSERT_EQ('\1', s[1]); + ASSERT_EQ('\2', s[2]); + ASSERT_EQ('\3', s[3]); + ASSERT_EQ('\4', s[4]); + ASSERT_EQ('\5', s[5]); + + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadRangeToString( + s, *accessor, "uuid", OrthancPluginContentType_Unknown, 4, 7), Orthanc::OrthancException); + } +} + + TEST(MySQL, ImplicitTransaction) { OrthancDatabases::MySQLDatabase::ClearDatabase(globalParameters_);
--- a/PostgreSQL/Plugins/PostgreSQLStorageArea.h Tue Apr 13 12:07:30 2021 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLStorageArea.h Tue Apr 13 17:00:02 2021 +0200 @@ -33,6 +33,12 @@ const PostgreSQLParameters& parameters, bool clearAll); + protected: + virtual bool HasReadRange() const + { + return false; // TODO + } + public: PostgreSQLStorageArea(const PostgreSQLParameters& parameters, bool clearAll);
--- a/PostgreSQL/UnitTests/PostgreSQLTests.cpp Tue Apr 13 12:07:30 2021 +0200 +++ b/PostgreSQL/UnitTests/PostgreSQLTests.cpp Tue Apr 13 17:00:02 2021 +0200 @@ -363,7 +363,7 @@ PostgreSQLStorageArea storageArea(globalParameters_, true /* clear database */); { - PostgreSQLStorageArea::Accessor accessor(storageArea); + std::unique_ptr<OrthancDatabases::StorageBackend::IAccessor> accessor(storageArea.CreateAccessor()); ASSERT_EQ(0, CountLargeObjects(*database)); @@ -371,15 +371,15 @@ { std::string uuid = boost::lexical_cast<std::string>(i); std::string value = "Value " + boost::lexical_cast<std::string>(i * 2); - accessor.Create(uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown); + accessor->Create(uuid, value.c_str(), value.size(), OrthancPluginContentType_Unknown); } std::string buffer; - ASSERT_THROW(accessor.ReadToString(buffer, "nope", OrthancPluginContentType_Unknown), + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, "nope", OrthancPluginContentType_Unknown), Orthanc::OrthancException); ASSERT_EQ(10, CountLargeObjects(*database)); - accessor.Remove("5", OrthancPluginContentType_Unknown); + accessor->Remove("5", OrthancPluginContentType_Unknown); ASSERT_EQ(9, CountLargeObjects(*database)); @@ -390,19 +390,19 @@ if (i == 5) { - ASSERT_THROW(accessor.ReadToString(buffer, uuid, OrthancPluginContentType_Unknown), + ASSERT_THROW(OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, uuid, OrthancPluginContentType_Unknown), Orthanc::OrthancException); } else { - accessor.ReadToString(buffer, uuid, OrthancPluginContentType_Unknown); + OrthancDatabases::StorageBackend::ReadWholeToString(buffer, *accessor, uuid, OrthancPluginContentType_Unknown); ASSERT_EQ(expected, buffer); } } for (int i = 0; i < 10; i++) { - accessor.Remove(boost::lexical_cast<std::string>(i), OrthancPluginContentType_Unknown); + accessor->Remove(boost::lexical_cast<std::string>(i), OrthancPluginContentType_Unknown); } ASSERT_EQ(0, CountLargeObjects(*database));