comparison OrthancServer/Sources/Database/StatelessDatabaseOperations.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 37357df3dc27
comparison
equal deleted inserted replaced
4626:686f189a903d 4627:f7d5372b59b3
918 case ResourceType_Instance: 918 case ResourceType_Instance:
919 { 919 {
920 target["Type"] = "Instance"; 920 target["Type"] = "Instance";
921 921
922 FileInfo attachment; 922 FileInfo attachment;
923 if (!transaction.LookupAttachment(attachment, internalId, FileContentType_Dicom)) 923 int64_t revision; // ignored
924 if (!transaction.LookupAttachment(attachment, revision, internalId, FileContentType_Dicom))
924 { 925 {
925 throw OrthancException(ErrorCode_InternalError); 926 throw OrthancException(ErrorCode_InternalError);
926 } 927 }
927 928
928 target["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); 929 target["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize());
1013 operations.Apply(*this, target, publicId, level); 1014 operations.Apply(*this, target, publicId, level);
1014 } 1015 }
1015 1016
1016 1017
1017 bool StatelessDatabaseOperations::LookupAttachment(FileInfo& attachment, 1018 bool StatelessDatabaseOperations::LookupAttachment(FileInfo& attachment,
1019 int64_t& revision,
1018 const std::string& instancePublicId, 1020 const std::string& instancePublicId,
1019 FileContentType contentType) 1021 FileContentType contentType)
1020 { 1022 {
1021 class Operations : public ReadOnlyOperationsT4<bool&, FileInfo&, const std::string&, FileContentType> 1023 class Operations : public ReadOnlyOperationsT5<bool&, FileInfo&, int64_t&, const std::string&, FileContentType>
1022 { 1024 {
1023 public: 1025 public:
1024 virtual void ApplyTuple(ReadOnlyTransaction& transaction, 1026 virtual void ApplyTuple(ReadOnlyTransaction& transaction,
1025 const Tuple& tuple) ORTHANC_OVERRIDE 1027 const Tuple& tuple) ORTHANC_OVERRIDE
1026 { 1028 {
1027 int64_t internalId; 1029 int64_t internalId;
1028 ResourceType type; 1030 ResourceType type;
1029 if (!transaction.LookupResource(internalId, type, tuple.get<2>())) 1031 if (!transaction.LookupResource(internalId, type, tuple.get<3>()))
1030 { 1032 {
1031 throw OrthancException(ErrorCode_UnknownResource); 1033 throw OrthancException(ErrorCode_UnknownResource);
1032 } 1034 }
1033 else if (transaction.LookupAttachment(tuple.get<1>(), internalId, tuple.get<3>())) 1035 else if (transaction.LookupAttachment(tuple.get<1>(), tuple.get<2>(), internalId, tuple.get<4>()))
1034 { 1036 {
1035 assert(tuple.get<1>().GetContentType() == tuple.get<3>()); 1037 assert(tuple.get<1>().GetContentType() == tuple.get<4>());
1036 tuple.get<0>() = true; 1038 tuple.get<0>() = true;
1037 } 1039 }
1038 else 1040 else
1039 { 1041 {
1040 tuple.get<0>() = false; 1042 tuple.get<0>() = false;
1042 } 1044 }
1043 }; 1045 };
1044 1046
1045 bool found; 1047 bool found;
1046 Operations operations; 1048 Operations operations;
1047 operations.Apply(*this, found, attachment, instancePublicId, contentType); 1049 operations.Apply(*this, found, attachment, revision, instancePublicId, contentType);
1048 return found; 1050 return found;
1049 } 1051 }
1050 1052
1051 1053
1052 void StatelessDatabaseOperations::GetAllUuids(std::list<std::string>& target, 1054 void StatelessDatabaseOperations::GetAllUuids(std::list<std::string>& target,
1545 1547
1546 for (std::set<FileContentType>::const_iterator 1548 for (std::set<FileContentType>::const_iterator
1547 it = f.begin(); it != f.end(); ++it) 1549 it = f.begin(); it != f.end(); ++it)
1548 { 1550 {
1549 FileInfo attachment; 1551 FileInfo attachment;
1550 if (transaction.LookupAttachment(attachment, resource, *it)) 1552 int64_t revision; // ignored
1553 if (transaction.LookupAttachment(attachment, revision, resource, *it))
1551 { 1554 {
1552 if (attachment.GetContentType() == FileContentType_Dicom) 1555 if (attachment.GetContentType() == FileContentType_Dicom)
1553 { 1556 {
1554 dicomDiskSize_ += attachment.GetCompressedSize(); 1557 dicomDiskSize_ += attachment.GetCompressedSize();
1555 dicomUncompressedSize_ += attachment.GetUncompressedSize(); 1558 dicomUncompressedSize_ += attachment.GetUncompressedSize();
2242 { 2245 {
2243 throw OrthancException(ErrorCode_Revision); 2246 throw OrthancException(ErrorCode_Revision);
2244 } 2247 }
2245 else 2248 else
2246 { 2249 {
2247 newRevision_ = oldRevision_ + 1; 2250 newRevision_ = expectedRevision + 1;
2248 } 2251 }
2249 } 2252 }
2250 else 2253 else
2251 { 2254 {
2252 // The metadata is not existing yet: Ignore "oldRevision" 2255 // The metadata is not existing yet: Ignore "oldRevision"
2476 Operations operations(property, shared, value); 2479 Operations operations(property, shared, value);
2477 Apply(operations); 2480 Apply(operations);
2478 } 2481 }
2479 2482
2480 2483
2481 void StatelessDatabaseOperations::DeleteAttachment(const std::string& publicId, 2484 bool StatelessDatabaseOperations::DeleteAttachment(const std::string& publicId,
2482 FileContentType type) 2485 FileContentType type,
2486 bool hasRevision,
2487 int64_t revision)
2483 { 2488 {
2484 class Operations : public IReadWriteOperations 2489 class Operations : public IReadWriteOperations
2485 { 2490 {
2486 private: 2491 private:
2487 const std::string& publicId_; 2492 const std::string& publicId_;
2488 FileContentType type_; 2493 FileContentType type_;
2494 bool hasRevision_;
2495 int64_t revision_;
2496 bool found_;
2497
2498 public:
2499 Operations(const std::string& publicId,
2500 FileContentType type,
2501 bool hasRevision,
2502 int64_t revision) :
2503 publicId_(publicId),
2504 type_(type),
2505 hasRevision_(hasRevision),
2506 revision_(revision),
2507 found_(false)
2508 {
2509 }
2510
2511 bool HasFound() const
2512 {
2513 return found_;
2514 }
2489 2515
2490 public:
2491 Operations(const std::string& publicId,
2492 FileContentType type) :
2493 publicId_(publicId),
2494 type_(type)
2495 {
2496 }
2497
2498 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE 2516 virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE
2499 { 2517 {
2500 ResourceType resourceType; 2518 ResourceType resourceType;
2501 int64_t id; 2519 int64_t id;
2502 if (!transaction.LookupResource(id, resourceType, publicId_)) 2520 if (!transaction.LookupResource(id, resourceType, publicId_))
2503 { 2521 {
2504 throw OrthancException(ErrorCode_UnknownResource); 2522 throw OrthancException(ErrorCode_UnknownResource);
2505 } 2523 }
2506 else 2524 else
2507 { 2525 {
2508 transaction.DeleteAttachment(id, type_); 2526 FileInfo info;
2527 int64_t expectedRevision;
2528 if (transaction.LookupAttachment(info, expectedRevision, id, type_))
2529 {
2530 if (hasRevision_ &&
2531 expectedRevision != revision_)
2532 {
2533 throw OrthancException(ErrorCode_Revision);
2534 }
2535
2536 found_ = true;
2537 transaction.DeleteAttachment(id, type_);
2509 2538
2510 if (IsUserContentType(type_)) 2539 if (IsUserContentType(type_))
2511 { 2540 {
2512 transaction.LogChange(id, ChangeType_UpdatedAttachment, resourceType, publicId_); 2541 transaction.LogChange(id, ChangeType_UpdatedAttachment, resourceType, publicId_);
2513 } 2542 }
2514 } 2543 }
2515 } 2544 else
2516 }; 2545 {
2517 2546 found_ = false;
2518 Operations operations(publicId, type); 2547 }
2548 }
2549 }
2550 };
2551
2552 Operations operations(publicId, type, hasRevision, revision);
2519 Apply(operations); 2553 Apply(operations);
2554 return operations.HasFound();
2520 } 2555 }
2521 2556
2522 2557
2523 void StatelessDatabaseOperations::LogChange(int64_t internalId, 2558 void StatelessDatabaseOperations::LogChange(int64_t internalId,
2524 ChangeType changeType, 2559 ChangeType changeType,
3016 3051
3017 // Attach the files to the newly created instance 3052 // Attach the files to the newly created instance
3018 for (Attachments::const_iterator it = attachments_.begin(); 3053 for (Attachments::const_iterator it = attachments_.begin();
3019 it != attachments_.end(); ++it) 3054 it != attachments_.end(); ++it)
3020 { 3055 {
3021 transaction.AddAttachment(instanceId, *it); 3056 transaction.AddAttachment(instanceId, *it, 0 /* this is the first revision */);
3022 } 3057 }
3023 3058
3024 3059
3025 { 3060 {
3026 ResourcesContent content(true /* new resource, metadata can be set */); 3061 ResourcesContent content(true /* new resource, metadata can be set */);
3217 Apply(operations); 3252 Apply(operations);
3218 return operations.GetStoreStatus(); 3253 return operations.GetStoreStatus();
3219 } 3254 }
3220 3255
3221 3256
3222 StoreStatus StatelessDatabaseOperations::AddAttachment(const FileInfo& attachment, 3257 StoreStatus StatelessDatabaseOperations::AddAttachment(int64_t& newRevision,
3258 const FileInfo& attachment,
3223 const std::string& publicId, 3259 const std::string& publicId,
3224 uint64_t maximumStorageSize, 3260 uint64_t maximumStorageSize,
3225 unsigned int maximumPatients) 3261 unsigned int maximumPatients,
3262 bool hasOldRevision,
3263 int64_t oldRevision)
3226 { 3264 {
3227 class Operations : public IReadWriteOperations 3265 class Operations : public IReadWriteOperations
3228 { 3266 {
3229 private: 3267 private:
3268 int64_t& newRevision_;
3230 StoreStatus status_; 3269 StoreStatus status_;
3231 const FileInfo& attachment_; 3270 const FileInfo& attachment_;
3232 const std::string& publicId_; 3271 const std::string& publicId_;
3233 uint64_t maximumStorageSize_; 3272 uint64_t maximumStorageSize_;
3234 unsigned int maximumPatientCount_; 3273 unsigned int maximumPatientCount_;
3235 3274 bool hasOldRevision_;
3236 public: 3275 int64_t oldRevision_;
3237 Operations(const FileInfo& attachment, 3276
3277 public:
3278 Operations(int64_t& newRevision,
3279 const FileInfo& attachment,
3238 const std::string& publicId, 3280 const std::string& publicId,
3239 uint64_t maximumStorageSize, 3281 uint64_t maximumStorageSize,
3240 unsigned int maximumPatientCount) : 3282 unsigned int maximumPatientCount,
3283 bool hasOldRevision,
3284 int64_t oldRevision) :
3285 newRevision_(newRevision),
3241 status_(StoreStatus_Failure), 3286 status_(StoreStatus_Failure),
3242 attachment_(attachment), 3287 attachment_(attachment),
3243 publicId_(publicId), 3288 publicId_(publicId),
3244 maximumStorageSize_(maximumStorageSize), 3289 maximumStorageSize_(maximumStorageSize),
3245 maximumPatientCount_(maximumPatientCount) 3290 maximumPatientCount_(maximumPatientCount),
3291 hasOldRevision_(hasOldRevision),
3292 oldRevision_(oldRevision)
3246 { 3293 {
3247 } 3294 }
3248 3295
3249 StoreStatus GetStatus() const 3296 StoreStatus GetStatus() const
3250 { 3297 {
3259 { 3306 {
3260 status_ = StoreStatus_Failure; // Inexistent resource 3307 status_ = StoreStatus_Failure; // Inexistent resource
3261 } 3308 }
3262 else 3309 else
3263 { 3310 {
3264 // Remove possible previous attachment 3311 // Possibly remove previous attachment
3265 transaction.DeleteAttachment(resourceId, attachment_.GetContentType()); 3312 {
3313 FileInfo oldFile;
3314 int64_t expectedRevision;
3315 if (transaction.LookupAttachment(oldFile, expectedRevision, resourceId, attachment_.GetContentType()))
3316 {
3317 if (hasOldRevision_ &&
3318 expectedRevision != oldRevision_)
3319 {
3320 throw OrthancException(ErrorCode_Revision);
3321 }
3322 else
3323 {
3324 newRevision_ = expectedRevision + 1;
3325 transaction.DeleteAttachment(resourceId, attachment_.GetContentType());
3326 }
3327 }
3328 else
3329 {
3330 // The attachment is not existing yet: Ignore "oldRevision"
3331 // and initialize a new sequence of revisions
3332 newRevision_ = 0;
3333 }
3334 }
3266 3335
3267 // Locate the patient of the target resource 3336 // Locate the patient of the target resource
3268 int64_t patientId = resourceId; 3337 int64_t patientId = resourceId;
3269 for (;;) 3338 for (;;)
3270 { 3339 {
3284 // Possibly apply the recycling mechanism while preserving this patient 3353 // Possibly apply the recycling mechanism while preserving this patient
3285 assert(transaction.GetResourceType(patientId) == ResourceType_Patient); 3354 assert(transaction.GetResourceType(patientId) == ResourceType_Patient);
3286 transaction.Recycle(maximumStorageSize_, maximumPatientCount_, 3355 transaction.Recycle(maximumStorageSize_, maximumPatientCount_,
3287 attachment_.GetCompressedSize(), transaction.GetPublicId(patientId)); 3356 attachment_.GetCompressedSize(), transaction.GetPublicId(patientId));
3288 3357
3289 transaction.AddAttachment(resourceId, attachment_); 3358 transaction.AddAttachment(resourceId, attachment_, newRevision_);
3290 3359
3291 if (IsUserContentType(attachment_.GetContentType())) 3360 if (IsUserContentType(attachment_.GetContentType()))
3292 { 3361 {
3293 transaction.LogChange(resourceId, ChangeType_UpdatedAttachment, resourceType, publicId_); 3362 transaction.LogChange(resourceId, ChangeType_UpdatedAttachment, resourceType, publicId_);
3294 } 3363 }
3299 } 3368 }
3300 } 3369 }
3301 }; 3370 };
3302 3371
3303 3372
3304 Operations operations(attachment, publicId, maximumStorageSize, maximumPatients); 3373 Operations operations(newRevision, attachment, publicId, maximumStorageSize, maximumPatients, hasOldRevision, oldRevision);
3305 Apply(operations); 3374 Apply(operations);
3306 return operations.GetStatus(); 3375 return operations.GetStatus();
3307 } 3376 }
3308 } 3377 }