Mercurial > hg > orthanc
changeset 4591:ff8170d17d90 db-changes
moving all accesses to databases from IDatabaseWrapper to ITransaction
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/CMakeLists.txt Mon Mar 15 15:30:42 2021 +0100 @@ -99,6 +99,7 @@ ${CMAKE_SOURCE_DIR}/Sources/Database/ResourcesContent.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/SQLiteDatabaseWrapper.cpp ${CMAKE_SOURCE_DIR}/Sources/Database/StatelessDatabaseOperations.cpp + ${CMAKE_SOURCE_DIR}/Sources/Database/VoidDatabaseListener.cpp ${CMAKE_SOURCE_DIR}/Sources/DicomInstanceOrigin.cpp ${CMAKE_SOURCE_DIR}/Sources/DicomInstanceToStore.cpp ${CMAKE_SOURCE_DIR}/Sources/EmbeddedResourceHttpHandler.cpp
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -41,13 +41,20 @@ #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" +#include "../../Sources/Database/VoidDatabaseListener.h" #include "PluginsEnumerations.h" #include <cassert> namespace Orthanc { - class OrthancPluginDatabase::Transaction : public IDatabaseWrapper::ITransaction + class OrthancPluginDatabase::Transaction : + public IDatabaseWrapper::ITransaction, + public Compatibility::ICreateInstance, + public Compatibility::IGetChildrenMetadata, + public Compatibility::ILookupResources, + public Compatibility::ILookupResourceAndParent, + public Compatibility::ISetResourcesContent { private: OrthancPluginDatabase& that_; @@ -101,7 +108,7 @@ uint64_t newDiskSize = (that_.currentDiskSize_ + diskSizeDelta); - assert(newDiskSize == that_.GetTotalCompressedSize()); + assert(newDiskSize == GetTotalCompressedSize()); CheckSuccess(that_.backend_.commitTransaction(that_.payload_)); @@ -109,6 +116,871 @@ that_.currentDiskSize_ = newDiskSize; } } + + + // From the "ILookupResources" interface + void GetAllInternalIds(std::list<int64_t>& target, + ResourceType resourceType) ORTHANC_OVERRIDE + { + if (that_.extensions_.getAllInternalIds == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin, + "The database plugin does not implement the mandatory GetAllInternalIds() extension"); + } + + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.getAllInternalIds(that_.GetContext(), that_.payload_, Plugins::Convert(resourceType))); + that_.ForwardAnswers(target); + } + + + + // From the "ILookupResources" interface + void LookupIdentifier(std::list<int64_t>& result, + ResourceType level, + const DicomTag& tag, + Compatibility::IdentifierConstraintType type, + const std::string& value) ORTHANC_OVERRIDE + { + if (that_.extensions_.lookupIdentifier3 == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin, + "The database plugin does not implement the mandatory LookupIdentifier3() extension"); + } + + OrthancPluginDicomTag tmp; + tmp.group = tag.GetGroup(); + tmp.element = tag.GetElement(); + tmp.value = value.c_str(); + + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.lookupIdentifier3(that_.GetContext(), that_.payload_, Plugins::Convert(level), + &tmp, Compatibility::Convert(type))); + that_.ForwardAnswers(result); + } + + + + /** + * Implementation of "ITransaction" + **/ + + virtual void ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) ORTHANC_OVERRIDE + { + if (that_.extensions_.lookupResources == NULL) + { + // Fallback to compatibility mode + ILookupResources::Apply + (*this, *this, resourcesId, instancesId, lookup, queryLevel, limit); + } + else + { + std::vector<OrthancPluginDatabaseConstraint> constraints; + std::vector< std::vector<const char*> > constraintsValues; + + constraints.resize(lookup.size()); + constraintsValues.resize(lookup.size()); + + for (size_t i = 0; i < lookup.size(); i++) + { + lookup[i].EncodeForPlugins(constraints[i], constraintsValues[i]); + } + + that_.ResetAnswers(); + that_.answerMatchingResources_ = &resourcesId; + that_.answerMatchingInstances_ = instancesId; + + CheckSuccess(that_.extensions_.lookupResources(that_.GetContext(), that_.payload_, lookup.size(), + (lookup.empty() ? NULL : &constraints[0]), + Plugins::Convert(queryLevel), + limit, (instancesId == NULL ? 0 : 1))); + } + } + + + bool CreateInstance(IDatabaseWrapper::CreateInstanceResult& result, + int64_t& instanceId, + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance) ORTHANC_OVERRIDE + { + if (that_.extensions_.createInstance == NULL) + { + // Fallback to compatibility mode + return ICreateInstance::Apply + (*this, result, instanceId, patient, study, series, instance); + } + else + { + OrthancPluginCreateInstanceResult output; + memset(&output, 0, sizeof(output)); + + CheckSuccess(that_.extensions_.createInstance(&output, that_.payload_, patient.c_str(), + study.c_str(), series.c_str(), instance.c_str())); + + instanceId = output.instanceId; + + if (output.isNewInstance) + { + result.isNewPatient_ = output.isNewPatient; + result.isNewStudy_ = output.isNewStudy; + result.isNewSeries_ = output.isNewSeries; + result.patientId_ = output.patientId; + result.studyId_ = output.studyId; + result.seriesId_ = output.seriesId; + return true; + } + else + { + return false; + } + } + } + + + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type) ORTHANC_OVERRIDE + { + int64_t id; + CheckSuccess(that_.backend_.createResource(&id, that_.payload_, publicId.c_str(), Plugins::Convert(type))); + return id; + } + + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) ORTHANC_OVERRIDE + { + OrthancPluginAttachment tmp; + tmp.uuid = attachment.GetUuid().c_str(); + tmp.contentType = static_cast<int32_t>(attachment.GetContentType()); + tmp.uncompressedSize = attachment.GetUncompressedSize(); + tmp.uncompressedHash = attachment.GetUncompressedMD5().c_str(); + tmp.compressionType = static_cast<int32_t>(attachment.GetCompressionType()); + tmp.compressedSize = attachment.GetCompressedSize(); + tmp.compressedHash = attachment.GetCompressedMD5().c_str(); + + CheckSuccess(that_.backend_.addAttachment(that_.payload_, id, &tmp)); + } + + + virtual void AttachChild(int64_t parent, + int64_t child) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.attachChild(that_.payload_, parent, child)); + } + + + virtual void ClearChanges() ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.clearChanges(that_.payload_)); + } + + + virtual void ClearExportedResources() ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.clearExportedResources(that_.payload_)); + } + + + virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE + { + if (that_.extensions_.clearMainDicomTags == NULL) + { + throw OrthancException(ErrorCode_DatabasePlugin, + "Your custom index plugin does not implement the mandatory ClearMainDicomTags() extension"); + } + + CheckSuccess(that_.extensions_.clearMainDicomTags(that_.payload_, id)); + } + + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteAttachment(that_.payload_, id, static_cast<int32_t>(attachment))); + } + + + virtual void DeleteMetadata(int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteMetadata(that_.payload_, id, static_cast<int32_t>(type))); + } + + + virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.deleteResource(that_.payload_, id)); + } + + + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id) ORTHANC_OVERRIDE + { + if (that_.extensions_.getAllMetadata == NULL) + { + // Fallback implementation if extension is missing + target.clear(); + + that_.ResetAnswers(); + CheckSuccess(that_.backend_.listAvailableMetadata(that_.GetContext(), that_.payload_, id)); + + if (that_.type_ != _OrthancPluginDatabaseAnswerType_None && + that_.type_ != _OrthancPluginDatabaseAnswerType_Int32) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + + target.clear(); + + if (that_.type_ == _OrthancPluginDatabaseAnswerType_Int32) + { + for (std::list<int32_t>::const_iterator + it = that_.answerInt32_.begin(); it != that_.answerInt32_.end(); ++it) + { + MetadataType type = static_cast<MetadataType>(*it); + + std::string value; + if (LookupMetadata(value, id, type)) + { + target[type] = value; + } + } + } + } + else + { + that_.ResetAnswers(); + + that_.answerMetadata_ = ⌖ + target.clear(); + + CheckSuccess(that_.extensions_.getAllMetadata(that_.GetContext(), that_.payload_, id)); + + if (that_.type_ != _OrthancPluginDatabaseAnswerType_None && + that_.type_ != _OrthancPluginDatabaseAnswerType_Metadata) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + } + + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.getAllPublicIds(that_.GetContext(), that_.payload_, Plugins::Convert(resourceType))); + that_.ForwardAnswers(target); + } + + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType, + size_t since, + size_t limit) ORTHANC_OVERRIDE + { + if (that_.extensions_.getAllPublicIdsWithLimit != NULL) + { + // This extension is available since Orthanc 0.9.4 + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.getAllPublicIdsWithLimit + (that_.GetContext(), that_.payload_, Plugins::Convert(resourceType), since, limit)); + that_.ForwardAnswers(target); + } + else + { + // The extension is not available in the database plugin, use a + // fallback implementation + target.clear(); + + if (limit == 0) + { + return; + } + + std::list<std::string> tmp; + GetAllPublicIds(tmp, resourceType); + + if (tmp.size() <= since) + { + // Not enough results => empty answer + return; + } + + std::list<std::string>::iterator current = tmp.begin(); + std::advance(current, since); + + while (limit > 0 && current != tmp.end()) + { + target.push_back(*current); + --limit; + ++current; + } + } + } + + + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + that_.answerChanges_ = ⌖ + that_.answerDone_ = &done; + done = false; + + CheckSuccess(that_.backend_.getChanges(that_.GetContext(), that_.payload_, since, maxResults)); + } + + + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.getChildrenInternalId(that_.GetContext(), that_.payload_, id)); + that_.ForwardAnswers(target); + } + + + virtual void GetChildrenMetadata(std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata) ORTHANC_OVERRIDE + { + if (that_.extensions_.getChildrenMetadata == NULL) + { + IGetChildrenMetadata::Apply(*this, target, resourceId, metadata); + } + else + { + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.getChildrenMetadata + (that_.GetContext(), that_.payload_, resourceId, static_cast<int32_t>(metadata))); + that_.ForwardAnswers(target); + } + } + + + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.getChildrenPublicId(that_.GetContext(), that_.payload_, id)); + that_.ForwardAnswers(target); + } + + + virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + that_.answerExportedResources_ = ⌖ + that_.answerDone_ = &done; + done = false; + + CheckSuccess(that_.backend_.getExportedResources(that_.GetContext(), that_.payload_, since, maxResults)); + } + + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE + { + that_.answerDoneIgnored_ = false; + + that_.ResetAnswers(); + that_.answerChanges_ = ⌖ + that_.answerDone_ = &that_.answerDoneIgnored_; + + CheckSuccess(that_.backend_.getLastChange(that_.GetContext(), that_.payload_)); + } + + + int64_t GetLastChangeIndex() ORTHANC_OVERRIDE + { + if (that_.extensions_.getLastChangeIndex == NULL) + { + // This was the default behavior in Orthanc <= 1.5.1 + // https://groups.google.com/d/msg/orthanc-users/QhzB6vxYeZ0/YxabgqpfBAAJ + return 0; + } + else + { + int64_t result = 0; + CheckSuccess(that_.extensions_.getLastChangeIndex(&result, that_.payload_)); + return result; + } + } + + + virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) ORTHANC_OVERRIDE + { + that_.answerDoneIgnored_ = false; + + that_.ResetAnswers(); + that_.answerExportedResources_ = ⌖ + that_.answerDone_ = &that_.answerDoneIgnored_; + + CheckSuccess(that_.backend_.getLastExportedResource(that_.GetContext(), that_.payload_)); + } + + + virtual void GetMainDicomTags(DicomMap& map, + int64_t id) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + that_.answerDicomMap_ = ↦ + + CheckSuccess(that_.backend_.getMainDicomTags(that_.GetContext(), that_.payload_, id)); + } + + + virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + std::string s; + + CheckSuccess(that_.backend_.getPublicId(that_.GetContext(), that_.payload_, resourceId)); + + if (!that_.ForwardSingleAnswer(s)) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + + return s; + } + + + virtual uint64_t GetResourceCount(ResourceType resourceType) ORTHANC_OVERRIDE + { + uint64_t count; + CheckSuccess(that_.backend_.getResourceCount(&count, that_.payload_, Plugins::Convert(resourceType))); + return count; + } + + + virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE + { + OrthancPluginResourceType type; + CheckSuccess(that_.backend_.getResourceType(&type, that_.payload_, resourceId)); + return Plugins::Convert(type); + } + + + virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE + { + uint64_t size; + CheckSuccess(that_.backend_.getTotalCompressedSize(&size, that_.payload_)); + return size; + } + + + virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE + { + uint64_t size; + CheckSuccess(that_.backend_.getTotalUncompressedSize(&size, that_.payload_)); + return size; + } + + + virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE + { + if (that_.fastGetTotalSize_) + { + return GetTotalCompressedSize() > threshold; + } + else + { + assert(GetTotalCompressedSize() == that_.currentDiskSize_); + return that_.currentDiskSize_ > threshold; + } + } + + + virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE + { + int32_t existing; + CheckSuccess(that_.backend_.isExistingResource(&existing, that_.payload_, internalId)); + return (existing != 0); + } + + + virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE + { + int32_t isProtected; + CheckSuccess(that_.backend_.isProtectedPatient(&isProtected, that_.payload_, internalId)); + return (isProtected != 0); + } + + + virtual void ListAvailableAttachments(std::set<FileContentType>& target, + int64_t id) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + + CheckSuccess(that_.backend_.listAvailableAttachments(that_.GetContext(), that_.payload_, id)); + + if (that_.type_ != _OrthancPluginDatabaseAnswerType_None && + that_.type_ != _OrthancPluginDatabaseAnswerType_Int32) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + + target.clear(); + + if (that_.type_ == _OrthancPluginDatabaseAnswerType_Int32) + { + for (std::list<int32_t>::const_iterator + it = that_.answerInt32_.begin(); it != that_.answerInt32_.end(); ++it) + { + target.insert(static_cast<FileContentType>(*it)); + } + } + } + + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change) ORTHANC_OVERRIDE + { + OrthancPluginChange tmp; + tmp.seq = change.GetSeq(); + tmp.changeType = static_cast<int32_t>(change.GetChangeType()); + tmp.resourceType = Plugins::Convert(change.GetResourceType()); + tmp.publicId = change.GetPublicId().c_str(); + tmp.date = change.GetDate().c_str(); + + CheckSuccess(that_.backend_.logChange(that_.payload_, &tmp)); + } + + + virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE + { + OrthancPluginExportedResource tmp; + tmp.seq = resource.GetSeq(); + tmp.resourceType = Plugins::Convert(resource.GetResourceType()); + tmp.publicId = resource.GetPublicId().c_str(); + tmp.modality = resource.GetModality().c_str(); + tmp.date = resource.GetDate().c_str(); + tmp.patientId = resource.GetPatientId().c_str(); + tmp.studyInstanceUid = resource.GetStudyInstanceUid().c_str(); + tmp.seriesInstanceUid = resource.GetSeriesInstanceUid().c_str(); + tmp.sopInstanceUid = resource.GetSopInstanceUid().c_str(); + + CheckSuccess(that_.backend_.logExportedResource(that_.payload_, &tmp)); + } + + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + + CheckSuccess(that_.backend_.lookupAttachment + (that_.GetContext(), that_.payload_, id, static_cast<int32_t>(contentType))); + + if (that_.type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (that_.type_ == _OrthancPluginDatabaseAnswerType_Attachment && + that_.answerAttachments_.size() == 1) + { + attachment = that_.answerAttachments_.front(); + return true; + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + + CheckSuccess(that_.backend_.lookupGlobalProperty + (that_.GetContext(), that_.payload_, static_cast<int32_t>(property))); + + return that_.ForwardSingleAnswer(target); + } + + + virtual void LookupIdentifierRange(std::list<int64_t>& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) ORTHANC_OVERRIDE + { + if (that_.extensions_.lookupIdentifierRange == NULL) + { + // Default implementation, for plugins using Orthanc SDK <= 1.3.2 + + LookupIdentifier(result, level, tag, Compatibility::IdentifierConstraintType_GreaterOrEqual, start); + + std::list<int64_t> b; + LookupIdentifier(result, level, tag, Compatibility::IdentifierConstraintType_SmallerOrEqual, end); + + result.splice(result.end(), b); + } + else + { + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.lookupIdentifierRange(that_.GetContext(), that_.payload_, Plugins::Convert(level), + tag.GetGroup(), tag.GetElement(), + start.c_str(), end.c_str())); + that_.ForwardAnswers(result); + } + } + + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.lookupMetadata(that_.GetContext(), that_.payload_, id, static_cast<int32_t>(type))); + return that_.ForwardSingleAnswer(target); + } + + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.lookupParent(that_.GetContext(), that_.payload_, resourceId)); + return that_.ForwardSingleAnswer(parentId); + } + + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + + CheckSuccess(that_.backend_.lookupResource(that_.GetContext(), that_.payload_, publicId.c_str())); + + if (that_.type_ == _OrthancPluginDatabaseAnswerType_None) + { + return false; + } + else if (that_.type_ == _OrthancPluginDatabaseAnswerType_Resource && + that_.answerResources_.size() == 1) + { + id = that_.answerResources_.front().first; + type = that_.answerResources_.front().second; + return true; + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + + + virtual bool LookupResourceAndParent(int64_t& id, + ResourceType& type, + std::string& parentPublicId, + const std::string& publicId) ORTHANC_OVERRIDE + { + if (that_.extensions_.lookupResourceAndParent == NULL) + { + return ILookupResourceAndParent::Apply(*this, id, type, parentPublicId, publicId); + } + else + { + std::list<std::string> parent; + + uint8_t isExisting; + OrthancPluginResourceType pluginType = OrthancPluginResourceType_Patient; + + that_.ResetAnswers(); + CheckSuccess(that_.extensions_.lookupResourceAndParent + (that_.GetContext(), &isExisting, &id, &pluginType, that_.payload_, publicId.c_str())); + that_.ForwardAnswers(parent); + + if (isExisting) + { + type = Plugins::Convert(pluginType); + + if (parent.empty()) + { + if (type != ResourceType_Patient) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + } + else if (parent.size() == 1) + { + if ((type != ResourceType_Study && + type != ResourceType_Series && + type != ResourceType_Instance) || + parent.front().empty()) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + + parentPublicId = parent.front(); + } + else + { + throw OrthancException(ErrorCode_DatabasePlugin); + } + + return true; + } + else + { + return false; + } + } + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.selectPatientToRecycle(that_.GetContext(), that_.payload_)); + return that_.ForwardSingleAnswer(internalId); + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) ORTHANC_OVERRIDE + { + that_.ResetAnswers(); + CheckSuccess(that_.backend_.selectPatientToRecycle2(that_.GetContext(), that_.payload_, patientIdToAvoid)); + return that_.ForwardSingleAnswer(internalId); + } + + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.setGlobalProperty + (that_.payload_, static_cast<int32_t>(property), value.c_str())); + } + + + virtual void SetIdentifierTag(int64_t id, + const DicomTag& tag, + const std::string& value) ORTHANC_OVERRIDE + { + OrthancPluginDicomTag tmp; + tmp.group = tag.GetGroup(); + tmp.element = tag.GetElement(); + tmp.value = value.c_str(); + + CheckSuccess(that_.backend_.setIdentifierTag(that_.payload_, id, &tmp)); + } + + + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) ORTHANC_OVERRIDE + { + OrthancPluginDicomTag tmp; + tmp.group = tag.GetGroup(); + tmp.element = tag.GetElement(); + tmp.value = value.c_str(); + + CheckSuccess(that_.backend_.setMainDicomTag(that_.payload_, id, &tmp)); + } + + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.setMetadata + (that_.payload_, id, static_cast<int32_t>(type), value.c_str())); + } + + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) ORTHANC_OVERRIDE + { + CheckSuccess(that_.backend_.setProtectedPatient(that_.payload_, internalId, isProtected)); + } + + + virtual void SetResourcesContent(const Orthanc::ResourcesContent& content) ORTHANC_OVERRIDE + { + if (that_.extensions_.setResourcesContent == NULL) + { + ISetResourcesContent::Apply(*this, content); + } + else + { + std::vector<OrthancPluginResourcesContentTags> identifierTags; + std::vector<OrthancPluginResourcesContentTags> mainDicomTags; + std::vector<OrthancPluginResourcesContentMetadata> metadata; + + identifierTags.reserve(content.GetListTags().size()); + mainDicomTags.reserve(content.GetListTags().size()); + metadata.reserve(content.GetListMetadata().size()); + + for (ResourcesContent::ListTags::const_iterator + it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) + { + OrthancPluginResourcesContentTags tmp; + tmp.resource = it->resourceId_; + tmp.group = it->tag_.GetGroup(); + tmp.element = it->tag_.GetElement(); + tmp.value = it->value_.c_str(); + + if (it->isIdentifier_) + { + identifierTags.push_back(tmp); + } + else + { + mainDicomTags.push_back(tmp); + } + } + + for (ResourcesContent::ListMetadata::const_iterator + it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) + { + OrthancPluginResourcesContentMetadata tmp; + tmp.resource = it->resourceId_; + tmp.metadata = it->metadata_; + tmp.value = it->value_.c_str(); + metadata.push_back(tmp); + } + + assert(identifierTags.size() + mainDicomTags.size() == content.GetListTags().size() && + metadata.size() == content.GetListMetadata().size()); + + CheckSuccess(that_.extensions_.setResourcesContent( + that_.payload_, + identifierTags.size(), + (identifierTags.empty() ? NULL : &identifierTags[0]), + mainDicomTags.size(), + (mainDicomTags.empty() ? NULL : &mainDicomTags[0]), + metadata.size(), + (metadata.empty() ? NULL : &metadata[0]))); + } + } + + + virtual void TagMostRecentPatient(int64_t patient) ORTHANC_OVERRIDE + { + if (that_.extensions_.tagMostRecentPatient != NULL) + { + CheckSuccess(that_.extensions_.tagMostRecentPatient(that_.payload_, patient)); + } + } + }; @@ -319,36 +1191,11 @@ } - namespace - { - class VoidListener : public IDatabaseListener - { - public: - virtual void SignalRemainingAncestor(ResourceType parentType, - const std::string& publicId) - { - throw OrthancException(ErrorCode_InternalError); // Should be read-only transaction - } - - virtual void SignalAttachmentDeleted(const FileInfo& info) - { - throw OrthancException(ErrorCode_InternalError); // Should be read-only transaction - } - - virtual void SignalResourceDeleted(ResourceType type, - const std::string& publicId) - { - throw OrthancException(ErrorCode_InternalError); // Should be read-only transaction - } - }; - } - - void OrthancPluginDatabase::Open() { CheckSuccess(backend_.open(payload_)); - VoidListener listener; + VoidDatabaseListener listener; { Transaction transaction(*this, listener); @@ -356,7 +1203,7 @@ std::string tmp; fastGetTotalSize_ = - (LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) && + (transaction.LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) && tmp == "1"); if (fastGetTotalSize_) @@ -367,7 +1214,7 @@ { // This is the case of database plugins using Orthanc SDK <= 1.5.2 LOG(WARNING) << "Your database index plugin is not compatible with multiple Orthanc writers"; - currentDiskSize_ = GetTotalCompressedSize(); + currentDiskSize_ = transaction.GetTotalCompressedSize(); } transaction.Commit(0); @@ -375,556 +1222,6 @@ } - void OrthancPluginDatabase::AddAttachment(int64_t id, - const FileInfo& attachment) - { - OrthancPluginAttachment tmp; - tmp.uuid = attachment.GetUuid().c_str(); - tmp.contentType = static_cast<int32_t>(attachment.GetContentType()); - tmp.uncompressedSize = attachment.GetUncompressedSize(); - tmp.uncompressedHash = attachment.GetUncompressedMD5().c_str(); - tmp.compressionType = static_cast<int32_t>(attachment.GetCompressionType()); - tmp.compressedSize = attachment.GetCompressedSize(); - tmp.compressedHash = attachment.GetCompressedMD5().c_str(); - - CheckSuccess(backend_.addAttachment(payload_, id, &tmp)); - } - - - void OrthancPluginDatabase::AttachChild(int64_t parent, - int64_t child) - { - CheckSuccess(backend_.attachChild(payload_, parent, child)); - } - - - void OrthancPluginDatabase::ClearChanges() - { - CheckSuccess(backend_.clearChanges(payload_)); - } - - - void OrthancPluginDatabase::ClearExportedResources() - { - CheckSuccess(backend_.clearExportedResources(payload_)); - } - - - int64_t OrthancPluginDatabase::CreateResource(const std::string& publicId, - ResourceType type) - { - int64_t id; - CheckSuccess(backend_.createResource(&id, payload_, publicId.c_str(), Plugins::Convert(type))); - return id; - } - - - void OrthancPluginDatabase::DeleteAttachment(int64_t id, - FileContentType attachment) - { - CheckSuccess(backend_.deleteAttachment(payload_, id, static_cast<int32_t>(attachment))); - } - - - void OrthancPluginDatabase::DeleteMetadata(int64_t id, - MetadataType type) - { - CheckSuccess(backend_.deleteMetadata(payload_, id, static_cast<int32_t>(type))); - } - - - void OrthancPluginDatabase::DeleteResource(int64_t id) - { - CheckSuccess(backend_.deleteResource(payload_, id)); - } - - - void OrthancPluginDatabase::GetAllMetadata(std::map<MetadataType, std::string>& target, - int64_t id) - { - if (extensions_.getAllMetadata == NULL) - { - // Fallback implementation if extension is missing - target.clear(); - - ResetAnswers(); - CheckSuccess(backend_.listAvailableMetadata(GetContext(), payload_, id)); - - if (type_ != _OrthancPluginDatabaseAnswerType_None && - type_ != _OrthancPluginDatabaseAnswerType_Int32) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - target.clear(); - - if (type_ == _OrthancPluginDatabaseAnswerType_Int32) - { - for (std::list<int32_t>::const_iterator - it = answerInt32_.begin(); it != answerInt32_.end(); ++it) - { - MetadataType type = static_cast<MetadataType>(*it); - - std::string value; - if (LookupMetadata(value, id, type)) - { - target[type] = value; - } - } - } - } - else - { - ResetAnswers(); - - answerMetadata_ = ⌖ - target.clear(); - - CheckSuccess(extensions_.getAllMetadata(GetContext(), payload_, id)); - - if (type_ != _OrthancPluginDatabaseAnswerType_None && - type_ != _OrthancPluginDatabaseAnswerType_Metadata) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - } - } - - - void OrthancPluginDatabase::GetAllInternalIds(std::list<int64_t>& target, - ResourceType resourceType) - { - if (extensions_.getAllInternalIds == NULL) - { - throw OrthancException(ErrorCode_DatabasePlugin, - "The database plugin does not implement the mandatory GetAllInternalIds() extension"); - } - - ResetAnswers(); - CheckSuccess(extensions_.getAllInternalIds(GetContext(), payload_, Plugins::Convert(resourceType))); - ForwardAnswers(target); - } - - - void OrthancPluginDatabase::GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType) - { - ResetAnswers(); - CheckSuccess(backend_.getAllPublicIds(GetContext(), payload_, Plugins::Convert(resourceType))); - ForwardAnswers(target); - } - - - void OrthancPluginDatabase::GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType, - size_t since, - size_t limit) - { - if (extensions_.getAllPublicIdsWithLimit != NULL) - { - // This extension is available since Orthanc 0.9.4 - ResetAnswers(); - CheckSuccess(extensions_.getAllPublicIdsWithLimit - (GetContext(), payload_, Plugins::Convert(resourceType), since, limit)); - ForwardAnswers(target); - } - else - { - // The extension is not available in the database plugin, use a - // fallback implementation - target.clear(); - - if (limit == 0) - { - return; - } - - std::list<std::string> tmp; - GetAllPublicIds(tmp, resourceType); - - if (tmp.size() <= since) - { - // Not enough results => empty answer - return; - } - - std::list<std::string>::iterator current = tmp.begin(); - std::advance(current, since); - - while (limit > 0 && current != tmp.end()) - { - target.push_back(*current); - --limit; - ++current; - } - } - } - - - - void OrthancPluginDatabase::GetChanges(std::list<ServerIndexChange>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - { - ResetAnswers(); - answerChanges_ = ⌖ - answerDone_ = &done; - done = false; - - CheckSuccess(backend_.getChanges(GetContext(), payload_, since, maxResults)); - } - - - void OrthancPluginDatabase::GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) - { - ResetAnswers(); - CheckSuccess(backend_.getChildrenInternalId(GetContext(), payload_, id)); - ForwardAnswers(target); - } - - - void OrthancPluginDatabase::GetChildrenPublicId(std::list<std::string>& target, - int64_t id) - { - ResetAnswers(); - CheckSuccess(backend_.getChildrenPublicId(GetContext(), payload_, id)); - ForwardAnswers(target); - } - - - void OrthancPluginDatabase::GetExportedResources(std::list<ExportedResource>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - { - ResetAnswers(); - answerExportedResources_ = ⌖ - answerDone_ = &done; - done = false; - - CheckSuccess(backend_.getExportedResources(GetContext(), payload_, since, maxResults)); - } - - - void OrthancPluginDatabase::GetLastChange(std::list<ServerIndexChange>& target /*out*/) - { - answerDoneIgnored_ = false; - - ResetAnswers(); - answerChanges_ = ⌖ - answerDone_ = &answerDoneIgnored_; - - CheckSuccess(backend_.getLastChange(GetContext(), payload_)); - } - - - void OrthancPluginDatabase::GetLastExportedResource(std::list<ExportedResource>& target /*out*/) - { - answerDoneIgnored_ = false; - - ResetAnswers(); - answerExportedResources_ = ⌖ - answerDone_ = &answerDoneIgnored_; - - CheckSuccess(backend_.getLastExportedResource(GetContext(), payload_)); - } - - - void OrthancPluginDatabase::GetMainDicomTags(DicomMap& map, - int64_t id) - { - ResetAnswers(); - answerDicomMap_ = ↦ - - CheckSuccess(backend_.getMainDicomTags(GetContext(), payload_, id)); - } - - - std::string OrthancPluginDatabase::GetPublicId(int64_t resourceId) - { - ResetAnswers(); - std::string s; - - CheckSuccess(backend_.getPublicId(GetContext(), payload_, resourceId)); - - if (!ForwardSingleAnswer(s)) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - return s; - } - - - uint64_t OrthancPluginDatabase::GetResourceCount(ResourceType resourceType) - { - uint64_t count; - CheckSuccess(backend_.getResourceCount(&count, payload_, Plugins::Convert(resourceType))); - return count; - } - - - ResourceType OrthancPluginDatabase::GetResourceType(int64_t resourceId) - { - OrthancPluginResourceType type; - CheckSuccess(backend_.getResourceType(&type, payload_, resourceId)); - return Plugins::Convert(type); - } - - - uint64_t OrthancPluginDatabase::GetTotalCompressedSize() - { - uint64_t size; - CheckSuccess(backend_.getTotalCompressedSize(&size, payload_)); - return size; - } - - - uint64_t OrthancPluginDatabase::GetTotalUncompressedSize() - { - uint64_t size; - CheckSuccess(backend_.getTotalUncompressedSize(&size, payload_)); - return size; - } - - - bool OrthancPluginDatabase::IsExistingResource(int64_t internalId) - { - int32_t existing; - CheckSuccess(backend_.isExistingResource(&existing, payload_, internalId)); - return (existing != 0); - } - - - bool OrthancPluginDatabase::IsProtectedPatient(int64_t internalId) - { - int32_t isProtected; - CheckSuccess(backend_.isProtectedPatient(&isProtected, payload_, internalId)); - return (isProtected != 0); - } - - - void OrthancPluginDatabase::ListAvailableAttachments(std::set<FileContentType>& target, - int64_t id) - { - ResetAnswers(); - - CheckSuccess(backend_.listAvailableAttachments(GetContext(), payload_, id)); - - if (type_ != _OrthancPluginDatabaseAnswerType_None && - type_ != _OrthancPluginDatabaseAnswerType_Int32) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - target.clear(); - - if (type_ == _OrthancPluginDatabaseAnswerType_Int32) - { - for (std::list<int32_t>::const_iterator - it = answerInt32_.begin(); it != answerInt32_.end(); ++it) - { - target.insert(static_cast<FileContentType>(*it)); - } - } - } - - - void OrthancPluginDatabase::LogChange(int64_t internalId, - const ServerIndexChange& change) - { - OrthancPluginChange tmp; - tmp.seq = change.GetSeq(); - tmp.changeType = static_cast<int32_t>(change.GetChangeType()); - tmp.resourceType = Plugins::Convert(change.GetResourceType()); - tmp.publicId = change.GetPublicId().c_str(); - tmp.date = change.GetDate().c_str(); - - CheckSuccess(backend_.logChange(payload_, &tmp)); - } - - - void OrthancPluginDatabase::LogExportedResource(const ExportedResource& resource) - { - OrthancPluginExportedResource tmp; - tmp.seq = resource.GetSeq(); - tmp.resourceType = Plugins::Convert(resource.GetResourceType()); - tmp.publicId = resource.GetPublicId().c_str(); - tmp.modality = resource.GetModality().c_str(); - tmp.date = resource.GetDate().c_str(); - tmp.patientId = resource.GetPatientId().c_str(); - tmp.studyInstanceUid = resource.GetStudyInstanceUid().c_str(); - tmp.seriesInstanceUid = resource.GetSeriesInstanceUid().c_str(); - tmp.sopInstanceUid = resource.GetSopInstanceUid().c_str(); - - CheckSuccess(backend_.logExportedResource(payload_, &tmp)); - } - - - bool OrthancPluginDatabase::LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) - { - ResetAnswers(); - - CheckSuccess(backend_.lookupAttachment - (GetContext(), payload_, id, static_cast<int32_t>(contentType))); - - if (type_ == _OrthancPluginDatabaseAnswerType_None) - { - return false; - } - else if (type_ == _OrthancPluginDatabaseAnswerType_Attachment && - answerAttachments_.size() == 1) - { - attachment = answerAttachments_.front(); - return true; - } - else - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - } - - - bool OrthancPluginDatabase::LookupGlobalProperty(std::string& target, - GlobalProperty property) - { - ResetAnswers(); - - CheckSuccess(backend_.lookupGlobalProperty - (GetContext(), payload_, static_cast<int32_t>(property))); - - return ForwardSingleAnswer(target); - } - - - bool OrthancPluginDatabase::LookupMetadata(std::string& target, - int64_t id, - MetadataType type) - { - ResetAnswers(); - CheckSuccess(backend_.lookupMetadata(GetContext(), payload_, id, static_cast<int32_t>(type))); - return ForwardSingleAnswer(target); - } - - - bool OrthancPluginDatabase::LookupParent(int64_t& parentId, - int64_t resourceId) - { - ResetAnswers(); - CheckSuccess(backend_.lookupParent(GetContext(), payload_, resourceId)); - return ForwardSingleAnswer(parentId); - } - - - bool OrthancPluginDatabase::LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) - { - ResetAnswers(); - - CheckSuccess(backend_.lookupResource(GetContext(), payload_, publicId.c_str())); - - if (type_ == _OrthancPluginDatabaseAnswerType_None) - { - return false; - } - else if (type_ == _OrthancPluginDatabaseAnswerType_Resource && - answerResources_.size() == 1) - { - id = answerResources_.front().first; - type = answerResources_.front().second; - return true; - } - else - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - } - - - bool OrthancPluginDatabase::SelectPatientToRecycle(int64_t& internalId) - { - ResetAnswers(); - CheckSuccess(backend_.selectPatientToRecycle(GetContext(), payload_)); - return ForwardSingleAnswer(internalId); - } - - - bool OrthancPluginDatabase::SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) - { - ResetAnswers(); - CheckSuccess(backend_.selectPatientToRecycle2(GetContext(), payload_, patientIdToAvoid)); - return ForwardSingleAnswer(internalId); - } - - - void OrthancPluginDatabase::SetGlobalProperty(GlobalProperty property, - const std::string& value) - { - CheckSuccess(backend_.setGlobalProperty - (payload_, static_cast<int32_t>(property), value.c_str())); - } - - - void OrthancPluginDatabase::ClearMainDicomTags(int64_t id) - { - if (extensions_.clearMainDicomTags == NULL) - { - throw OrthancException(ErrorCode_DatabasePlugin, - "Your custom index plugin does not implement the mandatory ClearMainDicomTags() extension"); - } - - CheckSuccess(extensions_.clearMainDicomTags(payload_, id)); - } - - - void OrthancPluginDatabase::SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) - { - OrthancPluginDicomTag tmp; - tmp.group = tag.GetGroup(); - tmp.element = tag.GetElement(); - tmp.value = value.c_str(); - - CheckSuccess(backend_.setMainDicomTag(payload_, id, &tmp)); - } - - - void OrthancPluginDatabase::SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) - { - OrthancPluginDicomTag tmp; - tmp.group = tag.GetGroup(); - tmp.element = tag.GetElement(); - tmp.value = value.c_str(); - - CheckSuccess(backend_.setIdentifierTag(payload_, id, &tmp)); - } - - - void OrthancPluginDatabase::SetMetadata(int64_t id, - MetadataType type, - const std::string& value) - { - CheckSuccess(backend_.setMetadata - (payload_, id, static_cast<int32_t>(type), value.c_str())); - } - - - void OrthancPluginDatabase::SetProtectedPatient(int64_t internalId, - bool isProtected) - { - CheckSuccess(backend_.setProtectedPatient(payload_, internalId, isProtected)); - } - - IDatabaseWrapper::ITransaction* OrthancPluginDatabase::StartTransaction(TransactionType type, IDatabaseListener& listener) { @@ -990,7 +1287,7 @@ void OrthancPluginDatabase::Upgrade(unsigned int targetVersion, IStorageArea& storageArea) { - VoidListener listener; + VoidDatabaseListener listener; if (extensions_.upgradeDatabase != NULL) { @@ -1267,313 +1564,4 @@ boost::lexical_cast<std::string>(answer.type)); } } - - - bool OrthancPluginDatabase::IsDiskSizeAbove(uint64_t threshold) - { - if (fastGetTotalSize_) - { - return GetTotalCompressedSize() > threshold; - } - else - { - assert(GetTotalCompressedSize() == currentDiskSize_); - return currentDiskSize_ > threshold; - } - } - - - void OrthancPluginDatabase::ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - { - if (extensions_.lookupResources == NULL) - { - // Fallback to compatibility mode - ILookupResources::Apply - (*this, *this, resourcesId, instancesId, lookup, queryLevel, limit); - } - else - { - std::vector<OrthancPluginDatabaseConstraint> constraints; - std::vector< std::vector<const char*> > constraintsValues; - - constraints.resize(lookup.size()); - constraintsValues.resize(lookup.size()); - - for (size_t i = 0; i < lookup.size(); i++) - { - lookup[i].EncodeForPlugins(constraints[i], constraintsValues[i]); - } - - ResetAnswers(); - answerMatchingResources_ = &resourcesId; - answerMatchingInstances_ = instancesId; - - CheckSuccess(extensions_.lookupResources(GetContext(), payload_, lookup.size(), - (lookup.empty() ? NULL : &constraints[0]), - Plugins::Convert(queryLevel), - limit, (instancesId == NULL ? 0 : 1))); - } - } - - - bool OrthancPluginDatabase::CreateInstance( - IDatabaseWrapper::CreateInstanceResult& result, - int64_t& instanceId, - const std::string& patient, - const std::string& study, - const std::string& series, - const std::string& instance) - { - if (extensions_.createInstance == NULL) - { - // Fallback to compatibility mode - return ICreateInstance::Apply - (*this, result, instanceId, patient, study, series, instance); - } - else - { - OrthancPluginCreateInstanceResult output; - memset(&output, 0, sizeof(output)); - - CheckSuccess(extensions_.createInstance(&output, payload_, patient.c_str(), - study.c_str(), series.c_str(), instance.c_str())); - - instanceId = output.instanceId; - - if (output.isNewInstance) - { - result.isNewPatient_ = output.isNewPatient; - result.isNewStudy_ = output.isNewStudy; - result.isNewSeries_ = output.isNewSeries; - result.patientId_ = output.patientId; - result.studyId_ = output.studyId; - result.seriesId_ = output.seriesId; - return true; - } - else - { - return false; - } - } - } - - - void OrthancPluginDatabase::LookupIdentifier(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - Compatibility::IdentifierConstraintType type, - const std::string& value) - { - if (extensions_.lookupIdentifier3 == NULL) - { - throw OrthancException(ErrorCode_DatabasePlugin, - "The database plugin does not implement the mandatory LookupIdentifier3() extension"); - } - - OrthancPluginDicomTag tmp; - tmp.group = tag.GetGroup(); - tmp.element = tag.GetElement(); - tmp.value = value.c_str(); - - ResetAnswers(); - CheckSuccess(extensions_.lookupIdentifier3(GetContext(), payload_, Plugins::Convert(level), - &tmp, Compatibility::Convert(type))); - ForwardAnswers(result); - } - - - void OrthancPluginDatabase::LookupIdentifierRange(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - const std::string& start, - const std::string& end) - { - if (extensions_.lookupIdentifierRange == NULL) - { - // Default implementation, for plugins using Orthanc SDK <= 1.3.2 - - LookupIdentifier(result, level, tag, Compatibility::IdentifierConstraintType_GreaterOrEqual, start); - - std::list<int64_t> b; - LookupIdentifier(result, level, tag, Compatibility::IdentifierConstraintType_SmallerOrEqual, end); - - result.splice(result.end(), b); - } - else - { - ResetAnswers(); - CheckSuccess(extensions_.lookupIdentifierRange(GetContext(), payload_, Plugins::Convert(level), - tag.GetGroup(), tag.GetElement(), - start.c_str(), end.c_str())); - ForwardAnswers(result); - } - } - - - void OrthancPluginDatabase::SetResourcesContent(const Orthanc::ResourcesContent& content) - { - if (extensions_.setResourcesContent == NULL) - { - ISetResourcesContent::Apply(*this, content); - } - else - { - std::vector<OrthancPluginResourcesContentTags> identifierTags; - std::vector<OrthancPluginResourcesContentTags> mainDicomTags; - std::vector<OrthancPluginResourcesContentMetadata> metadata; - - identifierTags.reserve(content.GetListTags().size()); - mainDicomTags.reserve(content.GetListTags().size()); - metadata.reserve(content.GetListMetadata().size()); - - for (ResourcesContent::ListTags::const_iterator - it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) - { - OrthancPluginResourcesContentTags tmp; - tmp.resource = it->resourceId_; - tmp.group = it->tag_.GetGroup(); - tmp.element = it->tag_.GetElement(); - tmp.value = it->value_.c_str(); - - if (it->isIdentifier_) - { - identifierTags.push_back(tmp); - } - else - { - mainDicomTags.push_back(tmp); - } - } - - for (ResourcesContent::ListMetadata::const_iterator - it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) - { - OrthancPluginResourcesContentMetadata tmp; - tmp.resource = it->resourceId_; - tmp.metadata = it->metadata_; - tmp.value = it->value_.c_str(); - metadata.push_back(tmp); - } - - assert(identifierTags.size() + mainDicomTags.size() == content.GetListTags().size() && - metadata.size() == content.GetListMetadata().size()); - - CheckSuccess(extensions_.setResourcesContent( - payload_, - identifierTags.size(), - (identifierTags.empty() ? NULL : &identifierTags[0]), - mainDicomTags.size(), - (mainDicomTags.empty() ? NULL : &mainDicomTags[0]), - metadata.size(), - (metadata.empty() ? NULL : &metadata[0]))); - } - } - - - - void OrthancPluginDatabase::GetChildrenMetadata(std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata) - { - if (extensions_.getChildrenMetadata == NULL) - { - IGetChildrenMetadata::Apply(*this, target, resourceId, metadata); - } - else - { - ResetAnswers(); - CheckSuccess(extensions_.getChildrenMetadata - (GetContext(), payload_, resourceId, static_cast<int32_t>(metadata))); - ForwardAnswers(target); - } - } - - - int64_t OrthancPluginDatabase::GetLastChangeIndex() - { - if (extensions_.getLastChangeIndex == NULL) - { - // This was the default behavior in Orthanc <= 1.5.1 - // https://groups.google.com/d/msg/orthanc-users/QhzB6vxYeZ0/YxabgqpfBAAJ - return 0; - } - else - { - int64_t result = 0; - CheckSuccess(extensions_.getLastChangeIndex(&result, payload_)); - return result; - } - } - - - void OrthancPluginDatabase::TagMostRecentPatient(int64_t patient) - { - if (extensions_.tagMostRecentPatient != NULL) - { - CheckSuccess(extensions_.tagMostRecentPatient(payload_, patient)); - } - } - - - bool OrthancPluginDatabase::LookupResourceAndParent(int64_t& id, - ResourceType& type, - std::string& parentPublicId, - const std::string& publicId) - { - if (extensions_.lookupResourceAndParent == NULL) - { - return ILookupResourceAndParent::Apply(*this, id, type, parentPublicId, publicId); - } - else - { - std::list<std::string> parent; - - uint8_t isExisting; - OrthancPluginResourceType pluginType = OrthancPluginResourceType_Patient; - - ResetAnswers(); - CheckSuccess(extensions_.lookupResourceAndParent - (GetContext(), &isExisting, &id, &pluginType, payload_, publicId.c_str())); - ForwardAnswers(parent); - - if (isExisting) - { - type = Plugins::Convert(pluginType); - - if (parent.empty()) - { - if (type != ResourceType_Patient) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - } - else if (parent.size() == 1) - { - if ((type != ResourceType_Study && - type != ResourceType_Series && - type != ResourceType_Instance) || - parent.front().empty()) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - parentPublicId = parent.front(); - } - else - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - return true; - } - else - { - return false; - } - } - } }
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.h Mon Mar 15 15:30:42 2021 +0100 @@ -47,12 +47,7 @@ namespace Orthanc { class OrthancPluginDatabase : - public IDatabaseWrapper, - public Compatibility::ICreateInstance, - public Compatibility::IGetChildrenMetadata, - public Compatibility::ILookupResources, - public Compatibility::ILookupResourceAndParent, - public Compatibility::ISetResourcesContent + public IDatabaseWrapper { private: class Transaction; @@ -111,11 +106,9 @@ size_t extensionsSize, void *payload); - virtual void Open() - ORTHANC_OVERRIDE; + virtual void Open() ORTHANC_OVERRIDE; - virtual void Close() - ORTHANC_OVERRIDE + virtual void Close() ORTHANC_OVERRIDE { CheckSuccess(backend_.close(payload_)); } @@ -125,247 +118,25 @@ return library_; } - virtual void AddAttachment(int64_t id, - const FileInfo& attachment) - ORTHANC_OVERRIDE; - - virtual void AttachChild(int64_t parent, - int64_t child) - ORTHANC_OVERRIDE; - - virtual void ClearChanges() - ORTHANC_OVERRIDE; - - virtual void ClearExportedResources() - ORTHANC_OVERRIDE; - - virtual int64_t CreateResource(const std::string& publicId, - ResourceType type) - ORTHANC_OVERRIDE; - - virtual void DeleteAttachment(int64_t id, - FileContentType attachment) - ORTHANC_OVERRIDE; - - virtual void DeleteMetadata(int64_t id, - MetadataType type) - ORTHANC_OVERRIDE; - - virtual void DeleteResource(int64_t id) - ORTHANC_OVERRIDE; - - virtual void FlushToDisk() - ORTHANC_OVERRIDE + virtual void FlushToDisk() ORTHANC_OVERRIDE { } - virtual bool HasFlushToDisk() const - ORTHANC_OVERRIDE + virtual bool HasFlushToDisk() const ORTHANC_OVERRIDE { return false; } - virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType) - ORTHANC_OVERRIDE; - - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType, - size_t since, - size_t limit) - ORTHANC_OVERRIDE; - - virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - ORTHANC_OVERRIDE; - - virtual void GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetChildrenPublicId(std::list<std::string>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - ORTHANC_OVERRIDE; - - virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) - ORTHANC_OVERRIDE; - - virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) - ORTHANC_OVERRIDE; - - virtual void GetMainDicomTags(DicomMap& map, - int64_t id) - ORTHANC_OVERRIDE; - - virtual std::string GetPublicId(int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual uint64_t GetResourceCount(ResourceType resourceType) - ORTHANC_OVERRIDE; - - virtual ResourceType GetResourceType(int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual uint64_t GetTotalCompressedSize() - ORTHANC_OVERRIDE; - - virtual uint64_t GetTotalUncompressedSize() - ORTHANC_OVERRIDE; - - virtual bool IsExistingResource(int64_t internalId) - ORTHANC_OVERRIDE; - - virtual bool IsProtectedPatient(int64_t internalId) - ORTHANC_OVERRIDE; - - virtual void ListAvailableAttachments(std::set<FileContentType>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) - ORTHANC_OVERRIDE; - - virtual void LogExportedResource(const ExportedResource& resource) - ORTHANC_OVERRIDE; - - virtual bool LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) - ORTHANC_OVERRIDE; - - virtual bool LookupGlobalProperty(std::string& target, - GlobalProperty property) - ORTHANC_OVERRIDE; - - virtual bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type) - ORTHANC_OVERRIDE; - - virtual bool LookupParent(int64_t& parentId, - int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual bool LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) - ORTHANC_OVERRIDE; - - virtual bool SelectPatientToRecycle(int64_t& internalId) - ORTHANC_OVERRIDE; - - virtual bool SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) - ORTHANC_OVERRIDE; - - virtual void SetGlobalProperty(GlobalProperty property, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void ClearMainDicomTags(int64_t id) - ORTHANC_OVERRIDE; - - virtual void SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void SetMetadata(int64_t id, - MetadataType type, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void SetProtectedPatient(int64_t internalId, - bool isProtected) - ORTHANC_OVERRIDE; - virtual IDatabaseWrapper::ITransaction* StartTransaction(TransactionType type, IDatabaseListener& listener) ORTHANC_OVERRIDE; - virtual unsigned int GetDatabaseVersion() - ORTHANC_OVERRIDE; + virtual unsigned int GetDatabaseVersion() ORTHANC_OVERRIDE; virtual void Upgrade(unsigned int targetVersion, - IStorageArea& storageArea) - ORTHANC_OVERRIDE; + IStorageArea& storageArea) ORTHANC_OVERRIDE; void AnswerReceived(const _OrthancPluginDatabaseAnswer& answer); - - virtual bool IsDiskSizeAbove(uint64_t threshold) - ORTHANC_OVERRIDE; - - virtual void ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - ORTHANC_OVERRIDE; - - virtual bool CreateInstance(CreateInstanceResult& result, - int64_t& instanceId, - const std::string& patient, - const std::string& study, - const std::string& series, - const std::string& instance) - ORTHANC_OVERRIDE; - - // From the "ILookupResources" interface - virtual void GetAllInternalIds(std::list<int64_t>& target, - ResourceType resourceType) - ORTHANC_OVERRIDE; - - // From the "ILookupResources" interface - virtual void LookupIdentifier(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - Compatibility::IdentifierConstraintType type, - const std::string& value) - ORTHANC_OVERRIDE; - - // From the "ILookupResources" interface - virtual void LookupIdentifierRange(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - const std::string& start, - const std::string& end) - ORTHANC_OVERRIDE; - - virtual void SetResourcesContent(const Orthanc::ResourcesContent& content) - ORTHANC_OVERRIDE; - - virtual void GetChildrenMetadata(std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata) - ORTHANC_OVERRIDE; - - virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE; - - virtual void TagMostRecentPatient(int64_t patient) ORTHANC_OVERRIDE; - - virtual bool LookupResourceAndParent(int64_t& id, - ResourceType& type, - std::string& parentPublicId, - const std::string& publicId) - ORTHANC_OVERRIDE; }; }
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -65,6 +65,7 @@ #include "../../../OrthancFramework/Sources/SerializationToolbox.h" #include "../../../OrthancFramework/Sources/StringMemoryBuffer.h" #include "../../../OrthancFramework/Sources/Toolbox.h" +#include "../../Sources/Database/VoidDatabaseListener.h" #include "../../Sources/OrthancConfiguration.h" #include "../../Sources/OrthancFindRequestHandler.h" #include "../../Sources/Search/HierarchicalMatcher.h" @@ -4970,8 +4971,16 @@ "The service ReconstructMainDicomTags can only be invoked by custom database plugins"); } - IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); - ServerToolbox::ReconstructMainDicomTags(*pimpl_->database_, storage, Plugins::Convert(p.level)); + VoidDatabaseListener listener; + + { + IStorageArea& storage = *reinterpret_cast<IStorageArea*>(p.storageArea); + + std::unique_ptr<IDatabaseWrapper::ITransaction> transaction( + pimpl_->database_->StartTransaction(TransactionType_ReadWrite, listener)); + ServerToolbox::ReconstructMainDicomTags(*transaction, storage, Plugins::Convert(p.level)); + transaction->Commit(0); + } return true; }
--- a/OrthancServer/Resources/RunCppCheck.sh Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Resources/RunCppCheck.sh Mon Mar 15 15:30:42 2021 +0100 @@ -17,8 +17,8 @@ stlFindInsert:../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp:1194 stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:164 stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:72 -stlFindInsert:../../OrthancServer/Sources/OrthancWebDav.cpp:385 -stlFindInsert:../../OrthancServer/Sources/ServerIndex.cpp:399 +stlFindInsert:../../OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp:383 +stlFindInsert:../../OrthancServer/Sources/OrthancWebDav.cpp:384 syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:50 syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:130 syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:321
--- a/OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -161,7 +161,7 @@ static void ApplyLevel(SetOfResources& candidates, - IDatabaseWrapper& database, + IDatabaseWrapper::ITransaction& transaction, ILookupResources& compatibility, const std::vector<DatabaseConstraint>& lookup, ResourceType level) @@ -267,7 +267,7 @@ candidate != source.end(); ++candidate) { DicomMap tags; - database.GetMainDicomTags(tags, *candidate); + transaction.GetMainDicomTags(tags, *candidate); bool match = true; @@ -291,7 +291,7 @@ } - static std::string GetOneInstance(IDatabaseWrapper& compatibility, + static std::string GetOneInstance(IDatabaseWrapper::ITransaction& compatibility, int64_t resource, ResourceType level) { @@ -348,11 +348,11 @@ assert(upperLevel <= queryLevel && queryLevel <= lowerLevel); - SetOfResources candidates(database_, upperLevel); + SetOfResources candidates(transaction_, upperLevel); for (int level = upperLevel; level <= lowerLevel; level++) { - ApplyLevel(candidates, database_, compatibility_, lookup, static_cast<ResourceType>(level)); + ApplyLevel(candidates, transaction_, compatibility_, lookup, static_cast<ResourceType>(level)); if (level != lowerLevel) { @@ -372,7 +372,7 @@ it = resources.begin(); it != resources.end(); ++it) { int64_t parent; - if (database_.LookupParent(parent, *it)) + if (transaction_.LookupParent(parent, *it)) { parents.push_back(parent); } @@ -396,9 +396,9 @@ for (std::list<int64_t>::const_iterator it = resources.begin(); it != resources.end(); ++it, pos++) { - assert(database_.GetResourceType(*it) == queryLevel); + assert(transaction_.GetResourceType(*it) == queryLevel); - const std::string resource = database_.GetPublicId(*it); + const std::string resource = transaction_.GetPublicId(*it); resourcesId.push_back(resource); if (instancesId != NULL) @@ -411,7 +411,7 @@ else { // Collect one child instance for each of the selected resources - instancesId->push_back(GetOneInstance(database_, *it, queryLevel)); + instancesId->push_back(GetOneInstance(transaction_, *it, queryLevel)); } } }
--- a/OrthancServer/Sources/Database/Compatibility/DatabaseLookup.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/DatabaseLookup.h Mon Mar 15 15:30:42 2021 +0100 @@ -43,13 +43,13 @@ class DatabaseLookup : public boost::noncopyable { private: - IDatabaseWrapper& database_; + IDatabaseWrapper::ITransaction& transaction_; ILookupResources& compatibility_; public: - DatabaseLookup(IDatabaseWrapper& database, + DatabaseLookup(IDatabaseWrapper::ITransaction& transaction, ILookupResources& compatibility) : - database_(database), + transaction_(transaction), compatibility_(compatibility) { }
--- a/OrthancServer/Sources/Database/Compatibility/ILookupResources.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/ILookupResources.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -41,7 +41,7 @@ namespace Compatibility { void ILookupResources::Apply( - IDatabaseWrapper& database, + IDatabaseWrapper::ITransaction& transaction, ILookupResources& compatibility, std::list<std::string>& resourcesId, std::list<std::string>* instancesId, @@ -49,7 +49,7 @@ ResourceType queryLevel, size_t limit) { - Compatibility::DatabaseLookup compat(database, compatibility); + Compatibility::DatabaseLookup compat(transaction, compatibility); compat.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); } }
--- a/OrthancServer/Sources/Database/Compatibility/ILookupResources.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/ILookupResources.h Mon Mar 15 15:30:42 2021 +0100 @@ -66,7 +66,7 @@ const std::string& start, const std::string& end) = 0; - static void Apply(IDatabaseWrapper& database, + static void Apply(IDatabaseWrapper::ITransaction& transaction, ILookupResources& compatibility, std::list<std::string>& resourcesId, std::list<std::string>* instancesId,
--- a/OrthancServer/Sources/Database/Compatibility/SetOfResources.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/SetOfResources.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -90,7 +90,7 @@ it != resources_->end(); ++it) { std::list<int64_t> tmp; - database_.GetChildrenInternalId(tmp, *it); + transaction_.GetChildrenInternalId(tmp, *it); for (std::list<int64_t>::const_iterator child = tmp.begin(); child != tmp.end(); ++child) @@ -133,14 +133,14 @@ if (resources_.get() == NULL) { // All the resources of this level are part of the filter - database_.GetAllPublicIds(result, level_); + transaction_.GetAllPublicIds(result, level_); } else { for (Resources::const_iterator it = resources_->begin(); it != resources_->end(); ++it) { - result.push_back(database_.GetPublicId(*it)); + result.push_back(transaction_.GetPublicId(*it)); } } }
--- a/OrthancServer/Sources/Database/Compatibility/SetOfResources.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/Compatibility/SetOfResources.h Mon Mar 15 15:30:42 2021 +0100 @@ -49,14 +49,14 @@ private: typedef std::set<int64_t> Resources; - IDatabaseWrapper& database_; + IDatabaseWrapper::ITransaction& transaction_; ResourceType level_; std::unique_ptr<Resources> resources_; public: - SetOfResources(IDatabaseWrapper& database, + SetOfResources(IDatabaseWrapper::ITransaction& transaction, ResourceType level) : - database_(database), + transaction_(transaction), level_(level) { }
--- a/OrthancServer/Sources/Database/IDatabaseListener.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/IDatabaseListener.h Mon Mar 15 15:30:42 2021 +0100 @@ -36,6 +36,7 @@ #include "../../../OrthancFramework/Sources/FileStorage/FileInfo.h" #include "../ServerEnumerations.h" +#include <boost/noncopyable.hpp> #include <string> namespace Orthanc
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Mon Mar 15 15:30:42 2021 +0100 @@ -55,6 +55,17 @@ class IDatabaseWrapper : public boost::noncopyable { public: + struct CreateInstanceResult + { + bool isNewPatient_; + bool isNewStudy_; + bool isNewSeries_; + int64_t patientId_; + int64_t studyId_; + int64_t seriesId_; + }; + + class ITransaction : public boost::noncopyable { public: @@ -68,19 +79,163 @@ // have no fast way to compute the size of all the stored // attachments (cf. "fastGetTotalSize_") virtual void Commit(int64_t fileSizeDelta) = 0; - }; + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) = 0; + + virtual void ClearChanges() = 0; + + virtual void ClearExportedResources() = 0; + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) = 0; + + virtual void DeleteMetadata(int64_t id, + MetadataType type) = 0; + + virtual void DeleteResource(int64_t id) = 0; + + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id) = 0; + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType) = 0; + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType, + size_t since, + size_t limit) = 0; + + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) = 0; + + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id) = 0; + + virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) = 0; + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) = 0; + + virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) = 0; + + virtual void GetMainDicomTags(DicomMap& map, + int64_t id) = 0; + + virtual std::string GetPublicId(int64_t resourceId) = 0; + + virtual uint64_t GetResourceCount(ResourceType resourceType) = 0; + + virtual ResourceType GetResourceType(int64_t resourceId) = 0; + + virtual uint64_t GetTotalCompressedSize() = 0; + + virtual uint64_t GetTotalUncompressedSize() = 0; + + virtual bool IsExistingResource(int64_t internalId) = 0; + + virtual bool IsProtectedPatient(int64_t internalId) = 0; + + virtual void ListAvailableAttachments(std::set<FileContentType>& target, + int64_t id) = 0; + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change) = 0; + + virtual void LogExportedResource(const ExportedResource& resource) = 0; + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) = 0; + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property) = 0; + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) = 0; + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) = 0; + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId) = 0; + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) = 0; + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value) = 0; + + virtual void ClearMainDicomTags(int64_t id) = 0; + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) = 0; + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) = 0; - struct CreateInstanceResult - { - bool isNewPatient_; - bool isNewStudy_; - bool isNewSeries_; - int64_t patientId_; - int64_t studyId_; - int64_t seriesId_; + /** + * Primitives introduced in Orthanc 1.5.2 + **/ + + virtual bool IsDiskSizeAbove(uint64_t threshold) = 0; + + virtual void ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, // Can be NULL if not needed + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) = 0; + + // Returns "true" iff. the instance is new and has been inserted + // into the database. If "false" is returned, the content of + // "result" is undefined, but "instanceId" must be properly + // set. This method must also tag the parent patient as the most + // recent in the patient recycling order if it is not protected + // (so as to fix issue #58). + virtual bool CreateInstance(CreateInstanceResult& result, /* out */ + int64_t& instanceId, /* out */ + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance) = 0; + + // It is guaranteed that the resources to be modified have no main + // DICOM tags, and no DICOM identifiers associated with + // them. However, some metadata might be already existing, and + // have to be overwritten. + virtual void SetResourcesContent(const ResourcesContent& content) = 0; + + virtual void GetChildrenMetadata(std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata) = 0; + + virtual int64_t GetLastChangeIndex() = 0; + + + /** + * Primitives introduced in Orthanc 1.5.4 + **/ + + virtual bool LookupResourceAndParent(int64_t& id, + ResourceType& type, + std::string& parentPublicId, + const std::string& publicId) = 0; }; + virtual ~IDatabaseWrapper() { } @@ -89,116 +244,10 @@ virtual void Close() = 0; - virtual void AddAttachment(int64_t id, - const FileInfo& attachment) = 0; - - virtual void ClearChanges() = 0; - - virtual void ClearExportedResources() = 0; - - virtual void DeleteAttachment(int64_t id, - FileContentType attachment) = 0; - - virtual void DeleteMetadata(int64_t id, - MetadataType type) = 0; - - virtual void DeleteResource(int64_t id) = 0; - virtual void FlushToDisk() = 0; virtual bool HasFlushToDisk() const = 0; - virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, - int64_t id) = 0; - - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType) = 0; - - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType, - size_t since, - size_t limit) = 0; - - virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) = 0; - - virtual void GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) = 0; - - virtual void GetChildrenPublicId(std::list<std::string>& target, - int64_t id) = 0; - - virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) = 0; - - virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) = 0; - - virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) = 0; - - virtual void GetMainDicomTags(DicomMap& map, - int64_t id) = 0; - - virtual std::string GetPublicId(int64_t resourceId) = 0; - - virtual uint64_t GetResourceCount(ResourceType resourceType) = 0; - - virtual ResourceType GetResourceType(int64_t resourceId) = 0; - - virtual uint64_t GetTotalCompressedSize() = 0; - - virtual uint64_t GetTotalUncompressedSize() = 0; - - virtual bool IsExistingResource(int64_t internalId) = 0; - - virtual bool IsProtectedPatient(int64_t internalId) = 0; - - virtual void ListAvailableAttachments(std::set<FileContentType>& target, - int64_t id) = 0; - - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) = 0; - - virtual void LogExportedResource(const ExportedResource& resource) = 0; - - virtual bool LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) = 0; - - virtual bool LookupGlobalProperty(std::string& target, - GlobalProperty property) = 0; - - virtual bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type) = 0; - - virtual bool LookupParent(int64_t& parentId, - int64_t resourceId) = 0; - - virtual bool LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) = 0; - - virtual bool SelectPatientToRecycle(int64_t& internalId) = 0; - - virtual bool SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) = 0; - - virtual void SetGlobalProperty(GlobalProperty property, - const std::string& value) = 0; - - virtual void ClearMainDicomTags(int64_t id) = 0; - - virtual void SetMetadata(int64_t id, - MetadataType type, - const std::string& value) = 0; - - virtual void SetProtectedPatient(int64_t internalId, - bool isProtected) = 0; - virtual ITransaction* StartTransaction(TransactionType type, IDatabaseListener& listener) = 0; @@ -206,53 +255,5 @@ virtual void Upgrade(unsigned int targetVersion, IStorageArea& storageArea) = 0; - - - /** - * Primitives introduced in Orthanc 1.5.2 - **/ - - virtual bool IsDiskSizeAbove(uint64_t threshold) = 0; - - virtual void ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, // Can be NULL if not needed - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) = 0; - - // Returns "true" iff. the instance is new and has been inserted - // into the database. If "false" is returned, the content of - // "result" is undefined, but "instanceId" must be properly - // set. This method must also tag the parent patient as the most - // recent in the patient recycling order if it is not protected - // (so as to fix issue #58). - virtual bool CreateInstance(CreateInstanceResult& result, /* out */ - int64_t& instanceId, /* out */ - const std::string& patient, - const std::string& study, - const std::string& series, - const std::string& instance) = 0; - - // It is guaranteed that the resources to be modified have no main - // DICOM tags, and no DICOM identifiers associated with - // them. However, some metadata might be already existing, and - // have to be overwritten. - virtual void SetResourcesContent(const ResourcesContent& content) = 0; - - virtual void GetChildrenMetadata(std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata) = 0; - - virtual int64_t GetLastChangeIndex() = 0; - - - /** - * Primitives introduced in Orthanc 1.5.4 - **/ - - virtual bool LookupResourceAndParent(int64_t& id, - ResourceType& type, - std::string& parentPublicId, - const std::string& publicId) = 0; }; }
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -39,6 +39,11 @@ #include "../../../OrthancFramework/Sources/SQLite/Transaction.h" #include "../Search/ISqlLookupFormatter.h" #include "../ServerToolbox.h" +#include "Compatibility/ICreateInstance.h" +#include "Compatibility/IGetChildrenMetadata.h" +#include "Compatibility/ILookupResourceAndParent.h" +#include "Compatibility/ISetResourcesContent.h" +#include "VoidDatabaseListener.h" #include <OrthancServerResources.h> @@ -46,85 +51,37 @@ #include <boost/lexical_cast.hpp> namespace Orthanc -{ - class SQLiteDatabaseWrapper::SignalFileDeleted : public SQLite::IScalarFunction +{ + class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter { private: - SQLiteDatabaseWrapper& sqlite_; + std::list<std::string> values_; public: - SignalFileDeleted(SQLiteDatabaseWrapper& sqlite) : - sqlite_(sqlite) - { - } - - virtual const char* GetName() const ORTHANC_OVERRIDE + virtual std::string GenerateParameter(const std::string& value) ORTHANC_OVERRIDE { - return "SignalFileDeleted"; + values_.push_back(value); + return "?"; } - - virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE + + virtual std::string FormatResourceType(ResourceType level) ORTHANC_OVERRIDE { - return 7; + return boost::lexical_cast<std::string>(level); } - virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE + virtual std::string FormatWildcardEscape() ORTHANC_OVERRIDE { - if (sqlite_.listener_ != NULL) - { - std::string uncompressedMD5, compressedMD5; - - if (!context.IsNullValue(5)) - { - uncompressedMD5 = context.GetStringValue(5); - } - - if (!context.IsNullValue(6)) - { - compressedMD5 = context.GetStringValue(6); - } - - FileInfo info(context.GetStringValue(0), - static_cast<FileContentType>(context.GetIntValue(1)), - static_cast<uint64_t>(context.GetInt64Value(2)), - uncompressedMD5, - static_cast<CompressionType>(context.GetIntValue(3)), - static_cast<uint64_t>(context.GetInt64Value(4)), - compressedMD5); - - sqlite_.listener_->SignalAttachmentDeleted(info); - } - } - }; - - - class SQLiteDatabaseWrapper::SignalResourceDeleted : public SQLite::IScalarFunction - { - private: - SQLiteDatabaseWrapper& sqlite_; - - public: - SignalResourceDeleted(SQLiteDatabaseWrapper& sqlite) : - sqlite_(sqlite) - { + return "ESCAPE '\\'"; } - virtual const char* GetName() const ORTHANC_OVERRIDE - { - return "SignalResourceDeleted"; - } - - virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE + void Bind(SQLite::Statement& statement) const { - return 2; - } - - virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE - { - if (sqlite_.listener_ != NULL) + size_t pos = 0; + + for (std::list<std::string>::const_iterator + it = values_.begin(); it != values_.end(); ++it, pos++) { - sqlite_.listener_->SignalResourceDeleted(static_cast<ResourceType>(context.GetIntValue(1)), - context.GetStringValue(0)); + statement.BindString(pos, *it); } } }; @@ -192,147 +149,1100 @@ }; - void SQLiteDatabaseWrapper::GetChangesInternal(std::list<ServerIndexChange>& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults) + class SQLiteDatabaseWrapper::TransactionBase : + public SQLiteDatabaseWrapper::UnitTestsTransaction, + public Compatibility::ICreateInstance, + public Compatibility::IGetChildrenMetadata, + public Compatibility::ILookupResourceAndParent, + public Compatibility::ISetResourcesContent { - target.clear(); - - while (target.size() < maxResults && s.Step()) + private: + void AnswerLookup(std::list<std::string>& resourcesId, + std::list<std::string>& instancesId, + ResourceType level) { - int64_t seq = s.ColumnInt64(0); - ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); - ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); - const std::string& date = s.ColumnString(4); + resourcesId.clear(); + instancesId.clear(); + + std::unique_ptr<SQLite::Statement> statement; + + switch (level) + { + case ResourceType_Patient: + { + statement.reset( + new SQLite::Statement( + db_, SQLITE_FROM_HERE, + "SELECT patients.publicId, instances.publicID FROM Lookup AS patients " + "INNER JOIN Resources studies ON patients.internalId=studies.parentId " + "INNER JOIN Resources series ON studies.internalId=series.parentId " + "INNER JOIN Resources instances ON series.internalId=instances.parentId " + "GROUP BY patients.publicId")); + + break; + } - int64_t internalId = s.ColumnInt64(2); - std::string publicId = GetPublicId(internalId); + case ResourceType_Study: + { + statement.reset( + new SQLite::Statement( + db_, SQLITE_FROM_HERE, + "SELECT studies.publicId, instances.publicID FROM Lookup AS studies " + "INNER JOIN Resources series ON studies.internalId=series.parentId " + "INNER JOIN Resources instances ON series.internalId=instances.parentId " + "GROUP BY studies.publicId")); + + break; + } - target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); + case ResourceType_Series: + { + statement.reset( + new SQLite::Statement( + db_, SQLITE_FROM_HERE, + "SELECT series.publicId, instances.publicID FROM Lookup AS series " + "INNER JOIN Resources instances ON series.internalId=instances.parentId " + "GROUP BY series.publicId")); + + break; + } + + case ResourceType_Instance: + { + statement.reset( + new SQLite::Statement( + db_, SQLITE_FROM_HERE, "SELECT publicId, publicId FROM Lookup")); + + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + + assert(statement.get() != NULL); + + while (statement->Step()) + { + resourcesId.push_back(statement->ColumnString(0)); + instancesId.push_back(statement->ColumnString(1)); + } } - done = !(target.size() == maxResults && s.Step()); - } + + void ClearTable(const std::string& tableName) + { + db_.Execute("DELETE FROM " + tableName); + } + + + void GetChangesInternal(std::list<ServerIndexChange>& target, + bool& done, + SQLite::Statement& s, + uint32_t maxResults) + { + target.clear(); + + while (target.size() < maxResults && s.Step()) + { + int64_t seq = s.ColumnInt64(0); + ChangeType changeType = static_cast<ChangeType>(s.ColumnInt(1)); + ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(3)); + const std::string& date = s.ColumnString(4); + + int64_t internalId = s.ColumnInt64(2); + std::string publicId = GetPublicId(internalId); + + target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); + } + + done = !(target.size() == maxResults && s.Step()); + } - void SQLiteDatabaseWrapper::GetExportedResourcesInternal(std::list<ExportedResource>& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults) - { - target.clear(); - - while (target.size() < maxResults && s.Step()) + void GetExportedResourcesInternal(std::list<ExportedResource>& target, + bool& done, + SQLite::Statement& s, + uint32_t maxResults) { - int64_t seq = s.ColumnInt64(0); - ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); - std::string publicId = s.ColumnString(2); + target.clear(); + + while (target.size() < maxResults && s.Step()) + { + int64_t seq = s.ColumnInt64(0); + ResourceType resourceType = static_cast<ResourceType>(s.ColumnInt(1)); + std::string publicId = s.ColumnString(2); + + ExportedResource resource(seq, + resourceType, + publicId, + s.ColumnString(3), // modality + s.ColumnString(8), // date + s.ColumnString(4), // patient ID + s.ColumnString(5), // study instance UID + s.ColumnString(6), // series instance UID + s.ColumnString(7)); // sop instance UID + + target.push_back(resource); + } + + done = !(target.size() == maxResults && s.Step()); + } + - ExportedResource resource(seq, - resourceType, - publicId, - s.ColumnString(3), // modality - s.ColumnString(8), // date - s.ColumnString(4), // patient ID - s.ColumnString(5), // study instance UID - s.ColumnString(6), // series instance UID - s.ColumnString(7)); // sop instance UID + void GetChildren(std::list<std::string>& childrenPublicIds, + int64_t id) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); + s.BindInt64(0, id); + + childrenPublicIds.clear(); + while (s.Step()) + { + childrenPublicIds.push_back(s.ColumnString(0)); + } + } - target.push_back(resource); + IDatabaseListener& listener_; + SignalRemainingAncestor& signalRemainingAncestor_; + + public: + TransactionBase(SQLite::Connection& db, + IDatabaseListener& listener, + SignalRemainingAncestor& signalRemainingAncestor) : + UnitTestsTransaction(db), + listener_(listener), + signalRemainingAncestor_(signalRemainingAncestor) + { + } + + IDatabaseListener& GetListener() const + { + return listener_; } - done = !(target.size() == maxResults && s.Step()); - } + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); + s.BindInt64(0, id); + s.BindInt(1, attachment.GetContentType()); + s.BindString(2, attachment.GetUuid()); + s.BindInt64(3, attachment.GetCompressedSize()); + s.BindInt64(4, attachment.GetUncompressedSize()); + s.BindInt(5, attachment.GetCompressionType()); + s.BindString(6, attachment.GetUncompressedMD5()); + s.BindString(7, attachment.GetCompressedMD5()); + s.Run(); + } + + + virtual void ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) ORTHANC_OVERRIDE + { + LookupFormatter formatter; + + std::string sql; + LookupFormatter::Apply(sql, formatter, lookup, queryLevel, limit); + + sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; + + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); + s.Run(); + } + + { + SQLite::Statement statement(db_, sql); + formatter.Bind(statement); + statement.Run(); + } + + if (instancesId != NULL) + { + AnswerLookup(resourcesId, *instancesId, queryLevel); + } + else + { + resourcesId.clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Lookup"); + + while (s.Step()) + { + resourcesId.push_back(s.ColumnString(0)); + } + } + } + + + virtual void AttachChild(int64_t parent, + int64_t child) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); + s.BindInt64(0, parent); + s.BindInt64(1, child); + s.Run(); + } + + + virtual void ClearChanges() ORTHANC_OVERRIDE + { + ClearTable("Changes"); + } + + virtual void ClearExportedResources() ORTHANC_OVERRIDE + { + ClearTable("ExportedResources"); + } - void SQLiteDatabaseWrapper::GetChildren(std::list<std::string>& childrenPublicIds, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); - s.BindInt64(0, id); + virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE + { + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); + s.BindInt64(0, id); + s.Run(); + } + + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); + s.BindInt64(0, id); + s.Run(); + } + } + + + virtual bool CreateInstance(CreateInstanceResult& result, + int64_t& instanceId, + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance) ORTHANC_OVERRIDE + { + return ICreateInstance::Apply + (*this, result, instanceId, patient, study, series, instance); + } + - childrenPublicIds.clear(); - while (s.Step()) + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); + s.BindInt(0, type); + s.BindString(1, publicId); + s.Run(); + return db_.GetLastInsertRowId(); + } + + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) ORTHANC_OVERRIDE { - childrenPublicIds.push_back(s.ColumnString(0)); + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); + s.BindInt64(0, id); + s.BindInt(1, attachment); + s.Run(); } - } + + + virtual void DeleteMetadata(int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); + s.BindInt64(0, id); + s.BindInt(1, type); + s.Run(); + } - void SQLiteDatabaseWrapper::DeleteResource(int64_t id) - { - signalRemainingAncestor_->Reset(); + virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE + { + signalRemainingAncestor_.Reset(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); + s.BindInt64(0, id); + s.Run(); + + if (signalRemainingAncestor_.HasRemainingAncestor()) + { + listener_.SignalRemainingAncestor(signalRemainingAncestor_.GetRemainingAncestorType(), + signalRemainingAncestor_.GetRemainingAncestorId()); + } + } + - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); - s.BindInt64(0, id); - s.Run(); + virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, + int64_t id) ORTHANC_OVERRIDE + { + target.clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); + s.BindInt64(0, id); + + while (s.Step()) + { + MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); + target[key] = s.ColumnString(1); + } + } + - if (signalRemainingAncestor_->HasRemainingAncestor() && - listener_ != NULL) + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); + s.BindInt(0, resourceType); + + target.clear(); + while (s.Step()) + { + target.push_back(s.ColumnString(0)); + } + } + + + virtual void GetAllPublicIds(std::list<std::string>& target, + ResourceType resourceType, + size_t since, + size_t limit) ORTHANC_OVERRIDE { - listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), - signalRemainingAncestor_->GetRemainingAncestorId()); + if (limit == 0) + { + target.clear(); + return; + } + + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT publicId FROM Resources WHERE " + "resourceType=? LIMIT ? OFFSET ?"); + s.BindInt(0, resourceType); + s.BindInt64(1, limit); + s.BindInt64(2, since); + + target.clear(); + while (s.Step()) + { + target.push_back(s.ColumnString(0)); + } } - } - bool SQLiteDatabaseWrapper::GetParentPublicId(std::string& target, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " - "WHERE a.internalId = b.parentId AND b.internalId = ?"); - s.BindInt64(0, id); + virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, + bool& done /*out*/, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); + s.BindInt64(0, since); + s.BindInt(1, maxResults + 1); + GetChangesInternal(target, done, s, maxResults); + } - if (s.Step()) + + virtual void GetChildrenMetadata(std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata) ORTHANC_OVERRIDE + { + IGetChildrenMetadata::Apply(*this, target, resourceId, metadata); + } + + + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) ORTHANC_OVERRIDE { - target = s.ColumnString(0); - return true; + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " + "WHERE a.parentId = b.internalId AND b.internalId = ?"); + s.BindInt64(0, id); + + target.clear(); + + while (s.Step()) + { + target.push_back(s.ColumnInt64(0)); + } } - else + + + virtual void GetChildrenPublicId(std::list<std::string>& target, + int64_t id) ORTHANC_OVERRIDE { - return false; + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " + "WHERE a.parentId = b.internalId AND b.internalId = ?"); + s.BindInt64(0, id); + + target.clear(); + + while (s.Step()) + { + target.push_back(s.ColumnString(0)); + } } - } + + + virtual void GetExportedResources(std::list<ExportedResource>& target, + bool& done, + int64_t since, + uint32_t maxResults) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); + s.BindInt64(0, since); + s.BindInt(1, maxResults + 1); + GetExportedResourcesInternal(target, done, s, maxResults); + } + + + virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE + { + bool done; // Ignored + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); + GetChangesInternal(target, done, s, 1); + } - int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table) - { - /** - * "Generally one cannot use SQL parameters/placeholders for - * database identifiers (tables, columns, views, schemas, etc.) or - * database functions (e.g., CURRENT_DATE), but instead only for - * binding literal values." => To avoid any SQL injection, we - * check that the "table" parameter has only alphabetic - * characters. - * https://stackoverflow.com/a/1274764/881731 - **/ - for (size_t i = 0; i < table.size(); i++) + int64_t GetLastChangeIndex() ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT seq FROM sqlite_sequence WHERE name='Changes'"); + + if (s.Step()) + { + int64_t c = s.ColumnInt(0); + assert(!s.Step()); + return c; + } + else + { + // No change has been recorded so far in the database + return 0; + } + } + + + virtual void GetLastExportedResource(std::list<ExportedResource>& target) ORTHANC_OVERRIDE + { + bool done; // Ignored + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); + GetExportedResourcesInternal(target, done, s, 1); + } + + + virtual void GetMainDicomTags(DicomMap& map, + int64_t id) ORTHANC_OVERRIDE { - if (!isalpha(table[i])) + map.Clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); + s.BindInt64(0, id); + while (s.Step()) { - throw OrthancException(ErrorCode_ParameterOutOfRange); + map.SetValue(s.ColumnInt(1), + s.ColumnInt(2), + s.ColumnString(3), false); + } + } + + + virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT publicId FROM Resources WHERE internalId=?"); + s.BindInt64(0, resourceId); + + if (s.Step()) + { + return s.ColumnString(0); + } + else + { + throw OrthancException(ErrorCode_UnknownResource); + } + } + + + virtual uint64_t GetResourceCount(ResourceType resourceType) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); + s.BindInt(0, resourceType); + + if (!s.Step()) + { + return 0; + } + else + { + int64_t c = s.ColumnInt(0); + assert(!s.Step()); + return c; } } - // Don't use "SQLITE_FROM_HERE", otherwise "table" would be cached - SQLite::Statement s(db_, "SELECT COUNT(*) FROM " + table); - if (s.Step()) + virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE { - int64_t c = s.ColumnInt(0); - assert(!s.Step()); - return c; + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT resourceType FROM Resources WHERE internalId=?"); + s.BindInt64(0, resourceId); + + if (s.Step()) + { + return static_cast<ResourceType>(s.ColumnInt(0)); + } + else + { + throw OrthancException(ErrorCode_UnknownResource); + } } - else + + + virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE { - throw OrthancException(ErrorCode_InternalError); + // Old SQL query that was used in Orthanc <= 1.5.0: + // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=0"); + s.Run(); + return static_cast<uint64_t>(s.ColumnInt64(0)); } - } + virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE + { + // Old SQL query that was used in Orthanc <= 1.5.0: + // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=1"); + s.Run(); + return static_cast<uint64_t>(s.ColumnInt64(0)); + } + + + virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE + { + return GetTotalCompressedSize() > threshold; + } + + + virtual bool IsExistingResource(int64_t internalId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT * FROM Resources WHERE internalId=?"); + s.BindInt64(0, internalId); + return s.Step(); + } + + + virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); + s.BindInt64(0, internalId); + return !s.Step(); + } + + + virtual void ListAvailableAttachments(std::set<FileContentType>& target, + int64_t id) ORTHANC_OVERRIDE + { + target.clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT fileType FROM AttachedFiles WHERE id=?"); + s.BindInt64(0, id); + + while (s.Step()) + { + target.insert(static_cast<FileContentType>(s.ColumnInt(0))); + } + } + + + virtual void LogChange(int64_t internalId, + const ServerIndexChange& change) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); + s.BindInt(0, change.GetChangeType()); + s.BindInt64(1, internalId); + s.BindInt(2, change.GetResourceType()); + s.BindString(3, change.GetDate()); + s.Run(); + } + + + virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); + + s.BindInt(0, resource.GetResourceType()); + s.BindString(1, resource.GetPublicId()); + s.BindString(2, resource.GetModality()); + s.BindString(3, resource.GetPatientId()); + s.BindString(4, resource.GetStudyInstanceUid()); + s.BindString(5, resource.GetSeriesInstanceUid()); + s.BindString(6, resource.GetSopInstanceUid()); + s.BindString(7, resource.GetDate()); + s.Run(); + } + + + virtual bool LookupAttachment(FileInfo& attachment, + int64_t id, + FileContentType contentType) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT uuid, uncompressedSize, compressionType, compressedSize, " + "uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); + s.BindInt64(0, id); + s.BindInt(1, contentType); + + if (!s.Step()) + { + return false; + } + else + { + attachment = FileInfo(s.ColumnString(0), + contentType, + s.ColumnInt64(1), + s.ColumnString(4), + static_cast<CompressionType>(s.ColumnInt(2)), + s.ColumnInt64(3), + s.ColumnString(5)); + return true; + } + } + + + virtual bool LookupGlobalProperty(std::string& target, + GlobalProperty property) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT value FROM GlobalProperties WHERE property=?"); + s.BindInt(0, property); + + if (!s.Step()) + { + return false; + } + else + { + target = s.ColumnString(0); + return true; + } + } + + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT value FROM Metadata WHERE id=? AND type=?"); + s.BindInt64(0, id); + s.BindInt(1, type); + + if (!s.Step()) + { + return false; + } + else + { + target = s.ColumnString(0); + return true; + } + } + + + virtual bool LookupParent(int64_t& parentId, + int64_t resourceId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT parentId FROM Resources WHERE internalId=?"); + s.BindInt64(0, resourceId); + + if (!s.Step()) + { + throw OrthancException(ErrorCode_UnknownResource); + } + + if (s.ColumnIsNull(0)) + { + return false; + } + else + { + parentId = s.ColumnInt(0); + return true; + } + } + + + virtual bool LookupResourceAndParent(int64_t& id, + ResourceType& type, + std::string& parentPublicId, + const std::string& publicId) ORTHANC_OVERRIDE + { + return ILookupResourceAndParent::Apply(*this, id, type, parentPublicId, publicId); + } + + + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); + s.BindString(0, publicId); + + if (!s.Step()) + { + return false; + } + else + { + id = s.ColumnInt(0); + type = static_cast<ResourceType>(s.ColumnInt(1)); + + // Check whether there is a single resource with this public id + assert(!s.Step()); + + return true; + } + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); + + if (!s.Step()) + { + // No patient remaining or all the patients are protected + return false; + } + else + { + internalId = s.ColumnInt(0); + return true; + } + } + + + virtual bool SelectPatientToRecycle(int64_t& internalId, + int64_t patientIdToAvoid) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "SELECT patientId FROM PatientRecyclingOrder " + "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); + s.BindInt64(0, patientIdToAvoid); + + if (!s.Step()) + { + // No patient remaining or all the patients are protected + return false; + } + else + { + internalId = s.ColumnInt(0); + return true; + } + } + + + virtual void SetGlobalProperty(GlobalProperty property, + const std::string& value) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); + s.BindInt(0, property); + s.BindString(1, value); + s.Run(); + } + + + virtual void SetIdentifierTag(int64_t id, + const DicomTag& tag, + const std::string& value) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); + s.BindInt64(0, id); + s.BindInt(1, tag.GetGroup()); + s.BindInt(2, tag.GetElement()); + s.BindString(3, value); + s.Run(); + } + + + virtual void SetProtectedPatient(int64_t internalId, + bool isProtected) ORTHANC_OVERRIDE + { + if (isProtected) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); + s.BindInt64(0, internalId); + s.Run(); + } + else if (IsProtectedPatient(internalId)) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); + s.BindInt64(0, internalId); + s.Run(); + } + else + { + // Nothing to do: The patient is already unprotected + } + } + + + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); + s.BindInt64(0, id); + s.BindInt(1, tag.GetGroup()); + s.BindInt(2, tag.GetElement()); + s.BindString(3, value); + s.Run(); + } + + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) ORTHANC_OVERRIDE + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); + s.BindInt64(0, id); + s.BindInt(1, type); + s.BindString(2, value); + s.Run(); + } + + + virtual void SetResourcesContent(const Orthanc::ResourcesContent& content) ORTHANC_OVERRIDE + { + ISetResourcesContent::Apply(*this, content); + } + + + virtual void TagMostRecentPatient(int64_t patient) ORTHANC_OVERRIDE + { + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); + s.BindInt64(0, patient); + s.Run(); + + assert(db_.GetLastChangeCount() == 0 || + db_.GetLastChangeCount() == 1); + + if (db_.GetLastChangeCount() == 0) + { + // The patient was protected, there was nothing to delete from the recycling order + return; + } + } + + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, + "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); + s.BindInt64(0, patient); + s.Run(); + } + } + }; + + + class SQLiteDatabaseWrapper::SignalFileDeleted : public SQLite::IScalarFunction + { + private: + SQLiteDatabaseWrapper& sqlite_; + + public: + SignalFileDeleted(SQLiteDatabaseWrapper& sqlite) : + sqlite_(sqlite) + { + } + + virtual const char* GetName() const ORTHANC_OVERRIDE + { + return "SignalFileDeleted"; + } + + virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE + { + return 7; + } + + virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE + { + if (sqlite_.activeTransaction_ != NULL) + { + std::string uncompressedMD5, compressedMD5; + + if (!context.IsNullValue(5)) + { + uncompressedMD5 = context.GetStringValue(5); + } + + if (!context.IsNullValue(6)) + { + compressedMD5 = context.GetStringValue(6); + } + + FileInfo info(context.GetStringValue(0), + static_cast<FileContentType>(context.GetIntValue(1)), + static_cast<uint64_t>(context.GetInt64Value(2)), + uncompressedMD5, + static_cast<CompressionType>(context.GetIntValue(3)), + static_cast<uint64_t>(context.GetInt64Value(4)), + compressedMD5); + + sqlite_.activeTransaction_->GetListener().SignalAttachmentDeleted(info); + } + } + }; + + + class SQLiteDatabaseWrapper::SignalResourceDeleted : public SQLite::IScalarFunction + { + private: + SQLiteDatabaseWrapper& sqlite_; + + public: + SignalResourceDeleted(SQLiteDatabaseWrapper& sqlite) : + sqlite_(sqlite) + { + } + + virtual const char* GetName() const ORTHANC_OVERRIDE + { + return "SignalResourceDeleted"; + } + + virtual unsigned int GetCardinality() const ORTHANC_OVERRIDE + { + return 2; + } + + virtual void Compute(SQLite::FunctionContext& context) ORTHANC_OVERRIDE + { + if (sqlite_.activeTransaction_ != NULL) + { + sqlite_.activeTransaction_->GetListener(). + SignalResourceDeleted(static_cast<ResourceType>(context.GetIntValue(1)), + context.GetStringValue(0)); + } + } + }; + + + class SQLiteDatabaseWrapper::ReadWriteTransaction : public SQLiteDatabaseWrapper::TransactionBase + { + private: + SQLiteDatabaseWrapper& that_; + std::unique_ptr<SQLite::Transaction> transaction_; + int64_t initialDiskSize_; + + public: + ReadWriteTransaction(SQLiteDatabaseWrapper& that, + IDatabaseListener& listener) : + TransactionBase(that.db_, listener, *that.signalRemainingAncestor_), + that_(that), + transaction_(new SQLite::Transaction(that_.db_)) + { + if (that_.activeTransaction_ != NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + that_.activeTransaction_ = this; + +#if defined(NDEBUG) + // Release mode + initialDiskSize_ = 0; +#else + // Debug mode + initialDiskSize_ = static_cast<int64_t>(GetTotalCompressedSize()); +#endif + } + + virtual ~ReadWriteTransaction() + { + assert(that_.activeTransaction_ != NULL); + that_.activeTransaction_ = NULL; + } + + void Begin() + { + transaction_->Begin(); + } + + virtual void Rollback() ORTHANC_OVERRIDE + { + transaction_->Rollback(); + } + + virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE + { + transaction_->Commit(); + + assert(initialDiskSize_ + fileSizeDelta >= 0 && + initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(GetTotalCompressedSize())); + } + }; + + + class SQLiteDatabaseWrapper::ReadOnlyTransaction : public SQLiteDatabaseWrapper::TransactionBase + { + private: + SQLiteDatabaseWrapper& that_; + + public: + ReadOnlyTransaction(SQLiteDatabaseWrapper& that, + IDatabaseListener& listener) : + TransactionBase(that.db_, listener, *that.signalRemainingAncestor_), + that_(that) + { + if (that_.activeTransaction_ != NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + + that_.activeTransaction_ = this; + } + + virtual ~ReadOnlyTransaction() + { + assert(that_.activeTransaction_ != NULL); + that_.activeTransaction_ = NULL; + } + + virtual void Rollback() ORTHANC_OVERRIDE + { + } + + virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE + { + if (fileSizeDelta != 0) + { + throw OrthancException(ErrorCode_InternalError); + } + } + }; + + SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : - listener_(NULL), + activeTransaction_(NULL), signalRemainingAncestor_(NULL), version_(0) { @@ -341,41 +1251,33 @@ SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : - listener_(NULL), + activeTransaction_(NULL), signalRemainingAncestor_(NULL), version_(0) { db_.OpenInMemory(); } - - int SQLiteDatabaseWrapper::GetGlobalIntegerProperty(GlobalProperty property, - int defaultValue) + SQLiteDatabaseWrapper::~SQLiteDatabaseWrapper() { - std::string tmp; - - if (!LookupGlobalProperty(tmp, GlobalProperty_DatabasePatchLevel)) + if (activeTransaction_ != NULL) { - return defaultValue; - } - else - { - try - { - return boost::lexical_cast<int>(tmp); - } - catch (boost::bad_lexical_cast&) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Global property " + boost::lexical_cast<std::string>(property) + - " should be an integer, but found: " + tmp); - } + LOG(ERROR) << "A SQLite transaction is still active in the SQLiteDatabaseWrapper destructor: Expect a crash"; } } void SQLiteDatabaseWrapper::Open() { + if (signalRemainingAncestor_ != NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); // Cannot open twice + } + + signalRemainingAncestor_ = dynamic_cast<SignalRemainingAncestor*>(db_.Register(new SignalRemainingAncestor)); + db_.Register(new SignalFileDeleted(*this)); + db_.Register(new SignalResourceDeleted(*this)); + db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); // Performance tuning of SQLite with PRAGMAs @@ -388,10 +1290,11 @@ // Make "LIKE" case-sensitive in SQLite db_.Execute("PRAGMA case_sensitive_like = true;"); - + + VoidDatabaseListener listener; + { - SQLite::Transaction t(db_); - t.Begin(); + std::unique_ptr<ITransaction> transaction(StartTransaction(TransactionType_ReadOnly, listener)); if (!db_.DoesTableExist("GlobalProperties")) { @@ -403,7 +1306,7 @@ // Check the version of the database std::string tmp; - if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) + if (!transaction->LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) { tmp = "Unknown"; } @@ -428,7 +1331,7 @@ // New in Orthanc 1.5.1 if (version_ == 6) { - if (!LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || + if (!transaction->LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || tmp != "1") { LOG(INFO) << "Installing the SQLite triggers to track the size of the attachments"; @@ -438,12 +1341,8 @@ } } - t.Commit(); + transaction->Commit(0); } - - signalRemainingAncestor_ = dynamic_cast<SignalRemainingAncestor*>(db_.Register(new SignalRemainingAncestor)); - db_.Register(new SignalFileDeleted(*this)); - db_.Register(new SignalResourceDeleted(*this)); } @@ -496,188 +1395,25 @@ // No change in the DB schema, the step from version 5 to 6 only // consists in reconstructing the main DICOM tags information // (as more tags got included). - db_.BeginTransaction(); - ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Patient); - ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Study); - ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Series); - ServerToolbox::ReconstructMainDicomTags(*this, storageArea, ResourceType_Instance); - db_.Execute("UPDATE GlobalProperties SET value=\"6\" WHERE property=" + - boost::lexical_cast<std::string>(GlobalProperty_DatabaseSchemaVersion) + ";"); - db_.CommitTransaction(); + + VoidDatabaseListener listener; + + { + std::unique_ptr<ITransaction> transaction(StartTransaction(TransactionType_ReadWrite, listener)); + ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Patient); + ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Study); + ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Series); + ServerToolbox::ReconstructMainDicomTags(*transaction, storageArea, ResourceType_Instance); + db_.Execute("UPDATE GlobalProperties SET value=\"6\" WHERE property=" + + boost::lexical_cast<std::string>(GlobalProperty_DatabaseSchemaVersion) + ";"); + transaction->Commit(0); + } + version_ = 6; } } - void SQLiteDatabaseWrapper::ClearTable(const std::string& tableName) - { - db_.Execute("DELETE FROM " + tableName); - } - - - bool SQLiteDatabaseWrapper::LookupParent(int64_t& parentId, - int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT parentId FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (!s.Step()) - { - throw OrthancException(ErrorCode_UnknownResource); - } - - if (s.ColumnIsNull(0)) - { - return false; - } - else - { - parentId = s.ColumnInt(0); - return true; - } - } - - - ResourceType SQLiteDatabaseWrapper::GetResourceType(int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT resourceType FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (s.Step()) - { - return static_cast<ResourceType>(s.ColumnInt(0)); - } - else - { - throw OrthancException(ErrorCode_UnknownResource); - } - } - - - std::string SQLiteDatabaseWrapper::GetPublicId(int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT publicId FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (s.Step()) - { - return s.ColumnString(0); - } - else - { - throw OrthancException(ErrorCode_UnknownResource); - } - } - - - void SQLiteDatabaseWrapper::GetChanges(std::list<ServerIndexChange>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); - s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - GetChangesInternal(target, done, s, maxResults); - } - - - void SQLiteDatabaseWrapper::GetLastChange(std::list<ServerIndexChange>& target /*out*/) - { - bool done; // Ignored - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); - GetChangesInternal(target, done, s, 1); - } - - - class SQLiteDatabaseWrapper::ReadWriteTransaction : public IDatabaseWrapper::ITransaction - { - private: - SQLiteDatabaseWrapper& that_; - std::unique_ptr<SQLite::Transaction> transaction_; - int64_t initialDiskSize_; - - public: - ReadWriteTransaction(SQLiteDatabaseWrapper& that, - IDatabaseListener& listener) : - that_(that), - transaction_(new SQLite::Transaction(that_.db_)) - { - assert(that_.listener_ == NULL); - that_.listener_ = &listener; // TODO - STORE IN TRANSACTION - -#if defined(NDEBUG) - // Release mode - initialDiskSize_ = 0; -#else - // Debug mode - initialDiskSize_ = static_cast<int64_t>(that_.GetTotalCompressedSize()); -#endif - } - - virtual ~ReadWriteTransaction() - { - assert(that_.listener_ != NULL); - that_.listener_ = NULL; // TODO - STORE IN TRANSACTION - } - - void Begin() - { - transaction_->Begin(); - } - - virtual void Rollback() ORTHANC_OVERRIDE - { - transaction_->Rollback(); - } - - virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE - { - transaction_->Commit(); - - assert(initialDiskSize_ + fileSizeDelta >= 0 && - initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(that_.GetTotalCompressedSize())); - } - }; - - - class SQLiteDatabaseWrapper::ReadOnlyTransaction : public IDatabaseWrapper::ITransaction - { - private: - SQLiteDatabaseWrapper& that_; - - public: - ReadOnlyTransaction(SQLiteDatabaseWrapper& that, - IDatabaseListener& listener) : - that_(that) - { - assert(that_.listener_ == NULL); - that_.listener_ = &listener; - } - - virtual ~ReadOnlyTransaction() - { - assert(that_.listener_ != NULL); - that_.listener_ = NULL; - } - - virtual void Rollback() ORTHANC_OVERRIDE - { - } - - virtual void Commit(int64_t fileSizeDelta /* only used in debug */) ORTHANC_OVERRIDE - { - if (fileSizeDelta != 0) - { - throw OrthancException(ErrorCode_InternalError); - } - } - }; - - IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction(TransactionType type, IDatabaseListener& listener) { @@ -699,54 +1435,9 @@ } } - - void SQLiteDatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& target, - int64_t id) - { - target.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type, value FROM Metadata WHERE id=?"); - s.BindInt64(0, id); - - while (s.Step()) - { - MetadataType key = static_cast<MetadataType>(s.ColumnInt(0)); - target[key] = s.ColumnString(1); - } - } - - - void SQLiteDatabaseWrapper::SetGlobalProperty(GlobalProperty property, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); - s.BindInt(0, property); - s.BindString(1, value); - s.Run(); - } - - - bool SQLiteDatabaseWrapper::LookupGlobalProperty(std::string& target, - GlobalProperty property) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT value FROM GlobalProperties WHERE property=?"); - s.BindInt(0, property); - - if (!s.Step()) - { - return false; - } - else - { - target = s.ColumnString(0); - return true; - } - } - - - int64_t SQLiteDatabaseWrapper::CreateResource(const std::string& publicId, - ResourceType type) + + int64_t SQLiteDatabaseWrapper::UnitTestsTransaction::CreateResource(const std::string& publicId, + ResourceType type) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); s.BindInt(0, type); @@ -756,33 +1447,8 @@ } - bool SQLiteDatabaseWrapper::LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); - s.BindString(0, publicId); - - if (!s.Step()) - { - return false; - } - else - { - id = s.ColumnInt(0); - type = static_cast<ResourceType>(s.ColumnInt(1)); - - // Check whether there is a single resource with this public id - assert(!s.Step()); - - return true; - } - } - - - void SQLiteDatabaseWrapper::AttachChild(int64_t parent, - int64_t child) + void SQLiteDatabaseWrapper::UnitTestsTransaction::AttachChild(int64_t parent, + int64_t child) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); s.BindInt64(0, parent); @@ -791,150 +1457,9 @@ } - void SQLiteDatabaseWrapper::SetMetadata(int64_t id, - MetadataType type, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, type); - s.BindString(2, value); - s.Run(); - } - - - void SQLiteDatabaseWrapper::DeleteMetadata(int64_t id, - MetadataType type) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); - s.BindInt64(0, id); - s.BindInt(1, type); - s.Run(); - } - - - bool SQLiteDatabaseWrapper::LookupMetadata(std::string& target, - int64_t id, - MetadataType type) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT value FROM Metadata WHERE id=? AND type=?"); - s.BindInt64(0, id); - s.BindInt(1, type); - - if (!s.Step()) - { - return false; - } - else - { - target = s.ColumnString(0); - return true; - } - } - - - void SQLiteDatabaseWrapper::AddAttachment(int64_t id, - const FileInfo& attachment) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, attachment.GetContentType()); - s.BindString(2, attachment.GetUuid()); - s.BindInt64(3, attachment.GetCompressedSize()); - s.BindInt64(4, attachment.GetUncompressedSize()); - s.BindInt(5, attachment.GetCompressionType()); - s.BindString(6, attachment.GetUncompressedMD5()); - s.BindString(7, attachment.GetCompressedMD5()); - s.Run(); - } - - - void SQLiteDatabaseWrapper::DeleteAttachment(int64_t id, - FileContentType attachment) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); - s.BindInt64(0, id); - s.BindInt(1, attachment); - s.Run(); - } - - - void SQLiteDatabaseWrapper::ListAvailableAttachments(std::set<FileContentType>& target, - int64_t id) - { - target.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT fileType FROM AttachedFiles WHERE id=?"); - s.BindInt64(0, id); - - while (s.Step()) - { - target.insert(static_cast<FileContentType>(s.ColumnInt(0))); - } - } - - bool SQLiteDatabaseWrapper::LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT uuid, uncompressedSize, compressionType, compressedSize, " - "uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); - s.BindInt64(0, id); - s.BindInt(1, contentType); - - if (!s.Step()) - { - return false; - } - else - { - attachment = FileInfo(s.ColumnString(0), - contentType, - s.ColumnInt64(1), - s.ColumnString(4), - static_cast<CompressionType>(s.ColumnInt(2)), - s.ColumnInt64(3), - s.ColumnString(5)); - return true; - } - } - - - void SQLiteDatabaseWrapper::ClearMainDicomTags(int64_t id) - { - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); - s.BindInt64(0, id); - s.Run(); - } - - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); - s.BindInt64(0, id); - s.Run(); - } - } - - - void SQLiteDatabaseWrapper::SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, tag.GetGroup()); - s.BindInt(2, tag.GetElement()); - s.BindString(3, value); - s.Run(); - } - - - void SQLiteDatabaseWrapper::SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) + void SQLiteDatabaseWrapper::UnitTestsTransaction::SetIdentifierTag(int64_t id, + const DicomTag& tag, + const std::string& value) { SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); s.BindInt64(0, id); @@ -945,427 +1470,40 @@ } - void SQLiteDatabaseWrapper::GetMainDicomTags(DicomMap& map, - int64_t id) + void SQLiteDatabaseWrapper::UnitTestsTransaction::SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) { - map.Clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); - s.BindInt64(0, id); - while (s.Step()) - { - map.SetValue(s.ColumnInt(1), - s.ColumnInt(2), - s.ColumnString(3), false); - } - } - - - void SQLiteDatabaseWrapper::GetChildrenPublicId(std::list<std::string>& target, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " - "WHERE a.parentId = b.internalId AND b.internalId = ?"); + SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); s.BindInt64(0, id); - - target.clear(); - - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - - void SQLiteDatabaseWrapper::GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " - "WHERE a.parentId = b.internalId AND b.internalId = ?"); - s.BindInt64(0, id); - - target.clear(); - - while (s.Step()) - { - target.push_back(s.ColumnInt64(0)); - } - } - - - void SQLiteDatabaseWrapper::LogChange(int64_t internalId, - const ServerIndexChange& change) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); - s.BindInt(0, change.GetChangeType()); - s.BindInt64(1, internalId); - s.BindInt(2, change.GetResourceType()); - s.BindString(3, change.GetDate()); + s.BindInt(1, tag.GetGroup()); + s.BindInt(2, tag.GetElement()); + s.BindString(3, value); s.Run(); } - void SQLiteDatabaseWrapper::LogExportedResource(const ExportedResource& resource) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); - - s.BindInt(0, resource.GetResourceType()); - s.BindString(1, resource.GetPublicId()); - s.BindString(2, resource.GetModality()); - s.BindString(3, resource.GetPatientId()); - s.BindString(4, resource.GetStudyInstanceUid()); - s.BindString(5, resource.GetSeriesInstanceUid()); - s.BindString(6, resource.GetSopInstanceUid()); - s.BindString(7, resource.GetDate()); - s.Run(); - } - - - void SQLiteDatabaseWrapper::GetExportedResources(std::list<ExportedResource>& target, - bool& done, - int64_t since, - uint32_t maxResults) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); - s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - GetExportedResourcesInternal(target, done, s, maxResults); - } - - - void SQLiteDatabaseWrapper::GetLastExportedResource(std::list<ExportedResource>& target) - { - bool done; // Ignored - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); - GetExportedResourcesInternal(target, done, s, 1); - } - - - uint64_t SQLiteDatabaseWrapper::GetTotalCompressedSize() - { - // Old SQL query that was used in Orthanc <= 1.5.0: - // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=0"); - s.Run(); - return static_cast<uint64_t>(s.ColumnInt64(0)); - } - - - uint64_t SQLiteDatabaseWrapper::GetTotalUncompressedSize() + int64_t SQLiteDatabaseWrapper::UnitTestsTransaction::GetTableRecordCount(const std::string& table) { - // Old SQL query that was used in Orthanc <= 1.5.0: - // SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT value FROM GlobalIntegers WHERE key=1"); - s.Run(); - return static_cast<uint64_t>(s.ColumnInt64(0)); - } - - - uint64_t SQLiteDatabaseWrapper::GetResourceCount(ResourceType resourceType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); - s.BindInt(0, resourceType); - - if (!s.Step()) - { - return 0; - } - else + /** + * "Generally one cannot use SQL parameters/placeholders for + * database identifiers (tables, columns, views, schemas, etc.) or + * database functions (e.g., CURRENT_DATE), but instead only for + * binding literal values." => To avoid any SQL injection, we + * check that the "table" parameter has only alphabetic + * characters. + * https://stackoverflow.com/a/1274764/881731 + **/ + for (size_t i = 0; i < table.size(); i++) { - int64_t c = s.ColumnInt(0); - assert(!s.Step()); - return c; - } - } - - - void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); - s.BindInt(0, resourceType); - - target.clear(); - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - - void SQLiteDatabaseWrapper::GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType, - size_t since, - size_t limit) - { - if (limit == 0) - { - target.clear(); - return; + if (!isalpha(table[i])) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } } - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT publicId FROM Resources WHERE " - "resourceType=? LIMIT ? OFFSET ?"); - s.BindInt(0, resourceType); - s.BindInt64(1, limit); - s.BindInt64(2, since); - - target.clear(); - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - - bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); - - if (!s.Step()) - { - // No patient remaining or all the patients are protected - return false; - } - else - { - internalId = s.ColumnInt(0); - return true; - } - } - - - bool SQLiteDatabaseWrapper::SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT patientId FROM PatientRecyclingOrder " - "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); - s.BindInt64(0, patientIdToAvoid); - - if (!s.Step()) - { - // No patient remaining or all the patients are protected - return false; - } - else - { - internalId = s.ColumnInt(0); - return true; - } - } - - - bool SQLiteDatabaseWrapper::IsProtectedPatient(int64_t internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); - s.BindInt64(0, internalId); - return !s.Step(); - } - - - void SQLiteDatabaseWrapper::SetProtectedPatient(int64_t internalId, - bool isProtected) - { - if (isProtected) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); - s.BindInt64(0, internalId); - s.Run(); - } - else if (IsProtectedPatient(internalId)) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); - s.BindInt64(0, internalId); - s.Run(); - } - else - { - // Nothing to do: The patient is already unprotected - } - } - - - bool SQLiteDatabaseWrapper::IsExistingResource(int64_t internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM Resources WHERE internalId=?"); - s.BindInt64(0, internalId); - return s.Step(); - } - - - bool SQLiteDatabaseWrapper::IsDiskSizeAbove(uint64_t threshold) - { - return GetTotalCompressedSize() > threshold; - } - - - - class SQLiteDatabaseWrapper::LookupFormatter : public ISqlLookupFormatter - { - private: - std::list<std::string> values_; - - public: - virtual std::string GenerateParameter(const std::string& value) ORTHANC_OVERRIDE - { - values_.push_back(value); - return "?"; - } - - virtual std::string FormatResourceType(ResourceType level) ORTHANC_OVERRIDE - { - return boost::lexical_cast<std::string>(level); - } - - virtual std::string FormatWildcardEscape() ORTHANC_OVERRIDE - { - return "ESCAPE '\\'"; - } - - void Bind(SQLite::Statement& statement) const - { - size_t pos = 0; - - for (std::list<std::string>::const_iterator - it = values_.begin(); it != values_.end(); ++it, pos++) - { - statement.BindString(pos, *it); - } - } - }; - - - static void AnswerLookup(std::list<std::string>& resourcesId, - std::list<std::string>& instancesId, - SQLite::Connection& db, - ResourceType level) - { - resourcesId.clear(); - instancesId.clear(); - - std::unique_ptr<SQLite::Statement> statement; - - switch (level) - { - case ResourceType_Patient: - { - statement.reset( - new SQLite::Statement( - db, SQLITE_FROM_HERE, - "SELECT patients.publicId, instances.publicID FROM Lookup AS patients " - "INNER JOIN Resources studies ON patients.internalId=studies.parentId " - "INNER JOIN Resources series ON studies.internalId=series.parentId " - "INNER JOIN Resources instances ON series.internalId=instances.parentId " - "GROUP BY patients.publicId")); - - break; - } - - case ResourceType_Study: - { - statement.reset( - new SQLite::Statement( - db, SQLITE_FROM_HERE, - "SELECT studies.publicId, instances.publicID FROM Lookup AS studies " - "INNER JOIN Resources series ON studies.internalId=series.parentId " - "INNER JOIN Resources instances ON series.internalId=instances.parentId " - "GROUP BY studies.publicId")); - - break; - } - - case ResourceType_Series: - { - statement.reset( - new SQLite::Statement( - db, SQLITE_FROM_HERE, - "SELECT series.publicId, instances.publicID FROM Lookup AS series " - "INNER JOIN Resources instances ON series.internalId=instances.parentId " - "GROUP BY series.publicId")); - - break; - } - - case ResourceType_Instance: - { - statement.reset( - new SQLite::Statement( - db, SQLITE_FROM_HERE, "SELECT publicId, publicId FROM Lookup")); - - break; - } - - default: - throw OrthancException(ErrorCode_InternalError); - } - - assert(statement.get() != NULL); - - while (statement->Step()) - { - resourcesId.push_back(statement->ColumnString(0)); - instancesId.push_back(statement->ColumnString(1)); - } - } - - - void SQLiteDatabaseWrapper::ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - { - LookupFormatter formatter; - - std::string sql; - LookupFormatter::Apply(sql, formatter, lookup, queryLevel, limit); - - sql = "CREATE TEMPORARY TABLE Lookup AS " + sql; - - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS Lookup"); - s.Run(); - } - - { - SQLite::Statement statement(db_, sql); - formatter.Bind(statement); - statement.Run(); - } - - if (instancesId != NULL) - { - AnswerLookup(resourcesId, *instancesId, db_, queryLevel); - } - else - { - resourcesId.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Lookup"); - - while (s.Step()) - { - resourcesId.push_back(s.ColumnString(0)); - } - } - } - - - int64_t SQLiteDatabaseWrapper::GetLastChangeIndex() - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT seq FROM sqlite_sequence WHERE name='Changes'"); + // Don't use "SQLITE_FROM_HERE", otherwise "table" would be cached + SQLite::Statement s(db_, "SELECT COUNT(*) FROM " + table); if (s.Step()) { @@ -1375,35 +1513,40 @@ } else { - // No change has been recorded so far in the database - return 0; + throw OrthancException(ErrorCode_InternalError); } } - void SQLiteDatabaseWrapper::TagMostRecentPatient(int64_t patient) + bool SQLiteDatabaseWrapper::UnitTestsTransaction::GetParentPublicId(std::string& target, + int64_t id) { - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); - s.BindInt64(0, patient); - s.Run(); + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " + "WHERE a.internalId = b.parentId AND b.internalId = ?"); + s.BindInt64(0, id); - assert(db_.GetLastChangeCount() == 0 || - db_.GetLastChangeCount() == 1); - - if (db_.GetLastChangeCount() == 0) - { - // The patient was protected, there was nothing to delete from the recycling order - return; - } + if (s.Step()) + { + target = s.ColumnString(0); + return true; + } + else + { + return false; } + } + + void SQLiteDatabaseWrapper::UnitTestsTransaction::GetChildren(std::list<std::string>& childrenPublicIds, + int64_t id) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE parentId=?"); + s.BindInt64(0, id); + + childrenPublicIds.clear(); + while (s.Step()) { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); - s.BindInt64(0, patient); - s.Run(); + childrenPublicIds.push_back(s.ColumnString(0)); } } }
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.h Mon Mar 15 15:30:42 2021 +0100 @@ -36,10 +36,6 @@ #include "IDatabaseWrapper.h" #include "../../../OrthancFramework/Sources/SQLite/Connection.h" -#include "Compatibility/ICreateInstance.h" -#include "Compatibility/IGetChildrenMetadata.h" -#include "Compatibility/ILookupResourceAndParent.h" -#include "Compatibility/ISetResourcesContent.h" namespace Orthanc { @@ -48,14 +44,10 @@ * translates low-level requests into SQL statements. Mutual * exclusion MUST be implemented at a higher level. **/ - class SQLiteDatabaseWrapper : - public IDatabaseWrapper, - public Compatibility::ICreateInstance, - public Compatibility::IGetChildrenMetadata, - public Compatibility::ILookupResourceAndParent, - public Compatibility::ISetResourcesContent + class SQLiteDatabaseWrapper : public IDatabaseWrapper { private: + class TransactionBase; class SignalFileDeleted; class SignalResourceDeleted; class SignalRemainingAncestor; @@ -63,8 +55,8 @@ class ReadWriteTransaction; class LookupFormatter; - IDatabaseListener* listener_; SQLite::Connection db_; + TransactionBase* activeTransaction_; SignalRemainingAncestor* signalRemainingAncestor_; unsigned int version_; @@ -78,292 +70,82 @@ SQLite::Statement& s, uint32_t maxResults); - void ClearTable(const std::string& tableName); - - // Unused => could be removed - int GetGlobalIntegerProperty(GlobalProperty property, - int defaultValue); - public: SQLiteDatabaseWrapper(const std::string& path); SQLiteDatabaseWrapper(); - virtual void Open() - ORTHANC_OVERRIDE; + virtual ~SQLiteDatabaseWrapper(); - virtual void Close() - ORTHANC_OVERRIDE + virtual void Open() ORTHANC_OVERRIDE; + + virtual void Close() ORTHANC_OVERRIDE { db_.Close(); } - virtual bool LookupParent(int64_t& parentId, - int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual std::string GetPublicId(int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual ResourceType GetResourceType(int64_t resourceId) - ORTHANC_OVERRIDE; - - virtual void DeleteResource(int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - ORTHANC_OVERRIDE; - - virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) - ORTHANC_OVERRIDE; - virtual IDatabaseWrapper::ITransaction* StartTransaction(TransactionType type, IDatabaseListener& listener) ORTHANC_OVERRIDE; - virtual void FlushToDisk() - ORTHANC_OVERRIDE + virtual void FlushToDisk() ORTHANC_OVERRIDE { db_.FlushToDisk(); } - virtual bool HasFlushToDisk() const - ORTHANC_OVERRIDE + virtual bool HasFlushToDisk() const ORTHANC_OVERRIDE { return true; } - virtual void ClearChanges() - ORTHANC_OVERRIDE - { - ClearTable("Changes"); - } - - virtual void ClearExportedResources() - ORTHANC_OVERRIDE - { - ClearTable("ExportedResources"); - } - - virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual unsigned int GetDatabaseVersion() - ORTHANC_OVERRIDE + virtual unsigned int GetDatabaseVersion() ORTHANC_OVERRIDE { return version_; } virtual void Upgrade(unsigned int targetVersion, - IStorageArea& storageArea) - ORTHANC_OVERRIDE; - - - /** - * The methods declared below are for unit testing only! - **/ - - const char* GetErrorMessage() const - { - return db_.GetErrorMessage(); - } - - void GetChildren(std::list<std::string>& childrenPublicIds, - int64_t id); - - int64_t GetTableRecordCount(const std::string& table); - - bool GetParentPublicId(std::string& target, - int64_t id); + IStorageArea& storageArea) ORTHANC_OVERRIDE; /** - * Until Orthanc 1.4.0, the methods below were part of the - * "DatabaseWrapperBase" class, that is now placed in the - * graveyard. + * The "StartTransaction()" method is guaranteed to return a class + * derived from "UnitTestsTransaction". The methods of + * "UnitTestsTransaction" give access to additional information + * about the underlying SQLite database to be used in unit tests. **/ - - virtual void SetGlobalProperty(GlobalProperty property, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual bool LookupGlobalProperty(std::string& target, - GlobalProperty property) - ORTHANC_OVERRIDE; - - virtual int64_t CreateResource(const std::string& publicId, - ResourceType type) - ORTHANC_OVERRIDE; - - virtual bool LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) - ORTHANC_OVERRIDE; - - virtual void AttachChild(int64_t parent, - int64_t child) - ORTHANC_OVERRIDE; - - virtual void SetMetadata(int64_t id, - MetadataType type, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void DeleteMetadata(int64_t id, - MetadataType type) - ORTHANC_OVERRIDE; - - virtual bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type) - ORTHANC_OVERRIDE; - - virtual void AddAttachment(int64_t id, - const FileInfo& attachment) - ORTHANC_OVERRIDE; + class UnitTestsTransaction : public ITransaction + { + protected: + SQLite::Connection& db_; + + public: + UnitTestsTransaction(SQLite::Connection& db) : + db_(db) + { + } + + void GetChildren(std::list<std::string>& childrenPublicIds, + int64_t id); - virtual void DeleteAttachment(int64_t id, - FileContentType attachment) - ORTHANC_OVERRIDE; - - virtual void ListAvailableAttachments(std::set<FileContentType>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual bool LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) - ORTHANC_OVERRIDE; - - virtual void ClearMainDicomTags(int64_t id) - ORTHANC_OVERRIDE; - - virtual void SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) - ORTHANC_OVERRIDE; - - virtual void GetMainDicomTags(DicomMap& map, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetChildrenPublicId(std::list<std::string>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void LogChange(int64_t internalId, - const ServerIndexChange& change) - ORTHANC_OVERRIDE; - - virtual void LogExportedResource(const ExportedResource& resource) - ORTHANC_OVERRIDE; + int64_t GetTableRecordCount(const std::string& table); - virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, - bool& done /*out*/, - int64_t since, - uint32_t maxResults) - ORTHANC_OVERRIDE; - - virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) - ORTHANC_OVERRIDE; - - virtual uint64_t GetTotalCompressedSize() - ORTHANC_OVERRIDE; - - virtual uint64_t GetTotalUncompressedSize() - ORTHANC_OVERRIDE; - - virtual uint64_t GetResourceCount(ResourceType resourceType) - ORTHANC_OVERRIDE; - - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType) - ORTHANC_OVERRIDE; + bool GetParentPublicId(std::string& target, + int64_t id); - virtual void GetAllPublicIds(std::list<std::string>& target, - ResourceType resourceType, - size_t since, - size_t limit) - ORTHANC_OVERRIDE; - - virtual bool SelectPatientToRecycle(int64_t& internalId) - ORTHANC_OVERRIDE; + int64_t CreateResource(const std::string& publicId, + ResourceType type); - virtual bool SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) - ORTHANC_OVERRIDE; - - virtual bool IsProtectedPatient(int64_t internalId) - ORTHANC_OVERRIDE; - - virtual void SetProtectedPatient(int64_t internalId, - bool isProtected) - ORTHANC_OVERRIDE; - - virtual bool IsExistingResource(int64_t internalId) - ORTHANC_OVERRIDE; + void AttachChild(int64_t parent, + int64_t child); - virtual bool IsDiskSizeAbove(uint64_t threshold) - ORTHANC_OVERRIDE; - - virtual void ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - ORTHANC_OVERRIDE; - - virtual bool CreateInstance(CreateInstanceResult& result, - int64_t& instanceId, - const std::string& patient, - const std::string& study, - const std::string& series, - const std::string& instance) - ORTHANC_OVERRIDE - { - return ICreateInstance::Apply - (*this, result, instanceId, patient, study, series, instance); - } + void SetIdentifierTag(int64_t id, + const DicomTag& tag, + const std::string& value); - virtual void SetResourcesContent(const Orthanc::ResourcesContent& content) - ORTHANC_OVERRIDE - { - ISetResourcesContent::Apply(*this, content); - } - - virtual void GetChildrenMetadata(std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata) - ORTHANC_OVERRIDE - { - IGetChildrenMetadata::Apply(*this, target, resourceId, metadata); - } - - virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE; - - virtual void TagMostRecentPatient(int64_t patient) ORTHANC_OVERRIDE; - - virtual bool LookupResourceAndParent(int64_t& id, - ResourceType& type, - std::string& parentPublicId, - const std::string& publicId) - ORTHANC_OVERRIDE - { - return ILookupResourceAndParent::Apply(*this, id, type, parentPublicId, publicId); - } + void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value); + }; }; }
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -425,7 +425,7 @@ if (changeType <= ChangeType_INTERNAL_LastLogged) { - db_.LogChange(internalId, change); + transaction_.LogChange(internalId, change); } GetTransactionContext().SignalChange(change); @@ -436,7 +436,7 @@ int64_t expectedNumberOfInstances) { std::list<std::string> values; - db_.GetChildrenMetadata(values, id, MetadataType_Instance_IndexInSeries); + transaction_.GetChildrenMetadata(values, id, MetadataType_Instance_IndexInSeries); std::set<int64_t> instances; @@ -555,6 +555,12 @@ } } + IDatabaseWrapper::ITransaction& GetDatabaseTransaction() + { + assert(transaction_.get() != NULL); + return *transaction_; + } + void Commit() { if (isCommitted_) @@ -611,7 +617,7 @@ Transaction transaction(db_, *factory_, TransactionType_ReadOnly); // TODO - Only if not "TransactionType_Implicit" { - ReadOnlyTransaction t(db_, transaction.GetContext()); + ReadOnlyTransaction t(transaction.GetDatabaseTransaction(), transaction.GetContext()); readOperations->Apply(t); } transaction.Commit(); @@ -622,7 +628,7 @@ Transaction transaction(db_, *factory_, TransactionType_ReadWrite); { - ReadWriteTransaction t(db_, transaction.GetContext()); + ReadWriteTransaction t(transaction.GetDatabaseTransaction(), transaction.GetContext()); writeOperations->Apply(t); } transaction.Commit(); @@ -2501,7 +2507,7 @@ DicomTransferSyntax transferSyntax_; public: - Operations(const ParsedDicomFile& dicom) + explicit Operations(const ParsedDicomFile& dicom) { OrthancConfiguration::DefaultExtractDicomSummary(summary_, dicom); hasher_.reset(new DicomInstanceHasher(summary_)); @@ -2563,7 +2569,7 @@ } - static bool IsRecyclingNeeded(IDatabaseWrapper& db, + static bool IsRecyclingNeeded(IDatabaseWrapper::ITransaction& transaction, uint64_t maximumStorageSize, unsigned int maximumPatients, uint64_t addedInstanceSize) @@ -2578,7 +2584,7 @@ boost::lexical_cast<std::string>(maximumStorageSize)); } - if (db.IsDiskSizeAbove(maximumStorageSize - addedInstanceSize)) + if (transaction.IsDiskSizeAbove(maximumStorageSize - addedInstanceSize)) { return true; } @@ -2586,7 +2592,7 @@ if (maximumPatients != 0) { - uint64_t patientCount = db.GetResourceCount(ResourceType_Patient); + uint64_t patientCount = transaction.GetResourceCount(ResourceType_Patient); if (patientCount > maximumPatients) { return true; @@ -2604,7 +2610,7 @@ { // TODO - Performance: Avoid calls to "IsRecyclingNeeded()" - if (IsRecyclingNeeded(db_, maximumStorageSize, maximumPatients, addedInstanceSize)) + if (IsRecyclingNeeded(transaction_, maximumStorageSize, maximumPatients, addedInstanceSize)) { // Check whether other DICOM instances from this patient are // already stored @@ -2618,7 +2624,7 @@ else { ResourceType type; - hasPatientToAvoid = db_.LookupResource(patientToAvoid, type, newPatientId); + hasPatientToAvoid = transaction_.LookupResource(patientToAvoid, type, newPatientId); if (type != ResourceType_Patient) { throw OrthancException(ErrorCode_InternalError); @@ -2633,8 +2639,8 @@ // If other instances of this patient are already in the store, // we must avoid to recycle them bool ok = (hasPatientToAvoid ? - db_.SelectPatientToRecycle(patientToRecycle, patientToAvoid) : - db_.SelectPatientToRecycle(patientToRecycle)); + transaction_.SelectPatientToRecycle(patientToRecycle, patientToAvoid) : + transaction_.SelectPatientToRecycle(patientToRecycle)); if (!ok) { @@ -2642,9 +2648,9 @@ } LOG(TRACE) << "Recycling one patient"; - db_.DeleteResource(patientToRecycle); - - if (!IsRecyclingNeeded(db_, maximumStorageSize, maximumPatients, addedInstanceSize)) + transaction_.DeleteResource(patientToRecycle); + + if (!IsRecyclingNeeded(transaction_, maximumStorageSize, maximumPatients, addedInstanceSize)) { // OK, we're done return;
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Mon Mar 15 15:30:42 2021 +0100 @@ -96,13 +96,13 @@ ITransactionContext& context_; protected: - IDatabaseWrapper& db_; + IDatabaseWrapper::ITransaction& transaction_; public: - explicit ReadOnlyTransaction(IDatabaseWrapper& db, + explicit ReadOnlyTransaction(IDatabaseWrapper::ITransaction& transaction, ITransactionContext& context) : context_(context), - db_(db) + transaction_(transaction) { } @@ -129,19 +129,19 @@ ResourceType queryLevel, size_t limit) { - return db_.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); + return transaction_.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); } void GetAllMetadata(std::map<MetadataType, std::string>& target, int64_t id) { - db_.GetAllMetadata(target, id); + transaction_.GetAllMetadata(target, id); } void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) { - return db_.GetAllPublicIds(target, resourceType); + return transaction_.GetAllPublicIds(target, resourceType); } void GetAllPublicIds(std::list<std::string>& target, @@ -149,7 +149,7 @@ size_t since, size_t limit) { - return db_.GetAllPublicIds(target, resourceType, since, limit); + return transaction_.GetAllPublicIds(target, resourceType, since, limit); } void GetChanges(std::list<ServerIndexChange>& target /*out*/, @@ -157,19 +157,19 @@ int64_t since, uint32_t maxResults) { - db_.GetChanges(target, done, since, maxResults); + transaction_.GetChanges(target, done, since, maxResults); } void GetChildrenInternalId(std::list<int64_t>& target, int64_t id) { - db_.GetChildrenInternalId(target, id); + transaction_.GetChildrenInternalId(target, id); } void GetChildrenPublicId(std::list<std::string>& target, int64_t id) { - db_.GetChildrenPublicId(target, id); + transaction_.GetChildrenPublicId(target, id); } void GetExportedResources(std::list<ExportedResource>& target /*out*/, @@ -177,97 +177,97 @@ int64_t since, uint32_t maxResults) { - return db_.GetExportedResources(target, done, since, maxResults); + return transaction_.GetExportedResources(target, done, since, maxResults); } void GetLastChange(std::list<ServerIndexChange>& target /*out*/) { - db_.GetLastChange(target); + transaction_.GetLastChange(target); } void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) { - return db_.GetLastExportedResource(target); + return transaction_.GetLastExportedResource(target); } int64_t GetLastChangeIndex() { - return db_.GetLastChangeIndex(); + return transaction_.GetLastChangeIndex(); } void GetMainDicomTags(DicomMap& map, int64_t id) { - db_.GetMainDicomTags(map, id); + transaction_.GetMainDicomTags(map, id); } std::string GetPublicId(int64_t resourceId) { - return db_.GetPublicId(resourceId); + return transaction_.GetPublicId(resourceId); } uint64_t GetResourceCount(ResourceType resourceType) { - return db_.GetResourceCount(resourceType); + return transaction_.GetResourceCount(resourceType); } ResourceType GetResourceType(int64_t resourceId) { - return db_.GetResourceType(resourceId); + return transaction_.GetResourceType(resourceId); } uint64_t GetTotalCompressedSize() { - return db_.GetTotalCompressedSize(); + return transaction_.GetTotalCompressedSize(); } uint64_t GetTotalUncompressedSize() { - return db_.GetTotalUncompressedSize(); + return transaction_.GetTotalUncompressedSize(); } bool IsProtectedPatient(int64_t internalId) { - return db_.IsProtectedPatient(internalId); + return transaction_.IsProtectedPatient(internalId); } void ListAvailableAttachments(std::set<FileContentType>& target, int64_t id) { - db_.ListAvailableAttachments(target, id); + transaction_.ListAvailableAttachments(target, id); } bool LookupAttachment(FileInfo& attachment, int64_t id, FileContentType contentType) { - return db_.LookupAttachment(attachment, id, contentType); + return transaction_.LookupAttachment(attachment, id, contentType); } bool LookupGlobalProperty(std::string& target, GlobalProperty property) { - return db_.LookupGlobalProperty(target, property); + return transaction_.LookupGlobalProperty(target, property); } bool LookupMetadata(std::string& target, int64_t id, MetadataType type) { - return db_.LookupMetadata(target, id, type); + return transaction_.LookupMetadata(target, id, type); } bool LookupParent(int64_t& parentId, int64_t resourceId) { - return db_.LookupParent(parentId, resourceId); + return transaction_.LookupParent(parentId, resourceId); } bool LookupResource(int64_t& id, ResourceType& type, const std::string& publicId) { - return db_.LookupResource(id, type, publicId); + return transaction_.LookupResource(id, type, publicId); } bool LookupResourceAndParent(int64_t& id, @@ -275,7 +275,7 @@ std::string& parentPublicId, const std::string& publicId) { - return db_.LookupResourceAndParent(id, type, parentPublicId, publicId); + return transaction_.LookupResourceAndParent(id, type, parentPublicId, publicId); } }; @@ -283,31 +283,31 @@ class ReadWriteTransaction : public ReadOnlyTransaction { public: - ReadWriteTransaction(IDatabaseWrapper& db, + ReadWriteTransaction(IDatabaseWrapper::ITransaction& transaction, ITransactionContext& context) : - ReadOnlyTransaction(db, context) + ReadOnlyTransaction(transaction, context) { } void AddAttachment(int64_t id, const FileInfo& attachment) { - db_.AddAttachment(id, attachment); + transaction_.AddAttachment(id, attachment); } void ClearChanges() { - db_.ClearChanges(); + transaction_.ClearChanges(); } void ClearExportedResources() { - db_.ClearExportedResources(); + transaction_.ClearExportedResources(); } void ClearMainDicomTags(int64_t id) { - return db_.ClearMainDicomTags(id); + return transaction_.ClearMainDicomTags(id); } bool CreateInstance(IDatabaseWrapper::CreateInstanceResult& result, /* out */ @@ -317,24 +317,24 @@ const std::string& series, const std::string& instance) { - return db_.CreateInstance(result, instanceId, patient, study, series, instance); + return transaction_.CreateInstance(result, instanceId, patient, study, series, instance); } void DeleteAttachment(int64_t id, FileContentType attachment) { - return db_.DeleteAttachment(id, attachment); + return transaction_.DeleteAttachment(id, attachment); } void DeleteMetadata(int64_t id, MetadataType type) { - db_.DeleteMetadata(id, type); + transaction_.DeleteMetadata(id, type); } void DeleteResource(int64_t id) { - db_.DeleteResource(id); + transaction_.DeleteResource(id); } void LogChange(int64_t internalId, @@ -344,31 +344,31 @@ void LogExportedResource(const ExportedResource& resource) { - db_.LogExportedResource(resource); + transaction_.LogExportedResource(resource); } void SetGlobalProperty(GlobalProperty property, const std::string& value) { - db_.SetGlobalProperty(property, value); + transaction_.SetGlobalProperty(property, value); } void SetMetadata(int64_t id, MetadataType type, const std::string& value) { - return db_.SetMetadata(id, type, value); + return transaction_.SetMetadata(id, type, value); } void SetProtectedPatient(int64_t internalId, bool isProtected) { - db_.SetProtectedPatient(internalId, isProtected); + transaction_.SetProtectedPatient(internalId, isProtected); } void SetResourcesContent(const ResourcesContent& content) { - db_.SetResourcesContent(content); + transaction_.SetResourcesContent(content); } void Recycle(uint64_t maximumStorageSize, @@ -423,7 +423,7 @@ unsigned int maximumPatientCount); public: - StatelessDatabaseOperations(IDatabaseWrapper& database); + explicit StatelessDatabaseOperations(IDatabaseWrapper& database); void SetTransactionContextFactory(ITransactionContextFactory* factory /* takes ownership */);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/VoidDatabaseListener.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -0,0 +1,57 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeadersServer.h" +#include "VoidDatabaseListener.h" + +#include "../../../OrthancFramework/Sources/OrthancException.h" + +namespace Orthanc +{ + void VoidDatabaseListener::SignalRemainingAncestor(ResourceType parentType, + const std::string& publicId) + { + throw OrthancException(ErrorCode_InternalError); + } + + void VoidDatabaseListener::SignalAttachmentDeleted(const FileInfo& info) + { + throw OrthancException(ErrorCode_InternalError); + } + + void VoidDatabaseListener::SignalResourceDeleted(ResourceType type, + const std::string& publicId) + { + throw OrthancException(ErrorCode_InternalError); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/Database/VoidDatabaseListener.h Mon Mar 15 15:30:42 2021 +0100 @@ -0,0 +1,56 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../../OrthancFramework/Sources/Compatibility.h" +#include "IDatabaseListener.h" + +namespace Orthanc +{ + /** + * This is a listener that can be used for transactions that do + * not create/delete attachments or resources. + **/ + class VoidDatabaseListener : public IDatabaseListener + { + public: + virtual void SignalRemainingAncestor(ResourceType parentType, + const std::string& publicId) ORTHANC_OVERRIDE; + + virtual void SignalAttachmentDeleted(const FileInfo& info) ORTHANC_OVERRIDE; + + virtual void SignalResourceDeleted(ResourceType type, + const std::string& publicId) ORTHANC_OVERRIDE; + }; +}
--- a/OrthancServer/Sources/ServerIndex.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/ServerIndex.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -91,6 +91,7 @@ { sizeOfFilesToRemove_ = 0; hasRemainingLevel_ = false; + remainingType_ = ResourceType_Instance; // dummy initialization pendingFilesToRemove_.clear(); pendingChanges_.clear(); sizeOfAddedAttachments_ = 0; @@ -234,7 +235,7 @@ ServerContext& context_; public: - TransactionContextFactory(ServerContext& context) : + explicit TransactionContextFactory(ServerContext& context) : context_(context) { }
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -711,7 +711,7 @@ unsigned int counter_; public: - MediaIndexVisitor(ZipCommands& commands) : + explicit MediaIndexVisitor(ZipCommands& commands) : commands_(commands), counter_(0) { @@ -750,7 +750,7 @@ bool isMedia_; public: - ZipWriterIterator(TemporaryFile& target, + ZipWriterIterator(const TemporaryFile& target, ServerContext& context, ArchiveIndex& archive, bool isMedia,
--- a/OrthancServer/Sources/ServerToolbox.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/ServerToolbox.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -165,7 +165,7 @@ namespace ServerToolbox { bool FindOneChildInstance(int64_t& result, - IDatabaseWrapper& database, + IDatabaseWrapper::ITransaction& transaction, int64_t resource, ResourceType type) { @@ -178,7 +178,7 @@ } std::list<int64_t> children; - database.GetChildrenInternalId(children, resource); + transaction.GetChildrenInternalId(children, resource); if (children.empty()) { return false; @@ -190,7 +190,7 @@ } - void ReconstructMainDicomTags(IDatabaseWrapper& database, + void ReconstructMainDicomTags(IDatabaseWrapper::ITransaction& transaction, IStorageArea& storageArea, ResourceType level) { @@ -230,7 +230,7 @@ LOG(WARNING) << "Upgrade: Reconstructing the main DICOM tags of all the " << plural << "..."; std::list<std::string> resources; - database.GetAllPublicIds(resources, level); + transaction.GetAllPublicIds(resources, level); for (std::list<std::string>::const_iterator it = resources.begin(); it != resources.end(); ++it) @@ -239,9 +239,9 @@ int64_t resource, instance; ResourceType tmp; - if (!database.LookupResource(resource, tmp, *it) || + if (!transaction.LookupResource(resource, tmp, *it) || tmp != level || - !FindOneChildInstance(instance, database, resource, level)) + !FindOneChildInstance(instance, transaction, resource, level)) { throw OrthancException(ErrorCode_InternalError, "Cannot find an instance for " + @@ -251,11 +251,11 @@ // Get the DICOM file attached to some instances in the resource FileInfo attachment; - if (!database.LookupAttachment(attachment, instance, FileContentType_Dicom)) + if (!transaction.LookupAttachment(attachment, instance, FileContentType_Dicom)) { throw OrthancException(ErrorCode_InternalError, "Cannot retrieve the DICOM file associated with instance " + - database.GetPublicId(instance)); + transaction.GetPublicId(instance)); } try @@ -272,16 +272,16 @@ DicomMap dicomSummary; OrthancConfiguration::DefaultExtractDicomSummary(dicomSummary, dicom); - database.ClearMainDicomTags(resource); + transaction.ClearMainDicomTags(resource); ResourcesContent tags; tags.AddResource(resource, level, dicomSummary); - database.SetResourcesContent(tags); + transaction.SetResourcesContent(tags); } catch (OrthancException&) { LOG(ERROR) << "Cannot decode the DICOM file with UUID " << attachment.GetUuid() - << " associated with instance " << database.GetPublicId(instance); + << " associated with instance " << transaction.GetPublicId(instance); throw; } }
--- a/OrthancServer/Sources/ServerToolbox.h Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/Sources/ServerToolbox.h Mon Mar 15 15:30:42 2021 +0100 @@ -33,6 +33,7 @@ #pragma once +#include "Database/IDatabaseWrapper.h" #include "ServerEnumerations.h" #include <boost/noncopyable.hpp> @@ -41,17 +42,16 @@ namespace Orthanc { class ServerContext; - class IDatabaseWrapper; class IStorageArea; namespace ServerToolbox { bool FindOneChildInstance(int64_t& result, - IDatabaseWrapper& database, + IDatabaseWrapper::ITransaction& transaction, int64_t resource, ResourceType type); - void ReconstructMainDicomTags(IDatabaseWrapper& database, + void ReconstructMainDicomTags(IDatabaseWrapper::ITransaction& transaction, IStorageArea& storageArea, ResourceType level);
--- a/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Fri Mar 12 16:04:09 2021 +0100 +++ b/OrthancServer/UnitTestsSources/ServerIndexTests.cpp Mon Mar 15 15:30:42 2021 +0100 @@ -96,7 +96,7 @@ protected: std::unique_ptr<TestDatabaseListener> listener_; std::unique_ptr<SQLiteDatabaseWrapper> index_; - std::unique_ptr<IDatabaseWrapper::ITransaction> transaction_; + std::unique_ptr<SQLiteDatabaseWrapper::UnitTestsTransaction> transaction_; public: DatabaseWrapperTest() @@ -108,7 +108,8 @@ listener_.reset(new TestDatabaseListener); index_.reset(new SQLiteDatabaseWrapper); index_->Open(); - transaction_.reset(index_->StartTransaction(TransactionType_ReadWrite, *listener_)); + transaction_.reset(dynamic_cast<SQLiteDatabaseWrapper::UnitTestsTransaction*>( + index_->StartTransaction(TransactionType_ReadWrite, *listener_))); } virtual void TearDown() ORTHANC_OVERRIDE @@ -123,33 +124,33 @@ void CheckTableRecordCount(uint32_t expected, const char* table) { - ASSERT_EQ(expected, index_->GetTableRecordCount(table)); + ASSERT_EQ(expected, transaction_->GetTableRecordCount(table)); } void CheckNoParent(int64_t id) { std::string s; - ASSERT_FALSE(index_->GetParentPublicId(s, id)); + ASSERT_FALSE(transaction_->GetParentPublicId(s, id)); } void CheckParentPublicId(const char* expected, int64_t id) { std::string s; - ASSERT_TRUE(index_->GetParentPublicId(s, id)); + ASSERT_TRUE(transaction_->GetParentPublicId(s, id)); ASSERT_EQ(expected, s); } void CheckNoChild(int64_t id) { std::list<std::string> j; - index_->GetChildren(j, id); + transaction_->GetChildren(j, id); ASSERT_EQ(0u, j.size()); } void CheckOneChild(const char* expected, int64_t id) { std::list<std::string> j; - index_->GetChildren(j, id); + transaction_->GetChildren(j, id); ASSERT_EQ(1u, j.size()); ASSERT_EQ(expected, j.front()); } @@ -159,7 +160,7 @@ int64_t id) { std::list<std::string> j; - index_->GetChildren(j, id); + transaction_->GetChildren(j, id); ASSERT_EQ(2u, j.size()); ASSERT_TRUE((expected1 == j.front() && expected2 == j.back()) || (expected1 == j.back() && expected2 == j.front())); @@ -178,7 +179,7 @@ std::vector<DatabaseConstraint> lookup; lookup.push_back(c.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); - index_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); + transaction_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); } void DoLookupIdentifier2(std::list<std::string>& result, @@ -198,7 +199,7 @@ lookup.push_back(c1.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); lookup.push_back(c2.ConvertToDatabaseConstraint(level, DicomTagType_Identifier)); - index_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); + transaction_->ApplyLookupResources(result, NULL, lookup, level, 0 /* no limit */); } }; } @@ -207,65 +208,65 @@ TEST_F(DatabaseWrapperTest, Simple) { int64_t a[] = { - index_->CreateResource("a", ResourceType_Patient), // 0 - index_->CreateResource("b", ResourceType_Study), // 1 - index_->CreateResource("c", ResourceType_Series), // 2 - index_->CreateResource("d", ResourceType_Instance), // 3 - index_->CreateResource("e", ResourceType_Instance), // 4 - index_->CreateResource("f", ResourceType_Instance), // 5 - index_->CreateResource("g", ResourceType_Study) // 6 + transaction_->CreateResource("a", ResourceType_Patient), // 0 + transaction_->CreateResource("b", ResourceType_Study), // 1 + transaction_->CreateResource("c", ResourceType_Series), // 2 + transaction_->CreateResource("d", ResourceType_Instance), // 3 + transaction_->CreateResource("e", ResourceType_Instance), // 4 + transaction_->CreateResource("f", ResourceType_Instance), // 5 + transaction_->CreateResource("g", ResourceType_Study) // 6 }; - ASSERT_EQ("a", index_->GetPublicId(a[0])); - ASSERT_EQ("b", index_->GetPublicId(a[1])); - ASSERT_EQ("c", index_->GetPublicId(a[2])); - ASSERT_EQ("d", index_->GetPublicId(a[3])); - ASSERT_EQ("e", index_->GetPublicId(a[4])); - ASSERT_EQ("f", index_->GetPublicId(a[5])); - ASSERT_EQ("g", index_->GetPublicId(a[6])); + ASSERT_EQ("a", transaction_->GetPublicId(a[0])); + ASSERT_EQ("b", transaction_->GetPublicId(a[1])); + ASSERT_EQ("c", transaction_->GetPublicId(a[2])); + ASSERT_EQ("d", transaction_->GetPublicId(a[3])); + ASSERT_EQ("e", transaction_->GetPublicId(a[4])); + ASSERT_EQ("f", transaction_->GetPublicId(a[5])); + ASSERT_EQ("g", transaction_->GetPublicId(a[6])); - ASSERT_EQ(ResourceType_Patient, index_->GetResourceType(a[0])); - ASSERT_EQ(ResourceType_Study, index_->GetResourceType(a[1])); - ASSERT_EQ(ResourceType_Series, index_->GetResourceType(a[2])); - ASSERT_EQ(ResourceType_Instance, index_->GetResourceType(a[3])); - ASSERT_EQ(ResourceType_Instance, index_->GetResourceType(a[4])); - ASSERT_EQ(ResourceType_Instance, index_->GetResourceType(a[5])); - ASSERT_EQ(ResourceType_Study, index_->GetResourceType(a[6])); + ASSERT_EQ(ResourceType_Patient, transaction_->GetResourceType(a[0])); + ASSERT_EQ(ResourceType_Study, transaction_->GetResourceType(a[1])); + ASSERT_EQ(ResourceType_Series, transaction_->GetResourceType(a[2])); + ASSERT_EQ(ResourceType_Instance, transaction_->GetResourceType(a[3])); + ASSERT_EQ(ResourceType_Instance, transaction_->GetResourceType(a[4])); + ASSERT_EQ(ResourceType_Instance, transaction_->GetResourceType(a[5])); + ASSERT_EQ(ResourceType_Study, transaction_->GetResourceType(a[6])); { std::list<std::string> t; - index_->GetAllPublicIds(t, ResourceType_Patient); + transaction_->GetAllPublicIds(t, ResourceType_Patient); ASSERT_EQ(1u, t.size()); ASSERT_EQ("a", t.front()); - index_->GetAllPublicIds(t, ResourceType_Series); + transaction_->GetAllPublicIds(t, ResourceType_Series); ASSERT_EQ(1u, t.size()); ASSERT_EQ("c", t.front()); - index_->GetAllPublicIds(t, ResourceType_Study); + transaction_->GetAllPublicIds(t, ResourceType_Study); ASSERT_EQ(2u, t.size()); - index_->GetAllPublicIds(t, ResourceType_Instance); + transaction_->GetAllPublicIds(t, ResourceType_Instance); ASSERT_EQ(3u, t.size()); } - index_->SetGlobalProperty(GlobalProperty_FlushSleep, "World"); + transaction_->SetGlobalProperty(GlobalProperty_FlushSleep, "World"); - index_->AttachChild(a[0], a[1]); - index_->AttachChild(a[1], a[2]); - index_->AttachChild(a[2], a[3]); - index_->AttachChild(a[2], a[4]); - index_->AttachChild(a[6], a[5]); + transaction_->AttachChild(a[0], a[1]); + transaction_->AttachChild(a[1], a[2]); + transaction_->AttachChild(a[2], a[3]); + transaction_->AttachChild(a[2], a[4]); + transaction_->AttachChild(a[6], a[5]); int64_t parent; - ASSERT_FALSE(index_->LookupParent(parent, a[0])); - ASSERT_TRUE(index_->LookupParent(parent, a[1])); ASSERT_EQ(a[0], parent); - ASSERT_TRUE(index_->LookupParent(parent, a[2])); ASSERT_EQ(a[1], parent); - ASSERT_TRUE(index_->LookupParent(parent, a[3])); ASSERT_EQ(a[2], parent); - ASSERT_TRUE(index_->LookupParent(parent, a[4])); ASSERT_EQ(a[2], parent); - ASSERT_TRUE(index_->LookupParent(parent, a[5])); ASSERT_EQ(a[6], parent); - ASSERT_FALSE(index_->LookupParent(parent, a[6])); + ASSERT_FALSE(transaction_->LookupParent(parent, a[0])); + ASSERT_TRUE(transaction_->LookupParent(parent, a[1])); ASSERT_EQ(a[0], parent); + ASSERT_TRUE(transaction_->LookupParent(parent, a[2])); ASSERT_EQ(a[1], parent); + ASSERT_TRUE(transaction_->LookupParent(parent, a[3])); ASSERT_EQ(a[2], parent); + ASSERT_TRUE(transaction_->LookupParent(parent, a[4])); ASSERT_EQ(a[2], parent); + ASSERT_TRUE(transaction_->LookupParent(parent, a[5])); ASSERT_EQ(a[6], parent); + ASSERT_FALSE(transaction_->LookupParent(parent, a[6])); std::string s; @@ -278,14 +279,14 @@ CheckParentPublicId("g", a[5]); std::list<std::string> l; - index_->GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); - index_->GetChildrenPublicId(l, a[1]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("c", l.front()); - index_->GetChildrenPublicId(l, a[3]); ASSERT_EQ(0u, l.size()); - index_->GetChildrenPublicId(l, a[4]); ASSERT_EQ(0u, l.size()); - index_->GetChildrenPublicId(l, a[5]); ASSERT_EQ(0u, l.size()); - index_->GetChildrenPublicId(l, a[6]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("f", l.front()); + transaction_->GetChildrenPublicId(l, a[0]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("b", l.front()); + transaction_->GetChildrenPublicId(l, a[1]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("c", l.front()); + transaction_->GetChildrenPublicId(l, a[3]); ASSERT_EQ(0u, l.size()); + transaction_->GetChildrenPublicId(l, a[4]); ASSERT_EQ(0u, l.size()); + transaction_->GetChildrenPublicId(l, a[5]); ASSERT_EQ(0u, l.size()); + transaction_->GetChildrenPublicId(l, a[6]); ASSERT_EQ(1u, l.size()); ASSERT_EQ("f", l.front()); - index_->GetChildrenPublicId(l, a[2]); ASSERT_EQ(2u, l.size()); + transaction_->GetChildrenPublicId(l, a[2]); ASSERT_EQ(2u, l.size()); if (l.front() == "d") { ASSERT_EQ("e", l.back()); @@ -297,64 +298,64 @@ } std::map<MetadataType, std::string> md; - index_->GetAllMetadata(md, a[4]); + transaction_->GetAllMetadata(md, a[4]); ASSERT_EQ(0u, md.size()); - index_->AddAttachment(a[4], FileInfo("my json file", FileContentType_DicomAsJson, 42, "md5", + transaction_->AddAttachment(a[4], FileInfo("my json file", FileContentType_DicomAsJson, 42, "md5", CompressionType_ZlibWithSize, 21, "compressedMD5")); - index_->AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42, "md5")); - index_->AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44, "md5")); - index_->SetMetadata(a[4], MetadataType_RemoteAet, "PINNACLE"); + transaction_->AddAttachment(a[4], FileInfo("my dicom file", FileContentType_Dicom, 42, "md5")); + transaction_->AddAttachment(a[6], FileInfo("world", FileContentType_Dicom, 44, "md5")); + transaction_->SetMetadata(a[4], MetadataType_RemoteAet, "PINNACLE"); - index_->GetAllMetadata(md, a[4]); + transaction_->GetAllMetadata(md, a[4]); ASSERT_EQ(1u, md.size()); ASSERT_EQ("PINNACLE", md[MetadataType_RemoteAet]); - index_->SetMetadata(a[4], MetadataType_ModifiedFrom, "TUTU"); - index_->GetAllMetadata(md, a[4]); + transaction_->SetMetadata(a[4], MetadataType_ModifiedFrom, "TUTU"); + transaction_->GetAllMetadata(md, a[4]); ASSERT_EQ(2u, md.size()); std::map<MetadataType, std::string> md2; - index_->GetAllMetadata(md2, a[4]); + transaction_->GetAllMetadata(md2, a[4]); ASSERT_EQ(2u, md2.size()); ASSERT_EQ("TUTU", md2[MetadataType_ModifiedFrom]); ASSERT_EQ("PINNACLE", md2[MetadataType_RemoteAet]); - index_->DeleteMetadata(a[4], MetadataType_ModifiedFrom); - index_->GetAllMetadata(md, a[4]); + transaction_->DeleteMetadata(a[4], MetadataType_ModifiedFrom); + transaction_->GetAllMetadata(md, a[4]); ASSERT_EQ(1u, md.size()); ASSERT_EQ("PINNACLE", md[MetadataType_RemoteAet]); - index_->GetAllMetadata(md2, a[4]); + transaction_->GetAllMetadata(md2, a[4]); ASSERT_EQ(1u, md2.size()); ASSERT_EQ("PINNACLE", md2[MetadataType_RemoteAet]); - ASSERT_EQ(21u + 42u + 44u, index_->GetTotalCompressedSize()); - ASSERT_EQ(42u + 42u + 44u, index_->GetTotalUncompressedSize()); + ASSERT_EQ(21u + 42u + 44u, transaction_->GetTotalCompressedSize()); + ASSERT_EQ(42u + 42u + 44u, transaction_->GetTotalUncompressedSize()); - index_->SetMainDicomTag(a[3], DicomTag(0x0010, 0x0010), "PatientName"); + transaction_->SetMainDicomTag(a[3], DicomTag(0x0010, 0x0010), "PatientName"); int64_t b; ResourceType t; - ASSERT_TRUE(index_->LookupResource(b, t, "g")); + ASSERT_TRUE(transaction_->LookupResource(b, t, "g")); ASSERT_EQ(7, b); ASSERT_EQ(ResourceType_Study, t); - ASSERT_TRUE(index_->LookupMetadata(s, a[4], MetadataType_RemoteAet)); - ASSERT_FALSE(index_->LookupMetadata(s, a[4], MetadataType_Instance_IndexInSeries)); + ASSERT_TRUE(transaction_->LookupMetadata(s, a[4], MetadataType_RemoteAet)); + ASSERT_FALSE(transaction_->LookupMetadata(s, a[4], MetadataType_Instance_IndexInSeries)); ASSERT_EQ("PINNACLE", s); std::string u; - ASSERT_TRUE(index_->LookupMetadata(u, a[4], MetadataType_RemoteAet)); + ASSERT_TRUE(transaction_->LookupMetadata(u, a[4], MetadataType_RemoteAet)); ASSERT_EQ("PINNACLE", u); - ASSERT_FALSE(index_->LookupMetadata(u, a[4], MetadataType_Instance_IndexInSeries)); + ASSERT_FALSE(transaction_->LookupMetadata(u, a[4], MetadataType_Instance_IndexInSeries)); - ASSERT_TRUE(index_->LookupGlobalProperty(s, GlobalProperty_FlushSleep)); - ASSERT_FALSE(index_->LookupGlobalProperty(s, static_cast<GlobalProperty>(42))); + ASSERT_TRUE(transaction_->LookupGlobalProperty(s, GlobalProperty_FlushSleep)); + ASSERT_FALSE(transaction_->LookupGlobalProperty(s, static_cast<GlobalProperty>(42))); ASSERT_EQ("World", s); FileInfo att; - ASSERT_TRUE(index_->LookupAttachment(att, a[4], FileContentType_DicomAsJson)); + ASSERT_TRUE(transaction_->LookupAttachment(att, a[4], FileContentType_DicomAsJson)); ASSERT_EQ("my json file", att.GetUuid()); ASSERT_EQ(21u, att.GetCompressedSize()); ASSERT_EQ("md5", att.GetUncompressedMD5()); @@ -362,7 +363,7 @@ ASSERT_EQ(42u, att.GetUncompressedSize()); ASSERT_EQ(CompressionType_ZlibWithSize, att.GetCompressionType()); - ASSERT_TRUE(index_->LookupAttachment(att, a[6], FileContentType_Dicom)); + ASSERT_TRUE(transaction_->LookupAttachment(att, a[6], FileContentType_Dicom)); ASSERT_EQ("world", att.GetUuid()); ASSERT_EQ(44u, att.GetCompressedSize()); ASSERT_EQ("md5", att.GetUncompressedMD5()); @@ -378,7 +379,7 @@ CheckTableRecordCount(1, "Metadata"); CheckTableRecordCount(1, "MainDicomTags"); - index_->DeleteResource(a[0]); + transaction_->DeleteResource(a[0]); ASSERT_EQ(5u, listener_->deletedResources_.size()); ASSERT_EQ(2u, listener_->deletedFiles_.size()); ASSERT_FALSE(std::find(listener_->deletedFiles_.begin(), @@ -393,7 +394,7 @@ CheckTableRecordCount(1, "AttachedFiles"); CheckTableRecordCount(0, "MainDicomTags"); - index_->DeleteResource(a[5]); + transaction_->DeleteResource(a[5]); ASSERT_EQ(7u, listener_->deletedResources_.size()); CheckTableRecordCount(0, "Resources"); @@ -401,11 +402,11 @@ CheckTableRecordCount(3, "GlobalProperties"); std::string tmp; - ASSERT_TRUE(index_->LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)); + ASSERT_TRUE(transaction_->LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)); ASSERT_EQ("6", tmp); - ASSERT_TRUE(index_->LookupGlobalProperty(tmp, GlobalProperty_FlushSleep)); + ASSERT_TRUE(transaction_->LookupGlobalProperty(tmp, GlobalProperty_FlushSleep)); ASSERT_EQ("World", tmp); - ASSERT_TRUE(index_->LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast)); + ASSERT_TRUE(transaction_->LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast)); ASSERT_EQ("1", tmp); ASSERT_EQ(3u, listener_->deletedFiles_.size()); @@ -418,23 +419,23 @@ TEST_F(DatabaseWrapperTest, Upward) { int64_t a[] = { - index_->CreateResource("a", ResourceType_Patient), // 0 - index_->CreateResource("b", ResourceType_Study), // 1 - index_->CreateResource("c", ResourceType_Series), // 2 - index_->CreateResource("d", ResourceType_Instance), // 3 - index_->CreateResource("e", ResourceType_Instance), // 4 - index_->CreateResource("f", ResourceType_Study), // 5 - index_->CreateResource("g", ResourceType_Series), // 6 - index_->CreateResource("h", ResourceType_Series) // 7 + transaction_->CreateResource("a", ResourceType_Patient), // 0 + transaction_->CreateResource("b", ResourceType_Study), // 1 + transaction_->CreateResource("c", ResourceType_Series), // 2 + transaction_->CreateResource("d", ResourceType_Instance), // 3 + transaction_->CreateResource("e", ResourceType_Instance), // 4 + transaction_->CreateResource("f", ResourceType_Study), // 5 + transaction_->CreateResource("g", ResourceType_Series), // 6 + transaction_->CreateResource("h", ResourceType_Series) // 7 }; - index_->AttachChild(a[0], a[1]); - index_->AttachChild(a[1], a[2]); - index_->AttachChild(a[2], a[3]); - index_->AttachChild(a[2], a[4]); - index_->AttachChild(a[1], a[6]); - index_->AttachChild(a[0], a[5]); - index_->AttachChild(a[5], a[7]); + transaction_->AttachChild(a[0], a[1]); + transaction_->AttachChild(a[1], a[2]); + transaction_->AttachChild(a[2], a[3]); + transaction_->AttachChild(a[2], a[4]); + transaction_->AttachChild(a[1], a[6]); + transaction_->AttachChild(a[0], a[5]); + transaction_->AttachChild(a[5], a[7]); CheckTwoChildren("b", "f", a[0]); CheckTwoChildren("c", "g", a[1]); @@ -446,22 +447,22 @@ CheckNoChild(a[7]); listener_->Reset(); - index_->DeleteResource(a[3]); + transaction_->DeleteResource(a[3]); ASSERT_EQ("c", listener_->ancestorId_); ASSERT_EQ(ResourceType_Series, listener_->ancestorType_); listener_->Reset(); - index_->DeleteResource(a[4]); + transaction_->DeleteResource(a[4]); ASSERT_EQ("b", listener_->ancestorId_); ASSERT_EQ(ResourceType_Study, listener_->ancestorType_); listener_->Reset(); - index_->DeleteResource(a[7]); + transaction_->DeleteResource(a[7]); ASSERT_EQ("a", listener_->ancestorId_); ASSERT_EQ(ResourceType_Patient, listener_->ancestorType_); listener_->Reset(); - index_->DeleteResource(a[6]); + transaction_->DeleteResource(a[6]); ASSERT_EQ("", listener_->ancestorId_); // No more ancestor } @@ -472,10 +473,10 @@ for (int i = 0; i < 10; i++) { std::string p = "Patient " + boost::lexical_cast<std::string>(i); - patients.push_back(index_->CreateResource(p, ResourceType_Patient)); - index_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, + patients.push_back(transaction_->CreateResource(p, ResourceType_Patient)); + transaction_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, "md5-" + boost::lexical_cast<std::string>(i))); - ASSERT_FALSE(index_->IsProtectedPatient(patients[i])); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[i])); } CheckTableRecordCount(10u, "Resources"); @@ -484,8 +485,8 @@ listener_->Reset(); ASSERT_EQ(0u, listener_->deletedResources_.size()); - index_->DeleteResource(patients[5]); - index_->DeleteResource(patients[0]); + transaction_->DeleteResource(patients[5]); + transaction_->DeleteResource(patients[0]); ASSERT_EQ(2u, listener_->deletedResources_.size()); CheckTableRecordCount(8u, "Resources"); @@ -496,28 +497,28 @@ ASSERT_EQ("Patient 0", listener_->deletedFiles_[1]); int64_t p; - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); + transaction_->DeleteResource(p); ASSERT_EQ(3u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); + transaction_->DeleteResource(p); ASSERT_EQ(4u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); + transaction_->DeleteResource(p); ASSERT_EQ(5u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); + transaction_->DeleteResource(p); ASSERT_EQ(6u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[6]); - index_->DeleteResource(p); - index_->DeleteResource(patients[8]); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[6]); + transaction_->DeleteResource(p); + transaction_->DeleteResource(patients[8]); ASSERT_EQ(8u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[7]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[7]); + transaction_->DeleteResource(p); ASSERT_EQ(9u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[9]); - index_->DeleteResource(p); - ASSERT_FALSE(index_->SelectPatientToRecycle(p)); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[9]); + transaction_->DeleteResource(p); + ASSERT_FALSE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(10u, listener_->deletedResources_.size()); ASSERT_EQ(10u, listener_->deletedFiles_.size()); @@ -533,39 +534,39 @@ for (int i = 0; i < 5; i++) { std::string p = "Patient " + boost::lexical_cast<std::string>(i); - patients.push_back(index_->CreateResource(p, ResourceType_Patient)); - index_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, + patients.push_back(transaction_->CreateResource(p, ResourceType_Patient)); + transaction_->AddAttachment(patients[i], FileInfo(p, FileContentType_Dicom, i + 10, "md5-" + boost::lexical_cast<std::string>(i))); - ASSERT_FALSE(index_->IsProtectedPatient(patients[i])); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[i])); } CheckTableRecordCount(5, "Resources"); CheckTableRecordCount(5, "PatientRecyclingOrder"); - ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); - index_->SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], true); + ASSERT_TRUE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(5, "Resources"); CheckTableRecordCount(4, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], true); + ASSERT_TRUE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(4, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], false); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(5, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], false); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(5, "PatientRecyclingOrder"); CheckTableRecordCount(5, "Resources"); - index_->SetProtectedPatient(patients[2], true); - ASSERT_TRUE(index_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], true); + ASSERT_TRUE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(4, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[2], false); - ASSERT_FALSE(index_->IsProtectedPatient(patients[2])); + transaction_->SetProtectedPatient(patients[2], false); + ASSERT_FALSE(transaction_->IsProtectedPatient(patients[2])); CheckTableRecordCount(5, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[3], true); - ASSERT_TRUE(index_->IsProtectedPatient(patients[3])); + transaction_->SetProtectedPatient(patients[3], true); + ASSERT_TRUE(transaction_->IsProtectedPatient(patients[3])); CheckTableRecordCount(4, "PatientRecyclingOrder"); CheckTableRecordCount(5, "Resources"); @@ -574,33 +575,33 @@ // Unprotecting a patient puts it at the last position in the recycling queue int64_t p; ASSERT_EQ(0u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[0]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[0]); + transaction_->DeleteResource(p); ASSERT_EQ(1u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p, patients[1])); ASSERT_EQ(p, patients[4]); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p, patients[1])); ASSERT_EQ(p, patients[4]); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[1]); + transaction_->DeleteResource(p); ASSERT_EQ(2u, listener_->deletedResources_.size()); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); - index_->DeleteResource(p); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[4]); + transaction_->DeleteResource(p); ASSERT_EQ(3u, listener_->deletedResources_.size()); - ASSERT_FALSE(index_->SelectPatientToRecycle(p, patients[2])); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); - index_->DeleteResource(p); + ASSERT_FALSE(transaction_->SelectPatientToRecycle(p, patients[2])); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[2]); + transaction_->DeleteResource(p); ASSERT_EQ(4u, listener_->deletedResources_.size()); // "patients[3]" is still protected - ASSERT_FALSE(index_->SelectPatientToRecycle(p)); + ASSERT_FALSE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(4u, listener_->deletedFiles_.size()); CheckTableRecordCount(1, "Resources"); CheckTableRecordCount(0, "PatientRecyclingOrder"); - index_->SetProtectedPatient(patients[3], false); + transaction_->SetProtectedPatient(patients[3], false); CheckTableRecordCount(1, "PatientRecyclingOrder"); - ASSERT_FALSE(index_->SelectPatientToRecycle(p, patients[3])); - ASSERT_TRUE(index_->SelectPatientToRecycle(p, patients[2])); - ASSERT_TRUE(index_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); - index_->DeleteResource(p); + ASSERT_FALSE(transaction_->SelectPatientToRecycle(p, patients[3])); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p, patients[2])); + ASSERT_TRUE(transaction_->SelectPatientToRecycle(p)); ASSERT_EQ(p, patients[3]); + transaction_->DeleteResource(p); ASSERT_EQ(5u, listener_->deletedResources_.size()); ASSERT_EQ(5u, listener_->deletedFiles_.size()); @@ -635,16 +636,16 @@ TEST_F(DatabaseWrapperTest, LookupIdentifier) { int64_t a[] = { - index_->CreateResource("a", ResourceType_Study), // 0 - index_->CreateResource("b", ResourceType_Study), // 1 - index_->CreateResource("c", ResourceType_Study), // 2 - index_->CreateResource("d", ResourceType_Series) // 3 + transaction_->CreateResource("a", ResourceType_Study), // 0 + transaction_->CreateResource("b", ResourceType_Study), // 1 + transaction_->CreateResource("c", ResourceType_Study), // 2 + transaction_->CreateResource("d", ResourceType_Series) // 3 }; - index_->SetIdentifierTag(a[0], DICOM_TAG_STUDY_INSTANCE_UID, "0"); - index_->SetIdentifierTag(a[1], DICOM_TAG_STUDY_INSTANCE_UID, "1"); - index_->SetIdentifierTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0"); - index_->SetIdentifierTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0"); + transaction_->SetIdentifierTag(a[0], DICOM_TAG_STUDY_INSTANCE_UID, "0"); + transaction_->SetIdentifierTag(a[1], DICOM_TAG_STUDY_INSTANCE_UID, "1"); + transaction_->SetIdentifierTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0"); + transaction_->SetIdentifierTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0"); std::list<std::string> s;