# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1237549899 -3600 # Node ID 1c24cde4bf728043812ee3bbe8a21eeca3bab54d # Parent 3f23846b7946a2ada5018685183c3db8e88bff73 fix ms planning w/ relation constraint propagation diff -r 3f23846b7946 -r 1c24cde4bf72 server/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 diff -r 3f23846b7946 -r 1c24cde4bf72 server/test/unittest_msplanner.py --- 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: