--- a/ChangeLog Tue Oct 25 11:11:30 2011 +0200
+++ b/ChangeLog Tue Oct 25 17:56:34 2011 +0200
@@ -4,6 +4,7 @@
--
* #78681: don't crash on column aliases used in outer join
* #81394: HAVING support in write queries (INSERT,SET,DELETE)
+ * #80799: fix wrong type analysis with 'NOT identity'
* when possible, use entity type as translation context of relation
(break cw < 3.13.10 compat)
--- a/analyze.py Tue Oct 25 11:11:30 2011 +0200
+++ b/analyze.py Tue Oct 25 17:56:34 2011 +0200
@@ -469,9 +469,11 @@
"""extract constraints for an relation according to it's type"""
if relation.is_types_restriction():
self.visit_type_restriction(relation, constraints)
- return True
+ return None
rtype = relation.r_type
lhs, rhs = relation.get_parts()
+ if rtype == 'identity' and relation.neged(strict=True):
+ return None
if rtype in self.uid_func_mapping:
if isinstance(relation.parent, nodes.Not) or relation.operator() != '=':
# non final entity types
@@ -480,22 +482,22 @@
etypes = self._uid_node_types(rhs)
if etypes:
constraints.var_has_types( lhs.name, etypes )
- return True
+ return None
if isinstance(rhs, nodes.Comparison):
rhs = rhs.children[0]
rschema = self.schema.rschema(rtype)
if isinstance(lhs, nodes.Constant): # lhs is a constant node (simplified tree)
if not isinstance(rhs, nodes.VariableRef):
- return True
+ return None
self._extract_constraint(constraints, rhs.name, lhs, rschema.objects)
elif isinstance(rhs, nodes.Constant) and not rschema.final:
# rhs.type is None <-> NULL
if not isinstance(lhs, nodes.VariableRef) or rhs.type is None:
- return True
+ return None
self._extract_constraint(constraints, lhs.name, rhs, rschema.subjects)
elif not isinstance(lhs, nodes.VariableRef):
# XXX: check relation is valid
- return True
+ return None
elif isinstance(rhs, nodes.VariableRef):
lhsvar = lhs.name
rhsvar = rhs.name
@@ -520,7 +522,7 @@
ptypes = [str(subj) for subj in rschema.subjects()
if subj in lhsdomain]
constraints.var_has_types( lhs.name, ptypes )
- return True
+ return None
def visit_type_restriction(self, relation, constraints):
lhs, rhs = relation.get_parts()
--- a/test/unittest_analyze.py Tue Oct 25 11:11:30 2011 +0200
+++ b/test/unittest_analyze.py Tue Oct 25 17:56:34 2011 +0200
@@ -300,6 +300,7 @@
{'X': 'Person', 'T': 'Eetype'},
{'X': 'Student', 'T': 'Eetype'}])
+
def test_not(self):
node = self.helper.parse('Any X WHERE NOT X is Person')
self.helper.compute_solutions(node, debug=DEBUG)
@@ -308,6 +309,15 @@
expected.remove({'X': 'Person'})
self.assertEqual(sols, expected)
+ def test_not_identity(self):
+ node = self.helper.parse('Any X WHERE X located A, P is Person, NOT X identity P')
+ self.helper.compute_solutions(node, debug=DEBUG)
+ sols = sorted(node.children[0].solutions)
+ self.assertEqual(sols, [{'X': 'Company', 'A': 'Address', 'P': 'Person'},
+ {'X': 'Person', 'A': 'Address', 'P': 'Person'},
+ {'X': 'Student', 'A': 'Address', 'P': 'Person'},
+ ])
+
def test_uid_func_mapping(self):
h = self.helper
def type_from_uid(name):