# HG changeset patch # User Sebastien Jodogne # Date 1445859034 -3600 # Node ID b3de74dec2d55b9ad88fe61c7721ed0c79b2edf5 # Parent 54d78925cbb6b8544efed41d44d1e2f79c7e6775# Parent 8fc1d096aa38e3b1c9a36c29e49bc73d8d2168aa integration mainline->db-changes diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/DatabaseWrapper.cpp --- a/OrthancServer/DatabaseWrapper.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/DatabaseWrapper.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -373,7 +373,7 @@ boost::lexical_cast(GlobalProperty_DatabaseSchemaVersion) + ";"); db_.CommitTransaction(); version_ = 6; - } + } } diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/DatabaseWrapper.h Mon Oct 26 12:30:34 2015 +0100 @@ -315,11 +315,19 @@ return base_.IsExistingResource(internalId); } - virtual void LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value) + virtual void LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value) { - base_.LookupIdentifier(target, tag, value); + base_.LookupIdentifierExact(target, level, tag, value); + } + + virtual void LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value) + { + base_.LookupIdentifierWildcard(target, tag, value); } virtual void GetAllMetadata(std::map& target, diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/DatabaseWrapperBase.cpp --- a/OrthancServer/DatabaseWrapperBase.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/DatabaseWrapperBase.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -665,22 +665,25 @@ } - void DatabaseWrapperBase::LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value) + void DatabaseWrapperBase::LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value) { - assert(tag == DICOM_TAG_PATIENT_ID || - tag == DICOM_TAG_STUDY_INSTANCE_UID || - tag == DICOM_TAG_SERIES_INSTANCE_UID || - tag == DICOM_TAG_SOP_INSTANCE_UID || - tag == DICOM_TAG_ACCESSION_NUMBER); + assert((level == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) || + (level == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) || + (level == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) || + (level == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) || + (level == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID)); SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT id FROM DicomIdentifiers WHERE tagGroup=? AND tagElement=? and value=?"); + "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " + "d.id = r.internalId AND r.resourceType=? AND d.tagGroup=? AND d.tagElement=? AND d.value=?"); - s.BindInt(0, tag.GetGroup()); - s.BindInt(1, tag.GetElement()); - s.BindString(2, value); + s.BindInt(0, level); + s.BindInt(1, tag.GetGroup()); + s.BindInt(2, tag.GetElement()); + s.BindString(3, value); target.clear(); @@ -689,4 +692,13 @@ target.push_back(s.ColumnInt64(0)); } } + + + void DatabaseWrapperBase::LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value) + { + // TODO + throw OrthancException(ErrorCode_NotImplemented); + } } diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/DatabaseWrapperBase.h --- a/OrthancServer/DatabaseWrapperBase.h Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/DatabaseWrapperBase.h Mon Oct 26 12:30:34 2015 +0100 @@ -190,9 +190,14 @@ bool IsExistingResource(int64_t internalId); - void LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value); + void LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value); + + void LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value); }; } diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/IDatabaseWrapper.h --- a/OrthancServer/IDatabaseWrapper.h Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/IDatabaseWrapper.h Mon Oct 26 12:30:34 2015 +0100 @@ -146,9 +146,33 @@ virtual bool LookupGlobalProperty(std::string& target, GlobalProperty property) = 0; - virtual void LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value) = 0; + virtual void LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value) = 0; + + /** + * Primitive for wildcard matching, as defined in DICOM: + * http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_C.2.html#sect_C.2.2.2.4 + * + * "Any occurrence of an "*" or a "?", then "*" shall match any + * sequence of characters (including a zero length value) and "?" + * shall match any single character. This matching is case + * sensitive, except for Attributes with an PN Value + * Representation (e.g., Patient Name (0010,0010))." + * + * Pay attention to the fact that "*" (resp. "?") generally + * corresponds to "%" (resp. "_") in primitive LIKE of SQL. The + * values "%", "_", "\" should in the user request should + * respectively be escaped as "\%", "\_" and "\\". + * + * This matching must be case sensitive: The special case of PN VR + * is taken into consideration by normalizing the query string in + * method "ServerIndex::LookupIdentifierWildcard()". + **/ + virtual void LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value) = 0; virtual bool LookupMetadata(std::string& target, int64_t id, diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/OrthancMoveRequestHandler.cpp --- a/OrthancServer/OrthancMoveRequestHandler.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/OrthancMoveRequestHandler.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -148,7 +148,7 @@ const std::string& content = value.GetContent(); std::list ids; - context_.GetIndex().LookupIdentifier(ids, tag, content, level); + context_.GetIndex().LookupIdentifierExact(ids, level, tag, content); if (ids.size() != 1) { diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -892,7 +892,7 @@ ResourceType level) { std::list tmp; - index.LookupIdentifier(tmp, tag, value, level); + index.LookupIdentifierExact(tmp, level, tag, value); for (std::list::const_iterator it = tmp.begin(); it != tmp.end(); ++it) diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/ResourceFinder.cpp --- a/OrthancServer/ResourceFinder.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/ResourceFinder.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -160,7 +160,7 @@ << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")"; std::list resources; - index_.LookupIdentifier(resources, tag, value, level_); + index_.LookupIdentifierExact(resources, level_, tag, value); if (isFilterApplied_) { diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/ServerIndex.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -1895,31 +1895,28 @@ - void ServerIndex::LookupIdentifier(std::list& result, - const DicomTag& tag, - const std::string& value, - ResourceType type) + void ServerIndex::LookupIdentifierExact(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& value) { - assert(tag == DICOM_TAG_PATIENT_ID || - tag == DICOM_TAG_STUDY_INSTANCE_UID || - tag == DICOM_TAG_SERIES_INSTANCE_UID || - tag == DICOM_TAG_SOP_INSTANCE_UID || - tag == DICOM_TAG_ACCESSION_NUMBER); + assert((level == ResourceType_Patient && tag == DICOM_TAG_PATIENT_ID) || + (level == ResourceType_Study && tag == DICOM_TAG_STUDY_INSTANCE_UID) || + (level == ResourceType_Study && tag == DICOM_TAG_ACCESSION_NUMBER) || + (level == ResourceType_Series && tag == DICOM_TAG_SERIES_INSTANCE_UID) || + (level == ResourceType_Instance && tag == DICOM_TAG_SOP_INSTANCE_UID)); result.clear(); boost::mutex::scoped_lock lock(mutex_); std::list id; - db_.LookupIdentifier(id, tag, value); + db_.LookupIdentifierExact(id, level, tag, value); for (std::list::const_iterator it = id.begin(); it != id.end(); ++it) { - if (db_.GetResourceType(*it) == type) - { - result.push_back(db_.GetPublicId(*it)); - } + result.push_back(db_.GetPublicId(*it)); } } diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/ServerIndex.h Mon Oct 26 12:30:34 2015 +0100 @@ -235,10 +235,10 @@ /* out */ unsigned int& countInstances, const std::string& publicId); - void LookupIdentifier(std::list& result, - const DicomTag& tag, - const std::string& value, - ResourceType type); + void LookupIdentifierExact(std::list& result, + ResourceType level, + const DicomTag& tag, + const std::string& value); StoreStatus AddAttachment(const FileInfo& attachment, const std::string& publicId); diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/ServerToolbox.cpp --- a/OrthancServer/ServerToolbox.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/ServerToolbox.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -211,7 +211,7 @@ tag != DICOM_TAG_SOP_INSTANCE_UID && tag != DICOM_TAG_ACCESSION_NUMBER) { - s = NormalizeIdentifierTag(s); + s = NormalizeTagForWildcard(s); } database.SetIdentifierTag(resource, tag, s); @@ -376,7 +376,7 @@ } - std::string NormalizeIdentifierTag(const std::string& value) + std::string NormalizeTagForWildcard(const std::string& value) { std::string s = Toolbox::ConvertToAscii(Toolbox::StripSpaces(value)); Toolbox::ToUpperCase(s); diff -r 8fc1d096aa38 -r b3de74dec2d5 OrthancServer/ServerToolbox.h --- a/OrthancServer/ServerToolbox.h Fri Oct 23 17:04:22 2015 +0200 +++ b/OrthancServer/ServerToolbox.h Mon Oct 26 12:30:34 2015 +0100 @@ -60,6 +60,6 @@ IStorageArea& storageArea, ResourceType level); - std::string NormalizeIdentifierTag(const std::string& value); + std::string NormalizeTagForWildcard(const std::string& value); } } diff -r 8fc1d096aa38 -r b3de74dec2d5 Plugins/Engine/OrthancPluginDatabase.cpp --- a/Plugins/Engine/OrthancPluginDatabase.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -594,9 +594,10 @@ } - void OrthancPluginDatabase::LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value) + void OrthancPluginDatabase::LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value) { ResetAnswers(); @@ -605,9 +606,52 @@ tmp.element = tag.GetElement(); tmp.value = value.c_str(); - CheckSuccess(backend_.lookupIdentifier(GetContext(), payload_, &tmp)); + if (extensions_.lookupIdentifierExact != NULL) + { + CheckSuccess(extensions_.lookupIdentifierExact(GetContext(), payload_, Plugins::Convert(level), &tmp)); + ForwardAnswers(target); + } + else + { + // Emulate "lookupIdentifierExact" if unavailable + + if (backend_.lookupIdentifier == NULL) + { + LOG(ERROR) << "The plugin does not have the extension \"lookupIdentifierExact\""; + throw OrthancException(ErrorCode_DatabasePlugin); + } + + CheckSuccess(backend_.lookupIdentifier(GetContext(), payload_, &tmp)); + + if (type_ != _OrthancPluginDatabaseAnswerType_None && + type_ != _OrthancPluginDatabaseAnswerType_Int64) + { + throw OrthancException(ErrorCode_DatabasePlugin); + } - ForwardAnswers(target); + target.clear(); + + if (type_ == _OrthancPluginDatabaseAnswerType_Int64) + { + for (std::list::const_iterator + it = answerInt64_.begin(); it != answerInt64_.end(); ++it) + { + if (GetResourceType(*it) == level) + { + target.push_back(*it); + } + } + } + } + } + + + void OrthancPluginDatabase::LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value) + { + // TODO + throw OrthancException(ErrorCode_NotImplemented); } diff -r 8fc1d096aa38 -r b3de74dec2d5 Plugins/Engine/OrthancPluginDatabase.h --- a/Plugins/Engine/OrthancPluginDatabase.h Fri Oct 23 17:04:22 2015 +0200 +++ b/Plugins/Engine/OrthancPluginDatabase.h Mon Oct 26 12:30:34 2015 +0100 @@ -203,9 +203,14 @@ virtual bool LookupGlobalProperty(std::string& target, GlobalProperty property); - virtual void LookupIdentifier(std::list& target, - const DicomTag& tag, - const std::string& value); + virtual void LookupIdentifierExact(std::list& target, + ResourceType level, + const DicomTag& tag, + const std::string& value); + + virtual void LookupIdentifierWildcard(std::list& target, + const DicomTag& tag, + const std::string& value); virtual bool LookupMetadata(std::string& target, int64_t id, diff -r 8fc1d096aa38 -r b3de74dec2d5 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -911,7 +911,7 @@ CheckContextAvailable(); std::list result; - pimpl_->context_->GetIndex().LookupIdentifier(result, tag, p.argument, level); + pimpl_->context_->GetIndex().LookupIdentifierExact(result, level, tag, p.argument); if (result.size() == 1) { diff -r 8fc1d096aa38 -r b3de74dec2d5 Plugins/Include/orthanc/OrthancCDatabasePlugin.h --- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Fri Oct 23 17:04:22 2015 +0200 +++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Mon Oct 26 12:30:34 2015 +0100 @@ -522,7 +522,9 @@ void* payload, int32_t property); - /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + /* Use "OrthancPluginDatabaseExtensions::lookupIdentifierExact" + instead of this function as of Orthanc 0.9.5 (db v6), can be set to NULL. + Output: Use OrthancPluginDatabaseAnswerInt64() */ OrthancPluginErrorCode (*lookupIdentifier) ( /* outputs */ OrthancPluginDatabaseContext* context, @@ -661,6 +663,15 @@ /* inputs */ void* payload, int64_t id); + + /* Output: Use OrthancPluginDatabaseAnswerInt64() */ + OrthancPluginErrorCode (*lookupIdentifierExact) ( + /* outputs */ + OrthancPluginDatabaseContext* context, + /* inputs */ + void* payload, + OrthancPluginResourceType resourceType, + const OrthancPluginDicomTag* tag); } OrthancPluginDatabaseExtensions; /*& target /*out*/, - uint16_t group, - uint16_t element, - const char* value) = 0; + virtual void LookupIdentifierExact(std::list& target /*out*/, + OrthancPluginResourceType resourceType, + uint16_t group, + uint16_t element, + const char* value) = 0; virtual bool LookupMetadata(std::string& target /*out*/, int64_t id, @@ -1295,9 +1296,10 @@ } - static OrthancPluginErrorCode LookupIdentifier(OrthancPluginDatabaseContext* context, - void* payload, - const OrthancPluginDicomTag* tag) + static OrthancPluginErrorCode LookupIdentifierExact(OrthancPluginDatabaseContext* context, + void* payload, + OrthancPluginResourceType resourceType, + const OrthancPluginDicomTag* tag) { IDatabaseBackend* backend = reinterpret_cast(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); @@ -1305,7 +1307,7 @@ try { std::list target; - backend->LookupIdentifier(target, tag->group, tag->element, tag->value); + backend->LookupIdentifierExact(target, resourceType, tag->group, tag->element, tag->value); for (std::list::const_iterator it = target.begin(); it != target.end(); ++it) @@ -1824,7 +1826,7 @@ params.logExportedResource = LogExportedResource; params.lookupAttachment = LookupAttachment; params.lookupGlobalProperty = LookupGlobalProperty; - params.lookupIdentifier = LookupIdentifier; + params.lookupIdentifier = NULL; // Unused starting with Orthanc 0.9.5 (db v6) params.lookupIdentifier2 = NULL; // Unused starting with Orthanc 0.9.5 (db v6) params.lookupMetadata = LookupMetadata; params.lookupParent = LookupParent; @@ -1846,6 +1848,7 @@ extensions.getDatabaseVersion = GetDatabaseVersion; extensions.upgradeDatabase = UpgradeDatabase; extensions.clearMainDicomTags = ClearMainDicomTags; + extensions.lookupIdentifierExact = LookupIdentifierExact; // New in Orthanc 0.9.5 (db v6) OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackendV2(context, ¶ms, &extensions, &backend); if (!context) diff -r 8fc1d096aa38 -r b3de74dec2d5 Plugins/Samples/DatabasePlugin/Database.h --- a/Plugins/Samples/DatabasePlugin/Database.h Fri Oct 23 17:04:22 2015 +0200 +++ b/Plugins/Samples/DatabasePlugin/Database.h Mon Oct 26 12:30:34 2015 +0100 @@ -187,12 +187,14 @@ return base_.LookupGlobalProperty(target, static_cast(property)); } - virtual void LookupIdentifier(std::list& target /*out*/, - uint16_t group, - uint16_t element, - const char* value) + virtual void LookupIdentifierExact(std::list& target /*out*/, + OrthancPluginResourceType level, + uint16_t group, + uint16_t element, + const char* value) { - base_.LookupIdentifier(target, Orthanc::DicomTag(group, element), value); + base_.LookupIdentifierExact(target, Orthanc::Plugins::Convert(level), + Orthanc::DicomTag(group, element), value); } virtual bool LookupMetadata(std::string& target /*out*/, diff -r 8fc1d096aa38 -r b3de74dec2d5 Resources/Configuration.json --- a/Resources/Configuration.json Fri Oct 23 17:04:22 2015 +0200 +++ b/Resources/Configuration.json Mon Oct 26 12:30:34 2015 +0100 @@ -258,8 +258,9 @@ // deleted as new requests are issued. "QueryRetrieveSize" : 10, - // When handling a C-Find SCP request, setting this flag to "false" - // will enable case-insensitive match for PN value representation - // (such as PatientName). By default, the search is case-insensitive. + // When handling a C-Find SCP request, setting this flag to "true" + // will enable case-sensitive match for PN value representation + // (such as PatientName). By default, the search is + // case-insensitive, which does not follow the DICOM standard. "CaseSensitivePN" : false } diff -r 8fc1d096aa38 -r b3de74dec2d5 UnitTestsSources/ServerIndexTests.cpp --- a/UnitTestsSources/ServerIndexTests.cpp Fri Oct 23 17:04:22 2015 +0200 +++ b/UnitTestsSources/ServerIndexTests.cpp Mon Oct 26 12:30:34 2015 +0100 @@ -692,29 +692,29 @@ std::list s; - index_->LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "0"); + index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "0"); ASSERT_EQ(2u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), a[0]) != s.end()); ASSERT_TRUE(std::find(s.begin(), s.end(), a[2]) != s.end()); - index_->LookupIdentifier(s, DICOM_TAG_SERIES_INSTANCE_UID, "0"); + index_->LookupIdentifierExact(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "0"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), a[3]) != s.end()); - index_->LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "1"); + index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); - index_->LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "1"); + index_->LookupIdentifierExact(s, ResourceType_Study, DICOM_TAG_STUDY_INSTANCE_UID, "1"); ASSERT_EQ(1u, s.size()); ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end()); - index_->LookupIdentifier(s, DICOM_TAG_SERIES_INSTANCE_UID, "1"); + index_->LookupIdentifierExact(s, ResourceType_Series, DICOM_TAG_SERIES_INSTANCE_UID, "1"); ASSERT_EQ(0u, s.size()); /*{ std::list s; - context.GetIndex().LookupIdentifier(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); + context.GetIndex().LookupIdentifierExact(s, DICOM_TAG_STUDY_INSTANCE_UID, "1.2.250.1.74.20130819132500.29000036381059"); for (std::list::iterator i = s.begin(); i != s.end(); i++) { std::cout << "*** " << *i << std::endl;;