diff OrthancServer/SQLiteDatabaseWrapper.cpp @ 3029:ea653ec47f31 db-changes

new class: DatabaseConstraint
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 18 Dec 2018 18:56:55 +0100
parents fd587cf51a89
children 25afa7b8cb51
line wrap: on
line diff
--- a/OrthancServer/SQLiteDatabaseWrapper.cpp	Tue Dec 18 12:50:27 2018 +0100
+++ b/OrthancServer/SQLiteDatabaseWrapper.cpp	Tue Dec 18 18:56:55 2018 +0100
@@ -1206,9 +1206,211 @@
   }
 
 
+  static void FormatJoin(std::string& target,
+                         const DatabaseConstraint& constraint,
+                         size_t index)
+  {
+    std::string tag = "t" + boost::lexical_cast<std::string>(index);
+
+    if (constraint.IsMandatory())
+    {
+      target = " INNER JOIN ";
+    }
+    else
+    {
+      target = " LEFT JOIN ";
+    }
+
+    if (constraint.IsIdentifier())
+    {
+      target += "DicomIdentifiers ";
+    }
+    else
+    {
+      target += "MainDicomTags ";
+    }
+
+    target += tag + " ON " + tag + ".id = ";
+
+    switch (constraint.GetLevel())
+    {
+      case ResourceType_Patient:
+        target += "patient";
+        break;
+        
+      case ResourceType_Study:
+        target += "study";
+        break;
+        
+      case ResourceType_Series:
+        target += "series";
+        break;
+        
+      case ResourceType_Instance:
+        target += "instance";
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+    
+    target += (".internalId AND " + tag + ".tagGroup = " +
+               boost::lexical_cast<std::string>(constraint.GetTag().GetGroup()) +
+               " AND " + tag + ".tagElement = " +
+               boost::lexical_cast<std::string>(constraint.GetTag().GetElement()));
+  }
+  
+
+  static bool FormatComparison(std::string& target,
+                               const DatabaseConstraint& constraint,
+                               size_t index,
+                               std::vector<std::string>& parameters)
+  {
+    std::string tag = "t" + boost::lexical_cast<std::string>(index);
+
+    std::string comparison;
+    
+    switch (constraint.GetConstraintType())
+    {
+      case ConstraintType_Equal:
+      case ConstraintType_SmallerOrEqual:
+      case ConstraintType_GreaterOrEqual:
+      {
+        std::string op;
+        switch (constraint.GetConstraintType())
+        {
+          case ConstraintType_Equal:
+            op = "=";
+            break;
+          
+          case ConstraintType_SmallerOrEqual:
+            op = "<=";
+            break;
+          
+          case ConstraintType_GreaterOrEqual:
+            op = ">=";
+            break;
+          
+          default:
+            throw OrthancException(ErrorCode_InternalError);
+        }
+          
+        parameters.push_back(constraint.GetSingleValue());
+
+        if (constraint.IsCaseSensitive())
+        {
+          comparison = tag + ".value " + op + " ?";
+        }
+        else
+        {
+          comparison = "lower(" + tag + ".value) " + op + " lower(?)";
+        }
+
+        break;
+      }
+
+      case ConstraintType_List:
+      {
+        for (size_t i = 0; i < constraint.GetValuesCount(); i++)
+        {
+          parameters.push_back(constraint.GetValue(i));
+
+          if (!comparison.empty())
+          {
+            comparison += ", ";
+          }
+            
+          if (constraint.IsCaseSensitive())
+          {
+            comparison += "?";
+          }
+          else
+          {
+            comparison += "lower(?)";
+          }
+        }
+
+        if (constraint.IsCaseSensitive())
+        {
+          comparison = tag + ".value IN (" + comparison + ")";
+        }
+        else
+        {
+          comparison = "lower(" +  tag + ".value) IN (" + comparison + ")";
+        }
+            
+        break;
+      }
+
+      case ConstraintType_Wildcard:
+      {
+        const std::string value = constraint.GetSingleValue();
+        std::string escaped;
+        escaped.reserve(value.size());
+
+        for (size_t i = 0; i < value.size(); i++)
+        {
+          if (value[i] == '*')
+          {
+            escaped += "%";
+          }
+          else if (value[i] == '?')
+          {
+            escaped += "_";
+          }
+          else if (value[i] == '%')
+          {
+            escaped += "\\%";
+          }
+          else if (value[i] == '_')
+          {
+            escaped += "\\_";
+          }
+          else if (value[i] == '\\')
+          {
+            escaped += "\\\\";
+          }
+          else
+          {
+            escaped += value[i];
+          }               
+        }
+
+        parameters.push_back(escaped);
+
+        if (constraint.IsCaseSensitive())
+        {
+          comparison = tag + ".value LIKE ? ESCAPE '\\'";
+        }
+        else
+        {
+          comparison = "lower(" + tag + ".value) LIKE lower(?) ESCAPE '\\'";
+        }
+          
+        break;
+      }
+
+      default:
+        // Don't modify "parameters"!
+        return false;
+    }
+
+    if (constraint.IsMandatory())
+    {
+      target += comparison;
+    }
+    else
+    {
+      target += tag + ".value IS NULL OR " + comparison;
+    }
+
+    return true;
+  }
+  
+  
   void SQLiteDatabaseWrapper::ApplyLookupPatients(std::vector<std::string>& patientsId,
                                                   std::vector<std::string>& instancesId,
-                                                  const DatabaseLookup& lookup,
+                                                  const std::vector<DatabaseConstraint>& lookup,
                                                   size_t limit)
   {
     printf("ICI 1\n");
@@ -1218,132 +1420,37 @@
       s.Run();
     }
     
-    std::string heading = "CREATE TEMPORARY TABLE Lookup AS SELECT patient.publicId, patient.internalId FROM Resources AS patient ";
-    std::string trailer;
+    std::string joins, comparisons;
     std::vector<std::string> parameters;
 
-    for (size_t i = 0; i < lookup.GetConstraintsCount(); i++)
+    size_t count = 0;
+    
+    for (size_t i = 0; i < lookup.size(); i++)
     {
-      const DicomTagConstraint& constraint = lookup.GetConstraint(i);
-      
-      if (constraint.GetTagType() != DicomTagType_Identifier &&
-          constraint.GetTagType() != DicomTagType_Main)
-      {
-        // This should have been set by ServerIndex::NormalizeLookup()"
-        throw OrthancException(ErrorCode_BadSequenceOfCalls);
-      }
+      std::string comparison;
       
-      std::string tag = "t" + boost::lexical_cast<std::string>(i);
-
-      char on[128];
-      sprintf(
-        on, "%s JOIN %s %s ON %s.id = patient.internalId AND %s.tagGroup = 0x%04x AND %s.tagElement = 0x%04x ",
-        constraint.IsMandatory() ? "INNER" : "LEFT",
-        constraint.GetTagType() == DicomTagType_Identifier ? "DicomIdentifiers" : "MainDicomTags",
-        tag.c_str(), tag.c_str(), tag.c_str(), constraint.GetTag().GetGroup(),
-        tag.c_str(), constraint.GetTag().GetElement());
-
-      std::string comparison;
-        
-      switch (constraint.GetConstraintType())
+      if (FormatComparison(comparison, lookup[i], count, parameters))
       {
-        case ConstraintType_Equal:
-        case ConstraintType_SmallerOrEqual:
-        case ConstraintType_GreaterOrEqual:
-        {
-          std::string op;
-          switch (constraint.GetConstraintType())
-          {
-            case ConstraintType_Equal:
-              op = "=";
-              break;
-          
-            case ConstraintType_SmallerOrEqual:
-              op = "<=";
-              break;
-          
-            case ConstraintType_GreaterOrEqual:
-              op = ">=";
-              break;
-          
-            default:
-              throw OrthancException(ErrorCode_InternalError);
-          }
-          
-          parameters.push_back(constraint.GetValue());
-
-          if (constraint.IsCaseSensitive())
-          {
-            comparison = tag + ".value " + op + " ?";
-          }
-          else
-          {
-            comparison = "lower(" + tag + ".value) " + op + " lower(?)";
-          }
+        std::string join;
+        FormatJoin(join, lookup[i], count);
+        joins += join;
 
-          break;
-        }
-
-        case ConstraintType_List:
-          for (std::set<std::string>::const_iterator
-                 it = constraint.GetValues().begin();
-               it != constraint.GetValues().end(); ++it)
-          {
-            parameters.push_back(*it);
-
-            if (!comparison.empty())
-            {
-              comparison += ", ";
-            }
-            
-            if (constraint.IsCaseSensitive())
-            {
-              comparison += "?";
-            }
-            else
-            {
-              comparison += "lower(?)";
-            }
-          }
-
-          if (constraint.IsCaseSensitive())
-          {
-            comparison = tag + ".value IN (" + comparison + ")";
-          }
-          else
-          {
-            comparison = "lower(" +  tag + ".value) IN (" + comparison + ")";
-          }
-            
-          break;
-
-          //case ConstraintType_Wildcard:
-          //break;
-
-        default:
-          continue;
+        comparisons += " AND (" + comparison + ")";
+        
+        count ++;
       }
-
-      heading += std::string(on);
-
-      trailer += "AND (";
-
-      if (!constraint.IsMandatory())
-      {
-        trailer += tag + ".value IS NULL OR ";
-      }
-
-      trailer += comparison + ") ";
-    }
-
-    if (limit != 0)
-    {
-      trailer += " LIMIT " + boost::lexical_cast<std::string>(limit);
     }
 
     {
-      std::string sql = (heading + "WHERE patient.resourceType = " +
-                         boost::lexical_cast<std::string>(ResourceType_Patient) + " " + trailer);
+      std::string sql = ("CREATE TEMPORARY TABLE Lookup AS "
+                         "SELECT patient.publicId, patient.internalId FROM Resources AS patient" +
+                         joins + " WHERE patient.resourceType = " +
+                         boost::lexical_cast<std::string>(ResourceType_Patient) + comparisons);
+
+      if (limit != 0)
+      {
+        sql += " LIMIT " + boost::lexical_cast<std::string>(limit);
+      }
 
       printf("[%s]\n", sql.c_str());
 
@@ -1377,18 +1484,32 @@
         printf("** [%s] [%s]\n", patient.c_str(), instance.c_str());
       }
     }
-    
-    throw OrthancException(ErrorCode_NotImplemented);
   }
   
 
   void SQLiteDatabaseWrapper::ApplyLookupResources(std::vector<std::string>& resourcesId,
                                                    std::vector<std::string>& instancesId,
-                                                   const DatabaseLookup& lookup,
+                                                   const std::vector<DatabaseConstraint>& lookup,
                                                    ResourceType queryLevel,
                                                    size_t limit)
   {
-    printf("ICI 2\n");
+    ResourceType upperLevel = queryLevel;
+    ResourceType lowerLevel = queryLevel;
+
+    for (size_t i = 0; i < lookup.size(); i++)
+    {
+      if (!IsResourceLevelAboveOrEqual(upperLevel, lookup[i].GetLevel()))
+      {
+        upperLevel = lookup[i].GetLevel();
+      }
+
+      if (!IsResourceLevelAboveOrEqual(lookup[i].GetLevel(), lowerLevel))
+      {
+        lowerLevel = lookup[i].GetLevel();
+      }
+    }
+    
+    printf("ICI 2: [%s] -> [%s]\n", EnumerationToString(upperLevel), EnumerationToString(lowerLevel));
     
     throw OrthancException(ErrorCode_NotImplemented);
   }