class SelectWithDistinct(sql.Select):
"""Select definition that allows for DISTINCT queries.
DISTINCT is - unfortunately - not yet available in the python-sql module,
so we implement this ourselves in this class, which can be monkey-patched
into the python-sql library. Not ideal, but it is robust enough.
The entry point for the monkey-patching would be in sql.From.select(..)
This method needs to be replaced with one that uses this Select
implementation instead of the default one.
"""
__slots__ = ('_columns', 'distincts', '_where', '_group_by', '_having',
'_for_', 'from_')
def __init__(self, columns, distincts=None, from_=None, where=None, group_by=None,
having=None, for_=None, **kwargs):
self._columns = None
self._where = None
self._group_by = None
self._having = None
self._for_ = None
super(SelectWithDistinct, self).__init__(columns, **kwargs)
self.distincts = distincts
self.columns = columns
self.from_ = from_
self.where = where
self.group_by = group_by
self.having = having
self.for_ = for_
def __str__(self):
if (
Flavor.get().limitstyle == 'rownum'
and (self.limit is not None or self.offset is not None)
):
return self._rownum(str)
with AliasManager():
from_ = str(self.from_)
if self.columns:
columns = ', '.join(map(self._format_column, self.columns))
else:
columns = '*'
if self.distincts is not None:
if len(self.distincts) == 0:
distinct = 'DISTINCT '
else:
distinct = 'DISTINCT ON ({}) '.format(
', '.join(map(self._format_column, self.distincts))
)
columns = distinct + columns
where = ''
if self.where:
where = ' WHERE ' + str(self.where)
group_by = ''
if self.group_by:
group_by = ' GROUP BY ' + ', '.join(map(str, self.group_by))
having = ''
if self.having:
having = ' HAVING ' + str(self.having)
window = ''
windows = [f.window for f in self._window_functions()]
if windows:
window = ' WINDOW ' + ', '.join(
'"%s" AS (%s)' % (w.alias, w) for w in windows)
for_ = ''
if self.for_ is not None:
for_ = ' ' + ' '.join(map(str, self.for_))
return (self._with_str()
+ 'SELECT %s FROM %s' % (columns, from_)
+ where + group_by + having + window + self._order_by_str
+ self._limit_offset_str + for_)
# Monkey-patch the python-sql library with an implementation of Select
# that supports DISTINCT
def monkey_select(self, *args, **kwargs):
return SelectWithDistinct(args, from_=self, **kwargs)
sql.From.select = monkey_select