[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
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 12 Jul 2011 11:27:43 +0200
changeset 7651 7c0af7ef3325
parent 7650 278fe9c1f3ad
child 7655 f06bb4a6f8b5
[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
server/msplanner.py
server/rqlannotation.py
server/test/unittest_msplanner.py
server/test/unittest_rql2sql.py
server/test/unittest_rqlannotation.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))):
--- 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):
--- 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'
--- 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 = [
--- 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()