[stcheck] fix visit_constant checker: a etype inside a CAST is a good thing

Actually, if you put a CAST after a relation (!= is or is_instance_of), the checker raises an error saying that the etype (inside the CAST) must be after a is or is_instance_of relation. In fact the CAST case was tested too late.

This allow to use CAST outside the selection section, so: Closes #79232

authorAlain Leufroy <alain.leufroy@logilab.fr>
changesetdbb9798ea5ad
branchdefault
phasepublic
hiddenno
parent revision#a9ef868f0444 [pkg] remove lenny and hardy packaging material
child revision#234f05113448 [stcheck] an etype constant should be the first arg to CAST, #659a6b26eedc [pkg] 0.33.0
files modified by this revision
stcheck.py
test/unittest_analyze.py
test/unittest_stcheck.py
# HG changeset patch
# User Alain Leufroy <alain.leufroy@logilab.fr>
# Date 1398347179 -7200
# Thu Apr 24 15:46:19 2014 +0200
# Node ID dbb9798ea5ad2a56d5aca5b77afdef62293d6e8b
# Parent a9ef868f04446ffecaac831d2c5f52a879ab651e
[stcheck] fix visit_constant checker: a etype inside a CAST is a good thing

Actually, if you put a CAST after a relation (!= ``is`` or ``is_instance_of``),
the checker raises an error saying that the etype (inside the CAST) must be
after a ``is`` or ``is_instance_of`` relation. In fact the CAST case was tested too late.

This allow to use CAST outside the selection section, so:
Closes #79232

diff --git a/stcheck.py b/stcheck.py
@@ -455,22 +455,22 @@
1 
2      def leave_variableref(self, node, state):
3          pass
4 
5      def visit_constant(self, constant, state):
6 -        #assert len(constant.children)==0
7 -        if constant.type == 'etype':
8 -            if constant.value not in self.schema:
9 -                state.error('unknown entity type %s' % constant.value)
10 -            rel = constant.relation()
11 -            if rel is not None:
12 -                if rel.r_type not in ('is', 'is_instance_of'):
13 -                    msg ='using an entity type in only allowed with "is" relation'
14 -                    state.error(msg)
15 -            elif not (isinstance(constant.parent, Function) and
16 -                      constant.parent.name == 'CAST'):
17 -                state.error('Entity types can only be used inside a CAST()')
18 +        if constant.type != 'etype':
19 +            return
20 +        if constant.value not in self.schema:
21 +            state.error('unknown entity type %s' % constant.value)
22 +        if (isinstance(constant.parent, Function) and
23 +            constant.parent.name == 'CAST'):
24 +            return
25 +        rel = constant.relation()
26 +        if rel is not None and rel.r_type in ('is', 'is_instance_of'):
27 +            return
28 +        state.error('Entity types can only be used inside a CAST() '
29 +                    'or with "is" relation')
30 
31      def leave_constant(self, node, state):
32          pass
33 
34 
diff --git a/test/unittest_analyze.py b/test/unittest_analyze.py
@@ -546,7 +546,24 @@
35          self.helper.compute_solutions(node, debug=DEBUG)
36          sols = sorted(node.children[0].solutions)
37          self.assertEqual(sols, [{'U': 'Person'}])
38 
39 
40 +    def test_selection_with_cast(self):
41 +        node = self.helper.parse('Any X WHERE X name CAST(String, E), Y eid E, X owned_by Y')
42 +        self.helper.compute_solutions(node, debug=DEBUG)
43 +        sols = sorted(node.children[0].solutions)
44 +        self.assertEqual(sols, [{'E': 'Int', 'X': 'Company', 'Y': 'Person'},
45 +                                {'E': 'Int', 'X': 'Person', 'Y': 'Person'},
46 +                                {'E': 'Int', 'X': 'Student', 'Y': 'Person'}])
47 +
48 +    def test_set_with_cast(self):
49 +        node = self.helper.parse('SET X name CAST(String, E), X work_for Y WHERE Y eid E')
50 +        self.helper.compute_solutions(node, debug=DEBUG)
51 +        sols = sorted(node.solutions)
52 +        self.assertEqual(sols, [{'X': 'Person', 'Y': 'Company', 'E': 'Int'},
53 +                                {'X': 'Student', 'Y': 'Company', 'E': 'Int'}])
54 +
55 +
56 +
57  if __name__ == '__main__':
58      unittest_main()
diff --git a/test/unittest_stcheck.py b/test/unittest_stcheck.py
@@ -78,15 +78,14 @@
59      # DISTINCT+ORDERBY tests ###################################################
60      # sorting allowed since order variable reachable from a selected
61      # variable with only ?1 cardinality
62      'DISTINCT Any P ORDERBY PN WHERE P work_for X, P name PN',
63      'DISTINCT Any P ORDERBY XN WHERE P work_for X, X name XN',
64 -
65      'Any X WHERE X eid > 0, X eid < 42',
66      'Any X WHERE X eid 1, X eid < 42',
67 -
68 -
69 +    'Any X WHERE X number CAST(Int, Y), X name Y',
70 +    'SET X number CAST(Int, Y) WHERE X name Y',
71      )
72 
73  class CheckClassTest(TestCase):
74      """check wrong queries are correctly detected"""
75