[rqlrewrite] if inlined relation has to be moved to a subquery, take care of the object of the relation (closes #1945725) stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 23 Sep 2011 12:23:09 +0200
branchstable
changeset 7843 3b51806da60b
parent 7842 fa7e463cae9d
child 7844 0208c65a88a5
[rqlrewrite] if inlined relation has to be moved to a subquery, take care of the object of the relation (closes #1945725) As the object of the relation is moved to the subquery, all variables/relations representing some of its attributes or inlined relations should be moved there as well. This avoid error such as "BadRQLQuery: variable AF should be selected by the subquery" on security insertion.
rqlrewrite.py
test/data/rewrite/schema.py
test/unittest_rqlrewrite.py
--- a/rqlrewrite.py	Fri Sep 23 10:42:16 2011 +0200
+++ b/rqlrewrite.py	Fri Sep 23 12:23:09 2011 +0200
@@ -337,43 +337,58 @@
         """introduce the given snippet in a subquery"""
         subselect = stmts.Select()
         snippetrqlst = n.Exists(transformedsnippet.copy(subselect))
+        get_rschema = self.schema.rschema
         aliases = []
-        rels_done = set()
-        for i, (selectvar, snippetvar) in enumerate(varmap):
+        done = set()
+        for i, (selectvar, _) in enumerate(varmap):
+            need_null_test = False
             subselectvar = subselect.get_variable(selectvar)
             subselect.append_selected(n.VariableRef(subselectvar))
             aliases.append(selectvar)
-            vi = self.varinfos[i]
-            need_null_test = False
-            stinfo = vi['stinfo']
-            for rel in stinfo['relations']:
-                if rel in rels_done:
-                    continue
-                rels_done.add(rel)
-                rschema = self.schema.rschema(rel.r_type)
-                if rschema.final or (rschema.inlined and
-                                     not rel in stinfo['rhsrelations']):
-                    rel.children[0].name = selectvar # XXX explain why
-                    subselect.add_restriction(rel.copy(subselect))
-                    for vref in rel.children[1].iget_nodes(n.VariableRef):
-                        if isinstance(vref.variable, n.ColumnAlias):
-                            # XXX could probably be handled by generating the
-                            # subquery into the detected subquery
-                            raise BadSchemaDefinition(
-                                "cant insert security because of usage two inlined "
-                                "relations in this query. You should probably at "
-                                "least uninline %s" % rel.r_type)
-                        subselect.append_selected(vref.copy(subselect))
-                        aliases.append(vref.name)
-                    self.select.remove_node(rel)
-                    # when some inlined relation has to be copied in the
-                    # subquery, we need to test that either value is NULL or
-                    # that the snippet condition is satisfied
-                    if rschema.inlined and rel.optional:
-                        need_null_test = True
+            todo = [(selectvar, self.varinfos[i]['stinfo'])]
+            while todo:
+                varname, stinfo = todo.pop()
+                done.add(varname)
+                for rel in stinfo['relations'] - stinfo['rhsrelations']:
+                    if rel in done:
+                        continue
+                    done.add(rel)
+                    rschema = get_rschema(rel.r_type)
+                    if rschema.final or rschema.inlined:
+                        rel.children[0].name = varname # XXX explain why
+                        subselect.add_restriction(rel.copy(subselect))
+                        for vref in rel.children[1].iget_nodes(n.VariableRef):
+                            if isinstance(vref.variable, n.ColumnAlias):
+                                # XXX could probably be handled by generating the
+                                # subquery into the detected subquery
+                                raise BadSchemaDefinition(
+                                    "cant insert security because of usage two inlined "
+                                    "relations in this query. You should probably at "
+                                    "least uninline %s" % rel.r_type)
+                            subselect.append_selected(vref.copy(subselect))
+                            aliases.append(vref.name)
+                        self.select.remove_node(rel)
+                        # when some inlined relation has to be copied in the
+                        # subquery and that relation is optional, we need to
+                        # test that either value is NULL or that the snippet
+                        # condition is satisfied
+                        if varname == selectvar and rel.optional and rschema.inlined:
+                            need_null_test = True
+                        # also, if some attributes or inlined relation of the
+                        # object variable are accessed, we need to get all those
+                        # from the subquery as well
+                        if vref.name not in done and rschema.inlined:
+                            # we can use vref here define in above for loop
+                            ostinfo = vref.variable.stinfo
+                            for orel in ostinfo['relations'] - ostinfo['rhsrelations']:
+                                orschema = get_rschema(orel.r_type)
+                                if orschema.final or orschema.inlined:
+                                    todo.append( (vref.name, ostinfo) )
+                                    break
             if need_null_test:
                 snippetrqlst = n.Or(
-                    n.make_relation(subselectvar, 'is', (None, None), n.Constant,
+                    n.make_relation(subselect.get_variable(selectvar), 'is',
+                                    (None, None), n.Constant,
                                     operator='='),
                     snippetrqlst)
         subselect.add_restriction(snippetrqlst)
--- a/test/data/rewrite/schema.py	Fri Sep 23 10:42:16 2011 +0200
+++ b/test/data/rewrite/schema.py	Fri Sep 23 12:23:09 2011 +0200
@@ -63,3 +63,15 @@
     object = 'Card'
     inlined = True
     cardinality = '?*'
+
+class inlined_note(RelationDefinition):
+    subject = 'Card'
+    object = 'Note'
+    inlined = True
+    cardinality = '?*'
+
+class inlined_affaire(RelationDefinition):
+    subject = 'Note'
+    object = 'Affaire'
+    inlined = True
+    cardinality = '?*'
--- a/test/unittest_rqlrewrite.py	Fri Sep 23 10:42:16 2011 +0200
+++ b/test/unittest_rqlrewrite.py	Fri Sep 23 12:23:09 2011 +0200
@@ -236,6 +236,18 @@
                                            ('A2', 'X'): (c2,),
                                            }, {})
 
+    def test_optional_var_inlined_linked(self):
+        c1 = ('X require_permission P')
+        c2 = ('X inlined_card O, O require_permission P')
+        rqlst = parse('Any A,W WHERE A inlined_card C?, C inlined_note N, '
+                      'N inlined_affaire W')
+        rewrite(rqlst, {('C', 'X'): (c1,)}, {})
+        self.failUnlessEqual(rqlst.as_string(),
+                             'Any A,W WHERE A inlined_card C?, A is Affaire '
+                             'WITH C,N,W BEING (Any C,N,W WHERE C inlined_note N, '
+                             'N inlined_affaire W, EXISTS(C require_permission B), '
+                             'C is Card, N is Note, W is Affaire)')
+
     def test_relation_optimization_1_lhs(self):
         # since Card in_state State as monovalued cardinality, the in_state
         # relation used in the rql expression can be ignored and S replaced by
@@ -246,6 +258,7 @@
         self.failUnlessEqual(rqlst.as_string(),
                              "Any C WHERE C in_state STATE, C is Card, "
                              "EXISTS(STATE name 'hop'), STATE is State")
+
     def test_relation_optimization_1_rhs(self):
         snippet = ('TW subworkflow_exit X, TW name "hop"')
         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')