Mercurial > hg > orthanc
changeset 3093:2e1808b6146a db-changes
reorganization of folders
line wrap: on
line diff
--- a/CMakeLists.txt Sat Jan 05 15:50:55 2019 +0100 +++ b/CMakeLists.txt Sat Jan 05 16:09:21 2019 +0100 @@ -53,6 +53,12 @@ ##################################################################### set(ORTHANC_SERVER_SOURCES + OrthancServer/Database/Compatibility/DatabaseLookup.cpp + OrthancServer/Database/Compatibility/ICreateInstance.cpp + OrthancServer/Database/Compatibility/IGetChildrenMetadata.cpp + OrthancServer/Database/Compatibility/ILookupResources.cpp + OrthancServer/Database/Compatibility/SetOfResources.cpp + OrthancServer/Database/SQLiteDatabaseWrapper.cpp OrthancServer/DicomInstanceOrigin.cpp OrthancServer/DicomInstanceToStore.cpp OrthancServer/ExportedResource.cpp @@ -70,12 +76,6 @@ OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/QueryRetrieveHandler.cpp - OrthancServer/SQLiteDatabaseWrapper.cpp - OrthancServer/Search/Compatibility/DatabaseLookup.cpp - OrthancServer/Search/Compatibility/ICreateInstance.cpp - OrthancServer/Search/Compatibility/IGetChildrenMetadata.cpp - OrthancServer/Search/Compatibility/ILookupResources.cpp - OrthancServer/Search/Compatibility/SetOfResources.cpp OrthancServer/Search/DatabaseConstraint.cpp OrthancServer/Search/DatabaseLookup.cpp OrthancServer/Search/DicomTagConstraint.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/DatabaseLookup.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,419 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "DatabaseLookup.h" + +#include "../../../Core/OrthancException.h" +#include "../../ServerToolbox.h" +#include "SetOfResources.h" + +namespace Orthanc +{ + namespace Compatibility + { + namespace + { + // Anonymous namespace to avoid clashes between compiler modules + class MainTagsConstraints : boost::noncopyable + { + private: + std::vector<DicomTagConstraint*> constraints_; + + public: + ~MainTagsConstraints() + { + for (size_t i = 0; i < constraints_.size(); i++) + { + assert(constraints_[i] != NULL); + delete constraints_[i]; + } + } + + void Reserve(size_t n) + { + constraints_.reserve(n); + } + + size_t GetSize() const + { + return constraints_.size(); + } + + DicomTagConstraint& GetConstraint(size_t i) const + { + if (i >= constraints_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + assert(constraints_[i] != NULL); + return *constraints_[i]; + } + } + + void Add(const DatabaseConstraint& constraint) + { + constraints_.push_back(new DicomTagConstraint(constraint)); + } + }; + } + + + static void ApplyIdentifierConstraint(SetOfResources& candidates, + ILookupResources& compatibility, + const DatabaseConstraint& constraint, + ResourceType level) + { + std::list<int64_t> matches; + + switch (constraint.GetConstraintType()) + { + case ConstraintType_Equal: + compatibility.LookupIdentifier(matches, level, constraint.GetTag(), + IdentifierConstraintType_Equal, constraint.GetSingleValue()); + break; + + case ConstraintType_SmallerOrEqual: + compatibility.LookupIdentifier(matches, level, constraint.GetTag(), + IdentifierConstraintType_SmallerOrEqual, constraint.GetSingleValue()); + break; + + case ConstraintType_GreaterOrEqual: + compatibility.LookupIdentifier(matches, level, constraint.GetTag(), + IdentifierConstraintType_GreaterOrEqual, constraint.GetSingleValue()); + + break; + + case ConstraintType_Wildcard: + compatibility.LookupIdentifier(matches, level, constraint.GetTag(), + IdentifierConstraintType_Wildcard, constraint.GetSingleValue()); + + break; + + case ConstraintType_List: + for (size_t i = 0; i < constraint.GetValuesCount(); i++) + { + std::list<int64_t> tmp; + compatibility.LookupIdentifier(tmp, level, constraint.GetTag(), + IdentifierConstraintType_Wildcard, constraint.GetValue(i)); + matches.splice(matches.end(), tmp); + } + + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + candidates.Intersect(matches); + } + + + static void ApplyIdentifierRange(SetOfResources& candidates, + ILookupResources& compatibility, + const DatabaseConstraint& smaller, + const DatabaseConstraint& greater, + ResourceType level) + { + assert(smaller.GetConstraintType() == ConstraintType_SmallerOrEqual && + greater.GetConstraintType() == ConstraintType_GreaterOrEqual && + smaller.GetTag() == greater.GetTag() && + ServerToolbox::IsIdentifier(smaller.GetTag(), level)); + + std::list<int64_t> matches; + compatibility.LookupIdentifierRange(matches, level, smaller.GetTag(), + greater.GetSingleValue(), smaller.GetSingleValue()); + candidates.Intersect(matches); + } + + + static void ApplyLevel(SetOfResources& candidates, + IDatabaseWrapper& database, + ILookupResources& compatibility, + const std::vector<DatabaseConstraint>& lookup, + ResourceType level) + { + typedef std::set<const DatabaseConstraint*> SetOfConstraints; + typedef std::map<DicomTag, SetOfConstraints> Identifiers; + + // (1) Select which constraints apply to this level, and split + // them between "identifier tags" constraints and "main DICOM + // tags" constraints + + Identifiers identifiers; + SetOfConstraints mainTags; + + for (size_t i = 0; i < lookup.size(); i++) + { + if (lookup[i].GetLevel() == level) + { + if (lookup[i].IsIdentifier()) + { + identifiers[lookup[i].GetTag()].insert(&lookup[i]); + } + else + { + mainTags.insert(&lookup[i]); + } + } + } + + + // (2) Apply the constraints over the identifiers + + for (Identifiers::const_iterator it = identifiers.begin(); + it != identifiers.end(); ++it) + { + // Check whether some range constraint over identifiers is + // present at this level + const DatabaseConstraint* smaller = NULL; + const DatabaseConstraint* greater = NULL; + + for (SetOfConstraints::const_iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2) + { + assert(*it2 != NULL); + + if ((*it2)->GetConstraintType() == ConstraintType_SmallerOrEqual) + { + smaller = *it2; + } + + if ((*it2)->GetConstraintType() == ConstraintType_GreaterOrEqual) + { + greater = *it2; + } + } + + if (smaller != NULL && + greater != NULL) + { + // There is a range constraint: Apply it, as it is more efficient + ApplyIdentifierRange(candidates, compatibility, *smaller, *greater, level); + } + else + { + smaller = NULL; + greater = NULL; + } + + for (SetOfConstraints::const_iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2) + { + // Check to avoid applying twice the range constraint + if (*it2 != smaller && + *it2 != greater) + { + ApplyIdentifierConstraint(candidates, compatibility, **it2, level); + } + } + } + + + // (3) Apply the constraints over the main DICOM tags (no index + // here, so this is less efficient than filtering over the + // identifiers) + if (!mainTags.empty()) + { + MainTagsConstraints c; + c.Reserve(mainTags.size()); + + for (SetOfConstraints::const_iterator it = mainTags.begin(); + it != mainTags.end(); ++it) + { + assert(*it != NULL); + c.Add(**it); + } + + std::list<int64_t> source; + candidates.Flatten(compatibility, source); + candidates.Clear(); + + std::list<int64_t> filtered; + for (std::list<int64_t>::const_iterator candidate = source.begin(); + candidate != source.end(); ++candidate) + { + DicomMap tags; + database.GetMainDicomTags(tags, *candidate); + + bool match = true; + + for (size_t i = 0; i < c.GetSize(); i++) + { + if (!c.GetConstraint(i).IsMatch(tags)) + { + match = false; + break; + } + } + + if (match) + { + filtered.push_back(*candidate); + } + } + + candidates.Intersect(filtered); + } + } + + + static std::string GetOneInstance(IDatabaseWrapper& compatibility, + int64_t resource, + ResourceType level) + { + for (int i = level; i < ResourceType_Instance; i++) + { + assert(compatibility.GetResourceType(resource) == static_cast<ResourceType>(i)); + + std::list<int64_t> children; + compatibility.GetChildrenInternalId(children, resource); + + if (children.empty()) + { + throw OrthancException(ErrorCode_Database); + } + + resource = children.front(); + } + + return compatibility.GetPublicId(resource); + } + + + void DatabaseLookup::ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) + { + // This is a re-implementation of + // "../../../Resources/Graveyard/DatabaseOptimizations/LookupResource.cpp" + + assert(ResourceType_Patient < ResourceType_Study && + ResourceType_Study < ResourceType_Series && + ResourceType_Series < ResourceType_Instance); + + ResourceType upperLevel = queryLevel; + ResourceType lowerLevel = queryLevel; + + for (size_t i = 0; i < lookup.size(); i++) + { + ResourceType level = lookup[i].GetLevel(); + + if (level < upperLevel) + { + upperLevel = level; + } + + if (level > lowerLevel) + { + lowerLevel = level; + } + } + + assert(upperLevel <= queryLevel && + queryLevel <= lowerLevel); + + SetOfResources candidates(database_, upperLevel); + + for (int level = upperLevel; level <= lowerLevel; level++) + { + ApplyLevel(candidates, database_, compatibility_, lookup, static_cast<ResourceType>(level)); + + if (level != lowerLevel) + { + candidates.GoDown(); + } + } + + std::list<int64_t> resources; + candidates.Flatten(compatibility_, resources); + + // Climb up, up to queryLevel + + for (int level = lowerLevel; level > queryLevel; level--) + { + std::list<int64_t> parents; + for (std::list<int64_t>::const_iterator + it = resources.begin(); it != resources.end(); ++it) + { + int64_t parent; + if (database_.LookupParent(parent, *it)) + { + parents.push_back(parent); + } + } + + resources.swap(parents); + } + + // Apply the limit, if given + + if (limit != 0 && + resources.size() > limit) + { + resources.resize(limit); + } + + // Get the public ID of all the selected resources + + size_t pos = 0; + + for (std::list<int64_t>::const_iterator + it = resources.begin(); it != resources.end(); ++it, pos++) + { + assert(database_.GetResourceType(*it) == queryLevel); + + const std::string resource = database_.GetPublicId(*it); + resourcesId.push_back(resource); + + if (instancesId != NULL) + { + if (queryLevel == ResourceType_Instance) + { + // The resource is itself the instance + instancesId->push_back(resource); + } + else + { + // Collect one child instance for each of the selected resources + instancesId->push_back(GetOneInstance(database_, *it, queryLevel)); + } + } + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/DatabaseLookup.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,64 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../IDatabaseWrapper.h" +#include "ILookupResources.h" + +namespace Orthanc +{ + namespace Compatibility + { + class DatabaseLookup : public boost::noncopyable + { + private: + IDatabaseWrapper& database_; + ILookupResources& compatibility_; + + public: + DatabaseLookup(IDatabaseWrapper& database, + ILookupResources& compatibility) : + database_(database), + compatibility_(compatibility) + { + } + + void ApplyLookupResources(std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/ICreateInstance.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,155 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "ICreateInstance.h" + +#include "../../../Core/OrthancException.h" + +namespace Orthanc +{ + namespace Compatibility + { + bool ICreateInstance::Apply(ICreateInstance& database, + IDatabaseWrapper::CreateInstanceResult& result, + int64_t& instanceId, + const std::string& hashPatient, + const std::string& hashStudy, + const std::string& hashSeries, + const std::string& hashInstance) + { + { + ResourceType type; + int64_t tmp; + + if (database.LookupResource(tmp, type, hashInstance)) + { + // The instance already exists + assert(type == ResourceType_Instance); + instanceId = tmp; + return false; + } + } + + instanceId = database.CreateResource(hashInstance, ResourceType_Instance); + + result.isNewPatient_ = false; + result.isNewStudy_ = false; + result.isNewSeries_ = false; + result.patientId_ = -1; + result.studyId_ = -1; + result.seriesId_ = -1; + + // Detect up to which level the patient/study/series/instance + // hierarchy must be created + + { + ResourceType dummy; + + if (database.LookupResource(result.seriesId_, dummy, hashSeries)) + { + assert(dummy == ResourceType_Series); + // The patient, the study and the series already exist + + bool ok = (database.LookupResource(result.patientId_, dummy, hashPatient) && + database.LookupResource(result.studyId_, dummy, hashStudy)); + assert(ok); + } + else if (database.LookupResource(result.studyId_, dummy, hashStudy)) + { + assert(dummy == ResourceType_Study); + + // New series: The patient and the study already exist + result.isNewSeries_ = true; + + bool ok = database.LookupResource(result.patientId_, dummy, hashPatient); + assert(ok); + } + else if (database.LookupResource(result.patientId_, dummy, hashPatient)) + { + assert(dummy == ResourceType_Patient); + + // New study and series: The patient already exist + result.isNewStudy_ = true; + result.isNewSeries_ = true; + } + else + { + // New patient, study and series: Nothing exists + result.isNewPatient_ = true; + result.isNewStudy_ = true; + result.isNewSeries_ = true; + } + } + + // Create the series if needed + if (result.isNewSeries_) + { + result.seriesId_ = database.CreateResource(hashSeries, ResourceType_Series); + } + + // Create the study if needed + if (result.isNewStudy_) + { + result.studyId_ = database.CreateResource(hashStudy, ResourceType_Study); + } + + // Create the patient if needed + if (result.isNewPatient_) + { + result.patientId_ = database.CreateResource(hashPatient, ResourceType_Patient); + } + + // Create the parent-to-child links + database.AttachChild(result.seriesId_, instanceId); + + if (result.isNewSeries_) + { + database.AttachChild(result.studyId_, result.seriesId_); + } + + if (result.isNewStudy_) + { + database.AttachChild(result.patientId_, result.studyId_); + } + + // Sanity checks + assert(result.patientId_ != -1); + assert(result.studyId_ != -1); + assert(result.seriesId_ != -1); + assert(instanceId != -1); + + return true; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/ICreateInstance.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,64 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../IDatabaseWrapper.h" + +namespace Orthanc +{ + namespace Compatibility + { + class ICreateInstance : public boost::noncopyable + { + public: + virtual bool LookupResource(int64_t& id, + ResourceType& type, + const std::string& publicId) = 0; + + virtual int64_t CreateResource(const std::string& publicId, + ResourceType type) = 0; + + virtual void AttachChild(int64_t parent, + int64_t child) = 0; + + static bool Apply(ICreateInstance& database, + IDatabaseWrapper::CreateInstanceResult& result, + int64_t& instanceId, + const std::string& patient, + const std::string& study, + const std::string& series, + const std::string& instance); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/IGetChildrenMetadata.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,67 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "IGetChildrenMetadata.h" + +namespace Orthanc +{ + namespace Compatibility + { + void IGetChildrenMetadata::Apply(IGetChildrenMetadata& database, + std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata) + { + // This function comes from an optimization of + // "ServerIndex::GetSeriesStatus()" in Orthanc <= 1.5.1 + // Loop over the instances of this series + + target.clear(); + + std::list<int64_t> children; + database.GetChildrenInternalId(children, resourceId); + + std::set<int64_t> instances; + for (std::list<int64_t>::const_iterator + it = children.begin(); it != children.end(); ++it) + { + std::string value; + if (database.LookupMetadata(value, *it, metadata)) + { + target.push_back(value); + } + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/IGetChildrenMetadata.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,61 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../../ServerEnumerations.h" + +#include <boost/noncopyable.hpp> +#include <list> + +namespace Orthanc +{ + namespace Compatibility + { + class IGetChildrenMetadata : public boost::noncopyable + { + public: + virtual void GetChildrenInternalId(std::list<int64_t>& target, + int64_t id) = 0; + + virtual bool LookupMetadata(std::string& target, + int64_t id, + MetadataType type) = 0; + + static void Apply(IGetChildrenMetadata& database, + std::list<std::string>& target, + int64_t resourceId, + MetadataType metadata); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/ILookupResources.cpp Sat Jan 05 16:09:21 2019 +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-2019 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 "ILookupResources.h" + +#include "DatabaseLookup.h" + +namespace Orthanc +{ + namespace Compatibility + { + void ILookupResources::Apply( + IDatabaseWrapper& database, + ILookupResources& compatibility, + std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit) + { + Compatibility::DatabaseLookup compat(database, compatibility); + compat.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/ILookupResources.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,78 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../IDatabaseWrapper.h" + +namespace Orthanc +{ + namespace Compatibility + { + /** + * This is a compatibility class that contains database primitives + * that were used in Orthanc <= 1.5.1, and that have been removed + * during the optimization of the database engine. + **/ + class ILookupResources : public boost::noncopyable + { + public: + virtual ~ILookupResources() + { + } + + virtual void GetAllInternalIds(std::list<int64_t>& target, + ResourceType resourceType) = 0; + + virtual void LookupIdentifier(std::list<int64_t>& result, + ResourceType level, + const DicomTag& tag, + IdentifierConstraintType type, + const std::string& value) = 0; + + virtual void LookupIdentifierRange(std::list<int64_t>& result, + ResourceType level, + const DicomTag& tag, + const std::string& start, + const std::string& end) = 0; + + static void Apply(IDatabaseWrapper& database, + ILookupResources& compatibility, + std::list<std::string>& resourcesId, + std::list<std::string>* instancesId, + const std::vector<DatabaseConstraint>& lookup, + ResourceType queryLevel, + size_t limit); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/ISetResourcesContent.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,68 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../../ServerToolbox.h" + +namespace Orthanc +{ + namespace Compatibility + { + class ISetResourcesContent : public boost::noncopyable + { + public: + virtual ~ISetResourcesContent() + { + } + + virtual void SetMainDicomTag(int64_t id, + const DicomTag& tag, + const std::string& value) = 0; + + virtual void SetIdentifierTag(int64_t id, + const DicomTag& tag, + const std::string& value) = 0; + + virtual void SetMetadata(int64_t id, + MetadataType type, + const std::string& value) = 0; + + static void Apply(ISetResourcesContent& that, + const ResourcesContent& content) + { + content.Store(that); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/SetOfResources.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,161 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "SetOfResources.h" + +#include "../../../Core/OrthancException.h" + + +namespace Orthanc +{ + namespace Compatibility + { + void SetOfResources::Intersect(const std::list<int64_t>& resources) + { + if (resources_.get() == NULL) + { + resources_.reset(new Resources); + + for (std::list<int64_t>::const_iterator + it = resources.begin(); it != resources.end(); ++it) + { + resources_->insert(*it); + } + } + else + { + std::auto_ptr<Resources> filtered(new Resources); + + for (std::list<int64_t>::const_iterator + it = resources.begin(); it != resources.end(); ++it) + { + if (resources_->find(*it) != resources_->end()) + { + filtered->insert(*it); + } + } + + resources_ = filtered; + } + } + + + void SetOfResources::GoDown() + { + if (level_ == ResourceType_Instance) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (resources_.get() != NULL) + { + std::auto_ptr<Resources> children(new Resources); + + for (Resources::const_iterator it = resources_->begin(); + it != resources_->end(); ++it) + { + std::list<int64_t> tmp; + database_.GetChildrenInternalId(tmp, *it); + + for (std::list<int64_t>::const_iterator + child = tmp.begin(); child != tmp.end(); ++child) + { + children->insert(*child); + } + } + + resources_ = children; + } + + switch (level_) + { + case ResourceType_Patient: + level_ = ResourceType_Study; + break; + + case ResourceType_Study: + level_ = ResourceType_Series; + break; + + case ResourceType_Series: + level_ = ResourceType_Instance; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + + void SetOfResources::Flatten(std::list<std::string>& result) + { + result.clear(); + + if (resources_.get() == NULL) + { + // All the resources of this level are part of the filter + database_.GetAllPublicIds(result, level_); + } + else + { + for (Resources::const_iterator it = resources_->begin(); + it != resources_->end(); ++it) + { + result.push_back(database_.GetPublicId(*it)); + } + } + } + + + void SetOfResources::Flatten(ILookupResources& compatibility, + std::list<int64_t>& result) + { + result.clear(); + + if (resources_.get() == NULL) + { + // All the resources of this level are part of the filter + compatibility.GetAllInternalIds(result, level_); + } + else + { + for (Resources::const_iterator it = resources_->begin(); + it != resources_->end(); ++it) + { + result.push_back(*it); + } + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/Compatibility/SetOfResources.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,83 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../IDatabaseWrapper.h" +#include "ILookupResources.h" + +#include <set> +#include <memory> + +namespace Orthanc +{ + namespace Compatibility + { + class SetOfResources : public boost::noncopyable + { + private: + typedef std::set<int64_t> Resources; + + IDatabaseWrapper& database_; + ResourceType level_; + std::auto_ptr<Resources> resources_; + + public: + SetOfResources(IDatabaseWrapper& database, + ResourceType level) : + database_(database), + level_(level) + { + } + + ResourceType GetLevel() const + { + return level_; + } + + void Intersect(const std::list<int64_t>& resources); + + void GoDown(); + + void Flatten(ILookupResources& compatibility, + std::list<int64_t>& result); + + void Flatten(std::list<std::string>& result); + + void Clear() + { + resources_.reset(NULL); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/IDatabaseWrapper.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,245 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "../../Core/DicomFormat/DicomMap.h" +#include "../../Core/FileStorage/FileInfo.h" +#include "../../Core/FileStorage/IStorageArea.h" +#include "../../Core/SQLite/ITransaction.h" + +#include "../ExportedResource.h" +#include "../IDatabaseListener.h" +#include "../Search/DatabaseConstraint.h" +#include "../Search/DatabaseLookup.h" + +#include <list> +#include <boost/noncopyable.hpp> + +namespace Orthanc +{ + class ResourcesContent; + + + class IDatabaseWrapper : public boost::noncopyable + { + public: + class ITransaction : public boost::noncopyable + { + public: + virtual ~ITransaction() + { + } + + virtual void Begin() = 0; + + virtual void Rollback() = 0; + + virtual void Commit(int64_t fileSizeDelta) = 0; + }; + + + struct CreateInstanceResult + { + bool isNewPatient_; + bool isNewStudy_; + bool isNewSeries_; + int64_t patientId_; + int64_t studyId_; + int64_t seriesId_; + }; + + virtual ~IDatabaseWrapper() + { + } + + virtual void Open() = 0; + + 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 ListAvailableMetadata(std::list<MetadataType>& target, + int64_t id) = 0; + + virtual void ListAvailableAttachments(std::list<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() = 0; + + virtual void SetListener(IDatabaseListener& listener) = 0; + + virtual unsigned int GetDatabaseVersion() = 0; + + 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 already exists. If "false" is + // returned, the content of "result" is undefined, but + // "instanceId" must be properly set. + 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; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/SQLiteDatabaseWrapper.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,1273 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "SQLiteDatabaseWrapper.h" + +#include "../../Core/DicomFormat/DicomArray.h" +#include "../../Core/Logging.h" +#include "../../Core/SQLite/Transaction.h" +#include "../Search/ISqlLookupFormatter.h" +#include "../ServerToolbox.h" + +#include <EmbeddedResources.h> + +#include <stdio.h> +#include <boost/lexical_cast.hpp> + +namespace Orthanc +{ + namespace Internals + { + class SignalFileDeleted : public SQLite::IScalarFunction + { + private: + IDatabaseListener& listener_; + + public: + SignalFileDeleted(IDatabaseListener& listener) : + listener_(listener) + { + } + + virtual const char* GetName() const + { + return "SignalFileDeleted"; + } + + virtual unsigned int GetCardinality() const + { + return 7; + } + + virtual void Compute(SQLite::FunctionContext& context) + { + 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); + + listener_.SignalFileDeleted(info); + } + }; + + class SignalResourceDeleted : public SQLite::IScalarFunction + { + private: + IDatabaseListener& listener_; + + public: + SignalResourceDeleted(IDatabaseListener& listener) : + listener_(listener) + { + } + + virtual const char* GetName() const + { + return "SignalResourceDeleted"; + } + + virtual unsigned int GetCardinality() const + { + return 2; + } + + virtual void Compute(SQLite::FunctionContext& context) + { + ResourceType type = static_cast<ResourceType>(context.GetIntValue(1)); + ServerIndexChange change(ChangeType_Deleted, type, context.GetStringValue(0)); + listener_.SignalChange(change); + } + }; + + class SignalRemainingAncestor : public SQLite::IScalarFunction + { + private: + bool hasRemainingAncestor_; + std::string remainingPublicId_; + ResourceType remainingType_; + + public: + SignalRemainingAncestor() : + hasRemainingAncestor_(false) + { + } + + void Reset() + { + hasRemainingAncestor_ = false; + } + + virtual const char* GetName() const + { + return "SignalRemainingAncestor"; + } + + virtual unsigned int GetCardinality() const + { + return 2; + } + + virtual void Compute(SQLite::FunctionContext& context) + { + VLOG(1) << "There exists a remaining ancestor with public ID \"" + << context.GetStringValue(0) + << "\" of type " + << context.GetIntValue(1); + + if (!hasRemainingAncestor_ || + remainingType_ >= context.GetIntValue(1)) + { + hasRemainingAncestor_ = true; + remainingPublicId_ = context.GetStringValue(0); + remainingType_ = static_cast<ResourceType>(context.GetIntValue(1)); + } + } + + bool HasRemainingAncestor() const + { + return hasRemainingAncestor_; + } + + const std::string& GetRemainingAncestorId() const + { + assert(hasRemainingAncestor_); + return remainingPublicId_; + } + + ResourceType GetRemainingAncestorType() const + { + assert(hasRemainingAncestor_); + return remainingType_; + } + }; + } + + + void SQLiteDatabaseWrapper::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()) + { + 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()); + } + + + 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); + + childrenPublicIds.clear(); + while (s.Step()) + { + childrenPublicIds.push_back(s.ColumnString(0)); + } + } + + + void SQLiteDatabaseWrapper::DeleteResource(int64_t id) + { + signalRemainingAncestor_->Reset(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); + s.BindInt64(0, id); + s.Run(); + + if (signalRemainingAncestor_->HasRemainingAncestor() && + listener_ != NULL) + { + listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), + signalRemainingAncestor_->GetRemainingAncestorId()); + } + } + + + 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); + + if (s.Step()) + { + target = s.ColumnString(0); + return true; + } + else + { + return false; + } + } + + + int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table) + { + char buf[128]; + sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str()); + SQLite::Statement s(db_, buf); + + if (!s.Step()) + { + throw OrthancException(ErrorCode_InternalError); + } + + int64_t c = s.ColumnInt(0); + assert(!s.Step()); + + return c; + } + + + SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : + listener_(NULL), + signalRemainingAncestor_(NULL), + version_(0) + { + db_.Open(path); + } + + + SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : + listener_(NULL), + signalRemainingAncestor_(NULL), + version_(0) + { + db_.OpenInMemory(); + } + + + void SQLiteDatabaseWrapper::Open() + { + db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); + + // Performance tuning of SQLite with PRAGMAs + // http://www.sqlite.org/pragma.html + db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); + db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); + db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); + db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); + //db_.Execute("PRAGMA TEMP_STORE=memory"); + + // Make "LIKE" case-sensitive in SQLite + db_.Execute("PRAGMA case_sensitive_like = true;"); + + if (!db_.DoesTableExist("GlobalProperties")) + { + LOG(INFO) << "Creating the database"; + std::string query; + EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE); + db_.Execute(query); + } + + // Check the version of the database + std::string tmp; + if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) + { + tmp = "Unknown"; + } + + bool ok = false; + try + { + LOG(INFO) << "Version of the Orthanc database: " << tmp; + version_ = boost::lexical_cast<unsigned int>(tmp); + ok = true; + } + catch (boost::bad_lexical_cast&) + { + } + + if (!ok) + { + throw OrthancException(ErrorCode_IncompatibleDatabaseVersion, + "Incompatible version of the Orthanc database: " + tmp); + } + + // New in Orthanc 1.5.1 + if (version_ == 6) + { + if (!LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || + tmp != "1") + { + LOG(INFO) << "Installing the SQLite triggers to track the size of the attachments"; + std::string query; + EmbeddedResources::GetFileResource(query, EmbeddedResources::INSTALL_TRACK_ATTACHMENTS_SIZE); + db_.Execute(query); + } + } + + signalRemainingAncestor_ = new Internals::SignalRemainingAncestor; + db_.Register(signalRemainingAncestor_); + } + + + static void ExecuteUpgradeScript(SQLite::Connection& db, + EmbeddedResources::FileResourceId script) + { + std::string upgrade; + EmbeddedResources::GetFileResource(upgrade, script); + db.BeginTransaction(); + db.Execute(upgrade); + db.CommitTransaction(); + } + + + void SQLiteDatabaseWrapper::Upgrade(unsigned int targetVersion, + IStorageArea& storageArea) + { + if (targetVersion != 6) + { + throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); + } + + // This version of Orthanc is only compatible with versions 3, 4, + // 5 and 6 of the DB schema + if (version_ != 3 && + version_ != 4 && + version_ != 5 && + version_ != 6) + { + throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); + } + + if (version_ == 3) + { + LOG(WARNING) << "Upgrading database version from 3 to 4"; + ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_3_TO_4); + version_ = 4; + } + + if (version_ == 4) + { + LOG(WARNING) << "Upgrading database version from 4 to 5"; + ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_4_TO_5); + version_ = 5; + } + + if (version_ == 5) + { + LOG(WARNING) << "Upgrading database version from 5 to 6"; + // 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(); + version_ = 6; + } + } + + + void SQLiteDatabaseWrapper::SetListener(IDatabaseListener& listener) + { + listener_ = &listener; + db_.Register(new Internals::SignalFileDeleted(listener)); + db_.Register(new Internals::SignalResourceDeleted(listener)); + } + + + 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::Transaction : public IDatabaseWrapper::ITransaction + { + private: + SQLiteDatabaseWrapper& that_; + std::auto_ptr<SQLite::Transaction> transaction_; + int64_t initialDiskSize_; + + public: + Transaction(SQLiteDatabaseWrapper& that) : + that_(that), + transaction_(new SQLite::Transaction(that_.db_)) + { +#if defined(NDEBUG) + // Release mode + initialDiskSize_ = 0; +#else + // Debug mode + initialDiskSize_ = static_cast<int64_t>(that_.GetTotalCompressedSize()); +#endif + } + + virtual void Begin() + { + transaction_->Begin(); + } + + virtual void Rollback() + { + transaction_->Rollback(); + } + + virtual void Commit(int64_t fileSizeDelta /* only used in debug */) + { + transaction_->Commit(); + + assert(initialDiskSize_ + fileSizeDelta >= 0 && + initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(that_.GetTotalCompressedSize())); + } + }; + + + IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction() + { + return new Transaction(*this); + } + + + 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) + { + 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(); + } + + + 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) + { + SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); + s.BindInt64(0, parent); + s.BindInt64(1, child); + s.Run(); + } + + + 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::ListAvailableMetadata(std::list<MetadataType>& target, + int64_t id) + { + target.clear(); + + SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type FROM Metadata WHERE id=?"); + s.BindInt64(0, id); + + while (s.Step()) + { + target.push_back(static_cast<MetadataType>(s.ColumnInt(0))); + } + } + + + 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::list<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.push_back(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) + { + 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(); + } + + + void SQLiteDatabaseWrapper::GetMainDicomTags(DicomMap& map, + int64_t id) + { + 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 = ?"); + 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.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() + { + // 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 + { + 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; + } + + 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) + { + values_.push_back(value); + return "?"; + } + + virtual std::string FormatResourceType(ResourceType level) + { + return boost::lexical_cast<std::string>(level); + } + + virtual std::string FormatWildcardEscape() + { + 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::auto_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)); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Database/SQLiteDatabaseWrapper.h Sat Jan 05 16:09:21 2019 +0100 @@ -0,0 +1,358 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 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 "IDatabaseWrapper.h" + +#include "../../Core/SQLite/Connection.h" +#include "../ServerToolbox.h" +#include "Compatibility/ICreateInstance.h" +#include "Compatibility/IGetChildrenMetadata.h" +#include "Compatibility/ISetResourcesContent.h" + +namespace Orthanc +{ + namespace Internals + { + class SignalRemainingAncestor; + } + + /** + * This class manages an instance of the Orthanc SQLite database. It + * 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::ISetResourcesContent + { + private: + class Transaction; + class LookupFormatter; + + IDatabaseListener* listener_; + SQLite::Connection db_; + Internals::SignalRemainingAncestor* signalRemainingAncestor_; + unsigned int version_; + + void GetChangesInternal(std::list<ServerIndexChange>& target, + bool& done, + SQLite::Statement& s, + uint32_t maxResults); + + void GetExportedResourcesInternal(std::list<ExportedResource>& target, + bool& done, + SQLite::Statement& s, + uint32_t maxResults); + + void ClearTable(const std::string& tableName); + + public: + SQLiteDatabaseWrapper(const std::string& path); + + SQLiteDatabaseWrapper(); + + virtual void Open() + ORTHANC_OVERRIDE; + + virtual void Close() + ORTHANC_OVERRIDE + { + db_.Close(); + } + + virtual void SetListener(IDatabaseListener& listener) + ORTHANC_OVERRIDE; + + 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() + ORTHANC_OVERRIDE; + + virtual void FlushToDisk() + ORTHANC_OVERRIDE + { + db_.FlushToDisk(); + } + + 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 + { + 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); + + + + /** + * Until Orthanc 1.4.0, the methods below were part of the + * "DatabaseWrapperBase" class, that is now placed in the + * graveyard. + **/ + + 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 ListAvailableMetadata(std::list<MetadataType>& target, + int64_t id) + ORTHANC_OVERRIDE; + + virtual void AddAttachment(int64_t id, + const FileInfo& attachment) + ORTHANC_OVERRIDE; + + virtual void DeleteAttachment(int64_t id, + FileContentType attachment) + ORTHANC_OVERRIDE; + + virtual void ListAvailableAttachments(std::list<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; + + 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; + + 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; + + 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; + + 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); + } + + 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); + } + }; +}
--- a/OrthancServer/IDatabaseWrapper.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../Core/DicomFormat/DicomMap.h" -#include "../Core/FileStorage/FileInfo.h" -#include "../Core/FileStorage/IStorageArea.h" -#include "../Core/SQLite/ITransaction.h" - -#include "ExportedResource.h" -#include "IDatabaseListener.h" -#include "Search/DatabaseConstraint.h" -#include "Search/DatabaseLookup.h" - -#include <list> -#include <boost/noncopyable.hpp> - -namespace Orthanc -{ - class ResourcesContent; - - - class IDatabaseWrapper : public boost::noncopyable - { - public: - class ITransaction : public boost::noncopyable - { - public: - virtual ~ITransaction() - { - } - - virtual void Begin() = 0; - - virtual void Rollback() = 0; - - virtual void Commit(int64_t fileSizeDelta) = 0; - }; - - - struct CreateInstanceResult - { - bool isNewPatient_; - bool isNewStudy_; - bool isNewSeries_; - int64_t patientId_; - int64_t studyId_; - int64_t seriesId_; - }; - - virtual ~IDatabaseWrapper() - { - } - - virtual void Open() = 0; - - 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 ListAvailableMetadata(std::list<MetadataType>& target, - int64_t id) = 0; - - virtual void ListAvailableAttachments(std::list<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() = 0; - - virtual void SetListener(IDatabaseListener& listener) = 0; - - virtual unsigned int GetDatabaseVersion() = 0; - - 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 already exists. If "false" is - // returned, the content of "result" is undefined, but - // "instanceId" must be properly set. - 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; - }; -}
--- a/OrthancServer/OrthancInitialization.cpp Sat Jan 05 15:50:55 2019 +0100 +++ b/OrthancServer/OrthancInitialization.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -45,7 +45,7 @@ #include "../Core/Logging.h" #include "../Core/OrthancException.h" -#include "SQLiteDatabaseWrapper.h" +#include "Database/SQLiteDatabaseWrapper.h" #include "OrthancConfiguration.h" #include <dcmtk/dcmnet/dul.h> // For dcmDisableGethostbyaddr()
--- a/OrthancServer/OrthancInitialization.h Sat Jan 05 15:50:55 2019 +0100 +++ b/OrthancServer/OrthancInitialization.h Sat Jan 05 16:09:21 2019 +0100 @@ -34,7 +34,7 @@ #pragma once #include "../Core/FileStorage/IStorageArea.h" -#include "IDatabaseWrapper.h" +#include "Database/IDatabaseWrapper.h" namespace Orthanc {
--- a/OrthancServer/SQLiteDatabaseWrapper.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1272 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "SQLiteDatabaseWrapper.h" - -#include "../Core/DicomFormat/DicomArray.h" -#include "../Core/Logging.h" -#include "../Core/SQLite/Transaction.h" -#include "EmbeddedResources.h" -#include "Search/ISqlLookupFormatter.h" -#include "ServerToolbox.h" - -#include <stdio.h> -#include <boost/lexical_cast.hpp> - -namespace Orthanc -{ - namespace Internals - { - class SignalFileDeleted : public SQLite::IScalarFunction - { - private: - IDatabaseListener& listener_; - - public: - SignalFileDeleted(IDatabaseListener& listener) : - listener_(listener) - { - } - - virtual const char* GetName() const - { - return "SignalFileDeleted"; - } - - virtual unsigned int GetCardinality() const - { - return 7; - } - - virtual void Compute(SQLite::FunctionContext& context) - { - 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); - - listener_.SignalFileDeleted(info); - } - }; - - class SignalResourceDeleted : public SQLite::IScalarFunction - { - private: - IDatabaseListener& listener_; - - public: - SignalResourceDeleted(IDatabaseListener& listener) : - listener_(listener) - { - } - - virtual const char* GetName() const - { - return "SignalResourceDeleted"; - } - - virtual unsigned int GetCardinality() const - { - return 2; - } - - virtual void Compute(SQLite::FunctionContext& context) - { - ResourceType type = static_cast<ResourceType>(context.GetIntValue(1)); - ServerIndexChange change(ChangeType_Deleted, type, context.GetStringValue(0)); - listener_.SignalChange(change); - } - }; - - class SignalRemainingAncestor : public SQLite::IScalarFunction - { - private: - bool hasRemainingAncestor_; - std::string remainingPublicId_; - ResourceType remainingType_; - - public: - SignalRemainingAncestor() : - hasRemainingAncestor_(false) - { - } - - void Reset() - { - hasRemainingAncestor_ = false; - } - - virtual const char* GetName() const - { - return "SignalRemainingAncestor"; - } - - virtual unsigned int GetCardinality() const - { - return 2; - } - - virtual void Compute(SQLite::FunctionContext& context) - { - VLOG(1) << "There exists a remaining ancestor with public ID \"" - << context.GetStringValue(0) - << "\" of type " - << context.GetIntValue(1); - - if (!hasRemainingAncestor_ || - remainingType_ >= context.GetIntValue(1)) - { - hasRemainingAncestor_ = true; - remainingPublicId_ = context.GetStringValue(0); - remainingType_ = static_cast<ResourceType>(context.GetIntValue(1)); - } - } - - bool HasRemainingAncestor() const - { - return hasRemainingAncestor_; - } - - const std::string& GetRemainingAncestorId() const - { - assert(hasRemainingAncestor_); - return remainingPublicId_; - } - - ResourceType GetRemainingAncestorType() const - { - assert(hasRemainingAncestor_); - return remainingType_; - } - }; - } - - - void SQLiteDatabaseWrapper::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()) - { - 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()); - } - - - 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); - - childrenPublicIds.clear(); - while (s.Step()) - { - childrenPublicIds.push_back(s.ColumnString(0)); - } - } - - - void SQLiteDatabaseWrapper::DeleteResource(int64_t id) - { - signalRemainingAncestor_->Reset(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); - s.BindInt64(0, id); - s.Run(); - - if (signalRemainingAncestor_->HasRemainingAncestor() && - listener_ != NULL) - { - listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(), - signalRemainingAncestor_->GetRemainingAncestorId()); - } - } - - - 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); - - if (s.Step()) - { - target = s.ColumnString(0); - return true; - } - else - { - return false; - } - } - - - int64_t SQLiteDatabaseWrapper::GetTableRecordCount(const std::string& table) - { - char buf[128]; - sprintf(buf, "SELECT COUNT(*) FROM %s", table.c_str()); - SQLite::Statement s(db_, buf); - - if (!s.Step()) - { - throw OrthancException(ErrorCode_InternalError); - } - - int64_t c = s.ColumnInt(0); - assert(!s.Step()); - - return c; - } - - - SQLiteDatabaseWrapper::SQLiteDatabaseWrapper(const std::string& path) : - listener_(NULL), - signalRemainingAncestor_(NULL), - version_(0) - { - db_.Open(path); - } - - - SQLiteDatabaseWrapper::SQLiteDatabaseWrapper() : - listener_(NULL), - signalRemainingAncestor_(NULL), - version_(0) - { - db_.OpenInMemory(); - } - - - void SQLiteDatabaseWrapper::Open() - { - db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); - - // Performance tuning of SQLite with PRAGMAs - // http://www.sqlite.org/pragma.html - db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); - db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); - db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); - db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); - //db_.Execute("PRAGMA TEMP_STORE=memory"); - - // Make "LIKE" case-sensitive in SQLite - db_.Execute("PRAGMA case_sensitive_like = true;"); - - if (!db_.DoesTableExist("GlobalProperties")) - { - LOG(INFO) << "Creating the database"; - std::string query; - EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE); - db_.Execute(query); - } - - // Check the version of the database - std::string tmp; - if (!LookupGlobalProperty(tmp, GlobalProperty_DatabaseSchemaVersion)) - { - tmp = "Unknown"; - } - - bool ok = false; - try - { - LOG(INFO) << "Version of the Orthanc database: " << tmp; - version_ = boost::lexical_cast<unsigned int>(tmp); - ok = true; - } - catch (boost::bad_lexical_cast&) - { - } - - if (!ok) - { - throw OrthancException(ErrorCode_IncompatibleDatabaseVersion, - "Incompatible version of the Orthanc database: " + tmp); - } - - // New in Orthanc 1.5.1 - if (version_ == 6) - { - if (!LookupGlobalProperty(tmp, GlobalProperty_GetTotalSizeIsFast) || - tmp != "1") - { - LOG(INFO) << "Installing the SQLite triggers to track the size of the attachments"; - std::string query; - EmbeddedResources::GetFileResource(query, EmbeddedResources::INSTALL_TRACK_ATTACHMENTS_SIZE); - db_.Execute(query); - } - } - - signalRemainingAncestor_ = new Internals::SignalRemainingAncestor; - db_.Register(signalRemainingAncestor_); - } - - - static void ExecuteUpgradeScript(SQLite::Connection& db, - EmbeddedResources::FileResourceId script) - { - std::string upgrade; - EmbeddedResources::GetFileResource(upgrade, script); - db.BeginTransaction(); - db.Execute(upgrade); - db.CommitTransaction(); - } - - - void SQLiteDatabaseWrapper::Upgrade(unsigned int targetVersion, - IStorageArea& storageArea) - { - if (targetVersion != 6) - { - throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); - } - - // This version of Orthanc is only compatible with versions 3, 4, - // 5 and 6 of the DB schema - if (version_ != 3 && - version_ != 4 && - version_ != 5 && - version_ != 6) - { - throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); - } - - if (version_ == 3) - { - LOG(WARNING) << "Upgrading database version from 3 to 4"; - ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_3_TO_4); - version_ = 4; - } - - if (version_ == 4) - { - LOG(WARNING) << "Upgrading database version from 4 to 5"; - ExecuteUpgradeScript(db_, EmbeddedResources::UPGRADE_DATABASE_4_TO_5); - version_ = 5; - } - - if (version_ == 5) - { - LOG(WARNING) << "Upgrading database version from 5 to 6"; - // 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(); - version_ = 6; - } - } - - - void SQLiteDatabaseWrapper::SetListener(IDatabaseListener& listener) - { - listener_ = &listener; - db_.Register(new Internals::SignalFileDeleted(listener)); - db_.Register(new Internals::SignalResourceDeleted(listener)); - } - - - 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::Transaction : public IDatabaseWrapper::ITransaction - { - private: - SQLiteDatabaseWrapper& that_; - std::auto_ptr<SQLite::Transaction> transaction_; - int64_t initialDiskSize_; - - public: - Transaction(SQLiteDatabaseWrapper& that) : - that_(that), - transaction_(new SQLite::Transaction(that_.db_)) - { -#if defined(NDEBUG) - // Release mode - initialDiskSize_ = 0; -#else - // Debug mode - initialDiskSize_ = static_cast<int64_t>(that_.GetTotalCompressedSize()); -#endif - } - - virtual void Begin() - { - transaction_->Begin(); - } - - virtual void Rollback() - { - transaction_->Rollback(); - } - - virtual void Commit(int64_t fileSizeDelta /* only used in debug */) - { - transaction_->Commit(); - - assert(initialDiskSize_ + fileSizeDelta >= 0 && - initialDiskSize_ + fileSizeDelta == static_cast<int64_t>(that_.GetTotalCompressedSize())); - } - }; - - - IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction() - { - return new Transaction(*this); - } - - - 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) - { - 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(); - } - - - 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) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); - s.BindInt64(0, parent); - s.BindInt64(1, child); - s.Run(); - } - - - 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::ListAvailableMetadata(std::list<MetadataType>& target, - int64_t id) - { - target.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type FROM Metadata WHERE id=?"); - s.BindInt64(0, id); - - while (s.Step()) - { - target.push_back(static_cast<MetadataType>(s.ColumnInt(0))); - } - } - - - 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::list<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.push_back(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) - { - 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(); - } - - - void SQLiteDatabaseWrapper::GetMainDicomTags(DicomMap& map, - int64_t id) - { - 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 = ?"); - 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.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() - { - // 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 - { - 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; - } - - 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) - { - values_.push_back(value); - return "?"; - } - - virtual std::string FormatResourceType(ResourceType level) - { - return boost::lexical_cast<std::string>(level); - } - - virtual std::string FormatWildcardEscape() - { - 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::auto_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)); - } - } - } -}
--- a/OrthancServer/SQLiteDatabaseWrapper.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,358 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "IDatabaseWrapper.h" - -#include "../Core/SQLite/Connection.h" -#include "Search/Compatibility/ICreateInstance.h" -#include "Search/Compatibility/IGetChildrenMetadata.h" -#include "Search/Compatibility/ISetResourcesContent.h" -#include "ServerToolbox.h" - -namespace Orthanc -{ - namespace Internals - { - class SignalRemainingAncestor; - } - - /** - * This class manages an instance of the Orthanc SQLite database. It - * 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::ISetResourcesContent - { - private: - class Transaction; - class LookupFormatter; - - IDatabaseListener* listener_; - SQLite::Connection db_; - Internals::SignalRemainingAncestor* signalRemainingAncestor_; - unsigned int version_; - - void GetChangesInternal(std::list<ServerIndexChange>& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults); - - void GetExportedResourcesInternal(std::list<ExportedResource>& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults); - - void ClearTable(const std::string& tableName); - - public: - SQLiteDatabaseWrapper(const std::string& path); - - SQLiteDatabaseWrapper(); - - virtual void Open() - ORTHANC_OVERRIDE; - - virtual void Close() - ORTHANC_OVERRIDE - { - db_.Close(); - } - - virtual void SetListener(IDatabaseListener& listener) - ORTHANC_OVERRIDE; - - 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() - ORTHANC_OVERRIDE; - - virtual void FlushToDisk() - ORTHANC_OVERRIDE - { - db_.FlushToDisk(); - } - - 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 - { - 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); - - - - /** - * Until Orthanc 1.4.0, the methods below were part of the - * "DatabaseWrapperBase" class, that is now placed in the - * graveyard. - **/ - - 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 ListAvailableMetadata(std::list<MetadataType>& target, - int64_t id) - ORTHANC_OVERRIDE; - - virtual void AddAttachment(int64_t id, - const FileInfo& attachment) - ORTHANC_OVERRIDE; - - virtual void DeleteAttachment(int64_t id, - FileContentType attachment) - ORTHANC_OVERRIDE; - - virtual void ListAvailableAttachments(std::list<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; - - 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; - - 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; - - 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; - - 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); - } - - 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); - } - }; -}
--- a/OrthancServer/Search/Compatibility/DatabaseLookup.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,419 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "DatabaseLookup.h" - -#include "../../../Core/OrthancException.h" -#include "../../ServerToolbox.h" -#include "SetOfResources.h" - -namespace Orthanc -{ - namespace Compatibility - { - namespace - { - // Anonymous namespace to avoid clashes between compiler modules - class MainTagsConstraints : boost::noncopyable - { - private: - std::vector<DicomTagConstraint*> constraints_; - - public: - ~MainTagsConstraints() - { - for (size_t i = 0; i < constraints_.size(); i++) - { - assert(constraints_[i] != NULL); - delete constraints_[i]; - } - } - - void Reserve(size_t n) - { - constraints_.reserve(n); - } - - size_t GetSize() const - { - return constraints_.size(); - } - - DicomTagConstraint& GetConstraint(size_t i) const - { - if (i >= constraints_.size()) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - else - { - assert(constraints_[i] != NULL); - return *constraints_[i]; - } - } - - void Add(const DatabaseConstraint& constraint) - { - constraints_.push_back(new DicomTagConstraint(constraint)); - } - }; - } - - - static void ApplyIdentifierConstraint(SetOfResources& candidates, - ILookupResources& compatibility, - const DatabaseConstraint& constraint, - ResourceType level) - { - std::list<int64_t> matches; - - switch (constraint.GetConstraintType()) - { - case ConstraintType_Equal: - compatibility.LookupIdentifier(matches, level, constraint.GetTag(), - IdentifierConstraintType_Equal, constraint.GetSingleValue()); - break; - - case ConstraintType_SmallerOrEqual: - compatibility.LookupIdentifier(matches, level, constraint.GetTag(), - IdentifierConstraintType_SmallerOrEqual, constraint.GetSingleValue()); - break; - - case ConstraintType_GreaterOrEqual: - compatibility.LookupIdentifier(matches, level, constraint.GetTag(), - IdentifierConstraintType_GreaterOrEqual, constraint.GetSingleValue()); - - break; - - case ConstraintType_Wildcard: - compatibility.LookupIdentifier(matches, level, constraint.GetTag(), - IdentifierConstraintType_Wildcard, constraint.GetSingleValue()); - - break; - - case ConstraintType_List: - for (size_t i = 0; i < constraint.GetValuesCount(); i++) - { - std::list<int64_t> tmp; - compatibility.LookupIdentifier(tmp, level, constraint.GetTag(), - IdentifierConstraintType_Wildcard, constraint.GetValue(i)); - matches.splice(matches.end(), tmp); - } - - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - candidates.Intersect(matches); - } - - - static void ApplyIdentifierRange(SetOfResources& candidates, - ILookupResources& compatibility, - const DatabaseConstraint& smaller, - const DatabaseConstraint& greater, - ResourceType level) - { - assert(smaller.GetConstraintType() == ConstraintType_SmallerOrEqual && - greater.GetConstraintType() == ConstraintType_GreaterOrEqual && - smaller.GetTag() == greater.GetTag() && - ServerToolbox::IsIdentifier(smaller.GetTag(), level)); - - std::list<int64_t> matches; - compatibility.LookupIdentifierRange(matches, level, smaller.GetTag(), - greater.GetSingleValue(), smaller.GetSingleValue()); - candidates.Intersect(matches); - } - - - static void ApplyLevel(SetOfResources& candidates, - IDatabaseWrapper& database, - ILookupResources& compatibility, - const std::vector<DatabaseConstraint>& lookup, - ResourceType level) - { - typedef std::set<const DatabaseConstraint*> SetOfConstraints; - typedef std::map<DicomTag, SetOfConstraints> Identifiers; - - // (1) Select which constraints apply to this level, and split - // them between "identifier tags" constraints and "main DICOM - // tags" constraints - - Identifiers identifiers; - SetOfConstraints mainTags; - - for (size_t i = 0; i < lookup.size(); i++) - { - if (lookup[i].GetLevel() == level) - { - if (lookup[i].IsIdentifier()) - { - identifiers[lookup[i].GetTag()].insert(&lookup[i]); - } - else - { - mainTags.insert(&lookup[i]); - } - } - } - - - // (2) Apply the constraints over the identifiers - - for (Identifiers::const_iterator it = identifiers.begin(); - it != identifiers.end(); ++it) - { - // Check whether some range constraint over identifiers is - // present at this level - const DatabaseConstraint* smaller = NULL; - const DatabaseConstraint* greater = NULL; - - for (SetOfConstraints::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); ++it2) - { - assert(*it2 != NULL); - - if ((*it2)->GetConstraintType() == ConstraintType_SmallerOrEqual) - { - smaller = *it2; - } - - if ((*it2)->GetConstraintType() == ConstraintType_GreaterOrEqual) - { - greater = *it2; - } - } - - if (smaller != NULL && - greater != NULL) - { - // There is a range constraint: Apply it, as it is more efficient - ApplyIdentifierRange(candidates, compatibility, *smaller, *greater, level); - } - else - { - smaller = NULL; - greater = NULL; - } - - for (SetOfConstraints::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); ++it2) - { - // Check to avoid applying twice the range constraint - if (*it2 != smaller && - *it2 != greater) - { - ApplyIdentifierConstraint(candidates, compatibility, **it2, level); - } - } - } - - - // (3) Apply the constraints over the main DICOM tags (no index - // here, so this is less efficient than filtering over the - // identifiers) - if (!mainTags.empty()) - { - MainTagsConstraints c; - c.Reserve(mainTags.size()); - - for (SetOfConstraints::const_iterator it = mainTags.begin(); - it != mainTags.end(); ++it) - { - assert(*it != NULL); - c.Add(**it); - } - - std::list<int64_t> source; - candidates.Flatten(compatibility, source); - candidates.Clear(); - - std::list<int64_t> filtered; - for (std::list<int64_t>::const_iterator candidate = source.begin(); - candidate != source.end(); ++candidate) - { - DicomMap tags; - database.GetMainDicomTags(tags, *candidate); - - bool match = true; - - for (size_t i = 0; i < c.GetSize(); i++) - { - if (!c.GetConstraint(i).IsMatch(tags)) - { - match = false; - break; - } - } - - if (match) - { - filtered.push_back(*candidate); - } - } - - candidates.Intersect(filtered); - } - } - - - static std::string GetOneInstance(IDatabaseWrapper& compatibility, - int64_t resource, - ResourceType level) - { - for (int i = level; i < ResourceType_Instance; i++) - { - assert(compatibility.GetResourceType(resource) == static_cast<ResourceType>(i)); - - std::list<int64_t> children; - compatibility.GetChildrenInternalId(children, resource); - - if (children.empty()) - { - throw OrthancException(ErrorCode_Database); - } - - resource = children.front(); - } - - return compatibility.GetPublicId(resource); - } - - - void DatabaseLookup::ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - { - // This is a re-implementation of - // "../../../Resources/Graveyard/DatabaseOptimizations/LookupResource.cpp" - - assert(ResourceType_Patient < ResourceType_Study && - ResourceType_Study < ResourceType_Series && - ResourceType_Series < ResourceType_Instance); - - ResourceType upperLevel = queryLevel; - ResourceType lowerLevel = queryLevel; - - for (size_t i = 0; i < lookup.size(); i++) - { - ResourceType level = lookup[i].GetLevel(); - - if (level < upperLevel) - { - upperLevel = level; - } - - if (level > lowerLevel) - { - lowerLevel = level; - } - } - - assert(upperLevel <= queryLevel && - queryLevel <= lowerLevel); - - SetOfResources candidates(database_, upperLevel); - - for (int level = upperLevel; level <= lowerLevel; level++) - { - ApplyLevel(candidates, database_, compatibility_, lookup, static_cast<ResourceType>(level)); - - if (level != lowerLevel) - { - candidates.GoDown(); - } - } - - std::list<int64_t> resources; - candidates.Flatten(compatibility_, resources); - - // Climb up, up to queryLevel - - for (int level = lowerLevel; level > queryLevel; level--) - { - std::list<int64_t> parents; - for (std::list<int64_t>::const_iterator - it = resources.begin(); it != resources.end(); ++it) - { - int64_t parent; - if (database_.LookupParent(parent, *it)) - { - parents.push_back(parent); - } - } - - resources.swap(parents); - } - - // Apply the limit, if given - - if (limit != 0 && - resources.size() > limit) - { - resources.resize(limit); - } - - // Get the public ID of all the selected resources - - size_t pos = 0; - - for (std::list<int64_t>::const_iterator - it = resources.begin(); it != resources.end(); ++it, pos++) - { - assert(database_.GetResourceType(*it) == queryLevel); - - const std::string resource = database_.GetPublicId(*it); - resourcesId.push_back(resource); - - if (instancesId != NULL) - { - if (queryLevel == ResourceType_Instance) - { - // The resource is itself the instance - instancesId->push_back(resource); - } - else - { - // Collect one child instance for each of the selected resources - instancesId->push_back(GetOneInstance(database_, *it, queryLevel)); - } - } - } - } - } -}
--- a/OrthancServer/Search/Compatibility/DatabaseLookup.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../IDatabaseWrapper.h" -#include "ILookupResources.h" - -namespace Orthanc -{ - namespace Compatibility - { - class DatabaseLookup : public boost::noncopyable - { - private: - IDatabaseWrapper& database_; - ILookupResources& compatibility_; - - public: - DatabaseLookup(IDatabaseWrapper& database, - ILookupResources& compatibility) : - database_(database), - compatibility_(compatibility) - { - } - - void ApplyLookupResources(std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit); - }; - } -}
--- a/OrthancServer/Search/Compatibility/ICreateInstance.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "ICreateInstance.h" - -#include "../../../Core/OrthancException.h" - -namespace Orthanc -{ - namespace Compatibility - { - bool ICreateInstance::Apply(ICreateInstance& database, - IDatabaseWrapper::CreateInstanceResult& result, - int64_t& instanceId, - const std::string& hashPatient, - const std::string& hashStudy, - const std::string& hashSeries, - const std::string& hashInstance) - { - { - ResourceType type; - int64_t tmp; - - if (database.LookupResource(tmp, type, hashInstance)) - { - // The instance already exists - assert(type == ResourceType_Instance); - instanceId = tmp; - return false; - } - } - - instanceId = database.CreateResource(hashInstance, ResourceType_Instance); - - result.isNewPatient_ = false; - result.isNewStudy_ = false; - result.isNewSeries_ = false; - result.patientId_ = -1; - result.studyId_ = -1; - result.seriesId_ = -1; - - // Detect up to which level the patient/study/series/instance - // hierarchy must be created - - { - ResourceType dummy; - - if (database.LookupResource(result.seriesId_, dummy, hashSeries)) - { - assert(dummy == ResourceType_Series); - // The patient, the study and the series already exist - - bool ok = (database.LookupResource(result.patientId_, dummy, hashPatient) && - database.LookupResource(result.studyId_, dummy, hashStudy)); - assert(ok); - } - else if (database.LookupResource(result.studyId_, dummy, hashStudy)) - { - assert(dummy == ResourceType_Study); - - // New series: The patient and the study already exist - result.isNewSeries_ = true; - - bool ok = database.LookupResource(result.patientId_, dummy, hashPatient); - assert(ok); - } - else if (database.LookupResource(result.patientId_, dummy, hashPatient)) - { - assert(dummy == ResourceType_Patient); - - // New study and series: The patient already exist - result.isNewStudy_ = true; - result.isNewSeries_ = true; - } - else - { - // New patient, study and series: Nothing exists - result.isNewPatient_ = true; - result.isNewStudy_ = true; - result.isNewSeries_ = true; - } - } - - // Create the series if needed - if (result.isNewSeries_) - { - result.seriesId_ = database.CreateResource(hashSeries, ResourceType_Series); - } - - // Create the study if needed - if (result.isNewStudy_) - { - result.studyId_ = database.CreateResource(hashStudy, ResourceType_Study); - } - - // Create the patient if needed - if (result.isNewPatient_) - { - result.patientId_ = database.CreateResource(hashPatient, ResourceType_Patient); - } - - // Create the parent-to-child links - database.AttachChild(result.seriesId_, instanceId); - - if (result.isNewSeries_) - { - database.AttachChild(result.studyId_, result.seriesId_); - } - - if (result.isNewStudy_) - { - database.AttachChild(result.patientId_, result.studyId_); - } - - // Sanity checks - assert(result.patientId_ != -1); - assert(result.studyId_ != -1); - assert(result.seriesId_ != -1); - assert(instanceId != -1); - - return true; - } - } -}
--- a/OrthancServer/Search/Compatibility/ICreateInstance.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../IDatabaseWrapper.h" - -namespace Orthanc -{ - namespace Compatibility - { - class ICreateInstance : public boost::noncopyable - { - public: - virtual bool LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) = 0; - - virtual int64_t CreateResource(const std::string& publicId, - ResourceType type) = 0; - - virtual void AttachChild(int64_t parent, - int64_t child) = 0; - - static bool Apply(ICreateInstance& database, - IDatabaseWrapper::CreateInstanceResult& result, - int64_t& instanceId, - const std::string& patient, - const std::string& study, - const std::string& series, - const std::string& instance); - }; - } -}
--- a/OrthancServer/Search/Compatibility/IGetChildrenMetadata.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "IGetChildrenMetadata.h" - -namespace Orthanc -{ - namespace Compatibility - { - void IGetChildrenMetadata::Apply(IGetChildrenMetadata& database, - std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata) - { - // This function comes from an optimization of - // "ServerIndex::GetSeriesStatus()" in Orthanc <= 1.5.1 - // Loop over the instances of this series - - target.clear(); - - std::list<int64_t> children; - database.GetChildrenInternalId(children, resourceId); - - std::set<int64_t> instances; - for (std::list<int64_t>::const_iterator - it = children.begin(); it != children.end(); ++it) - { - std::string value; - if (database.LookupMetadata(value, *it, metadata)) - { - target.push_back(value); - } - } - } - } -}
--- a/OrthancServer/Search/Compatibility/IGetChildrenMetadata.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../ServerEnumerations.h" - -#include <boost/noncopyable.hpp> -#include <list> - -namespace Orthanc -{ - namespace Compatibility - { - class IGetChildrenMetadata : public boost::noncopyable - { - public: - virtual void GetChildrenInternalId(std::list<int64_t>& target, - int64_t id) = 0; - - virtual bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type) = 0; - - static void Apply(IGetChildrenMetadata& database, - std::list<std::string>& target, - int64_t resourceId, - MetadataType metadata); - }; - } -}
--- a/OrthancServer/Search/Compatibility/ILookupResources.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "ILookupResources.h" - -#include "DatabaseLookup.h" - -namespace Orthanc -{ - namespace Compatibility - { - void ILookupResources::Apply( - IDatabaseWrapper& database, - ILookupResources& compatibility, - std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit) - { - Compatibility::DatabaseLookup compat(database, compatibility); - compat.ApplyLookupResources(resourcesId, instancesId, lookup, queryLevel, limit); - } - } -}
--- a/OrthancServer/Search/Compatibility/ILookupResources.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../IDatabaseWrapper.h" - -namespace Orthanc -{ - namespace Compatibility - { - /** - * This is a compatibility class that contains database primitives - * that were used in Orthanc <= 1.5.1, and that have been removed - * during the optimization of the database engine. - **/ - class ILookupResources : public boost::noncopyable - { - public: - virtual ~ILookupResources() - { - } - - virtual void GetAllInternalIds(std::list<int64_t>& target, - ResourceType resourceType) = 0; - - virtual void LookupIdentifier(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) = 0; - - virtual void LookupIdentifierRange(std::list<int64_t>& result, - ResourceType level, - const DicomTag& tag, - const std::string& start, - const std::string& end) = 0; - - static void Apply(IDatabaseWrapper& database, - ILookupResources& compatibility, - std::list<std::string>& resourcesId, - std::list<std::string>* instancesId, - const std::vector<DatabaseConstraint>& lookup, - ResourceType queryLevel, - size_t limit); - }; - } -}
--- a/OrthancServer/Search/Compatibility/ISetResourcesContent.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../ServerToolbox.h" - -namespace Orthanc -{ - namespace Compatibility - { - class ISetResourcesContent : public boost::noncopyable - { - public: - virtual ~ISetResourcesContent() - { - } - - virtual void SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) = 0; - - virtual void SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) = 0; - - virtual void SetMetadata(int64_t id, - MetadataType type, - const std::string& value) = 0; - - static void Apply(ISetResourcesContent& that, - const ResourcesContent& content) - { - content.Store(that); - } - }; - } -}
--- a/OrthancServer/Search/Compatibility/SetOfResources.cpp Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "SetOfResources.h" - -#include "../../../Core/OrthancException.h" - - -namespace Orthanc -{ - namespace Compatibility - { - void SetOfResources::Intersect(const std::list<int64_t>& resources) - { - if (resources_.get() == NULL) - { - resources_.reset(new Resources); - - for (std::list<int64_t>::const_iterator - it = resources.begin(); it != resources.end(); ++it) - { - resources_->insert(*it); - } - } - else - { - std::auto_ptr<Resources> filtered(new Resources); - - for (std::list<int64_t>::const_iterator - it = resources.begin(); it != resources.end(); ++it) - { - if (resources_->find(*it) != resources_->end()) - { - filtered->insert(*it); - } - } - - resources_ = filtered; - } - } - - - void SetOfResources::GoDown() - { - if (level_ == ResourceType_Instance) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - - if (resources_.get() != NULL) - { - std::auto_ptr<Resources> children(new Resources); - - for (Resources::const_iterator it = resources_->begin(); - it != resources_->end(); ++it) - { - std::list<int64_t> tmp; - database_.GetChildrenInternalId(tmp, *it); - - for (std::list<int64_t>::const_iterator - child = tmp.begin(); child != tmp.end(); ++child) - { - children->insert(*child); - } - } - - resources_ = children; - } - - switch (level_) - { - case ResourceType_Patient: - level_ = ResourceType_Study; - break; - - case ResourceType_Study: - level_ = ResourceType_Series; - break; - - case ResourceType_Series: - level_ = ResourceType_Instance; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - - void SetOfResources::Flatten(std::list<std::string>& result) - { - result.clear(); - - if (resources_.get() == NULL) - { - // All the resources of this level are part of the filter - database_.GetAllPublicIds(result, level_); - } - else - { - for (Resources::const_iterator it = resources_->begin(); - it != resources_->end(); ++it) - { - result.push_back(database_.GetPublicId(*it)); - } - } - } - - - void SetOfResources::Flatten(ILookupResources& compatibility, - std::list<int64_t>& result) - { - result.clear(); - - if (resources_.get() == NULL) - { - // All the resources of this level are part of the filter - compatibility.GetAllInternalIds(result, level_); - } - else - { - for (Resources::const_iterator it = resources_->begin(); - it != resources_->end(); ++it) - { - result.push_back(*it); - } - } - } - } -}
--- a/OrthancServer/Search/Compatibility/SetOfResources.h Sat Jan 05 15:50:55 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2019 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 "../../IDatabaseWrapper.h" -#include "ILookupResources.h" - -#include <set> -#include <memory> - -namespace Orthanc -{ - namespace Compatibility - { - class SetOfResources : public boost::noncopyable - { - private: - typedef std::set<int64_t> Resources; - - IDatabaseWrapper& database_; - ResourceType level_; - std::auto_ptr<Resources> resources_; - - public: - SetOfResources(IDatabaseWrapper& database, - ResourceType level) : - database_(database), - level_(level) - { - } - - ResourceType GetLevel() const - { - return level_; - } - - void Intersect(const std::list<int64_t>& resources); - - void GoDown(); - - void Flatten(ILookupResources& compatibility, - std::list<int64_t>& result); - - void Flatten(std::list<std::string>& result); - - void Clear() - { - resources_.reset(NULL); - } - }; - } -}
--- a/OrthancServer/ServerIndex.cpp Sat Jan 05 15:50:55 2019 +0100 +++ b/OrthancServer/ServerIndex.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -944,7 +944,6 @@ // Check whether the series of this new instance is now completed - // TODO - SPEED THIS UP SeriesStatus seriesStatus = GetSeriesStatus(status.seriesId_); if (seriesStatus == SeriesStatus_Complete) {
--- a/OrthancServer/ServerIndex.h Sat Jan 05 15:50:55 2019 +0100 +++ b/OrthancServer/ServerIndex.h Sat Jan 05 16:09:21 2019 +0100 @@ -40,7 +40,7 @@ #include "../Core/DicomFormat/DicomMap.h" #include "ServerEnumerations.h" -#include "IDatabaseWrapper.h" +#include "Database/IDatabaseWrapper.h" namespace Orthanc {
--- a/OrthancServer/ServerToolbox.cpp Sat Jan 05 15:50:55 2019 +0100 +++ b/OrthancServer/ServerToolbox.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -40,7 +40,7 @@ #include "../Core/Logging.h" #include "../Core/OrthancException.h" #include "../Plugins/Engine/OrthancPlugins.h" -#include "IDatabaseWrapper.h" +#include "Database/IDatabaseWrapper.h" #include "ServerContext.h" #include <cassert>
--- a/Plugins/Engine/OrthancPluginDatabase.h Sat Jan 05 15:50:55 2019 +0100 +++ b/Plugins/Engine/OrthancPluginDatabase.h Sat Jan 05 16:09:21 2019 +0100 @@ -36,10 +36,10 @@ #if ORTHANC_ENABLE_PLUGINS == 1 #include "../../Core/SharedLibrary.h" -#include "../../OrthancServer/Search/Compatibility/ICreateInstance.h" -#include "../../OrthancServer/Search/Compatibility/IGetChildrenMetadata.h" -#include "../../OrthancServer/Search/Compatibility/ILookupResources.h" -#include "../../OrthancServer/Search/Compatibility/ISetResourcesContent.h" +#include "../../OrthancServer/Database/Compatibility/ICreateInstance.h" +#include "../../OrthancServer/Database/Compatibility/IGetChildrenMetadata.h" +#include "../../OrthancServer/Database/Compatibility/ILookupResources.h" +#include "../../OrthancServer/Database/Compatibility/ISetResourcesContent.h" #include "../Include/orthanc/OrthancCDatabasePlugin.h" #include "PluginsErrorDictionary.h"
--- a/UnitTestsSources/MultiThreadingTests.cpp Sat Jan 05 15:50:55 2019 +0100 +++ b/UnitTestsSources/MultiThreadingTests.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -42,7 +42,7 @@ #include "../Core/SerializationToolbox.h" #include "../Core/SystemToolbox.h" #include "../Core/Toolbox.h" -#include "../OrthancServer/SQLiteDatabaseWrapper.h" +#include "../OrthancServer/Database/SQLiteDatabaseWrapper.h" #include "../OrthancServer/ServerContext.h" #include "../OrthancServer/ServerJobs/LuaJobManager.h" #include "../OrthancServer/ServerJobs/OrthancJobUnserializer.h"
--- a/UnitTestsSources/ServerIndexTests.cpp Sat Jan 05 15:50:55 2019 +0100 +++ b/UnitTestsSources/ServerIndexTests.cpp Sat Jan 05 16:09:21 2019 +0100 @@ -37,7 +37,7 @@ #include "../Core/FileStorage/FilesystemStorage.h" #include "../Core/FileStorage/MemoryStorageArea.h" #include "../Core/Logging.h" -#include "../OrthancServer/SQLiteDatabaseWrapper.h" +#include "../OrthancServer/Database/SQLiteDatabaseWrapper.h" #include "../OrthancServer/ServerContext.h" #include "../OrthancServer/ServerToolbox.h"