Add "undo" support for HAVING clause

Closes #10058776.

authorDenis Laxalde <denis.laxalde@logilab.fr>
changeseta0bf7565501f
branchdefault
phasepublic
hiddenno
parent revision#efce2b326d64 Added tag 0.34.2, debian/0.34.2-1, centos/0.34.2-1 for changeset 0736f99993c4
child revision#11a23b86752b [tox] Use py3 environment instead of py34
files modified by this revision
rql/stmts.py
rql/undo.py
test/unittest_nodes.py
# HG changeset patch
# User Denis Laxalde <denis.laxalde@logilab.fr>
# Date 1486051851 -3600
# Thu Feb 02 17:10:51 2017 +0100
# Node ID a0bf7565501f822946028d95a85f443b2a5bd385
# Parent efce2b326d64ed6c9e1057fe5b76b690bc721906
Add "undo" support for HAVING clause

Closes #10058776.

diff --git a/rql/stmts.py b/rql/stmts.py
@@ -77,10 +77,14 @@
1      def set_where(self, node):
2          self.where = node
3          node.parent = self
4 
5      def set_having(self, terms):
6 +        if self.should_register_op:
7 +            from rql.undo import SetHavingOperation
8 +            self.undo_manager.add_operation(
9 +                SetHavingOperation(self, self.having))
10          self.having = terms
11          for node in terms:
12              node.parent = self
13 
14      def copy(self, copy_solutions=True, solutions=None):
diff --git a/rql/undo.py b/rql/undo.py
@@ -245,10 +245,26 @@
15 
16      def undo(self, selection):
17          """undo the operation on the selection"""
18          self.rel.optional = self.value
19 
20 +class SetHavingOperation(object):
21 +    """Defines how to undo 'set_having'."""
22 +    def __init__(self, select, previous_value):
23 +        self.select = select
24 +        self.value = previous_value
25 +
26 +    def undo(self, selection):
27 +        """undo the operation on the selection"""
28 +        for term in self.select.having:
29 +            # Unregister any VariableRef in the HAVING clause which would
30 +            # otherwise be attempted to be undefined whereas they are not
31 +            # actually defined.
32 +            for varref in term.iget_nodes(VariableRef):
33 +                varref.unregister_reference()
34 +        self.select.having = self.value
35 +
36  # Union operations ############################################################
37 
38  class AppendSelectOperation(object):
39      """Defines how to undo append_select()."""
40      def __init__(self, union, select):
diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py
@@ -703,10 +703,25 @@
41          tree = sparse(u'Any X WHERE X name "toto tata" HAVING MAX(X) > 1')
42          select = tree.children[0]
43          funcnode = sparse(u'Any X HAVING MAX(X) > 1').children[0].having[0]
44          select.replace(funcnode, nodes.Constant(1.0, 'Float'))
45 
46 +    def test_undo_node_having(self):
47 +        qs = u'Any X WHERE X name N'
48 +        tree = sparse(qs)
49 +        select = tree.children[0]
50 +        select.save_state()
51 +        namevar = select.where.relation().children[-1].children[-1].variable
52 +        comp = nodes.Comparison('>')
53 +        maxf = nodes.Function('MAX')
54 +        maxf.append(nodes.VariableRef(namevar))
55 +        comp.append(maxf)
56 +        comp.append(nodes.Constant(1, 'Int'))
57 +        select.set_having([comp])
58 +        select.recover()
59 +        self.assertEqual(select.as_string(), qs)
60 +
61 
62  class GetNodesFunctionTest(TestCase):
63      def test_known_values_1(self):
64          tree = parse('Any X where X name "turlututu"').children[0]
65          constants = tree.get_nodes(nodes.Constant)