comparison OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp @ 5083:75e949689c08 attach-custom-data

PluginDatabaseV4 to handle customData
author Alain Mazy <am@osimis.io>
date Wed, 14 Sep 2022 17:11:45 +0200
parents
children
comparison
equal deleted inserted replaced
5082:4af5f496a0dd 5083:75e949689c08
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation, either version 3 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "../../Sources/PrecompiledHeadersServer.h"
24 #include "OrthancPluginDatabaseV4.h"
25
26 #if ORTHANC_ENABLE_PLUGINS != 1
27 # error The plugin support is disabled
28 #endif
29
30 #include "../../../OrthancFramework/Sources/Logging.h"
31 #include "../../../OrthancFramework/Sources/OrthancException.h"
32 #include "../../Sources/Database/ResourcesContent.h"
33 #include "../../Sources/Database/VoidDatabaseListener.h"
34 #include "PluginsEnumerations.h"
35
36 #include <cassert>
37
38
39 #define CHECK_FUNCTION_EXISTS(backend, func) \
40 if (backend.func == NULL) \
41 { \
42 throw OrthancException( \
43 ErrorCode_DatabasePlugin, "Missing primitive: " #func "()"); \
44 }
45
46 namespace Orthanc
47 {
48 class OrthancPluginDatabaseV4::Transaction : public IDatabaseWrapper::ITransaction
49 {
50 private:
51 OrthancPluginDatabaseV4& that_;
52 IDatabaseListener& listener_;
53 OrthancPluginDatabaseTransaction* transaction_;
54
55
56 void CheckSuccess(OrthancPluginErrorCode code) const
57 {
58 that_.CheckSuccess(code);
59 }
60
61
62 static FileInfo Convert(const OrthancPluginAttachment& attachment)
63 {
64 std::string customData;
65 return FileInfo(attachment.uuid,
66 static_cast<FileContentType>(attachment.contentType),
67 attachment.uncompressedSize,
68 attachment.uncompressedHash,
69 static_cast<CompressionType>(attachment.compressionType),
70 attachment.compressedSize,
71 attachment.compressedHash,
72 customData);
73 }
74
75 static FileInfo Convert(const OrthancPluginAttachment2& attachment)
76 {
77 return FileInfo(attachment.uuid,
78 static_cast<FileContentType>(attachment.contentType),
79 attachment.uncompressedSize,
80 attachment.uncompressedHash,
81 static_cast<CompressionType>(attachment.compressionType),
82 attachment.compressedSize,
83 attachment.compressedHash,
84 attachment.customData);
85 }
86
87 void ReadStringAnswers(std::list<std::string>& target)
88 {
89 uint32_t count;
90 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
91
92 target.clear();
93 for (uint32_t i = 0; i < count; i++)
94 {
95 const char* value = NULL;
96 CheckSuccess(that_.backend_.readAnswerString(transaction_, &value, i));
97 if (value == NULL)
98 {
99 throw OrthancException(ErrorCode_DatabasePlugin);
100 }
101 else
102 {
103 target.push_back(value);
104 }
105 }
106 }
107
108
109 bool ReadSingleStringAnswer(std::string& target)
110 {
111 uint32_t count;
112 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
113
114 if (count == 0)
115 {
116 return false;
117 }
118 else if (count == 1)
119 {
120 const char* value = NULL;
121 CheckSuccess(that_.backend_.readAnswerString(transaction_, &value, 0));
122 if (value == NULL)
123 {
124 throw OrthancException(ErrorCode_DatabasePlugin);
125 }
126 else
127 {
128 target.assign(value);
129 return true;
130 }
131 }
132 else
133 {
134 throw OrthancException(ErrorCode_DatabasePlugin);
135 }
136 }
137
138
139 bool ReadSingleInt64Answer(int64_t& target)
140 {
141 uint32_t count;
142 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
143
144 if (count == 0)
145 {
146 return false;
147 }
148 else if (count == 1)
149 {
150 CheckSuccess(that_.backend_.readAnswerInt64(transaction_, &target, 0));
151 return true;
152 }
153 else
154 {
155 throw OrthancException(ErrorCode_DatabasePlugin);
156 }
157 }
158
159
160 ExportedResource ReadAnswerExportedResource(uint32_t answerIndex)
161 {
162 OrthancPluginExportedResource exported;
163 CheckSuccess(that_.backend_.readAnswerExportedResource(transaction_, &exported, answerIndex));
164
165 if (exported.publicId == NULL ||
166 exported.modality == NULL ||
167 exported.date == NULL ||
168 exported.patientId == NULL ||
169 exported.studyInstanceUid == NULL ||
170 exported.seriesInstanceUid == NULL ||
171 exported.sopInstanceUid == NULL)
172 {
173 throw OrthancException(ErrorCode_DatabasePlugin);
174 }
175 else
176 {
177 return ExportedResource(exported.seq,
178 Plugins::Convert(exported.resourceType),
179 exported.publicId,
180 exported.modality,
181 exported.date,
182 exported.patientId,
183 exported.studyInstanceUid,
184 exported.seriesInstanceUid,
185 exported.sopInstanceUid);
186 }
187 }
188
189
190 ServerIndexChange ReadAnswerChange(uint32_t answerIndex)
191 {
192 OrthancPluginChange change;
193 CheckSuccess(that_.backend_.readAnswerChange(transaction_, &change, answerIndex));
194
195 if (change.publicId == NULL ||
196 change.date == NULL)
197 {
198 throw OrthancException(ErrorCode_DatabasePlugin);
199 }
200 else
201 {
202 return ServerIndexChange(change.seq,
203 static_cast<ChangeType>(change.changeType),
204 Plugins::Convert(change.resourceType),
205 change.publicId,
206 change.date);
207 }
208 }
209
210
211 void CheckNoEvent()
212 {
213 uint32_t count;
214 CheckSuccess(that_.backend_.readEventsCount(transaction_, &count));
215 if (count != 0)
216 {
217 throw OrthancException(ErrorCode_DatabasePlugin);
218 }
219 }
220
221
222 void ProcessEvents(bool isDeletingAttachment)
223 {
224 uint32_t count;
225 CheckSuccess(that_.backend_.readEventsCount(transaction_, &count));
226
227 for (uint32_t i = 0; i < count; i++)
228 {
229 OrthancPluginDatabaseEvent2 event;
230 CheckSuccess(that_.backend_.readEvent2(transaction_, &event, i));
231
232 switch (event.type)
233 {
234 case OrthancPluginDatabaseEventType_DeletedAttachment:
235 listener_.SignalAttachmentDeleted(Convert(event.content.attachment));
236 break;
237
238 case OrthancPluginDatabaseEventType_DeletedResource:
239 if (isDeletingAttachment)
240 {
241 // This event should only be triggered by "DeleteResource()"
242 throw OrthancException(ErrorCode_DatabasePlugin);
243 }
244 else
245 {
246 listener_.SignalResourceDeleted(Plugins::Convert(event.content.resource.level), event.content.resource.publicId);
247 }
248 break;
249
250 case OrthancPluginDatabaseEventType_RemainingAncestor:
251 if (isDeletingAttachment)
252 {
253 // This event should only triggered by "DeleteResource()"
254 throw OrthancException(ErrorCode_DatabasePlugin);
255 }
256 else
257 {
258 listener_.SignalRemainingAncestor(Plugins::Convert(event.content.resource.level), event.content.resource.publicId);
259 }
260 break;
261
262 default:
263 break; // Unhandled event
264 }
265 }
266 }
267
268
269 public:
270 Transaction(OrthancPluginDatabaseV4& that,
271 IDatabaseListener& listener,
272 OrthancPluginDatabaseTransactionType type) :
273 that_(that),
274 listener_(listener)
275 {
276 CheckSuccess(that.backend_.startTransaction(that.database_, &transaction_, type));
277 if (transaction_ == NULL)
278 {
279 throw OrthancException(ErrorCode_DatabasePlugin);
280 }
281 }
282
283
284 virtual ~Transaction()
285 {
286 OrthancPluginErrorCode code = that_.backend_.destructTransaction(transaction_);
287 if (code != OrthancPluginErrorCode_Success)
288 {
289 // Don't throw exception in destructors
290 that_.errorDictionary_.LogError(code, true);
291 }
292 }
293
294
295 virtual void Rollback() ORTHANC_OVERRIDE
296 {
297 CheckSuccess(that_.backend_.rollback(transaction_));
298 CheckNoEvent();
299 }
300
301
302 virtual void Commit(int64_t fileSizeDelta) ORTHANC_OVERRIDE
303 {
304 CheckSuccess(that_.backend_.commit(transaction_, fileSizeDelta));
305 CheckNoEvent();
306 }
307
308
309 virtual void AddAttachment(int64_t id,
310 const FileInfo& attachment,
311 int64_t revision) ORTHANC_OVERRIDE
312 {
313 OrthancPluginAttachment2 tmp;
314 tmp.uuid = attachment.GetUuid().c_str();
315 tmp.contentType = static_cast<int32_t>(attachment.GetContentType());
316 tmp.uncompressedSize = attachment.GetUncompressedSize();
317 tmp.uncompressedHash = attachment.GetUncompressedMD5().c_str();
318 tmp.compressionType = static_cast<int32_t>(attachment.GetCompressionType());
319 tmp.compressedSize = attachment.GetCompressedSize();
320 tmp.compressedHash = attachment.GetCompressedMD5().c_str();
321 tmp.customData = attachment.GetCustomData().c_str();
322
323 CheckSuccess(that_.backend_.addAttachment2(transaction_, id, &tmp, revision));
324 CheckNoEvent();
325 }
326
327
328 virtual void ClearChanges() ORTHANC_OVERRIDE
329 {
330 CheckSuccess(that_.backend_.clearChanges(transaction_));
331 CheckNoEvent();
332 }
333
334
335 virtual void ClearExportedResources() ORTHANC_OVERRIDE
336 {
337 CheckSuccess(that_.backend_.clearExportedResources(transaction_));
338 CheckNoEvent();
339 }
340
341
342 virtual void DeleteAttachment(int64_t id,
343 FileContentType attachment) ORTHANC_OVERRIDE
344 {
345 CheckSuccess(that_.backend_.deleteAttachment(transaction_, id, static_cast<int32_t>(attachment)));
346 ProcessEvents(true);
347 }
348
349
350 virtual void DeleteMetadata(int64_t id,
351 MetadataType type) ORTHANC_OVERRIDE
352 {
353 CheckSuccess(that_.backend_.deleteMetadata(transaction_, id, static_cast<int32_t>(type)));
354 CheckNoEvent();
355 }
356
357
358 virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE
359 {
360 CheckSuccess(that_.backend_.deleteResource(transaction_, id));
361 ProcessEvents(false);
362 }
363
364
365 virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
366 int64_t id) ORTHANC_OVERRIDE
367 {
368 CheckSuccess(that_.backend_.getAllMetadata(transaction_, id));
369 CheckNoEvent();
370
371 uint32_t count;
372 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
373
374 target.clear();
375 for (uint32_t i = 0; i < count; i++)
376 {
377 int32_t metadata;
378 const char* value = NULL;
379 CheckSuccess(that_.backend_.readAnswerMetadata(transaction_, &metadata, &value, i));
380
381 if (value == NULL)
382 {
383 throw OrthancException(ErrorCode_DatabasePlugin);
384 }
385 else
386 {
387 target[static_cast<MetadataType>(metadata)] = value;
388 }
389 }
390 }
391
392
393 virtual void GetAllPublicIds(std::list<std::string>& target,
394 ResourceType resourceType) ORTHANC_OVERRIDE
395 {
396 CheckSuccess(that_.backend_.getAllPublicIds(transaction_, Plugins::Convert(resourceType)));
397 CheckNoEvent();
398
399 ReadStringAnswers(target);
400 }
401
402
403 virtual void GetAllPublicIds(std::list<std::string>& target,
404 ResourceType resourceType,
405 size_t since,
406 size_t limit) ORTHANC_OVERRIDE
407 {
408 CheckSuccess(that_.backend_.getAllPublicIdsWithLimit(
409 transaction_, Plugins::Convert(resourceType),
410 static_cast<uint64_t>(since), static_cast<uint64_t>(limit)));
411 CheckNoEvent();
412
413 ReadStringAnswers(target);
414 }
415
416
417 virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/,
418 bool& done /*out*/,
419 int64_t since,
420 uint32_t maxResults) ORTHANC_OVERRIDE
421 {
422 uint8_t tmpDone = true;
423 CheckSuccess(that_.backend_.getChanges(transaction_, &tmpDone, since, maxResults));
424 CheckNoEvent();
425
426 done = (tmpDone != 0);
427
428 uint32_t count;
429 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
430
431 target.clear();
432 for (uint32_t i = 0; i < count; i++)
433 {
434 target.push_back(ReadAnswerChange(i));
435 }
436 }
437
438
439 virtual void GetChildrenInternalId(std::list<int64_t>& target,
440 int64_t id) ORTHANC_OVERRIDE
441 {
442 CheckSuccess(that_.backend_.getChildrenInternalId(transaction_, id));
443 CheckNoEvent();
444
445 uint32_t count;
446 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
447
448 target.clear();
449 for (uint32_t i = 0; i < count; i++)
450 {
451 int64_t value;
452 CheckSuccess(that_.backend_.readAnswerInt64(transaction_, &value, i));
453 target.push_back(value);
454 }
455 }
456
457
458 virtual void GetChildrenPublicId(std::list<std::string>& target,
459 int64_t id) ORTHANC_OVERRIDE
460 {
461 CheckSuccess(that_.backend_.getChildrenPublicId(transaction_, id));
462 CheckNoEvent();
463
464 ReadStringAnswers(target);
465 }
466
467
468 virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/,
469 bool& done /*out*/,
470 int64_t since,
471 uint32_t maxResults) ORTHANC_OVERRIDE
472 {
473 uint8_t tmpDone = true;
474 CheckSuccess(that_.backend_.getExportedResources(transaction_, &tmpDone, since, maxResults));
475 CheckNoEvent();
476
477 done = (tmpDone != 0);
478
479 uint32_t count;
480 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
481
482 target.clear();
483 for (uint32_t i = 0; i < count; i++)
484 {
485 target.push_back(ReadAnswerExportedResource(i));
486 }
487 }
488
489
490 virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE
491 {
492 CheckSuccess(that_.backend_.getLastChange(transaction_));
493 CheckNoEvent();
494
495 uint32_t count;
496 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
497
498 target.clear();
499 if (count == 1)
500 {
501 target.push_back(ReadAnswerChange(0));
502 }
503 else if (count > 1)
504 {
505 throw OrthancException(ErrorCode_DatabasePlugin);
506 }
507 }
508
509
510 virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) ORTHANC_OVERRIDE
511 {
512 CheckSuccess(that_.backend_.getLastExportedResource(transaction_));
513 CheckNoEvent();
514
515 uint32_t count;
516 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
517
518 target.clear();
519 if (count == 1)
520 {
521 target.push_back(ReadAnswerExportedResource(0));
522 }
523 else if (count > 1)
524 {
525 throw OrthancException(ErrorCode_DatabasePlugin);
526 }
527 }
528
529
530 virtual void GetMainDicomTags(DicomMap& target,
531 int64_t id) ORTHANC_OVERRIDE
532 {
533 CheckSuccess(that_.backend_.getMainDicomTags(transaction_, id));
534 CheckNoEvent();
535
536 uint32_t count;
537 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
538
539 target.Clear();
540 for (uint32_t i = 0; i < count; i++)
541 {
542 uint16_t group, element;
543 const char* value = NULL;
544 CheckSuccess(that_.backend_.readAnswerDicomTag(transaction_, &group, &element, &value, i));
545
546 if (value == NULL)
547 {
548 throw OrthancException(ErrorCode_DatabasePlugin);
549 }
550 else
551 {
552 target.SetValue(group, element, std::string(value), false);
553 }
554 }
555 }
556
557
558 virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE
559 {
560 CheckSuccess(that_.backend_.getPublicId(transaction_, resourceId));
561 CheckNoEvent();
562
563 std::string s;
564 if (ReadSingleStringAnswer(s))
565 {
566 return s;
567 }
568 else
569 {
570 throw OrthancException(ErrorCode_InexistentItem);
571 }
572 }
573
574
575 virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE
576 {
577 uint64_t value;
578 CheckSuccess(that_.backend_.getResourcesCount(transaction_, &value, Plugins::Convert(resourceType)));
579 CheckNoEvent();
580 return value;
581 }
582
583
584 virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE
585 {
586 OrthancPluginResourceType type;
587 CheckSuccess(that_.backend_.getResourceType(transaction_, &type, resourceId));
588 CheckNoEvent();
589 return Plugins::Convert(type);
590 }
591
592
593 virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE
594 {
595 uint64_t s;
596 CheckSuccess(that_.backend_.getTotalCompressedSize(transaction_, &s));
597 CheckNoEvent();
598 return s;
599 }
600
601
602 virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE
603 {
604 uint64_t s;
605 CheckSuccess(that_.backend_.getTotalUncompressedSize(transaction_, &s));
606 CheckNoEvent();
607 return s;
608 }
609
610
611 virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE
612 {
613 uint8_t b;
614 CheckSuccess(that_.backend_.isExistingResource(transaction_, &b, internalId));
615 CheckNoEvent();
616 return (b != 0);
617 }
618
619
620 virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE
621 {
622 uint8_t b;
623 CheckSuccess(that_.backend_.isProtectedPatient(transaction_, &b, internalId));
624 CheckNoEvent();
625 return (b != 0);
626 }
627
628
629 virtual void ListAvailableAttachments(std::set<FileContentType>& target,
630 int64_t id) ORTHANC_OVERRIDE
631 {
632 CheckSuccess(that_.backend_.listAvailableAttachments(transaction_, id));
633 CheckNoEvent();
634
635 uint32_t count;
636 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
637
638 target.clear();
639 for (uint32_t i = 0; i < count; i++)
640 {
641 int32_t value;
642 CheckSuccess(that_.backend_.readAnswerInt32(transaction_, &value, i));
643 target.insert(static_cast<FileContentType>(value));
644 }
645 }
646
647
648 virtual void LogChange(int64_t internalId,
649 const ServerIndexChange& change) ORTHANC_OVERRIDE
650 {
651 CheckSuccess(that_.backend_.logChange(transaction_, static_cast<int32_t>(change.GetChangeType()),
652 internalId, Plugins::Convert(change.GetResourceType()),
653 change.GetDate().c_str()));
654 CheckNoEvent();
655 }
656
657
658 virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE
659 {
660 CheckSuccess(that_.backend_.logExportedResource(transaction_, Plugins::Convert(resource.GetResourceType()),
661 resource.GetPublicId().c_str(),
662 resource.GetModality().c_str(),
663 resource.GetDate().c_str(),
664 resource.GetPatientId().c_str(),
665 resource.GetStudyInstanceUid().c_str(),
666 resource.GetSeriesInstanceUid().c_str(),
667 resource.GetSopInstanceUid().c_str()));
668 CheckNoEvent();
669 }
670
671
672 virtual bool LookupAttachment(FileInfo& attachment,
673 int64_t& revision,
674 int64_t id,
675 FileContentType contentType) ORTHANC_OVERRIDE
676 {
677 CheckSuccess(that_.backend_.lookupAttachment(transaction_, &revision, id, static_cast<int32_t>(contentType)));
678 CheckNoEvent();
679
680 uint32_t count;
681 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
682
683 if (count == 0)
684 {
685 return false;
686 }
687 else if (count == 1)
688 {
689 OrthancPluginAttachment2 tmp;
690 CheckSuccess(that_.backend_.readAnswerAttachment2(transaction_, &tmp, 0));
691 attachment = Convert(tmp);
692 return true;
693 }
694 else
695 {
696 throw OrthancException(ErrorCode_DatabasePlugin);
697 }
698 }
699
700
701 virtual bool LookupGlobalProperty(std::string& target,
702 GlobalProperty property,
703 bool shared) ORTHANC_OVERRIDE
704 {
705 const char* id = (shared ? "" : that_.serverIdentifier_.c_str());
706
707 CheckSuccess(that_.backend_.lookupGlobalProperty(transaction_, id, static_cast<int32_t>(property)));
708 CheckNoEvent();
709 return ReadSingleStringAnswer(target);
710 }
711
712
713 virtual bool LookupMetadata(std::string& target,
714 int64_t& revision,
715 int64_t id,
716 MetadataType type) ORTHANC_OVERRIDE
717 {
718 CheckSuccess(that_.backend_.lookupMetadata(transaction_, &revision, id, static_cast<int32_t>(type)));
719 CheckNoEvent();
720 return ReadSingleStringAnswer(target);
721 }
722
723
724 virtual bool LookupParent(int64_t& parentId,
725 int64_t resourceId) ORTHANC_OVERRIDE
726 {
727 uint8_t existing;
728 CheckSuccess(that_.backend_.lookupParent(transaction_, &existing, &parentId, resourceId));
729 CheckNoEvent();
730 return (existing != 0);
731 }
732
733
734 virtual bool LookupResource(int64_t& id,
735 ResourceType& type,
736 const std::string& publicId) ORTHANC_OVERRIDE
737 {
738 uint8_t existing;
739 OrthancPluginResourceType t;
740 CheckSuccess(that_.backend_.lookupResource(transaction_, &existing, &id, &t, publicId.c_str()));
741 CheckNoEvent();
742
743 if (existing == 0)
744 {
745 return false;
746 }
747 else
748 {
749 type = Plugins::Convert(t);
750 return true;
751 }
752 }
753
754
755 virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE
756 {
757 uint8_t available;
758 CheckSuccess(that_.backend_.selectPatientToRecycle(transaction_, &available, &internalId));
759 CheckNoEvent();
760 return (available != 0);
761 }
762
763
764 virtual bool SelectPatientToRecycle(int64_t& internalId,
765 int64_t patientIdToAvoid) ORTHANC_OVERRIDE
766 {
767 uint8_t available;
768 CheckSuccess(that_.backend_.selectPatientToRecycle2(transaction_, &available, &internalId, patientIdToAvoid));
769 CheckNoEvent();
770 return (available != 0);
771 }
772
773
774 virtual void SetGlobalProperty(GlobalProperty property,
775 bool shared,
776 const std::string& value) ORTHANC_OVERRIDE
777 {
778 const char* id = (shared ? "" : that_.serverIdentifier_.c_str());
779
780 CheckSuccess(that_.backend_.setGlobalProperty(transaction_, id, static_cast<int32_t>(property), value.c_str()));
781 CheckNoEvent();
782 }
783
784
785 virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE
786 {
787 CheckSuccess(that_.backend_.clearMainDicomTags(transaction_, id));
788 CheckNoEvent();
789 }
790
791
792 virtual void SetMetadata(int64_t id,
793 MetadataType type,
794 const std::string& value,
795 int64_t revision) ORTHANC_OVERRIDE
796 {
797 CheckSuccess(that_.backend_.setMetadata(transaction_, id, static_cast<int32_t>(type), value.c_str(), revision));
798 CheckNoEvent();
799 }
800
801
802 virtual void SetProtectedPatient(int64_t internalId,
803 bool isProtected) ORTHANC_OVERRIDE
804 {
805 CheckSuccess(that_.backend_.setProtectedPatient(transaction_, internalId, (isProtected ? 1 : 0)));
806 CheckNoEvent();
807 }
808
809
810 virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE
811 {
812 uint8_t tmp;
813 CheckSuccess(that_.backend_.isDiskSizeAbove(transaction_, &tmp, threshold));
814 CheckNoEvent();
815 return (tmp != 0);
816 }
817
818
819 virtual void ApplyLookupResources(std::list<std::string>& resourcesId,
820 std::list<std::string>* instancesId, // Can be NULL if not needed
821 const std::vector<DatabaseConstraint>& lookup,
822 ResourceType queryLevel,
823 size_t limit) ORTHANC_OVERRIDE
824 {
825 std::vector<OrthancPluginDatabaseConstraint> constraints;
826 std::vector< std::vector<const char*> > constraintsValues;
827
828 constraints.resize(lookup.size());
829 constraintsValues.resize(lookup.size());
830
831 for (size_t i = 0; i < lookup.size(); i++)
832 {
833 lookup[i].EncodeForPlugins(constraints[i], constraintsValues[i]);
834 }
835
836 CheckSuccess(that_.backend_.lookupResources(transaction_, lookup.size(),
837 (lookup.empty() ? NULL : &constraints[0]),
838 Plugins::Convert(queryLevel),
839 limit, (instancesId == NULL ? 0 : 1)));
840 CheckNoEvent();
841
842 uint32_t count;
843 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
844
845 resourcesId.clear();
846
847 if (instancesId != NULL)
848 {
849 instancesId->clear();
850 }
851
852 for (uint32_t i = 0; i < count; i++)
853 {
854 OrthancPluginMatchingResource resource;
855 CheckSuccess(that_.backend_.readAnswerMatchingResource(transaction_, &resource, i));
856
857 if (resource.resourceId == NULL)
858 {
859 throw OrthancException(ErrorCode_DatabasePlugin);
860 }
861
862 resourcesId.push_back(resource.resourceId);
863
864 if (instancesId != NULL)
865 {
866 if (resource.someInstanceId == NULL)
867 {
868 throw OrthancException(ErrorCode_DatabasePlugin);
869 }
870 else
871 {
872 instancesId->push_back(resource.someInstanceId);
873 }
874 }
875 }
876 }
877
878
879 virtual bool CreateInstance(CreateInstanceResult& result, /* out */
880 int64_t& instanceId, /* out */
881 const std::string& patient,
882 const std::string& study,
883 const std::string& series,
884 const std::string& instance) ORTHANC_OVERRIDE
885 {
886 OrthancPluginCreateInstanceResult output;
887 memset(&output, 0, sizeof(output));
888
889 CheckSuccess(that_.backend_.createInstance(transaction_, &output, patient.c_str(),
890 study.c_str(), series.c_str(), instance.c_str()));
891 CheckNoEvent();
892
893 instanceId = output.instanceId;
894
895 if (output.isNewInstance)
896 {
897 result.isNewPatient_ = output.isNewPatient;
898 result.isNewStudy_ = output.isNewStudy;
899 result.isNewSeries_ = output.isNewSeries;
900 result.patientId_ = output.patientId;
901 result.studyId_ = output.studyId;
902 result.seriesId_ = output.seriesId;
903 return true;
904 }
905 else
906 {
907 return false;
908 }
909
910 }
911
912
913 virtual void SetResourcesContent(const ResourcesContent& content) ORTHANC_OVERRIDE
914 {
915 std::vector<OrthancPluginResourcesContentTags> identifierTags;
916 std::vector<OrthancPluginResourcesContentTags> mainDicomTags;
917 std::vector<OrthancPluginResourcesContentMetadata> metadata;
918
919 identifierTags.reserve(content.GetListTags().size());
920 mainDicomTags.reserve(content.GetListTags().size());
921 metadata.reserve(content.GetListMetadata().size());
922
923 for (ResourcesContent::ListTags::const_iterator
924 it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it)
925 {
926 OrthancPluginResourcesContentTags tmp;
927 tmp.resource = it->resourceId_;
928 tmp.group = it->tag_.GetGroup();
929 tmp.element = it->tag_.GetElement();
930 tmp.value = it->value_.c_str();
931
932 if (it->isIdentifier_)
933 {
934 identifierTags.push_back(tmp);
935 }
936 else
937 {
938 mainDicomTags.push_back(tmp);
939 }
940 }
941
942 for (ResourcesContent::ListMetadata::const_iterator
943 it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it)
944 {
945 OrthancPluginResourcesContentMetadata tmp;
946 tmp.resource = it->resourceId_;
947 tmp.metadata = it->metadata_;
948 tmp.value = it->value_.c_str();
949 metadata.push_back(tmp);
950 }
951
952 assert(identifierTags.size() + mainDicomTags.size() == content.GetListTags().size() &&
953 metadata.size() == content.GetListMetadata().size());
954
955 CheckSuccess(that_.backend_.setResourcesContent(transaction_,
956 identifierTags.size(),
957 (identifierTags.empty() ? NULL : &identifierTags[0]),
958 mainDicomTags.size(),
959 (mainDicomTags.empty() ? NULL : &mainDicomTags[0]),
960 metadata.size(),
961 (metadata.empty() ? NULL : &metadata[0])));
962 CheckNoEvent();
963 }
964
965
966 virtual void GetChildrenMetadata(std::list<std::string>& target,
967 int64_t resourceId,
968 MetadataType metadata) ORTHANC_OVERRIDE
969 {
970 CheckSuccess(that_.backend_.getChildrenMetadata(transaction_, resourceId, static_cast<int32_t>(metadata)));
971 CheckNoEvent();
972 ReadStringAnswers(target);
973 }
974
975
976 virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE
977 {
978 int64_t tmp;
979 CheckSuccess(that_.backend_.getLastChangeIndex(transaction_, &tmp));
980 CheckNoEvent();
981 return tmp;
982 }
983
984
985 virtual bool LookupResourceAndParent(int64_t& id,
986 ResourceType& type,
987 std::string& parentPublicId,
988 const std::string& publicId) ORTHANC_OVERRIDE
989 {
990 uint8_t isExisting;
991 OrthancPluginResourceType tmpType;
992 CheckSuccess(that_.backend_.lookupResourceAndParent(transaction_, &isExisting, &id, &tmpType, publicId.c_str()));
993 CheckNoEvent();
994
995 if (isExisting)
996 {
997 type = Plugins::Convert(tmpType);
998
999 uint32_t count;
1000 CheckSuccess(that_.backend_.readAnswersCount(transaction_, &count));
1001
1002 if (count > 1)
1003 {
1004 throw OrthancException(ErrorCode_DatabasePlugin);
1005 }
1006
1007 switch (type)
1008 {
1009 case ResourceType_Patient:
1010 // A patient has no parent
1011 if (count == 1)
1012 {
1013 throw OrthancException(ErrorCode_DatabasePlugin);
1014 }
1015 break;
1016
1017 case ResourceType_Study:
1018 case ResourceType_Series:
1019 case ResourceType_Instance:
1020 if (count == 0)
1021 {
1022 throw OrthancException(ErrorCode_DatabasePlugin);
1023 }
1024 else
1025 {
1026 const char* value = NULL;
1027 CheckSuccess(that_.backend_.readAnswerString(transaction_, &value, 0));
1028 if (value == NULL)
1029 {
1030 throw OrthancException(ErrorCode_DatabasePlugin);
1031 }
1032 else
1033 {
1034 parentPublicId.assign(value);
1035 }
1036 }
1037 break;
1038
1039 default:
1040 throw OrthancException(ErrorCode_DatabasePlugin);
1041 }
1042
1043 return true;
1044 }
1045 else
1046 {
1047 return false;
1048 }
1049 }
1050 };
1051
1052
1053 void OrthancPluginDatabaseV4::CheckSuccess(OrthancPluginErrorCode code) const
1054 {
1055 if (code != OrthancPluginErrorCode_Success)
1056 {
1057 errorDictionary_.LogError(code, true);
1058 throw OrthancException(static_cast<ErrorCode>(code));
1059 }
1060 }
1061
1062
1063 OrthancPluginDatabaseV4::OrthancPluginDatabaseV4(SharedLibrary& library,
1064 PluginsErrorDictionary& errorDictionary,
1065 const OrthancPluginDatabaseBackendV4* backend,
1066 size_t backendSize,
1067 void* database,
1068 const std::string& serverIdentifier) :
1069 library_(library),
1070 errorDictionary_(errorDictionary),
1071 database_(database),
1072 serverIdentifier_(serverIdentifier)
1073 {
1074 CLOG(INFO, PLUGINS) << "Identifier of this Orthanc server for the global properties "
1075 << "of the custom database: \"" << serverIdentifier << "\"";
1076
1077 if (backendSize >= sizeof(backend_))
1078 {
1079 memcpy(&backend_, backend, sizeof(backend_));
1080 }
1081 else
1082 {
1083 // Not all the primitives are implemented by the plugin
1084 memset(&backend_, 0, sizeof(backend_));
1085 memcpy(&backend_, backend, backendSize);
1086 }
1087
1088 // Sanity checks
1089 CHECK_FUNCTION_EXISTS(backend_, readAnswersCount);
1090 CHECK_FUNCTION_EXISTS(backend_, readAnswerAttachment2);
1091 CHECK_FUNCTION_EXISTS(backend_, readAnswerChange);
1092 CHECK_FUNCTION_EXISTS(backend_, readAnswerDicomTag);
1093 CHECK_FUNCTION_EXISTS(backend_, readAnswerExportedResource);
1094 CHECK_FUNCTION_EXISTS(backend_, readAnswerInt32);
1095 CHECK_FUNCTION_EXISTS(backend_, readAnswerInt64);
1096 CHECK_FUNCTION_EXISTS(backend_, readAnswerMatchingResource);
1097 CHECK_FUNCTION_EXISTS(backend_, readAnswerMetadata);
1098 CHECK_FUNCTION_EXISTS(backend_, readAnswerString);
1099
1100 CHECK_FUNCTION_EXISTS(backend_, readEventsCount);
1101 CHECK_FUNCTION_EXISTS(backend_, readEvent2);
1102
1103 CHECK_FUNCTION_EXISTS(backend_, open);
1104 CHECK_FUNCTION_EXISTS(backend_, close);
1105 CHECK_FUNCTION_EXISTS(backend_, destructDatabase);
1106 CHECK_FUNCTION_EXISTS(backend_, getDatabaseVersion);
1107 CHECK_FUNCTION_EXISTS(backend_, upgradeDatabase);
1108 CHECK_FUNCTION_EXISTS(backend_, startTransaction);
1109 CHECK_FUNCTION_EXISTS(backend_, destructTransaction);
1110 CHECK_FUNCTION_EXISTS(backend_, hasRevisionsSupport);
1111 CHECK_FUNCTION_EXISTS(backend_, hasAttachmentCustomDataSupport); // new in v4
1112
1113 CHECK_FUNCTION_EXISTS(backend_, rollback);
1114 CHECK_FUNCTION_EXISTS(backend_, commit);
1115
1116 CHECK_FUNCTION_EXISTS(backend_, addAttachment2);
1117 CHECK_FUNCTION_EXISTS(backend_, clearChanges);
1118 CHECK_FUNCTION_EXISTS(backend_, clearExportedResources);
1119 CHECK_FUNCTION_EXISTS(backend_, clearMainDicomTags);
1120 CHECK_FUNCTION_EXISTS(backend_, createInstance);
1121 CHECK_FUNCTION_EXISTS(backend_, deleteAttachment);
1122 CHECK_FUNCTION_EXISTS(backend_, deleteMetadata);
1123 CHECK_FUNCTION_EXISTS(backend_, deleteResource);
1124 CHECK_FUNCTION_EXISTS(backend_, getAllMetadata);
1125 CHECK_FUNCTION_EXISTS(backend_, getAllPublicIds);
1126 CHECK_FUNCTION_EXISTS(backend_, getAllPublicIdsWithLimit);
1127 CHECK_FUNCTION_EXISTS(backend_, getChanges);
1128 CHECK_FUNCTION_EXISTS(backend_, getChildrenInternalId);
1129 CHECK_FUNCTION_EXISTS(backend_, getChildrenMetadata);
1130 CHECK_FUNCTION_EXISTS(backend_, getChildrenPublicId);
1131 CHECK_FUNCTION_EXISTS(backend_, getExportedResources);
1132 CHECK_FUNCTION_EXISTS(backend_, getLastChange);
1133 CHECK_FUNCTION_EXISTS(backend_, getLastChangeIndex);
1134 CHECK_FUNCTION_EXISTS(backend_, getLastExportedResource);
1135 CHECK_FUNCTION_EXISTS(backend_, getMainDicomTags);
1136 CHECK_FUNCTION_EXISTS(backend_, getPublicId);
1137 CHECK_FUNCTION_EXISTS(backend_, getResourceType);
1138 CHECK_FUNCTION_EXISTS(backend_, getResourcesCount);
1139 CHECK_FUNCTION_EXISTS(backend_, getTotalCompressedSize);
1140 CHECK_FUNCTION_EXISTS(backend_, getTotalUncompressedSize);
1141 CHECK_FUNCTION_EXISTS(backend_, isDiskSizeAbove);
1142 CHECK_FUNCTION_EXISTS(backend_, isExistingResource);
1143 CHECK_FUNCTION_EXISTS(backend_, isProtectedPatient);
1144 CHECK_FUNCTION_EXISTS(backend_, listAvailableAttachments);
1145 CHECK_FUNCTION_EXISTS(backend_, logChange);
1146 CHECK_FUNCTION_EXISTS(backend_, logExportedResource);
1147 CHECK_FUNCTION_EXISTS(backend_, lookupAttachment);
1148 CHECK_FUNCTION_EXISTS(backend_, lookupGlobalProperty);
1149 CHECK_FUNCTION_EXISTS(backend_, lookupMetadata);
1150 CHECK_FUNCTION_EXISTS(backend_, lookupParent);
1151 CHECK_FUNCTION_EXISTS(backend_, lookupResource);
1152 CHECK_FUNCTION_EXISTS(backend_, lookupResourceAndParent);
1153 CHECK_FUNCTION_EXISTS(backend_, lookupResources);
1154 CHECK_FUNCTION_EXISTS(backend_, selectPatientToRecycle);
1155 CHECK_FUNCTION_EXISTS(backend_, selectPatientToRecycle2);
1156 CHECK_FUNCTION_EXISTS(backend_, setGlobalProperty);
1157 CHECK_FUNCTION_EXISTS(backend_, setMetadata);
1158 CHECK_FUNCTION_EXISTS(backend_, setProtectedPatient);
1159 CHECK_FUNCTION_EXISTS(backend_, setResourcesContent);
1160 }
1161
1162
1163 OrthancPluginDatabaseV4::~OrthancPluginDatabaseV4()
1164 {
1165 if (database_ != NULL)
1166 {
1167 OrthancPluginErrorCode code = backend_.destructDatabase(database_);
1168 if (code != OrthancPluginErrorCode_Success)
1169 {
1170 // Don't throw exception in destructors
1171 errorDictionary_.LogError(code, true);
1172 }
1173 }
1174 }
1175
1176
1177 void OrthancPluginDatabaseV4::Open()
1178 {
1179 CheckSuccess(backend_.open(database_));
1180 }
1181
1182
1183 void OrthancPluginDatabaseV4::Close()
1184 {
1185 CheckSuccess(backend_.close(database_));
1186 }
1187
1188
1189 IDatabaseWrapper::ITransaction* OrthancPluginDatabaseV4::StartTransaction(TransactionType type,
1190 IDatabaseListener& listener)
1191 {
1192 switch (type)
1193 {
1194 case TransactionType_ReadOnly:
1195 return new Transaction(*this, listener, OrthancPluginDatabaseTransactionType_ReadOnly);
1196
1197 case TransactionType_ReadWrite:
1198 return new Transaction(*this, listener, OrthancPluginDatabaseTransactionType_ReadWrite);
1199
1200 default:
1201 throw OrthancException(ErrorCode_InternalError);
1202 }
1203 }
1204
1205
1206 unsigned int OrthancPluginDatabaseV4::GetDatabaseVersion()
1207 {
1208 uint32_t version = 0;
1209 CheckSuccess(backend_.getDatabaseVersion(database_, &version));
1210 return version;
1211 }
1212
1213
1214 void OrthancPluginDatabaseV4::Upgrade(unsigned int targetVersion,
1215 IStorageArea& storageArea)
1216 {
1217 VoidDatabaseListener listener;
1218
1219 if (backend_.upgradeDatabase != NULL)
1220 {
1221 Transaction transaction(*this, listener, OrthancPluginDatabaseTransactionType_ReadWrite);
1222
1223 OrthancPluginErrorCode code = backend_.upgradeDatabase(
1224 database_, reinterpret_cast<OrthancPluginStorageArea*>(&storageArea),
1225 static_cast<uint32_t>(targetVersion));
1226
1227 if (code == OrthancPluginErrorCode_Success)
1228 {
1229 transaction.Commit(0);
1230 }
1231 else
1232 {
1233 transaction.Rollback();
1234 errorDictionary_.LogError(code, true);
1235 throw OrthancException(static_cast<ErrorCode>(code));
1236 }
1237 }
1238 }
1239
1240
1241 bool OrthancPluginDatabaseV4::HasRevisionsSupport() const
1242 {
1243 // WARNING: This method requires "Open()" to have been called
1244 uint8_t hasRevisions;
1245 CheckSuccess(backend_.hasRevisionsSupport(database_, &hasRevisions));
1246 return (hasRevisions != 0);
1247 }
1248
1249 bool OrthancPluginDatabaseV4::HasAttachmentCustomDataSupport() const
1250 {
1251 // WARNING: This method requires "Open()" to have been called
1252 uint8_t hasAttachmentCustomDataSupport;
1253 CheckSuccess(backend_.hasAttachmentCustomDataSupport(database_, &hasAttachmentCustomDataSupport));
1254 return (hasAttachmentCustomDataSupport != 0);
1255 }
1256 }