[rqlrewrite] closes #1772135: 1. don't try to reuse a relation from another statement (eg because a subquery has been introduced) 2. _use_orig_term should consider the current statement stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 24 Jun 2011 14:02:29 +0200
branchstable
changeset 7555 c3bf459268d7
parent 7551 2d4ba5b984dc
child 7556 867ec36530b8
[rqlrewrite] closes #1772135: 1. don't try to reuse a relation from another statement (eg because a subquery has been introduced) 2. _use_orig_term should consider the current statement
rqlrewrite.py
test/data/rewrite/schema.py
test/unittest_rqlrewrite.py
--- a/rqlrewrite.py	Wed Jun 22 18:24:56 2011 +0200
+++ b/rqlrewrite.py	Fri Jun 24 14:02:29 2011 +0200
@@ -467,6 +467,7 @@
         original query, return that relation node
         """
         rschema = self.schema.rschema(sniprel.r_type)
+        stmt = self.current_statement()
         for vi in self.varinfos:
             try:
                 if target == 'object':
@@ -482,6 +483,9 @@
             except KeyError:
                 # may be raised by vi['xhs_rels'][sniprel.r_type]
                 return None
+            # don't share if relation's statement is not the current statement
+            if orel.stmt is not stmt:
+                return None
             # can't share neged relation or relations with different outer join
             if (orel.neged(strict=True) or sniprel.neged(strict=True)
                 or (orel.optional and orel.optional != sniprel.optional)):
@@ -498,9 +502,10 @@
     def _use_orig_term(self, snippet_varname, term):
         key = (self.current_expr, self.varmap, snippet_varname)
         if key in self.rewritten:
-            insertedvar = self.select.defined_vars.pop(self.rewritten[key])
+            stmt = self.current_statement()
+            insertedvar = stmt.defined_vars.pop(self.rewritten[key])
             for inserted_vref in insertedvar.references():
-                inserted_vref.parent.replace(inserted_vref, term.copy(self.select))
+                inserted_vref.parent.replace(inserted_vref, term.copy(stmt))
         self.rewritten[key] = term.name
 
     def _get_varname_or_term(self, vname):
--- a/test/data/rewrite/schema.py	Wed Jun 22 18:24:56 2011 +0200
+++ b/test/data/rewrite/schema.py	Fri Jun 24 14:02:29 2011 +0200
@@ -27,8 +27,8 @@
         'delete': ('managers', 'owners', ERQLExpression('X concerne S, S owned_by U')),
         }
     ref = String(fulltextindexed=True, indexed=True, maxsize=16)
-    documented_by = SubjectRelation('Card')
-    concerne = SubjectRelation(('Societe', 'Note'))
+    documented_by = SubjectRelation('Card', cardinality='1*')
+    concerne = SubjectRelation(('Societe', 'Note'), cardinality='1*')
 
 
 class Societe(EntityType):
--- a/test/unittest_rqlrewrite.py	Wed Jun 22 18:24:56 2011 +0200
+++ b/test/unittest_rqlrewrite.py	Fri Jun 24 14:02:29 2011 +0200
@@ -33,7 +33,8 @@
     config.bootstrap_cubes()
     schema = config.load_schema()
     from yams.buildobjs import RelationDefinition
-    schema.add_relation_def(RelationDefinition(subject='Card', name='in_state', object='State', cardinality='1*'))
+    schema.add_relation_def(RelationDefinition(subject='Card', name='in_state',
+                                               object='State', cardinality='1*'))
 
     rqlhelper = RQLHelper(schema, special_relations={'eid': 'uid',
                                                      'has_text': 'fti'})
@@ -78,12 +79,21 @@
     return rewriter.rewritten
 
 def test_vrefs(node):
-    vrefmap = {}
+    vrefmaps = {}
+    selects = []
     for vref in node.iget_nodes(nodes.VariableRef):
-        vrefmap.setdefault(vref.name, set()).add(vref)
-    for var in node.defined_vars.itervalues():
-        assert var.stinfo['references']
-        assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var.stinfo['references'], vrefmap[var.name])
+        stmt = vref.stmt
+        try:
+            vrefmaps[stmt].setdefault(vref.name, set()).add(vref)
+        except KeyError:
+            vrefmaps[stmt] = {vref.name: set( (vref,) )}
+            selects.append(stmt)
+    assert node in selects
+    for stmt in selects:
+        for var in stmt.defined_vars.itervalues():
+            assert var.stinfo['references']
+            vrefmap = vrefmaps[stmt]
+            assert not (var.stinfo['references'] ^ vrefmap[var.name]), (node.as_string(), var, var.stinfo['references'], vrefmap[var.name])
 
 
 class RQLRewriteTC(TestCase):
@@ -140,7 +150,7 @@
                              "EXISTS(2 in_state A, B in_group D, E require_state A, "
                              "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
 
-    def test_optional_var_base_1(self):
+    def test_optional_var_1(self):
         constraint = ('X in_state S, U in_group G, P require_state S,'
                            'P name "read", P require_group G')
         rqlst = parse('Any A,C WHERE A documented_by C?')
@@ -151,7 +161,7 @@
                              "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
                              "G require_group F), D eid %(A)s, C is Card)")
 
-    def test_optional_var_base_2(self):
+    def test_optional_var_2(self):
         constraint = ('X in_state S, U in_group G, P require_state S,'
                            'P name "read", P require_group G')
         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
@@ -163,7 +173,7 @@
                              "G require_state B, G name 'read', G require_group F), "
                              "D eid %(A)s, C is Card)")
 
-    def test_optional_var_base_3(self):
+    def test_optional_var_3(self):
         constraint1 = ('X in_state S, U in_group G, P require_state S,'
                        'P name "read", P require_group G')
         constraint2 = 'X in_state S, S name "public"'
@@ -176,6 +186,21 @@
                              "D eid %(A)s, C is Card, "
                              "EXISTS(C in_state E, E name 'public'))")
 
+    def test_optional_var_4(self):
+        constraint1 = 'A created_by U, X documented_by A'
+        constraint2 = 'A created_by U, X concerne A'
+        constraint3 = 'X created_by U'
+        rqlst = parse('Any X,LA,Y WHERE LA? documented_by X, LA concerne Y')
+        rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
+                        ('X', 'X'): (constraint3,),
+                        ('Y', 'X'): (constraint3,)}, {})
+        self.failUnlessEqual(rqlst.as_string(),
+                             u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
+                             'EXISTS(X created_by B), EXISTS(Y created_by B), '
+                             'X is Card, Y is IN(Division, Note, Societe) '
+                             'WITH LA BEING (Any LA WHERE EXISTS(A created_by B, LA documented_by A), '
+                             'B eid %(D)s, LA is Affaire, EXISTS(E created_by B, LA concerne E))')
+
     def test_optional_var_inlined(self):
         c1 = ('X require_permission P')
         c2 = ('X inlined_card O, O require_permission P')