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