[rset] fix entity building for some result set with UNION and subqueries stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 28 Jan 2011 15:15:14 +0100
branchstable
changeset 6915 99eb71b311e4
parent 6914 5be96d9cbedc
child 6916 3970185a8f8c
[rset] fix entity building for some result set with UNION and subqueries
rset.py
test/unittest_rset.py
--- a/rset.py	Fri Jan 28 15:12:13 2011 +0100
+++ b/rset.py	Fri Jan 28 15:15:14 2011 +0100
@@ -487,24 +487,21 @@
                 select = rqlst
             # take care, due to outer join support, we may find None
             # values for non final relation
-            for i, attr, role in attr_desc_iterator(select, col):
-                outerselidx = rqlst.subquery_selection_index(select, i)
-                if outerselidx is None:
-                    continue
+            for i, attr, role in attr_desc_iterator(select, col, entity.cw_col):
                 if role == 'subject':
                     rschema = eschema.subjrels[attr]
                     if rschema.final:
                         if attr == 'eid':
-                            entity.eid = rowvalues[outerselidx]
+                            entity.eid = rowvalues[i]
                         else:
-                            entity.cw_attr_cache[attr] = rowvalues[outerselidx]
+                            entity.cw_attr_cache[attr] = rowvalues[i]
                         continue
                 else:
                     rschema = eschema.objrels[attr]
                 rdef = eschema.rdef(attr, role)
                 # only keep value if it can't be multivalued
                 if rdef.role_cardinality(role) in '1?':
-                    if rowvalues[outerselidx] is None:
+                    if rowvalues[i] is None:
                         if role == 'subject':
                             rql = 'Any Y WHERE X %s Y, X eid %s'
                         else:
@@ -512,7 +509,7 @@
                         rrset = ResultSet([], rql % (attr, entity.eid))
                         rrset.req = req
                     else:
-                        rrset = self._build_entity(row, outerselidx).as_rset()
+                        rrset = self._build_entity(row, i).as_rset()
                     entity.cw_set_relation_cache(attr, role, rrset)
         return entity
 
@@ -650,8 +647,13 @@
                 return rhs.eval(self.args)
         return None
 
+def _get_variable(term):
+    # XXX rewritten const
+    # use iget_nodes for (hack) case where we have things like MAX(V)
+    for vref in term.iget_nodes(nodes.VariableRef):
+        return vref.variable
 
-def attr_desc_iterator(rqlst, index=0):
+def attr_desc_iterator(select, selectidx, rootidx):
     """return an iterator on a list of 2-uple (index, attr_relation)
     localizing attribute relations of the main variable in a result's row
 
@@ -662,25 +664,33 @@
       a generator on (index, relation, target) describing column being
       attribute of the main variable
     """
-    main = rqlst.selection[index]
-    for i, term in enumerate(rqlst.selection):
-        if i == index:
+    rootselect = select
+    while rootselect.parent.parent is not None:
+        rootselect = rootselect.parent.parent.parent
+    rootmain = rootselect.selection[selectidx]
+    rootmainvar = _get_variable(rootmain)
+    assert rootmainvar
+    root = rootselect.parent
+    selectmain = select.selection[selectidx]
+    for i, term in enumerate(rootselect.selection):
+        rootvar = _get_variable(term)
+        if rootvar is None:
             continue
-        # XXX rewritten const
-        # use iget_nodes for (hack) case where we have things like MAX(V)
-        for vref in term.iget_nodes(nodes.VariableRef):
-            var = vref.variable
-            break
-        else:
+        if rootvar.name == rootmainvar.name:
+            continue
+        if select is not rootselect:
+            term = select.selection[root.subquery_selection_index(select, i)]
+        var = _get_variable(term)
+        if var is None:
             continue
         for ref in var.references():
             rel = ref.relation()
             if rel is None or rel.is_types_restriction():
                 continue
             lhs, rhs = rel.get_variable_parts()
-            if main.is_equivalent(lhs):
+            if selectmain.is_equivalent(lhs):
                 if rhs.is_equivalent(term):
                     yield (i, rel.r_type, 'subject')
-            elif main.is_equivalent(rhs):
+            elif selectmain.is_equivalent(rhs):
                 if lhs.is_equivalent(term):
                     yield (i, rel.r_type, 'object')
--- a/test/unittest_rset.py	Fri Jan 28 15:12:13 2011 +0100
+++ b/test/unittest_rset.py	Fri Jan 28 15:15:14 2011 +0100
@@ -49,7 +49,7 @@
             'Any C where C is Company, C employs P' : [],
             }
         for rql, relations in queries.items():
-            result = list(attr_desc_iterator(parse(rql).children[0]))
+            result = list(attr_desc_iterator(parse(rql).children[0], 0, 0))
             self.assertEqual((rql, result), (rql, relations))
 
     def test_relations_description_indexed(self):
@@ -59,8 +59,8 @@
             {0: [(2,'employs', 'subject')], 1: [(3,'login', 'subject'), (4,'mail', 'subject')]},
             }
         for rql, results in queries.items():
-            for var_index, relations in results.items():
-                result = list(attr_desc_iterator(parse(rql).children[0], var_index))
+            for idx, relations in results.items():
+                result = list(attr_desc_iterator(parse(rql).children[0], idx, idx))
                 self.assertEqual(result, relations)
 
 
@@ -328,7 +328,7 @@
         self.assertEqual(entity, None)
         self.assertEqual(rtype, None)
 
-    def test_related_entity_union_subquery(self):
+    def test_related_entity_union_subquery_1(self):
         e = self.request().create_entity('Bookmark', title=u'aaaa', path=u'path')
         rset = self.execute('Any X,N ORDERBY N WITH X,N BEING '
                             '((Any X,N WHERE X is CWGroup, X name N)'
@@ -337,10 +337,14 @@
         entity, rtype = rset.related_entity(0, 1)
         self.assertEqual(entity.eid, e.eid)
         self.assertEqual(rtype, 'title')
+        self.assertEqual(entity.title, 'aaaa')
         entity, rtype = rset.related_entity(1, 1)
         self.assertEqual(entity.__regid__, 'CWGroup')
         self.assertEqual(rtype, 'name')
-        #
+        self.assertEqual(entity.name, 'guests')
+
+    def test_related_entity_union_subquery_2(self):
+        e = self.request().create_entity('Bookmark', title=u'aaaa', path=u'path')
         rset = self.execute('Any X,N ORDERBY N WHERE X is Bookmark WITH X,N BEING '
                             '((Any X,N WHERE X is CWGroup, X name N)'
                             ' UNION '
@@ -348,7 +352,10 @@
         entity, rtype = rset.related_entity(0, 1)
         self.assertEqual(entity.eid, e.eid)
         self.assertEqual(rtype, 'title')
-        #
+        self.assertEqual(entity.title, 'aaaa')
+
+    def test_related_entity_union_subquery_3(self):
+        e = self.request().create_entity('Bookmark', title=u'aaaa', path=u'path')
         rset = self.execute('Any X,N ORDERBY N WITH N,X BEING '
                             '((Any N,X WHERE X is CWGroup, X name N)'
                             ' UNION '
@@ -356,6 +363,18 @@
         entity, rtype = rset.related_entity(0, 1)
         self.assertEqual(entity.eid, e.eid)
         self.assertEqual(rtype, 'title')
+        self.assertEqual(entity.title, 'aaaa')
+
+    def test_related_entity_union_subquery_4(self):
+        e = self.request().create_entity('Bookmark', title=u'aaaa', path=u'path')
+        rset = self.execute('Any X,X, N ORDERBY N WITH X,N BEING '
+                            '((Any X,N WHERE X is CWGroup, X name N)'
+                            ' UNION '
+                            ' (Any X,N WHERE X is Bookmark, X title N))')
+        entity, rtype = rset.related_entity(0, 2)
+        self.assertEqual(entity.eid, e.eid)
+        self.assertEqual(rtype, 'title')
+        self.assertEqual(entity.title, 'aaaa')
 
     def test_related_entity_trap_subquery(self):
         req = self.request()