[rqlrewrite] Enhance detection of need for Exists node
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 20 Apr 2017 18:05:06 +0200
changeset 12175 c0ceadfc8aee
parent 12174 02b8325720d6
child 12176 c23aa49b2336
[rqlrewrite] Enhance detection of need for Exists node We actually want one only if this is not a (`And` / `Or`) binary tree of `Not` or `Exists` nodes, so write a recursive function to tell so. Related to #17074119
cubicweb/rqlrewrite.py
cubicweb/test/unittest_rqlrewrite.py
--- a/cubicweb/rqlrewrite.py	Fri Apr 21 09:57:04 2017 +0200
+++ b/cubicweb/rqlrewrite.py	Thu Apr 20 18:05:06 2017 +0200
@@ -206,6 +206,19 @@
     return stinfo['relations'] - stinfo['rhsrelations']
 
 
+def need_exists(node):
+    """Return true if the given node should be wrapped in an `Exists` node.
+
+    This is true when node isn't already an `Exists` or `Not` node, nor a
+    `And`/`Or` of `Exists` or `Not` nodes.
+    """
+    if isinstance(node, (n.Exists, n.Not)):
+        return False
+    if isinstance(node, (n.Or, n.And)):
+        return need_exists(node.children[0]) or need_exists(node.children[1])
+    return True
+
+
 class Unsupported(Exception):
     """raised when an rql expression can't be inserted in some rql query
     because it create an unresolvable query (eg no solutions found)
@@ -474,7 +487,7 @@
             self.existingvars = existing
 
     def _inserted_root(self, new):
-        if not isinstance(new, (n.Exists, n.Not)):
+        if need_exists(new):
             new = n.Exists(new)
         return new
 
--- a/cubicweb/test/unittest_rqlrewrite.py	Fri Apr 21 09:57:04 2017 +0200
+++ b/cubicweb/test/unittest_rqlrewrite.py	Thu Apr 20 18:05:06 2017 +0200
@@ -528,9 +528,9 @@
         self.assertMultiLineEqual(
             rqlst.as_string(),
             u'Any P WHERE NOT P require_state S, '
-            'EXISTS(((NOT EXISTS(A require_permission P, A is IN(Card, Note)))'
+            '((NOT EXISTS(A require_permission P, A is IN(Card, Note)))'
             ' OR (EXISTS(B require_permission P, B is Card, S name "state1")))'
-            ' OR (EXISTS(C require_permission P, C is Note, S name "state2"))), '
+            ' OR (EXISTS(C require_permission P, C is Note, S name "state2")), '
             'P is CWPermission, S is State')
 
     def test_ambiguous_using_is_in_function(self):
@@ -543,8 +543,8 @@
         self.assertMultiLineEqual(
             rqlst.as_string(),
             u'Any P WHERE NOT P require_state S, '
-            'EXISTS((NOT EXISTS(A require_permission P, A is IN(Card, Note))) '
-            'OR (EXISTS(B require_permission P, B is IN(Card, Note), S name "state1"))), '
+            '(NOT EXISTS(A require_permission P, A is IN(Card, Note))) '
+            'OR (EXISTS(B require_permission P, B is IN(Card, Note), S name "state1")), '
             'P is CWPermission, S is State')
 
 from cubicweb.devtools.testlib import CubicWebTC