diff -r 01c04bbbe7b8 -r a76feec0a861 cubicweb/server/sources/rql2sql.py --- a/cubicweb/server/sources/rql2sql.py Tue Jun 21 14:13:54 2016 +0200 +++ b/cubicweb/server/sources/rql2sql.py Tue Jun 21 16:35:21 2016 +0200 @@ -187,7 +187,8 @@ try: thisexistssols, thisexistsvars = existssols[var.scope] except KeyError: - thisexistssols = [newsols[0]] + # copy to avoid shared dict in newsols and exists sols + thisexistssols = [newsols[0].copy()] thisexistsvars = set() existssols[var.scope] = thisexistssols, thisexistsvars for i in range(len(newsols)-1, 0, -1): @@ -199,6 +200,13 @@ for i in range(1, len(newsols)): if vtype != newsols[i][vname]: unstable.add(vname) + # remove unstable variables from exists solutions: the possible types of these variables are not + # properly represented in exists solutions, so we have to remove and reinject them later + # according to the outer solution (see `iter_exists_sols`) + for sols, _ in existssols.values(): + for vname in unstable: + for sol in sols: + sol.pop(vname, None) if invariants: # filter out duplicates newsols_ = [] @@ -399,24 +407,30 @@ thisexistssols, thisexistsvars = self.existssols[exists] notdone_outside_vars = set() # when iterating other solutions inner to an EXISTS subquery, we should - # reset variables which have this exists node as scope at each iteration + # reset variables which have this EXISTS node as scope at each iteration for var in exists.stmt.defined_vars.values(): if var.scope is exists: thisexistsvars.add(var.name) elif var.name not in self.done: notdone_outside_vars.add(var) - origsol = self.solution + # make a copy of the outer statement's solution for later restore + origsol = self.solution.copy() origtables = self.tables done = self.done for thisexistssol in thisexistssols: for vname in self.unstablevars: - if thisexistssol[vname] != origsol[vname] and vname in thisexistsvars: + # check first if variable belong to the EXISTS's scope, else it may be missing from + # `thisexistssol` + if vname in thisexistsvars and thisexistssol[vname] != origsol[vname]: break else: self.tables = origtables.copy() - self.solution = thisexistssol + # overwrite current outer solution by EXISTS solution (the later will be missing + # unstable outer variables) + self.solution.update(thisexistssol) yield 1 - # cleanup self.done from stuff specific to exists + # cleanup self.done from stuff specific to EXISTS, so they will be reconsidered in + # the next round for var in thisexistsvars: if var in done: done.remove(var) @@ -427,6 +441,7 @@ for rel in exists.iget_nodes(Relation): if rel in done: done.remove(rel) + # restore original solution self.solution = origsol self.tables = origtables