# HG changeset patch # User Sylvain Thénault # Date 1474554111 -7200 # Node ID 1913a3e8816a1bab38263d1fb3ac40286d7ff645 # Parent 45c38bd3e96df2ba57fd028f782e83cec0fb8bef [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 diff -r 45c38bd3e96d -r 1913a3e8816a cubicweb/server/sources/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) diff -r 45c38bd3e96d -r 1913a3e8816a cubicweb/server/test/unittest_rql2sql.py --- 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: