server/msplanner.py
branchstable
changeset 6794 140d42b41b31
parent 6759 5d016d5bacca
child 6943 406a41c25e13
--- a/server/msplanner.py	Fri Jan 07 15:21:56 2011 +0100
+++ b/server/msplanner.py	Fri Jan 07 18:51:47 2011 +0100
@@ -824,7 +824,7 @@
             while sourceterms:
                 # take a term randomly, and all terms supporting the
                 # same solutions
-                term, solindices = self._choose_term(sourceterms)
+                term, solindices = self._choose_term(source, sourceterms)
                 if source.uri == 'system':
                     # ensure all variables are available for the latest step
                     # (missing one will be available from temporary tables
@@ -854,7 +854,7 @@
                 # set of terms which should be additionaly selected when
                 # possible
                 needsel = set()
-                if not self._sourcesterms:
+                if not self._sourcesterms and scope is select:
                     terms += scope.defined_vars.values() + scope.aliases.values()
                     if isinstance(term, Relation) and len(sources) > 1:
                         variants = set()
@@ -867,13 +867,10 @@
                             # before a join with prefetched inputs
                             # (see test_crossed_relation_noeid_needattr in
                             #  unittest_msplanner / unittest_multisources)
-                            needsel2 = needsel.copy()
-                            needsel2.update(variants)
                             lhs, rhs = term.get_variable_parts()
                             steps.append( (sources, [term, getattr(lhs, 'variable', lhs),
                                                      getattr(rhs, 'variable', rhs)],
-                                           solindices, scope,
-                                           needsel2, False) )
+                                           solindices, scope, variants, False) )
                             sources = [self.system_source]
                     final = True
                 else:
@@ -906,7 +903,7 @@
                                 break
                         else:
                             if not scope is select:
-                                self._exists_relation(rel, terms, needsel)
+                                self._exists_relation(rel, terms, needsel, source)
                             # if relation is supported by all sources and some of
                             # its lhs/rhs variable isn't in "terms", and the
                             # other end *is* in "terms", mark it have to be
@@ -950,9 +947,14 @@
                     self._cleanup_sourcesterms(sources, solindices)
                 steps.append((sources, terms, solindices, scope, needsel, final)
                              )
+        if not steps[-1][-1]:
+            # add a final step
+            terms = select.defined_vars.values() + select.aliases.values()
+            steps.append( ([self.system_source], terms, set(self._solindices),
+                           select, set(), True) )
         return steps
 
-    def _exists_relation(self, rel, terms, needsel):
+    def _exists_relation(self, rel, terms, needsel, source):
         rschema = self._schema.rschema(rel.r_type)
         lhs, rhs = rel.get_variable_parts()
         try:
@@ -965,13 +967,24 @@
             # variable is refed by an outer scope and should be substituted
             # using an 'identity' relation (else we'll get a conflict of
             # temporary tables)
-            if rhsvar in terms and not lhsvar in terms and ms_scope(lhsvar) is lhsvar.stmt:
-                self._identity_substitute(rel, lhsvar, terms, needsel)
-            elif lhsvar in terms and not rhsvar in terms and ms_scope(rhsvar) is rhsvar.stmt:
-                self._identity_substitute(rel, rhsvar, terms, needsel)
+            relscope = ms_scope(rel)
+            lhsscope = ms_scope(lhsvar)
+            rhsscope = ms_scope(rhsvar)
+            if rhsvar in terms and not lhsvar in terms and lhsscope is lhsvar.stmt:
+                self._identity_substitute(rel, lhsvar, terms, needsel, relscope)
+            elif lhsvar in terms and not rhsvar in terms and rhsscope is rhsvar.stmt:
+                self._identity_substitute(rel, rhsvar, terms, needsel, relscope)
+            elif self.crossed_relation(source, rel):
+                if lhsscope is not relscope:
+                    self._identity_substitute(rel, lhsvar, terms, needsel,
+                                              relscope, lhsscope)
+                if rhsscope is not relscope:
+                    self._identity_substitute(rel, rhsvar, terms, needsel,
+                                              relscope, rhsscope)
 
-    def _identity_substitute(self, relation, var, terms, needsel):
-        newvar = self._insert_identity_variable(ms_scope(relation), var)
+    def _identity_substitute(self, relation, var, terms, needsel, exist,
+                             idrelscope=None):
+        newvar = self._insert_identity_variable(exist, var, idrelscope)
         # ensure relation is using '=' operator, else we rely on a
         # sqlgenerator side effect (it won't insert an inequality operator
         # in this case)
@@ -979,12 +992,28 @@
         terms.append(newvar)
         needsel.add(newvar.name)
 
-    def _choose_term(self, sourceterms):
+    def _choose_term(self, source, sourceterms):
         """pick one term among terms supported by a source, which will be used
         as a base to generate an execution step
         """
         secondchoice = None
         if len(self._sourcesterms) > 1:
+            # first, return non invariant variable of crossed relation, then the
+            # crossed relation itself
+            for term in sourceterms:
+                if (isinstance(term, Relation)
+                    and self.crossed_relation(source, term)
+                    and not ms_scope(term) is self.rqlst):
+                    for vref in term.get_variable_parts():
+                        try:
+                            var = vref.variable
+                        except AttributeError:
+                            # Constant
+                            continue
+                        if ((len(var.stinfo['relations']) > 1 or var.stinfo['selected'])
+                            and var in sourceterms):
+                            return var, sourceterms.pop(var)
+                    return term, sourceterms.pop(term)
             # priority to variable from subscopes
             for term in sourceterms:
                 if not ms_scope(term) is self.rqlst: