[rql2sql] Handle comparison of eid on variables from an subquery
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 22 Sep 2016 16:21:51 +0200
changeset 11733 1913a3e8816a
parent 11732 45c38bd3e96d
child 11734 7e2c2354dc99
[rql2sql] Handle comparison of eid on variables from an subquery We don't want to force using 'identity' in this case which may be easily handled by detecting variable is a `ColumnAlias` and relation is 'eid'. Closes #15393583
cubicweb/server/sources/rql2sql.py
cubicweb/server/test/unittest_rql2sql.py
--- a/cubicweb/server/sources/rql2sql.py	Wed Jun 01 17:04:33 2016 +0200
+++ b/cubicweb/server/sources/rql2sql.py	Thu Sep 22 16:21:51 2016 +0200
@@ -1323,24 +1323,34 @@
         """generate SQL for an attribute relation"""
         lhs, rhs = rel.get_parts()
         rhssql = rhs.accept(self)
-        table = self._var_table(lhs.variable)
-        if table is None:
-            assert rel.r_type == 'eid'
+        if isinstance(lhs.variable, ColumnAlias):
+            if rel.r_type != 'eid':
+                raise BadRQLQuery('Attribute %s of %s must be selected from subqueries'
+                                  % (rel.r_type, lhs.variable))
+            # nb: case where subquery variable isn't an eid will raise a TypeResolverException, no
+            # need for defense here
             lhssql = lhs.accept(self)
         else:
-            mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type)
-            if mapkey in self.attr_map:
-                cb, sourcecb = self.attr_map[mapkey]
-                if sourcecb:
-                    # callback is a source callback, we can't use this
-                    # attribute in restriction
-                    raise QueryError("can't use %s (%s) in restriction"
-                                     % (mapkey, rel.as_string()))
-                lhssql = cb(self, lhs.variable, rel)
-            elif rel.r_type == 'eid':
-                lhssql = lhs.variable._q_sql
+            table = self._var_table(lhs.variable)
+            if table is None:
+                # table is None if variable has been annotated as invariant, hence we don't expect
+                # accessing another attribute than eid
+                assert rel.r_type == 'eid'
+                lhssql = lhs.accept(self)
             else:
-                lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type)
+                mapkey = '%s.%s' % (self._state.solution[lhs.name], rel.r_type)
+                if mapkey in self.attr_map:
+                    cb, sourcecb = self.attr_map[mapkey]
+                    if sourcecb:
+                        # callback is a source callback, we can't use this
+                        # attribute in restriction
+                        raise QueryError("can't use %s (%s) in restriction"
+                                         % (mapkey, rel.as_string()))
+                    lhssql = cb(self, lhs.variable, rel)
+                elif rel.r_type == 'eid':
+                    lhssql = lhs.variable._q_sql
+                else:
+                    lhssql = '%s.%s%s' % (table, SQL_PREFIX, rel.r_type)
         try:
             if rel._q_needcast == 'TODAY':
                 sql = 'DATE(%s)%s' % (lhssql, rhssql)
--- a/cubicweb/server/test/unittest_rql2sql.py	Wed Jun 01 17:04:33 2016 +0200
+++ b/cubicweb/server/test/unittest_rql2sql.py	Thu Sep 22 16:21:51 2016 +0200
@@ -1512,17 +1512,39 @@
 FROM cw_Affaire AS _T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_to=_T.cw_eid) LEFT OUTER JOIN cw_Tag AS _TAG ON (rel_tags0.eid_from=_TAG.cw_eid AND _TAG.cw_name=t)
 GROUP BY _T.cw_eid) AS _T0 ON (_T1.C0=_T0.C0)'''),
 
+            ('''Any TT1,STD,STDD WHERE TT2 identity TT1?
+ WITH TT1,STDD BEING (Any T,SUM(TD) GROUPBY T WHERE T is Affaire, T duration TD, TAG? tags T, TAG name "t"),
+      TT2,STD BEING (Any T,SUM(TD) GROUPBY T WHERE T is Affaire, T duration TD)''',
+             '''SELECT _T0.C0, _T1.C1, _T0.C1
+FROM (SELECT _T.cw_eid AS C0, SUM(_T.cw_duration) AS C1
+FROM cw_Affaire AS _T
+GROUP BY _T.cw_eid) AS _T1 LEFT OUTER JOIN (SELECT _T.cw_eid AS C0, SUM(_T.cw_duration) AS C1
+FROM cw_Affaire AS _T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_to=_T.cw_eid) LEFT OUTER JOIN cw_Tag AS _TAG ON (rel_tags0.eid_from=_TAG.cw_eid AND _TAG.cw_name=t)
+GROUP BY _T.cw_eid) AS _T0 ON (_T1.C0=_T0.C0)'''),
+
+            (''' Any X WHERE X eid >= 1200 WITH X BEING ((Any X WHERE X is CWUser) UNION (Any X WHERE X is CWGroup))''',
+             '''SELECT _T0.C0
+FROM ((SELECT _X.cw_eid AS C0
+FROM cw_CWUser AS _X)
+UNION ALL
+(SELECT _X.cw_eid AS C0
+FROM cw_CWGroup AS _X)) AS _T0
+WHERE _T0.C0>=1200'''),
+
         ]:
             with self.subTest(rql=rql):
                 self._check(rql, sql)
 
     def test_subquery_error(self):
-        rql = ('Any N WHERE X name N WITH X BEING '
-               '((Any X WHERE X is State)'
-               ' UNION '
-               ' (Any X WHERE X is Transition))')
-        rqlst = self._prepare(rql)
-        self.assertRaises(BadRQLQuery, self.o.generate, rqlst)
+        for rql in (
+                'Any N WHERE X name N WITH X BEING '
+                '((Any X WHERE X is State) UNION (Any X WHERE X is Transition))',
+
+                'Any X WHERE X modification_date >= TODAY WITH X BEING '
+                '((Any X WHERE X is CWUser) UNION (Any X WHERE X is CWGroup))''',
+        ):
+            rqlst = self._prepare(rql)
+            self.assertRaises(BadRQLQuery, self.o.generate, rqlst)
 
     def test_inline(self):
         for rql, sql in INLINE: