fix ms planning w/ relation constraint propagation
authorsylvain.thenault@logilab.fr
Fri, 20 Mar 2009 12:51:39 +0100
changeset 1120 1c24cde4bf72
parent 1119 3f23846b7946
child 1121 43fa67acd9d0
fix ms planning w/ relation constraint propagation
server/msplanner.py
server/test/unittest_msplanner.py
--- a/server/msplanner.py	Fri Mar 20 09:15:14 2009 +0100
+++ b/server/msplanner.py	Fri Mar 20 12:51:39 2009 +0100
@@ -149,7 +149,15 @@
                 select.append_selected(vref.copy(select))
                 if select.groupby and not vref in select.groupby:
                     select.add_group_var(vref.copy(select))
-            
+
+# XXX move to rql
+def is_ancestor(n1, n2):
+    p = n1.parent
+    while p is not None:
+        if p is n2:
+            return True
+        p = p.parent
+    return False
 
 class PartPlanInformation(object):
     """regroups necessary information to execute some part of a "global" rql
@@ -194,7 +202,7 @@
         self._inputmaps = {}
         if rqlhelper is not None: # else test
             self._insert_identity_variable = rqlhelper._annotator.rewrite_shared_optional
-
+            
     def copy_solutions(self, solindices):
         return [self._solutions[solidx].copy() for solidx in solindices]
     
@@ -431,8 +439,14 @@
                 # can't get information from relation inside a NOT exists
                 # where variables don't belong to the same scope
                 continue
-            if not (var.scope is rel.scope and ovar.scope is rel.scope) and rel.ored():
-                continue
+            need_ancestor_scope = False
+            if not (var.scope is rel.scope and ovar.scope is rel.scope):
+                if rel.ored():
+                    continue
+                if rel.ored(traverse_scope=True):
+                    # if relation has some OR as parent, constraints should only
+                    # propagate from parent scope to child scope, nothing else
+                    need_ancestor_scope = True
             relsources = self._session.repo.rel_type_sources(rel.r_type)
             if rel.neged(strict=True) and (
                 len(relsources) < 2
@@ -448,8 +462,10 @@
                 continue
             norelsup = self._norel_support_set(rel)
             # compute invalid sources for variables and remove them
-            self._remove_var_sources(var, norelsup, ovar, vsources)
-            self._remove_var_sources(ovar, norelsup, var, vsources)
+            if not need_ancestor_scope or is_ancestor(var.scope, ovar.scope):
+                self._remove_var_sources(var, norelsup, ovar, vsources)
+            if not need_ancestor_scope or is_ancestor(ovar.scope, var.scope):
+                self._remove_var_sources(ovar, norelsup, var, vsources)
     
     def _remove_var_sources(self, var, norelsup, ovar, vsources):
         """remove invalid sources for var according to ovar's sources and the
--- a/server/test/unittest_msplanner.py	Fri Mar 20 09:15:14 2009 +0100
+++ b/server/test/unittest_msplanner.py	Fri Mar 20 12:51:39 2009 +0100
@@ -1818,6 +1818,28 @@
                       )],
                    {'x': 999999})
 
+    def test_nonregr13(self):
+        self._test('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File '
+                   'WITH U,UL BEING (Any U,UL WHERE ME eid %(x)s, (EXISTS(U identity ME) '
+                   'OR (EXISTS(U in_group G, G name IN("managers", "staff")))) '
+                   'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is EUser)',
+                   [('FetchStep', [('Any U,UL WHERE U login UL, U is EUser',
+                                    [{'U': 'EUser', 'UL': 'String'}])],
+                     [self.ldap, self.system], None,
+                     {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
+                     []),
+                    ('FetchStep', [('Any U,UL WHERE ((EXISTS(U identity 5)) OR (EXISTS(U in_group G, G name IN("managers", "staff"), G is EGroup))) OR (EXISTS(U in_group H, 5 in_group H, NOT H name "users", H is EGroup)), U login UL, U is EUser',
+                                    [{'G': 'EGroup', 'H': 'EGroup', 'U': 'EUser', 'UL': 'String'}])],
+                     [self.system],
+                     {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
+                     {'U': 'table1.C0', 'U.login': 'table1.C1', 'UL': 'table1.C1'},
+                     []),
+                    ('OneFetchStep', [('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File',
+                                       [{'B': 'File', 'U': 'EUser', 'UL': 'String'}])],
+                     None, None, [self.system],
+                     {'U': 'table1.C0', 'UL': 'table1.C1'},
+                     [])],
+                   {'x': self.session.user.eid})
 
 class MSPlannerTwoSameExternalSourcesTC(BasePlannerTC):
     """test planner related feature on a 3-sources repository: