Add a "should_register_op" class attribute to ScopeNode
authorDenis Laxalde <denis.laxalde@logilab.fr>
Wed, 13 Feb 2019 14:04:48 +0100
changeset 822 94819f3c3d64
parent 821 919a00aea53d
child 823 01b2152b778a
Add a "should_register_op" class attribute to ScopeNode This a follow-up on changeset a0bf7565501f which introduce usage of this attribute in set_having() method. Sometimes, derived would classes implement this method (e.g. through EditableMixIn or directly, like Union) but some statement would not (e.g. Delete). So this breaks any mutable query using an HAVING clause. For instance, in cubicweb: self = <unittest_querier.QuerierTC testMethod=test_update_having> def test_update_having(self): peid1 = self.qexecute("INSERT Personne Y: Y nom 'hop', Y tel 1")[0][0] peid2 = self.qexecute("INSERT Personne Y: Y nom 'hop', Y tel 2")[0][0] > rset = self.qexecute("SET X tel 3 WHERE X tel TEL HAVING TEL&1=1") cubicweb/server/test/unittest_querier.py:1354: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ cubicweb/devtools/repotest.py:193: in qexecute return self.o.execute(cnx, rql, args, build_descr) cubicweb/server/sqlutils.py:488: in new_execute rset = base_execute(*args, **kwargs) cubicweb/statsd_logger.py:135: in __call__ return self.callable(*args, **kw) cubicweb/server/querier.py:532: in execute rqlst, cachekey = self.rql_cache.get(cnx, rql, args) cubicweb/server/querier.py:659: in get rqlst = self._parse(rql) cubicweb/server/querier.py:629: in parse return parse(text_type(rql), annotate=annotate) .tox/py27-server/local/lib/python2.7/site-packages/rql/__init__.py:91: in parse rqlst = parse(rqlstring, False) .tox/py27-server/local/lib/python2.7/site-packages/rql/__init__.py:213: in parse return parser.goal() .tox/py27-server/local/lib/python2.7/site-packages/rql/parser.py:123: in goal update = self.update(Set(), _context) .tox/py27-server/local/lib/python2.7/site-packages/rql/parser.py:167: in update having = self.having(R, _context) .tox/py27-server/local/lib/python2.7/site-packages/rql/parser.py:243: in having S.set_having([logical_expr]) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = SET Relation(VarRef(X) tel = 3) WHERE Relation(VarRef(X) tel = VarRef(TEL)) terms = [= (VarRef(TEL) & 1), 1] def set_having(self, terms): > if self.should_register_op: E AttributeError: 'Set' object has no attribute 'should_register_op' .tox/py27-server/local/lib/python2.7/site-packages/rql/stmts.py:82: AttributeError So, we simply add a class attribute with a None value so that the interface of this ScopeNode class is at least self-consistent. Adding a non-regression test with a DELETE and a SET queries. (Note that most existing non-regression tests do not contain assertions, they just call functions in the hope they do not crash; the new test just follows this pattern as I don't know what to check.)
rql/stmts.py
test/unittest_nodes.py
--- a/rql/stmts.py	Thu Dec 13 10:02:45 2018 +0100
+++ b/rql/stmts.py	Wed Feb 13 14:04:48 2019 +0100
@@ -66,6 +66,7 @@
     _varmaker = None # variable names generator, built when necessary
     where = None     # where clause node
     having = ()      # XXX now a single node
+    should_register_op = None
 
     def __init__(self):
         # dictionnary of defined variables in the original RQL syntax tree
--- a/test/unittest_nodes.py	Thu Dec 13 10:02:45 2018 +0100
+++ b/test/unittest_nodes.py	Wed Feb 13 14:04:48 2019 +0100
@@ -720,6 +720,10 @@
         select.recover()
         self.assertEqual(select.as_string(), qs)
 
+    def test_undo_node_having_in_mutable(self):
+        sparse('DELETE Person X WHERE X name N HAVING MAX(X) > 1')
+        sparse('SET X name "foo" WHERE X is Person HAVING MAX(X) > 1')
+
 
 class GetNodesFunctionTest(TestCase):
     def test_known_values_1(self):