fix wrong type analysis with 'NOT identity' (closes #80799)

authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
changeset8c2612f53bf6
branchdefault
phasepublic
hiddenno
parent revision#3daa0b5c31c5 update changelog
child revision#1965eda264bd fix Referencable.get_type crash when no solution given and 'is IN(ET1, ET2..) is used. Closes #81865
files modified by this revision
ChangeLog
analyze.py
test/unittest_analyze.py
# HG changeset patch
# User Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
# Date 1319558194 -7200
# Tue Oct 25 17:56:34 2011 +0200
# Node ID 8c2612f53bf66217c2765aed8cfb549f3d4411e3
# Parent 3daa0b5c31c5321f89c359413f609140d5c61bdc
fix wrong type analysis with 'NOT identity' (closes #80799)

diff --git a/ChangeLog b/ChangeLog
@@ -2,10 +2,11 @@
1  =================
2 
3  --
4      * #78681: don't crash on column aliases used in outer join
5      * #81394: HAVING support in write queries (INSERT,SET,DELETE)
6 +    * #80799: fix wrong type analysis with 'NOT identity'
7      * when possible, use entity type as translation context of relation
8        (break cw < 3.13.10 compat)
9 
10 
11  2011-09-07  --  0.30.1
diff --git a/analyze.py b/analyze.py
@@ -467,37 +467,39 @@
12 
13      def visit_relation(self, relation, constraints):
14          """extract constraints for an relation according to it's  type"""
15          if relation.is_types_restriction():
16              self.visit_type_restriction(relation, constraints)
17 -            return True
18 +            return None
19          rtype = relation.r_type
20          lhs, rhs = relation.get_parts()
21 +        if rtype == 'identity' and relation.neged(strict=True):
22 +            return None
23          if rtype in self.uid_func_mapping:
24              if isinstance(relation.parent, nodes.Not) or relation.operator() != '=':
25                  # non final entity types
26                  etypes = self._nonfinal_domain
27              else:
28                  etypes = self._uid_node_types(rhs)
29              if etypes:
30                  constraints.var_has_types( lhs.name, etypes )
31 -                return True
32 +                return None
33          if isinstance(rhs, nodes.Comparison):
34              rhs = rhs.children[0]
35          rschema = self.schema.rschema(rtype)
36          if isinstance(lhs, nodes.Constant): # lhs is a constant node (simplified tree)
37              if not isinstance(rhs, nodes.VariableRef):
38 -                return True
39 +                return None
40              self._extract_constraint(constraints, rhs.name, lhs, rschema.objects)
41          elif isinstance(rhs, nodes.Constant) and not rschema.final:
42              # rhs.type is None <-> NULL
43              if not isinstance(lhs, nodes.VariableRef) or rhs.type is None:
44 -                return True
45 +                return None
46              self._extract_constraint(constraints, lhs.name, rhs, rschema.subjects)
47          elif not isinstance(lhs, nodes.VariableRef):
48              # XXX: check relation is valid
49 -            return True
50 +            return None
51          elif isinstance(rhs, nodes.VariableRef):
52              lhsvar = lhs.name
53              rhsvar = rhs.name
54              lhsdomain = constraints.domains[lhsvar]
55              # filter according to domain necessary for column aliases
@@ -518,11 +520,11 @@
56              # XXX consider rhs.get_type?
57              lhsdomain = constraints.domains[lhs.name]
58              ptypes = [str(subj) for subj in rschema.subjects()
59                        if subj in lhsdomain]
60              constraints.var_has_types( lhs.name, ptypes )
61 -        return True
62 +        return None
63 
64      def visit_type_restriction(self, relation, constraints):
65          lhs, rhs = relation.get_parts()
66          etypes = set(c.value for c in rhs.iget_nodes(nodes.Constant)
67                       if c.type == 'etype')
diff --git a/test/unittest_analyze.py b/test/unittest_analyze.py
@@ -298,18 +298,28 @@
68                                  {'X': 'Company', 'T': 'Eetype'},
69                                  {'X': 'Eetype', 'T': 'Eetype'},
70                                  {'X': 'Person', 'T': 'Eetype'},
71                                  {'X': 'Student', 'T': 'Eetype'}])
72 
73 +
74      def test_not(self):
75          node = self.helper.parse('Any X WHERE NOT X is Person')
76          self.helper.compute_solutions(node, debug=DEBUG)
77          sols = sorted(node.children[0].solutions)
78          expected = ALL_SOLS[:]
79          expected.remove({'X': 'Person'})
80          self.assertEqual(sols, expected)
81 
82 +    def test_not_identity(self):
83 +        node = self.helper.parse('Any X WHERE X located A, P is Person, NOT X identity P')
84 +        self.helper.compute_solutions(node, debug=DEBUG)
85 +        sols = sorted(node.children[0].solutions)
86 +        self.assertEqual(sols, [{'X': 'Company', 'A': 'Address', 'P': 'Person'},
87 +                                {'X': 'Person', 'A': 'Address', 'P': 'Person'},
88 +                                {'X': 'Student', 'A': 'Address', 'P': 'Person'},
89 +                                ])
90 +
91      def test_uid_func_mapping(self):
92          h = self.helper
93          def type_from_uid(name):
94              self.assertEqual(name, "Logilab")
95              return 'Company'