# HG changeset patch # User Sebastien Jodogne # Date 1681559896 -7200 # Node ID ca6dc9bb8b795f9749ce691e5d8915014a50977c # Parent 1938ba8fba35cb6183780cc1f53573eeed5b4811# Parent de6de66d70b2a982daf836fbdc2c43da4de3e1a9 integration db-protobuf->mainline diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/DatabaseBackendAdapterV2.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV2.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV2.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -90,7 +90,8 @@ if (manager_.get() == NULL) { - manager_.reset(IndexBackend::CreateSingleDatabaseManager(*backend_)); + std::list identifierTags; + manager_.reset(IndexBackend::CreateSingleDatabaseManager(*backend_, false, identifierTags)); } else { @@ -1419,7 +1420,10 @@ lookup.push_back(Orthanc::DatabaseConstraint(constraints[i])); } - adapter->GetBackend().LookupResources(*output, accessor.GetManager(), lookup, queryLevel, limit, (requestSomeInstance != 0)); + std::set noLabel; + adapter->GetBackend().LookupResources(*output, accessor.GetManager(), lookup, queryLevel, noLabel, + Orthanc::LabelsConstraint_All, limit, (requestSomeInstance != 0)); + return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH; diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/DatabaseBackendAdapterV3.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -800,7 +800,8 @@ try { - pool->OpenConnections(); + std::list identifierTags; + pool->OpenConnections(false, identifierTags); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH(pool->GetContext()); @@ -1646,7 +1647,9 @@ lookup.push_back(Orthanc::DatabaseConstraint(constraints[i])); } - t->GetBackend().LookupResources(t->GetOutput(), t->GetManager(), lookup, queryLevel, limit, (requestSomeInstanceId != 0)); + std::set noLabel; + t->GetBackend().LookupResources(t->GetOutput(), t->GetManager(), lookup, queryLevel, noLabel, + Orthanc::LabelsConstraint_All, limit, (requestSomeInstanceId != 0)); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH(t->GetBackend().GetContext()); diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/DatabaseBackendAdapterV4.cpp --- a/Framework/Plugins/DatabaseBackendAdapterV4.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -90,6 +90,28 @@ } + static Orthanc::ResourceType Convert2(Orthanc::DatabasePluginMessages::ResourceType resourceType) + { + switch (resourceType) + { + case Orthanc::DatabasePluginMessages::RESOURCE_PATIENT: + return Orthanc::ResourceType_Patient; + + case Orthanc::DatabasePluginMessages::RESOURCE_STUDY: + return Orthanc::ResourceType_Study; + + case Orthanc::DatabasePluginMessages::RESOURCE_SERIES: + return Orthanc::ResourceType_Series; + + case Orthanc::DatabasePluginMessages::RESOURCE_INSTANCE: + return Orthanc::ResourceType_Instance; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + class Output : public IDatabaseBackendOutput { private: @@ -408,12 +430,30 @@ response.mutable_get_system_information()->set_database_version(accessor.GetBackend().GetDatabaseVersion(accessor.GetManager())); response.mutable_get_system_information()->set_supports_flush_to_disk(false); response.mutable_get_system_information()->set_supports_revisions(accessor.GetBackend().HasRevisionsSupport()); + response.mutable_get_system_information()->set_supports_labels(accessor.GetBackend().HasLabelsSupport()); break; } case Orthanc::DatabasePluginMessages::OPERATION_OPEN: { - pool.OpenConnections(); + std::list identifierTags; + + if (request.open().identifier_tags().empty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "No identifier tag was provided by the Orthanc core"); + } + + for (int i = 0; i < request.open().identifier_tags().size(); i++) + { + const Orthanc::DatabasePluginMessages::Open_Request_IdentifierTag& tag = request.open().identifier_tags(i); + identifierTags.push_back(IdentifierTag(Convert2(tag.level()), + Orthanc::DicomTag(tag.group(), tag.element()), + tag.name())); + } + + pool.OpenConnections(true, identifierTags); + break; } @@ -567,9 +607,35 @@ assert(values.size() == countValues); + std::set labels; + + for (int i = 0; i < request.labels().size(); i++) + { + labels.insert(request.labels(i)); + } + + Orthanc::LabelsConstraint labelsConstraint; + switch (request.labels_constraint()) + { + case Orthanc::DatabasePluginMessages::LABELS_CONSTRAINT_ALL: + labelsConstraint = Orthanc::LabelsConstraint_All; + break; + + case Orthanc::DatabasePluginMessages::LABELS_CONSTRAINT_ANY: + labelsConstraint = Orthanc::LabelsConstraint_Any; + break; + + case Orthanc::DatabasePluginMessages::LABELS_CONSTRAINT_NONE: + labelsConstraint = Orthanc::LabelsConstraint_None; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + Output output(response); backend.LookupResources(output, manager, lookup, Convert(request.query_level()), - request.limit(), request.retrieve_instances_ids()); + labels, labelsConstraint, request.limit(), request.retrieve_instances_ids()); } @@ -1154,6 +1220,40 @@ break; } + case Orthanc::DatabasePluginMessages::OPERATION_ADD_LABEL: + { + backend.AddLabel(manager, request.add_label().id(), request.add_label().label()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_REMOVE_LABEL: + { + backend.RemoveLabel(manager, request.remove_label().id(), request.remove_label().label()); + break; + } + + case Orthanc::DatabasePluginMessages::OPERATION_LIST_LABELS: + { + std::list labels; + + if (request.list_labels().single_resource()) + { + backend.ListLabels(labels, manager, request.list_labels().id()); + } + else + { + backend.ListAllLabels(labels, manager); + } + + response.mutable_list_available_attachments()->mutable_attachments()->Reserve(labels.size()); + for (std::list::const_iterator it = labels.begin(); it != labels.end(); ++it) + { + response.mutable_list_labels()->add_labels(*it); + } + + break; + } + default: LOG(ERROR) << "Not implemented transaction operation from protobuf: " << request.operation(); throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IDatabaseBackend.h --- a/Framework/Plugins/IDatabaseBackend.h Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IDatabaseBackend.h Sat Apr 15 13:58:16 2023 +0200 @@ -23,9 +23,11 @@ #pragma once -#include "IDatabaseBackendOutput.h" +#include "../../Resources/Orthanc/Databases/ISqlLookupFormatter.h" +#include "../Common/DatabaseManager.h" #include "../Common/DatabasesEnumerations.h" -#include "../Common/DatabaseManager.h" +#include "IDatabaseBackendOutput.h" +#include "IdentifierTag.h" #include @@ -42,8 +44,13 @@ virtual IDatabaseFactory* CreateDatabaseFactory() = 0; - // This function is invoked once, even if multiple connections are open - virtual void ConfigureDatabase(DatabaseManager& database) = 0; + /** + * This function is invoked once, even if multiple connections are + * open. It is notably used to update the schema of the database. + **/ + virtual void ConfigureDatabase(DatabaseManager& database, + bool hasIdentifierTags, + const std::list& identifierTags) = 0; virtual void SetOutputFactory(IDatabaseBackendOutput::IFactory* factory) = 0; @@ -271,6 +278,8 @@ DatabaseManager& manager, const std::vector& lookup, OrthancPluginResourceType queryLevel, + const std::set& labels, // New in Orthanc 1.12.0 + Orthanc::LabelsConstraint labelsConstraint, // New in Orthanc 1.12.0 uint32_t limit, bool requestSomeInstance) = 0; #endif @@ -307,23 +316,37 @@ virtual void TagMostRecentPatient(DatabaseManager& manager, int64_t patientId) = 0; -#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 -# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) // NB: "parentPublicId" must be cleared if the resource has no parent virtual bool LookupResourceAndParent(int64_t& id, OrthancPluginResourceType& type, std::string& parentPublicId, DatabaseManager& manager, const char* publicId) = 0; -# endif -#endif -#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 -# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) virtual void GetAllMetadata(std::map& result, DatabaseManager& manager, int64_t id) = 0; -# endif -#endif + + // New in Orthanc 1.12.0 + virtual bool HasLabelsSupport() const = 0; + + // New in Orthanc 1.12.0 + virtual void AddLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) = 0; + + // New in Orthanc 1.12.0 + virtual void RemoveLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) = 0; + + // New in Orthanc 1.12.0 + virtual void ListLabels(std::list& target, + DatabaseManager& manager, + int64_t resource) = 0; + + // New in Orthanc 1.12.0 + virtual void ListAllLabels(std::list& target, + DatabaseManager& manager) = 0; }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IdentifierTag.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Plugins/IdentifierTag.h Sat Apr 15 13:58:16 2023 +0200 @@ -0,0 +1,62 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + + +#pragma once + +#include + +namespace OrthancDatabases +{ + class IdentifierTag + { + private: + Orthanc::ResourceType level_; + Orthanc::DicomTag tag_; + std::string name_; + + public: + IdentifierTag(Orthanc::ResourceType level, + const Orthanc::DicomTag& tag, + const std::string& name) : + level_(level), + tag_(tag), + name_(name) + { + } + + Orthanc::ResourceType GetLevel() const + { + return level_; + } + + const Orthanc::DicomTag& GetTag() const + { + return tag_; + } + + const std::string& GetName() const + { + return name_; + } + }; +} diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IndexBackend.cpp --- a/Framework/Plugins/IndexBackend.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IndexBackend.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -22,7 +22,6 @@ #include "IndexBackend.h" -#include "../../Resources/Orthanc/Databases/ISqlLookupFormatter.h" #include "../Common/BinaryStringValue.h" #include "../Common/Integer64Value.h" #include "../Common/Utf8StringValue.h" @@ -2065,15 +2064,16 @@ DatabaseManager& manager, const std::vector& lookup, OrthancPluginResourceType queryLevel, + const std::set& labels, + Orthanc::LabelsConstraint labelsConstraint, uint32_t limit, bool requestSomeInstance) { LookupFormatter formatter(manager.GetDialect()); - std::set noLabels; std::string sql; Orthanc::ISqlLookupFormatter::Apply(sql, formatter, lookup, Orthanc::Plugins::Convert(queryLevel), - noLabels, Orthanc::LabelsConstraint_All, limit); + labels, labelsConstraint, limit); if (requestSomeInstance) { @@ -2610,6 +2610,96 @@ #endif + void IndexBackend::AddLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) + { + std::unique_ptr statement; + + switch (manager.GetDialect()) + { + case Dialect_PostgreSQL: + statement.reset(new DatabaseManager::CachedStatement( + STATEMENT_FROM_HERE, manager, + "INSERT INTO Labels VALUES(${id}, ${label}) ON CONFLICT DO NOTHING")); + break; + + case Dialect_SQLite: + statement.reset(new DatabaseManager::CachedStatement( + STATEMENT_FROM_HERE, manager, + "INSERT OR IGNORE INTO Labels VALUES(${id}, ${label})")); + break; + + case Dialect_MySQL: + statement.reset(new DatabaseManager::CachedStatement( + STATEMENT_FROM_HERE, manager, + "INSERT IGNORE INTO Labels VALUES(${id}, ${label})")); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + statement->SetParameterType("id", ValueType_Integer64); + statement->SetParameterType("label", ValueType_Utf8String); + + Dictionary args; + args.SetIntegerValue("id", resource); + args.SetUtf8Value("label", label); + + statement->Execute(args); + } + + + void IndexBackend::RemoveLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "DELETE FROM Labels WHERE id=${id} AND label=${label}"); + + statement.SetParameterType("id", ValueType_Integer64); + statement.SetParameterType("label", ValueType_Utf8String); + + Dictionary args; + args.SetIntegerValue("id", resource); + args.SetUtf8Value("label", label); + + statement.Execute(args); + } + + + void IndexBackend::ListLabels(std::list& target, + DatabaseManager& manager, + int64_t resource) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT label FROM Labels WHERE id=${id}"); + + statement.SetReadOnly(true); + statement.SetParameterType("id", ValueType_Integer64); + + Dictionary args; + args.SetIntegerValue("id", resource); + + ReadListOfStrings(target, statement, args); + } + + + void IndexBackend::ListAllLabels(std::list& target, + DatabaseManager& manager) + { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT DISTINCT label FROM Labels"); + + Dictionary args; + ReadListOfStrings(target, statement, args); + } + + void IndexBackend::Register(IndexBackend* backend, size_t countConnections, unsigned int maxDatabaseRetries) @@ -2663,7 +2753,7 @@ } catch (boost::bad_lexical_cast&) { - LOG(ERROR) << "Corrupted PostgreSQL database"; + LOG(ERROR) << "Corrupted database"; throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); } } @@ -2702,10 +2792,12 @@ } - DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend) + DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend, + bool hasIdentifierTags, + const std::list& identifierTags) { std::unique_ptr manager(new DatabaseManager(backend.CreateDatabaseFactory())); - backend.ConfigureDatabase(*manager); + backend.ConfigureDatabase(*manager, hasIdentifierTags, identifierTags); return manager.release(); } } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IndexBackend.h --- a/Framework/Plugins/IndexBackend.h Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IndexBackend.h Sat Apr 15 13:58:16 2023 +0200 @@ -302,6 +302,8 @@ DatabaseManager& manager, const std::vector& lookup, OrthancPluginResourceType queryLevel, + const std::set& labels, + Orthanc::LabelsConstraint labelsConstraint, uint32_t limit, bool requestSomeInstance) ORTHANC_OVERRIDE; #endif @@ -327,25 +329,17 @@ virtual void TagMostRecentPatient(DatabaseManager& manager, int64_t patient) ORTHANC_OVERRIDE; -#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 -# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) // New primitive since Orthanc 1.5.4 virtual bool LookupResourceAndParent(int64_t& id, OrthancPluginResourceType& type, std::string& parentPublicId, DatabaseManager& manager, const char* publicId) ORTHANC_OVERRIDE; -# endif -#endif -#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 -# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) // New primitive since Orthanc 1.5.4 virtual void GetAllMetadata(std::map& result, DatabaseManager& manager, int64_t id) ORTHANC_OVERRIDE; -# endif -#endif virtual bool HasCreateInstance() const ORTHANC_OVERRIDE { @@ -387,6 +381,21 @@ int32_t property, int value); + virtual void AddLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) ORTHANC_OVERRIDE; + + virtual void RemoveLabel(DatabaseManager& manager, + int64_t resource, + const std::string& label) ORTHANC_OVERRIDE; + + virtual void ListLabels(std::list& target, + DatabaseManager& manager, + int64_t resource) ORTHANC_OVERRIDE; + + virtual void ListAllLabels(std::list& target, + DatabaseManager& manager) ORTHANC_OVERRIDE; + /** * "maxDatabaseRetries" is to handle * "OrthancPluginErrorCode_DatabaseCannotSerialize" if there is a @@ -399,6 +408,8 @@ static void Finalize(); - static DatabaseManager* CreateSingleDatabaseManager(IDatabaseBackend& backend); + static DatabaseManager* CreateSingleDatabaseManager(IDatabaseBackend& backend, + bool hasIdentifierTags, + const std::list& identifierTags); }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IndexConnectionsPool.cpp --- a/Framework/Plugins/IndexConnectionsPool.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IndexConnectionsPool.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -75,7 +75,8 @@ } - void IndexConnectionsPool::OpenConnections() + void IndexConnectionsPool::OpenConnections(bool hasIdentifierTags, + const std::list& identifierTags) { boost::unique_lock lock(connectionsMutex_); @@ -87,7 +88,7 @@ std::unique_ptr manager(new DatabaseManager(backend_->CreateDatabaseFactory())); manager->GetDatabase(); // Make sure to open the database connection - backend_->ConfigureDatabase(*manager); + backend_->ConfigureDatabase(*manager, hasIdentifierTags, identifierTags); connections_.push_back(manager.release()); } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IndexConnectionsPool.h --- a/Framework/Plugins/IndexConnectionsPool.h Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IndexConnectionsPool.h Sat Apr 15 13:58:16 2023 +0200 @@ -22,6 +22,7 @@ #pragma once +#include "IdentifierTag.h" #include "IndexBackend.h" #include @@ -53,7 +54,8 @@ return context_; } - void OpenConnections(); + void OpenConnections(bool hasIdentifierTags, + const std::list& identifierTags); void CloseConnections(); diff -r 1938ba8fba35 -r ca6dc9bb8b79 Framework/Plugins/IndexUnitTests.h --- a/Framework/Plugins/IndexUnitTests.h Sat Apr 15 13:49:53 2023 +0200 +++ b/Framework/Plugins/IndexUnitTests.h Sat Apr 15 13:58:16 2023 +0200 @@ -246,7 +246,8 @@ db.SetOutputFactory(new DatabaseBackendAdapterV2::Factory(&context, NULL)); - std::unique_ptr manager(IndexBackend::CreateSingleDatabaseManager(db)); + std::list identifierTags; + std::unique_ptr manager(IndexBackend::CreateSingleDatabaseManager(db, false, identifierTags)); std::unique_ptr output(db.CreateOutput()); diff -r 1938ba8fba35 -r ca6dc9bb8b79 MySQL/NEWS --- a/MySQL/NEWS Sat Apr 15 13:49:53 2023 +0200 +++ b/MySQL/NEWS Sat Apr 15 13:58:16 2023 +0200 @@ -4,7 +4,7 @@ * Compatibility with Orthanc SDK 1.12.0 (communications between the Orthanc core and the database plugin using Google Protocol Buffers) * Upgraded dependencies for static builds (notably on Windows and LSB): - - openssl 3.0.1 + - openssl 3.1.0 Release 4.3 (2021-07-22) diff -r 1938ba8fba35 -r ca6dc9bb8b79 MySQL/Plugins/MySQLIndex.cpp --- a/MySQL/Plugins/MySQLIndex.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/MySQL/Plugins/MySQLIndex.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -63,7 +63,9 @@ } - void MySQLIndex::ConfigureDatabase(DatabaseManager& manager) + void MySQLIndex::ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) { uint32_t expectedVersion = 6; @@ -295,7 +297,27 @@ t.Commit(); } - if (revision != 6) + if (revision == 6) + { + // Added new table "Labels" since release 5.0 to deal with + // labels that were introduced in Orthanc 1.12.0 + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); + + t.GetDatabaseTransaction().ExecuteMultiLines( + "CREATE TABLE Labels(id BIGINT NOT NULL," + "label VARCHAR(64) NOT NULL," + "PRIMARY KEY(id, label)," + "CONSTRAINT Labels1 FOREIGN KEY (id) REFERENCES Resources(internalId) ON DELETE CASCADE);" + "CREATE INDEX LabelsIndex1 ON Labels(id);" + "CREATE INDEX LabelsIndex2 ON Labels(label);"); + + revision = 7; + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + + t.Commit(); + } + + if (revision != 7) { LOG(ERROR) << "MySQL plugin is incompatible with database schema revision: " << revision; throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); diff -r 1938ba8fba35 -r ca6dc9bb8b79 MySQL/Plugins/MySQLIndex.h --- a/MySQL/Plugins/MySQLIndex.h Sat Apr 15 13:49:53 2023 +0200 +++ b/MySQL/Plugins/MySQLIndex.h Sat Apr 15 13:58:16 2023 +0200 @@ -44,7 +44,9 @@ virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(DatabaseManager& database) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& database, + bool hasIdentifierTags, + const std::list& identifierTags) ORTHANC_OVERRIDE; virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE { @@ -76,5 +78,11 @@ const char* hashInstance) ORTHANC_OVERRIDE; #endif + + // New primitive since Orthanc 1.12.0 + virtual bool HasLabelsSupport() const ORTHANC_OVERRIDE + { + return true; + } }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 MySQL/UnitTests/UnitTestsMain.cpp --- a/MySQL/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/MySQL/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -51,19 +51,21 @@ OrthancDatabases::MySQLIndex db1(NULL, noLock); db1.SetClearAll(true); - std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); + std::list identifierTags; + + std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1, false, identifierTags)); { OrthancDatabases::MySQLIndex db2(NULL, lock); - std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2)); + std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2, false, identifierTags)); OrthancDatabases::MySQLIndex db3(NULL, lock); - ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3), Orthanc::OrthancException); + ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3, false, identifierTags), Orthanc::OrthancException); } OrthancDatabases::MySQLIndex db4(NULL, lock); - std::unique_ptr manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4)); + std::unique_ptr manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4, false, identifierTags)); } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Odbc/CMakeLists.txt --- a/Odbc/CMakeLists.txt Sat Apr 15 13:49:53 2023 +0200 +++ b/Odbc/CMakeLists.txt Sat Apr 15 13:58:16 2023 +0200 @@ -24,8 +24,8 @@ set(ORTHANC_PLUGIN_VERSION "mainline") set(ORTHANC_OPTIMAL_VERSION_MAJOR 1) -set(ORTHANC_OPTIMAL_VERSION_MINOR 9) -set(ORTHANC_OPTIMAL_VERSION_REVISION 2) +set(ORTHANC_OPTIMAL_VERSION_MINOR 12) +set(ORTHANC_OPTIMAL_VERSION_REVISION 0) if (ORTHANC_PLUGIN_VERSION STREQUAL "mainline") set(ORTHANC_FRAMEWORK_VERSION "mainline") @@ -79,6 +79,23 @@ ODBC_PREPARE_STORAGE ${CMAKE_SOURCE_DIR}/Plugins/PrepareStorage.sql ) +if (EXISTS ${ORTHANC_SDK_ROOT}/orthanc/OrthancDatabasePlugin.proto) + add_custom_command( + COMMAND + ${PROTOC_EXECUTABLE} ${ORTHANC_SDK_ROOT}/orthanc/OrthancDatabasePlugin.proto --cpp_out=${AUTOGENERATED_DIR} -I${ORTHANC_SDK_ROOT}/orthanc/ + DEPENDS + ProtobufCompiler + ${ORTHANC_SDK_ROOT}/orthanc/OrthancDatabasePlugin.proto + OUTPUT + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.h + ) + + list(APPEND AUTOGENERATED_SOURCES + ${AUTOGENERATED_DIR}/OrthancDatabasePlugin.pb.cc + ) +endif() + add_custom_target( AutogeneratedTarget DEPENDS diff -r 1938ba8fba35 -r ca6dc9bb8b79 Odbc/NEWS --- a/Odbc/NEWS Sat Apr 15 13:49:53 2023 +0200 +++ b/Odbc/NEWS Sat Apr 15 13:58:16 2023 +0200 @@ -1,6 +1,9 @@ Pending changes in the mainline =============================== +* Compatibility with Orthanc SDK 1.12.0 (communications between the + Orthanc core and the database plugin using Google Protocol Buffers) + Release 1.1 (2021-12-06) ======================== diff -r 1938ba8fba35 -r ca6dc9bb8b79 Odbc/Plugins/IndexPlugin.cpp --- a/Odbc/Plugins/IndexPlugin.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Odbc/Plugins/IndexPlugin.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -42,6 +42,9 @@ #endif +#include + + static const char* const KEY_ODBC = "Odbc"; @@ -54,6 +57,8 @@ ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + if (!OrthancDatabases::InitializePlugin(context, "ODBC", true)) { return -1; @@ -130,6 +135,7 @@ { LOG(WARNING) << "ODBC index is finalizing"; OrthancDatabases::IndexBackend::Finalize(); + google::protobuf::ShutdownProtobufLibrary(); } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Odbc/Plugins/OdbcIndex.cpp --- a/Odbc/Plugins/OdbcIndex.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/Odbc/Plugins/OdbcIndex.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -162,7 +162,9 @@ } - void OdbcIndex::ConfigureDatabase(DatabaseManager& manager) + void OdbcIndex::ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) { uint32_t expectedVersion = 6; diff -r 1938ba8fba35 -r ca6dc9bb8b79 Odbc/Plugins/OdbcIndex.h --- a/Odbc/Plugins/OdbcIndex.h Sat Apr 15 13:49:53 2023 +0200 +++ b/Odbc/Plugins/OdbcIndex.h Sat Apr 15 13:58:16 2023 +0200 @@ -56,7 +56,9 @@ virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(DatabaseManager& manager) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) ORTHANC_OVERRIDE; virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE { @@ -83,5 +85,11 @@ DatabaseManager& manager, int64_t id, int32_t attachment) ORTHANC_OVERRIDE; + + // New primitive since Orthanc 1.12.0 + virtual bool HasLabelsSupport() const ORTHANC_OVERRIDE + { + return false; + } }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 PostgreSQL/NEWS --- a/PostgreSQL/NEWS Sat Apr 15 13:49:53 2023 +0200 +++ b/PostgreSQL/NEWS Sat Apr 15 13:58:16 2023 +0200 @@ -4,7 +4,7 @@ * Compatibility with Orthanc SDK 1.12.0 (communications between the Orthanc core and the database plugin using Google Protocol Buffers) * Upgraded dependencies for static builds (notably on Windows and LSB): - - openssl 3.0.1 + - openssl 3.1.0 Release 4.0 (2021-04-22) diff -r 1938ba8fba35 -r ca6dc9bb8b79 PostgreSQL/Plugins/PostgreSQLIndex.cpp --- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -61,7 +61,9 @@ } - void PostgreSQLIndex::ConfigureDatabase(DatabaseManager& manager) + void PostgreSQLIndex::ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) { uint32_t expectedVersion = 6; @@ -297,6 +299,24 @@ t.Commit(); } + + + { + // New in release 5.0 to deal with labels + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); + + if (!t.GetDatabaseTransaction().DoesTableExist("Labels")) + { + t.GetDatabaseTransaction().ExecuteMultiLines( + "CREATE TABLE Labels(" + "id BIGINT REFERENCES Resources(internalId) ON DELETE CASCADE," + "label TEXT, PRIMARY KEY(id, label));" + "CREATE INDEX LabelsIndex1 ON LABELS(id);" + "CREATE INDEX LabelsIndex2 ON LABELS(label);"); + } + + t.Commit(); + } } } diff -r 1938ba8fba35 -r ca6dc9bb8b79 PostgreSQL/Plugins/PostgreSQLIndex.h --- a/PostgreSQL/Plugins/PostgreSQLIndex.h Sat Apr 15 13:49:53 2023 +0200 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.h Sat Apr 15 13:58:16 2023 +0200 @@ -44,7 +44,9 @@ virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(DatabaseManager& manager) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) ORTHANC_OVERRIDE; virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE { @@ -82,5 +84,11 @@ virtual void TagMostRecentPatient(DatabaseManager& manager, int64_t patient) ORTHANC_OVERRIDE; + + // New primitive since Orthanc 1.12.0 + virtual bool HasLabelsSupport() const ORTHANC_OVERRIDE + { + return true; + } }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 PostgreSQL/UnitTests/PostgreSQLTests.cpp --- a/PostgreSQL/UnitTests/PostgreSQLTests.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/PostgreSQL/UnitTests/PostgreSQLTests.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -542,7 +542,8 @@ OrthancDatabases::PostgreSQLIndex db(NULL, globalParameters_); db.SetClearAll(true); - std::unique_ptr manager(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db)); + std::list tags; + std::unique_ptr manager(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db, false, tags)); std::string s; ASSERT_TRUE(db.LookupGlobalProperty(s, *manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabaseInternal1)); diff -r 1938ba8fba35 -r ca6dc9bb8b79 PostgreSQL/UnitTests/UnitTestsMain.cpp --- a/PostgreSQL/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/PostgreSQL/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -95,18 +95,19 @@ OrthancDatabases::PostgreSQLIndex db1(NULL, noLock); db1.SetClearAll(true); - std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); + std::list identifierTags; + std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1, false, identifierTags)); { OrthancDatabases::PostgreSQLIndex db2(NULL, lock); - std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2)); + std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2, false, identifierTags)); OrthancDatabases::PostgreSQLIndex db3(NULL, lock); - ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3), Orthanc::OrthancException); + ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3, false, identifierTags), Orthanc::OrthancException); } OrthancDatabases::PostgreSQLIndex db4(NULL, lock); - std::unique_ptr manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4)); + std::unique_ptr manager4(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db4, false, identifierTags)); } diff -r 1938ba8fba35 -r ca6dc9bb8b79 Resources/Orthanc/Databases/ISqlLookupFormatter.cpp diff -r 1938ba8fba35 -r ca6dc9bb8b79 Resources/Orthanc/Databases/ISqlLookupFormatter.h diff -r 1938ba8fba35 -r ca6dc9bb8b79 Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp diff -r 1938ba8fba35 -r ca6dc9bb8b79 Resources/SyncOrthancFolder.py --- a/Resources/SyncOrthancFolder.py Sat Apr 15 13:49:53 2023 +0200 +++ b/Resources/SyncOrthancFolder.py Sat Apr 15 13:58:16 2023 +0200 @@ -8,7 +8,12 @@ import multiprocessing import os import stat -import urllib2 +import sys + +if sys.version_info[0] < 3: + from urllib2 import urlopen +else: + from urllib.request import urlopen TARGET = os.path.join(os.path.dirname(__file__), 'Orthanc') PLUGIN_SDK_VERSION_OLD = [ '0.9.5', '1.4.0', '1.5.2', '1.5.4' ] @@ -55,7 +60,7 @@ branch = x[0] source = x[1] target = os.path.join(TARGET, x[2]) - print target + print(target) try: os.makedirs(os.path.dirname(target)) @@ -65,8 +70,8 @@ url = '%s/%s/%s' % (REPOSITORY, branch, source) try: - with open(target, 'w') as f: - f.write(urllib2.urlopen(url).read()) + with open(target, 'wb') as f: + f.write(urlopen(url).read()) except Exception as e: raise Exception('Cannot download: %s' % url) diff -r 1938ba8fba35 -r ca6dc9bb8b79 SQLite/Plugins/SQLiteIndex.cpp --- a/SQLite/Plugins/SQLiteIndex.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/SQLite/Plugins/SQLiteIndex.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -85,7 +85,9 @@ } - void SQLiteIndex::ConfigureDatabase(DatabaseManager& manager) + void SQLiteIndex::ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) { uint32_t expectedVersion = 6; @@ -166,6 +168,23 @@ t.Commit(); } + + { + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); + + if (!t.GetDatabaseTransaction().DoesTableExist("Labels")) + { + t.GetDatabaseTransaction().ExecuteMultiLines( + "CREATE TABLE Labels(" + " id INTEGER REFERENCES Resources(internalId) ON DELETE CASCADE," + " label TEXT NOT NULL," + " PRIMARY KEY(id, label));" + "CREATE INDEX LabelsIndex1 ON Labels(id);" + "CREATE INDEX LabelsIndex2 ON Labels(label);"); + } + + t.Commit(); + } } diff -r 1938ba8fba35 -r ca6dc9bb8b79 SQLite/Plugins/SQLiteIndex.h --- a/SQLite/Plugins/SQLiteIndex.h Sat Apr 15 13:49:53 2023 +0200 +++ b/SQLite/Plugins/SQLiteIndex.h Sat Apr 15 13:58:16 2023 +0200 @@ -45,7 +45,9 @@ virtual IDatabaseFactory* CreateDatabaseFactory() ORTHANC_OVERRIDE; - virtual void ConfigureDatabase(DatabaseManager& manager) ORTHANC_OVERRIDE; + virtual void ConfigureDatabase(DatabaseManager& manager, + bool hasIdentifierTags, + const std::list& identifierTags) ORTHANC_OVERRIDE; virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE { @@ -58,5 +60,11 @@ // New primitive since Orthanc 1.5.2 virtual int64_t GetLastChangeIndex(DatabaseManager& manager) ORTHANC_OVERRIDE; + + // New primitive since Orthanc 1.12.0 + virtual bool HasLabelsSupport() const ORTHANC_OVERRIDE + { + return true; + } }; } diff -r 1938ba8fba35 -r ca6dc9bb8b79 SQLite/UnitTests/UnitTestsMain.cpp --- a/SQLite/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:49:53 2023 +0200 +++ b/SQLite/UnitTests/UnitTestsMain.cpp Sat Apr 15 13:58:16 2023 +0200 @@ -35,28 +35,30 @@ TEST(SQLiteIndex, Lock) { + std::list identifierTags; + { // No locking if using memory backend OrthancDatabases::SQLiteIndex db1(NULL); - std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); + std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1, false, identifierTags)); OrthancDatabases::SQLiteIndex db2(NULL); - std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2)); + std::unique_ptr manager2(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2, false, identifierTags)); } Orthanc::SystemToolbox::RemoveFile("index.db"); { OrthancDatabases::SQLiteIndex db1(NULL, "index.db"); - std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1)); + std::unique_ptr manager1(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db1, false, identifierTags)); OrthancDatabases::SQLiteIndex db2(NULL, "index.db"); - ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2), Orthanc::OrthancException); + ASSERT_THROW(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db2, false, identifierTags), Orthanc::OrthancException); } { OrthancDatabases::SQLiteIndex db3(NULL, "index.db"); - std::unique_ptr manager3(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3)); + std::unique_ptr manager3(OrthancDatabases::IndexBackend::CreateSingleDatabaseManager(db3, false, identifierTags)); } }