Commits

Olemis Lang committed 7edf12c

GViz QL : Match types for column supplied in to aggregate functions

Comments (0)

Files changed (5)

trac-dev/gviz/TODO

 
 X GViz API QL: Implement `group by` clause.
 
-- GViz API QL: Match types for column supplied in to aggregate functions.
+X GViz API QL: Match types for column supplied in to aggregate functions.
 
 - Add `limit` parameter for history methods in VersionControlRPC.
 

trac-dev/gviz/tracgviz/aggregate.py

 from itertools import count, ifilter, izip
 from math import fsum
 
+from tracgviz.api import GVizRuntimeError
+
 def notimpl(*args, **kwds):
   raise NotImplementedError('Aggregation not supported ... yet')
 
   # Ignore null values
   dataset = _iter_col(rows, getvalue, colnm)
   c = [0]
-  s = fsum(c.__setitem__(0, idx) or value \
-      for idx, value in izip(count(1), dataset))
+  try:
+    s = fsum(c.__setitem__(0, idx) or value \
+        for idx, value in izip(count(1), dataset))
+  except TypeError:
+    raise GVizRuntimeError('Numeric column is required')
   c = c[0]
   return None if c == 0 else s/c
 
 agg_avg.return_type = 'number'
+agg_avg.types = ('number',)
 
 def agg_count(rows, getvalue, colnm):
   idx = 0
 
 def agg_sum(rows, getvalue, colnm):
   dataset = _iter_col(rows, getvalue, colnm)
-  return fsum(dataset)
+  try:
+    return fsum(dataset)
+  except TypeError:
+    raise GVizRuntimeError('Numeric column is required')
 
 agg_sum.return_type = 'number'
+agg_sum.types = ('number',)
 

trac-dev/gviz/tracgviz/api.py

     GVizQL expression.
     """
 
+class GVizUnknownColumn(GVizInvalidQuery):
+    r"""Unknown column name specified in query.
+    """
+
 class GVizIllegalPattern(GVizException):
     r"""Exception raised when an invalid, or incorrect formatting 
     pattern has been specified by the end-user.

trac-dev/gviz/tracgviz/gvizql.py

           raise GVizRuntimeError("Column [%s] should be added to GROUP BY, "
               "removed from SELECT, or aggregated in SELECT." % (colnm,) )
         else:
-          raise GVizRuntimeError("Unknown column " + colnm + ".")
+          raise GVizUnknownColumn("Unknown column " + colnm + ".")
 
     return get_col_value
 
           raise GVizRuntimeError("Column [%s] should be added to GROUP BY, "
               "removed from SELECT, or aggregated in SELECT." % (colnm,) )
         else:
-          raise GVizRuntimeError("Unknown column " + colnm + ".")
+          raise GVizUnknownColumn("Unknown column " + colnm + ".")
 
     get_col_schema.__schema__ = schema
     return get_col_schema
 
   def _formatter(self, colnm, col, pattern):
     if col is None:
-      raise LookupError("Unknown column '%s'" % (colnm,))
+      raise GVizUnknownColumn("Unknown column '%s'" % (colnm,))
     coltype = col[1]
     if coltype == 'boolean':
       pattern = pattern.encode('utf-8').rsplit(':', 1)
         get_group_value = GVizQLClauseHandler._column_accessor(row.__schema__)
         try:
           return f(row.__group__, get_group_value, col_schema[0])
-        except GVizRuntimeError:
+        except GVizUnknownColumn:
           # Target column is not available
-          raise GVizRuntimeError('%s(%s) : No such column in GROUP BY, PIVOT'% \
+          raise GVizUnknownColumn('%s(%s) : No such column in GROUP BY, PIVOT'% \
               (funcnm, col_schema[0]) )
+        except GVizRuntimeError, exc:
+          raise GVizRuntimeError(func_label + ' : ' + str(exc))
 
     if not return_type:
       def agg_schema(get_col_schema):
             
         try:
           return (func_label, get_group_col_schema(col_schema[0])[1])
-        except GVizRuntimeError:
+        except GVizUnknownColumn:
           # Target column is not available
           raise GVizRuntimeError('%s(%s) : No such column in GROUP BY, PIVOT'% \
               (funcnm, col_schema[0]) )
+        except GVizRuntimeError, exc:
+          raise GVizRuntimeError(func_label + ' : ' + str(exc))
 
     return dict(
         eval=eval_aggregate,
 # Imports introducing circular reference with tracgviz.api
 
 from tracgviz.api import GVizUnsupportedQueryOp, GVizInvalidQuery, \
-    GVizRuntimeError
+    GVizRuntimeError, GVizUnknownColumn
 

trac-dev/gviz/tracgviz/testing/test_gvizql.py

       *****
       * Result
       *****
-      GVizRuntimeError  :  Unknown column group.
+      GVizUnknownColumn  :  Unknown column group.
 
       >>> parse("select dept, salary  ", 'cols')
       *****
       *****
       * Result
       *****
-      GVizRuntimeError  :  Unknown column email address.
+      GVizUnknownColumn  :  Unknown column email address.
 
       >>> parse("select lunchTime , name", 'cols')
       *****
       isSenior boolean
       seniorityStartTime datetime
       <BLANKLINE>
-      GVizRuntimeError  :  Unknown column suffix.
+      GVizUnknownColumn  :  Unknown column suffix.
 
 
       >>> parse("where country matches '.*ia'", 'filter')
       isSenior boolean
       seniorityStartTime datetime
       <BLANKLINE>
-      GVizRuntimeError  :  Unknown column country.
+      GVizUnknownColumn  :  Unknown column country.
 
       >>> parse("where name like 'fre%'", 'filter')
       ...
          min(salary) = 400
 
       """,
+  'Parsing GROUP BY (type check for aggregation functions)' : r"""
+      >>> parse("  select isSenior, sum(dept) group by isSenior",
+      ...       'aggregate', 'cols')
+      ... 
+      *****
+      * Tokens
+      *****
+      Token.Keyword.Reserved select
+      Token.Name.Variable isSenior
+      Token.Punctuation ,
+      Token.Name.Builtin sum
+      Token.Punctuation (
+      Token.Name.Variable dept
+      Token.Punctuation )
+      Token.Keyword.Reserved group by
+      Token.Name.Variable isSenior
+      *****
+      * Parsing
+      *****
+      ['isSenior']
+      ['isSenior', 'sum(dept)']
+      *****
+      * Result
+      *****
+      = Columns =
+      isSenior boolean
+      sum(dept) number
+      <BLANKLINE>
+      GVizRuntimeError  :  sum(dept) : Numeric column is required
+
+      >>> parse("  select isSenior, avg(name) group by isSenior",
+      ...       'aggregate', 'cols')
+      ... 
+      *****
+      * Tokens
+      *****
+      Token.Keyword.Reserved select
+      Token.Name.Variable isSenior
+      Token.Punctuation ,
+      Token.Name.Builtin avg
+      Token.Punctuation (
+      Token.Name.Variable name
+      Token.Punctuation )
+      Token.Keyword.Reserved group by
+      Token.Name.Variable isSenior
+      *****
+      * Parsing
+      *****
+      ['isSenior']
+      ['isSenior', 'avg(name)']
+      *****
+      * Result
+      *****
+      = Columns =
+      isSenior boolean
+      avg(name) number
+      <BLANKLINE>
+      GVizRuntimeError  :  avg(name) : Numeric column is required
+
+      """,
   'Parsing GROUP BY (failures)' : r"""
       >>> parse("  select dept , lunchTime   group by dept "
       ...       "   order by dept", 'sort', 'aggregate', 'cols')
       *****
       * Result
       *****
-      LookupError  :  Unknown column 'done'
+      GVizUnknownColumn  :  Unknown column 'done'
 
       >>> parse(r'''select name, done, total - done '''
       ...       '''format `total - done` '#,##0.0' ''', 'fmt')
       *****
       * Result
       *****
-      GVizRuntimeError  :  Unknown column done.
+      GVizUnknownColumn  :  Unknown column done.
 
       """,
   'Parsing FORMAT (simple)' : r"""
       *****
       * Result
       *****
-      GVizRuntimeError  :  Unknown column email address.
+      GVizUnknownColumn  :  Unknown column email address.
 
       """,
     'Parsing (failures)' : r"""
       = Columns =
       empSalary - empTax number
       <BLANKLINE>
-      GVizRuntimeError  :  Unknown column empSalary.
+      GVizUnknownColumn  :  Unknown column empSalary.
 
 
       >>> parse("select 2 * (max(empSalary) / max(empTax))", 'cols')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.