comparison OrthancServer/Sources/ServerContext.cpp @ 4627:f7d5372b59b3 db-changes

handling revisions of attachments
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 20 Apr 2021 15:11:59 +0200
parents 95ffe3b6ef7c
children 88e892e25a51
comparison
equal deleted inserted replaced
4626:686f189a903d 4627:f7d5372b59b3
751 void ServerContext::AnswerAttachment(RestApiOutput& output, 751 void ServerContext::AnswerAttachment(RestApiOutput& output,
752 const std::string& resourceId, 752 const std::string& resourceId,
753 FileContentType content) 753 FileContentType content)
754 { 754 {
755 FileInfo attachment; 755 FileInfo attachment;
756 if (!index_.LookupAttachment(attachment, resourceId, content)) 756 int64_t revision;
757 if (!index_.LookupAttachment(attachment, revision, resourceId, content))
757 { 758 {
758 throw OrthancException(ErrorCode_UnknownResource); 759 throw OrthancException(ErrorCode_UnknownResource);
759 } 760 }
760 761 else
761 StorageAccessor accessor(area_, GetMetricsRegistry()); 762 {
762 accessor.AnswerFile(output, attachment, GetFileContentMime(content)); 763 StorageAccessor accessor(area_, GetMetricsRegistry());
764 accessor.AnswerFile(output, attachment, GetFileContentMime(content));
765 }
763 } 766 }
764 767
765 768
766 void ServerContext::ChangeAttachmentCompression(const std::string& resourceId, 769 void ServerContext::ChangeAttachmentCompression(const std::string& resourceId,
767 FileContentType attachmentType, 770 FileContentType attachmentType,
771 << EnumerationToString(attachmentType) 774 << EnumerationToString(attachmentType)
772 << " of resource " << resourceId << " to " 775 << " of resource " << resourceId << " to "
773 << compression; 776 << compression;
774 777
775 FileInfo attachment; 778 FileInfo attachment;
776 if (!index_.LookupAttachment(attachment, resourceId, attachmentType)) 779 int64_t revision;
780 if (!index_.LookupAttachment(attachment, revision, resourceId, attachmentType))
777 { 781 {
778 throw OrthancException(ErrorCode_UnknownResource); 782 throw OrthancException(ErrorCode_UnknownResource);
779 } 783 }
780 784
781 if (attachment.GetCompressionType() == compression) 785 if (attachment.GetCompressionType() == compression)
792 FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(), 796 FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(),
793 content.size(), attachmentType, compression, storeMD5_); 797 content.size(), attachmentType, compression, storeMD5_);
794 798
795 try 799 try
796 { 800 {
797 StoreStatus status = index_.AddAttachment(modified, resourceId); 801 int64_t newRevision; // ignored
802 StoreStatus status = index_.AddAttachment(newRevision, modified, resourceId, true, revision);
798 if (status != StoreStatus_Success) 803 if (status != StoreStatus_Success)
799 { 804 {
800 accessor.Remove(modified); 805 accessor.Remove(modified);
801 throw OrthancException(ErrorCode_Database); 806 throw OrthancException(ErrorCode_Database);
802 } 807 }
831 * area does not support range reads, or it "StorageCompression" 836 * area does not support range reads, or it "StorageCompression"
832 * is enabled). Simply return this attachment. 837 * is enabled). Simply return this attachment.
833 **/ 838 **/
834 839
835 FileInfo attachment; 840 FileInfo attachment;
836 841 int64_t revision;
837 if (index_.LookupAttachment(attachment, instancePublicId, FileContentType_DicomUntilPixelData)) 842
843 if (index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomUntilPixelData))
838 { 844 {
839 std::string dicom; 845 std::string dicom;
840 846
841 { 847 {
842 StorageAccessor accessor(area_, GetMetricsRegistry()); 848 StorageAccessor accessor(area_, GetMetricsRegistry());
891 } 897 }
892 898
893 899
894 if (hasPixelDataOffset && 900 if (hasPixelDataOffset &&
895 area_.HasReadRange() && 901 area_.HasReadRange() &&
896 index_.LookupAttachment(attachment, instancePublicId, FileContentType_Dicom) && 902 index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_Dicom) &&
897 attachment.GetCompressionType() == CompressionType_None) 903 attachment.GetCompressionType() == CompressionType_None)
898 { 904 {
899 /** 905 /**
900 * CASE 2: The pixel data offset is known, AND that a range read 906 * CASE 2: The pixel data offset is known, AND that a range read
901 * can be used to retrieve the truncated DICOM file. Note that 907 * can be used to retrieve the truncated DICOM file. Note that
920 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength); 926 OrthancConfiguration::DefaultDicomDatasetToJson(result, parsed, ignoreTagLength);
921 InjectEmptyPixelData(result); 927 InjectEmptyPixelData(result);
922 } 928 }
923 } 929 }
924 else if (ignoreTagLength.empty() && 930 else if (ignoreTagLength.empty() &&
925 index_.LookupAttachment(attachment, instancePublicId, FileContentType_DicomAsJson)) 931 index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_DicomAsJson))
926 { 932 {
927 /** 933 /**
928 * CASE 3: This instance was created using Orthanc <= 934 * CASE 3: This instance was created using Orthanc <=
929 * 1.9.0. Reuse the old "DICOM-as-JSON" attachment if available. 935 * 1.9.0. Reuse the old "DICOM-as-JSON" attachment if available.
930 * This is for backward compatibility: A call to 936 * This is for backward compatibility: A call to
976 boost::lexical_cast<std::string>(pixelDataOffset)); 982 boost::lexical_cast<std::string>(pixelDataOffset));
977 983
978 if (!area_.HasReadRange() || 984 if (!area_.HasReadRange() ||
979 compressionEnabled_) 985 compressionEnabled_)
980 { 986 {
981 AddAttachment(instancePublicId, FileContentType_DicomUntilPixelData, 987 int64_t newRevision;
982 dicom.empty() ? NULL: dicom.c_str(), pixelDataOffset); 988 AddAttachment(newRevision, instancePublicId, FileContentType_DicomUntilPixelData,
989 dicom.empty() ? NULL: dicom.c_str(), pixelDataOffset,
990 false /* no old revision */, -1 /* dummy revision */);
983 } 991 }
984 } 992 }
985 } 993 }
986 } 994 }
987 } 995 }
997 1005
998 1006
999 void ServerContext::ReadDicom(std::string& dicom, 1007 void ServerContext::ReadDicom(std::string& dicom,
1000 const std::string& instancePublicId) 1008 const std::string& instancePublicId)
1001 { 1009 {
1002 ReadAttachment(dicom, instancePublicId, FileContentType_Dicom, true /* uncompress */); 1010 int64_t revision;
1011 ReadAttachment(dicom, revision, instancePublicId, FileContentType_Dicom, true /* uncompress */);
1003 } 1012 }
1004 1013
1005 1014
1006 bool ServerContext::ReadDicomUntilPixelData(std::string& dicom, 1015 bool ServerContext::ReadDicomUntilPixelData(std::string& dicom,
1007 const std::string& instancePublicId) 1016 const std::string& instancePublicId)
1010 { 1019 {
1011 return false; 1020 return false;
1012 } 1021 }
1013 1022
1014 FileInfo attachment; 1023 FileInfo attachment;
1015 if (!index_.LookupAttachment(attachment, instancePublicId, FileContentType_Dicom)) 1024 int64_t revision; // Ignored
1025 if (!index_.LookupAttachment(attachment, revision, instancePublicId, FileContentType_Dicom))
1016 { 1026 {
1017 throw OrthancException(ErrorCode_InternalError, 1027 throw OrthancException(ErrorCode_InternalError,
1018 "Unable to read the DICOM file of instance " + instancePublicId); 1028 "Unable to read the DICOM file of instance " + instancePublicId);
1019 } 1029 }
1020 1030
1021 std::string s; 1031 std::string s;
1022 int64_t revision; // Ignored
1023 1032
1024 if (attachment.GetCompressionType() == CompressionType_None && 1033 if (attachment.GetCompressionType() == CompressionType_None &&
1025 index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance, 1034 index_.LookupMetadata(s, revision, instancePublicId, ResourceType_Instance,
1026 MetadataType_Instance_PixelDataOffset) && 1035 MetadataType_Instance_PixelDataOffset) &&
1027 !s.empty()) 1036 !s.empty())
1044 return false; 1053 return false;
1045 } 1054 }
1046 1055
1047 1056
1048 void ServerContext::ReadAttachment(std::string& result, 1057 void ServerContext::ReadAttachment(std::string& result,
1058 int64_t& revision,
1049 const std::string& instancePublicId, 1059 const std::string& instancePublicId,
1050 FileContentType content, 1060 FileContentType content,
1051 bool uncompressIfNeeded) 1061 bool uncompressIfNeeded)
1052 { 1062 {
1053 FileInfo attachment; 1063 FileInfo attachment;
1054 if (!index_.LookupAttachment(attachment, instancePublicId, content)) 1064 if (!index_.LookupAttachment(attachment, revision, instancePublicId, content))
1055 { 1065 {
1056 throw OrthancException(ErrorCode_InternalError, 1066 throw OrthancException(ErrorCode_InternalError,
1057 "Unable to read attachment " + EnumerationToString(content) + 1067 "Unable to read attachment " + EnumerationToString(content) +
1058 " of instance " + instancePublicId); 1068 " of instance " + instancePublicId);
1059 } 1069 }
1145 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no"); 1155 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no");
1146 storeMD5_ = storeMD5; 1156 storeMD5_ = storeMD5;
1147 } 1157 }
1148 1158
1149 1159
1150 bool ServerContext::AddAttachment(const std::string& resourceId, 1160 bool ServerContext::AddAttachment(int64_t& newRevision,
1161 const std::string& resourceId,
1151 FileContentType attachmentType, 1162 FileContentType attachmentType,
1152 const void* data, 1163 const void* data,
1153 size_t size) 1164 size_t size,
1165 bool hasOldRevision,
1166 int64_t oldRevision)
1154 { 1167 {
1155 LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId; 1168 LOG(INFO) << "Adding attachment " << EnumerationToString(attachmentType) << " to resource " << resourceId;
1156 1169
1157 // TODO Should we use "gzip" instead? 1170 // TODO Should we use "gzip" instead?
1158 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None); 1171 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
1159 1172
1160 StorageAccessor accessor(area_, GetMetricsRegistry()); 1173 StorageAccessor accessor(area_, GetMetricsRegistry());
1161 FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_); 1174 FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_);
1162 1175
1163 StoreStatus status = index_.AddAttachment(attachment, resourceId); 1176 StoreStatus status = index_.AddAttachment(newRevision, attachment, resourceId, hasOldRevision, oldRevision);
1164 if (status != StoreStatus_Success) 1177 if (status != StoreStatus_Success)
1165 { 1178 {
1166 accessor.Remove(attachment); 1179 accessor.Remove(attachment);
1167 return false; 1180 return false;
1168 } 1181 }