123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- """
- Classes to represent the default SQL aggregate functions
- """
- class AggregateField(object):
- """An internal field mockup used to identify aggregates in the
- data-conversion parts of the database backend.
- """
- def __init__(self, internal_type):
- self.internal_type = internal_type
- def get_internal_type(self):
- return self.internal_type
- ordinal_aggregate_field = AggregateField('IntegerField')
- computed_aggregate_field = AggregateField('FloatField')
- class Aggregate(object):
- """
- Default SQL Aggregate.
- """
- is_ordinal = False
- is_computed = False
- sql_template = '%(function)s(%(field)s)'
- def __init__(self, col, source=None, is_summary=False, **extra):
- """Instantiate an SQL aggregate
- * col is a column reference describing the subject field
- of the aggregate. It can be an alias, or a tuple describing
- a table and column name.
- * source is the underlying field or aggregate definition for
- the column reference. If the aggregate is not an ordinal or
- computed type, this reference is used to determine the coerced
- output type of the aggregate.
- * extra is a dictionary of additional data to provide for the
- aggregate definition
- Also utilizes the class variables:
- * sql_function, the name of the SQL function that implements the
- aggregate.
- * sql_template, a template string that is used to render the
- aggregate into SQL.
- * is_ordinal, a boolean indicating if the output of this aggregate
- is an integer (e.g., a count)
- * is_computed, a boolean indicating if this output of this aggregate
- is a computed float (e.g., an average), regardless of the input
- type.
- """
- self.col = col
- self.source = source
- self.is_summary = is_summary
- self.extra = extra
- # Follow the chain of aggregate sources back until you find an
- # actual field, or an aggregate that forces a particular output
- # type. This type of this field will be used to coerce values
- # retrieved from the database.
- tmp = self
- while tmp and isinstance(tmp, Aggregate):
- if getattr(tmp, 'is_ordinal', False):
- tmp = ordinal_aggregate_field
- elif getattr(tmp, 'is_computed', False):
- tmp = computed_aggregate_field
- else:
- tmp = tmp.source
- self.field = tmp
- def relabel_aliases(self, change_map):
- if isinstance(self.col, (list, tuple)):
- self.col = (change_map.get(self.col[0], self.col[0]), self.col[1])
- def as_sql(self, quote_func=None):
- "Return the aggregate, rendered as SQL."
- if not quote_func:
- quote_func = lambda x: x
- if hasattr(self.col, 'as_sql'):
- field_name = self.col.as_sql(quote_func)
- elif isinstance(self.col, (list, tuple)):
- field_name = '.'.join([quote_func(c) for c in self.col])
- else:
- field_name = self.col
- params = {
- 'function': self.sql_function,
- 'field': field_name
- }
- params.update(self.extra)
- return self.sql_template % params
- class Avg(Aggregate):
- is_computed = True
- sql_function = 'AVG'
- class Count(Aggregate):
- is_ordinal = True
- sql_function = 'COUNT'
- sql_template = '%(function)s(%(distinct)s%(field)s)'
- def __init__(self, col, distinct=False, **extra):
- super(Count, self).__init__(col, distinct=distinct and 'DISTINCT ' or '', **extra)
- class Max(Aggregate):
- sql_function = 'MAX'
- class Min(Aggregate):
- sql_function = 'MIN'
- class StdDev(Aggregate):
- is_computed = True
- def __init__(self, col, sample=False, **extra):
- super(StdDev, self).__init__(col, **extra)
- self.sql_function = sample and 'STDDEV_SAMP' or 'STDDEV_POP'
- class Sum(Aggregate):
- sql_function = 'SUM'
- class Variance(Aggregate):
- is_computed = True
- def __init__(self, col, sample=False, **extra):
- super(Variance, self).__init__(col, **extra)
- self.sql_function = sample and 'VAR_SAMP' or 'VAR_POP'
|