Browse Source

Allow user-specified lists as values for comparisons in parameterized queries

Stefan Schmidt 6 years ago
parent
commit
323053b2eb
2 changed files with 30 additions and 12 deletions
  1. 11 3
      code/Core/QueryParser.py
  2. 19 9
      code/Core/StatsDatabase.py

+ 11 - 3
code/Core/QueryParser.py

@@ -6,7 +6,6 @@ class QueryParser:
         """
         Constructs a parser for all named queries using PyParsing.
         """
-        # TODO: allow lists as input, like: ipaddress(macaddress in [1,2,3])
         extractor = pp.Keyword("random") ^ pp.Keyword("first") ^ pp.Keyword("last")
 
         # Valid selectors - except "avg", because not all attributes can be combined with it
@@ -39,8 +38,17 @@ class QueryParser:
         # Placeholder for nesting in parameterized queries
         expr = pp.Forward()
 
-        # One "attribute-operator-value" triplet. Value can be alphanumeric plus dot and colon, or a nested query
-        comparison = pp.Group(attributes_all + operators + (pp.Word(pp.alphanums + ".:") ^ expr))
+        # Simple values for comparisons inside a parameterized query can be alphanumeric plus dot and colon
+        simple_value = pp.Word(pp.alphanums + ".:")
+
+        # Values in parameterized queries can either be simple values, or a list of them.
+        # If it's a list, we insert a "list"-token to be able to distinguish it
+        parameterized_value = simple_value ^\
+                              (pp.Suppress("[") + pp.Group(pp.Empty().addParseAction(pp.replaceWith('list')) +
+                               pp.delimitedList(simple_value)) + pp.Suppress("]"))
+
+        # One "attribute-operator-value" triplet for parameterized queries
+        comparison = pp.Group(attributes_all + operators + (parameterized_value ^ expr))
 
         # A full parameterized query, consisting of a parameterized selector and a comma-separated list of comparisons
         parameterized_query = param_selectors + pp.Suppress("(") + pp.Group(pp.delimitedList(comparison)) + pp.Suppress(")")

+ 19 - 9
code/Core/StatsDatabase.py

@@ -172,16 +172,26 @@ class StatsDatabase:
         field_types = self.get_field_types('ip_mac', 'ip_ttl', 'ip_ports', 'ip_protocols', 'ip_statistics', 'ip_mac')
         conditions = []
         for key, op, value in param_op_val:
+            # Check whether the value is not a simple value, but another query (or list)
             if isinstance(value, pp.ParseResults):
-                # If we have another query instead of a direct value, execute and replace it
-                rvalue = self._execute_query_list(value)
-
-                # Do we have a comparison operator with a multiple-result query?
-                if op is not "in" and value[0] in ['most_used', 'least_used', 'all']:
-                    raise QueryExecutionException("The extractor '" + value[0] + "' may return more than one result!")
-
-                # Make value contain a simple list with the results of the query
-                value = map(lambda x: str(x[0]), rvalue)
+                if value[0] == "list":
+                    # We have a list, cut the token off and use the remaining elements
+                    value = value[1:]
+
+                    # Lists can only be used with "in"
+                    if op is not "in":
+                        raise QueryExecutionException("List values require the usage of the 'in' operator!")
+                else:
+                    # If we have another query instead of a direct value, execute and replace it
+                    rvalue = self._execute_query_list(value)
+
+                    # Do we have a comparison operator with a multiple-result query?
+                    if op is not "in" and value[0] in ['most_used', 'least_used', 'all']:
+                        raise QueryExecutionException("The extractor '" + value[0] +
+                                                      "' may return more than one result!")
+
+                    # Make value contain a simple list with the results of the query
+                    value = map(lambda x: str(x[0]), rvalue)
             else:
                 # Make sure value is a list now to simplify handling
                 value = [value]