# HG changeset patch # User Sylvain Thénault # Date 1310462863 -7200 # Node ID 7c0af7ef3325417a493b931de8b38a1f5b7ce018 # Parent 278fe9c1f3adc7cf8d498b806f027a5f175da8de [repo, ms] fix planning of some queries where variable 'non-invarianess' should be considered, leading for instance to junk in entities table on source deletion diff -r 278fe9c1f3ad -r 7c0af7ef3325 server/msplanner.py --- a/server/msplanner.py Tue Jul 12 11:27:42 2011 +0200 +++ b/server/msplanner.py Tue Jul 12 11:27:43 2011 +0200 @@ -370,7 +370,7 @@ eid = const.eval(self.plan.args) source = self._session.source_from_eid(eid) if (source is self.system_source - or (hasrel and + or (hasrel and varobj._q_invariant and not any(source.support_relation(r.r_type) for r in varobj.stinfo['relations'] if not r is rel))): diff -r 278fe9c1f3ad -r 7c0af7ef3325 server/rqlannotation.py --- a/server/rqlannotation.py Tue Jul 12 11:27:42 2011 +0200 +++ b/server/rqlannotation.py Tue Jul 12 11:27:43 2011 +0200 @@ -202,8 +202,8 @@ # since introduced duplicates will be removed if scope.stmt.distinct and diffscope_rels: return iter(_sort(diffscope_rels)).next() - # XXX could use a relation for a different scope if it can't generate - # duplicates, so we would have to check cardinality + # XXX could use a relation from a different scope if it can't generate + # duplicates, so we should have to check cardinality raise CantSelectPrincipal() def _select_main_var(relations): diff -r 278fe9c1f3ad -r 7c0af7ef3325 server/test/unittest_msplanner.py --- a/server/test/unittest_msplanner.py Tue Jul 12 11:27:42 2011 +0200 +++ b/server/test/unittest_msplanner.py Tue Jul 12 11:27:43 2011 +0200 @@ -314,12 +314,14 @@ def test_simplified_var(self): repo._type_source_cache[999999] = ('Note', 'cards', 999999, 'cards') + # need access to source since X table has to be accessed because of the outer join self._test('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR (X require_permission P?, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s', {'x': 999999, 'u': self.session.user.eid}, - {self.system: {'P': s[0], 'G': s[0], 'X': s[0], + {self.system: {'P': s[0], 'G': s[0], 'require_permission': s[0], 'in_group': s[0], 'P': s[0], 'require_group': s[0], - 'u': s[0]}}, - False) + 'u': s[0]}, + self.cards: {'X': s[0]}}, + True) def test_delete_relation1(self): ueid = self.session.user.eid @@ -1312,12 +1314,31 @@ {'x': 999999}) - def test_simplified_var(self): + def test_simplified_var_1(self): ueid = self.session.user.eid repo._type_source_cache[999999] = ('Note', 'cards', 999999, 'cards') - self._test('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR (X require_permission P?, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s', - [('OneFetchStep', [('Any %s WHERE %s in_group G, (G name IN("managers", "logilab")) OR (X require_permission P?, P name "bla", P require_group G), X eid 999999' % (ueid, ueid), - [{'X': 'Note', 'G': 'CWGroup', 'P': 'CWPermission'}])], + # need access to cards source since X table has to be accessed because of the outer join + self._test('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR ' + '(X require_permission P?, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s', + [('FetchStep', + [('Any 999999', [{}])], [self.cards], + None, {u'%(x)s': 'table0.C0'}, []), + ('OneFetchStep', + [(u'Any 6 WHERE 6 in_group G, (G name IN("managers", "logilab")) OR ' + '(X require_permission P?, P name "bla", P require_group G), ' + 'G is CWGroup, P is CWPermission, X is Note', + [{'G': 'CWGroup', 'P': 'CWPermission', 'X': 'Note'}])], + None, None, [self.system], {u'%(x)s': 'table0.C0'}, [])], + {'x': 999999, 'u': ueid}) + + def test_simplified_var_2(self): + ueid = self.session.user.eid + repo._type_source_cache[999999] = ('Note', 'cards', 999999, 'cards') + # no need access to source since X is invariant + self._test('Any U WHERE U in_group G, (G name IN ("managers", "logilab") OR ' + '(X require_permission P, P name "bla", P require_group G)), X eid %(x)s, U eid %(u)s', + [('OneFetchStep', [('Any %s WHERE %s in_group G, (G name IN("managers", "logilab")) OR (999999 require_permission P, P name "bla", P require_group G)' % (ueid, ueid), + [{'G': 'CWGroup', 'P': 'CWPermission'}])], None, None, [self.system], {}, [])], {'x': 999999, 'u': ueid}) @@ -2671,6 +2692,29 @@ ]) ]) + def test_remove_from_deleted_source_1(self): + self.repo._type_source_cache[999999] = ('Note', 'cards', 999999, 'cards') + self._test('Note X WHERE X eid 999999, NOT X cw_source Y', + [('OneFetchStep', + [('Any 999999 WHERE NOT EXISTS(999999 cw_source Y)', + [{'Y': 'CWSource'}])], + None, None, [self.system], {}, []) + ]) + + def test_remove_from_deleted_source_2(self): + self.repo._type_source_cache[999999] = ('Note', 'cards', 999999, 'cards') + self.repo._type_source_cache[999998] = ('Note', 'cards', 999998, 'cards') + self._test('Note X WHERE X eid IN (999998, 999999), NOT X cw_source Y', + [('FetchStep', + [('Any X WHERE X eid IN(999998, 999999), X is Note', + [{'X': 'Note'}])], + [self.cards], None, {'X': 'table0.C0'}, []), + ('OneFetchStep', + [('Any X WHERE NOT EXISTS(X cw_source Y, Y is CWSource), X is Note', + [{'X': 'Note', 'Y': 'CWSource'}])], + None, None, [self.system],{'X': 'table0.C0'}, []) + ]) + class FakeVCSSource(AbstractSource): uri = 'ccc' diff -r 278fe9c1f3ad -r 7c0af7ef3325 server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Tue Jul 12 11:27:42 2011 +0200 +++ b/server/test/unittest_rql2sql.py Tue Jul 12 11:27:43 2011 +0200 @@ -554,6 +554,11 @@ '''SELECT _R2.eid FROM concerne_relation AS rel_concerne0, entities AS _R2 WHERE _R2.eid=rel_concerne0.eid_from AND _R2.eid>rel_concerne0.eid_to'''), + + ('Note X WHERE X eid IN (999998, 999999), NOT X cw_source Y', + '''SELECT _X.cw_eid +FROM cw_Note AS _X +WHERE _X.cw_eid IN(999998, 999999) AND NOT (EXISTS(SELECT 1 FROM cw_source_relation AS rel_cw_source0 WHERE rel_cw_source0.eid_from=_X.cw_eid))'''), ] ADVANCED_WITH_GROUP_CONCAT = [ diff -r 278fe9c1f3ad -r 7c0af7ef3325 server/test/unittest_rqlannotation.py --- a/server/test/unittest_rqlannotation.py Tue Jul 12 11:27:42 2011 +0200 +++ b/server/test/unittest_rqlannotation.py Tue Jul 12 11:27:43 2011 +0200 @@ -340,6 +340,16 @@ self.assertEqual(rqlst.defined_vars['X']._q_invariant, False) self.assertEqual(rqlst.defined_vars['S']._q_invariant, False) + def test_remove_from_deleted_source_1(self): + rqlst = self._prepare('Note X WHERE X eid 999998, NOT X cw_source Y') + self.failIf('X' in rqlst.defined_vars) # simplified + self.assertEqual(rqlst.defined_vars['Y']._q_invariant, True) + + def test_remove_from_deleted_source_2(self): + rqlst = self._prepare('Note X WHERE X eid IN (999998, 999999), NOT X cw_source Y') + self.assertEqual(rqlst.defined_vars['X']._q_invariant, False) + self.assertEqual(rqlst.defined_vars['Y']._q_invariant, True) + if __name__ == '__main__': from logilab.common.testlib import unittest_main unittest_main()