[ms planner] use an index to avoid doing the same step twice stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 06 Apr 2011 22:52:39 +0200
branchstable
changeset 7188 b2c45b7396fb
parent 7186 287f2273917f
child 7189 0c2c41fcb89d
[ms planner] use an index to avoid doing the same step twice
server/msplanner.py
server/test/unittest_msplanner.py
--- a/server/msplanner.py	Wed Apr 06 16:00:56 2011 +0200
+++ b/server/msplanner.py	Wed Apr 06 22:52:39 2011 +0200
@@ -1223,11 +1223,22 @@
     def build_non_final_part(self, select, solindices, sources, insertedvars,
                              table):
         """non final step, will have to store results in a temporary table"""
+        inputmapkey = tuple(sorted(solindices))
         solutions = [self._solutions[i] for i in solindices]
-        rqlst = self.plan.finalize(select, solutions, insertedvars)
-        step = FetchStep(self.plan, rqlst, sources, table, False)
+        # XXX be smarter vs rql comparison
+        idx_key = (select.as_string(), inputmapkey,
+                   tuple(sorted(sources)), tuple(sorted(insertedvars)))
+        try:
+            # if a similar step has already been process, simply backport its
+            # input map
+            step = self.plan.ms_steps_idx[idx_key]
+        except KeyError:
+            # processing needed
+            rqlst = self.plan.finalize(select, solutions, insertedvars)
+            step = FetchStep(self.plan, rqlst, sources, table, False)
+            self.plan.ms_steps_idx[idx_key] = step
+            self.plan.add_step(step)
         # update input map for following steps, according to processed solutions
-        inputmapkey = tuple(sorted(solindices))
         inputmap = self._inputmaps.setdefault(inputmapkey, {})
         for varname, mapping in step.outputmap.iteritems():
             if varname in inputmap and not '.' in varname and  \
@@ -1235,7 +1246,6 @@
                         self._schema.eschema(solutions[0][varname]).final):
                 self._conflicts.append((varname, inputmap[varname]))
         inputmap.update(step.outputmap)
-        self.plan.add_step(step)
 
 
 class MSPlanner(SSPlanner):
@@ -1259,6 +1269,7 @@
             print 'PLANNING', rqlst
         ppis = [PartPlanInformation(plan, select, self.rqlhelper)
                 for select in rqlst.children]
+        plan.ms_steps_idx = {}
         steps = self._union_plan(plan, ppis)
         if server.DEBUG & server.DBG_MS:
             from pprint import pprint
--- a/server/test/unittest_msplanner.py	Wed Apr 06 16:00:56 2011 +0200
+++ b/server/test/unittest_msplanner.py	Wed Apr 06 22:52:39 2011 +0200
@@ -2383,6 +2383,56 @@
                      None, None, [self.system], {}, [])],
                    {'x': 999999, 'u': 999998})
 
+    def test_nonregr_similar_subquery(self):
+        repo._type_source_cache[999999] = ('Personne', 'system', 999999)
+        self._test('Any T,TD,U,T,UL WITH T,TD,U,UL BEING ('
+                   '(Any T,TD,U,UL WHERE X eid %(x)s, T comments X, T content TD, T created_by U?, U login UL)'
+                   ' UNION '
+                   '(Any T,TD,U,UL WHERE X eid %(x)s, X connait P, T comments P, T content TD, T created_by U?, U login UL))',
+                   # XXX optimization: use a OneFetchStep with a UNION of both queries
+                   [('FetchStep', [('Any U,UL WHERE U login UL, U is CWUser',
+                                    [{'U': 'CWUser', 'UL': 'String'}])],
+                     [self.ldap, self.system], None,
+                     {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
+                     []),
+                    ('UnionFetchStep',
+                     [('FetchStep',
+                       [('Any T,TD,U,UL WHERE T comments 999999, T content TD, T created_by U?, U login UL, T is Comment, U is CWUser',
+                         [{'T': 'Comment', 'TD': 'String', 'U': 'CWUser', 'UL': 'String'}])],
+                       [self.system],
+                       {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
+                       {'T': 'table1.C0',
+                        'T.content': 'table1.C1',
+                        'TD': 'table1.C1',
+                        'U': 'table1.C2',
+                        'U.login': 'table1.C3',
+                        'UL': 'table1.C3'},
+                       []),
+                      ('FetchStep',
+                       [('Any T,TD,U,UL WHERE 999999 connait P, T comments P, T content TD, T created_by U?, U login UL, P is Personne, T is Comment, U is CWUser',
+                         [{'P': 'Personne',
+                           'T': 'Comment',
+                           'TD': 'String',
+                           'U': 'CWUser',
+                           'UL': 'String'}])],
+                       [self.system],
+                       {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
+                       {'T': 'table1.C0',
+                        'T.content': 'table1.C1',
+                        'TD': 'table1.C1',
+                        'U': 'table1.C2',
+                        'U.login': 'table1.C3',
+                        'UL': 'table1.C3'},
+                       [])]),
+                    ('OneFetchStep',
+                     [('Any T,TD,U,T,UL',
+                       [{'T': 'Comment', 'TD': 'String', 'U': 'CWUser', 'UL': 'String'}])],
+                     None, None,
+                     [self.system],
+                     {'T': 'table1.C0', 'TD': 'table1.C1', 'U': 'table1.C2', 'UL': 'table1.C3'},
+                     [])],
+                   {'x': 999999})
+
 
 class MSPlannerTwoSameExternalSourcesTC(BasePlannerTC):
     """test planner related feature on a 3-sources repository: