comparison OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp @ 5301:f26ed26a7793 am-experimental

merge
author Alain Mazy <am@osimis.io>
date Wed, 24 May 2023 08:56:41 +0200
parents c04230962098 c78138dc3889
children
comparison
equal deleted inserted replaced
5300:7d913ee2f665 5301:f26ed26a7793
2035 { 2035 {
2036 remainingAncestor_["RemainingAncestor"] = Json::Value(Json::objectValue); 2036 remainingAncestor_["RemainingAncestor"] = Json::Value(Json::objectValue);
2037 remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId); 2037 remainingAncestor_["RemainingAncestor"]["Path"] = GetBasePath(remainingLevel, remainingPublicId);
2038 remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel); 2038 remainingAncestor_["RemainingAncestor"]["Type"] = EnumerationToString(remainingLevel);
2039 remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId; 2039 remainingAncestor_["RemainingAncestor"]["ID"] = remainingPublicId;
2040
2041 { // update the LastUpdate metadata of all parents
2042 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */);
2043 ResourcesContent content(true);
2044
2045 int64_t parentId = 0;
2046 if (transaction.LookupResource(parentId, remainingLevel, remainingPublicId))
2047 {
2048
2049 do
2050 {
2051 content.AddMetadata(parentId, MetadataType_LastUpdate, now);
2052 }
2053 while (transaction.LookupParent(parentId, parentId));
2054
2055 transaction.SetResourcesContent(content);
2056 }
2057 }
2040 } 2058 }
2041 else 2059 else
2042 { 2060 {
2043 remainingAncestor_["RemainingAncestor"] = Json::nullValue; 2061 remainingAncestor_["RemainingAncestor"] = Json::nullValue;
2044 } 2062 }
3097 return storeStatus_; 3115 return storeStatus_;
3098 } 3116 }
3099 3117
3100 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE 3118 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
3101 { 3119 {
3102 try 3120 IDatabaseWrapper::CreateInstanceResult status;
3103 { 3121 int64_t instanceId;
3104 IDatabaseWrapper::CreateInstanceResult status; 3122
3105 int64_t instanceId; 3123 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_,
3106 3124 hashStudy_, hashSeries_, hashInstance_);
3107 bool isNewInstance = transaction.CreateInstance(status, instanceId, hashPatient_, 3125
3108 hashStudy_, hashSeries_, hashInstance_); 3126 if (isReconstruct_ && isNewInstance)
3109 3127 {
3110 if (isReconstruct_ && isNewInstance) 3128 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex
3111 { 3129 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance
3112 // In case of reconstruct, we just want to modify the attachments and some metadata like the TransferSyntex 3130 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen.");
3113 // The DicomTags and many metadata have already been updated before we get here in ReconstructInstance 3131 }
3114 throw OrthancException(ErrorCode_InternalError, "New instance while reconstructing; this should not happen."); 3132
3115 } 3133 // Check whether this instance is already stored
3116 3134 if (!isNewInstance && !isReconstruct_)
3117 // Check whether this instance is already stored 3135 {
3118 if (!isNewInstance && !isReconstruct_) 3136 // The instance already exists
3119 { 3137 if (overwrite_)
3120 // The instance already exists 3138 {
3121 if (overwrite_) 3139 // Overwrite the old instance
3140 LOG(INFO) << "Overwriting instance: " << hashInstance_;
3141 transaction.DeleteResource(instanceId);
3142
3143 // Re-create the instance, now that the old one is removed
3144 if (!transaction.CreateInstance(status, instanceId, hashPatient_,
3145 hashStudy_, hashSeries_, hashInstance_))
3122 { 3146 {
3123 // Overwrite the old instance 3147 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen.");
3124 LOG(INFO) << "Overwriting instance: " << hashInstance_;
3125 transaction.DeleteResource(instanceId);
3126
3127 // Re-create the instance, now that the old one is removed
3128 if (!transaction.CreateInstance(status, instanceId, hashPatient_,
3129 hashStudy_, hashSeries_, hashInstance_))
3130 {
3131 throw OrthancException(ErrorCode_InternalError, "No new instance while overwriting; this should not happen.");
3132 }
3133 } 3148 }
3134 else 3149 }
3150 else
3151 {
3152 // Do nothing if the instance already exists and overwriting is disabled
3153 transaction.GetAllMetadata(instanceMetadata_, instanceId);
3154 storeStatus_ = StoreStatus_AlreadyStored;
3155 return;
3156 }
3157 }
3158
3159
3160 if (!isReconstruct_) // don't signal new resources if this is a reconstruction
3161 {
3162 // Warn about the creation of new resources. The order must be
3163 // from instance to patient.
3164
3165 // NB: In theory, could be sped up by grouping the underlying
3166 // calls to "transaction.LogChange()". However, this would only have an
3167 // impact when new patient/study/series get created, which
3168 // occurs far less often that creating new instances. The
3169 // positive impact looks marginal in practice.
3170 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_);
3171
3172 if (status.isNewSeries_)
3173 {
3174 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_);
3175 }
3176
3177 if (status.isNewStudy_)
3178 {
3179 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_);
3180 }
3181
3182 if (status.isNewPatient_)
3183 {
3184 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_);
3185 }
3186 }
3187
3188 // Ensure there is enough room in the storage for the new instance
3189 uint64_t instanceSize = 0;
3190 for (Attachments::const_iterator it = attachments_.begin();
3191 it != attachments_.end(); ++it)
3192 {
3193 instanceSize += it->GetCompressedSize();
3194 }
3195
3196 if (!isReconstruct_) // reconstruction should not affect recycling
3197 {
3198 if (maximumStorageMode_ == MaxStorageMode_Reject)
3199 {
3200 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize))
3135 { 3201 {
3136 // Do nothing if the instance already exists and overwriting is disabled 3202 storeStatus_ = StoreStatus_StorageFull;
3137 transaction.GetAllMetadata(instanceMetadata_, instanceId); 3203 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction
3138 storeStatus_ = StoreStatus_AlreadyStored;
3139 return;
3140 } 3204 }
3141 } 3205 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_))
3142 3206 {
3143 3207 storeStatus_ = StoreStatus_StorageFull;
3144 if (!isReconstruct_) // don't signal new resources if this is a reconstruction 3208 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction
3145 { 3209 }
3146 // Warn about the creation of new resources. The order must be 3210 }
3147 // from instance to patient. 3211 else
3148 3212 {
3149 // NB: In theory, could be sped up by grouping the underlying 3213 transaction.Recycle(maximumStorageSize_, maximumPatientCount_,
3150 // calls to "transaction.LogChange()". However, this would only have an 3214 instanceSize, hashPatient_ /* don't consider the current patient for recycling */);
3151 // impact when new patient/study/series get created, which 3215 }
3152 // occurs far less often that creating new instances. The 3216 }
3153 // positive impact looks marginal in practice. 3217
3154 transaction.LogChange(instanceId, ChangeType_NewInstance, ResourceType_Instance, hashInstance_); 3218 // Attach the files to the newly created instance
3219 for (Attachments::const_iterator it = attachments_.begin();
3220 it != attachments_.end(); ++it)
3221 {
3222 if (isReconstruct_)
3223 {
3224 // we are replacing attachments during a reconstruction
3225 transaction.DeleteAttachment(instanceId, it->GetContentType());
3226 }
3227
3228 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */);
3229 }
3230
3231 if (!isReconstruct_)
3232 {
3233 ResourcesContent content(true /* new resource, metadata can be set */);
3234
3235 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep)
3236 for (MetadataMap::const_iterator
3237 it = metadata_.begin(); it != metadata_.end(); ++it)
3238 {
3239 switch (it->first.first)
3240 {
3241 case ResourceType_Patient:
3242 content.AddMetadata(status.patientId_, it->first.second, it->second);
3243 break;
3244
3245 case ResourceType_Study:
3246 content.AddMetadata(status.studyId_, it->first.second, it->second);
3247 break;
3248
3249 case ResourceType_Series:
3250 content.AddMetadata(status.seriesId_, it->first.second, it->second);
3251 break;
3252
3253 case ResourceType_Instance:
3254 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3255 it->first.second, it->second);
3256 break;
3257
3258 default:
3259 throw OrthancException(ErrorCode_ParameterOutOfRange);
3260 }
3261 }
3262
3263 if (!isReconstruct_)
3264 {
3265 // Populate the tags of the newly-created resources
3266 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_);
3267 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0
3268 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1
3155 3269
3156 if (status.isNewSeries_) 3270 if (status.isNewSeries_)
3157 { 3271 {
3158 transaction.LogChange(status.seriesId_, ChangeType_NewSeries, ResourceType_Series, hashSeries_); 3272 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_);
3273 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0
3274 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1
3159 } 3275 }
3160 3276
3161 if (status.isNewStudy_) 3277 if (status.isNewStudy_)
3162 { 3278 {
3163 transaction.LogChange(status.studyId_, ChangeType_NewStudy, ResourceType_Study, hashStudy_); 3279 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_);
3280 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0
3281 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1
3164 } 3282 }
3165 3283
3166 if (status.isNewPatient_) 3284 if (status.isNewPatient_)
3167 { 3285 {
3168 transaction.LogChange(status.patientId_, ChangeType_NewPatient, ResourceType_Patient, hashPatient_); 3286 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_);
3287 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0
3288 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1
3169 } 3289 }
3170 } 3290
3291 // Attach the auto-computed metadata for the patient/study/series levels
3292 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */);
3293 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now);
3294 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now);
3295 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now);
3296
3297 if (status.isNewSeries_)
3298 {
3299 if (hasExpectedInstances_)
3300 {
3301 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances,
3302 boost::lexical_cast<std::string>(expectedInstances_));
3303 }
3304
3305 // New in Orthanc 1.9.0
3306 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet,
3307 origin_.GetRemoteAetC());
3308 }
3309 // Attach the auto-computed metadata for the instance level,
3310 // reflecting these additions into the input metadata map
3311 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3312 MetadataType_Instance_ReceptionDate, now);
3313 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet,
3314 origin_.GetRemoteAetC());
3315 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin,
3316 EnumerationToString(origin_.GetRequestOrigin()));
3317
3318 std::string s;
3319
3320 if (origin_.LookupRemoteIp(s))
3321 {
3322 // New in Orthanc 1.4.0
3323 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3324 MetadataType_Instance_RemoteIp, s);
3325 }
3326
3327 if (origin_.LookupCalledAet(s))
3328 {
3329 // New in Orthanc 1.4.0
3330 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3331 MetadataType_Instance_CalledAet, s);
3332 }
3333
3334 if (origin_.LookupHttpUsername(s))
3335 {
3336 // New in Orthanc 1.4.0
3337 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3338 MetadataType_Instance_HttpUsername, s);
3339 }
3340 }
3341
3342 // Following metadatas are also updated if reconstructing the instance.
3343 // They might be missing since they have been introduced along Orthanc versions.
3344
3345 if (hasTransferSyntax_)
3346 {
3347 // New in Orthanc 1.2.0
3348 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3349 MetadataType_Instance_TransferSyntax,
3350 GetTransferSyntaxUid(transferSyntax_));
3351 }
3352
3353 if (hasPixelDataOffset_)
3354 {
3355 // New in Orthanc 1.9.1
3356 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3357 MetadataType_Instance_PixelDataOffset,
3358 boost::lexical_cast<std::string>(pixelDataOffset_));
3359 }
3171 3360
3172 // Ensure there is enough room in the storage for the new instance 3361 const DicomValue* value;
3173 uint64_t instanceSize = 0; 3362 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL &&
3174 for (Attachments::const_iterator it = attachments_.begin(); 3363 !value->IsNull() &&
3175 it != attachments_.end(); ++it) 3364 !value->IsBinary())
3176 { 3365 {
3177 instanceSize += it->GetCompressedSize(); 3366 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3178 } 3367 MetadataType_Instance_SopClassUid, value->GetContent());
3179 3368 }
3180 if (!isReconstruct_) // reconstruction should not affect recycling 3369
3181 { 3370
3182 if (maximumStorageMode_ == MaxStorageMode_Reject) 3371 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL ||
3183 { 3372 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL)
3184 if (transaction.HasReachedMaxStorageSize(maximumStorageSize_, instanceSize)) 3373 {
3185 { 3374 if (!value->IsNull() &&
3186 storeStatus_ = StoreStatus_StorageFull;
3187 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum storage size reached"); // throw to cancel the transaction
3188 }
3189 if (transaction.HasReachedMaxPatientCount(maximumPatientCount_, hashPatient_))
3190 {
3191 storeStatus_ = StoreStatus_StorageFull;
3192 throw OrthancException(ErrorCode_FullStorage, HttpStatus_507_InsufficientStorage, "Maximum patient count reached"); // throw to cancel the transaction
3193 }
3194 }
3195 else
3196 {
3197 transaction.Recycle(maximumStorageSize_, maximumPatientCount_,
3198 instanceSize, hashPatient_ /* don't consider the current patient for recycling */);
3199 }
3200 }
3201
3202 // Attach the files to the newly created instance
3203 for (Attachments::const_iterator it = attachments_.begin();
3204 it != attachments_.end(); ++it)
3205 {
3206 if (isReconstruct_)
3207 {
3208 // we are replacing attachments during a reconstruction
3209 transaction.DeleteAttachment(instanceId, it->GetContentType());
3210 }
3211
3212 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */);
3213 }
3214
3215 if (!isReconstruct_)
3216 {
3217 ResourcesContent content(true /* new resource, metadata can be set */);
3218
3219 // Attach the user-specified metadata (in case of reconstruction, metadata_ contains all past metadata, including the system ones we want to keep)
3220 for (MetadataMap::const_iterator
3221 it = metadata_.begin(); it != metadata_.end(); ++it)
3222 {
3223 switch (it->first.first)
3224 {
3225 case ResourceType_Patient:
3226 content.AddMetadata(status.patientId_, it->first.second, it->second);
3227 break;
3228
3229 case ResourceType_Study:
3230 content.AddMetadata(status.studyId_, it->first.second, it->second);
3231 break;
3232
3233 case ResourceType_Series:
3234 content.AddMetadata(status.seriesId_, it->first.second, it->second);
3235 break;
3236
3237 case ResourceType_Instance:
3238 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3239 it->first.second, it->second);
3240 break;
3241
3242 default:
3243 throw OrthancException(ErrorCode_ParameterOutOfRange);
3244 }
3245 }
3246
3247 if (!isReconstruct_)
3248 {
3249 // Populate the tags of the newly-created resources
3250 content.AddResource(instanceId, ResourceType_Instance, dicomSummary_);
3251 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Instance)); // New in Orthanc 1.11.0
3252 SetMainDicomSequenceMetadata(content, instanceId, dicomSummary_, ResourceType_Instance); // new in Orthanc 1.11.1
3253
3254 if (status.isNewSeries_)
3255 {
3256 content.AddResource(status.seriesId_, ResourceType_Series, dicomSummary_);
3257 content.AddMetadata(status.seriesId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Series)); // New in Orthanc 1.11.0
3258 SetMainDicomSequenceMetadata(content, status.seriesId_, dicomSummary_, ResourceType_Series); // new in Orthanc 1.11.1
3259 }
3260
3261 if (status.isNewStudy_)
3262 {
3263 content.AddResource(status.studyId_, ResourceType_Study, dicomSummary_);
3264 content.AddMetadata(status.studyId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Study)); // New in Orthanc 1.11.0
3265 SetMainDicomSequenceMetadata(content, status.studyId_, dicomSummary_, ResourceType_Study); // new in Orthanc 1.11.1
3266 }
3267
3268 if (status.isNewPatient_)
3269 {
3270 content.AddResource(status.patientId_, ResourceType_Patient, dicomSummary_);
3271 content.AddMetadata(status.patientId_, MetadataType_MainDicomTagsSignature, DicomMap::GetMainDicomTagsSignature(ResourceType_Patient)); // New in Orthanc 1.11.0
3272 SetMainDicomSequenceMetadata(content, status.patientId_, dicomSummary_, ResourceType_Patient); // new in Orthanc 1.11.1
3273 }
3274
3275 // Attach the auto-computed metadata for the patient/study/series levels
3276 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */);
3277 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now);
3278 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now);
3279 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now);
3280
3281 if (status.isNewSeries_)
3282 {
3283 if (hasExpectedInstances_)
3284 {
3285 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances,
3286 boost::lexical_cast<std::string>(expectedInstances_));
3287 }
3288
3289 // New in Orthanc 1.9.0
3290 content.AddMetadata(status.seriesId_, MetadataType_RemoteAet,
3291 origin_.GetRemoteAetC());
3292 }
3293 // Attach the auto-computed metadata for the instance level,
3294 // reflecting these additions into the input metadata map
3295 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3296 MetadataType_Instance_ReceptionDate, now);
3297 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_RemoteAet,
3298 origin_.GetRemoteAetC());
3299 SetInstanceMetadata(content, instanceMetadata_, instanceId, MetadataType_Instance_Origin,
3300 EnumerationToString(origin_.GetRequestOrigin()));
3301
3302 std::string s;
3303
3304 if (origin_.LookupRemoteIp(s))
3305 {
3306 // New in Orthanc 1.4.0
3307 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3308 MetadataType_Instance_RemoteIp, s);
3309 }
3310
3311 if (origin_.LookupCalledAet(s))
3312 {
3313 // New in Orthanc 1.4.0
3314 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3315 MetadataType_Instance_CalledAet, s);
3316 }
3317
3318 if (origin_.LookupHttpUsername(s))
3319 {
3320 // New in Orthanc 1.4.0
3321 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3322 MetadataType_Instance_HttpUsername, s);
3323 }
3324 }
3325
3326 // Following metadatas are also updated if reconstructing the instance.
3327 // They might be missing since they have been introduced along Orthanc versions.
3328
3329 if (hasTransferSyntax_)
3330 {
3331 // New in Orthanc 1.2.0
3332 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3333 MetadataType_Instance_TransferSyntax,
3334 GetTransferSyntaxUid(transferSyntax_));
3335 }
3336
3337 if (hasPixelDataOffset_)
3338 {
3339 // New in Orthanc 1.9.1
3340 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3341 MetadataType_Instance_PixelDataOffset,
3342 boost::lexical_cast<std::string>(pixelDataOffset_));
3343 }
3344
3345 const DicomValue* value;
3346 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_SOP_CLASS_UID)) != NULL &&
3347 !value->IsNull() &&
3348 !value->IsBinary()) 3375 !value->IsBinary())
3349 { 3376 {
3350 SetInstanceMetadata(content, instanceMetadata_, instanceId, 3377 SetInstanceMetadata(content, instanceMetadata_, instanceId,
3351 MetadataType_Instance_SopClassUid, value->GetContent()); 3378 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent()));
3352 } 3379 }
3353 3380 }
3354 3381
3355 if ((value = dicomSummary_.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || 3382
3356 (value = dicomSummary_.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) 3383 transaction.SetResourcesContent(content);
3357 { 3384 }
3358 if (!value->IsNull() && 3385
3359 !value->IsBinary()) 3386
3360 { 3387 // Check whether the series of this new instance is now completed
3361 SetInstanceMetadata(content, instanceMetadata_, instanceId, 3388 int64_t expectedNumberOfInstances;
3362 MetadataType_Instance_IndexInSeries, Toolbox::StripSpaces(value->GetContent())); 3389 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_))
3363 } 3390 {
3364 } 3391 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances);
3365 3392 if (seriesStatus == SeriesStatus_Complete)
3393 {
3394 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_);
3395 }
3396 }
3366 3397
3367 transaction.SetResourcesContent(content); 3398 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_);
3368 } 3399 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_);
3369 3400 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_);
3370 3401
3371 // Check whether the series of this new instance is now completed 3402 // Mark the parent resources of this instance as unstable
3372 int64_t expectedNumberOfInstances; 3403 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_);
3373 if (ComputeExpectedNumberOfInstances(expectedNumberOfInstances, dicomSummary_)) 3404 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_);
3374 { 3405 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_);
3375 SeriesStatus seriesStatus = transaction.GetSeriesStatus(status.seriesId_, expectedNumberOfInstances); 3406 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize);
3376 if (seriesStatus == SeriesStatus_Complete) 3407
3377 { 3408 storeStatus_ = StoreStatus_Success;
3378 transaction.LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries_);
3379 }
3380 }
3381
3382 transaction.LogChange(status.seriesId_, ChangeType_NewChildInstance, ResourceType_Series, hashSeries_);
3383 transaction.LogChange(status.studyId_, ChangeType_NewChildInstance, ResourceType_Study, hashStudy_);
3384 transaction.LogChange(status.patientId_, ChangeType_NewChildInstance, ResourceType_Patient, hashPatient_);
3385
3386 // Mark the parent resources of this instance as unstable
3387 transaction.GetTransactionContext().MarkAsUnstable(status.seriesId_, ResourceType_Series, hashSeries_);
3388 transaction.GetTransactionContext().MarkAsUnstable(status.studyId_, ResourceType_Study, hashStudy_);
3389 transaction.GetTransactionContext().MarkAsUnstable(status.patientId_, ResourceType_Patient, hashPatient_);
3390 transaction.GetTransactionContext().SignalAttachmentsAdded(instanceSize);
3391
3392 storeStatus_ = StoreStatus_Success;
3393 }
3394 catch (OrthancException& e)
3395 {
3396 if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize)
3397 {
3398 throw; // the transaction has failed -> do not commit the current transaction (and retry)
3399 }
3400 else
3401 {
3402 LOG(ERROR) << "EXCEPTION [" << e.What() << " - " << e.GetDetails() << "]";
3403
3404 if (e.GetErrorCode() == ErrorCode_FullStorage)
3405 {
3406 throw; // do not commit the current transaction
3407 }
3408
3409 // this is an expected failure, exit normaly and commit the current transaction
3410 storeStatus_ = StoreStatus_Failure;
3411 }
3412 }
3413 } 3409 }
3414 }; 3410 };
3415 3411
3416 3412
3417 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin, 3413 Operations operations(instanceMetadata, dicomSummary, attachments, metadata, origin,
3418 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset, 3414 overwrite, hasTransferSyntax, transferSyntax, hasPixelDataOffset,
3419 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct); 3415 pixelDataOffset, maximumStorageMode, maximumStorageSize, maximumPatients, isReconstruct);
3420 Apply(operations); 3416
3421 return operations.GetStoreStatus(); 3417 try
3418 {
3419 Apply(operations);
3420 return operations.GetStoreStatus();
3421 }
3422 catch (OrthancException& e)
3423 {
3424 if (e.GetErrorCode() == ErrorCode_FullStorage)
3425 {
3426 return StoreStatus_StorageFull;
3427 }
3428 else
3429 {
3430 // the transaction has failed -> do not commit the current transaction (and retry)
3431 throw;
3432 }
3433 }
3422 } 3434 }
3423 3435
3424 3436
3425 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision, 3437 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision,
3426 const FileInfo& attachment, 3438 const FileInfo& attachment,
3474 { 3486 {
3475 ResourceType resourceType; 3487 ResourceType resourceType;
3476 int64_t resourceId; 3488 int64_t resourceId;
3477 if (!transaction.LookupResource(resourceId, resourceType, publicId_)) 3489 if (!transaction.LookupResource(resourceId, resourceType, publicId_))
3478 { 3490 {
3479 status_ = StoreStatus_Failure; // Inexistent resource 3491 throw OrthancException(ErrorCode_InexistentItem, HttpStatus_404_NotFound);
3480 } 3492 }
3481 else 3493 else
3482 { 3494 {
3483 // Possibly remove previous attachment 3495 // Possibly remove previous attachment
3484 { 3496 {