Mercurial > hg > orthanc-databases
comparison SQLite/Plugins/IndexPlugin.cpp @ 207:d9ef3f16e6a2
wrapping transactions in API v3
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 19 Mar 2021 15:11:45 +0100 |
parents | 6dcf57074dd4 |
children | 696bc0c9fddb |
comparison
equal
deleted
inserted
replaced
206:6dcf57074dd4 | 207:d9ef3f16e6a2 |
---|---|
30 | 30 |
31 | 31 |
32 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 | 32 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 |
33 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 0) | 33 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 0) |
34 | 34 |
35 | |
36 #define ORTHANC_PLUGINS_DATABASE_CATCH(context) \ | |
37 catch (::Orthanc::OrthancException& e) \ | |
38 { \ | |
39 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); \ | |
40 } \ | |
41 catch (::std::runtime_error& e) \ | |
42 { \ | |
43 const std::string message = "Exception in database back-end: " + std::string(e.what()); \ | |
44 OrthancPluginLogError(context, message.c_str()); \ | |
45 return OrthancPluginErrorCode_DatabasePlugin; \ | |
46 } \ | |
47 catch (...) \ | |
48 { \ | |
49 OrthancPluginLogError(context, "Native exception"); \ | |
50 return OrthancPluginErrorCode_DatabasePlugin; \ | |
51 } | |
52 | |
53 | |
35 namespace OrthancDatabases | 54 namespace OrthancDatabases |
36 { | 55 { |
37 class Output : public IDatabaseBackendOutput | 56 class Output : public IDatabaseBackendOutput |
38 { | 57 { |
39 private: | 58 private: |
59 struct Metadata | |
60 { | |
61 int32_t metadata; | |
62 const char* value; | |
63 }; | |
64 | |
40 _OrthancPluginDatabaseAnswerType answerType_; | 65 _OrthancPluginDatabaseAnswerType answerType_; |
41 std::list<std::string> strings_; | 66 std::list<std::string> stringsStore_; |
42 | 67 |
43 std::vector<OrthancPluginAttachment> attachments_; | 68 std::vector<OrthancPluginAttachment> attachments_; |
44 std::vector<OrthancPluginChange> changes_; | 69 std::vector<OrthancPluginChange> changes_; |
45 std::vector<OrthancPluginDicomTag> tags_; | 70 std::vector<OrthancPluginDicomTag> tags_; |
46 std::vector<OrthancPluginExportedResource> exported_; | 71 std::vector<OrthancPluginExportedResource> exported_; |
47 std::vector<OrthancPluginDatabaseEvent> events_; | 72 std::vector<OrthancPluginDatabaseEvent> events_; |
73 std::vector<int32_t> integers32_; | |
74 std::vector<int64_t> integers64_; | |
75 std::vector<OrthancPluginMatchingResource> matches_; | |
76 std::vector<Metadata> metadata_; | |
77 std::vector<std::string> stringAnswers_; | |
48 | 78 |
49 const char* StoreString(const std::string& s) | 79 const char* StoreString(const std::string& s) |
50 { | 80 { |
51 strings_.push_back(s); | 81 stringsStore_.push_back(s); |
52 return strings_.back().c_str(); | 82 return stringsStore_.back().c_str(); |
53 } | 83 } |
54 | 84 |
55 void SetupAnswerType(_OrthancPluginDatabaseAnswerType type) | 85 void SetupAnswerType(_OrthancPluginDatabaseAnswerType type) |
56 { | 86 { |
57 if (answerType_ == _OrthancPluginDatabaseAnswerType_None) | 87 if (answerType_ == _OrthancPluginDatabaseAnswerType_None) |
72 | 102 |
73 | 103 |
74 void Clear() | 104 void Clear() |
75 { | 105 { |
76 answerType_ = _OrthancPluginDatabaseAnswerType_None; | 106 answerType_ = _OrthancPluginDatabaseAnswerType_None; |
77 strings_.clear(); | 107 stringsStore_.clear(); |
78 | 108 |
79 attachments_.clear(); | 109 attachments_.clear(); |
80 changes_.clear(); | 110 changes_.clear(); |
81 tags_.clear(); | 111 tags_.clear(); |
82 exported_.clear(); | 112 exported_.clear(); |
83 events_.clear(); | 113 events_.clear(); |
114 integers32_.clear(); | |
115 integers64_.clear(); | |
116 matches_.clear(); | |
117 metadata_.clear(); | |
118 stringAnswers_.clear(); | |
84 } | 119 } |
85 | 120 |
86 | 121 |
87 static OrthancPluginErrorCode ReadAnswersCount(OrthancPluginDatabaseTransaction* transaction, | 122 static OrthancPluginErrorCode ReadAnswersCount(OrthancPluginDatabaseTransaction* transaction, |
88 uint32_t* target /* out */) | 123 uint32_t* target /* out */) |
109 size = that.tags_.size(); | 144 size = that.tags_.size(); |
110 break; | 145 break; |
111 | 146 |
112 case _OrthancPluginDatabaseAnswerType_ExportedResource: | 147 case _OrthancPluginDatabaseAnswerType_ExportedResource: |
113 size = that.exported_.size(); | 148 size = that.exported_.size(); |
149 break; | |
150 | |
151 case _OrthancPluginDatabaseAnswerType_Int32: | |
152 size = that.integers32_.size(); | |
153 break; | |
154 | |
155 case _OrthancPluginDatabaseAnswerType_Int64: | |
156 size = that.integers64_.size(); | |
157 break; | |
158 | |
159 case _OrthancPluginDatabaseAnswerType_MatchingResource: | |
160 size = that.matches_.size(); | |
161 break; | |
162 | |
163 case _OrthancPluginDatabaseAnswerType_Metadata: | |
164 size = that.metadata_.size(); | |
165 break; | |
166 | |
167 case _OrthancPluginDatabaseAnswerType_String: | |
168 size = that.stringAnswers_.size(); | |
114 break; | 169 break; |
115 | 170 |
116 default: | 171 default: |
117 return OrthancPluginErrorCode_InternalError; | 172 return OrthancPluginErrorCode_InternalError; |
118 } | 173 } |
188 const Output& that = *reinterpret_cast<const Output*>(transaction); | 243 const Output& that = *reinterpret_cast<const Output*>(transaction); |
189 | 244 |
190 if (index < that.exported_.size()) | 245 if (index < that.exported_.size()) |
191 { | 246 { |
192 *target = that.exported_[index]; | 247 *target = that.exported_[index]; |
248 return OrthancPluginErrorCode_Success; | |
249 } | |
250 else | |
251 { | |
252 return OrthancPluginErrorCode_ParameterOutOfRange; | |
253 } | |
254 } | |
255 | |
256 | |
257 static OrthancPluginErrorCode ReadAnswerInt32(OrthancPluginDatabaseTransaction* transaction, | |
258 int32_t* target, | |
259 uint32_t index) | |
260 { | |
261 const Output& that = *reinterpret_cast<const Output*>(transaction); | |
262 | |
263 if (index < that.integers32_.size()) | |
264 { | |
265 *target = that.integers32_[index]; | |
266 return OrthancPluginErrorCode_Success; | |
267 } | |
268 else | |
269 { | |
270 return OrthancPluginErrorCode_ParameterOutOfRange; | |
271 } | |
272 } | |
273 | |
274 | |
275 static OrthancPluginErrorCode ReadAnswerInt64(OrthancPluginDatabaseTransaction* transaction, | |
276 int64_t* target, | |
277 uint32_t index) | |
278 { | |
279 const Output& that = *reinterpret_cast<const Output*>(transaction); | |
280 | |
281 if (index < that.integers64_.size()) | |
282 { | |
283 *target = that.integers64_[index]; | |
284 return OrthancPluginErrorCode_Success; | |
285 } | |
286 else | |
287 { | |
288 return OrthancPluginErrorCode_ParameterOutOfRange; | |
289 } | |
290 } | |
291 | |
292 | |
293 static OrthancPluginErrorCode ReadAnswerMatchingResource(OrthancPluginDatabaseTransaction* transaction, | |
294 OrthancPluginMatchingResource* target, | |
295 uint32_t index) | |
296 { | |
297 const Output& that = *reinterpret_cast<const Output*>(transaction); | |
298 | |
299 if (index < that.matches_.size()) | |
300 { | |
301 *target = that.matches_[index]; | |
302 return OrthancPluginErrorCode_Success; | |
303 } | |
304 else | |
305 { | |
306 return OrthancPluginErrorCode_ParameterOutOfRange; | |
307 } | |
308 } | |
309 | |
310 | |
311 static OrthancPluginErrorCode ReadAnswerMetadata(OrthancPluginDatabaseTransaction* transaction, | |
312 int32_t* metadata, | |
313 const char** value, | |
314 uint32_t index) | |
315 { | |
316 const Output& that = *reinterpret_cast<const Output*>(transaction); | |
317 | |
318 if (index < that.metadata_.size()) | |
319 { | |
320 const Metadata& tmp = that.metadata_[index]; | |
321 *metadata = tmp.metadata; | |
322 *value = tmp.value; | |
323 return OrthancPluginErrorCode_Success; | |
324 } | |
325 else | |
326 { | |
327 return OrthancPluginErrorCode_ParameterOutOfRange; | |
328 } | |
329 } | |
330 | |
331 | |
332 static OrthancPluginErrorCode ReadAnswerString(OrthancPluginDatabaseTransaction* transaction, | |
333 const char** target, | |
334 uint32_t index) | |
335 { | |
336 const Output& that = *reinterpret_cast<const Output*>(transaction); | |
337 | |
338 if (index < that.stringAnswers_.size()) | |
339 { | |
340 *target = that.stringAnswers_[index].c_str(); | |
193 return OrthancPluginErrorCode_Success; | 341 return OrthancPluginErrorCode_Success; |
194 } | 342 } |
195 else | 343 else |
196 { | 344 { |
197 return OrthancPluginErrorCode_ParameterOutOfRange; | 345 return OrthancPluginErrorCode_ParameterOutOfRange; |
356 } | 504 } |
357 | 505 |
358 | 506 |
359 virtual void AnswerMatchingResource(const std::string& resourceId) ORTHANC_OVERRIDE | 507 virtual void AnswerMatchingResource(const std::string& resourceId) ORTHANC_OVERRIDE |
360 { | 508 { |
361 | 509 SetupAnswerType(_OrthancPluginDatabaseAnswerType_MatchingResource); |
510 | |
511 OrthancPluginMatchingResource match; | |
512 match.resourceId = StoreString(resourceId); | |
513 match.someInstanceId = NULL; | |
514 | |
515 matches_.push_back(match); | |
362 } | 516 } |
363 | 517 |
364 | 518 |
365 virtual void AnswerMatchingResource(const std::string& resourceId, | 519 virtual void AnswerMatchingResource(const std::string& resourceId, |
366 const std::string& someInstanceId) ORTHANC_OVERRIDE | 520 const std::string& someInstanceId) ORTHANC_OVERRIDE |
367 { | 521 { |
368 | 522 SetupAnswerType(_OrthancPluginDatabaseAnswerType_MatchingResource); |
523 | |
524 OrthancPluginMatchingResource match; | |
525 match.resourceId = StoreString(resourceId); | |
526 match.someInstanceId = StoreString(someInstanceId); | |
527 | |
528 matches_.push_back(match); | |
529 } | |
530 | |
531 | |
532 void AnswerIntegers32(const std::list<int32_t>& values) | |
533 { | |
534 SetupAnswerType(_OrthancPluginDatabaseAnswerType_Int32); | |
535 | |
536 integers32_.reserve(values.size()); | |
537 std::copy(std::begin(values), std::end(values), std::back_inserter(integers32_)); | |
538 } | |
539 | |
540 | |
541 void AnswerIntegers64(const std::list<int64_t>& values) | |
542 { | |
543 SetupAnswerType(_OrthancPluginDatabaseAnswerType_Int64); | |
544 | |
545 integers64_.reserve(values.size()); | |
546 std::copy(std::begin(values), std::end(values), std::back_inserter(integers64_)); | |
547 } | |
548 | |
549 | |
550 void AnswerMetadata(int32_t metadata, | |
551 const std::string& value) | |
552 { | |
553 SetupAnswerType(_OrthancPluginDatabaseAnswerType_Metadata); | |
554 | |
555 Metadata tmp; | |
556 tmp.metadata = metadata; | |
557 tmp.value = StoreString(value); | |
558 | |
559 metadata_.push_back(tmp); | |
369 } | 560 } |
370 }; | 561 }; |
371 | 562 |
372 | 563 |
373 class Factory : public IDatabaseBackendOutput::IFactory | 564 class Factory : public IDatabaseBackendOutput::IFactory |
381 { | 572 { |
382 return new Output; | 573 return new Output; |
383 } | 574 } |
384 }; | 575 }; |
385 | 576 |
577 | |
578 class Transaction : public boost::noncopyable | |
579 { | |
580 private: | |
581 IDatabaseBackend& backend_; | |
582 std::unique_ptr<Output> output_; | |
583 | |
584 public: | |
585 Transaction(IDatabaseBackend& backend) : | |
586 backend_(backend), | |
587 output_(new Output) | |
588 { | |
589 } | |
590 | |
591 IDatabaseBackend& GetBackend() const | |
592 { | |
593 return backend_; | |
594 } | |
595 | |
596 Output& GetOutput() const | |
597 { | |
598 return *output_; | |
599 } | |
600 | |
601 OrthancPluginContext* GetContext() const | |
602 { | |
603 return backend_.GetContext(); | |
604 } | |
605 }; | |
606 | |
386 | 607 |
387 static void Register() | 608 static OrthancPluginErrorCode Open(void* database) |
388 { | 609 { |
389 OrthancPluginDatabaseBackendV3 backend; | 610 IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(database); |
390 memset(&backend, 0, sizeof(backend)); | 611 |
391 | 612 try |
392 backend.readAnswersCount = Output::ReadAnswersCount; | 613 { |
393 backend.readAnswerAttachment = Output::ReadAnswerAttachment; | 614 backend->Open(); |
394 backend.readAnswerChange = Output::ReadAnswerChange; | 615 return OrthancPluginErrorCode_Success; |
395 backend.readAnswerDicomTag = Output::ReadAnswerDicomTag; | 616 } |
396 backend.readAnswerExportedResource = Output::ReadAnswerExportedResource; | 617 ORTHANC_PLUGINS_DATABASE_CATCH(backend->GetContext()); |
397 | 618 } |
398 backend.readEventsCount = Output::ReadEventsCount; | 619 |
399 backend.readEvent = Output::ReadEvent; | 620 |
621 static OrthancPluginErrorCode Close(void* database) | |
622 { | |
623 IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(database); | |
624 | |
625 try | |
626 { | |
627 backend->Close(); | |
628 return OrthancPluginErrorCode_Success; | |
629 } | |
630 ORTHANC_PLUGINS_DATABASE_CATCH(backend->GetContext()); | |
631 } | |
632 | |
633 | |
634 static OrthancPluginErrorCode DestructDatabase(void* database) | |
635 { | |
636 // Nothing to delete, as this plugin uses a singleton to store backend | |
637 if (database == NULL) | |
638 { | |
639 return OrthancPluginErrorCode_InternalError; | |
640 } | |
641 else | |
642 { | |
643 return OrthancPluginErrorCode_Success; | |
644 } | |
645 } | |
646 | |
647 | |
648 static OrthancPluginErrorCode GetDatabaseVersion(void* database, | |
649 uint32_t* version) | |
650 { | |
651 IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(database); | |
652 | |
653 try | |
654 { | |
655 *version = backend->GetDatabaseVersion(); | |
656 return OrthancPluginErrorCode_Success; | |
657 } | |
658 ORTHANC_PLUGINS_DATABASE_CATCH(backend->GetContext()); | |
659 } | |
660 | |
661 | |
662 static OrthancPluginErrorCode UpgradeDatabase(void* database, | |
663 OrthancPluginStorageArea* storageArea, | |
664 uint32_t targetVersion) | |
665 { | |
666 IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(database); | |
667 | |
668 try | |
669 { | |
670 backend->UpgradeDatabase(targetVersion, storageArea); | |
671 return OrthancPluginErrorCode_Success; | |
672 } | |
673 ORTHANC_PLUGINS_DATABASE_CATCH(backend->GetContext()); | |
674 } | |
675 | |
676 | |
677 static OrthancPluginErrorCode StartTransaction(void* database, | |
678 OrthancPluginDatabaseTransaction** target /* out */, | |
679 OrthancPluginDatabaseTransactionType type) | |
680 { | |
681 IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(database); | |
682 | |
683 try | |
684 { | |
685 std::unique_ptr<Transaction> transaction(new Transaction(*backend)); | |
686 | |
687 switch (type) | |
688 { | |
689 case OrthancPluginDatabaseTransactionType_ReadOnly: | |
690 backend->StartTransaction(TransactionType_ReadOnly); | |
691 break; | |
692 | |
693 case OrthancPluginDatabaseTransactionType_ReadWrite: | |
694 backend->StartTransaction(TransactionType_ReadWrite); | |
695 break; | |
696 | |
697 default: | |
698 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
699 } | |
700 | |
701 *target = reinterpret_cast<OrthancPluginDatabaseTransaction*>(transaction.release()); | |
702 | |
703 return OrthancPluginErrorCode_Success; | |
704 } | |
705 ORTHANC_PLUGINS_DATABASE_CATCH(backend->GetContext()); | |
706 } | |
707 | |
708 | |
709 static OrthancPluginErrorCode DestructTransaction(OrthancPluginDatabaseTransaction* transaction) | |
710 { | |
711 if (transaction == NULL) | |
712 { | |
713 return OrthancPluginErrorCode_NullPointer; | |
714 } | |
715 else | |
716 { | |
717 delete reinterpret_cast<Output*>(transaction); | |
718 return OrthancPluginErrorCode_Success; | |
719 } | |
720 } | |
721 | |
722 | |
723 static OrthancPluginErrorCode Rollback(OrthancPluginDatabaseTransaction* transaction) | |
724 { | |
725 Transaction* t = reinterpret_cast<Transaction*>(transaction); | |
726 | |
727 try | |
728 { | |
729 t->GetBackend().RollbackTransaction(); | |
730 return OrthancPluginErrorCode_Success; | |
731 } | |
732 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext()); | |
733 } | |
734 | |
735 | |
736 static OrthancPluginErrorCode Commit(OrthancPluginDatabaseTransaction* transaction, | |
737 int64_t fileSizeDelta /* TODO - not used? */) | |
738 { | |
739 Transaction* t = reinterpret_cast<Transaction*>(transaction); | |
740 | |
741 try | |
742 { | |
743 t->GetBackend().CommitTransaction(); | |
744 return OrthancPluginErrorCode_Success; | |
745 } | |
746 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext()); | |
747 } | |
748 | |
749 | |
750 static OrthancPluginErrorCode AddAttachment(OrthancPluginDatabaseTransaction* transaction, | |
751 int64_t id, | |
752 const OrthancPluginAttachment* attachment) | |
753 { | |
754 Transaction* t = reinterpret_cast<Transaction*>(transaction); | |
755 | |
756 try | |
757 { | |
758 t->GetOutput().Clear(); | |
759 t->GetBackend().AddAttachment(id, *attachment); | |
760 return OrthancPluginErrorCode_Success; | |
761 } | |
762 ORTHANC_PLUGINS_DATABASE_CATCH(t->GetContext()); | |
763 } | |
764 | |
765 | |
766 | |
767 static void RegisterV3(IDatabaseBackend& database) | |
768 { | |
769 OrthancPluginDatabaseBackendV3 params; | |
770 memset(¶ms, 0, sizeof(params)); | |
771 | |
772 params.readAnswersCount = Output::ReadAnswersCount; | |
773 params.readAnswerAttachment = Output::ReadAnswerAttachment; | |
774 params.readAnswerChange = Output::ReadAnswerChange; | |
775 params.readAnswerDicomTag = Output::ReadAnswerDicomTag; | |
776 params.readAnswerExportedResource = Output::ReadAnswerExportedResource; | |
777 params.readAnswerInt32 = Output::ReadAnswerInt32; | |
778 params.readAnswerInt64 = Output::ReadAnswerInt64; | |
779 params.readAnswerMatchingResource = Output::ReadAnswerMatchingResource; | |
780 params.readAnswerMetadata = Output::ReadAnswerMetadata; | |
781 params.readAnswerString = Output::ReadAnswerString; | |
782 | |
783 params.readEventsCount = Output::ReadEventsCount; | |
784 params.readEvent = Output::ReadEvent; | |
785 | |
786 params.open = Open; | |
787 params.close = Close; | |
788 params.destructDatabase = DestructDatabase; | |
789 params.getDatabaseVersion = GetDatabaseVersion; | |
790 params.upgradeDatabase = UpgradeDatabase; | |
791 params.startTransaction = StartTransaction; | |
792 params.destructTransaction = DestructTransaction; | |
793 params.rollback = Rollback; | |
794 params.commit = Commit; | |
795 | |
796 params.addAttachment = AddAttachment; | |
797 | |
798 OrthancPluginContext* context = database.GetContext(); | |
799 | |
800 if (OrthancPluginRegisterDatabaseBackendV3(context, ¶ms, sizeof(params), &database) != OrthancPluginErrorCode_Success) | |
801 { | |
802 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to register the database backend"); | |
803 } | |
804 | |
805 database.SetOutputFactory(new Factory); | |
400 } | 806 } |
401 } | 807 } |
402 | 808 |
403 # endif | 809 # endif |
404 #endif | 810 #endif |
440 { | 846 { |
441 /* Create the database back-end */ | 847 /* Create the database back-end */ |
442 backend_.reset(new OrthancDatabases::SQLiteIndex(context, "index.db")); // TODO parameter | 848 backend_.reset(new OrthancDatabases::SQLiteIndex(context, "index.db")); // TODO parameter |
443 | 849 |
444 /* Register the SQLite index into Orthanc */ | 850 /* Register the SQLite index into Orthanc */ |
445 OrthancDatabases::DatabaseBackendAdapterV2::Register(context, *backend_); | 851 |
852 bool hasLoadedV3 = false; | |
853 | |
854 #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 | |
855 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 0) | |
856 if (OrthancPluginCheckVersionAdvanced(context, 1, 10, 0) == 1) | |
857 { | |
858 RegisterV3(*backend_); | |
859 hasLoadedV3 = true; | |
860 } | |
861 # endif | |
862 #endif | |
863 | |
864 if (!hasLoadedV3) | |
865 { | |
866 OrthancDatabases::DatabaseBackendAdapterV2::Register(*backend_); | |
867 } | |
446 } | 868 } |
447 catch (Orthanc::OrthancException& e) | 869 catch (Orthanc::OrthancException& e) |
448 { | 870 { |
449 LOG(ERROR) << e.What(); | 871 LOG(ERROR) << e.What(); |
450 return -1; | 872 return -1; |