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) |