server/sources/rql2sql.py
changeset 11293 63c589e6b076
parent 10677 59ec0aaae08b
equal deleted inserted replaced
11289:3e69bccc2022 11293:63c589e6b076
   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_:
   398             yield 1
   406             yield 1
   399             return
   407             return
   400         thisexistssols, thisexistsvars = self.existssols[exists]
   408         thisexistssols, thisexistsvars = self.existssols[exists]
   401         notdone_outside_vars = set()
   409         notdone_outside_vars = set()
   402         # when iterating other solutions inner to an EXISTS subquery, we should
   410         # when iterating other solutions inner to an EXISTS subquery, we should
   403         # reset variables which have this exists node as scope at each iteration
   411         # reset variables which have this EXISTS node as scope at each iteration
   404         for var in exists.stmt.defined_vars.values():
   412         for var in exists.stmt.defined_vars.values():
   405             if var.scope is exists:
   413             if var.scope is exists:
   406                 thisexistsvars.add(var.name)
   414                 thisexistsvars.add(var.name)
   407             elif var.name not in self.done:
   415             elif var.name not in self.done:
   408                 notdone_outside_vars.add(var)
   416                 notdone_outside_vars.add(var)
   409         origsol = self.solution
   417         # make a copy of the outer statement's solution for later restore
       
   418         origsol = self.solution.copy()
   410         origtables = self.tables
   419         origtables = self.tables
   411         done = self.done
   420         done = self.done
   412         for thisexistssol in thisexistssols:
   421         for thisexistssol in thisexistssols:
   413             for vname in self.unstablevars:
   422             for vname in self.unstablevars:
   414                 if thisexistssol[vname] != origsol[vname] and vname in thisexistsvars:
   423                 # check first if variable belong to the EXISTS's scope, else it may be missing from
       
   424                 # `thisexistssol`
       
   425                 if vname in thisexistsvars and thisexistssol[vname] != origsol[vname]:
   415                     break
   426                     break
   416             else:
   427             else:
   417                 self.tables = origtables.copy()
   428                 self.tables = origtables.copy()
   418                 self.solution = thisexistssol
   429                 # overwrite current outer solution by EXISTS solution (the later will be missing
       
   430                 # unstable outer variables)
       
   431                 self.solution.update(thisexistssol)
   419                 yield 1
   432                 yield 1
   420                 # cleanup self.done from stuff specific to exists
   433                 # cleanup self.done from stuff specific to EXISTS, so they will be reconsidered in
       
   434                 # the next round
   421                 for var in thisexistsvars:
   435                 for var in thisexistsvars:
   422                     if var in done:
   436                     if var in done:
   423                         done.remove(var)
   437                         done.remove(var)
   424                 for var in list(notdone_outside_vars):
   438                 for var in list(notdone_outside_vars):
   425                     if var.name in done and var._q_sqltable in self.tables:
   439                     if var.name in done and var._q_sqltable in self.tables:
   426                         origtables[var._q_sqltable] = self.tables[var._q_sqltable]
   440                         origtables[var._q_sqltable] = self.tables[var._q_sqltable]
   427                         notdone_outside_vars.remove(var)
   441                         notdone_outside_vars.remove(var)
   428                 for rel in exists.iget_nodes(Relation):
   442                 for rel in exists.iget_nodes(Relation):
   429                     if rel in done:
   443                     if rel in done:
   430                         done.remove(rel)
   444                         done.remove(rel)
       
   445         # restore original solution
   431         self.solution = origsol
   446         self.solution = origsol
   432         self.tables = origtables
   447         self.tables = origtables
   433 
   448 
   434     def push_scope(self, scope_node):
   449     def push_scope(self, scope_node):
   435         self.scope_nodes.append(scope_node)
   450         self.scope_nodes.append(scope_node)