[rql2sql] fix handling of the ORDERBY clause when the aggregation function is "embedded"
ie. a situation like:
Any X GROUPBY X ORDERBY COUNT(Y) WHERE ...
works fine since the aggregation function (COUNT) if the first one, whereas a situation like the proposed test:
Any X GROUPBY X ORDERBY ABS(COUNT(Y)) WHERE ...
failed due to the fact we only checked the top aggratation function (ABS).
Closes #17108671
--- a/cubicweb/server/sources/rql2sql.py Mon Apr 23 15:19:19 2018 +0200
+++ b/cubicweb/server/sources/rql2sql.py Thu Oct 12 15:22:00 2017 +0200
@@ -279,6 +279,14 @@
groups.append(vref)
+def is_in_aggregat(node):
+ while node:
+ node = node.parent
+ if isinstance(node, Function) and node.descr().aggregat:
+ return True
+ return False
+
+
def fix_selection_and_group(rqlst, needwrap, selectsortterms,
sorts, groups, having):
if selectsortterms and sorts:
@@ -288,13 +296,11 @@
# when a query is grouped, ensure sort terms are grouped as well
for sortterm in sorts:
term = sortterm.term
- if not (isinstance(term, Constant) or
- (isinstance(term, Function) and
- get_func_descr(term.name).aggregat)):
- for vref in term.iget_nodes(VariableRef):
- if not any(vref.is_equivalent(group) for group in groupvrefs):
- groups.append(vref)
- groupvrefs.append(vref)
+ for vref in term.iget_nodes(VariableRef):
+ if not (any(vref.is_equivalent(group) for group in groupvrefs)
+ or is_in_aggregat(vref)):
+ groups.append(vref)
+ groupvrefs.append(vref)
if needwrap and (groups or having):
selectedidx = set(vref.name for term in rqlst.selection
for vref in term.get_nodes(VariableRef))
--- a/cubicweb/server/test/unittest_rql2sql.py Mon Apr 23 15:19:19 2018 +0200
+++ b/cubicweb/server/test/unittest_rql2sql.py Thu Oct 12 15:22:00 2017 +0200
@@ -1437,6 +1437,16 @@
WHERE _X.cw_login ~ [0-9].*
''')
+ def test_aggregate_in_orderby(self):
+ self._check("Any X GROUPBY X ORDERBY ABS(COUNT(Y)) WHERE"
+ "X is CWGROUP, Y is CWUSER, Y in_group X",
+ '''SELECT rel_in_group1.eid_to
+FROM in_group_relation AS rel_in_group1, is_relation AS rel_is0, is_relation AS rel_is2
+WHERE rel_in_group1.eid_to=rel_is0.eid_from AND rel_in_group1.eid_from=rel_is2.eid_from
+GROUP BY rel_in_group1.eid_to
+ORDER BY ABS(COUNT(rel_in_group1.eid_from))
+''')
+
def test_parser_parse(self):
for rql, sql in PARSER:
with self.subTest(rql=rql):