[schema] edit syntax tree instead of playing with strings for RQLExpressions stable
authorJulien Cristau <julien.cristau@logilab.fr>
Wed, 09 Oct 2013 16:30:27 +0200
branchstable
changeset 9280 bae0caa8477a
parent 9279 0814b9dd9bf3
child 9281 49a7392bb5b5
[schema] edit syntax tree instead of playing with strings for RQLExpressions Lets us support more complex expressions involving e.g. "HAVING" clauses. Closes #3202855
schema.py
test/unittest_schema.py
--- a/schema.py	Thu Oct 10 12:40:23 2013 +0200
+++ b/schema.py	Wed Oct 09 16:30:27 2013 +0200
@@ -25,7 +25,7 @@
 from logging import getLogger
 from warnings import warn
 
-from logilab.common.decorators import cached, clear_cache, monkeypatch
+from logilab.common.decorators import cached, clear_cache, monkeypatch, cachedproperty
 from logilab.common.logging_ext import set_log_methods
 from logilab.common.deprecation import deprecated, class_moved, moved
 from logilab.common.textutils import splitstrip
@@ -668,7 +668,7 @@
     # only defining here to prevent pylint from complaining
     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
     # to be defined in concrete classes
-    full_rql = None
+    rqlst = None
     predefined_variables = None
 
     def __init__(self, expression, mainvars, eid):
@@ -687,7 +687,7 @@
         self.mainvars = mainvars
         self.expression = normalize_expression(expression)
         try:
-            self.rqlst = parse(self.full_rql, print_errors=False).children[0]
+            self.full_rql = self.rqlst.as_string()
         except RQLSyntaxError:
             raise RQLSyntaxError(expression)
         for mainvar in mainvars:
@@ -730,6 +730,15 @@
     def __setstate__(self, state):
         self.__init__(*state)
 
+    @cachedproperty
+    def rqlst(self):
+        select = parse(self.minimal_rql, print_errors=False).children[0]
+        defined = set(split_expression(self.expression))
+        for varname in self.predefined_variables:
+            if varname in defined:
+                select.add_eid_restriction(select.get_variable(varname), varname.lower(), 'Substitute')
+        return select
+
     # permission rql expression specific stuff #################################
 
     @cached
@@ -848,25 +857,11 @@
 # rql expressions for use in permission definition #############################
 
 class ERQLExpression(RQLExpression):
-    predefined_variables = 'UX'
+    predefined_variables = 'XU'
 
     def __init__(self, expression, mainvars=None, eid=None):
         RQLExpression.__init__(self, expression, mainvars or 'X', eid)
 
-    @property
-    def full_rql(self):
-        rql = self.minimal_rql
-        rqlst = getattr(self, 'rqlst', None) # may be not set yet
-        if rqlst is not None:
-            defined = rqlst.defined_vars
-        else:
-            defined = set(split_expression(self.expression))
-        if 'X' in defined:
-            rql += ', X eid %(x)s'
-        if 'U' in defined:
-            rql += ', U eid %(u)s'
-        return rql
-
     def check(self, _cw, eid=None, creating=False, **kwargs):
         if 'X' in self.rqlst.defined_vars:
             if eid is None:
@@ -905,29 +900,13 @@
 
 
 class RRQLExpression(RQLExpression):
-    predefined_variables = 'USO'
+    predefined_variables = 'SOU'
 
     def __init__(self, expression, mainvars=None, eid=None):
         if mainvars is None:
             mainvars = guess_rrqlexpr_mainvars(expression)
         RQLExpression.__init__(self, expression, mainvars, eid)
 
-    @property
-    def full_rql(self):
-        rql = self.minimal_rql
-        rqlst = getattr(self, 'rqlst', None) # may be not set yet
-        if rqlst is not None:
-            defined = rqlst.defined_vars
-        else:
-            defined = set(split_expression(self.expression))
-        if 'S' in defined:
-            rql += ', S eid %(s)s'
-        if 'O' in defined:
-            rql += ', O eid %(o)s'
-        if 'U' in defined:
-            rql += ', U eid %(u)s'
-        return rql
-
     def check(self, _cw, fromeid=None, toeid=None):
         kwargs = {}
         if 'S' in self.rqlst.defined_vars:
--- a/test/unittest_schema.py	Thu Oct 10 12:40:23 2013 +0200
+++ b/test/unittest_schema.py	Wed Oct 09 16:30:27 2013 +0200
@@ -132,6 +132,8 @@
         self.assertRaises(RQLSyntaxError, ERQLExpression, '1')
         expr = ERQLExpression('X travaille S, S owned_by U')
         self.assertEqual(str(expr), 'Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s')
+        expr = ERQLExpression('X foo S, S bar U, X baz XE, S quux SE HAVING XE > SE')
+        self.assertEqual(str(expr), 'Any X WHERE X foo S, S bar U, X baz XE, S quux SE, X eid %(x)s, U eid %(u)s HAVING XE > SE')
 
     def test_rrqlexpression(self):
         self.assertRaises(Exception, RRQLExpression, '1')