changeset 1726:9d8bb6bc2890

integration db-changes->mainline
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 20 Oct 2015 11:22:50 +0200
parents d72cf0c11f42 (current diff) a7c05bbfaf6a (diff)
children bc34c69b594a
files
diffstat 25 files changed, 234 insertions(+), 320 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/Core/DicomFormat/DicomMap.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -57,10 +57,10 @@
   {
     //DicomTag(0x0010, 0x1020), // PatientSize
     //DicomTag(0x0010, 0x1030)  // PatientWeight
-    DicomTag(0x0008, 0x0020),   // StudyDate
+    DICOM_TAG_STUDY_DATE,
     DicomTag(0x0008, 0x0030),   // StudyTime
-    DicomTag(0x0008, 0x1030),   // StudyDescription
     DicomTag(0x0020, 0x0010),   // StudyID
+    DICOM_TAG_STUDY_DESCRIPTION,
     DICOM_TAG_ACCESSION_NUMBER,
     DICOM_TAG_STUDY_INSTANCE_UID
   };
@@ -73,7 +73,7 @@
     DicomTag(0x0008, 0x0060),   // Modality
     DicomTag(0x0008, 0x0070),   // Manufacturer
     DicomTag(0x0008, 0x1010),   // StationName
-    DicomTag(0x0008, 0x103e),   // SeriesDescription
+    DICOM_TAG_SERIES_DESCRIPTION,
     DicomTag(0x0018, 0x0015),   // BodyPartExamined
     DicomTag(0x0018, 0x0024),   // SequenceName
     DicomTag(0x0018, 0x1030),   // ProtocolName
--- a/Core/DicomFormat/DicomTag.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/Core/DicomFormat/DicomTag.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -243,14 +243,4 @@
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
   }
-
-
-  bool DicomTag::IsIdentifier() const
-  {
-    return (*this == DICOM_TAG_PATIENT_ID ||
-            *this == DICOM_TAG_STUDY_INSTANCE_UID ||
-            *this == DICOM_TAG_ACCESSION_NUMBER ||
-            *this == DICOM_TAG_SERIES_INSTANCE_UID ||
-            *this == DICOM_TAG_SOP_INSTANCE_UID);
-  }
 }
--- a/Core/DicomFormat/DicomTag.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/Core/DicomFormat/DicomTag.h	Tue Oct 20 11:22:50 2015 +0200
@@ -86,8 +86,6 @@
 
     static void AddTagsForModule(std::set<DicomTag>& target,
                                  DicomModule module);
-
-    bool IsIdentifier() const;
   };
 
   // Aliases for the most useful tags
@@ -109,6 +107,9 @@
   static const DicomTag DICOM_TAG_PATIENT_NAME(0x0010, 0x0010);
   static const DicomTag DICOM_TAG_ENCAPSULATED_DOCUMENT(0x0042, 0x0011);
 
+  static const DicomTag DICOM_TAG_STUDY_DESCRIPTION(0x0008, 0x1030);
+  static const DicomTag DICOM_TAG_SERIES_DESCRIPTION(0x0008, 0x103e);
+
   // The following is used for "modify/anonymize" operations
   static const DicomTag DICOM_TAG_SOP_CLASS_UID(0x0008, 0x0016);
   static const DicomTag DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID(0x0002, 0x0002);
--- a/NEWS	Fri Oct 16 17:26:11 2015 +0200
+++ b/NEWS	Tue Oct 20 11:22:50 2015 +0200
@@ -22,6 +22,7 @@
 Maintenance
 -----------
 
+* C-Move SCP for studies using AccessionNumber tag
 * Fix issue 4 (C-Store Association not renegotiated on Specific-to-specific transfer syntax change)
 * "/system" URI gives information about the plugins used for storage area and DB back-end
 * Plugin callbacks should now return explicit "OrthancPluginErrorCode" instead of integers
--- a/OrthancServer/DatabaseWrapper.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/DatabaseWrapper.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -268,6 +268,8 @@
 
   void DatabaseWrapper::Open()
   {
+    db_.Execute("PRAGMA ENCODING=\"UTF-8\";");
+
     // Performance tuning of SQLite with PRAGMAs
     // http://www.sqlite.org/pragma.html
     db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;");
@@ -462,19 +464,6 @@
   }
 
 
-  void DatabaseWrapper::LookupIdentifier(std::list<int64_t>& target,
-                                         const DicomTag& tag,
-                                         const std::string& value)
-  {
-    if (!tag.IsIdentifier())
-    {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
-    }
-
-    base_.LookupIdentifier(target, tag, value);
-  }
-
-
   void DatabaseWrapper::GetAllMetadata(std::map<MetadataType, std::string>& target,
                                        int64_t id)
   {
--- a/OrthancServer/DatabaseWrapper.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/DatabaseWrapper.h	Tue Oct 20 11:22:50 2015 +0200
@@ -178,6 +178,13 @@
       base_.SetMainDicomTag(id, tag, value);
     }
 
+    virtual void SetIdentifierTag(int64_t id,
+                                 const DicomTag& tag,
+                                 const std::string& value)
+    {
+      base_.SetIdentifierTag(id, tag, value);
+    }
+
     virtual void GetMainDicomTags(DicomMap& map,
                                   int64_t id)
     {
@@ -310,12 +317,9 @@
 
     virtual void LookupIdentifier(std::list<int64_t>& target,
                                   const DicomTag& tag,
-                                  const std::string& value);
-
-    virtual void LookupIdentifier(std::list<int64_t>& target,
                                   const std::string& value)
     {
-      base_.LookupIdentifier(target, value);
+      base_.LookupIdentifier(target, tag, value);
     }
 
     virtual void GetAllMetadata(std::map<MetadataType, std::string>& target,
--- a/OrthancServer/DatabaseWrapperBase.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/DatabaseWrapperBase.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -312,11 +312,11 @@
   }
 
 
-  static void SetMainDicomTagsInternal(SQLite::Statement& s,
-                                       int64_t id,
-                                       const DicomTag& tag,
-                                       const std::string& value)
+  void DatabaseWrapperBase::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());
@@ -325,22 +325,19 @@
   }
 
 
-  void DatabaseWrapperBase::SetMainDicomTag(int64_t id,
-                                            const DicomTag& tag,
-                                            const std::string& value)
+  void DatabaseWrapperBase::SetIdentifierTag(int64_t id,
+                                             const DicomTag& tag,
+                                             const std::string& value)
   {
-    if (tag.IsIdentifier())
-    {
-      SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)");
-      SetMainDicomTagsInternal(s, id, tag, value);
-    }
-    else
-    {
-      SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)");
-      SetMainDicomTagsInternal(s, id, tag, 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 DatabaseWrapperBase::GetMainDicomTags(DicomMap& map,
                                              int64_t id)
   {
@@ -354,15 +351,6 @@
                    s.ColumnInt(2),
                    s.ColumnString(3));
     }
-
-    SQLite::Statement s2(db_, SQLITE_FROM_HERE, "SELECT * FROM DicomIdentifiers WHERE id=?");
-    s2.BindInt64(0, id);
-    while (s2.Step())
-    {
-      map.SetValue(s2.ColumnInt(1),
-                   s2.ColumnInt(2),
-                   s2.ColumnString(3));
-    }
   }
 
 
@@ -681,6 +669,12 @@
                                              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);
+    
     SQLite::Statement s(db_, SQLITE_FROM_HERE, 
                         "SELECT id FROM DicomIdentifiers WHERE tagGroup=? AND tagElement=? and value=?");
 
@@ -695,21 +689,4 @@
       target.push_back(s.ColumnInt64(0));
     }
   }
-
-
-  void DatabaseWrapperBase::LookupIdentifier(std::list<int64_t>& target,
-                                             const std::string& value)
-  {
-    SQLite::Statement s(db_, SQLITE_FROM_HERE, 
-                        "SELECT id FROM DicomIdentifiers WHERE value=?");
-
-    s.BindString(0, value);
-
-    target.clear();
-
-    while (s.Step())
-    {
-      target.push_back(s.ColumnInt64(0));
-    }
-  }
 }
--- a/OrthancServer/DatabaseWrapperBase.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/DatabaseWrapperBase.h	Tue Oct 20 11:22:50 2015 +0200
@@ -132,6 +132,10 @@
                          const DicomTag& tag,
                          const std::string& value);
 
+    void SetIdentifierTag(int64_t id,
+                          const DicomTag& tag,
+                          const std::string& value);
+
     void GetMainDicomTags(DicomMap& map,
                           int64_t id);
 
@@ -189,9 +193,6 @@
     void LookupIdentifier(std::list<int64_t>& target,
                           const DicomTag& tag,
                           const std::string& value);
-
-    void LookupIdentifier(std::list<int64_t>& target,
-                          const std::string& value);
   };
 }
 
--- a/OrthancServer/IDatabaseWrapper.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/IDatabaseWrapper.h	Tue Oct 20 11:22:50 2015 +0200
@@ -150,9 +150,6 @@
                                   const DicomTag& tag,
                                   const std::string& value) = 0;
 
-    virtual void LookupIdentifier(std::list<int64_t>& target,
-                                  const std::string& value) = 0;
-
     virtual bool LookupMetadata(std::string& target,
                                 int64_t id,
                                 MetadataType type) = 0;
@@ -178,6 +175,10 @@
                                  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;
--- a/OrthancServer/OrthancMoveRequestHandler.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/OrthancMoveRequestHandler.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -105,9 +105,34 @@
 
 
   bool OrthancMoveRequestHandler::LookupIdentifier(std::string& publicId,
-                                                   DicomTag tag,
+                                                   ResourceType level,
                                                    const DicomMap& input)
   {
+    DicomTag tag(0, 0);   // Dummy initialization
+
+    switch (level)
+    {
+      case ResourceType_Patient:
+        tag = DICOM_TAG_PATIENT_ID;
+        break;
+
+      case ResourceType_Study:
+        tag = (input.HasTag(DICOM_TAG_ACCESSION_NUMBER) ? 
+               DICOM_TAG_ACCESSION_NUMBER : DICOM_TAG_STUDY_INSTANCE_UID);
+        break;
+        
+      case ResourceType_Series:
+        tag = DICOM_TAG_SERIES_INSTANCE_UID;
+        break;
+        
+      case ResourceType_Instance:
+        tag = DICOM_TAG_SOP_INSTANCE_UID;
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
     if (!input.HasTag(tag))
     {
       return false;
@@ -116,7 +141,7 @@
     std::string value = input.GetValue(tag).AsString();
 
     std::list<std::string> ids;
-    context_.GetIndex().LookupIdentifier(ids, tag, value);
+    context_.GetIndex().LookupIdentifier(ids, tag, value, level);
 
     if (ids.size() != 1)
     {
@@ -156,14 +181,9 @@
      * Retrieve the query level.
      **/
 
-    ResourceType level;
     const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL);
 
-    if (levelTmp != NULL) 
-    {
-      level = StringToResourceType(levelTmp->AsString().c_str());
-    }
-    else
+    if (levelTmp == NULL) 
     {
       // The query level is not present in the C-Move request, which
       // does not follow the DICOM standard. This is for instance the
@@ -173,10 +193,10 @@
 
       std::string publicId;
 
-      if (LookupIdentifier(publicId, DICOM_TAG_SOP_INSTANCE_UID, input) ||
-          LookupIdentifier(publicId, DICOM_TAG_SERIES_INSTANCE_UID, input) ||
-          LookupIdentifier(publicId, DICOM_TAG_STUDY_INSTANCE_UID, input) ||
-          LookupIdentifier(publicId, DICOM_TAG_PATIENT_ID, input))
+      if (LookupIdentifier(publicId, ResourceType_Instance, input) ||
+          LookupIdentifier(publicId, ResourceType_Series, input) ||
+          LookupIdentifier(publicId, ResourceType_Study, input) ||
+          LookupIdentifier(publicId, ResourceType_Patient, input))
       {
         return new OrthancMoveRequestIterator(context_, targetAet, publicId);
       }
@@ -187,42 +207,23 @@
       }
     }
 
+    assert(levelTmp != NULL);
+    ResourceType level = StringToResourceType(levelTmp->AsString().c_str());      
 
 
     /**
      * Lookup for the resource to be sent.
      **/
 
-    bool ok;
     std::string publicId;
 
-    switch (level)
+    if (LookupIdentifier(publicId, level, input))
     {
-      case ResourceType_Patient:
-        ok = LookupIdentifier(publicId, DICOM_TAG_PATIENT_ID, input);
-        break;
-
-      case ResourceType_Study:
-        ok = LookupIdentifier(publicId, DICOM_TAG_STUDY_INSTANCE_UID, input);
-        break;
-
-      case ResourceType_Series:
-        ok = LookupIdentifier(publicId, DICOM_TAG_SERIES_INSTANCE_UID, input);
-        break;
-
-      case ResourceType_Instance:
-        ok = LookupIdentifier(publicId, DICOM_TAG_SOP_INSTANCE_UID, input);
-        break;
-
-      default:
-        ok = false;
+      return new OrthancMoveRequestIterator(context_, targetAet, publicId);
     }
-
-    if (!ok)
+    else
     {
       throw OrthancException(ErrorCode_BadRequest);
     }
-
-    return new OrthancMoveRequestIterator(context_, targetAet, publicId);
   }
 }
--- a/OrthancServer/OrthancMoveRequestHandler.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/OrthancMoveRequestHandler.h	Tue Oct 20 11:22:50 2015 +0200
@@ -42,7 +42,7 @@
     ServerContext& context_;
 
     bool LookupIdentifier(std::string& publicId,
-                          DicomTag tag,
+                          ResourceType level,
                           const DicomMap& input);
 
   public:
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -879,20 +879,44 @@
   }
 
 
+  namespace
+  {
+    typedef std::list< std::pair<ResourceType, std::string> >  LookupResults;
+  }
+
+
+  static void AccumulateLookupResults(LookupResults& result,
+                                      ServerIndex& index,
+                                      const DicomTag& tag,
+                                      const std::string& value,
+                                      ResourceType level)
+  {
+    std::list<std::string> tmp;
+    index.LookupIdentifier(tmp, tag, value, level);
+
+    for (std::list<std::string>::const_iterator
+           it = tmp.begin(); it != tmp.end(); ++it)
+    {
+      result.push_back(std::make_pair(level, *it));
+    }
+  }
+
+
   static void Lookup(RestApiPostCall& call)
   {
-    typedef std::list< std::pair<ResourceType, std::string> >  Resources;
-
     std::string tag;
     call.BodyToString(tag);
-    Resources resources;
-
-    OrthancRestApi::GetIndex(call).LookupIdentifier(resources, tag);
 
-    Json::Value result = Json::arrayValue;
-    
-    for (Resources::const_iterator it = resources.begin();
-         it != resources.end(); ++it)
+    LookupResults resources;
+    ServerIndex& index = OrthancRestApi::GetIndex(call);
+    AccumulateLookupResults(resources, index, DICOM_TAG_PATIENT_ID, tag, ResourceType_Patient);
+    AccumulateLookupResults(resources, index, DICOM_TAG_STUDY_INSTANCE_UID, tag, ResourceType_Study);
+    AccumulateLookupResults(resources, index, DICOM_TAG_SERIES_INSTANCE_UID, tag, ResourceType_Series);
+    AccumulateLookupResults(resources, index, DICOM_TAG_SOP_INSTANCE_UID, tag, ResourceType_Instance);
+
+    Json::Value result = Json::arrayValue;    
+    for (LookupResults::const_iterator 
+           it = resources.begin(); it != resources.end(); ++it)
     {     
       ResourceType type = it->first;
       const std::string& id = it->second;
--- a/OrthancServer/ServerEnumerations.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerEnumerations.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -364,5 +364,4 @@
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
   }
-
 }
--- a/OrthancServer/ServerEnumerations.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerEnumerations.h	Tue Oct 20 11:22:50 2015 +0200
@@ -35,6 +35,7 @@
 #include <map>
 
 #include "../Core/Enumerations.h"
+#include "../Core/DicomFormat/DicomTag.h"
 
 namespace Orthanc
 {
--- a/OrthancServer/ServerIndex.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerIndex.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -627,7 +627,7 @@
 
       // Create the instance
       int64_t instance = CreateResource(hasher.HashInstance(), ResourceType_Instance);
-      Toolbox::SetMainDicomTags(db_, instance, ResourceType_Instance, dicomSummary, true);
+      Toolbox::SetMainDicomTags(db_, instance, ResourceType_Instance, dicomSummary);
 
       // Detect up to which level the patient/study/series/instance
       // hierarchy must be created
@@ -679,22 +679,21 @@
       if (isNewSeries)
       {
         series = CreateResource(hasher.HashSeries(), ResourceType_Series);
-        Toolbox::SetMainDicomTags(db_, series, ResourceType_Series, dicomSummary, true);
+        Toolbox::SetMainDicomTags(db_, series, ResourceType_Series, dicomSummary);
       }
 
       // Create the study if needed
       if (isNewStudy)
       {
         study = CreateResource(hasher.HashStudy(), ResourceType_Study);
-        Toolbox::SetMainDicomTags(db_, study, ResourceType_Study, dicomSummary, true);
-        Toolbox::SetMainDicomTags(db_, study, ResourceType_Patient, dicomSummary, false);  // New in version 0.9.5 (db v6)
+        Toolbox::SetMainDicomTags(db_, study, ResourceType_Study, dicomSummary);
       }
 
       // Create the patient if needed
       if (isNewPatient)
       {
         patient = CreateResource(hasher.HashPatient(), ResourceType_Patient);
-        Toolbox::SetMainDicomTags(db_, patient, ResourceType_Patient, dicomSummary, true);
+        Toolbox::SetMainDicomTags(db_, patient, ResourceType_Patient, dicomSummary);
       }
 
       // Create the parent-to-child links
@@ -874,29 +873,6 @@
   }
 
 
-  static std::string GetPatientIdOfStudy(IDatabaseWrapper& db,
-                                         int64_t resourceId)
-  {
-    int64_t patient;
-    if (!db.LookupParent(patient, resourceId))
-    {
-      throw OrthancException(ErrorCode_InternalError);
-    }
-
-    DicomMap tags;
-    db.GetMainDicomTags(tags, patient);
-
-    if (tags.HasTag(DICOM_TAG_PATIENT_ID))
-    {
-      return tags.GetValue(DICOM_TAG_PATIENT_ID).AsString();
-    }
-    else
-    {
-      return "";
-    }
-  }
-
-
   void ServerIndex::MainDicomTagsToJson(Json::Value& target,
                                         int64_t resourceId,
                                         ResourceType resourceType)
@@ -915,8 +891,6 @@
 
       target["PatientMainDicomTags"] = Json::objectValue;
       FromDcmtkBridge::ToJson(target["PatientMainDicomTags"], t2, true);
-
-      target["PatientMainDicomTags"]["PatientID"] = GetPatientIdOfStudy(db_, resourceId);
     }
     else
     {
@@ -1919,6 +1893,12 @@
                                      const std::string& value,
                                      ResourceType type)
   {
+    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);
+    
     result.clear();
 
     boost::mutex::scoped_lock lock(mutex_);
@@ -1937,44 +1917,6 @@
   }
 
 
-  void ServerIndex::LookupIdentifier(std::list<std::string>& result,
-                                     const DicomTag& tag,
-                                     const std::string& value)
-  {
-    result.clear();
-
-    boost::mutex::scoped_lock lock(mutex_);
-
-    std::list<int64_t> id;
-    db_.LookupIdentifier(id, tag, value);
-
-    for (std::list<int64_t>::const_iterator 
-           it = id.begin(); it != id.end(); ++it)
-    {
-      result.push_back(db_.GetPublicId(*it));
-    }
-  }
-
-
-  void ServerIndex::LookupIdentifier(std::list< std::pair<ResourceType, std::string> >& result,
-                                     const std::string& value)
-  {
-    result.clear();
-
-    boost::mutex::scoped_lock lock(mutex_);
-
-    std::list<int64_t> id;
-    db_.LookupIdentifier(id, value);
-
-    for (std::list<int64_t>::const_iterator 
-           it = id.begin(); it != id.end(); ++it)
-    {
-      result.push_back(std::make_pair(db_.GetResourceType(*it),
-                                      db_.GetPublicId(*it)));
-    }
-  }
-
-
   StoreStatus ServerIndex::AddAttachment(const FileInfo& attachment,
                                          const std::string& publicId)
   {
@@ -2137,7 +2079,6 @@
       {
         case ResourceType_Patient:
           tmp.ExtractPatientInformation(result);
-          result.SetValue(DICOM_TAG_PATIENT_ID, GetPatientIdOfStudy(db_, id));
           return true;
 
         case ResourceType_Study:
--- a/OrthancServer/ServerIndex.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerIndex.h	Tue Oct 20 11:22:50 2015 +0200
@@ -240,13 +240,6 @@
                           const std::string& value,
                           ResourceType type);
 
-    void LookupIdentifier(std::list<std::string>& result,
-                          const DicomTag& tag,
-                          const std::string& value);
-
-    void LookupIdentifier(std::list< std::pair<ResourceType, std::string> >& result,
-                          const std::string& value);
-
     StoreStatus AddAttachment(const FileInfo& attachment,
                               const std::string& publicId);
 
--- a/OrthancServer/ServerToolbox.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerToolbox.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -158,11 +158,63 @@
     }
 
 
+    static void SetMainDicomTagsInternal(IDatabaseWrapper& database,
+                                         int64_t resource,
+                                         const DicomMap& tags)
+    {
+      DicomArray flattened(tags);
+
+      for (size_t i = 0; i < flattened.GetSize(); i++)
+      {
+        const DicomElement& element = flattened.GetElement(i);
+        const DicomTag& tag = element.GetTag();
+        database.SetMainDicomTag(resource, tag, element.GetValue().AsString());
+      }
+    }
+
+
+    static void SetIdentifierTagInternal(IDatabaseWrapper& database,
+                                         int64_t resource,
+                                         const DicomMap& tags,
+                                         const DicomTag& tag)
+    {
+      const DicomValue* value = tags.TestAndGetValue(tag);
+      if (value != NULL &&
+          !value->IsNull())
+      {
+        std::string s = value->AsString();
+
+        if (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)
+        {
+          s = NormalizeIdentifierTag(s);
+        }
+
+        database.SetIdentifierTag(resource, tag, s);
+      }
+    }
+
+
+    static void AttachPatientInformation(IDatabaseWrapper& database,
+                                         int64_t resource,
+                                         const DicomMap& dicomSummary)
+    {
+      DicomMap tags;
+      dicomSummary.ExtractPatientInformation(tags);
+      SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_PATIENT_ID);
+      SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_PATIENT_NAME);
+      SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_PATIENT_BIRTH_DATE);
+      SetMainDicomTagsInternal(database, resource, tags);
+    }
+
+
     void SetMainDicomTags(IDatabaseWrapper& database,
                           int64_t resource,
                           ResourceType level,
-                          const DicomMap& dicomSummary,
-                          bool includeIdentifiers)
+                          const DicomMap& dicomSummary)
     {
       // WARNING: The database should be locked with a transaction!
 
@@ -171,36 +223,35 @@
       switch (level)
       {
         case ResourceType_Patient:
-          dicomSummary.ExtractPatientInformation(tags);
+          AttachPatientInformation(database, resource, dicomSummary);
           break;
 
         case ResourceType_Study:
+          // Duplicate the patient tags at the study level (new in Orthanc 0.9.5 - db v6)
+          AttachPatientInformation(database, resource, dicomSummary);
+
           dicomSummary.ExtractStudyInformation(tags);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_STUDY_INSTANCE_UID);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_ACCESSION_NUMBER);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_STUDY_DESCRIPTION);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_STUDY_DATE);
           break;
 
         case ResourceType_Series:
           dicomSummary.ExtractSeriesInformation(tags);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_SERIES_INSTANCE_UID);
           break;
 
         case ResourceType_Instance:
           dicomSummary.ExtractInstanceInformation(tags);
+          SetIdentifierTagInternal(database, resource, tags, DICOM_TAG_SOP_INSTANCE_UID);
           break;
 
         default:
           throw OrthancException(ErrorCode_InternalError);
       }
 
-      DicomArray flattened(tags);
-      for (size_t i = 0; i < flattened.GetSize(); i++)
-      {
-        const DicomElement& element = flattened.GetElement(i);
-
-        if (includeIdentifiers ||
-            !element.GetTag().IsIdentifier())
-        {
-          database.SetMainDicomTag(resource, element.GetTag(), element.GetValue().AsString());
-        }
-      }
+      SetMainDicomTagsInternal(database, resource, tags);
     }
 
 
@@ -299,32 +350,16 @@
         dicom.Convert(dicomSummary);
 
         database.ClearMainDicomTags(resource);
+        Toolbox::SetMainDicomTags(database, resource, level, dicomSummary);
+      }
+    }
 
-        switch (level)
-        {
-          case ResourceType_Patient:
-            Toolbox::SetMainDicomTags(database, resource, ResourceType_Patient, dicomSummary, true);
-            break;
-
-          case ResourceType_Study:
-            Toolbox::SetMainDicomTags(database, resource, ResourceType_Study, dicomSummary, true);
 
-            // Duplicate the patient tags at the study level (new in Orthanc 0.9.5 - db v6)
-            Toolbox::SetMainDicomTags(database, resource, ResourceType_Patient, dicomSummary, false);
-            break;
-
-          case ResourceType_Series:
-            Toolbox::SetMainDicomTags(database, resource, ResourceType_Series, dicomSummary, true);
-            break;
-
-          case ResourceType_Instance:
-            Toolbox::SetMainDicomTags(database, resource, ResourceType_Instance, dicomSummary, true);
-            break;
-
-          default:
-            throw OrthancException(ErrorCode_InternalError);
-        }
-      }
+    std::string NormalizeIdentifierTag(const std::string& value)
+    {
+      std::string s = Toolbox::ConvertToAscii(Toolbox::StripSpaces(value));
+      Toolbox::ToUpperCase(s);
+      return s;
     }
   }
 }
--- a/OrthancServer/ServerToolbox.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/OrthancServer/ServerToolbox.h	Tue Oct 20 11:22:50 2015 +0200
@@ -49,8 +49,7 @@
     void SetMainDicomTags(IDatabaseWrapper& database,
                           int64_t resource,
                           ResourceType level,
-                          const DicomMap& dicomSummary,
-                          bool includeIdentifiers);
+                          const DicomMap& dicomSummary);
 
     bool FindOneChildInstance(int64_t& result,
                               IDatabaseWrapper& database,
@@ -60,5 +59,7 @@
     void ReconstructMainDicomTags(IDatabaseWrapper& database,
                                   IStorageArea& storageArea,
                                   ResourceType level);
+
+    std::string NormalizeIdentifierTag(const std::string& value);
   }
 }
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -611,15 +611,6 @@
   }
 
 
-  void OrthancPluginDatabase::LookupIdentifier(std::list<int64_t>& target,
-                                               const std::string& value)
-  {
-    ResetAnswers();
-    CheckSuccess(backend_.lookupIdentifier2(GetContext(), payload_, value.c_str()));
-    ForwardAnswers(target);
-  }
-
-
   bool OrthancPluginDatabase::LookupMetadata(std::string& target,
                                              int64_t id,
                                              MetadataType type)
@@ -711,18 +702,20 @@
     tmp.element = tag.GetElement();
     tmp.value = value.c_str();
 
-    OrthancPluginErrorCode code;
+    CheckSuccess(backend_.setMainDicomTag(payload_, id, &tmp));
+  }
+
 
-    if (tag.IsIdentifier())
-    {
-      code = backend_.setIdentifierTag(payload_, id, &tmp);
-    }
-    else
-    {
-      code = backend_.setMainDicomTag(payload_, id, &tmp);
-    }
+  void OrthancPluginDatabase::SetIdentifierTag(int64_t id,
+                                               const DicomTag& tag,
+                                               const std::string& value)
+  {
+    OrthancPluginDicomTag tmp;
+    tmp.group = tag.GetGroup();
+    tmp.element = tag.GetElement();
+    tmp.value = value.c_str();
 
-    CheckSuccess(code);
+    CheckSuccess(backend_.setIdentifierTag(payload_, id, &tmp));
   }
 
 
--- a/Plugins/Engine/OrthancPluginDatabase.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.h	Tue Oct 20 11:22:50 2015 +0200
@@ -207,9 +207,6 @@
                                   const DicomTag& tag,
                                   const std::string& value);
 
-    virtual void LookupIdentifier(std::list<int64_t>& target,
-                                  const std::string& value);
-
     virtual bool LookupMetadata(std::string& target,
                                 int64_t id,
                                 MetadataType type);
@@ -235,6 +232,10 @@
                                  const DicomTag& tag,
                                  const std::string& value);
 
+    virtual void SetIdentifierTag(int64_t id,
+                                  const DicomTag& tag,
+                                  const std::string& value);
+
     virtual void SetMetadata(int64_t id,
                              MetadataType type,
                              const std::string& value);
--- a/Plugins/Include/orthanc/OrthancCDatabasePlugin.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Include/orthanc/OrthancCDatabasePlugin.h	Tue Oct 20 11:22:50 2015 +0200
@@ -530,7 +530,8 @@
       void* payload,
       const OrthancPluginDicomTag* tag);
 
-    /* Output: Use OrthancPluginDatabaseAnswerInt64() */
+    /* Unused starting with Orthanc 0.9.5 (db v6), can be set to NULL.
+       Output: Use OrthancPluginDatabaseAnswerInt64() */
     OrthancPluginErrorCode  (*lookupIdentifier2) (
       /* outputs */
       OrthancPluginDatabaseContext* context,
--- a/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Include/orthanc/OrthancCppDatabasePlugin.h	Tue Oct 20 11:22:50 2015 +0200
@@ -414,9 +414,6 @@
                                   uint16_t element,
                                   const char* value) = 0;
 
-    virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
-                                  const char* value) = 0;
-
     virtual bool LookupMetadata(std::string& target /*out*/,
                                 int64_t id,
                                 int32_t metadataType) = 0;
@@ -1331,39 +1328,6 @@
     }
 
 
-    static OrthancPluginErrorCode  LookupIdentifier2(OrthancPluginDatabaseContext* context,
-                                                     void* payload,
-                                                     const char* value)
-    {
-      IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload);
-      backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None);
-
-      try
-      {
-        std::list<int64_t> target;
-        backend->LookupIdentifier(target, value);
-
-        for (std::list<int64_t>::const_iterator
-               it = target.begin(); it != target.end(); ++it)
-        {
-          OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_,
-                                           backend->GetOutput().database_, *it);
-        }
-
-        return OrthancPluginErrorCode_Success;
-      }
-      catch (std::runtime_error& e)
-      {
-        LogError(backend, e);
-        return OrthancPluginErrorCode_DatabasePlugin;
-      }
-      catch (DatabaseException& e)
-      {
-        return e.GetErrorCode();
-      }
-    }
-
-
     static OrthancPluginErrorCode  LookupMetadata(OrthancPluginDatabaseContext* context,
                                                   void* payload,
                                                   int64_t id,
@@ -1861,7 +1825,7 @@
       params.lookupAttachment = LookupAttachment;
       params.lookupGlobalProperty = LookupGlobalProperty;
       params.lookupIdentifier = LookupIdentifier;
-      params.lookupIdentifier2 = LookupIdentifier2;
+      params.lookupIdentifier2 = NULL;   // Unused starting with Orthanc 0.9.5 (db v6)
       params.lookupMetadata = LookupMetadata;
       params.lookupParent = LookupParent;
       params.lookupResource = LookupResource;
--- a/Plugins/Samples/DatabasePlugin/Database.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Samples/DatabasePlugin/Database.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -184,6 +184,8 @@
 {
   db_.Open(path_);
 
+  db_.Execute("PRAGMA ENCODING=\"UTF-8\";");
+
   // http://www.sqlite.org/pragma.html
   db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;");
   db_.Execute("PRAGMA JOURNAL_MODE=WAL;");
--- a/Plugins/Samples/DatabasePlugin/Database.h	Fri Oct 16 17:26:11 2015 +0200
+++ b/Plugins/Samples/DatabasePlugin/Database.h	Tue Oct 20 11:22:50 2015 +0200
@@ -195,12 +195,6 @@
     base_.LookupIdentifier(target, Orthanc::DicomTag(group, element), value);
   }
 
-  virtual void LookupIdentifier(std::list<int64_t>& target /*out*/,
-                                const char* value)
-  {
-    base_.LookupIdentifier(target, value);
-  }
-
   virtual bool LookupMetadata(std::string& target /*out*/,
                               int64_t id,
                               int32_t metadataType)
@@ -246,7 +240,7 @@
                                 uint16_t element,
                                 const char* value)
   {
-    base_.SetMainDicomTag(id, Orthanc::DicomTag(group, element), value);
+    base_.SetIdentifierTag(id, Orthanc::DicomTag(group, element), value);
   }
 
   virtual void SetMetadata(int64_t id,
--- a/UnitTestsSources/ServerIndexTests.cpp	Fri Oct 16 17:26:11 2015 +0200
+++ b/UnitTestsSources/ServerIndexTests.cpp	Tue Oct 20 11:22:50 2015 +0200
@@ -686,10 +686,10 @@
     index_->CreateResource("d", ResourceType_Series)   // 3
   };
 
-  index_->SetMainDicomTag(a[0], DICOM_TAG_STUDY_INSTANCE_UID, "0");
-  index_->SetMainDicomTag(a[1], DICOM_TAG_STUDY_INSTANCE_UID, "1");
-  index_->SetMainDicomTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0");
-  index_->SetMainDicomTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0");
+  index_->SetIdentifierTag(a[0], DICOM_TAG_STUDY_INSTANCE_UID, "0");
+  index_->SetIdentifierTag(a[1], DICOM_TAG_STUDY_INSTANCE_UID, "1");
+  index_->SetIdentifierTag(a[2], DICOM_TAG_STUDY_INSTANCE_UID, "0");
+  index_->SetIdentifierTag(a[3], DICOM_TAG_SERIES_INSTANCE_UID, "0");
 
   std::list<int64_t> s;
 
@@ -698,20 +698,20 @@
   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, "0");
-  ASSERT_EQ(3u, 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");
+  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");
   ASSERT_EQ(1u, s.size());
   ASSERT_TRUE(std::find(s.begin(), s.end(), a[1]) != s.end());
 
-  index_->LookupIdentifier(s, "1");
+  index_->LookupIdentifier(s, 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");
+  ASSERT_EQ(0u, s.size());
 
   /*{
     std::list<std::string> s;