backport stable
authorDavid Douard <david.douard@logilab.fr>
Fri, 06 Jun 2014 11:26:23 +0200
changeset 729 17e72a50aca2
parent 725 0c8840b3be04 (current diff)
parent 728 4e935a3c321d (diff)
child 730 e82edc0de3de
backport stable
nodes.py
--- a/.hgtags	Mon Dec 16 15:31:56 2013 +0100
+++ b/.hgtags	Fri Jun 06 11:26:23 2014 +0200
@@ -81,3 +81,5 @@
 5cad47e1ffbf6c9a234d8a355c3c93655ba32737 rql-centos-version-0.31.4-1
 5cad47e1ffbf6c9a234d8a355c3c93655ba32737 rql-version-0.31.4
 5cad47e1ffbf6c9a234d8a355c3c93655ba32737 rql-debian-version-0.31.4-1
+816abac7f9ad7e0cf4153adc33cb0f53b8ec7f6e rql-version-0.31.5
+2fd17ed747d80fc795e14e651abea64d4c9735ff rql-debian-version-0.31.5-1
--- a/ChangeLog	Mon Dec 16 15:31:56 2013 +0100
+++ b/ChangeLog	Fri Jun 06 11:26:23 2014 +0200
@@ -1,6 +1,13 @@
 ChangeLog for RQL
 =================
 
+2014-03-11  --  0.31.5
+    * #176472: fix implementation of add_type_restriction vs is_instance_of
+
+    * #176469: add_type_restriction is not properly undoable in some cases
+
+
+
 2012-03-29  --  0.31.2
     * #88559: speed up query solutions analysis
 
--- a/__pkginfo__.py	Mon Dec 16 15:31:56 2013 +0100
+++ b/__pkginfo__.py	Fri Jun 06 11:26:23 2014 +0200
@@ -20,7 +20,7 @@
 __docformat__ = "restructuredtext en"
 
 modname = "rql"
-numversion = (0, 31, 4)
+numversion = (0, 31, 5)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL'
--- a/debian/changelog	Mon Dec 16 15:31:56 2013 +0100
+++ b/debian/changelog	Fri Jun 06 11:26:23 2014 +0200
@@ -1,3 +1,9 @@
+rql (0.31.5-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Tue, 11 Mar 2014 14:18:18 +0100
+
 rql (0.31.4-1) unstable; urgency=low
 
   * new upstream release
--- a/nodes.py	Mon Dec 16 15:31:56 2013 +0100
+++ b/nodes.py	Fri Jun 06 11:26:23 2014 +0200
@@ -1,4 +1,4 @@
-# copyright 2004-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2004-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of rql.
@@ -220,8 +220,8 @@
         """builds a restriction node to express : variable is etype"""
         typerel = var.stinfo.get('typerel', None)
         if typerel:
-            istarget = typerel.children[1].children[0]
             if typerel.r_type == 'is':
+                istarget = typerel.children[1].children[0]
                 if isinstance(istarget, Constant):
                     etypes = (istarget.value,)
                 else: # Function (IN)
@@ -232,26 +232,11 @@
                     # iterate a copy of children because it's modified inplace
                     for child in istarget.children[:]:
                         if child.value != etype:
-                            istarget.remove(child)
+                            typerel.stmt.remove_node(child)
+                return typerel
             else:
-                # let's botte en touche IN cases (who would do that anyway ?)
-                if isinstance(istarget, Function):
-                    msg = 'adding type restriction over is_instance_of IN is not supported'
-                    raise NotImplementedError(msg)
-                schema = self.root.schema
-                if schema is None:
-                    msg = 'restriction with is_instance_of cannot be done without a schema'
-                    raise RQLException(msg)
-                # let's check the restriction is compatible
-                eschema = schema[etype]
-                ancestors = set(eschema.ancestors())
-                ancestors.add(etype) # let's be unstrict
-                if istarget.value in ancestors:
-                    istarget.value = etype
-                else:
-                    raise RQLException('type restriction %s-%s cannot be made on %s' %
-                                       (var, etype, self))
-            return typerel
+                assert typerel.r_type == 'is_instance_of'
+                typerel.stmt.remove_node(typerel)
         return self.add_constant_restriction(var, 'is', etype, 'etype')
 
 
@@ -539,7 +524,7 @@
         if root is not None and root.should_register_op and value != self.optional:
             from rql.undo import SetOptionalOperation
             root.undo_manager.add_operation(SetOptionalOperation(self, self.optional))
-        self.optional= value
+        self.set_optional(value)
 
 
 CMP_OPERATORS = frozenset(('=', '!=', '<', '<=', '>=', '>', 'ILIKE', 'LIKE', 'REGEXP'))
--- a/test/unittest_nodes.py	Mon Dec 16 15:31:56 2013 +0100
+++ b/test/unittest_nodes.py	Fri Jun 06 11:26:23 2014 +0200
@@ -1,5 +1,5 @@
 # -*- coding: iso-8859-1 -*-
-# copyright 2004-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2004-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of rql.
@@ -62,35 +62,48 @@
         self.simplify = helper.simplify
 
     def test_add_is_type_restriction(self):
-        tree = self.parse('Any X WHERE X is Person')
+        tree = self.parse("Any X WHERE X is Person, X name ILIKE 'A%'")
         select = tree.children[0]
         x = select.get_selected_variables().next()
         self.assertRaises(RQLException, select.add_type_restriction, x.variable, 'Babar')
         select.add_type_restriction(x.variable, 'Person')
-        self.assertEqual(tree.as_string(), 'Any X WHERE X is Person')
+        self.assertEqual(tree.as_string(), "Any X WHERE X is Person, X name ILIKE 'A%'")
 
     def test_add_new_is_type_restriction_in(self):
+        tree = self.parse("Any X WHERE X is IN(Person, Company), X name ILIKE 'A%'")
+        select = tree.children[0]
+        x = select.get_selected_variables().next()
+        select.add_type_restriction(x.variable, 'Company')
+        # implementation is KISS (the IN remains)
+        self.assertEqual(tree.as_string(), "Any X WHERE X is IN(Company), X name ILIKE 'A%'")
+
+    def test_add_new_is_type_restriction_in_nonregr(self):
         tree = self.parse('Any X WHERE X is IN(Person, Company, Student)')
         select = tree.children[0]
         x = select.get_selected_variables().next()
         select.add_type_restriction(x.variable, 'Person')
-        # implementation is KISS (the IN remains)
         self.assertEqual(tree.as_string(), 'Any X WHERE X is IN(Person)')
 
     def test_add_is_in_type_restriction(self):
-        tree = self.parse('Any X WHERE X is IN(Person, Company)')
+        tree = self.parse("Any X WHERE X is IN(Person, Company), X name ILIKE 'A%'")
         select = tree.children[0]
         x = select.get_selected_variables().next()
         self.assertRaises(RQLException, select.add_type_restriction, x.variable, 'Babar')
-        self.assertEqual(tree.as_string(), 'Any X WHERE X is IN(Person, Company)')
+        self.assertEqual(tree.as_string(), "Any X WHERE X is IN(Person, Company), X name ILIKE 'A%'")
+
+    def test_add_is_type_restriction_on_is_instance_of(self):
+        select = self.parse("Any X WHERE X is_instance_of Person, X name ILIKE 'A%'").children[0]
+        x = select.get_selected_variables().next()
+        select.add_type_restriction(x.variable, 'Person')
+        self.assertEqual(select.as_string(), "Any X WHERE X name ILIKE 'A%', X is Person")
 
-    # XXX a full schema is needed, see test in cw/server/test/unittest_security
-    # def test_add_is_against_isintance_type_restriction(self):
-    #     tree = self.parse('Any X WHERE X is_instance_of Person')
-    #     select = tree.children[0]
-    #     x = select.get_selected_variables().next()
-    #     select.add_type_restriction(x.variable, 'Student')
-    #     self.parse(tree.as_string())
+    def test_add_new_is_type_restriction_in_on_is_instance_of(self):
+        tree = self.parse("Any X WHERE X is_instance_of IN(Person, Company), X name ILIKE 'A%'")
+        select = tree.children[0]
+        x = select.get_selected_variables().next()
+        select.add_type_restriction(x.variable, 'Company')
+        self.assertEqual(tree.as_string(), "Any X WHERE X name ILIKE 'A%', X is Company")
+
 
 class NodesTest(TestCase):
     def _parse(self, rql, normrql=None):
@@ -291,6 +304,30 @@
         tree.check_references()
         self.assertEqual(tree.as_string(), 'Any X,Y GROUPBY X,Y')
 
+    def test_recover_add_type_restriction_is_in(self):
+        tree = self._parse("Any X WHERE X is IN(Person, Company), X name ILIKE 'A%'")
+        annotator.annotate(tree) # needed to get typerel index
+        tree.save_state()
+        select = tree.children[0]
+        x = select.get_selected_variables().next()
+        select.add_type_restriction(x.variable, 'Company')
+        self.assertEqual(tree.as_string(), "Any X WHERE X is IN(Company), X name ILIKE 'A%'")
+        tree.recover()
+        tree.check_references()
+        self.assertEqual(tree.as_string(), "Any X WHERE X is IN(Person, Company), X name ILIKE 'A%'")
+
+    def test_recover_add_type_restriction_is_instance_of(self):
+        tree = self._parse("Any X WHERE X is_instance_of IN(Person, Company), X name ILIKE 'A%'")
+        annotator.annotate(tree) # needed to get typerel index
+        tree.save_state()
+        select = tree.children[0]
+        x = select.get_selected_variables().next()
+        select.add_type_restriction(x.variable, 'Company')
+        self.assertEqual(tree.as_string(), "Any X WHERE X name ILIKE 'A%', X is Company")
+        tree.recover()
+        tree.check_references()
+        self.assertEqual(tree.as_string(), "Any X WHERE X is_instance_of IN(Person, Company), X name ILIKE 'A%'")
+
     def test_select_base_1(self):
         tree = self._parse("Any X WHERE X is Person")
         self.assertIsInstance(tree, stmts.Union)