comparison OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp @ 5796:16ce3c920f71 find-refactoring tip

rewrote SQLite find using CTEs instead of temporary tables
author Alain Mazy <am@orthanc.team>
date Thu, 19 Sep 2024 17:50:20 +0200
parents a3d283f61304
children
comparison
equal deleted inserted replaced
5795:9990b4140c1c 5796:16ce3c920f71
41 #include <stdio.h> 41 #include <stdio.h>
42 #include <boost/lexical_cast.hpp> 42 #include <boost/lexical_cast.hpp>
43 43
44 namespace Orthanc 44 namespace Orthanc
45 { 45 {
46 static std::string JoinRequestedMetadata(const FindRequest::ChildrenSpecification& childrenSpec)
47 {
48 std::set<std::string> metadataTypes;
49 for (std::set<MetadataType>::const_iterator it = childrenSpec.GetMetadata().begin(); it != childrenSpec.GetMetadata().end(); ++it)
50 {
51 metadataTypes.insert(boost::lexical_cast<std::string>(*it));
52 }
53 std::string joinedMetadataTypes;
54 Orthanc::Toolbox::JoinStrings(joinedMetadataTypes, metadataTypes, ", ");
55
56 return joinedMetadataTypes;
57 }
58
59 static std::string JoinRequestedTags(const FindRequest::ChildrenSpecification& childrenSpec)
60 {
61 // note: SQLite does not seem to support (tagGroup, tagElement) in ((x, y), (z, w)) in complex subqueries.
62 // Therefore, since we expect the requested tag list to be short, we write it as
63 // ((tagGroup = x AND tagElement = y ) OR (tagGroup = z AND tagElement = w))
64
65 std::string sql = " (";
66 std::set<std::string> tags;
67 for (std::set<DicomTag>::const_iterator it = childrenSpec.GetMainDicomTags().begin(); it != childrenSpec.GetMainDicomTags().end(); ++it)
68 {
69 tags.insert("(tagGroup = " + boost::lexical_cast<std::string>(it->GetGroup())
70 + " AND tagElement = " + boost::lexical_cast<std::string>(it->GetElement()) + ")");
71 }
72 std::string joinedTags;
73 Orthanc::Toolbox::JoinStrings(joinedTags, tags, " OR ");
74
75 sql += joinedTags + ") ";
76 return sql;
77 }
78
46 class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter 79 class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter
47 { 80 {
48 private: 81 private:
49 std::list<std::string> values_; 82 std::list<std::string> values_;
50 83
388 LookupFormatter formatter; 421 LookupFormatter formatter;
389 422
390 std::string sql; 423 std::string sql;
391 LookupFormatter::Apply(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit); 424 LookupFormatter::Apply(sql, formatter, lookup, queryLevel, labels, labelsConstraint, limit);
392 425
393 sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; 426 sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; // TODO-FIND: use a CTE (or is this method obsolete ?)
394 427
395 { 428 {
396 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); 429 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup");
397 s.Run(); 430 s.Run();
398 } 431 }
418 resourcesId.push_back(s.ColumnString(0)); 451 resourcesId.push_back(s.ColumnString(0));
419 } 452 }
420 } 453 }
421 } 454 }
422 455
456 #define C0_QUERY_ID 0
457 #define C1_INTERNAL_ID 1
458 #define C2_ROW_NUMBER 2
459 #define C3_STRING_1 3
460 #define C4_STRING_2 4
461 #define C5_STRING_3 5
462 #define C6_INT_1 6
463 #define C7_INT_2 7
464 #define C8_BIG_INT_1 8
465 #define C9_BIG_INT_2 9
466
467 #define QUERY_LOOKUP 1
468 #define QUERY_MAIN_DICOM_TAGS 2
469 #define QUERY_ATTACHMENTS 3
470 #define QUERY_METADATA 4
471 #define QUERY_LABELS 5
472 #define QUERY_PARENT_MAIN_DICOM_TAGS 10
473 #define QUERY_PARENT_IDENTIFIER 11
474 #define QUERY_PARENT_METADATA 12
475 #define QUERY_GRAND_PARENT_MAIN_DICOM_TAGS 15
476 #define QUERY_GRAND_PARENT_METADATA 16
477 #define QUERY_CHILDREN_IDENTIFIERS 20
478 #define QUERY_CHILDREN_MAIN_DICOM_TAGS 21
479 #define QUERY_CHILDREN_METADATA 22
480 #define QUERY_GRAND_CHILDREN_IDENTIFIERS 30
481 #define QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS 31
482 #define QUERY_GRAND_CHILDREN_METADATA 32
483 #define QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS 40
484 #define QUERY_ONE_INSTANCE_IDENTIFIER 50
485 #define QUERY_ONE_INSTANCE_METADATA 51
486 #define QUERY_ONE_INSTANCE_ATTACHMENTS 52
487
488 #define STRINGIFY(x) #x
489 #define TOSTRING(x) STRINGIFY(x)
490
491
423 virtual void ExecuteFind(FindResponse& response, 492 virtual void ExecuteFind(FindResponse& response,
424 const FindRequest& request, 493 const FindRequest& request,
425 const Capabilities& capabilities) ORTHANC_OVERRIDE 494 const Capabilities& capabilities) ORTHANC_OVERRIDE
426 { 495 {
496 LookupFormatter formatter;
497 std::string sql;
427 const ResourceType requestLevel = request.GetLevel(); 498 const ResourceType requestLevel = request.GetLevel();
428 std::string sql; 499
429 500 std::string lookupSql;
430 { 501 LookupFormatter::Apply(lookupSql, formatter, request);
431 // clean previous lookup table 502
432 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); 503 // base query, retrieve the ordered internalId and publicId of the selected resources
433 s.Run(); 504 sql = "WITH Lookup AS (" + lookupSql + ") ";
434 } 505
435 506 // in SQLite, all CTEs must be created at the beginning of the query, you can not define local CTE inside subqueries
436 { 507 // need one instance info ? (part 1: create the CTE)
437 // extract the resource id of interest by executing the lookup 508 if (request.GetLevel() != ResourceType_Instance &&
438 LookupFormatter formatter; 509 request.IsRetrieveOneInstanceMetadataAndAttachments())
439 LookupFormatter::Apply(sql, formatter, request); 510 {
440 511 // Here, we create a nested CTE 'OneInstance' with one instance ID to join with metadata and main
441 sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; // TODO-FIND: use a CTE 512 sql += ", OneInstance AS";
442 513
443 SQLite::Statement statement(db_, sql); 514 switch (requestLevel)
444 formatter.Bind(statement);
445 statement.Run();
446
447 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId, internalId FROM Lookup");
448 while (s.Step())
449 { 515 {
450 response.Add(new FindResponse::Resource(requestLevel, s.ColumnInt64(1), s.ColumnString(0))); 516 case ResourceType_Series:
517 {
518 sql+= " (SELECT Lookup.internalId AS parentInternalId, childLevel.publicId AS instancePublicId, childLevel.internalId AS instanceInternalId"
519 " FROM Resources AS childLevel "
520 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId) ";
521 break;
522 }
523
524 case ResourceType_Study:
525 {
526 sql+= " (SELECT Lookup.internalId AS parentInternalId, grandChildLevel.publicId AS instancePublicId, grandChildLevel.internalId AS instanceInternalId"
527 " FROM Resources AS grandChildLevel "
528 " INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId "
529 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId) ";
530 break;
531 }
532
533 case ResourceType_Patient:
534 {
535 sql+= " (SELECT Lookup.internalId AS parentInternalId, grandGrandChildLevel.publicId AS instancePublicId, grandGrandChildLevel.internalId AS instanceInternalId"
536 " FROM Resources AS grandGrandChildLevel "
537 " INNER JOIN Resources grandChildLevel ON grandGrandChildLevel.parentId = grandChildLevel.internalId "
538 " INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId "
539 " INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId) ";
540 break;
541 }
542
543 default:
544 throw OrthancException(ErrorCode_InternalError);
451 } 545 }
546 }
547
548 sql += "SELECT "
549 " " TOSTRING(QUERY_LOOKUP) " AS c0_queryId, "
550 " Lookup.internalId AS c1_internalId, "
551 " Lookup.rowNumber AS c2_rowNumber, "
552 " Lookup.publicId AS c3_string1, "
553 " NULL AS c4_string2, "
554 " NULL AS c5_string3, "
555 " NULL AS c6_int1, "
556 " NULL AS c7_int2, "
557 " NULL AS c8_big_int1, "
558 " NULL AS c9_big_int2 "
559 " FROM Lookup ";
560
561 // need one instance info ? (part 2: execute the queries)
562 if (request.GetLevel() != ResourceType_Instance &&
563 request.IsRetrieveOneInstanceMetadataAndAttachments())
564 {
565 sql += " UNION SELECT"
566 " " TOSTRING(QUERY_ONE_INSTANCE_IDENTIFIER) " AS c0_queryId, "
567 " parentInternalId AS c1_internalId, "
568 " NULL AS c2_rowNumber, "
569 " instancePublicId AS c3_string1, "
570 " NULL AS c4_string2, "
571 " NULL AS c5_string3, "
572 " NULL AS c6_int1, "
573 " NULL AS c7_int2, "
574 " instanceInternalId AS c8_big_int1, "
575 " NULL AS c9_big_int2 "
576 " FROM OneInstance ";
577
578 sql += " UNION SELECT"
579 " " TOSTRING(QUERY_ONE_INSTANCE_METADATA) " AS c0_queryId, "
580 " parentInternalId AS c1_internalId, "
581 " NULL AS c2_rowNumber, "
582 " Metadata.value AS c3_string1, "
583 " NULL AS c4_string2, "
584 " NULL AS c5_string3, "
585 " Metadata.type AS c6_int1, "
586 " NULL AS c7_int2, "
587 " NULL AS c8_big_int1, "
588 " NULL AS c9_big_int2 "
589 " FROM Metadata "
590 " INNER JOIN OneInstance ON Metadata.id = OneInstance.instanceInternalId ";
591
592 sql += " UNION SELECT"
593 " " TOSTRING(QUERY_ONE_INSTANCE_ATTACHMENTS) " AS c0_queryId, "
594 " parentInternalId AS c1_internalId, "
595 " NULL AS c2_rowNumber, "
596 " uuid AS c3_string1, "
597 " uncompressedMD5 AS c4_string2, "
598 " compressedMD5 AS c5_string3, "
599 " fileType AS c6_int1, "
600 " compressionType AS c7_int2, "
601 " compressedSize AS c8_big_int1, "
602 " uncompressedSize AS c9_big_int2 "
603 " FROM AttachedFiles "
604 " INNER JOIN OneInstance ON AttachedFiles.id = OneInstance.instanceInternalId ";
605
452 } 606 }
453 607
454 // need MainDicomTags from resource ? 608 // need MainDicomTags from resource ?
455 if (request.IsRetrieveMainDicomTags()) 609 if (request.IsRetrieveMainDicomTags())
456 { 610 {
457 sql = "SELECT id, tagGroup, tagElement, value " 611 sql += "UNION SELECT "
458 "FROM MainDicomTags " 612 " " TOSTRING(QUERY_MAIN_DICOM_TAGS) " AS c0_queryId, "
459 "INNER JOIN Lookup ON MainDicomTags.id = Lookup.internalId"; 613 " Lookup.internalId AS c1_internalId, "
460 614 " NULL AS c2_rowNumber, "
461 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 615 " value AS c3_string1, "
462 while (s.Step()) 616 " NULL AS c4_string2, "
463 { 617 " NULL AS c5_string3, "
464 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 618 " tagGroup AS c6_int1, "
465 res.AddStringDicomTag(requestLevel, 619 " tagElement AS c7_int2, "
466 static_cast<uint16_t>(s.ColumnInt(1)), 620 " NULL AS c8_big_int1, "
467 static_cast<uint16_t>(s.ColumnInt(2)), 621 " NULL AS c9_big_int2 "
468 s.ColumnString(3)); 622 "FROM MainDicomTags "
469 } 623 "INNER JOIN Lookup ON MainDicomTags.id = Lookup.internalId ";
470 } 624 }
471 625
472 626 // need resource metadata ?
627 if (request.IsRetrieveMetadata())
628 {
629 sql += "UNION SELECT "
630 " " TOSTRING(QUERY_METADATA) " AS c0_queryId, "
631 " Lookup.internalId AS c1_internalId, "
632 " NULL AS c2_rowNumber, "
633 " value AS c3_string1, "
634 " NULL AS c4_string2, "
635 " NULL AS c5_string3, "
636 " type AS c6_int1, "
637 " NULL AS c7_int2, "
638 " NULL AS c8_big_int1, "
639 " NULL AS c9_big_int2 "
640 "FROM Metadata "
641 "INNER JOIN Lookup ON Metadata.id = Lookup.internalId ";
642 }
643
644 // need resource attachments ?
645 if (request.IsRetrieveAttachments())
646 {
647 sql += "UNION SELECT "
648 " " TOSTRING(QUERY_ATTACHMENTS) " AS c0_queryId, "
649 " Lookup.internalId AS c1_internalId, "
650 " NULL AS c2_rowNumber, "
651 " uuid AS c3_string1, "
652 " uncompressedMD5 AS c4_string2, "
653 " compressedMD5 AS c5_string3, "
654 " fileType AS c6_int1, "
655 " compressionType AS c7_int2, "
656 " compressedSize AS c8_big_int1, "
657 " uncompressedSize AS c9_big_int2 "
658 "FROM AttachedFiles "
659 "INNER JOIN Lookup ON AttachedFiles.id = Lookup.internalId ";
660 }
661
662
663 // need resource labels ?
664 if (request.IsRetrieveLabels())
665 {
666 sql += "UNION SELECT "
667 " " TOSTRING(QUERY_LABELS) " AS c0_queryId, "
668 " Lookup.internalId AS c1_internalId, "
669 " NULL AS c2_rowNumber, "
670 " label AS c3_string1, "
671 " NULL AS c4_string2, "
672 " NULL AS c5_string3, "
673 " NULL AS c6_int1, "
674 " NULL AS c7_int2, "
675 " NULL AS c8_big_int1, "
676 " NULL AS c9_big_int2 "
677 "FROM Labels "
678 "INNER JOIN Lookup ON Labels.id = Lookup.internalId ";
679 }
680
473 if (requestLevel > ResourceType_Patient) 681 if (requestLevel > ResourceType_Patient)
474 { 682 {
475 // need MainDicomTags from parent ? 683 // need MainDicomTags from parent ?
476 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 1)).IsRetrieveMainDicomTags()) 684 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 1)).IsRetrieveMainDicomTags())
477 { 685 {
478 sql = "SELECT currentLevel.internalId, tagGroup, tagElement, value " 686 sql += "UNION SELECT "
479 "FROM MainDicomTags " 687 " " TOSTRING(QUERY_PARENT_MAIN_DICOM_TAGS) " AS c0_queryId, "
480 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " 688 " Lookup.internalId AS c1_internalId, "
481 "INNER JOIN Lookup ON MainDicomTags.id = currentLevel.parentId"; 689 " NULL AS c2_rowNumber, "
482 690 " value AS c3_string1, "
483 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 691 " NULL AS c4_string2, "
484 while (s.Step()) 692 " NULL AS c5_string3, "
485 { 693 " tagGroup AS c6_int1, "
486 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 694 " tagElement AS c7_int2, "
487 res.AddStringDicomTag(static_cast<ResourceType>(requestLevel - 1), 695 " NULL AS c8_big_int1, "
488 static_cast<uint16_t>(s.ColumnInt(1)), 696 " NULL AS c9_big_int2 "
489 static_cast<uint16_t>(s.ColumnInt(2)), 697 "FROM Lookup "
490 s.ColumnString(3)); 698 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
491 } 699 "INNER JOIN MainDicomTags ON MainDicomTags.id = currentLevel.parentId ";
492 } 700 }
493 701
494 // need metadata from parent ? 702 // need metadata from parent ?
495 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 1)).IsRetrieveMetadata()) 703 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 1)).IsRetrieveMetadata())
496 { 704 {
497 sql = "SELECT currentLevel.internalId, type, value " 705 sql += "UNION SELECT "
498 "FROM Metadata " 706 " " TOSTRING(QUERY_PARENT_METADATA) " AS c0_queryId, "
499 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId " 707 " Lookup.internalId AS c1_internalId, "
500 "INNER JOIN Lookup ON Metadata.id = currentLevel.parentId"; 708 " NULL AS c2_rowNumber, "
501 709 " value AS c3_string1, "
502 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 710 " NULL AS c4_string2, "
503 while (s.Step()) 711 " NULL AS c5_string3, "
712 " type AS c6_int1, "
713 " NULL AS c7_int2, "
714 " NULL AS c8_big_int1, "
715 " NULL AS c9_big_int2 "
716 "FROM Lookup "
717 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
718 "INNER JOIN Metadata ON Metadata.id = currentLevel.parentId ";
719 }
720
721 if (requestLevel > ResourceType_Study)
722 {
723 // need MainDicomTags from grandparent ?
724 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 2)).IsRetrieveMainDicomTags())
504 { 725 {
505 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 726 sql += "UNION SELECT "
506 res.AddMetadata(static_cast<ResourceType>(requestLevel - 1), 727 " " TOSTRING(QUERY_GRAND_PARENT_MAIN_DICOM_TAGS) " AS c0_queryId, "
507 static_cast<MetadataType>(s.ColumnInt(1)), 728 " Lookup.internalId AS c1_internalId, "
508 s.ColumnString(2)); 729 " NULL AS c2_rowNumber, "
730 " value AS c3_string1, "
731 " NULL AS c4_string2, "
732 " NULL AS c5_string3, "
733 " tagGroup AS c6_int1, "
734 " tagElement AS c7_int2, "
735 " NULL AS c8_big_int1, "
736 " NULL AS c9_big_int2 "
737 "FROM Lookup "
738 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
739 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "
740 "INNER JOIN MainDicomTags ON MainDicomTags.id = parentLevel.parentId ";
741 }
742
743 // need metadata from grandparent ?
744 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 2)).IsRetrieveMetadata())
745 {
746 sql += "UNION SELECT "
747 " " TOSTRING(QUERY_GRAND_PARENT_METADATA) " AS c0_queryId, "
748 " Lookup.internalId AS c1_internalId, "
749 " NULL AS c2_rowNumber, "
750 " value AS c3_string1, "
751 " NULL AS c4_string2, "
752 " NULL AS c5_string3, "
753 " type AS c6_int1, "
754 " NULL AS c7_int2, "
755 " NULL AS c8_big_int1, "
756 " NULL AS c9_big_int2 "
757 "FROM Lookup "
758 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
759 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "
760 "INNER JOIN Metadata ON Metadata.id = parentLevel.parentId ";
509 } 761 }
510 } 762 }
511 } 763 }
512 764
513 if (requestLevel > ResourceType_Study)
514 {
515 // need MainDicomTags from grandparent ?
516 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 2)).IsRetrieveMainDicomTags())
517 {
518 sql = "SELECT currentLevel.internalId, tagGroup, tagElement, value "
519 "FROM MainDicomTags "
520 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
521 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "
522 "INNER JOIN Lookup ON MainDicomTags.id = parentLevel.parentId";
523
524 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
525 while (s.Step())
526 {
527 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
528 res.AddStringDicomTag(static_cast<ResourceType>(requestLevel - 2),
529 static_cast<uint16_t>(s.ColumnInt(1)),
530 static_cast<uint16_t>(s.ColumnInt(2)),
531 s.ColumnString(3));
532 }
533 }
534
535 // need metadata from grandparent ?
536 if (request.GetParentSpecification(static_cast<ResourceType>(requestLevel - 2)).IsRetrieveMetadata())
537 {
538 sql = "SELECT currentLevel.internalId, type, value "
539 "FROM Metadata "
540 "INNER JOIN Resources currentLevel ON Lookup.internalId = currentLevel.internalId "
541 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "
542 "INNER JOIN Lookup ON Metadata.id = parentLevel.parentId";
543
544 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
545 while (s.Step())
546 {
547 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
548 res.AddMetadata(static_cast<ResourceType>(requestLevel - 2),
549 static_cast<MetadataType>(s.ColumnInt(1)),
550 s.ColumnString(2));
551 }
552 }
553 }
554
555 // need MainDicomTags from children ? 765 // need MainDicomTags from children ?
556 if (requestLevel <= ResourceType_Series && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1)).GetMainDicomTags().size() > 0) 766 if (requestLevel <= ResourceType_Series && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1)).GetMainDicomTags().size() > 0)
557 { 767 {
558 sql = "SELECT Lookup.internalId, tagGroup, tagElement, value " 768 sql += "UNION SELECT "
559 "FROM MainDicomTags " 769 " " TOSTRING(QUERY_CHILDREN_MAIN_DICOM_TAGS) " AS c0_queryId, "
560 "INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " 770 " Lookup.internalId AS c1_internalId, "
561 "INNER JOIN Lookup ON MainDicomTags.id = childLevel.internalId "; 771 " NULL AS c2_rowNumber, "
562 772 " value AS c3_string1, "
563 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 773 " NULL AS c4_string2, "
564 while (s.Step()) 774 " NULL AS c5_string3, "
565 { 775 " tagGroup AS c6_int1, "
566 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 776 " tagElement AS c7_int2, "
567 res.AddChildrenMainDicomTagValue(static_cast<ResourceType>(requestLevel + 1), 777 " NULL AS c8_big_int1, "
568 DicomTag(static_cast<uint16_t>(s.ColumnInt(1)), 778 " NULL AS c9_big_int2 "
569 static_cast<uint16_t>(s.ColumnInt(2))), 779 "FROM Lookup "
570 s.ColumnString(3)); 780 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId "
571 } 781 " INNER JOIN MainDicomTags ON MainDicomTags.id = childLevel.internalId AND " + JoinRequestedTags(request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1)));
572 } 782 }
573 783
574 // need MainDicomTags from grandchildren ? 784 // need MainDicomTags from grandchildren ?
575 if (requestLevel <= ResourceType_Study && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2)).GetMainDicomTags().size() > 0) 785 if (requestLevel <= ResourceType_Study && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2)).GetMainDicomTags().size() > 0)
576 { 786 {
577 sql = "SELECT Lookup.internalId, tagGroup, tagElement, value " 787 sql += "UNION SELECT "
578 "FROM MainDicomTags " 788 " " TOSTRING(QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS) " AS c0_queryId, "
579 "INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId " 789 " Lookup.internalId AS c1_internalId, "
580 "INNER JOIN Resources grandChildLevel ON childLevel.parentId = Lookup.internalId " 790 " NULL AS c2_rowNumber, "
581 "INNER JOIN Lookup ON MainDicomTags.id = grandChildLevel.internalId "; 791 " value AS c3_string1, "
582 792 " NULL AS c4_string2, "
583 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 793 " NULL AS c5_string3, "
584 while (s.Step()) 794 " tagGroup AS c6_int1, "
585 { 795 " tagElement AS c7_int2, "
586 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 796 " NULL AS c8_big_int1, "
587 res.AddChildrenMainDicomTagValue(static_cast<ResourceType>(requestLevel + 2), 797 " NULL AS c9_big_int2 "
588 DicomTag(static_cast<uint16_t>(s.ColumnInt(1)), 798 "FROM Lookup "
589 static_cast<uint16_t>(s.ColumnInt(2))), 799 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId "
590 s.ColumnString(3)); 800 " INNER JOIN Resources grandChildLevel ON childLevel.parentId = Lookup.internalId "
591 } 801 " INNER JOIN MainDicomTags ON MainDicomTags.id = grandChildLevel.internalId AND " + JoinRequestedTags(request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2)));
592 } 802 }
593 803
594 // need parent identifier ? 804 // need parent identifier ?
595 if (request.IsRetrieveParentIdentifier()) 805 if (request.IsRetrieveParentIdentifier())
596 { 806 {
597 sql = "SELECT currentLevel.internalId, parentLevel.publicId " 807 sql += "UNION SELECT "
598 "FROM Resources AS currentLevel " 808 " " TOSTRING(QUERY_PARENT_IDENTIFIER) " AS c0_queryId, "
599 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId " 809 " Lookup.internalId AS c1_internalId, "
600 "INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId "; 810 " NULL AS c2_rowNumber, "
601 811 " parentLevel.publicId AS c3_string1, "
602 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 812 " NULL AS c4_string2, "
603 while (s.Step()) 813 " NULL AS c5_string3, "
604 { 814 " NULL AS c6_int1, "
605 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 815 " NULL AS c7_int2, "
606 res.SetParentIdentifier(s.ColumnString(1)); 816 " NULL AS c8_big_int1, "
607 } 817 " NULL AS c9_big_int2 "
608 } 818 "FROM Resources AS currentLevel "
609 819 " INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId "
610 // need resource metadata ? 820 " INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId ";
611 if (request.IsRetrieveMetadata())
612 {
613 sql = "SELECT id, type, value "
614 "FROM Metadata "
615 "INNER JOIN Lookup ON Metadata.id = Lookup.internalId";
616
617 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
618 while (s.Step())
619 {
620 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
621 res.AddMetadata(requestLevel,
622 static_cast<MetadataType>(s.ColumnInt(1)),
623 s.ColumnString(2));
624 }
625 }
626
627 // need resource labels ?
628 if (request.IsRetrieveLabels())
629 {
630 sql = "SELECT Lookup.internalId, label "
631 "FROM Labels "
632 "INNER JOIN Lookup ON Labels.id = Lookup.internalId";
633
634 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
635 while (s.Step())
636 {
637 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
638 res.AddLabel(s.ColumnString(1));
639 }
640 }
641
642 // need one instance info ?
643 if (request.GetLevel() != ResourceType_Instance &&
644 request.IsRetrieveOneInstanceMetadataAndAttachments())
645 {
646 {
647 SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS OneInstance"); // TODO-FIND: use a CTE
648 s.Run();
649 }
650
651 switch (requestLevel)
652 {
653 case ResourceType_Patient:
654 {
655 SQLite::Statement s(
656 db_, SQLITE_FROM_HERE,
657 "CREATE TEMPORARY TABLE OneInstance AS "
658 "SELECT Lookup.internalId AS parentInternalId, grandGrandChildLevel.publicId AS instancePublicId, grandGrandChildLevel.internalId AS instanceInternalId "
659 "FROM Resources AS grandGrandChildLevel "
660 "INNER JOIN Resources grandChildLevel ON grandGrandChildLevel.parentId = grandChildLevel.internalId "
661 "INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId "
662 "INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId");
663 s.Run();
664 break;
665 }
666
667 case ResourceType_Study:
668 {
669 SQLite::Statement s(
670 db_, SQLITE_FROM_HERE,
671 "CREATE TEMPORARY TABLE OneInstance AS "
672 "SELECT Lookup.internalId AS parentInternalId, grandChildLevel.publicId AS instancePublicId, grandChildLevel.internalId AS instanceInternalId "
673 "FROM Resources AS grandChildLevel "
674 "INNER JOIN Resources childLevel ON grandChildLevel.parentId = childLevel.internalId "
675 "INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId");
676 s.Run();
677 break;
678 }
679
680 case ResourceType_Series:
681 {
682 SQLite::Statement s(
683 db_, SQLITE_FROM_HERE,
684 "CREATE TEMPORARY TABLE OneInstance AS "
685 "SELECT Lookup.internalId AS parentInternalId, childLevel.publicId AS instancePublicId, childLevel.internalId AS instanceInternalId "
686 "FROM Resources AS childLevel "
687 "INNER JOIN Lookup ON childLevel.parentId = Lookup.internalId GROUP BY Lookup.internalId");
688 s.Run();
689 break;
690 }
691
692 default:
693 throw OrthancException(ErrorCode_InternalError);
694 }
695
696 {
697 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT parentInternalId, instancePublicId FROM OneInstance");
698 while (s.Step())
699 {
700 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
701 res.SetOneInstancePublicId(s.ColumnString(1));
702 }
703 }
704
705 {
706 SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT OneInstance.parentInternalId, Metadata.type, Metadata.value "
707 "FROM Metadata INNER JOIN OneInstance ON Metadata.id = OneInstance.instanceInternalId");
708 while (s.Step())
709 {
710 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
711 res.AddOneInstanceMetadata(static_cast<MetadataType>(s.ColumnInt(1)), s.ColumnString(2));
712 }
713 }
714
715 {
716 SQLite::Statement s(db_, SQLITE_FROM_HERE,
717 "SELECT OneInstance.parentInternalId, AttachedFiles.fileType, AttachedFiles.uuid, "
718 "AttachedFiles.uncompressedSize, AttachedFiles.compressedSize, "
719 "AttachedFiles.compressionType, AttachedFiles.uncompressedMD5, AttachedFiles.compressedMD5 "
720 "FROM AttachedFiles INNER JOIN OneInstance ON AttachedFiles.id = OneInstance.instanceInternalId");
721 while (s.Step())
722 {
723 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
724 res.AddOneInstanceAttachment(
725 FileInfo(s.ColumnString(2), static_cast<FileContentType>(s.ColumnInt(1)),
726 s.ColumnInt64(3), s.ColumnString(6),
727 static_cast<CompressionType>(s.ColumnInt(5)),
728 s.ColumnInt64(4), s.ColumnString(7)));
729 }
730 }
731 } 821 }
732 822
733 // need children metadata ? 823 // need children metadata ?
734 if (requestLevel <= ResourceType_Series && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1)).GetMetadata().size() > 0) 824 if (requestLevel <= ResourceType_Series && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1)).GetMetadata().size() > 0)
735 { 825 {
736 sql = "SELECT Lookup.internalId, type, value " 826 sql += "UNION SELECT "
737 "FROM Metadata " 827 " " TOSTRING(QUERY_CHILDREN_METADATA) " AS c0_queryId, "
738 "INNER JOIN Lookup ON Lookup.internalId = childLevel.parentId " 828 " Lookup.internalId AS c1_internalId, "
739 "INNER JOIN Resources childLevel ON childLevel.internalId = Metadata.id"; 829 " NULL AS c2_rowNumber, "
740 830 " value AS c3_string1, "
741 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 831 " NULL AS c4_string2, "
742 while (s.Step()) 832 " NULL AS c5_string3, "
743 { 833 " type AS c6_int1, "
744 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 834 " NULL AS c7_int2, "
745 res.AddChildrenMetadataValue(static_cast<ResourceType>(requestLevel + 1), 835 " NULL AS c8_big_int1, "
746 static_cast<MetadataType>(s.ColumnInt(1)), 836 " NULL AS c9_big_int2 "
747 s.ColumnString(2)); 837 "FROM Lookup "
748 } 838 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId "
839 " INNER JOIN Metadata ON Metadata.id = childLevel.internalId AND Metadata.type IN (" + JoinRequestedMetadata(request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 1))) + ") ";
749 } 840 }
750 841
751 // need grandchildren metadata ? 842 // need grandchildren metadata ?
752 if (requestLevel <= ResourceType_Study && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2)).GetMetadata().size() > 0) 843 if (requestLevel <= ResourceType_Study && request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2)).GetMetadata().size() > 0)
753 { 844 {
754 sql = "SELECT Lookup.internalId, type, value " 845 sql += "UNION SELECT "
755 "FROM Metadata " 846 " " TOSTRING(QUERY_GRAND_CHILDREN_METADATA) " AS c0_queryId, "
756 "INNER JOIN Lookup ON Lookup.internalId = childLevel.parentId " 847 " Lookup.internalId AS c1_internalId, "
757 "INNER JOIN Resources childLevel ON childLevel.internalId = grandChildLevel.parentId " 848 " NULL AS c2_rowNumber, "
758 "INNER JOIN Resources grandChildLevel ON grandChildLevel.internalId = Metadata.id"; 849 " value AS c3_string1, "
759 850 " NULL AS c4_string2, "
760 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 851 " NULL AS c5_string3, "
761 while (s.Step()) 852 " type AS c6_int1, "
762 { 853 " NULL AS c7_int2, "
763 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 854 " NULL AS c8_big_int1, "
764 res.AddChildrenMetadataValue(static_cast<ResourceType>(requestLevel + 2), 855 " NULL AS c9_big_int2 "
765 static_cast<MetadataType>(s.ColumnInt(1)), 856 "FROM Lookup "
766 s.ColumnString(2)); 857 " INNER JOIN Resources childLevel ON childLevel.parentId = Lookup.internalId "
767 } 858 " INNER JOIN Resources grandChildLevel ON childLevel.parentId = Lookup.internalId "
859 " INNER JOIN Metadata ON Metadata.id = grandChildLevel.internalId AND Metadata.type IN (" + JoinRequestedMetadata(request.GetChildrenSpecification(static_cast<ResourceType>(requestLevel + 2))) + ") ";
768 } 860 }
769 861
770 // need children identifiers ? 862 // need children identifiers ?
771 if ((requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Study).IsRetrieveIdentifiers()) || 863 if ((requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Study).IsRetrieveIdentifiers()) ||
772 (requestLevel == ResourceType_Study && request.GetChildrenSpecification(ResourceType_Series).IsRetrieveIdentifiers()) || 864 (requestLevel == ResourceType_Study && request.GetChildrenSpecification(ResourceType_Series).IsRetrieveIdentifiers()) ||
773 (requestLevel == ResourceType_Series && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers())) 865 (requestLevel == ResourceType_Series && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers()))
774 { 866 {
775 sql = "SELECT Lookup.internalId, childLevel.publicId " 867 sql += "UNION SELECT "
776 "FROM Resources AS currentLevel " 868 " " TOSTRING(QUERY_CHILDREN_IDENTIFIERS) " AS c0_queryId, "
777 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId " 869 " Lookup.internalId AS c1_internalId, "
778 "INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId "; 870 " NULL AS c2_rowNumber, "
779 871 " childLevel.publicId AS c3_string1, "
780 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 872 " NULL AS c4_string2, "
781 while (s.Step()) 873 " NULL AS c5_string3, "
782 { 874 " NULL AS c6_int1, "
783 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 875 " NULL AS c7_int2, "
784 res.AddChildIdentifier(static_cast<ResourceType>(requestLevel + 1), s.ColumnString(1)); 876 " NULL AS c8_big_int1, "
785 } 877 " NULL AS c9_big_int2 "
878 "FROM Resources AS currentLevel "
879 " INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId "
880 " INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId ";
786 } 881 }
787 882
788 // need grandchildren identifiers ? 883 // need grandchildren identifiers ?
789 if ((requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Series).IsRetrieveIdentifiers()) || 884 if ((requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Series).IsRetrieveIdentifiers()) ||
790 (requestLevel == ResourceType_Study && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers())) 885 (requestLevel == ResourceType_Study && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers()))
791 { 886 {
792 sql = "SELECT Lookup.internalId, grandChildLevel.publicId " 887 sql += "UNION SELECT "
888 " " TOSTRING(QUERY_GRAND_CHILDREN_IDENTIFIERS) " AS c0_queryId, "
889 " Lookup.internalId AS c1_internalId, "
890 " NULL AS c2_rowNumber, "
891 " grandChildLevel.publicId AS c3_string1, "
892 " NULL AS c4_string2, "
893 " NULL AS c5_string3, "
894 " NULL AS c6_int1, "
895 " NULL AS c7_int2, "
896 " NULL AS c8_big_int1, "
897 " NULL AS c9_big_int2 "
793 "FROM Resources AS currentLevel " 898 "FROM Resources AS currentLevel "
794 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId " 899 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId "
795 "INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId " 900 "INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId "
796 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId "; 901 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId ";
797
798 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
799 while (s.Step())
800 {
801 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
802 res.AddChildIdentifier(static_cast<ResourceType>(requestLevel + 2), s.ColumnString(1));
803 }
804 } 902 }
805 903
806 // need grandgrandchildren identifiers ? 904 // need grandgrandchildren identifiers ?
807 if (requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers()) 905 if (requestLevel == ResourceType_Patient && request.GetChildrenSpecification(ResourceType_Instance).IsRetrieveIdentifiers())
808 { 906 {
809 sql = "SELECT Lookup.internalId, grandGrandChildLevel.publicId " 907 sql += "UNION SELECT "
908 " " TOSTRING(QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS) " AS c0_queryId, "
909 " Lookup.internalId AS c1_internalId, "
910 " NULL AS c2_rowNumber, "
911 " grandGrandChildLevel.publicId AS c3_string1, "
912 " NULL AS c4_string2, "
913 " NULL AS c5_string3, "
914 " NULL AS c6_int1, "
915 " NULL AS c7_int2, "
916 " NULL AS c8_big_int1, "
917 " NULL AS c9_big_int2 "
810 "FROM Resources AS currentLevel " 918 "FROM Resources AS currentLevel "
811 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId " 919 "INNER JOIN Lookup ON currentLevel.internalId = Lookup.internalId "
812 "INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId " 920 "INNER JOIN Resources childLevel ON currentLevel.internalId = childLevel.parentId "
813 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId " 921 "INNER JOIN Resources grandChildLevel ON childLevel.internalId = grandChildLevel.parentId "
814 "INNER JOIN Resources grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId "; 922 "INNER JOIN Resources grandGrandChildLevel ON grandChildLevel.internalId = grandGrandChildLevel.parentId ";
815 923 }
816 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql); 924
817 while (s.Step()) 925
926 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 !
927
928 SQLite::Statement s(db_, SQLITE_FROM_HERE_DYNAMIC(sql), sql);
929 formatter.Bind(s);
930
931 while (s.Step())
932 {
933 int queryId = s.ColumnInt(C0_QUERY_ID);
934 int64_t internalId = s.ColumnInt64(C1_INTERNAL_ID);
935
936 // LOG(INFO) << queryId << ": " << internalId;
937 // continue;
938
939 assert(queryId == QUERY_LOOKUP || response.HasResource(internalId)); // the QUERY_LOOKUP must be read first and must create the response before any other query tries to populate the fields
940
941 switch (queryId)
818 { 942 {
819 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0)); 943 case QUERY_LOOKUP:
820 res.AddChildIdentifier(ResourceType_Instance, s.ColumnString(1)); 944 response.Add(new FindResponse::Resource(requestLevel, internalId, s.ColumnString(C3_STRING_1)));
945 break;
946
947 case QUERY_LABELS:
948 {
949 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
950 res.AddLabel(s.ColumnString(C3_STRING_1));
951 }; break;
952
953 case QUERY_ATTACHMENTS:
954 {
955 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
956 FileInfo file(s.ColumnString(C3_STRING_1), static_cast<FileContentType>(s.ColumnInt(C6_INT_1)),
957 s.ColumnInt64(C8_BIG_INT_1), s.ColumnString(C4_STRING_2),
958 static_cast<CompressionType>(s.ColumnInt(C7_INT_2)),
959 s.ColumnInt64(C9_BIG_INT_2), s.ColumnString(C5_STRING_3));
960 res.AddAttachment(file);
961 }; break;
962
963 case QUERY_MAIN_DICOM_TAGS:
964 {
965 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
966 res.AddStringDicomTag(requestLevel,
967 static_cast<uint16_t>(s.ColumnInt(C6_INT_1)),
968 static_cast<uint16_t>(s.ColumnInt(C7_INT_2)),
969 s.ColumnString(C3_STRING_1));
970 }; break;
971
972 case QUERY_PARENT_MAIN_DICOM_TAGS:
973 {
974 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
975 res.AddStringDicomTag(static_cast<ResourceType>(requestLevel - 1),
976 static_cast<uint16_t>(s.ColumnInt(C6_INT_1)),
977 static_cast<uint16_t>(s.ColumnInt(C7_INT_2)),
978 s.ColumnString(C3_STRING_1));
979 }; break;
980
981 case QUERY_GRAND_PARENT_MAIN_DICOM_TAGS:
982 {
983 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
984 res.AddStringDicomTag(static_cast<ResourceType>(requestLevel - 2),
985 static_cast<uint16_t>(s.ColumnInt(C6_INT_1)),
986 static_cast<uint16_t>(s.ColumnInt(C7_INT_2)),
987 s.ColumnString(C3_STRING_1));
988 }; break;
989
990 case QUERY_CHILDREN_MAIN_DICOM_TAGS:
991 {
992 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
993 res.AddChildrenMainDicomTagValue(static_cast<ResourceType>(requestLevel + 1),
994 DicomTag(static_cast<uint16_t>(s.ColumnInt(C6_INT_1)), static_cast<uint16_t>(s.ColumnInt(C7_INT_2))),
995 s.ColumnString(C3_STRING_1));
996 }; break;
997
998 case QUERY_GRAND_CHILDREN_MAIN_DICOM_TAGS:
999 {
1000 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1001 res.AddChildrenMainDicomTagValue(static_cast<ResourceType>(requestLevel + 2),
1002 DicomTag(static_cast<uint16_t>(s.ColumnInt(C6_INT_1)), static_cast<uint16_t>(s.ColumnInt(C7_INT_2))),
1003 s.ColumnString(C3_STRING_1));
1004 }; break;
1005
1006 case QUERY_METADATA:
1007 {
1008 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1009 res.AddMetadata(static_cast<ResourceType>(requestLevel),
1010 static_cast<MetadataType>(s.ColumnInt(C6_INT_1)),
1011 s.ColumnString(C3_STRING_1));
1012 }; break;
1013
1014 case QUERY_PARENT_METADATA:
1015 {
1016 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1017 res.AddMetadata(static_cast<ResourceType>(requestLevel - 1),
1018 static_cast<MetadataType>(s.ColumnInt(C6_INT_1)),
1019 s.ColumnString(C3_STRING_1));
1020 }; break;
1021
1022 case QUERY_GRAND_PARENT_METADATA:
1023 {
1024 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1025 res.AddMetadata(static_cast<ResourceType>(requestLevel - 2),
1026 static_cast<MetadataType>(s.ColumnInt(C6_INT_1)),
1027 s.ColumnString(C3_STRING_1));
1028 }; break;
1029
1030 case QUERY_CHILDREN_METADATA:
1031 {
1032 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1033 res.AddChildrenMetadataValue(static_cast<ResourceType>(requestLevel + 1),
1034 static_cast<MetadataType>(s.ColumnInt(C6_INT_1)),
1035 s.ColumnString(C3_STRING_1));
1036 }; break;
1037
1038 case QUERY_GRAND_CHILDREN_METADATA:
1039 {
1040 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1041 res.AddChildrenMetadataValue(static_cast<ResourceType>(requestLevel + 2),
1042 static_cast<MetadataType>(s.ColumnInt(C6_INT_1)),
1043 s.ColumnString(C3_STRING_1));
1044 }; break;
1045
1046 case QUERY_PARENT_IDENTIFIER:
1047 {
1048 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1049 res.SetParentIdentifier(s.ColumnString(C3_STRING_1));
1050 }; break;
1051
1052 case QUERY_CHILDREN_IDENTIFIERS:
1053 {
1054 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1055 res.AddChildIdentifier(static_cast<ResourceType>(requestLevel + 1),
1056 s.ColumnString(C3_STRING_1));
1057 }; break;
1058
1059 case QUERY_GRAND_CHILDREN_IDENTIFIERS:
1060 {
1061 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1062 res.AddChildIdentifier(static_cast<ResourceType>(requestLevel + 2),
1063 s.ColumnString(C3_STRING_1));
1064 }; break;
1065
1066 case QUERY_GRAND_GRAND_CHILDREN_IDENTIFIERS:
1067 {
1068 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1069 res.AddChildIdentifier(static_cast<ResourceType>(requestLevel + 3),
1070 s.ColumnString(C3_STRING_1));
1071 }; break;
1072
1073 case QUERY_ONE_INSTANCE_IDENTIFIER:
1074 {
1075 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1076 res.SetOneInstancePublicId(s.ColumnString(C3_STRING_1));
1077 }; break;
1078
1079 case QUERY_ONE_INSTANCE_METADATA:
1080 {
1081 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1082 res.AddOneInstanceMetadata(static_cast<MetadataType>(s.ColumnInt(C6_INT_1)), s.ColumnString(C3_STRING_1));
1083 }; break;
1084
1085 case QUERY_ONE_INSTANCE_ATTACHMENTS:
1086 {
1087 FindResponse::Resource& res = response.GetResourceByInternalId(internalId);
1088 FileInfo file(s.ColumnString(C3_STRING_1), static_cast<FileContentType>(s.ColumnInt(C6_INT_1)),
1089 s.ColumnInt64(C8_BIG_INT_1), s.ColumnString(C4_STRING_2),
1090 static_cast<CompressionType>(s.ColumnInt(C7_INT_2)),
1091 s.ColumnInt64(C9_BIG_INT_2), s.ColumnString(C5_STRING_3));
1092 res.AddOneInstanceAttachment(file);
1093 }; break;
1094
1095 default:
1096 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
821 } 1097 }
822 } 1098 }
823 1099 }
824 // need resource attachments ?
825 if (request.IsRetrieveAttachments())
826 {
827 sql = "SELECT Lookup.internalId, fileType, uuid, uncompressedSize, compressedSize, compressionType, uncompressedMD5, compressedMD5 "
828 "FROM AttachedFiles "
829 "INNER JOIN Lookup ON AttachedFiles.id = Lookup.internalId";
830
831 SQLite::Statement s(db_, SQLITE_FROM_HERE, sql);
832 while (s.Step())
833 {
834 FindResponse::Resource& res = response.GetResourceByInternalId(s.ColumnInt64(0));
835 FileInfo file(s.ColumnString(2), static_cast<FileContentType>(s.ColumnInt(1)),
836 s.ColumnInt64(3), s.ColumnString(6),
837 static_cast<CompressionType>(s.ColumnInt(5)),
838 s.ColumnInt64(4), s.ColumnString(7));
839 res.AddAttachment(file);
840 }
841
842 }
843 }
844
845
846 1100
847 // From the "ICreateInstance" interface 1101 // From the "ICreateInstance" interface
848 virtual void AttachChild(int64_t parent, 1102 virtual void AttachChild(int64_t parent,
849 int64_t child) ORTHANC_OVERRIDE 1103 int64_t child) ORTHANC_OVERRIDE
850 { 1104 {