cubicweb/server/sources/rql2sql.py
changeset 11301 a76feec0a861
parent 11293 63c589e6b076
parent 11237 f32134dd0067
child 11347 b4dcfd734686
equal deleted inserted replaced
11300:01c04bbbe7b8 11301:a76feec0a861
   185         elif var.scope is not rqlst:
   185         elif var.scope is not rqlst:
   186             # move apart variables which are in a EXISTS scope and are variating
   186             # move apart variables which are in a EXISTS scope and are variating
   187             try:
   187             try:
   188                 thisexistssols, thisexistsvars = existssols[var.scope]
   188                 thisexistssols, thisexistsvars = existssols[var.scope]
   189             except KeyError:
   189             except KeyError:
   190                 thisexistssols = [newsols[0]]
   190                 # copy to avoid shared dict in newsols and exists sols
       
   191                 thisexistssols = [newsols[0].copy()]
   191                 thisexistsvars = set()
   192                 thisexistsvars = set()
   192                 existssols[var.scope] = thisexistssols, thisexistsvars
   193                 existssols[var.scope] = thisexistssols, thisexistsvars
   193             for i in range(len(newsols)-1, 0, -1):
   194             for i in range(len(newsols)-1, 0, -1):
   194                 if vtype != newsols[i][vname]:
   195                 if vtype != newsols[i][vname]:
   195                     thisexistssols.append(newsols.pop(i))
   196                     thisexistssols.append(newsols.pop(i))
   197         else:
   198         else:
   198             # remember unstable variables
   199             # remember unstable variables
   199             for i in range(1, len(newsols)):
   200             for i in range(1, len(newsols)):
   200                 if vtype != newsols[i][vname]:
   201                 if vtype != newsols[i][vname]:
   201                     unstable.add(vname)
   202                     unstable.add(vname)
       
   203     # remove unstable variables from exists solutions: the possible types of these variables are not
       
   204     # properly represented in exists solutions, so we have to remove and reinject them later
       
   205     # according to the outer solution (see `iter_exists_sols`)
       
   206     for sols, _ in existssols.values():
       
   207         for vname in unstable:
       
   208             for sol in sols:
       
   209                 sol.pop(vname, None)
   202     if invariants:
   210     if invariants:
   203         # filter out duplicates
   211         # filter out duplicates
   204         newsols_ = []
   212         newsols_ = []
   205         for sol in newsols:
   213         for sol in newsols:
   206             if not sol in newsols_:
   214             if not sol in newsols_:
   397             yield 1
   405             yield 1
   398             return
   406             return
   399         thisexistssols, thisexistsvars = self.existssols[exists]
   407         thisexistssols, thisexistsvars = self.existssols[exists]
   400         notdone_outside_vars = set()
   408         notdone_outside_vars = set()
   401         # when iterating other solutions inner to an EXISTS subquery, we should
   409         # when iterating other solutions inner to an EXISTS subquery, we should
   402         # reset variables which have this exists node as scope at each iteration
   410         # reset variables which have this EXISTS node as scope at each iteration
   403         for var in exists.stmt.defined_vars.values():
   411         for var in exists.stmt.defined_vars.values():
   404             if var.scope is exists:
   412             if var.scope is exists:
   405                 thisexistsvars.add(var.name)
   413                 thisexistsvars.add(var.name)
   406             elif var.name not in self.done:
   414             elif var.name not in self.done:
   407                 notdone_outside_vars.add(var)
   415                 notdone_outside_vars.add(var)
   408         origsol = self.solution
   416         # make a copy of the outer statement's solution for later restore
       
   417         origsol = self.solution.copy()
   409         origtables = self.tables
   418         origtables = self.tables
   410         done = self.done
   419         done = self.done
   411         for thisexistssol in thisexistssols:
   420         for thisexistssol in thisexistssols:
   412             for vname in self.unstablevars:
   421             for vname in self.unstablevars:
   413                 if thisexistssol[vname] != origsol[vname] and vname in thisexistsvars:
   422                 # check first if variable belong to the EXISTS's scope, else it may be missing from
       
   423                 # `thisexistssol`
       
   424                 if vname in thisexistsvars and thisexistssol[vname] != origsol[vname]:
   414                     break
   425                     break
   415             else:
   426             else:
   416                 self.tables = origtables.copy()
   427                 self.tables = origtables.copy()
   417                 self.solution = thisexistssol
   428                 # overwrite current outer solution by EXISTS solution (the later will be missing
       
   429                 # unstable outer variables)
       
   430                 self.solution.update(thisexistssol)
   418                 yield 1
   431                 yield 1
   419                 # cleanup self.done from stuff specific to exists
   432                 # cleanup self.done from stuff specific to EXISTS, so they will be reconsidered in
       
   433                 # the next round
   420                 for var in thisexistsvars:
   434                 for var in thisexistsvars:
   421                     if var in done:
   435                     if var in done:
   422                         done.remove(var)
   436                         done.remove(var)
   423                 for var in list(notdone_outside_vars):
   437                 for var in list(notdone_outside_vars):
   424                     if var.name in done and var._q_sqltable in self.tables:
   438                     if var.name in done and var._q_sqltable in self.tables:
   425                         origtables[var._q_sqltable] = self.tables[var._q_sqltable]
   439                         origtables[var._q_sqltable] = self.tables[var._q_sqltable]
   426                         notdone_outside_vars.remove(var)
   440                         notdone_outside_vars.remove(var)
   427                 for rel in exists.iget_nodes(Relation):
   441                 for rel in exists.iget_nodes(Relation):
   428                     if rel in done:
   442                     if rel in done:
   429                         done.remove(rel)
   443                         done.remove(rel)
       
   444         # restore original solution
   430         self.solution = origsol
   445         self.solution = origsol
   431         self.tables = origtables
   446         self.tables = origtables
   432 
   447 
   433     def push_scope(self, scope_node):
   448     def push_scope(self, scope_node):
   434         self.scope_nodes.append(scope_node)
   449         self.scope_nodes.append(scope_node)