Mercurial > hg > orthanc-databases
comparison Framework/Plugins/IndexBackend.cpp @ 569:f18e46d7dbf8 attach-custom-data
merged find-refactoring -> attach-custom-data
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 24 Sep 2024 15:04:21 +0200 |
parents | cd9521e04249 77c8544bbd7d |
children | 991b9b285e1a |
comparison
equal
deleted
inserted
replaced
368:82f73188b58d | 569:f18e46d7dbf8 |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
4 * Department, University Hospital of Liege, Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * Copyright (C) 2017-2021 Osimis S.A., Belgium | 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium | |
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | |
6 * | 8 * |
7 * This program is free software: you can redistribute it and/or | 9 * This program is free software: you can redistribute it and/or |
8 * modify it under the terms of the GNU Affero General Public License | 10 * modify it under the terms of the GNU Affero General Public License |
9 * as published by the Free Software Foundation, either version 3 of | 11 * as published by the Free Software Foundation, either version 3 of |
10 * the License, or (at your option) any later version. | 12 * the License, or (at your option) any later version. |
19 **/ | 21 **/ |
20 | 22 |
21 | 23 |
22 #include "IndexBackend.h" | 24 #include "IndexBackend.h" |
23 | 25 |
24 #include "../../Resources/Orthanc/Databases/ISqlLookupFormatter.h" | |
25 #include "../Common/BinaryStringValue.h" | 26 #include "../Common/BinaryStringValue.h" |
26 #include "../Common/Integer64Value.h" | 27 #include "../Common/Integer64Value.h" |
27 #include "../Common/Utf8StringValue.h" | 28 #include "../Common/Utf8StringValue.h" |
28 #include "DatabaseBackendAdapterV2.h" | 29 #include "DatabaseBackendAdapterV2.h" |
29 #include "DatabaseBackendAdapterV3.h" | 30 #include "DatabaseBackendAdapterV3.h" |
31 #include "GlobalProperties.h" | 32 #include "GlobalProperties.h" |
32 | 33 |
33 #include <Compatibility.h> // For std::unique_ptr<> | 34 #include <Compatibility.h> // For std::unique_ptr<> |
34 #include <Logging.h> | 35 #include <Logging.h> |
35 #include <OrthancException.h> | 36 #include <OrthancException.h> |
37 #include <Toolbox.h> | |
36 | 38 |
37 | 39 |
38 namespace OrthancDatabases | 40 namespace OrthancDatabases |
39 { | 41 { |
40 static std::string ConvertWildcardToLike(const std::string& query) | 42 static std::string ConvertWildcardToLike(const std::string& query) |
56 // TODO Escape underscores and percents | 58 // TODO Escape underscores and percents |
57 | 59 |
58 return s; | 60 return s; |
59 } | 61 } |
60 | 62 |
63 static std::string JoinChanges(const std::set<uint32_t>& changeTypes) | |
64 { | |
65 std::set<std::string> changeTypesString; | |
66 for (std::set<uint32_t>::const_iterator it = changeTypes.begin(); it != changeTypes.end(); ++it) | |
67 { | |
68 changeTypesString.insert(boost::lexical_cast<std::string>(*it)); | |
69 } | |
70 | |
71 std::string joinedChangesTypes; | |
72 Orthanc::Toolbox::JoinStrings(joinedChangesTypes, changeTypesString, ", "); | |
73 | |
74 return joinedChangesTypes; | |
75 } | |
61 | 76 |
62 template <typename T> | 77 template <typename T> |
63 static void ReadListOfIntegers(std::list<T>& target, | 78 static void ReadListOfIntegers(std::list<T>& target, |
64 DatabaseManager::CachedStatement& statement, | 79 DatabaseManager::CachedStatement& statement, |
65 const Dictionary& args) | 80 const Dictionary& args) |
108 } | 123 } |
109 } | 124 } |
110 } | 125 } |
111 | 126 |
112 | 127 |
128 namespace // Anonymous namespace to avoid clashes between compilation modules | |
129 { | |
130 struct Change | |
131 { | |
132 int64_t seq_; | |
133 int32_t changeType_; | |
134 OrthancPluginResourceType resourceType_; | |
135 std::string publicId_; | |
136 std::string changeDate_; | |
137 | |
138 Change(int64_t seq, int32_t changeType, OrthancPluginResourceType resourceType, const std::string& publicId, const std::string& changeDate) | |
139 : seq_(seq), changeType_(changeType), resourceType_(resourceType), publicId_(publicId), changeDate_(changeDate) | |
140 { | |
141 } | |
142 }; | |
143 } | |
144 | |
145 | |
113 void IndexBackend::ReadChangesInternal(IDatabaseBackendOutput& output, | 146 void IndexBackend::ReadChangesInternal(IDatabaseBackendOutput& output, |
114 bool& done, | 147 bool& done, |
115 DatabaseManager& manager, | 148 DatabaseManager& manager, |
116 DatabaseManager::CachedStatement& statement, | 149 DatabaseManager::CachedStatement& statement, |
117 const Dictionary& args, | 150 const Dictionary& args, |
118 uint32_t maxResults) | 151 uint32_t limit, |
152 bool returnFirstResults) | |
119 { | 153 { |
120 statement.Execute(args); | 154 statement.Execute(args); |
121 | 155 |
122 uint32_t count = 0; | 156 std::list<Change> changes; |
123 | 157 while (!statement.IsDone()) |
124 while (count < maxResults && | 158 { |
125 !statement.IsDone()) | 159 changes.push_back(Change( |
126 { | |
127 output.AnswerChange( | |
128 statement.ReadInteger64(0), | 160 statement.ReadInteger64(0), |
129 statement.ReadInteger32(1), | 161 statement.ReadInteger32(1), |
130 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(2)), | 162 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(2)), |
131 statement.ReadString(3), | 163 statement.ReadString(3), |
132 statement.ReadString(4)); | 164 statement.ReadString(4) |
165 )); | |
133 | 166 |
134 statement.Next(); | 167 statement.Next(); |
135 count++; | 168 } |
136 } | 169 |
137 | 170 done = changes.size() <= limit; // 'done' means we have returned all requested changes |
138 done = (count < maxResults || | 171 |
139 statement.IsDone()); | 172 // if we have retrieved more changes than requested -> cleanup |
173 if (changes.size() > limit) | |
174 { | |
175 assert(changes.size() == limit+1); // the statement should only request 1 element more | |
176 | |
177 if (returnFirstResults) | |
178 { | |
179 changes.pop_back(); | |
180 } | |
181 else | |
182 { | |
183 changes.pop_front(); | |
184 } | |
185 } | |
186 | |
187 for (std::list<Change>::const_iterator it = changes.begin(); it != changes.end(); ++it) | |
188 { | |
189 output.AnswerChange(it->seq_, it->changeType_, it->resourceType_, it->publicId_, it->changeDate_); | |
190 } | |
140 } | 191 } |
141 | 192 |
142 | 193 |
143 void IndexBackend::ReadExportedResourcesInternal(IDatabaseBackendOutput& output, | 194 void IndexBackend::ReadExportedResourcesInternal(IDatabaseBackendOutput& output, |
144 bool& done, | 195 bool& done, |
145 DatabaseManager::CachedStatement& statement, | 196 DatabaseManager::CachedStatement& statement, |
146 const Dictionary& args, | 197 const Dictionary& args, |
147 uint32_t maxResults) | 198 uint32_t limit) |
148 { | 199 { |
149 statement.Execute(args); | 200 statement.Execute(args); |
150 | 201 |
151 uint32_t count = 0; | 202 uint32_t count = 0; |
152 | 203 |
153 while (count < maxResults && | 204 while (count < limit && |
154 !statement.IsDone()) | 205 !statement.IsDone()) |
155 { | 206 { |
156 int64_t seq = statement.ReadInteger64(0); | 207 int64_t seq = statement.ReadInteger64(0); |
157 OrthancPluginResourceType resourceType = | 208 OrthancPluginResourceType resourceType = |
158 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(1)); | 209 static_cast<OrthancPluginResourceType>(statement.ReadInteger32(1)); |
170 | 221 |
171 statement.Next(); | 222 statement.Next(); |
172 count++; | 223 count++; |
173 } | 224 } |
174 | 225 |
175 done = (count < maxResults || | 226 done = (count < limit || |
176 statement.IsDone()); | 227 statement.IsDone()); |
177 } | 228 } |
229 | |
230 void IndexBackend::ClearRemainingAncestor(DatabaseManager& manager) | |
231 { | |
232 DatabaseManager::CachedStatement statement( | |
233 STATEMENT_FROM_HERE, manager, | |
234 "DELETE FROM RemainingAncestor"); | |
235 | |
236 statement.Execute(); | |
237 } | |
238 | |
178 | 239 |
179 | 240 |
180 void IndexBackend::ClearDeletedFiles(DatabaseManager& manager) | 241 void IndexBackend::ClearDeletedFiles(DatabaseManager& manager) |
181 { | 242 { |
182 DatabaseManager::CachedStatement statement( | 243 DatabaseManager::CachedStatement statement( |
215 statement.ReadInteger64(2), | 276 statement.ReadInteger64(2), |
216 statement.ReadString(3), | 277 statement.ReadString(3), |
217 statement.ReadInteger32(4), | 278 statement.ReadInteger32(4), |
218 statement.ReadInteger64(5), | 279 statement.ReadInteger64(5), |
219 statement.ReadString(6), | 280 statement.ReadString(6), |
220 statement.ReadString(8)); | 281 statement.ReadStringOrNull(8)); |
221 | 282 |
222 statement.Next(); | 283 statement.Next(); |
223 } | 284 } |
224 } | 285 } |
225 | 286 |
439 DatabaseManager& manager, | 500 DatabaseManager& manager, |
440 int64_t id) | 501 int64_t id) |
441 { | 502 { |
442 ClearDeletedFiles(manager); | 503 ClearDeletedFiles(manager); |
443 ClearDeletedResources(manager); | 504 ClearDeletedResources(manager); |
444 | 505 ClearRemainingAncestor(manager); |
445 { | |
446 DatabaseManager::CachedStatement statement( | |
447 STATEMENT_FROM_HERE, manager, | |
448 "DELETE FROM RemainingAncestor"); | |
449 | |
450 statement.Execute(); | |
451 } | |
452 | 506 |
453 { | 507 { |
454 DatabaseManager::CachedStatement statement( | 508 DatabaseManager::CachedStatement statement( |
455 STATEMENT_FROM_HERE, manager, | 509 STATEMENT_FROM_HERE, manager, |
456 "DELETE FROM Resources WHERE internalId=${id}"); | 510 "DELETE FROM Resources WHERE internalId=${id}"); |
466 | 520 |
467 { | 521 { |
468 DatabaseManager::CachedStatement statement( | 522 DatabaseManager::CachedStatement statement( |
469 STATEMENT_FROM_HERE, manager, | 523 STATEMENT_FROM_HERE, manager, |
470 "SELECT * FROM RemainingAncestor"); | 524 "SELECT * FROM RemainingAncestor"); |
471 | |
472 statement.Execute(); | 525 statement.Execute(); |
473 | 526 |
474 if (!statement.IsDone()) | 527 if (!statement.IsDone()) |
475 { | 528 { |
476 output.SignalRemainingAncestor( | 529 output.SignalRemainingAncestor( |
479 | 532 |
480 // There is at most 1 remaining ancestor | 533 // There is at most 1 remaining ancestor |
481 assert((statement.Next(), statement.IsDone())); | 534 assert((statement.Next(), statement.IsDone())); |
482 } | 535 } |
483 } | 536 } |
484 | 537 |
485 SignalDeletedFiles(output, manager); | 538 SignalDeletedFiles(output, manager); |
486 SignalDeletedResources(output, manager); | 539 SignalDeletedResources(output, manager); |
540 | |
487 } | 541 } |
488 | 542 |
489 | 543 |
490 void IndexBackend::GetAllInternalIds(std::list<int64_t>& target, | 544 void IndexBackend::GetAllInternalIds(std::list<int64_t>& target, |
491 DatabaseManager& manager, | 545 DatabaseManager& manager, |
524 | 578 |
525 | 579 |
526 void IndexBackend::GetAllPublicIds(std::list<std::string>& target, | 580 void IndexBackend::GetAllPublicIds(std::list<std::string>& target, |
527 DatabaseManager& manager, | 581 DatabaseManager& manager, |
528 OrthancPluginResourceType resourceType, | 582 OrthancPluginResourceType resourceType, |
529 uint64_t since, | 583 int64_t since, |
530 uint64_t limit) | 584 uint32_t limit) |
531 { | 585 { |
532 std::string suffix; | 586 std::string suffix; |
533 if (manager.GetDialect() == Dialect_MSSQL) | 587 if (manager.GetDialect() == Dialect_MSSQL) |
534 { | 588 { |
535 suffix = "OFFSET ${since} ROWS FETCH FIRST ${limit} ROWS ONLY"; | 589 suffix = "OFFSET ${since} ROWS FETCH FIRST ${limit} ROWS ONLY"; |
536 } | 590 } |
537 else | 591 else if (limit > 0) |
538 { | 592 { |
539 suffix = "LIMIT ${limit} OFFSET ${since}"; | 593 suffix = "LIMIT ${limit} OFFSET ${since}"; |
540 } | 594 } |
541 | 595 |
542 DatabaseManager::CachedStatement statement( | 596 std::string sql = "SELECT publicId FROM (SELECT publicId FROM Resources " |
543 STATEMENT_FROM_HERE, manager, | 597 "WHERE resourceType=${type}) AS tmp ORDER BY tmp.publicId " + suffix; |
544 "SELECT publicId FROM (SELECT publicId FROM Resources " | 598 |
545 "WHERE resourceType=${type}) AS tmp ORDER BY tmp.publicId " + suffix); | 599 DatabaseManager::CachedStatement statement(STATEMENT_FROM_HERE_DYNAMIC(sql), manager, sql); |
546 | 600 |
547 statement.SetReadOnly(true); | 601 statement.SetReadOnly(true); |
602 | |
603 Dictionary args; | |
604 | |
548 statement.SetParameterType("type", ValueType_Integer64); | 605 statement.SetParameterType("type", ValueType_Integer64); |
549 statement.SetParameterType("limit", ValueType_Integer64); | |
550 statement.SetParameterType("since", ValueType_Integer64); | |
551 | |
552 Dictionary args; | |
553 args.SetIntegerValue("type", static_cast<int>(resourceType)); | 606 args.SetIntegerValue("type", static_cast<int>(resourceType)); |
554 args.SetIntegerValue("limit", limit); | 607 |
555 args.SetIntegerValue("since", since); | 608 if (limit > 0) |
609 { | |
610 statement.SetParameterType("limit", ValueType_Integer64); | |
611 statement.SetParameterType("since", ValueType_Integer64); | |
612 args.SetIntegerValue("limit", limit); | |
613 args.SetIntegerValue("since", since); | |
614 } | |
556 | 615 |
557 ReadListOfStrings(target, statement, args); | 616 ReadListOfStrings(target, statement, args); |
558 } | 617 } |
559 | 618 |
560 | |
561 /* Use GetOutput().AnswerChange() */ | |
562 void IndexBackend::GetChanges(IDatabaseBackendOutput& output, | 619 void IndexBackend::GetChanges(IDatabaseBackendOutput& output, |
563 bool& done /*out*/, | 620 bool& done /*out*/, |
564 DatabaseManager& manager, | 621 DatabaseManager& manager, |
565 int64_t since, | 622 int64_t since, |
566 uint32_t maxResults) | 623 uint32_t limit) |
567 { | 624 { |
568 std::string suffix; | 625 std::set<uint32_t> changeTypes; |
626 GetChangesExtended(output, done, manager, since, -1, changeTypes, limit); | |
627 } | |
628 | |
629 /* Use GetOutput().AnswerChange() */ | |
630 void IndexBackend::GetChangesExtended(IDatabaseBackendOutput& output, | |
631 bool& done /*out*/, | |
632 DatabaseManager& manager, | |
633 int64_t since, | |
634 int64_t to, | |
635 const std::set<uint32_t>& changeTypes, | |
636 uint32_t limit) | |
637 { | |
638 std::string limitSuffix; | |
569 if (manager.GetDialect() == Dialect_MSSQL) | 639 if (manager.GetDialect() == Dialect_MSSQL) |
570 { | 640 { |
571 suffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; | 641 limitSuffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; |
572 } | 642 } |
573 else | 643 else |
574 { | 644 { |
575 suffix = "LIMIT ${limit}"; | 645 limitSuffix = "LIMIT ${limit}"; |
576 } | 646 } |
577 | 647 |
578 DatabaseManager::CachedStatement statement( | 648 std::vector<std::string> filters; |
579 STATEMENT_FROM_HERE, manager, | 649 bool hasSince = false; |
580 "SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, " | 650 bool hasTo = false; |
581 "Changes.date FROM Changes INNER JOIN Resources " | 651 |
582 "ON Changes.internalId = Resources.internalId WHERE seq>${since} ORDER BY seq " + suffix); | 652 if (since > 0) |
583 | 653 { |
654 hasSince = true; | |
655 filters.push_back("seq>${since}"); | |
656 } | |
657 if (to != -1) | |
658 { | |
659 hasTo = true; | |
660 filters.push_back("seq<=${to}"); | |
661 } | |
662 #if ORTHANC_PLUGINS_HAS_CHANGES_EXTENDED == 1 | |
663 if (changeTypes.size() > 0) | |
664 { | |
665 filters.push_back("changeType IN (" + JoinChanges(changeTypes) + ") "); | |
666 } | |
667 #endif | |
668 | |
669 std::string filtersString; | |
670 if (filters.size() > 0) | |
671 { | |
672 Orthanc::Toolbox::JoinStrings(filtersString, filters, " AND "); | |
673 filtersString = "WHERE " + filtersString; | |
674 } | |
675 | |
676 std::string sql; | |
677 bool returnFirstResults; | |
678 if (hasTo && !hasSince) | |
679 { | |
680 // in this case, we want the largest values but we want them ordered in ascending order | |
681 sql = "SELECT * FROM (SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, Changes.date " | |
682 "FROM Changes INNER JOIN Resources " | |
683 "ON Changes.internalId = Resources.internalId " + filtersString + " ORDER BY seq DESC " + limitSuffix + | |
684 ") AS FilteredChanges ORDER BY seq ASC"; | |
685 | |
686 returnFirstResults = false; | |
687 } | |
688 else | |
689 { | |
690 // default query: we want the smallest values ordered in ascending order | |
691 sql = "SELECT Changes.seq, Changes.changeType, Changes.resourceType, Resources.publicId, " | |
692 "Changes.date FROM Changes INNER JOIN Resources " | |
693 "ON Changes.internalId = Resources.internalId " + filtersString + " ORDER BY seq ASC " + limitSuffix; | |
694 returnFirstResults = true; | |
695 } | |
696 | |
697 DatabaseManager::CachedStatement statement(STATEMENT_FROM_HERE_DYNAMIC(sql), manager, sql); | |
584 statement.SetReadOnly(true); | 698 statement.SetReadOnly(true); |
699 Dictionary args; | |
700 | |
585 statement.SetParameterType("limit", ValueType_Integer64); | 701 statement.SetParameterType("limit", ValueType_Integer64); |
586 statement.SetParameterType("since", ValueType_Integer64); | 702 args.SetIntegerValue("limit", limit + 1); // we take limit+1 because we use the +1 to know if "Done" must be set to true |
587 | 703 |
588 Dictionary args; | 704 if (hasSince) |
589 args.SetIntegerValue("limit", maxResults + 1); | 705 { |
590 args.SetIntegerValue("since", since); | 706 statement.SetParameterType("since", ValueType_Integer64); |
591 | 707 args.SetIntegerValue("since", since); |
592 ReadChangesInternal(output, done, manager, statement, args, maxResults); | 708 } |
709 | |
710 if (hasTo) | |
711 { | |
712 statement.SetParameterType("to", ValueType_Integer64); | |
713 args.SetIntegerValue("to", to); | |
714 } | |
715 | |
716 ReadChangesInternal(output, done, manager, statement, args, limit, returnFirstResults); | |
593 } | 717 } |
594 | 718 |
595 | 719 |
596 void IndexBackend::GetChildrenInternalId(std::list<int64_t>& target /*out*/, | 720 void IndexBackend::GetChildrenInternalId(std::list<int64_t>& target /*out*/, |
597 DatabaseManager& manager, | 721 DatabaseManager& manager, |
634 /* Use GetOutput().AnswerExportedResource() */ | 758 /* Use GetOutput().AnswerExportedResource() */ |
635 void IndexBackend::GetExportedResources(IDatabaseBackendOutput& output, | 759 void IndexBackend::GetExportedResources(IDatabaseBackendOutput& output, |
636 bool& done /*out*/, | 760 bool& done /*out*/, |
637 DatabaseManager& manager, | 761 DatabaseManager& manager, |
638 int64_t since, | 762 int64_t since, |
639 uint32_t maxResults) | 763 uint32_t limit) |
640 { | 764 { |
641 std::string suffix; | 765 std::string suffix; |
642 if (manager.GetDialect() == Dialect_MSSQL) | 766 if (manager.GetDialect() == Dialect_MSSQL) |
643 { | 767 { |
644 suffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; | 768 suffix = "OFFSET 0 ROWS FETCH FIRST ${limit} ROWS ONLY"; |
655 statement.SetReadOnly(true); | 779 statement.SetReadOnly(true); |
656 statement.SetParameterType("limit", ValueType_Integer64); | 780 statement.SetParameterType("limit", ValueType_Integer64); |
657 statement.SetParameterType("since", ValueType_Integer64); | 781 statement.SetParameterType("since", ValueType_Integer64); |
658 | 782 |
659 Dictionary args; | 783 Dictionary args; |
660 args.SetIntegerValue("limit", maxResults + 1); | 784 args.SetIntegerValue("limit", limit + 1); |
661 args.SetIntegerValue("since", since); | 785 args.SetIntegerValue("since", since); |
662 | 786 |
663 ReadExportedResourcesInternal(output, done, statement, args, maxResults); | 787 ReadExportedResourcesInternal(output, done, statement, args, limit); |
664 } | 788 } |
665 | 789 |
666 | 790 |
667 /* Use GetOutput().AnswerChange() */ | 791 /* Use GetOutput().AnswerChange() */ |
668 void IndexBackend::GetLastChange(IDatabaseBackendOutput& output, | 792 void IndexBackend::GetLastChange(IDatabaseBackendOutput& output, |
687 statement.SetReadOnly(true); | 811 statement.SetReadOnly(true); |
688 | 812 |
689 Dictionary args; | 813 Dictionary args; |
690 | 814 |
691 bool done; // Ignored | 815 bool done; // Ignored |
692 ReadChangesInternal(output, done, manager, statement, args, 1); | 816 ReadChangesInternal(output, done, manager, statement, args, 1, true); |
693 } | 817 } |
694 | 818 |
695 | 819 |
696 /* Use GetOutput().AnswerExportedResource() */ | 820 /* Use GetOutput().AnswerExportedResource() */ |
697 void IndexBackend::GetLastExportedResource(IDatabaseBackendOutput& output, | 821 void IndexBackend::GetLastExportedResource(IDatabaseBackendOutput& output, |
762 | 886 |
763 statement.Execute(args); | 887 statement.Execute(args); |
764 | 888 |
765 if (statement.IsDone()) | 889 if (statement.IsDone()) |
766 { | 890 { |
767 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | 891 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource, "No public id found for internal id"); |
768 } | 892 } |
769 else | 893 else |
770 { | 894 { |
771 return statement.ReadString(0); | 895 return statement.ReadString(0); |
772 } | 896 } |
830 | 954 |
831 statement.Execute(args); | 955 statement.Execute(args); |
832 | 956 |
833 if (statement.IsDone()) | 957 if (statement.IsDone()) |
834 { | 958 { |
835 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | 959 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource, "No resource type found for internal id."); |
836 } | 960 } |
837 else | 961 else |
838 { | 962 { |
839 return static_cast<OrthancPluginResourceType>(statement.ReadInteger32(0)); | 963 return static_cast<OrthancPluginResourceType>(statement.ReadInteger32(0)); |
840 } | 964 } |
1015 statement.Execute(args); | 1139 statement.Execute(args); |
1016 } | 1140 } |
1017 | 1141 |
1018 | 1142 |
1019 void IndexBackend::LogExportedResource(DatabaseManager& manager, | 1143 void IndexBackend::LogExportedResource(DatabaseManager& manager, |
1020 const OrthancPluginExportedResource& resource) | 1144 OrthancPluginResourceType resourceType, |
1145 const char* publicId, | |
1146 const char* modality, | |
1147 const char* date, | |
1148 const char* patientId, | |
1149 const char* studyInstanceUid, | |
1150 const char* seriesInstanceUid, | |
1151 const char* sopInstanceUid) | |
1021 { | 1152 { |
1022 DatabaseManager::CachedStatement statement( | 1153 DatabaseManager::CachedStatement statement( |
1023 STATEMENT_FROM_HERE, manager, | 1154 STATEMENT_FROM_HERE, manager, |
1024 "INSERT INTO ExportedResources VALUES(${AUTOINCREMENT} ${type}, ${publicId}, " | 1155 "INSERT INTO ExportedResources VALUES(${AUTOINCREMENT} ${type}, ${publicId}, " |
1025 "${modality}, ${patient}, ${study}, ${series}, ${instance}, ${date})"); | 1156 "${modality}, ${patient}, ${study}, ${series}, ${instance}, ${date})"); |
1032 statement.SetParameterType("series", ValueType_Utf8String); | 1163 statement.SetParameterType("series", ValueType_Utf8String); |
1033 statement.SetParameterType("instance", ValueType_Utf8String); | 1164 statement.SetParameterType("instance", ValueType_Utf8String); |
1034 statement.SetParameterType("date", ValueType_Utf8String); | 1165 statement.SetParameterType("date", ValueType_Utf8String); |
1035 | 1166 |
1036 Dictionary args; | 1167 Dictionary args; |
1037 args.SetIntegerValue("type", resource.resourceType); | 1168 args.SetIntegerValue("type", resourceType); |
1038 args.SetUtf8Value("publicId", resource.publicId); | 1169 args.SetUtf8Value("publicId", publicId); |
1039 args.SetUtf8Value("modality", resource.modality); | 1170 args.SetUtf8Value("modality", modality); |
1040 args.SetUtf8Value("patient", resource.patientId); | 1171 args.SetUtf8Value("patient", patientId); |
1041 args.SetUtf8Value("study", resource.studyInstanceUid); | 1172 args.SetUtf8Value("study", studyInstanceUid); |
1042 args.SetUtf8Value("series", resource.seriesInstanceUid); | 1173 args.SetUtf8Value("series", seriesInstanceUid); |
1043 args.SetUtf8Value("instance", resource.sopInstanceUid); | 1174 args.SetUtf8Value("instance", sopInstanceUid); |
1044 args.SetUtf8Value("date", resource.date); | 1175 args.SetUtf8Value("date", date); |
1045 | 1176 |
1046 statement.Execute(args); | 1177 statement.Execute(args); |
1047 } | 1178 } |
1048 | 1179 |
1049 | 1180 |
1183 return ReadGlobalProperty(target, statement, args); | 1314 return ReadGlobalProperty(target, statement, args); |
1184 } | 1315 } |
1185 } | 1316 } |
1186 } | 1317 } |
1187 | 1318 |
1188 | 1319 bool IndexBackend::HasAtomicIncrementGlobalProperty() |
1320 { | |
1321 return false; // currently only implemented in Postgres | |
1322 } | |
1323 | |
1324 int64_t IndexBackend::IncrementGlobalProperty(DatabaseManager& manager, | |
1325 const char* serverIdentifier, | |
1326 int32_t property, | |
1327 int64_t increment) | |
1328 { | |
1329 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
1330 } | |
1331 | |
1332 bool IndexBackend::HasUpdateAndGetStatistics() | |
1333 { | |
1334 return false; // currently only implemented in Postgres | |
1335 } | |
1336 | |
1337 void IndexBackend::UpdateAndGetStatistics(DatabaseManager& manager, | |
1338 int64_t& patientsCount, | |
1339 int64_t& studiesCount, | |
1340 int64_t& seriesCount, | |
1341 int64_t& instancesCount, | |
1342 int64_t& compressedSize, | |
1343 int64_t& uncompressedSize) | |
1344 { | |
1345 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
1346 } | |
1347 | |
1348 bool IndexBackend::HasMeasureLatency() | |
1349 { | |
1350 return true; | |
1351 } | |
1352 | |
1353 | |
1189 void IndexBackend::LookupIdentifier(std::list<int64_t>& target /*out*/, | 1354 void IndexBackend::LookupIdentifier(std::list<int64_t>& target /*out*/, |
1190 DatabaseManager& manager, | 1355 DatabaseManager& manager, |
1191 OrthancPluginResourceType resourceType, | 1356 OrthancPluginResourceType resourceType, |
1192 uint16_t group, | 1357 uint16_t group, |
1193 uint16_t element, | 1358 uint16_t element, |
1961 ReadListOfStrings(childrenPublicIds, statement, args); | 2126 ReadListOfStrings(childrenPublicIds, statement, args); |
1962 } | 2127 } |
1963 | 2128 |
1964 | 2129 |
1965 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 | 2130 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 |
1966 class IndexBackend::LookupFormatter : public Orthanc::ISqlLookupFormatter | 2131 class IndexBackend::LookupFormatter : public ISqlLookupFormatter |
1967 { | 2132 { |
1968 private: | 2133 private: |
1969 Dialect dialect_; | 2134 Dialect dialect_; |
1970 size_t count_; | 2135 size_t count_; |
1971 Dictionary dictionary_; | 2136 Dictionary dictionary_; |
1992 return "${" + key + "}"; | 2157 return "${" + key + "}"; |
1993 } | 2158 } |
1994 | 2159 |
1995 virtual std::string FormatResourceType(Orthanc::ResourceType level) | 2160 virtual std::string FormatResourceType(Orthanc::ResourceType level) |
1996 { | 2161 { |
1997 return boost::lexical_cast<std::string>(Orthanc::Plugins::Convert(level)); | 2162 return boost::lexical_cast<std::string>(MessagesToolbox::ConvertToPlainC(level)); |
1998 } | 2163 } |
1999 | 2164 |
2000 virtual std::string FormatWildcardEscape() | 2165 virtual std::string FormatWildcardEscape() |
2001 { | 2166 { |
2002 switch (dialect_) | 2167 switch (dialect_) |
2012 default: | 2177 default: |
2013 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | 2178 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
2014 } | 2179 } |
2015 } | 2180 } |
2016 | 2181 |
2182 virtual std::string FormatLimits(uint64_t since, uint64_t count) | |
2183 { | |
2184 std::string sql; | |
2185 | |
2186 switch (dialect_) | |
2187 { | |
2188 case Dialect_MSSQL: | |
2189 { | |
2190 if (since > 0) | |
2191 { | |
2192 sql += " OFFSET " + boost::lexical_cast<std::string>(since) + " ROWS "; | |
2193 } | |
2194 if (count > 0) | |
2195 { | |
2196 sql += " FETCH NEXT " + boost::lexical_cast<std::string>(count) + " ROWS ONLY "; | |
2197 } | |
2198 }; break; | |
2199 case Dialect_SQLite: | |
2200 case Dialect_PostgreSQL: | |
2201 case Dialect_MySQL: | |
2202 { | |
2203 if (count > 0) | |
2204 { | |
2205 sql += " LIMIT " + boost::lexical_cast<std::string>(count); | |
2206 } | |
2207 if (since > 0) | |
2208 { | |
2209 sql += " OFFSET " + boost::lexical_cast<std::string>(since); | |
2210 } | |
2211 }; break; | |
2212 default: | |
2213 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
2214 } | |
2215 | |
2216 return sql; | |
2217 } | |
2218 | |
2017 virtual bool IsEscapeBrackets() const | 2219 virtual bool IsEscapeBrackets() const |
2018 { | 2220 { |
2019 // This was initially done at a bad location by the following changeset: | 2221 // This was initially done at a bad location by the following changeset: |
2020 // https://hg.orthanc-server.com/orthanc-databases/rev/389c037387ea | 2222 // https://orthanc.uclouvain.be/hg/orthanc-databases/rev/389c037387ea |
2021 return (dialect_ == Dialect_MSSQL); | 2223 return (dialect_ == Dialect_MSSQL); |
2022 } | 2224 } |
2023 | 2225 |
2024 void PrepareStatement(DatabaseManager::StandaloneStatement& statement) const | 2226 void PrepareStatement(DatabaseManager::StandaloneStatement& statement) const |
2025 { | 2227 { |
2041 | 2243 |
2042 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 | 2244 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 |
2043 // New primitive since Orthanc 1.5.2 | 2245 // New primitive since Orthanc 1.5.2 |
2044 void IndexBackend::LookupResources(IDatabaseBackendOutput& output, | 2246 void IndexBackend::LookupResources(IDatabaseBackendOutput& output, |
2045 DatabaseManager& manager, | 2247 DatabaseManager& manager, |
2046 const std::vector<Orthanc::DatabaseConstraint>& lookup, | 2248 const DatabaseConstraints& lookup, |
2047 OrthancPluginResourceType queryLevel, | 2249 OrthancPluginResourceType queryLevel_, |
2250 const std::set<std::string>& labels, | |
2251 LabelsConstraint labelsConstraint, | |
2048 uint32_t limit, | 2252 uint32_t limit, |
2049 bool requestSomeInstance) | 2253 bool requestSomeInstance) |
2050 { | 2254 { |
2051 LookupFormatter formatter(manager.GetDialect()); | 2255 LookupFormatter formatter(manager.GetDialect()); |
2256 Orthanc::ResourceType queryLevel = MessagesToolbox::Convert(queryLevel_); | |
2257 Orthanc::ResourceType lowerLevel, upperLevel; | |
2258 ISqlLookupFormatter::GetLookupLevels(lowerLevel, upperLevel, queryLevel, lookup); | |
2052 | 2259 |
2053 std::string sql; | 2260 std::string sql; |
2054 Orthanc::ISqlLookupFormatter::Apply(sql, formatter, lookup, | 2261 bool enableNewStudyCode = true; |
2055 Orthanc::Plugins::Convert(queryLevel), limit); | 2262 |
2056 | 2263 if (enableNewStudyCode && lowerLevel == queryLevel && upperLevel == queryLevel) |
2057 if (requestSomeInstance) | 2264 { |
2058 { | 2265 ISqlLookupFormatter::ApplySingleLevel(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit); |
2059 // Composite query to find some instance if requested | 2266 |
2060 switch (queryLevel) | 2267 if (requestSomeInstance) |
2061 { | 2268 { |
2062 case OrthancPluginResourceType_Patient: | 2269 // Composite query to find some instance if requested |
2063 sql = ("SELECT patients.publicId, MIN(instances.publicId) FROM (" + sql + ") patients " | 2270 switch (queryLevel) |
2064 "INNER JOIN Resources studies ON studies.parentId = patients.internalId " | 2271 { |
2065 "INNER JOIN Resources series ON series.parentId = studies.internalId " | 2272 case Orthanc::ResourceType_Patient: |
2066 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | 2273 sql = ("SELECT patients_studies.patients_public_id, MIN(instances.publicId) AS instances_public_id " |
2067 "GROUP BY patients.publicId"); | 2274 "FROM (SELECT patients.publicId AS patients_public_id, MIN(studies.internalId) AS studies_internal_id " |
2068 break; | 2275 "FROM (" + sql + |
2069 | 2276 ") AS patients " |
2070 case OrthancPluginResourceType_Study: | 2277 "INNER JOIN Resources studies ON studies.parentId = patients.internalId " |
2071 sql = ("SELECT studies.publicId, MIN(instances.publicId) FROM (" + sql + ") studies " | 2278 "GROUP BY patients.publicId " |
2072 "INNER JOIN Resources series ON series.parentId = studies.internalId " | 2279 ") AS patients_studies " |
2073 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | 2280 "INNER JOIN Resources series ON series.parentId = patients_studies.studies_internal_id " |
2074 "GROUP BY studies.publicId"); | 2281 "INNER JOIN Resources instances ON instances.parentId = series.internalId " |
2075 break; | 2282 "GROUP BY patients_studies.patients_public_id"); |
2076 | 2283 break; |
2077 case OrthancPluginResourceType_Series: | 2284 case Orthanc::ResourceType_Study: |
2078 sql = ("SELECT series.publicId, MIN(instances.publicId) FROM (" + sql + ") series " | 2285 sql = ("SELECT studies_series.studies_public_id, MIN(instances.publicId) AS instances_public_id " |
2079 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | 2286 "FROM (SELECT studies.publicId AS studies_public_id, MIN(series.internalId) AS series_internal_id " |
2080 "GROUP BY series.publicId"); | 2287 "FROM (" + sql + |
2081 break; | 2288 ") AS studies " |
2082 | 2289 "INNER JOIN Resources series ON series.parentId = studies.internalId " |
2083 case OrthancPluginResourceType_Instance: | 2290 "GROUP BY studies.publicId " |
2084 sql = ("SELECT instances.publicId, instances.publicId FROM (" + sql + ") instances"); | 2291 ") AS studies_series " |
2085 break; | 2292 "INNER JOIN Resources instances ON instances.parentId = studies_series.series_internal_id " |
2086 | 2293 "GROUP BY studies_series.studies_public_id"); |
2087 default: | 2294 break; |
2088 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 2295 case Orthanc::ResourceType_Series: |
2296 sql = ("SELECT series.publicId AS series_public_id, MIN(instances.publicId) AS instances_public_id " | |
2297 "FROM (" + sql + | |
2298 ") AS series " | |
2299 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | |
2300 "GROUP BY series.publicId "); | |
2301 break; | |
2302 | |
2303 case Orthanc::ResourceType_Instance: | |
2304 sql = ("SELECT instances.publicId, instances.publicId FROM (" + sql + ") instances"); | |
2305 break; | |
2306 | |
2307 default: | |
2308 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
2309 } | |
2310 } | |
2311 } | |
2312 else | |
2313 { | |
2314 ISqlLookupFormatter::Apply(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit); | |
2315 | |
2316 if (requestSomeInstance) | |
2317 { | |
2318 // Composite query to find some instance if requested | |
2319 switch (queryLevel) | |
2320 { | |
2321 case Orthanc::ResourceType_Patient: | |
2322 sql = ("SELECT patients.publicId, MIN(instances.publicId) FROM (" + sql + ") patients " | |
2323 "INNER JOIN Resources studies ON studies.parentId = patients.internalId " | |
2324 "INNER JOIN Resources series ON series.parentId = studies.internalId " | |
2325 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | |
2326 "GROUP BY patients.publicId"); | |
2327 break; | |
2328 | |
2329 case Orthanc::ResourceType_Study: | |
2330 sql = ("SELECT studies.publicId, MIN(instances.publicId) FROM (" + sql + ") studies " | |
2331 "INNER JOIN Resources series ON series.parentId = studies.internalId " | |
2332 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | |
2333 "GROUP BY studies.publicId"); | |
2334 break; | |
2335 case Orthanc::ResourceType_Series: | |
2336 sql = ("SELECT series.publicId, MIN(instances.publicId) FROM (" + sql + ") series " | |
2337 "INNER JOIN Resources instances ON instances.parentId = series.internalId " | |
2338 "GROUP BY series.publicId"); | |
2339 break; | |
2340 | |
2341 case Orthanc::ResourceType_Instance: | |
2342 sql = ("SELECT instances.publicId, instances.publicId FROM (" + sql + ") instances"); | |
2343 break; | |
2344 | |
2345 default: | |
2346 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
2347 } | |
2089 } | 2348 } |
2090 } | 2349 } |
2091 | 2350 |
2092 DatabaseManager::StandaloneStatement statement(manager, sql); | 2351 DatabaseManager::StandaloneStatement statement(manager, sql); |
2093 formatter.PrepareStatement(statement); | 2352 formatter.PrepareStatement(statement); |
2361 statement.Execute(args); | 2620 statement.Execute(args); |
2362 } | 2621 } |
2363 } | 2622 } |
2364 | 2623 |
2365 | 2624 |
2366 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 | 2625 // New primitive since Orthanc 1.5.4 |
2367 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) | |
2368 // New primitive since Orthanc 1.5.4 | |
2369 bool IndexBackend::LookupResourceAndParent(int64_t& id, | 2626 bool IndexBackend::LookupResourceAndParent(int64_t& id, |
2370 OrthancPluginResourceType& type, | 2627 OrthancPluginResourceType& type, |
2371 std::string& parentPublicId, | 2628 std::string& parentPublicId, |
2372 DatabaseManager& manager, | 2629 DatabaseManager& manager, |
2373 const char* publicId) | 2630 const char* publicId) |
2421 | 2678 |
2422 assert((statement.Next(), statement.IsDone())); | 2679 assert((statement.Next(), statement.IsDone())); |
2423 return true; | 2680 return true; |
2424 } | 2681 } |
2425 } | 2682 } |
2426 # endif | |
2427 #endif | |
2428 | 2683 |
2429 | 2684 |
2430 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 | |
2431 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) | |
2432 // New primitive since Orthanc 1.5.4 | 2685 // New primitive since Orthanc 1.5.4 |
2433 void IndexBackend::GetAllMetadata(std::map<int32_t, std::string>& result, | 2686 void IndexBackend::GetAllMetadata(std::map<int32_t, std::string>& result, |
2434 DatabaseManager& manager, | 2687 DatabaseManager& manager, |
2435 int64_t id) | 2688 int64_t id) |
2436 { | 2689 { |
2463 result[statement.ReadInteger32(0)] = statement.ReadString(1); | 2716 result[statement.ReadInteger32(0)] = statement.ReadString(1); |
2464 statement.Next(); | 2717 statement.Next(); |
2465 } | 2718 } |
2466 } | 2719 } |
2467 } | 2720 } |
2468 # endif | |
2469 #endif | |
2470 | 2721 |
2471 | 2722 |
2472 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 | 2723 #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 |
2473 void IndexBackend::CreateInstanceGeneric(OrthancPluginCreateInstanceResult& result, | 2724 void IndexBackend::CreateInstanceGeneric(OrthancPluginCreateInstanceResult& result, |
2474 DatabaseManager& manager, | 2725 DatabaseManager& manager, |
2587 assert(result.instanceId != -1); | 2838 assert(result.instanceId != -1); |
2588 } | 2839 } |
2589 #endif | 2840 #endif |
2590 | 2841 |
2591 | 2842 |
2843 void IndexBackend::AddLabel(DatabaseManager& manager, | |
2844 int64_t resource, | |
2845 const std::string& label) | |
2846 { | |
2847 std::unique_ptr<DatabaseManager::CachedStatement> statement; | |
2848 | |
2849 switch (manager.GetDialect()) | |
2850 { | |
2851 case Dialect_PostgreSQL: | |
2852 statement.reset(new DatabaseManager::CachedStatement( | |
2853 STATEMENT_FROM_HERE, manager, | |
2854 "INSERT INTO Labels VALUES(${id}, ${label}) ON CONFLICT DO NOTHING")); | |
2855 break; | |
2856 | |
2857 case Dialect_SQLite: | |
2858 statement.reset(new DatabaseManager::CachedStatement( | |
2859 STATEMENT_FROM_HERE, manager, | |
2860 "INSERT OR IGNORE INTO Labels VALUES(${id}, ${label})")); | |
2861 break; | |
2862 | |
2863 case Dialect_MySQL: | |
2864 statement.reset(new DatabaseManager::CachedStatement( | |
2865 STATEMENT_FROM_HERE, manager, | |
2866 "INSERT IGNORE INTO Labels VALUES(${id}, ${label})")); | |
2867 break; | |
2868 | |
2869 default: | |
2870 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
2871 } | |
2872 | |
2873 statement->SetParameterType("id", ValueType_Integer64); | |
2874 statement->SetParameterType("label", ValueType_Utf8String); | |
2875 | |
2876 Dictionary args; | |
2877 args.SetIntegerValue("id", resource); | |
2878 args.SetUtf8Value("label", label); | |
2879 | |
2880 statement->Execute(args); | |
2881 } | |
2882 | |
2883 | |
2884 void IndexBackend::RemoveLabel(DatabaseManager& manager, | |
2885 int64_t resource, | |
2886 const std::string& label) | |
2887 { | |
2888 DatabaseManager::CachedStatement statement( | |
2889 STATEMENT_FROM_HERE, manager, | |
2890 "DELETE FROM Labels WHERE id=${id} AND label=${label}"); | |
2891 | |
2892 statement.SetParameterType("id", ValueType_Integer64); | |
2893 statement.SetParameterType("label", ValueType_Utf8String); | |
2894 | |
2895 Dictionary args; | |
2896 args.SetIntegerValue("id", resource); | |
2897 args.SetUtf8Value("label", label); | |
2898 | |
2899 statement.Execute(args); | |
2900 } | |
2901 | |
2902 | |
2903 void IndexBackend::ListLabels(std::list<std::string>& target, | |
2904 DatabaseManager& manager, | |
2905 int64_t resource) | |
2906 { | |
2907 DatabaseManager::CachedStatement statement( | |
2908 STATEMENT_FROM_HERE, manager, | |
2909 "SELECT label FROM Labels WHERE id=${id}"); | |
2910 | |
2911 statement.SetReadOnly(true); | |
2912 statement.SetParameterType("id", ValueType_Integer64); | |
2913 | |
2914 Dictionary args; | |
2915 args.SetIntegerValue("id", resource); | |
2916 | |
2917 ReadListOfStrings(target, statement, args); | |
2918 } | |
2919 | |
2920 | |
2921 void IndexBackend::ListAllLabels(std::list<std::string>& target, | |
2922 DatabaseManager& manager) | |
2923 { | |
2924 DatabaseManager::CachedStatement statement( | |
2925 STATEMENT_FROM_HERE, manager, | |
2926 "SELECT DISTINCT label FROM Labels"); | |
2927 | |
2928 Dictionary args; | |
2929 ReadListOfStrings(target, statement, args); | |
2930 } | |
2931 | |
2932 | |
2592 void IndexBackend::Register(IndexBackend* backend, | 2933 void IndexBackend::Register(IndexBackend* backend, |
2593 size_t countConnections, | 2934 size_t countConnections, |
2594 unsigned int maxDatabaseRetries) | 2935 unsigned int maxDatabaseRetries) |
2595 { | 2936 { |
2596 if (backend == NULL) | 2937 if (backend == NULL) |
2597 { | 2938 { |
2598 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | 2939 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); |
2599 } | 2940 } |
2600 | 2941 |
2601 bool hasLoadedV3OrAbove = false; | 2942 LOG(WARNING) << "The index plugin will use " << countConnections << " connection(s) to the database, " |
2943 << "and will retry up to " << maxDatabaseRetries << " time(s) in the case of a collision"; | |
2602 | 2944 |
2603 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 | 2945 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 |
2604 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) | 2946 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) |
2605 if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 12, 0) == 1) | 2947 if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 12, 0) == 1) |
2606 { | 2948 { |
2607 LOG(WARNING) << "The index plugin will use " << countConnections << " connection(s) to the database, " | 2949 DatabaseBackendAdapterV4::Register(backend, countConnections, maxDatabaseRetries); |
2608 << "and will retry up to " << maxDatabaseRetries << " time(s) in the case of a collision"; | 2950 return; |
2609 | |
2610 OrthancDatabases::DatabaseBackendAdapterV4::Register(backend, countConnections, maxDatabaseRetries); | |
2611 hasLoadedV3OrAbove = true; | |
2612 } | |
2613 # elif ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) | |
2614 if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 9, 2) == 1) | |
2615 { | |
2616 LOG(WARNING) << "The index plugin will use " << countConnections << " connection(s) to the database, " | |
2617 << "and will retry up to " << maxDatabaseRetries << " time(s) in the case of a collision"; | |
2618 | |
2619 OrthancDatabases::DatabaseBackendAdapterV3::Register(backend, countConnections, maxDatabaseRetries); | |
2620 hasLoadedV3OrAbove = true; | |
2621 } | 2951 } |
2622 # endif | 2952 # endif |
2623 #endif | 2953 #endif |
2624 | 2954 |
2625 if (!hasLoadedV3OrAbove) | 2955 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 |
2626 { | 2956 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) |
2627 LOG(WARNING) << "Performance warning: Your version of the Orthanc core or SDK doesn't support multiple readers/writers"; | 2957 if (OrthancPluginCheckVersionAdvanced(backend->GetContext(), 1, 9, 2) == 1) |
2628 OrthancDatabases::DatabaseBackendAdapterV2::Register(backend); | 2958 { |
2629 } | 2959 DatabaseBackendAdapterV3::Register(backend, countConnections, maxDatabaseRetries); |
2960 return; | |
2961 } | |
2962 # endif | |
2963 #endif | |
2964 | |
2965 LOG(WARNING) << "Performance warning: Your version of the Orthanc core or SDK doesn't support multiple readers/writers"; | |
2966 DatabaseBackendAdapterV2::Register(backend); | |
2630 } | 2967 } |
2631 | 2968 |
2632 | 2969 |
2633 bool IndexBackend::LookupGlobalIntegerProperty(int& target, | 2970 bool IndexBackend::LookupGlobalIntegerProperty(int& target, |
2634 DatabaseManager& manager, | 2971 DatabaseManager& manager, |
2644 target = boost::lexical_cast<int>(value); | 2981 target = boost::lexical_cast<int>(value); |
2645 return true; | 2982 return true; |
2646 } | 2983 } |
2647 catch (boost::bad_lexical_cast&) | 2984 catch (boost::bad_lexical_cast&) |
2648 { | 2985 { |
2649 LOG(ERROR) << "Corrupted PostgreSQL database"; | 2986 LOG(ERROR) << "Corrupted database"; |
2650 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); | 2987 throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); |
2651 } | 2988 } |
2652 } | 2989 } |
2653 else | 2990 else |
2654 { | 2991 { |
2667 } | 3004 } |
2668 | 3005 |
2669 | 3006 |
2670 void IndexBackend::Finalize() | 3007 void IndexBackend::Finalize() |
2671 { | 3008 { |
2672 OrthancDatabases::DatabaseBackendAdapterV2::Finalize(); | 3009 DatabaseBackendAdapterV2::Finalize(); |
2673 | 3010 |
2674 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 | 3011 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 |
2675 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) | 3012 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) |
2676 OrthancDatabases::DatabaseBackendAdapterV3::Finalize(); | 3013 DatabaseBackendAdapterV3::Finalize(); |
2677 # endif | 3014 # endif |
2678 #endif | 3015 #endif |
2679 } | 3016 |
2680 | 3017 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 |
2681 | 3018 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 0) |
2682 DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend) | 3019 DatabaseBackendAdapterV4::Finalize(); |
3020 # endif | |
3021 #endif | |
3022 } | |
3023 | |
3024 | |
3025 uint64_t IndexBackend::MeasureLatency(DatabaseManager& manager) | |
3026 { | |
3027 // execute 11x the simplest statement and return the median value | |
3028 std::vector<uint64_t> measures; | |
3029 | |
3030 for (int i = 0; i < 11; i++) | |
3031 { | |
3032 DatabaseManager::StandaloneStatement statement(manager, "SELECT 1"); | |
3033 | |
3034 Orthanc::Toolbox::ElapsedTimer timer; | |
3035 | |
3036 statement.ExecuteWithoutResult(); | |
3037 | |
3038 measures.push_back(timer.GetElapsedMicroseconds()); | |
3039 } | |
3040 | |
3041 std::sort(measures.begin(), measures.end()); | |
3042 | |
3043 return measures[measures.size() / 2]; | |
3044 } | |
3045 | |
3046 | |
3047 DatabaseManager* IndexBackend::CreateSingleDatabaseManager(IDatabaseBackend& backend, | |
3048 bool hasIdentifierTags, | |
3049 const std::list<IdentifierTag>& identifierTags) | |
2683 { | 3050 { |
2684 std::unique_ptr<DatabaseManager> manager(new DatabaseManager(backend.CreateDatabaseFactory())); | 3051 std::unique_ptr<DatabaseManager> manager(new DatabaseManager(backend.CreateDatabaseFactory())); |
2685 backend.ConfigureDatabase(*manager); | 3052 backend.ConfigureDatabase(*manager, hasIdentifierTags, identifierTags); |
2686 return manager.release(); | 3053 return manager.release(); |
2687 } | 3054 } |
3055 | |
3056 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 5) | |
3057 bool IndexBackend::HasFindSupport() const | |
3058 { | |
3059 // TODO-FIND move to child plugins ? | |
3060 return true; | |
3061 } | |
3062 #endif | |
3063 | |
3064 | |
3065 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 5) | |
3066 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* GetResourceContent( | |
3067 Orthanc::DatabasePluginMessages::Find_Response* response, | |
3068 Orthanc::DatabasePluginMessages::ResourceType level) | |
3069 { | |
3070 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = NULL; // the protobuf response will be the owner | |
3071 | |
3072 switch (level) | |
3073 { | |
3074 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT: | |
3075 content = response->mutable_patient_content(); | |
3076 break; | |
3077 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3078 content = response->mutable_study_content(); | |
3079 break; | |
3080 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3081 content =response->mutable_series_content(); | |
3082 break; | |
3083 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_INSTANCE: | |
3084 content = response->mutable_instance_content(); | |
3085 break; | |
3086 default: | |
3087 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
3088 } | |
3089 return content; | |
3090 } | |
3091 | |
3092 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* GetChildrenContent( | |
3093 Orthanc::DatabasePluginMessages::Find_Response* response, | |
3094 Orthanc::DatabasePluginMessages::ResourceType childrenLevel) | |
3095 { | |
3096 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = NULL; // the protobuf response will be the owner | |
3097 | |
3098 switch (childrenLevel) | |
3099 { | |
3100 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3101 content = response->mutable_children_studies_content(); | |
3102 break; | |
3103 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3104 content =response->mutable_children_series_content(); | |
3105 break; | |
3106 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_INSTANCE: | |
3107 content = response->mutable_children_instances_content(); | |
3108 break; | |
3109 default: | |
3110 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
3111 } | |
3112 return content; | |
3113 } | |
3114 | |
3115 std::string JoinRequestedMetadata(const Orthanc::DatabasePluginMessages::Find_Request_ChildrenSpecification* childrenSpec) | |
3116 { | |
3117 std::set<std::string> metadataTypes; | |
3118 for (int i = 0; i < childrenSpec->retrieve_metadata_size(); ++i) | |
3119 { | |
3120 metadataTypes.insert(boost::lexical_cast<std::string>(childrenSpec->retrieve_metadata(i))); | |
3121 } | |
3122 std::string joinedMetadataTypes; | |
3123 Orthanc::Toolbox::JoinStrings(joinedMetadataTypes, metadataTypes, ", "); | |
3124 | |
3125 return joinedMetadataTypes; | |
3126 } | |
3127 | |
3128 std::string JoinRequestedTags(const Orthanc::DatabasePluginMessages::Find_Request_ChildrenSpecification* childrenSpec) | |
3129 { | |
3130 std::set<std::string> tags; | |
3131 for (int i = 0; i < childrenSpec->retrieve_main_dicom_tags_size(); ++i) | |
3132 { | |
3133 tags.insert("(" + boost::lexical_cast<std::string>(childrenSpec->retrieve_main_dicom_tags(i).group()) | |
3134 + ", " + boost::lexical_cast<std::string>(childrenSpec->retrieve_main_dicom_tags(i).element()) + ")"); | |
3135 } | |
3136 std::string joinedTags; | |
3137 Orthanc::Toolbox::JoinStrings(joinedTags, tags, ", "); | |
3138 | |
3139 return joinedTags; | |
3140 } | |
3141 | |
3142 | |
3143 #define C0_QUERY_ID 0 | |
3144 #define C1_INTERNAL_ID 1 | |
3145 #define C2_ROW_NUMBER 2 | |
3146 #define C3_STRING_1 3 | |
3147 #define C4_STRING_2 4 | |
3148 #define C5_STRING_3 5 | |
3149 #define C6_INT_1 6 | |
3150 #define C7_INT_2 7 | |
3151 #define C8_BIG_INT_1 8 | |
3152 #define C9_BIG_INT_2 9 | |
3153 | |
3154 #define QUERY_LOOKUP 1 | |
3155 #define QUERY_MAIN_DICOM_TAGS 2 | |
3156 #define QUERY_ATTACHMENTS 3 | |
3157 #define QUERY_METADATA 4 | |
3158 #define QUERY_LABELS 5 | |
3159 #define QUERY_PARENT_MAIN_DICOM_TAGS 10 | |
3160 #define QUERY_PARENT_IDENTIFIER 11 | |
3161 #define QUERY_PARENT_METADATA 12 | |
3162 #define QUERY_GRAND_PARENT_MAIN_DICOM_TAGS 15 | |
3163 #define QUERY_GRAND_PARENT_METADATA 16 | |
3164 #define QUERY_CHILDREN_IDENTIFIERS 20 | |
3165 #define QUERY_CHILDREN_MAIN_DICOM_TAGS 21 | |
3166 #define QUERY_CHILDREN_METADATA 22 | |
3167 #define QUERY_GRAND_CHILDREN_IDENTIFIERS 30 | |
3168 #define QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS 31 | |
3169 #define QUERY_GRAND_CHILDREN_METADATA 32 | |
3170 #define QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS 40 | |
3171 #define QUERY_ONE_INSTANCE_IDENTIFIER 50 | |
3172 #define QUERY_ONE_INSTANCE_METADATA 51 | |
3173 #define QUERY_ONE_INSTANCE_ATTACHMENTS 52 | |
3174 | |
3175 #define STRINGIFY(x) #x | |
3176 #define TOSTRING(x) STRINGIFY(x) | |
3177 | |
3178 void IndexBackend::ExecuteFind(Orthanc::DatabasePluginMessages::TransactionResponse& response, | |
3179 DatabaseManager& manager, | |
3180 const Orthanc::DatabasePluginMessages::Find_Request& request) | |
3181 { | |
3182 // TODO-FIND move to child plugins ? | |
3183 | |
3184 | |
3185 // If we want the Find to use a read-only transaction, we can not create temporary tables with | |
3186 // the lookup results. So we must use a CTE (Common Table Expression). | |
3187 // However, a CTE can only be used in a single query -> we must unionize all the following | |
3188 // queries to retrieve values from various tables. | |
3189 // However, to use UNION, all tables must have the same columns (numbers and types). That's | |
3190 // why we have generic column names. | |
3191 // So, at the end we'll have only one very big query ! | |
3192 | |
3193 std::string sql; | |
3194 | |
3195 // extract the resource id of interest by executing the lookup in a CTE | |
3196 LookupFormatter formatter(manager.GetDialect()); | |
3197 std::string lookupSql; | |
3198 ISqlLookupFormatter::Apply(lookupSql, formatter, request); | |
3199 | |
3200 // base query, retrieve the ordered internalId and publicId of the selected resources | |
3201 sql = "WITH Lookup AS (" + lookupSql + ") " | |
3202 "SELECT " | |
3203 " " TOSTRING(QUERY_LOOKUP) " AS c0_queryId, " | |
3204 " Lookup.internalId AS c1_internalId, " | |
3205 " Lookup.rowNumber AS c2_rowNumber, " | |
3206 " Lookup.publicId AS c3_string1, " | |
3207 " NULL::TEXT AS c4_string2, " | |
3208 " NULL::TEXT AS c5_string3, " | |
3209 " NULL::INT AS c6_int1, " | |
3210 " NULL::INT AS c7_int2, " | |
3211 " NULL::BIGINT AS c8_big_int1, " | |
3212 " NULL::BIGINT AS c9_big_int2 " | |
3213 " FROM Lookup "; | |
3214 | |
3215 // need MainDicomTags from resource ? | |
3216 if (request.retrieve_main_dicom_tags()) | |
3217 { | |
3218 sql += "UNION SELECT " | |
3219 " " TOSTRING(QUERY_MAIN_DICOM_TAGS) " AS c0_queryId, " | |
3220 " Lookup.internalId AS c1_internalId, " | |
3221 " NULL::BIGINT AS c2_rowNumber, " | |
3222 " value AS c3_string1, " | |
3223 " NULL::TEXT AS c4_string2, " | |
3224 " NULL::TEXT AS c5_string3, " | |
3225 " tagGroup AS c6_int1, " | |
3226 " tagElement AS c7_int2, " | |
3227 " NULL::BIGINT AS c8_big_int1, " | |
3228 " NULL::BIGINT AS c9_big_int2 " | |
3229 "FROM Lookup " | |
3230 "INNER JOIN MainDicomTags ON MainDicomTags.id = Lookup.internalId "; | |
3231 } | |
3232 | |
3233 // need resource metadata ? | |
3234 if (request.retrieve_metadata()) | |
3235 { | |
3236 sql += "UNION SELECT " | |
3237 " " TOSTRING(QUERY_METADATA) " AS c0_queryId, " | |
3238 " Lookup.internalId AS c1_internalId, " | |
3239 " NULL::BIGINT AS c2_rowNumber, " | |
3240 " value AS c3_string1, " | |
3241 " NULL::TEXT AS c4_string2, " | |
3242 " NULL::TEXT AS c5_string3, " | |
3243 " type AS c6_int1, " | |
3244 " NULL::INT AS c7_int2, " | |
3245 " NULL::BIGINT AS c8_big_int1, " | |
3246 " NULL::BIGINT AS c9_big_int2 " | |
3247 "FROM Lookup " | |
3248 "INNER JOIN Metadata ON Metadata.id = Lookup.internalId "; | |
3249 } | |
3250 | |
3251 // need resource attachments ? | |
3252 if (request.retrieve_attachments()) | |
3253 { | |
3254 sql += "UNION SELECT " | |
3255 " " TOSTRING(QUERY_ATTACHMENTS) " AS c0_queryId, " | |
3256 " Lookup.internalId AS c1_internalId, " | |
3257 " NULL::BIGINT AS c2_rowNumber, " | |
3258 " uuid AS c3_string1, " | |
3259 " uncompressedHash AS c4_string2, " | |
3260 " compressedHash AS c5_string3, " | |
3261 " fileType AS c6_int1, " | |
3262 " compressionType AS c7_int2, " | |
3263 " compressedSize AS c8_big_int1, " | |
3264 " uncompressedSize AS c9_big_int2 " | |
3265 "FROM Lookup " | |
3266 "INNER JOIN AttachedFiles ON AttachedFiles.id = Lookup.internalId "; | |
3267 } | |
3268 | |
3269 // need resource labels ? | |
3270 if (request.retrieve_labels()) | |
3271 { | |
3272 sql += "UNION SELECT " | |
3273 " " TOSTRING(QUERY_LABELS) " AS c0_queryId, " | |
3274 " Lookup.internalId AS c1_internalId, " | |
3275 " NULL::BIGINT AS c2_rowNumber, " | |
3276 " label AS c3_string1, " | |
3277 " NULL::TEXT AS c4_string2, " | |
3278 " NULL::TEXT AS c5_string3, " | |
3279 " NULL::INT AS c6_int1, " | |
3280 " NULL::INT AS c7_int2, " | |
3281 " NULL::BIGINT AS c8_big_int1, " | |
3282 " NULL::BIGINT AS c9_big_int2 " | |
3283 "FROM Lookup " | |
3284 "INNER JOIN Labels ON Labels.id = Lookup.internalId "; | |
3285 } | |
3286 | |
3287 // need MainDicomTags from parent ? | |
3288 if (request.level() > Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT) | |
3289 { | |
3290 const Orthanc::DatabasePluginMessages::Find_Request_ParentSpecification* parentSpec = NULL; | |
3291 switch (request.level()) | |
3292 { | |
3293 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3294 parentSpec = &(request.parent_patient()); | |
3295 break; | |
3296 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3297 parentSpec = &(request.parent_study()); | |
3298 break; | |
3299 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_INSTANCE: | |
3300 parentSpec = &(request.parent_series()); | |
3301 break; | |
3302 | |
3303 default: | |
3304 break; | |
3305 } | |
3306 | |
3307 if (parentSpec->retrieve_main_dicom_tags()) | |
3308 { | |
3309 sql += "UNION SELECT " | |
3310 " " TOSTRING(QUERY_PARENT_MAIN_DICOM_TAGS) " AS c0_queryId, " | |
3311 " Lookup.internalId AS c1_internalId, " | |
3312 " NULL::BIGINT AS c2_rowNumber, " | |
3313 " value AS c3_string1, " | |
3314 " NULL::TEXT AS c4_string2, " | |
3315 " NULL::TEXT AS c5_string3, " | |
3316 " tagGroup AS c6_int1, " | |
3317 " tagElement AS c7_int2, " | |
3318 " NULL::BIGINT AS c8_big_int1, " | |
3319 " NULL::BIGINT AS c9_big_int2 " | |
3320 "FROM Lookup " | |
3321 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " | |
3322 "INNER JOIN MainDicomTags ON MainDicomTags.id = currentLevel.parentId "; | |
3323 } | |
3324 | |
3325 if (parentSpec->retrieve_metadata()) | |
3326 { | |
3327 sql += "UNION SELECT " | |
3328 " " TOSTRING(QUERY_PARENT_METADATA) " AS c0_queryId, " | |
3329 " Lookup.internalId AS c1_internalId, " | |
3330 " NULL::BIGINT AS c2_rowNumber, " | |
3331 " value AS c3_string1, " | |
3332 " NULL::TEXT AS c4_string2, " | |
3333 " NULL::TEXT AS c5_string3, " | |
3334 " type AS c6_int1, " | |
3335 " NULL::INT AS c7_int2, " | |
3336 " NULL::BIGINT AS c8_big_int1, " | |
3337 " NULL::BIGINT AS c9_big_int2 " | |
3338 "FROM Lookup " | |
3339 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " | |
3340 "INNER JOIN Metadata ON Metadata.id = currentLevel.parentId "; | |
3341 } | |
3342 | |
3343 // need MainDicomTags from grandparent ? | |
3344 if (request.level() > Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY) | |
3345 { | |
3346 const Orthanc::DatabasePluginMessages::Find_Request_ParentSpecification* grandparentSpec = NULL; | |
3347 switch (request.level()) | |
3348 { | |
3349 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3350 grandparentSpec = &(request.parent_patient()); | |
3351 break; | |
3352 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_INSTANCE: | |
3353 grandparentSpec = &(request.parent_study()); | |
3354 break; | |
3355 | |
3356 default: | |
3357 break; | |
3358 } | |
3359 | |
3360 if (grandparentSpec->retrieve_main_dicom_tags()) | |
3361 { | |
3362 sql += "UNION SELECT " | |
3363 " " TOSTRING(QUERY_GRAND_PARENT_MAIN_DICOM_TAGS) " AS c0_queryId, " | |
3364 " Lookup.internalId AS c1_internalId, " | |
3365 " NULL::BIGINT AS c2_rowNumber, " | |
3366 " value AS c3_string1, " | |
3367 " NULL::TEXT AS c4_string2, " | |
3368 " NULL::TEXT AS c5_string3, " | |
3369 " tagGroup AS c6_int1, " | |
3370 " tagElement AS c7_int2, " | |
3371 " NULL::BIGINT AS c8_big_int1, " | |
3372 " NULL::BIGINT AS c9_big_int2 " | |
3373 "FROM Lookup " | |
3374 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " | |
3375 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId " | |
3376 "INNER JOIN MainDicomTags ON MainDicomTags.id = parentLevel.parentId "; | |
3377 } | |
3378 | |
3379 if (grandparentSpec->retrieve_metadata()) | |
3380 { | |
3381 sql += "UNION SELECT " | |
3382 " " TOSTRING(QUERY_GRAND_PARENT_METADATA) " AS c0_queryId, " | |
3383 " Lookup.internalId AS c1_internalId, " | |
3384 " NULL::BIGINT AS c2_rowNumber, " | |
3385 " value AS c3_string1, " | |
3386 " NULL::TEXT AS c4_string2, " | |
3387 " NULL::TEXT AS c5_string3, " | |
3388 " type AS c6_int1, " | |
3389 " NULL::INT AS c7_int2, " | |
3390 " NULL::BIGINT AS c8_big_int1, " | |
3391 " NULL::BIGINT AS c9_big_int2 " | |
3392 "FROM Lookup " | |
3393 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " | |
3394 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId " | |
3395 "INNER JOIN Metadata ON Metadata.id = parentLevel.parentId "; | |
3396 } | |
3397 } | |
3398 } | |
3399 | |
3400 // need MainDicomTags from children ? | |
3401 if (request.level() <= Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES) | |
3402 { | |
3403 const Orthanc::DatabasePluginMessages::Find_Request_ChildrenSpecification* childrenSpec = NULL; | |
3404 switch (request.level()) | |
3405 { | |
3406 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT: | |
3407 childrenSpec = &(request.children_studies()); | |
3408 break; | |
3409 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3410 childrenSpec = &(request.children_series()); | |
3411 break; | |
3412 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3413 childrenSpec = &(request.children_instances()); | |
3414 break; | |
3415 | |
3416 default: | |
3417 break; | |
3418 } | |
3419 | |
3420 if (childrenSpec->retrieve_main_dicom_tags_size() > 0) | |
3421 { | |
3422 sql += "UNION SELECT " | |
3423 " " TOSTRING(QUERY_CHILDREN_MAIN_DICOM_TAGS) " AS c0_queryId, " | |
3424 " Lookup.internalId AS c1_internalId, " | |
3425 " NULL::BIGINT AS c2_rowNumber, " | |
3426 " value AS c3_string1, " | |
3427 " NULL::TEXT AS c4_string2, " | |
3428 " NULL::TEXT AS c5_string3, " | |
3429 " tagGroup AS c6_int1, " | |
3430 " tagElement AS c7_int2, " | |
3431 " NULL::BIGINT AS c8_big_int1, " | |
3432 " NULL::BIGINT AS c9_big_int2 " | |
3433 "FROM Lookup " | |
3434 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " | |
3435 " INNER JOIN MainDicomTags ON MainDicomTags.id = childLevel.internalId AND (tagGroup, tagElement) IN (" + JoinRequestedTags(childrenSpec) + ")"; | |
3436 } | |
3437 | |
3438 // need children identifiers ? | |
3439 if (childrenSpec->retrieve_identifiers()) | |
3440 { | |
3441 sql += "UNION SELECT " | |
3442 " " TOSTRING(QUERY_CHILDREN_IDENTIFIERS) " AS c0_queryId, " | |
3443 " Lookup.internalId AS c1_internalId, " | |
3444 " NULL::BIGINT AS c2_rowNumber, " | |
3445 " childLevel.publicId AS c3_string1, " | |
3446 " NULL::TEXT AS c4_string2, " | |
3447 " NULL::TEXT AS c5_string3, " | |
3448 " NULL::INT AS c6_int1, " | |
3449 " NULL::INT AS c7_int2, " | |
3450 " NULL::BIGINT AS c8_big_int1, " | |
3451 " NULL::BIGINT AS c9_big_int2 " | |
3452 "FROM Lookup " | |
3453 " INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId "; | |
3454 } | |
3455 | |
3456 if (childrenSpec->retrieve_metadata_size() > 0) | |
3457 { | |
3458 sql += "UNION SELECT " | |
3459 " " TOSTRING(QUERY_CHILDREN_METADATA) " AS c0_queryId, " | |
3460 " Lookup.internalId AS c1_internalId, " | |
3461 " NULL::BIGINT AS c2_rowNumber, " | |
3462 " value AS c3_string1, " | |
3463 " NULL::TEXT AS c4_string2, " | |
3464 " NULL::TEXT AS c5_string3, " | |
3465 " type AS c6_int1, " | |
3466 " NULL::INT AS c7_int2, " | |
3467 " NULL::BIGINT AS c8_big_int1, " | |
3468 " NULL::BIGINT AS c9_big_int2 " | |
3469 "FROM Lookup " | |
3470 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " | |
3471 " INNER JOIN Metadata ON Metadata.id = childLevel.internalId AND Metadata.type IN (" + JoinRequestedMetadata(childrenSpec) + ") "; | |
3472 } | |
3473 | |
3474 if (request.level() <= Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY) | |
3475 { | |
3476 const Orthanc::DatabasePluginMessages::Find_Request_ChildrenSpecification* grandchildrenSpec = NULL; | |
3477 switch (request.level()) | |
3478 { | |
3479 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT: | |
3480 grandchildrenSpec = &(request.children_series()); | |
3481 break; | |
3482 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3483 grandchildrenSpec = &(request.children_instances()); | |
3484 break; | |
3485 | |
3486 default: | |
3487 break; | |
3488 } | |
3489 | |
3490 // need grand children identifiers ? | |
3491 if (grandchildrenSpec->retrieve_identifiers()) | |
3492 { | |
3493 sql += "UNION SELECT " | |
3494 " " TOSTRING(QUERY_GRAND_CHILDREN_IDENTIFIERS) " AS c0_queryId, " | |
3495 " Lookup.internalId AS c1_internalId, " | |
3496 " NULL::BIGINT AS c2_rowNumber, " | |
3497 " grandChildLevel.publicId AS c3_string1, " | |
3498 " NULL::TEXT AS c4_string2, " | |
3499 " NULL::TEXT AS c5_string3, " | |
3500 " NULL::INT AS c6_int1, " | |
3501 " NULL::INT AS c7_int2, " | |
3502 " NULL::BIGINT AS c8_big_int1, " | |
3503 " NULL::BIGINT AS c9_big_int2 " | |
3504 "FROM Lookup " | |
3505 "INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " | |
3506 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId "; | |
3507 } | |
3508 | |
3509 if (grandchildrenSpec->retrieve_main_dicom_tags_size() > 0) | |
3510 { | |
3511 sql += "UNION SELECT " | |
3512 " " TOSTRING(QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS) " AS c0_queryId, " | |
3513 " Lookup.internalId AS c1_internalId, " | |
3514 " NULL::BIGINT AS c2_rowNumber, " | |
3515 " value AS c3_string1, " | |
3516 " NULL::TEXT AS c4_string2, " | |
3517 " NULL::TEXT AS c5_string3, " | |
3518 " tagGroup AS c6_int1, " | |
3519 " tagElement AS c7_int2, " | |
3520 " NULL::BIGINT AS c8_big_int1, " | |
3521 " NULL::BIGINT AS c9_big_int2 " | |
3522 "FROM Lookup " | |
3523 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " | |
3524 " INNER JOIN Resources grandChildLevel ON grandChildLevel.parentId = childLevel.internalId " | |
3525 " INNER JOIN MainDicomTags ON MainDicomTags.id = grandChildLevel.internalId AND (tagGroup, tagElement) IN (" + JoinRequestedTags(grandchildrenSpec) + ")"; | |
3526 } | |
3527 | |
3528 if (grandchildrenSpec->retrieve_metadata_size() > 0) | |
3529 { | |
3530 sql += "UNION SELECT " | |
3531 " " TOSTRING(QUERY_GRAND_CHILDREN_METADATA) " AS c0_queryId, " | |
3532 " Lookup.internalId AS c1_internalId, " | |
3533 " NULL::BIGINT AS c2_rowNumber, " | |
3534 " value AS c3_string1, " | |
3535 " NULL::TEXT AS c4_string2, " | |
3536 " NULL::TEXT AS c5_string3, " | |
3537 " type AS c6_int1, " | |
3538 " NULL::INT AS c7_int2, " | |
3539 " NULL::BIGINT AS c8_big_int1, " | |
3540 " NULL::BIGINT AS c9_big_int2 " | |
3541 "FROM Lookup " | |
3542 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " | |
3543 " INNER JOIN Resources grandChildLevel ON grandChildLevel.parentId = childLevel.internalId " | |
3544 " INNER JOIN Metadata ON Metadata.id = grandChildLevel.internalId AND Metadata.type IN (" + JoinRequestedMetadata(grandchildrenSpec) + ") "; | |
3545 } | |
3546 | |
3547 if (request.level() == Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT) | |
3548 { | |
3549 const Orthanc::DatabasePluginMessages::Find_Request_ChildrenSpecification* grandgrandchildrenSpec = &(request.children_instances()); | |
3550 | |
3551 // need grand children identifiers ? | |
3552 if (grandgrandchildrenSpec->retrieve_identifiers()) | |
3553 { | |
3554 sql += "UNION SELECT " | |
3555 " " TOSTRING(QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS) " AS c0_queryId, " | |
3556 " Lookup.internalId AS c1_internalId, " | |
3557 " NULL::BIGINT AS c2_rowNumber, " | |
3558 " grandGrandChildLevel.publicId AS c3_string1, " | |
3559 " NULL::TEXT AS c4_string2, " | |
3560 " NULL::TEXT AS c5_string3, " | |
3561 " NULL::INT AS c6_int1, " | |
3562 " NULL::INT AS c7_int2, " | |
3563 " NULL::BIGINT AS c8_big_int1, " | |
3564 " NULL::BIGINT AS c9_big_int2 " | |
3565 "FROM Lookup " | |
3566 "INNER JOIN Resources childLevel ON Lookup.internalId = childLevel.parentId " | |
3567 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId " | |
3568 "INNER JOIN Resources grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId "; | |
3569 } | |
3570 } | |
3571 } | |
3572 } | |
3573 | |
3574 // need parent identifier ? | |
3575 if (request.retrieve_parent_identifier()) | |
3576 { | |
3577 sql += "UNION SELECT " | |
3578 " " TOSTRING(QUERY_PARENT_IDENTIFIER) " AS c0_queryId, " | |
3579 " Lookup.internalId AS c1_internalId, " | |
3580 " NULL::BIGINT AS c2_rowNumber, " | |
3581 " parentLevel.publicId AS c3_string1, " | |
3582 " NULL::TEXT AS c4_string2, " | |
3583 " NULL::TEXT AS c5_string3, " | |
3584 " NULL::INT AS c6_int1, " | |
3585 " NULL::INT AS c7_int2, " | |
3586 " NULL::BIGINT AS c8_big_int1, " | |
3587 " NULL::BIGINT AS c9_big_int2 " | |
3588 "FROM Lookup " | |
3589 " INNER JOIN Resources currentLevel ON currentLevel.internalId = Lookup.internalId " | |
3590 " INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "; | |
3591 } | |
3592 | |
3593 // need one instance info ? | |
3594 if (request.level() != Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_INSTANCE && | |
3595 request.retrieve_one_instance_metadata_and_attachments()) | |
3596 { | |
3597 // Here, we create a nested CTE 'OneInstance' with one instance ID to join with metadata and main | |
3598 sql += "UNION" | |
3599 " (WITH OneInstance AS"; | |
3600 | |
3601 switch (request.level()) | |
3602 { | |
3603 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_SERIES: | |
3604 { | |
3605 sql+= " (SELECT DISTINCT ON (Lookup.internalId) Lookup.internalId AS parentInternalId, childLevel.publicId AS instancePublicId, childLevel.internalId AS instanceInternalId" | |
3606 " FROM Resources AS childLevel " | |
3607 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId) "; | |
3608 }; break; | |
3609 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_STUDY: | |
3610 { | |
3611 sql+= " (SELECT DISTINCT ON (Lookup.internalId) Lookup.internalId AS parentInternalId, grandChildLevel.publicId AS instancePublicId, grandChildLevel.internalId AS instanceInternalId" | |
3612 " FROM Resources AS grandChildLevel " | |
3613 " INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId " | |
3614 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId) "; | |
3615 }; break; | |
3616 case Orthanc::DatabasePluginMessages::ResourceType::RESOURCE_PATIENT: | |
3617 { | |
3618 sql+= " (SELECT DISTINCT ON (Lookup.internalId) Lookup.internalId AS parentInternalId, grandGrandChildLevel.publicId AS instancePublicId, grandGrandChildLevel.internalId AS instanceInternalId" | |
3619 " FROM Resources AS grandGrandChildLevel " | |
3620 " INNER JOIN Resources grandChildLevel ON grandGrandChildLevel.parentId = grandChildLevel.internalId " | |
3621 " INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId " | |
3622 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId) "; | |
3623 }; break; | |
3624 default: | |
3625 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
3626 } | |
3627 | |
3628 sql += " SELECT" | |
3629 " " TOSTRING(QUERY_ONE_INSTANCE_IDENTIFIER) " AS c0_queryId, " | |
3630 " parentInternalId AS c1_internalId, " | |
3631 " NULL::BIGINT AS c2_rowNumber, " | |
3632 " instancePublicId AS c3_string1, " | |
3633 " NULL::TEXT AS c4_string2, " | |
3634 " NULL::TEXT AS c5_string3, " | |
3635 " NULL::INT AS c6_int1, " | |
3636 " NULL::INT AS c7_int2, " | |
3637 " instanceInternalId AS c8_big_int1, " | |
3638 " NULL::BIGINT AS c9_big_int2 " | |
3639 " FROM OneInstance "; | |
3640 | |
3641 sql += " UNION SELECT" | |
3642 " " TOSTRING(QUERY_ONE_INSTANCE_METADATA) " AS c0_queryId, " | |
3643 " parentInternalId AS c1_internalId, " | |
3644 " NULL::BIGINT AS c2_rowNumber, " | |
3645 " Metadata.value AS c3_string1, " | |
3646 " NULL::TEXT AS c4_string2, " | |
3647 " NULL::TEXT AS c5_string3, " | |
3648 " Metadata.type AS c6_int1, " | |
3649 " NULL::INT AS c7_int2, " | |
3650 " NULL::BIGINT AS c8_big_int1, " | |
3651 " NULL::BIGINT AS c9_big_int2 " | |
3652 " FROM Metadata " | |
3653 " INNER JOIN OneInstance ON Metadata.id = OneInstance.instanceInternalId"; | |
3654 | |
3655 sql += " UNION SELECT" | |
3656 " " TOSTRING(QUERY_ONE_INSTANCE_ATTACHMENTS) " AS c0_queryId, " | |
3657 " parentInternalId AS c1_internalId, " | |
3658 " NULL::BIGINT AS c2_rowNumber, " | |
3659 " uuid AS c3_string1, " | |
3660 " uncompressedHash AS c4_string2, " | |
3661 " compressedHash AS c5_string3, " | |
3662 " fileType AS c6_int1, " | |
3663 " compressionType AS c7_int2, " | |
3664 " compressedSize AS c8_big_int1, " | |
3665 " uncompressedSize AS c9_big_int2 " | |
3666 " FROM AttachedFiles " | |
3667 " INNER JOIN OneInstance ON AttachedFiles.id = OneInstance.instanceInternalId"; | |
3668 | |
3669 sql += " ) "; | |
3670 | |
3671 } | |
3672 | |
3673 sql += " ORDER BY c0_queryId, c2_rowNumber"; // this is really important to make sure that the Lookup query is the first one to provide results since we use it to create the responses element ! | |
3674 | |
3675 DatabaseManager::StandaloneStatement statement(manager, sql); // TODO-FIND: cache dynamic statement ? Probably worth it since it can be very complex queries ! | |
3676 formatter.PrepareStatement(statement); | |
3677 statement.Execute(formatter.GetDictionary()); | |
3678 | |
3679 | |
3680 std::map<int64_t, Orthanc::DatabasePluginMessages::Find_Response*> responses; | |
3681 | |
3682 while (!statement.IsDone()) | |
3683 { | |
3684 int32_t queryId = statement.ReadInteger32(C0_QUERY_ID); | |
3685 int64_t internalId = statement.ReadInteger64(C1_INTERNAL_ID); | |
3686 | |
3687 assert(queryId == QUERY_LOOKUP || responses.find(internalId) != responses.end()); // the QUERY_LOOKUP must be read first and must create the response before any other query tries to populate the fields | |
3688 | |
3689 switch (queryId) | |
3690 { | |
3691 case QUERY_LOOKUP: | |
3692 responses[internalId] = response.add_find(); | |
3693 responses[internalId]->set_public_id(statement.ReadString(C3_STRING_1)); | |
3694 responses[internalId]->set_internal_id(internalId); | |
3695 break; | |
3696 | |
3697 case QUERY_LABELS: | |
3698 responses[internalId]->add_labels(statement.ReadString(C3_STRING_1)); | |
3699 break; | |
3700 | |
3701 case QUERY_MAIN_DICOM_TAGS: | |
3702 { | |
3703 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], request.level()); | |
3704 Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); | |
3705 | |
3706 tag->set_value(statement.ReadString(C3_STRING_1)); | |
3707 tag->set_group(statement.ReadInteger32(C6_INT_1)); | |
3708 tag->set_element(statement.ReadInteger32(C7_INT_2)); | |
3709 }; break; | |
3710 | |
3711 case QUERY_PARENT_MAIN_DICOM_TAGS: | |
3712 { | |
3713 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() - 1)); | |
3714 Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); | |
3715 | |
3716 tag->set_value(statement.ReadString(C3_STRING_1)); | |
3717 tag->set_group(statement.ReadInteger32(C6_INT_1)); | |
3718 tag->set_element(statement.ReadInteger32(C7_INT_2)); | |
3719 }; break; | |
3720 | |
3721 case QUERY_GRAND_PARENT_MAIN_DICOM_TAGS: | |
3722 { | |
3723 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() - 2)); | |
3724 Orthanc::DatabasePluginMessages::Find_Response_Tag* tag = content->add_main_dicom_tags(); | |
3725 | |
3726 tag->set_value(statement.ReadString(C3_STRING_1)); | |
3727 tag->set_group(statement.ReadInteger32(C6_INT_1)); | |
3728 tag->set_element(statement.ReadInteger32(C7_INT_2)); | |
3729 }; break; | |
3730 | |
3731 case QUERY_CHILDREN_IDENTIFIERS: | |
3732 { | |
3733 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 1)); | |
3734 content->add_identifiers(statement.ReadString(C3_STRING_1)); | |
3735 }; break; | |
3736 | |
3737 case QUERY_CHILDREN_MAIN_DICOM_TAGS: | |
3738 { | |
3739 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 1)); | |
3740 Orthanc::DatabasePluginMessages::Find_Response_MultipleTags* tag = content->add_main_dicom_tags(); | |
3741 tag->add_values(statement.ReadString(C3_STRING_1)); // TODO: handle sequences ?? | |
3742 tag->set_group(statement.ReadInteger32(C6_INT_1)); | |
3743 tag->set_element(statement.ReadInteger32(C7_INT_2)); | |
3744 }; break; | |
3745 | |
3746 case QUERY_CHILDREN_METADATA: | |
3747 { | |
3748 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 1)); | |
3749 Orthanc::DatabasePluginMessages::Find_Response_MultipleMetadata* metadata = content->add_metadata(); | |
3750 | |
3751 metadata->add_values(statement.ReadString(C3_STRING_1)); | |
3752 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3753 }; break; | |
3754 | |
3755 case QUERY_GRAND_CHILDREN_IDENTIFIERS: | |
3756 { | |
3757 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 2)); | |
3758 content->add_identifiers(statement.ReadString(C3_STRING_1)); | |
3759 }; break; | |
3760 | |
3761 case QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS: | |
3762 { | |
3763 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 2)); | |
3764 Orthanc::DatabasePluginMessages::Find_Response_MultipleTags* tag = content->add_main_dicom_tags(); | |
3765 | |
3766 tag->add_values(statement.ReadString(C3_STRING_1)); // TODO: handle sequences ?? | |
3767 tag->set_group(statement.ReadInteger32(C6_INT_1)); | |
3768 tag->set_element(statement.ReadInteger32(C7_INT_2)); | |
3769 }; break; | |
3770 | |
3771 case QUERY_GRAND_CHILDREN_METADATA: | |
3772 { | |
3773 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 2)); | |
3774 Orthanc::DatabasePluginMessages::Find_Response_MultipleMetadata* metadata = content->add_metadata(); | |
3775 | |
3776 metadata->add_values(statement.ReadString(C3_STRING_1)); | |
3777 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3778 }; break; | |
3779 | |
3780 case QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS: | |
3781 { | |
3782 Orthanc::DatabasePluginMessages::Find_Response_ChildrenContent* content = GetChildrenContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() + 3)); | |
3783 content->add_identifiers(statement.ReadString(C3_STRING_1)); | |
3784 }; break; | |
3785 | |
3786 case QUERY_ATTACHMENTS: | |
3787 { | |
3788 Orthanc::DatabasePluginMessages::FileInfo* attachment = responses[internalId]->add_attachments(); | |
3789 | |
3790 attachment->set_uuid(statement.ReadString(C3_STRING_1)); | |
3791 attachment->set_uncompressed_hash(statement.ReadString(C4_STRING_2)); | |
3792 attachment->set_compressed_hash(statement.ReadString(C5_STRING_3)); | |
3793 attachment->set_content_type(statement.ReadInteger32(C6_INT_1)); | |
3794 attachment->set_compression_type(statement.ReadInteger32(C7_INT_2)); | |
3795 attachment->set_compressed_size(statement.ReadInteger64(C8_BIG_INT_1)); | |
3796 attachment->set_uncompressed_size(statement.ReadInteger64(C9_BIG_INT_2)); | |
3797 }; break; | |
3798 | |
3799 case QUERY_METADATA: | |
3800 { | |
3801 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], request.level()); | |
3802 Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); | |
3803 | |
3804 metadata->set_value(statement.ReadString(C3_STRING_1)); | |
3805 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3806 }; break; | |
3807 | |
3808 case QUERY_PARENT_METADATA: | |
3809 { | |
3810 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() - 1)); | |
3811 Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); | |
3812 | |
3813 metadata->set_value(statement.ReadString(C3_STRING_1)); | |
3814 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3815 }; break; | |
3816 | |
3817 case QUERY_GRAND_PARENT_METADATA: | |
3818 { | |
3819 Orthanc::DatabasePluginMessages::Find_Response_ResourceContent* content = GetResourceContent(responses[internalId], static_cast<Orthanc::DatabasePluginMessages::ResourceType>(request.level() - 2)); | |
3820 Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = content->add_metadata(); | |
3821 | |
3822 metadata->set_value(statement.ReadString(C3_STRING_1)); | |
3823 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3824 }; break; | |
3825 | |
3826 case QUERY_PARENT_IDENTIFIER: | |
3827 { | |
3828 responses[internalId]->set_parent_public_id(statement.ReadString(C3_STRING_1)); | |
3829 }; break; | |
3830 | |
3831 case QUERY_ONE_INSTANCE_IDENTIFIER: | |
3832 { | |
3833 responses[internalId]->set_one_instance_public_id(statement.ReadString(C3_STRING_1)); | |
3834 }; break; | |
3835 case QUERY_ONE_INSTANCE_METADATA: | |
3836 { | |
3837 Orthanc::DatabasePluginMessages::Find_Response_Metadata* metadata = responses[internalId]->add_one_instance_metadata(); | |
3838 | |
3839 metadata->set_value(statement.ReadString(C3_STRING_1)); | |
3840 metadata->set_key(statement.ReadInteger32(C6_INT_1)); | |
3841 }; break; | |
3842 case QUERY_ONE_INSTANCE_ATTACHMENTS: | |
3843 { | |
3844 Orthanc::DatabasePluginMessages::FileInfo* attachment = responses[internalId]->add_one_instance_attachments(); | |
3845 | |
3846 attachment->set_uuid(statement.ReadString(C3_STRING_1)); | |
3847 attachment->set_uncompressed_hash(statement.ReadString(C4_STRING_2)); | |
3848 attachment->set_compressed_hash(statement.ReadString(C5_STRING_3)); | |
3849 attachment->set_content_type(statement.ReadInteger32(C6_INT_1)); | |
3850 attachment->set_compression_type(statement.ReadInteger32(C7_INT_2)); | |
3851 attachment->set_compressed_size(statement.ReadInteger64(C8_BIG_INT_1)); | |
3852 attachment->set_uncompressed_size(statement.ReadInteger64(C9_BIG_INT_2)); | |
3853 }; break; | |
3854 | |
3855 default: | |
3856 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
3857 } | |
3858 statement.Next(); | |
3859 } | |
3860 } | |
3861 #endif | |
3862 | |
2688 } | 3863 } |