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(&params, 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, &params, 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;