Mercurial > hg > orthanc-databases
changeset 652:e5051580aeac attach-custom-data
merged default -> attach-custom-data
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Mon, 10 Mar 2025 18:53:00 +0100 (2 months ago) |
parents | 354d4c617387 (diff) f9e43680c480 (current diff) |
children | d5c889dea585 |
files | Framework/Common/DatabaseManager.cpp Framework/Common/DatabaseManager.h Framework/Plugins/IndexBackend.cpp Framework/Plugins/IndexBackend.h MySQL/Plugins/MySQLIndex.cpp MySQL/Plugins/MySQLIndex.h Odbc/Plugins/OdbcIndex.cpp Odbc/Plugins/OdbcIndex.h PostgreSQL/CMakeLists.txt PostgreSQL/NEWS PostgreSQL/Plugins/PostgreSQLIndex.cpp PostgreSQL/Plugins/PostgreSQLIndex.h PostgreSQL/Plugins/SQL/Downgrades/Rev4ToRev3.sql PostgreSQL/Plugins/SQL/PrepareIndex.sql PostgreSQL/Plugins/SQL/Upgrades/Rev2ToRev3.sql PostgreSQL/Plugins/SQL/Upgrades/Rev3ToRev4.sql SQLite/Plugins/SQLiteIndex.cpp SQLite/Plugins/SQLiteIndex.h |
diffstat | 37 files changed, 723 insertions(+), 398 deletions(-) [+] |
line wrap: on
line diff
--- a/Framework/Common/DatabaseManager.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Common/DatabaseManager.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -550,6 +550,18 @@ } } + std::string DatabaseManager::StatementBase::ReadStringOrNull(size_t field) const + { + if (IsNull(field)) + { + return std::string(); + } + else + { + return ReadString(field); + } + } + DatabaseManager::CachedStatement::CachedStatement(const StatementId& statementId, DatabaseManager& manager, const std::string& sql) :
--- a/Framework/Common/DatabaseManager.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Common/DatabaseManager.h Mon Mar 10 18:53:00 2025 +0100 @@ -188,6 +188,8 @@ std::string ReadString(size_t field) const; + std::string ReadStringOrNull(size_t field) const; + bool IsNull(size_t field) const; void PrintResult(std::ostream& stream)
--- a/Framework/MySQL/MySQLStatement.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/MySQL/MySQLStatement.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -468,62 +468,64 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); } - ValueType type = formatter_.GetParameterType(i); - const IValue& value = parameters.GetValue(name); - if (value.GetType() != type) - { - LOG(ERROR) << "Bad type of argument provided to a SQL query: " << name; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType); - } - // https://dev.mysql.com/doc/refman/8.0/en/c-api-prepared-statement-type-codes.html - switch (type) + if (value.GetType() == ValueType_Null) { - case ValueType_Integer64: + inputs[i].buffer = NULL; + inputs[i].buffer_type = MYSQL_TYPE_NULL; + } + else + { + ValueType type = formatter_.GetParameterType(i); + + if (value.GetType() != type) { - int64Parameters.push_back(dynamic_cast<const Integer64Value&>(value).GetValue()); - inputs[i].buffer = &int64Parameters.back(); - inputs[i].buffer_type = MYSQL_TYPE_LONGLONG; - break; + LOG(ERROR) << "Bad type of argument provided to a SQL query: " << name; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType); } - case ValueType_Utf8String: + // https://dev.mysql.com/doc/refman/8.0/en/c-api-prepared-statement-type-codes.html + switch (type) { - const std::string& utf8 = dynamic_cast<const Utf8StringValue&>(value).GetContent(); - inputs[i].buffer = const_cast<char*>(utf8.c_str()); - inputs[i].buffer_length = utf8.size(); - inputs[i].buffer_type = MYSQL_TYPE_STRING; - break; - } + case ValueType_Integer64: + { + int64Parameters.push_back(dynamic_cast<const Integer64Value&>(value).GetValue()); + inputs[i].buffer = &int64Parameters.back(); + inputs[i].buffer_type = MYSQL_TYPE_LONGLONG; + break; + } - case ValueType_BinaryString: - { - const std::string& content = dynamic_cast<const BinaryStringValue&>(value).GetContent(); - inputs[i].buffer = const_cast<char*>(content.c_str()); - inputs[i].buffer_length = content.size(); - inputs[i].buffer_type = MYSQL_TYPE_BLOB; - break; - } + case ValueType_Utf8String: + { + const std::string& utf8 = dynamic_cast<const Utf8StringValue&>(value).GetContent(); + inputs[i].buffer = const_cast<char*>(utf8.c_str()); + inputs[i].buffer_length = utf8.size(); + inputs[i].buffer_type = MYSQL_TYPE_STRING; + break; + } - case ValueType_InputFile: - { - const std::string& content = dynamic_cast<const InputFileValue&>(value).GetContent(); - inputs[i].buffer = const_cast<char*>(content.c_str()); - inputs[i].buffer_length = content.size(); - inputs[i].buffer_type = MYSQL_TYPE_BLOB; - break; - } + case ValueType_BinaryString: + { + const std::string& content = dynamic_cast<const BinaryStringValue&>(value).GetContent(); + inputs[i].buffer = const_cast<char*>(content.c_str()); + inputs[i].buffer_length = content.size(); + inputs[i].buffer_type = MYSQL_TYPE_BLOB; + break; + } - case ValueType_Null: - { - inputs[i].buffer = NULL; - inputs[i].buffer_type = MYSQL_TYPE_NULL; - break; + case ValueType_InputFile: + { + const std::string& content = dynamic_cast<const InputFileValue&>(value).GetContent(); + inputs[i].buffer = const_cast<char*>(content.c_str()); + inputs[i].buffer_length = content.size(); + inputs[i].buffer_type = MYSQL_TYPE_BLOB; + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } }
--- a/Framework/Plugins/DatabaseBackendAdapterV2.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/DatabaseBackendAdapterV2.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -187,7 +187,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& /*customData*/) ORTHANC_OVERRIDE { OrthancPluginAttachment attachment; attachment.uuid = uuid.c_str(); @@ -219,7 +220,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& /*customData*/) ORTHANC_OVERRIDE { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_Attachment)
--- a/Framework/Plugins/DatabaseBackendAdapterV3.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/DatabaseBackendAdapterV3.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -421,7 +421,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& /*customData*/) ORTHANC_OVERRIDE { OrthancPluginDatabaseEvent event; event.type = OrthancPluginDatabaseEventType_DeletedAttachment; @@ -467,7 +468,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& /*customData*/) ORTHANC_OVERRIDE { SetupAnswerType(_OrthancPluginDatabaseAnswerType_Attachment);
--- a/Framework/Plugins/DatabaseBackendAdapterV4.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -195,7 +195,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& customData) ORTHANC_OVERRIDE { Orthanc::DatabasePluginMessages::FileInfo* attachment; @@ -224,6 +225,9 @@ attachment->set_compression_type(compressionType); attachment->set_compressed_size(compressedSize); attachment->set_compressed_hash(compressedHash); +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA == 1 + attachment->set_custom_data(customData); +#endif } virtual void SignalDeletedResource(const std::string& publicId, @@ -269,7 +273,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) ORTHANC_OVERRIDE + const std::string& compressedHash, + const std::string& customData) ORTHANC_OVERRIDE { if (lookupAttachment_ != NULL) { @@ -286,6 +291,9 @@ lookupAttachment_->mutable_attachment()->set_compression_type(compressionType); lookupAttachment_->mutable_attachment()->set_compressed_size(compressedSize); lookupAttachment_->mutable_attachment()->set_compressed_hash(compressedHash); +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA==1 + lookupAttachment_->mutable_attachment()->set_custom_data(customData); +#endif } else { @@ -682,6 +690,9 @@ case Orthanc::DatabasePluginMessages::OPERATION_ADD_ATTACHMENT: { +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA + backend.AddAttachment(response, manager, request.add_attachment()); +#else OrthancPluginAttachment attachment; attachment.uuid = request.add_attachment().attachment().uuid().c_str(); attachment.contentType = request.add_attachment().attachment().content_type(); @@ -692,9 +703,9 @@ attachment.compressedHash = request.add_attachment().attachment().compressed_hash().c_str(); backend.AddAttachment(manager, request.add_attachment().id(), attachment, request.add_attachment().revision()); +#endif break; } - case Orthanc::DatabasePluginMessages::OPERATION_CLEAR_CHANGES: { backend.ClearChanges(manager);
--- a/Framework/Plugins/IDatabaseBackend.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/IDatabaseBackend.h Mon Mar 10 18:53:00 2025 +0100 @@ -59,11 +59,20 @@ virtual bool HasRevisionsSupport() const = 0; + virtual bool HasAttachmentCustomDataSupport() const = 0; + virtual void AddAttachment(DatabaseManager& manager, int64_t id, const OrthancPluginAttachment& attachment, int64_t revision) = 0; +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA + // New in Orthanc 1.12.7 + virtual void AddAttachment(Orthanc::DatabasePluginMessages::TransactionResponse& response, + DatabaseManager& manager, + const Orthanc::DatabasePluginMessages::AddAttachment_Request& request) = 0; +#endif + virtual void AttachChild(DatabaseManager& manager, int64_t parent, int64_t child) = 0;
--- a/Framework/Plugins/IDatabaseBackendOutput.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/IDatabaseBackendOutput.h Mon Mar 10 18:53:00 2025 +0100 @@ -56,7 +56,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) = 0; + const std::string& compressedHash, + const std::string& customData) = 0; virtual void SignalDeletedResource(const std::string& publicId, OrthancPluginResourceType resourceType) = 0; @@ -70,7 +71,8 @@ const std::string& uncompressedHash, int32_t compressionType, uint64_t compressedSize, - const std::string& compressedHash) = 0; + const std::string& compressedHash, + const std::string& customData) = 0; virtual void AnswerChange(int64_t seq, int32_t changeType,
--- a/Framework/Plugins/IndexBackend.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/IndexBackend.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -270,7 +270,7 @@ DatabaseManager::CachedStatement statement( STATEMENT_FROM_HERE, manager, "SELECT uuid, fileType, uncompressedSize, uncompressedHash, compressionType, " - "compressedSize, compressedHash FROM DeletedFiles"); + "compressedSize, compressedHash, revision, customData FROM DeletedFiles"); statement.SetReadOnly(true); statement.Execute(); @@ -283,7 +283,8 @@ statement.ReadString(3), statement.ReadInteger32(4), statement.ReadInteger64(5), - statement.ReadString(6)); + statement.ReadString(6), + statement.ReadStringOrNull(8)); statement.Next(); } @@ -354,12 +355,25 @@ } } - - static void ExecuteAddAttachment(DatabaseManager::CachedStatement& statement, - Dictionary& args, + static void ExecuteAddAttachment(DatabaseManager& manager, int64_t id, - const OrthancPluginAttachment& attachment) + const char* uuid, + int32_t contentType, + uint64_t uncompressedSize, + const char* uncompressedHash, + int32_t compressionType, + uint64_t compressedSize, + const char* compressedHash, + const char* customData, + int64_t revision) { + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "INSERT INTO AttachedFiles VALUES(${id}, ${type}, ${uuid}, ${compressed}, " + "${uncompressed}, ${compression}, ${hash}, ${hash-compressed}, ${revision}, ${custom-data})"); + + Dictionary args; + statement.SetParameterType("id", ValueType_Integer64); statement.SetParameterType("type", ValueType_Integer64); statement.SetParameterType("uuid", ValueType_Utf8String); @@ -368,52 +382,62 @@ statement.SetParameterType("compression", ValueType_Integer64); statement.SetParameterType("hash", ValueType_Utf8String); statement.SetParameterType("hash-compressed", ValueType_Utf8String); + statement.SetParameterType("revision", ValueType_Integer64); + statement.SetParameterType("custom-data", ValueType_Utf8String); args.SetIntegerValue("id", id); - args.SetIntegerValue("type", attachment.contentType); - args.SetUtf8Value("uuid", attachment.uuid); - args.SetIntegerValue("compressed", attachment.compressedSize); - args.SetIntegerValue("uncompressed", attachment.uncompressedSize); - args.SetIntegerValue("compression", attachment.compressionType); - args.SetUtf8Value("hash", attachment.uncompressedHash); - args.SetUtf8Value("hash-compressed", attachment.compressedHash); + args.SetIntegerValue("type", contentType); + args.SetUtf8Value("uuid", uuid); + args.SetIntegerValue("compressed", compressedSize); + args.SetIntegerValue("uncompressed", uncompressedSize); + args.SetIntegerValue("compression", compressionType); + args.SetUtf8Value("hash", uncompressedHash); + args.SetUtf8Value("hash-compressed", compressedHash); + args.SetIntegerValue("revision", revision); + if (customData != NULL && strlen(customData) > 0) + { + args.SetUtf8Value("custom-data", customData); + } + else + { + args.SetNullValue("custom-data"); + } statement.Execute(args); } - + void IndexBackend::AddAttachment(DatabaseManager& manager, int64_t id, const OrthancPluginAttachment& attachment, int64_t revision) { - if (HasRevisionsSupport()) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, manager, - "INSERT INTO AttachedFiles VALUES(${id}, ${type}, ${uuid}, ${compressed}, " - "${uncompressed}, ${compression}, ${hash}, ${hash-compressed}, ${revision})"); - - Dictionary args; - - statement.SetParameterType("revision", ValueType_Integer64); - args.SetIntegerValue("revision", revision); - - ExecuteAddAttachment(statement, args, id, attachment); - } - else - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, manager, - "INSERT INTO AttachedFiles VALUES(${id}, ${type}, ${uuid}, ${compressed}, " - "${uncompressed}, ${compression}, ${hash}, ${hash-compressed})"); - - Dictionary args; - ExecuteAddAttachment(statement, args, id, attachment); - } + assert(HasRevisionsSupport() && HasAttachmentCustomDataSupport()); // all plugins support these features now + ExecuteAddAttachment(manager, id, attachment.uuid, attachment.contentType, attachment.uncompressedSize, attachment.uncompressedHash, + attachment.compressionType, attachment.compressedSize, attachment.compressedHash, "", revision); } - +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA + void IndexBackend::AddAttachment(Orthanc::DatabasePluginMessages::TransactionResponse& response, + DatabaseManager& manager, + const Orthanc::DatabasePluginMessages::AddAttachment_Request& request) + { + assert(HasRevisionsSupport() && HasAttachmentCustomDataSupport()); // all plugins support these features now + ExecuteAddAttachment(manager, + request.id(), + request.attachment().uuid().c_str(), + request.attachment().content_type(), + request.attachment().uncompressed_size(), + request.attachment().uncompressed_hash().c_str(), + request.attachment().compression_type(), + request.attachment().compressed_size(), + request.attachment().compressed_hash().c_str(), + request.attachment().custom_data().c_str(), + request.revision()); + } +#endif + + void IndexBackend::AttachChild(DatabaseManager& manager, int64_t parent, int64_t child) @@ -1178,12 +1202,20 @@ statement.Execute(args); } - - static bool ExecuteLookupAttachment(DatabaseManager::CachedStatement& statement, - IDatabaseBackendOutput& output, + + bool IndexBackend::LookupAttachment(IDatabaseBackendOutput& output, + int64_t& revision /*out*/, + DatabaseManager& manager, int64_t id, int32_t contentType) { + assert(HasRevisionsSupport() && HasAttachmentCustomDataSupport()); // we have forced all v4 plugins to support both ! + DatabaseManager::CachedStatement statement( + STATEMENT_FROM_HERE, manager, + "SELECT uuid, uncompressedSize, compressionType, compressedSize, uncompressedHash, " + "compressedHash, revision, customData FROM AttachedFiles WHERE id=${id} AND fileType=${type}"); + + statement.SetReadOnly(true); statement.SetParameterType("id", ValueType_Integer64); statement.SetParameterType("type", ValueType_Integer64); @@ -1200,64 +1232,34 @@ } else { + if (statement.GetResultField(6).GetType() == ValueType_Null) + { + // "NULL" can happen with a database created by PostgreSQL + // plugin <= 3.3 (because of "ALTER TABLE AttachedFiles") + revision = 0; + } + else + { + revision = statement.ReadInteger64(6); + } + + std::string customData; + if (statement.GetResultField(7).GetType() == ValueType_Utf8String) // column has been added in 1.12.0 + { + customData = statement.ReadString(7); + } + output.AnswerAttachment(statement.ReadString(0), contentType, statement.ReadInteger64(1), statement.ReadString(4), statement.ReadInteger32(2), statement.ReadInteger64(3), - statement.ReadString(5)); + statement.ReadString(5), + customData); return true; } - } - - - - /* Use GetOutput().AnswerAttachment() */ - bool IndexBackend::LookupAttachment(IDatabaseBackendOutput& output, - int64_t& revision /*out*/, - DatabaseManager& manager, - int64_t id, - int32_t contentType) - { - if (HasRevisionsSupport()) - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, manager, - "SELECT uuid, uncompressedSize, compressionType, compressedSize, uncompressedHash, " - "compressedHash, revision FROM AttachedFiles WHERE id=${id} AND fileType=${type}"); - - if (ExecuteLookupAttachment(statement, output, id, contentType)) - { - if (statement.GetResultField(6).GetType() == ValueType_Null) - { - // "NULL" can happen with a database created by PostgreSQL - // plugin <= 3.3 (because of "ALTER TABLE AttachedFiles") - revision = 0; - } - else - { - revision = statement.ReadInteger64(6); - } - - return true; - } - else - { - return false; - } - } - else - { - DatabaseManager::CachedStatement statement( - STATEMENT_FROM_HERE, manager, - "SELECT uuid, uncompressedSize, compressionType, compressedSize, uncompressedHash, " - "compressedHash FROM AttachedFiles WHERE id=${id} AND fileType=${type}"); - - revision = 0; - - return ExecuteLookupAttachment(statement, output, id, contentType); - } + } @@ -3253,11 +3255,12 @@ #define C3_STRING_1 3 #define C4_STRING_2 4 #define C5_STRING_3 5 -#define C6_INT_1 6 -#define C7_INT_2 7 -#define C8_INT_3 8 -#define C9_BIG_INT_1 9 -#define C10_BIG_INT_2 10 +#define C6_STRING_4 6 +#define C7_INT_1 7 +#define C8_INT_2 8 +#define C9_INT_3 9 +#define C10_BIG_INT_1 10 +#define C11_BIG_INT_2 11 #define QUERY_LOOKUP 1 #define QUERY_MAIN_DICOM_TAGS 2 @@ -3431,11 +3434,11 @@ std::string revisionInC7; if (HasRevisionsSupport()) { - revisionInC7 = " revision AS c7_int2, "; + revisionInC7 = " revision AS c8_int2, "; } else { - revisionInC7 = " 0 AS C7_int2, "; + revisionInC7 = " 0 AS c8_int2, "; } @@ -3446,11 +3449,12 @@ " Lookup.publicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " " FROM Lookup "; // need MainDicomTags from resource ? @@ -3463,11 +3467,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " tagGroup AS c6_int1, " - " tagElement AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " tagGroup AS c7_int1, " + " tagElement AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN MainDicomTags ON MainDicomTags.id = Lookup.internalId "; } @@ -3482,11 +3487,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Metadata ON Metadata.id = Lookup.internalId "; } @@ -3501,11 +3507,12 @@ " uuid AS c3_string1, " " uncompressedHash AS c4_string2, " " compressedHash AS c5_string3, " - " fileType AS c6_int1, " + " customData AS c6_string4, " + " fileType AS c7_int1, " + revisionInC7 + - " compressionType AS c8_int3, " - " compressedSize AS c9_big_int1, " - " uncompressedSize AS c10_big_int2 " + " compressionType AS c9_int3, " + " compressedSize AS c10_big_int1, " + " uncompressedSize AS c11_big_int2 " "FROM Lookup " "INNER JOIN AttachedFiles ON AttachedFiles.id = Lookup.internalId "; } @@ -3520,11 +3527,12 @@ " label AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Labels ON Labels.id = Lookup.internalId "; } @@ -3558,11 +3566,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " tagGroup AS c6_int1, " - " tagElement AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " tagGroup AS c7_int1, " + " tagElement AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " "INNER JOIN MainDicomTags ON MainDicomTags.id = currentLevel.parentId "; @@ -3577,11 +3586,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " "INNER JOIN Metadata ON Metadata.id = currentLevel.parentId "; @@ -3613,11 +3623,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " tagGroup AS c6_int1, " - " tagElement AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " tagGroup AS c7_int1, " + " tagElement AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId " @@ -3633,11 +3644,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId " @@ -3660,11 +3672,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " tagGroup AS c6_int1, " - " tagElement AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " tagGroup AS c7_int1, " + " tagElement AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " " INNER JOIN MainDicomTags ON MainDicomTags.id = childLevel.internalId AND (tagGroup, tagElement) IN (" + JoinRequestedTags(childrenSpec) + ")"; @@ -3680,11 +3693,12 @@ " childLevel.publicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId "; } @@ -3715,16 +3729,17 @@ " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " " COALESCE(" " (" + getResourcesChildCount + ")," " (SELECT COUNT(childLevel.internalId)" " FROM Resources AS childLevel" " WHERE Lookup.internalId = childLevel.parentId" - " )) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " )) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "LEFT JOIN Resources ON Lookup.internalId = Resources.internalId "; } @@ -3737,11 +3752,12 @@ " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " COUNT(childLevel.internalId) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " COUNT(childLevel.internalId) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId GROUP BY Lookup.internalId "; } @@ -3756,11 +3772,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " " INNER JOIN Metadata ON Metadata.id = childLevel.internalId AND Metadata.type IN (" + JoinRequestedMetadata(childrenSpec) + ") "; @@ -3780,11 +3797,12 @@ " grandChildLevel.publicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId "; @@ -3793,6 +3811,7 @@ { if (HasChildCountColumn()) { + // we get the count value either from the childCount column if it has been computed or from the Resources table std::string getResourcesGrandChildCount; @@ -3815,23 +3834,25 @@ // we get the count value either from the childCount column if it has been computed or from the Resources table sql += "UNION ALL SELECT " + " " TOSTRING(QUERY_GRAND_CHILDREN_COUNT) " AS c0_queryId, " " Lookup.internalId AS c1_internalId, " " " + formatter.FormatNull("BIGINT") + " AS c2_rowNumber, " " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " " COALESCE(" " (" + getResourcesGrandChildCount + ")," " (SELECT COUNT(grandChildLevel.internalId)" " FROM Resources AS childLevel" " INNER JOIN Resources AS grandChildLevel ON childLevel.internalId = grandChildLevel.parentId" " WHERE Lookup.internalId = childLevel.parentId" - " )) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " )) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup "; } else @@ -3843,11 +3864,12 @@ " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " COUNT(grandChildLevel.internalId) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " COUNT(grandChildLevel.internalId) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " " INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId GROUP BY Lookup.internalId "; @@ -3863,11 +3885,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " tagGroup AS c6_int1, " - " tagElement AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " tagGroup AS c7_int1, " + " tagElement AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " " INNER JOIN Resources grandChildLevel ON grandChildLevel.parentId = childLevel.internalId " @@ -3883,11 +3906,12 @@ " value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " " INNER JOIN Resources grandChildLevel ON grandChildLevel.parentId = childLevel.internalId " @@ -3908,11 +3932,12 @@ " grandGrandChildLevel.publicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId " @@ -3952,9 +3977,10 @@ " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " " COALESCE(" " (" + getResourcesGrandGrandChildCount + ")," " (SELECT COUNT(grandGrandChildLevel.internalId)" @@ -3962,8 +3988,8 @@ " INNER JOIN Resources AS grandChildLevel ON childLevel.internalId = grandChildLevel.parentId" " INNER JOIN Resources AS grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId" " WHERE Lookup.internalId = childLevel.parentId" - " )) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " )) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup "; } else @@ -3975,11 +4001,12 @@ " " + formatter.FormatNull("TEXT") + " AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " COUNT(grandChildLevel.internalId) AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " COUNT(grandChildLevel.internalId) AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " "INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId " @@ -4000,11 +4027,12 @@ " parentLevel.publicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " "FROM Lookup " " INNER JOIN Resources currentLevel ON currentLevel.internalId = Lookup.internalId " " INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "; @@ -4021,11 +4049,12 @@ " instancePublicId AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " " + formatter.FormatNull("INT") + " AS c6_int1, " - " " + formatter.FormatNull("INT") + " AS c7_int2, " - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " instanceInternalId AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " " + formatter.FormatNull("INT") + " AS c7_int1, " + " " + formatter.FormatNull("INT") + " AS c8_int2, " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " instanceInternalId AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " " FROM OneInstance "; sql += " UNION ALL SELECT" @@ -4035,11 +4064,12 @@ " Metadata.value AS c3_string1, " " " + formatter.FormatNull("TEXT") + " AS c4_string2, " " " + formatter.FormatNull("TEXT") + " AS c5_string3, " - " Metadata.type AS c6_int1, " + " " + formatter.FormatNull("TEXT") + " AS c6_string4, " + " Metadata.type AS c7_int1, " + revisionInC7 + - " " + formatter.FormatNull("INT") + " AS c8_int3, " - " " + formatter.FormatNull("BIGINT") + " AS c9_big_int1, " - " " + formatter.FormatNull("BIGINT") + " AS c10_big_int2 " + " " + formatter.FormatNull("INT") + " AS c9_int3, " + " " + formatter.FormatNull("BIGINT") + " AS c10_big_int1, " + " " + formatter.FormatNull("BIGINT") + " AS c11_big_int2 " " FROM Metadata " " INNER JOIN OneInstance ON Metadata.id = OneInstance.instanceInternalId"; @@ -4050,11 +4080,12 @@ " uuid AS c3_string1, " " uncompressedHash AS c4_string2, " " compressedHash AS c5_string3, " - " fileType AS c6_int1, " + " customData AS c6_string4, " + " fileType AS c7_int1, " + revisionInC7 + - " compressionType AS c8_int3, " - " compressedSize AS c9_big_int1, " - " uncompressedSize AS c10_big_int2 " + " compressionType AS c9_int3, " + " compressedSize AS c10_big_int1, " + " uncompressedSize AS c11_big_int2 " " FROM AttachedFiles " " INNER JOIN OneInstance ON AttachedFiles.id = OneInstance.instanceInternalId"; @@ -4110,8 +4141,8 @@ Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); tag->set_value(statement->ReadString(C3_STRING_1)); - tag->set_group(statement->ReadInteger32(C6_INT_1)); - tag->set_element(statement->ReadInteger32(C7_INT_2)); + tag->set_group(statement->ReadInteger32(C7_INT_1)); + tag->set_element(statement->ReadInteger32(C8_INT_2)); }; break; case QUERY_PARENT_MAIN_DICOM_TAGS: @@ -4120,8 +4151,8 @@ Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); tag->set_value(statement->ReadString(C3_STRING_1)); - tag->set_group(statement->ReadInteger32(C6_INT_1)); - tag->set_element(statement->ReadInteger32(C7_INT_2)); + tag->set_group(statement->ReadInteger32(C7_INT_1)); + tag->set_element(statement->ReadInteger32(C8_INT_2)); }; break; case QUERY_GRAND_PARENT_MAIN_DICOM_TAGS: @@ -4130,8 +4161,8 @@ Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); tag->set_value(statement->ReadString(C3_STRING_1)); - tag->set_group(statement->ReadInteger32(C6_INT_1)); - tag->set_element(statement->ReadInteger32(C7_INT_2)); + tag->set_group(statement->ReadInteger32(C7_INT_1)); + tag->set_element(statement->ReadInteger32(C8_INT_2)); }; break; case QUERY_CHILDREN_IDENTIFIERS: @@ -4144,7 +4175,7 @@ case QUERY_CHILDREN_COUNT: { Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 1)); - content->set_count(statement->ReadInteger64(C9_BIG_INT_1)); + content->set_count(statement->ReadInteger64(C10_BIG_INT_1)); }; break; case QUERY_CHILDREN_MAIN_DICOM_TAGS: @@ -4152,8 +4183,8 @@ Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 1)); Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); tag->set_value(statement->ReadString(C3_STRING_1)); // TODO: handle sequences ?? - tag->set_group(statement->ReadInteger32(C6_INT_1)); - tag->set_element(statement->ReadInteger32(C7_INT_2)); + tag->set_group(statement->ReadInteger32(C7_INT_1)); + tag->set_element(statement->ReadInteger32(C8_INT_2)); }; break; case QUERY_CHILDREN_METADATA: @@ -4162,7 +4193,7 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); + metadata->set_key(statement->ReadInteger32(C7_INT_1)); metadata->set_revision(0); // Setting a revision is not required in this case, as of Orthanc 1.12.5 }; break; @@ -4176,7 +4207,7 @@ case QUERY_GRAND_CHILDREN_COUNT: { Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 2)); - content->set_count(statement->ReadInteger64(C9_BIG_INT_1)); + content->set_count(statement->ReadInteger64(C10_BIG_INT_1)); }; break; case QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS: @@ -4185,8 +4216,8 @@ Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); tag->set_value(statement->ReadString(C3_STRING_1)); // TODO: handle sequences ?? - tag->set_group(statement->ReadInteger32(C6_INT_1)); - tag->set_element(statement->ReadInteger32(C7_INT_2)); + tag->set_group(statement->ReadInteger32(C7_INT_1)); + tag->set_element(statement->ReadInteger32(C8_INT_2)); }; break; case QUERY_GRAND_CHILDREN_METADATA: @@ -4195,7 +4226,7 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); + metadata->set_key(statement->ReadInteger32(C7_INT_1)); metadata->set_revision(0); // Setting a revision is not required in this case, as of Orthanc 1.12.5 }; break; @@ -4209,7 +4240,7 @@ case QUERY_GRAND_GRAND_CHILDREN_COUNT: { Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 3)); - content->set_count(statement->ReadInteger64(C9_BIG_INT_1)); + content->set_count(statement->ReadInteger64(C10_BIG_INT_1)); }; break; case QUERY_ATTACHMENTS: @@ -4219,14 +4250,15 @@ attachment->set_uuid(statement->ReadString(C3_STRING_1)); attachment->set_uncompressed_hash(statement->ReadString(C4_STRING_2)); attachment->set_compressed_hash(statement->ReadString(C5_STRING_3)); - attachment->set_content_type(statement->ReadInteger32(C6_INT_1)); - attachment->set_compression_type(statement->ReadInteger32(C8_INT_3)); - attachment->set_compressed_size(statement->ReadInteger64(C9_BIG_INT_1)); - attachment->set_uncompressed_size(statement->ReadInteger64(C10_BIG_INT_2)); - - if (!statement->IsNull(C7_INT_2)) // revision can be null for files that have been atttached by older Orthanc versions + attachment->set_custom_data(statement->ReadStringOrNull(C6_STRING_4)); + attachment->set_content_type(statement->ReadInteger32(C7_INT_1)); + attachment->set_compression_type(statement->ReadInteger32(C9_INT_3)); + attachment->set_compressed_size(statement->ReadInteger64(C10_BIG_INT_1)); + attachment->set_uncompressed_size(statement->ReadInteger64(C11_BIG_INT_2)); + + if (!statement->IsNull(C8_INT_2)) // revision can be null for files that have been atttached by older Orthanc versions { - responses[internalId]->add_attachments_revisions(statement->ReadInteger32(C7_INT_2)); + responses[internalId]->add_attachments_revisions(statement->ReadInteger32(C8_INT_2)); } else { @@ -4240,11 +4272,11 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); + metadata->set_key(statement->ReadInteger32(C7_INT_1)); - if (!statement->IsNull(C7_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions + if (!statement->IsNull(C8_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions { - metadata->set_revision(statement->ReadInteger32(C7_INT_2)); + metadata->set_revision(statement->ReadInteger32(C8_INT_2)); } else { @@ -4258,11 +4290,11 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); - - if (!statement->IsNull(C7_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions + metadata->set_key(statement->ReadInteger32(C7_INT_1)); + + if (!statement->IsNull(C8_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions { - metadata->set_revision(statement->ReadInteger32(C7_INT_2)); + metadata->set_revision(statement->ReadInteger32(C8_INT_2)); } else { @@ -4276,11 +4308,11 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); - - if (!statement->IsNull(C7_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions + metadata->set_key(statement->ReadInteger32(C7_INT_1)); + + if (!statement->IsNull(C8_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions { - metadata->set_revision(statement->ReadInteger32(C7_INT_2)); + metadata->set_revision(statement->ReadInteger32(C8_INT_2)); } else { @@ -4302,11 +4334,11 @@ Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = responses[internalId]->add_one_instance_metadata(); metadata->set_value(statement->ReadString(C3_STRING_1)); - metadata->set_key(statement->ReadInteger32(C6_INT_1)); - - if (!statement->IsNull(C7_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions + metadata->set_key(statement->ReadInteger32(C7_INT_1)); + + if (!statement->IsNull(C8_INT_2)) // revision can be null for metadata that have been created by older Orthanc versions { - metadata->set_revision(statement->ReadInteger32(C7_INT_2)); + metadata->set_revision(statement->ReadInteger32(C8_INT_2)); } else { @@ -4320,10 +4352,11 @@ attachment->set_uuid(statement->ReadString(C3_STRING_1)); attachment->set_uncompressed_hash(statement->ReadString(C4_STRING_2)); attachment->set_compressed_hash(statement->ReadString(C5_STRING_3)); - attachment->set_content_type(statement->ReadInteger32(C6_INT_1)); - attachment->set_compression_type(statement->ReadInteger32(C8_INT_3)); - attachment->set_compressed_size(statement->ReadInteger64(C9_BIG_INT_1)); - attachment->set_uncompressed_size(statement->ReadInteger64(C10_BIG_INT_2)); + attachment->set_custom_data(statement->ReadStringOrNull(C6_STRING_4)); + attachment->set_content_type(statement->ReadInteger32(C7_INT_1)); + attachment->set_compression_type(statement->ReadInteger32(C9_INT_3)); + attachment->set_compressed_size(statement->ReadInteger64(C10_BIG_INT_1)); + attachment->set_uncompressed_size(statement->ReadInteger64(C11_BIG_INT_2)); }; break; default:
--- a/Framework/Plugins/IndexBackend.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/IndexBackend.h Mon Mar 10 18:53:00 2025 +0100 @@ -102,7 +102,14 @@ int64_t id, const OrthancPluginAttachment& attachment, int64_t revision) ORTHANC_OVERRIDE; - + +#if ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA + // New in Orthanc 1.12.7 + virtual void AddAttachment(Orthanc::DatabasePluginMessages::TransactionResponse& response, + DatabaseManager& manager, + const Orthanc::DatabasePluginMessages::AddAttachment_Request& request) ORTHANC_OVERRIDE; +#endif + virtual void AttachChild(DatabaseManager& manager, int64_t parent, int64_t child) ORTHANC_OVERRIDE;
--- a/Framework/Plugins/MessagesToolbox.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/Plugins/MessagesToolbox.h Mon Mar 10 18:53:00 2025 +0100 @@ -59,6 +59,15 @@ #endif +#define ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA 0 + +#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) +# if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 7) +# undef ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA +# define ORTHANC_PLUGINS_HAS_ATTACHMENTS_CUSTOM_DATA 1 +# endif +#endif + #include <Enumerations.h>
--- a/Framework/PostgreSQL/PostgreSQLStatement.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Framework/PostgreSQL/PostgreSQLStatement.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -528,42 +528,47 @@ { const std::string& name = formatter_.GetParameterName(i); - switch (formatter_.GetParameterType(i)) - { - case ValueType_Integer64: - BindInteger64(i, dynamic_cast<const Integer64Value&>(parameters.GetValue(name)).GetValue()); - break; + const IValue& value = parameters.GetValue(name); - case ValueType_Integer32: - BindInteger(i, dynamic_cast<const Integer32Value&>(parameters.GetValue(name)).GetValue()); - break; + if (value.GetType() == ValueType_Null) + { + BindNull(i); + } + else + { + switch (formatter_.GetParameterType(i)) + { + case ValueType_Integer64: + BindInteger64(i, dynamic_cast<const Integer64Value&>(parameters.GetValue(name)).GetValue()); + break; - case ValueType_Null: - BindNull(i); - break; + case ValueType_Integer32: + BindInteger(i, dynamic_cast<const Integer32Value&>(parameters.GetValue(name)).GetValue()); + break; - case ValueType_Utf8String: - BindString(i, dynamic_cast<const Utf8StringValue&> - (parameters.GetValue(name)).GetContent()); - break; + case ValueType_Utf8String: + BindString(i, dynamic_cast<const Utf8StringValue&> + (parameters.GetValue(name)).GetContent()); + break; - case ValueType_BinaryString: - BindString(i, dynamic_cast<const BinaryStringValue&> - (parameters.GetValue(name)).GetContent()); - break; + case ValueType_BinaryString: + BindString(i, dynamic_cast<const BinaryStringValue&> + (parameters.GetValue(name)).GetContent()); + break; - case ValueType_InputFile: - { - const InputFileValue& blob = - dynamic_cast<const InputFileValue&>(parameters.GetValue(name)); + case ValueType_InputFile: + { + const InputFileValue& blob = + dynamic_cast<const InputFileValue&>(parameters.GetValue(name)); - PostgreSQLLargeObject largeObject(database_, blob.GetContent()); - BindLargeObject(i, largeObject); - break; + PostgreSQLLargeObject largeObject(database_, blob.GetContent()); + BindLargeObject(i, largeObject); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } }
--- a/MySQL/CMakeLists.txt Thu Feb 27 09:14:30 2025 +0100 +++ b/MySQL/CMakeLists.txt Mon Mar 10 18:53:00 2025 +0100 @@ -91,6 +91,7 @@ MYSQL_PREPARE_INDEX ${CMAKE_SOURCE_DIR}/Plugins/PrepareIndex.sql MYSQL_GET_LAST_CHANGE_INDEX ${CMAKE_SOURCE_DIR}/Plugins/GetLastChangeIndex.sql MYSQL_CREATE_INSTANCE ${CMAKE_SOURCE_DIR}/Plugins/CreateInstance.sql + MYSQL_INSTALL_REVISION_AND_CUSTOM_DATA ${CMAKE_SOURCE_DIR}/Plugins/InstallRevisionAndCustomData.sql MYSQL_DELETE_RESOURCES ${CMAKE_SOURCE_DIR}/Plugins/DeleteResources.sql )
--- a/MySQL/NEWS Thu Feb 27 09:14:30 2025 +0100 +++ b/MySQL/NEWS Mon Mar 10 18:53:00 2025 +0100 @@ -1,6 +1,8 @@ Pending changes in the mainline =============================== +* Added support for customData in AttachedFiles +* Added support for revision in AttachedFiles & Metadata * Added support for ExtendedChanges: - changes?type=...&to=... * Added support for ExtendedFind
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MySQL/Plugins/InstallRevisionAndCustomData.sql Mon Mar 10 18:53:00 2025 +0100 @@ -0,0 +1,28 @@ +ALTER TABLE AttachedFiles ADD COLUMN revision INTEGER; +ALTER TABLE DeletedFiles ADD COLUMN revision INTEGER; +ALTER TABLE Metadata ADD COLUMN revision INTEGER; + +ALTER TABLE AttachedFiles ADD COLUMN customData LONGTEXT; +ALTER TABLE DeletedFiles ADD COLUMN customData LONGTEXT; + +DROP TRIGGER AttachedFileDeleted; + +CREATE TRIGGER AttachedFileDeleted +AFTER DELETE ON AttachedFiles +FOR EACH ROW + BEGIN + INSERT INTO DeletedFiles VALUES(old.uuid, old.filetype, old.compressedSize, + old.uncompressedSize, old.compressionType, + old.uncompressedHash, old.compressedHash, + old.revision, old.customData)@ + END; + + +DROP TRIGGER ResourceDeleted; + +CREATE TRIGGER ResourceDeleted +BEFORE DELETE ON Resources +FOR EACH ROW + BEGIN + INSERT INTO DeletedFiles SELECT uuid, fileType, compressedSize, uncompressedSize, compressionType, uncompressedHash, compressedHash, revision, customData FROM AttachedFiles WHERE id=old.internalId@ + END; \ No newline at end of file
--- a/MySQL/Plugins/MySQLIndex.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/MySQL/Plugins/MySQLIndex.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -338,8 +338,26 @@ t.Commit(); } + if (revision == 8) + { + DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); + + // Install revision and customData extension + std::string query; + + Orthanc::EmbeddedResources::GetFileResource + (query, Orthanc::EmbeddedResources::MYSQL_INSTALL_REVISION_AND_CUSTOM_DATA); - if (revision != 8) + // Need to escape arobases: Don't use "t.GetDatabaseTransaction().ExecuteMultiLines()" here + db.ExecuteMultiLines(query, true); + + revision = 9; + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + + t.Commit(); + } + + if (revision != 9) { LOG(ERROR) << "MySQL plugin is incompatible with database schema revision: " << revision; throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
--- a/MySQL/Plugins/MySQLIndex.h Thu Feb 27 09:14:30 2025 +0100 +++ b/MySQL/Plugins/MySQLIndex.h Mon Mar 10 18:53:00 2025 +0100 @@ -58,7 +58,12 @@ virtual bool HasRevisionsSupport() const ORTHANC_OVERRIDE { - return false; // TODO - REVISIONS + return true; + } + + virtual bool HasAttachmentCustomDataSupport() const ORTHANC_OVERRIDE + { + return true; } virtual int64_t CreateResource(DatabaseManager& manager,
--- a/MySQL/Plugins/PrepareIndex.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/MySQL/Plugins/PrepareIndex.sql Mon Mar 10 18:53:00 2025 +0100 @@ -48,6 +48,8 @@ compressionType INTEGER, uncompressedHash VARCHAR(40), compressedHash VARCHAR(40), + -- revision INTEGER, -- new in v 4.X, added in MySQLIndex::ConfigureDatabase + -- customData LONGTEXT, -- new in v 4.X, added in MySQLIndex::ConfigureDatabase PRIMARY KEY(id, fileType), CONSTRAINT AttachedFiles1 FOREIGN KEY (id) REFERENCES Resources(internalId) ON DELETE CASCADE ); @@ -104,6 +106,8 @@ compressionType INTEGER, -- 4 uncompressedHash VARCHAR(40), -- 5 compressedHash VARCHAR(40) -- 6 + -- revision INTEGER, -- new in v 4.X, added in MySQLIndex::ConfigureDatabase + -- customData LONGTEXT, -- new in v 4.X, added in MySQLIndex::ConfigureDatabase ); -- End of differences @@ -119,6 +123,8 @@ INSERT INTO DeletedFiles VALUES(old.uuid, old.filetype, old.compressedSize, old.uncompressedSize, old.compressionType, old.uncompressedHash, old.compressedHash)@ + -- old.revision, old.customData -- new in v 4.X, added in MySQLIndex::ConfigureDatabase + END; @@ -127,6 +133,7 @@ FOR EACH ROW BEGIN INSERT INTO DeletedFiles SELECT uuid, fileType, compressedSize, uncompressedSize, compressionType, uncompressedHash, compressedHash FROM AttachedFiles WHERE id=old.internalId@ + -- revision, customData -- new in v 4.X, added in MySQLIndex::ConfigureDatabase END;
--- a/Odbc/NEWS Thu Feb 27 09:14:30 2025 +0100 +++ b/Odbc/NEWS Mon Mar 10 18:53:00 2025 +0100 @@ -21,7 +21,6 @@ * Now detecting communication link failure with the DB and retrying to connect. * Fixed "MaximumConnectionRetries" configuration that was not taken into account. - Release 1.1 (2021-12-06) ========================
--- a/Odbc/Plugins/OdbcIndex.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/Odbc/Plugins/OdbcIndex.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -163,6 +163,49 @@ return OdbcDatabase::CreateDatabaseFactory(maxConnectionRetries_, connectionRetryInterval_, connectionString_, true); } + static void AdaptTypesToDialect(std::string& sql, Dialect dialect) + { + switch (dialect) + { + case Dialect_SQLite: + boost::replace_all(sql, "${LONGTEXT}", "TEXT"); + boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT"); + boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "NULL, "); + break; + + case Dialect_PostgreSQL: + boost::replace_all(sql, "${LONGTEXT}", "TEXT"); + boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGSERIAL NOT NULL PRIMARY KEY"); + boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "DEFAULT, "); + break; + + case Dialect_MySQL: + boost::replace_all(sql, "${LONGTEXT}", "LONGTEXT"); + boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY"); + boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "NULL, "); + break; + + case Dialect_MSSQL: + /** + * cf. OMSSQL-5: Use VARCHAR(MAX) instead of TEXT: (1) + * Microsoft issued a warning stating that "ntext, text, and + * image data types will be removed in a future version of + * SQL Server" + * (https://msdn.microsoft.com/en-us/library/ms187993.aspx), + * and (2) SQL Server does not support comparison of TEXT + * with '=' operator (e.g. in WHERE statements such as + * IndexBackend::LookupIdentifier())." + **/ + boost::replace_all(sql, "${LONGTEXT}", "VARCHAR(MAX)"); + boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGINT IDENTITY NOT NULL PRIMARY KEY"); + boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", ""); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + void OdbcIndex::ConfigureDatabase(DatabaseManager& manager, bool hasIdentifierTags, @@ -190,46 +233,8 @@ { std::string sql; Orthanc::EmbeddedResources::GetFileResource(sql, Orthanc::EmbeddedResources::ODBC_PREPARE_INDEX); - - switch (db.GetDialect()) - { - case Dialect_SQLite: - boost::replace_all(sql, "${LONGTEXT}", "TEXT"); - boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT"); - boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "NULL, "); - break; - - case Dialect_PostgreSQL: - boost::replace_all(sql, "${LONGTEXT}", "TEXT"); - boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGSERIAL NOT NULL PRIMARY KEY"); - boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "DEFAULT, "); - break; - - case Dialect_MySQL: - boost::replace_all(sql, "${LONGTEXT}", "LONGTEXT"); - boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY"); - boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", "NULL, "); - break; - - case Dialect_MSSQL: - /** - * cf. OMSSQL-5: Use VARCHAR(MAX) instead of TEXT: (1) - * Microsoft issued a warning stating that "ntext, text, and - * image data types will be removed in a future version of - * SQL Server" - * (https://msdn.microsoft.com/en-us/library/ms187993.aspx), - * and (2) SQL Server does not support comparison of TEXT - * with '=' operator (e.g. in WHERE statements such as - * IndexBackend::LookupIdentifier())." - **/ - boost::replace_all(sql, "${LONGTEXT}", "VARCHAR(MAX)"); - boost::replace_all(sql, "${AUTOINCREMENT_TYPE}", "BIGINT IDENTITY NOT NULL PRIMARY KEY"); - boost::replace_all(sql, "${AUTOINCREMENT_INSERT}", ""); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } + + AdaptTypesToDialect(sql, db.GetDialect()); { DatabaseManager::Transaction t(manager, TransactionType_ReadWrite); @@ -243,6 +248,22 @@ db.ExecuteMultiLines("ALTER DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); } + { // v 4.X: add customData + int patchLevel; + + if (!LookupGlobalIntegerProperty(patchLevel, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel)) + { + std::string sqlAddCustomData = "ALTER TABLE AttachedFiles ADD customData ${LONGTEXT};" + "ALTER TABLE DeletedFiles ADD customData ${LONGTEXT}"; + + AdaptTypesToDialect(sqlAddCustomData, db.GetDialect()); + + db.ExecuteMultiLines(sqlAddCustomData); + + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, 1); + } + } + t.Commit(); } }
--- a/Odbc/Plugins/OdbcIndex.h Thu Feb 27 09:14:30 2025 +0100 +++ b/Odbc/Plugins/OdbcIndex.h Mon Mar 10 18:53:00 2025 +0100 @@ -73,6 +73,11 @@ return true; } + bool HasAttachmentCustomDataSupport() const ORTHANC_OVERRIDE + { + return true; + } + virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, OrthancPluginResourceType type) ORTHANC_OVERRIDE;
--- a/PostgreSQL/CMakeLists.txt Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/CMakeLists.txt Mon Mar 10 18:53:00 2025 +0100 @@ -94,6 +94,7 @@ POSTGRESQL_UPGRADE_REV1_TO_REV2 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev1ToRev2.sql POSTGRESQL_UPGRADE_REV2_TO_REV3 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev2ToRev3.sql POSTGRESQL_UPGRADE_REV3_TO_REV4 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev3ToRev4.sql + POSTGRESQL_UPGRADE_REV4_TO_REV5 ${CMAKE_SOURCE_DIR}/Plugins/SQL/Upgrades/Rev4ToRev5.sql )
--- a/PostgreSQL/NEWS Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/NEWS Mon Mar 10 18:53:00 2025 +0100 @@ -1,6 +1,20 @@ Pending changes in the mainline =============================== +DB schema revision: 4 + +Minimum plugin SDK (for build): 1.12.6 +Optimal plugin SDK: 1.12.6 + +Minimum Orthanc runtime: 1.12.6 +Optimal Orthanc runtime: 1.12.6 + +Minimal Postgresql Server version: 9 +Optimal Postgresql Server version: 11+ + + +* Added support for customData in AttachedFiles + Release 7.2 (2025-02-27)
--- a/PostgreSQL/Plugins/PostgreSQLIndex.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -49,7 +49,7 @@ static const GlobalProperty GlobalProperty_HasComputeStatisticsReadOnly = GlobalProperty_DatabaseInternal4; } -#define CURRENT_DB_REVISION 4 +#define CURRENT_DB_REVISION 5 namespace OrthancDatabases { @@ -235,6 +235,19 @@ currentRevision = 4; } + if (currentRevision == 4) + { + LOG(WARNING) << "Upgrading DB schema from revision 4 to revision 5"; + + std::string query; + + Orthanc::EmbeddedResources::GetFileResource + (query, Orthanc::EmbeddedResources::POSTGRESQL_UPGRADE_REV4_TO_REV5); + t.GetDatabaseTransaction().ExecuteMultiLines(query); + hasAppliedAnUpgrade = true; + currentRevision = 5; + } + if (hasAppliedAnUpgrade) { LOG(WARNING) << "Upgrading DB schema by applying PrepareIndex.sql";
--- a/PostgreSQL/Plugins/PostgreSQLIndex.h Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/PostgreSQLIndex.h Mon Mar 10 18:53:00 2025 +0100 @@ -72,6 +72,11 @@ return true; } + virtual bool HasAttachmentCustomDataSupport() const ORTHANC_OVERRIDE + { + return true; + } + virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, OrthancPluginResourceType type) ORTHANC_OVERRIDE;
--- a/PostgreSQL/Plugins/SQL/Downgrades/Rev3ToRev2.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/Downgrades/Rev3ToRev2.sql Mon Mar 10 18:53:00 2025 +0100 @@ -1,4 +1,4 @@ --- This file contains an SQL procedure to downgrade from schema Rev3 to Rev2 (version = 6, revision = 1). +-- This file contains an SQL procedure to downgrade from schema Rev3 to Rev2 (version = 6). -- It actually deletes the ChildCount table and triggers -- It actually does not uninstall ChildrenIndex2 because it is anyway more efficient than -- ChildrenIndex and is not incompatible with previous revisions.
--- a/PostgreSQL/Plugins/SQL/Downgrades/Rev4ToRev3.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/Downgrades/Rev4ToRev3.sql Mon Mar 10 18:53:00 2025 +0100 @@ -1,5 +1,5 @@ -- This file contains an SQL procedure to downgrade from schema Rev4 to Rev3 (version = 6). - -- It re-installs the old childcount trigger mechanisms +-- It re-installs the old childcount trigger mechanisms DO $$ DECLARE
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql Mon Mar 10 18:53:00 2025 +0100 @@ -53,6 +53,7 @@ uncompressedHash VARCHAR(40), compressedHash VARCHAR(40), revision INTEGER, + customData TEXT, -- new in schema rev 4 PRIMARY KEY(id, fileType) ); @@ -305,7 +306,9 @@ uncompressedSize BIGINT, compressionType INTEGER, uncompressedHash VARCHAR(40), - compressedHash VARCHAR(40) + compressedHash VARCHAR(40), + revision INTEGER, + customData TEXT ); RESET client_min_messages; @@ -323,7 +326,8 @@ INSERT INTO DeletedFiles VALUES (old.uuid, old.filetype, old.compressedSize, old.uncompressedSize, old.compressionType, - old.uncompressedHash, old.compressedHash); + old.uncompressedHash, old.compressedHash, + old.revision, old.customData); RETURN NULL; END; $body$ LANGUAGE plpgsql;
--- a/PostgreSQL/Plugins/SQL/Upgrades/Rev1ToRev2.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/Upgrades/Rev1ToRev2.sql Mon Mar 10 18:53:00 2025 +0100 @@ -1,4 +1,4 @@ --- This file contains part of the changes required to upgrade from Revision 1 to Revision 2 (DB version 6 and revision 1) +-- This file contains part of the changes required to upgrade from Revision 1 to Revision 2 (DB version 6) -- It actually contains only the changes that: -- can not be executed with an idempotent statement in SQL -- or would polute the PrepareIndex.sql
--- a/PostgreSQL/Plugins/SQL/Upgrades/Rev2ToRev3.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/Upgrades/Rev2ToRev3.sql Mon Mar 10 18:53:00 2025 +0100 @@ -41,4 +41,3 @@ -- other changes performed in PrepareIndex.sql: -- add ChildCount triggers -
--- a/PostgreSQL/Plugins/SQL/Upgrades/Rev3ToRev4.sql Thu Feb 27 09:14:30 2025 +0100 +++ b/PostgreSQL/Plugins/SQL/Upgrades/Rev3ToRev4.sql Mon Mar 10 18:53:00 2025 +0100 @@ -1,2 +1,2 @@ -- everything is performed in PrepareIndex.sql -SELECT 1; \ No newline at end of file +SELECT 1;>>>>>>> merge rev
--- a/README Thu Feb 27 09:14:30 2025 +0100 +++ b/README Mon Mar 10 18:53:00 2025 +0100 @@ -57,6 +57,69 @@ https://orthanc.uclouvain.be/book/developers/repositories.html +Development +----------- + +PostgreSQL +========== + +To quickly start a test PG server: + + docker run -p 5432:5432 --rm --env POSTGRES_HOST_AUTH_METHOD=trust postgres:13.4 + +And use this Orthanc configuration: + "PostgreSQL": { + "EnableIndex": true, + "EnableStorage": false, // DICOM files are stored in the Orthanc container in /var/lib/orthanc/db/ + "Host": "localhost", // the name of the PostgreSQL container + "Database": "postgres", // default database name in PostgreSQL container (no need to create it) + "Username": "postgres", // default user name in PostgreSQL container (no need to create it) + "Password": "postgres" + }, + +MySQL +===== + +To quickly start a test MySQL server: + + docker run -p 3306:3306 --rm --env MYSQL_PASSWORD=orthanc --env MYSQL_USER=orthanc --env MYSQL_DATABASE=orthanc --env MYSQL_ROOT_PASSWORD=pwd-root mysql:8.0 mysqld --default-authentication-plugin=mysql_native_password --log-bin-trust-function-creators=1 + +And use this Orthanc configuration: + "MySQL": { + "EnableIndex": true, + "EnableStorage": false, + "Host": "localhost", + "Database": "orthanc", + "Username": "orthanc", + "Password": "orthanc", + "UnixSocket": "" + }, + + +ODBC (SQL Server) +================= + +To quickly start a test MySQL server: + + docker run -e "ACCEPT_EULA=Y" --rm --env "SA_PASSWORD=yourStrong-Password" --entrypoint=bash -it -p 1433:1433 mcr.microsoft.com/mssql/server:2019-latest + +Then: + (sleep 15s && /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P yourStrong-Password -Q 'CREATE DATABASE orthanctest') & /opt/mssql/bin/sqlservr + +And use this Orthanc configuration: + "Odbc" : { + "IndexConnectionString": "Driver={ODBC Driver 17 for SQL Server};Server=tcp:localhost,1433;Database=orthanctest;Uid=sa;Pwd=yourStrong-Password;Encrypt=yes;TrustServerCertificate=yes;Connection Timeout=30;", + "EnableIndex": true, + "EnableStorage": false + } + + +SQLite +====== + +To quickly test the SQLite plugin, simply run orthanc and load the plugin (no configuration required). + + Licensing ---------
--- a/SQLite/CMakeLists.txt Thu Feb 27 09:14:30 2025 +0100 +++ b/SQLite/CMakeLists.txt Mon Mar 10 18:53:00 2025 +0100 @@ -55,6 +55,7 @@ EmbedResources( SQLITE_PREPARE_INDEX ${CMAKE_SOURCE_DIR}/Plugins/PrepareIndex.sql + SQLITE_INSTALL_CUSTOM_DATA ${CMAKE_SOURCE_DIR}/Plugins/InstallCustomData.sql ) if (EXISTS ${ORTHANC_SDK_ROOT}/orthanc/OrthancDatabasePlugin.proto)
--- a/SQLite/NEWS Thu Feb 27 09:14:30 2025 +0100 +++ b/SQLite/NEWS Mon Mar 10 18:53:00 2025 +0100 @@ -1,3 +1,9 @@ +Pending changes in the mainline +=============================== + +* Added support for customData in AttachedFiles + + Pending changes in the mainline ===============================
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SQLite/Plugins/InstallCustomData.sql Mon Mar 10 18:53:00 2025 +0100 @@ -0,0 +1,17 @@ + +-- Add new column for customData +ALTER TABLE AttachedFiles ADD COLUMN customData TEXT; +ALTER TABLE DeletedFiles ADD COLUMN revision INTEGER; +ALTER TABLE DeletedFiles ADD COLUMN customData TEXT; + + +DROP TRIGGER AttachedFileDeleted; + +CREATE TRIGGER AttachedFileDeleted +AFTER DELETE ON AttachedFiles +BEGIN + INSERT INTO DeletedFiles VALUES(old.uuid, old.filetype, old.compressedSize, + old.uncompressedSize, old.compressionType, + old.uncompressedHash, old.compressedHash, + old.revision, old.customData); +END;
--- a/SQLite/Plugins/SQLiteIndex.cpp Thu Feb 27 09:14:30 2025 +0100 +++ b/SQLite/Plugins/SQLiteIndex.cpp Mon Mar 10 18:53:00 2025 +0100 @@ -149,7 +149,22 @@ SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); } - if (revision != 1) + // install customData + if (!LookupGlobalIntegerProperty(revision, manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel) + || revision == 1) + { + std::string query; + + Orthanc::EmbeddedResources::GetFileResource + (query, Orthanc::EmbeddedResources::SQLITE_INSTALL_CUSTOM_DATA); + + t.GetDatabaseTransaction().ExecuteMultiLines(query); + + revision = 2; + SetGlobalIntegerProperty(manager, MISSING_SERVER_IDENTIFIER, Orthanc::GlobalProperty_DatabasePatchLevel, revision); + } + + if (revision != 2) { LOG(ERROR) << "SQLite plugin is incompatible with database schema revision: " << revision; throw Orthanc::OrthancException(Orthanc::ErrorCode_Database);
--- a/SQLite/Plugins/SQLiteIndex.h Thu Feb 27 09:14:30 2025 +0100 +++ b/SQLite/Plugins/SQLiteIndex.h Mon Mar 10 18:53:00 2025 +0100 @@ -55,6 +55,11 @@ return true; } + bool HasAttachmentCustomDataSupport() const ORTHANC_OVERRIDE + { + return true; + } + virtual int64_t CreateResource(DatabaseManager& manager, const char* publicId, OrthancPluginResourceType type) ORTHANC_OVERRIDE;