[rqlhelper] cache the syntax tree (rqlst) built when parsing a rql string

There are already rql cache mechanisms in Wubicweb, but the pure RQL->RQLST parsing cannot be cached from Cubicweb (since this later annotates rqlst, Cubicweb only caches annotated syntax trees).

This requires to fix the Select.copy() method to honor the aggregated attribute, so we can store the syntax tree that have been checked and annotated by the RQLSTAnnotator.

This cache can save up to a few thousands of ms on rendering complex pages.

authorDavid Douard <david.douard@logilab.fr>
changeset034d5f28325b
branchdefault
phasedraft
hiddenno
parent revision#e3a69da9c6d0 Added tag rql-version-0.33.0, rql-debian-version-0.33.0-1, rql-centos-version-0.33.0-1 for changeset 659a6b26eedc
child revision<not specified>
files modified by this revision
__init__.py
stmts.py
# HG changeset patch
# User David Douard <david.douard@logilab.fr>
# Date 1429794053 -7200
# Thu Apr 23 15:00:53 2015 +0200
# Node ID 034d5f28325bbf8b3e2cc599a8268fedd4ec7ea3
# Parent e3a69da9c6d035d6a58b5ae34178e20d0071386c
[rqlhelper] cache the syntax tree (rqlst) built when parsing a rql string

There are already rql cache mechanisms in Wubicweb, but the pure
RQL->RQLST parsing cannot be cached from Cubicweb (since this later
annotates rqlst, Cubicweb only caches annotated syntax trees).

This requires to fix the Select.copy() method to honor the aggregated
attribute, so we can store the syntax tree that have been checked and
annotated by the RQLSTAnnotator.

This cache can save up to a few thousands of ms on rendering complex
pages.

diff --git a/__init__.py b/__init__.py
@@ -73,21 +73,25 @@
1              if is_keyword(rtype):
2                  raise UsesReservedWord(rtype)
3          self._checker.schema = schema
4          self._annotator.schema = schema
5          self._analyser.set_schema(schema)
6 +        self._cache = {}
7 
8      def get_backend(self):
9          return self._checker.backend
10      def set_backend(self, backend):
11          self._checker.backend = backend
12      backend = property(get_backend, set_backend)
13 
14      def parse(self, rqlstring, annotate=True):
15          """Return a syntax tree created from a RQL string."""
16 -        rqlst = parse(rqlstring, False)
17 -        self._checker.check(rqlst)
18 +        if rqlstring not in self._cache:
19 +            rqlst = parse(rqlstring, False)
20 +            self._checker.check(rqlst)
21 +            self._cache[rqlstring] = rqlst
22 +        rqlst = self._cache[rqlstring].copy()
23          if annotate:
24              self.annotate(rqlst)
25          rqlst.schema = self._annotator.schema
26          return rqlst
27 
diff --git a/stmts.py b/stmts.py
@@ -405,10 +405,11 @@
28  class Select(Statement, nodes.EditableMixIn, ScopeNode):
29      """the select node is the base statement of the syntax tree for selection
30      statement, always child of a UNION root.
31      """
32      vargraph = None
33 +    aggregated = None
34      parent = None
35      distinct = False
36      # limit / offset
37      limit = None
38      offset = 0
@@ -513,10 +514,11 @@
39              new.set_having([sq.copy(new) for sq in self.having])
40          new.distinct = self.distinct
41          new.limit = self.limit
42          new.offset = self.offset
43          new.vargraph = self.vargraph
44 +        new.aggregated = self.aggregated
45          return new
46 
47      # select specific methods #################################################
48 
49      def set_possible_types(self, solutions, kwargs=_MARKER, key='possibletypes'):