[rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet) stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 16 Jun 2011 15:52:05 +0200
branchstable
changeset 7523 f6856231cc51
parent 7522 6f6b334a14b7
child 7524 c019c3426049
[rql annotation] fix bad invariant variable w/ has_text relation: this is only true when has_text will be used as principal (though we don't know that yet)
server/rqlannotation.py
server/test/unittest_querier.py
server/test/unittest_rqlannotation.py
--- a/server/rqlannotation.py	Thu Jun 16 15:16:58 2011 +0200
+++ b/server/rqlannotation.py	Thu Jun 16 15:52:05 2011 +0200
@@ -271,9 +271,17 @@
         return has_text_query
 
     def is_ambiguous(self, var):
-        # ignore has_text relation
-        if len([rel for rel in var.stinfo['relations']
-                if rel.scope is var.scope and rel.r_type == 'has_text']) == 1:
+        # ignore has_text relation when we know it will be used as principal.
+        # This is expected by the rql2sql generator which will use the `entities`
+        # table to filter out by type if necessary, This optimisation is very
+        # interesting in multi-sources cases, as it may avoid a costly query
+        # on sources to get all entities of a given type to achieve this, while
+        # we have all the necessary information.
+        root = var.stmt.root # Union node
+        # rel.scope -> Select or Exists node, so add .parent to get Union from
+        # Select node
+        rels = [rel for rel in var.stinfo['relations'] if rel.scope.parent is root]
+        if len(rels) == 1 and rels[0].r_type == 'has_text':
             return False
         try:
             data = var.stmt._deamb_data
--- a/server/test/unittest_querier.py	Thu Jun 16 15:16:58 2011 +0200
+++ b/server/test/unittest_querier.py	Thu Jun 16 15:52:05 2011 +0200
@@ -1443,5 +1443,14 @@
         rset = self.execute('Any X,Y WHERE X nom XD, Y nom XD, X eid Z, Y eid > Z')
         self.assertEqual(rset.rows, [[peid1, peid2]])
 
+    def test_nonregr_has_text_ambiguity_1(self):
+        peid = self.execute("INSERT CWUser X: X login 'bidule', X upassword 'bidule', X in_group G WHERE G name 'users'")[0][0]
+        aeid = self.execute("INSERT Affaire X: X ref 'bidule'")[0][0]
+        self.commit()
+        rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule"')
+        self.assertEqual(rset.rows, [[peid]])
+        rset = self.execute('Any X WHERE X is CWUser, X has_text "bidule", X in_state S, S name SN')
+        self.assertEqual(rset.rows, [[peid]])
+
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_rqlannotation.py	Thu Jun 16 15:16:58 2011 +0200
+++ b/server/test/unittest_rqlannotation.py	Thu Jun 16 15:52:05 2011 +0200
@@ -333,6 +333,13 @@
         self.assertEqual(rqlst.defined_vars['N']._q_invariant, False)
         self.assertEqual(rqlst.defined_vars['F']._q_invariant, True)
 
+    def test_nonregr_ambiguity_2(self):
+        rqlst = self._prepare('Any S,SN WHERE X has_text "tot", X in_state S, S name SN, X is CWUser')
+        # X use has_text but should not be invariant as ambiguous, and has_text
+        # may not be its principal
+        self.assertEqual(rqlst.defined_vars['X']._q_invariant, False)
+        self.assertEqual(rqlst.defined_vars['S']._q_invariant, False)
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()