Beispiel #1
0
def _fixMaybeFilters(projectPreds):
    from itertools import groupby

    projectPreds.sort()
    for k, v in groupby(projectPreds, lambda v: (v[0], v[1])):
        v = list(v)
        if all(not ismaybe for (joinname, projectname, ismaybe, pred) in v):
            continue  # no MAYBEs for this project
        # these are filters that have a predicate equivalent to the project
        # if the predicate isn't the maybe one, remove it
        # if it is and the filter has another predicate that not a simple selector
        # or tries to match null, move the maybe predicate to its own filter
        # otherwise make sure jointype = 'l'
        for joinname, projectname, ismaybe, pred in v:
            filter = pred.parent
            if not filter or not filter.parent:
                continue
            separate = False
            assert len(pred.siblings) <= 1
            for sib in pred.siblings:
                # if not simpleSelector(other): separate = True
                # XXX refactor with SimpleEngine._findSimplePredicates()
                if not isinstance(sib, Eq):
                    separate = True
                    break
                elif isinstance(sib.left, Project):
                    other = sib.right
                elif isinstance(sib.right, Project):
                    other = sib.left
                else:
                    separate = True
                    break
                if not other.isIndependent():
                    separate = True
                elif other == Constant(None):
                    separate = True

            if separate:
                filter.complexPredicates = True
                filter.removeLabel(projectname, OBJECT)
                for project in sib.depthfirst():
                    if isinstance(project, Project) and project.name == OBJECT:
                        # convert OBJECT to name
                        project.fields = [projectname]

                # if mabye move pred to separate join condition
                # otherwise just remove it
                if not ismaybe:
                    pred.parent = None
                else:
                    filter.parent.parent.appendArg(JoinConditionOp(Filter(pred, objectlabel=projectname), join="l"))
            elif ismaybe:
                if filter.parent.join not in ["i", "l"]:
                    raise QueryException('property with "maybe" can not be used here', filter.parent)
                filter.parent.join = "l"
Beispiel #2
0
    def buildJoins(self, root):
        # combine joins that have the same label:
        joins, removedJoins = self._joinLabeledJoins()
        validateTree(root)
        joinsInDocOrder = []
        self._findJoinsInDocOrder(root, joinsInDocOrder, removedJoins)
        assert not self.orphanedJoins, "orphaned joins left-over: %s" % self.orphanedJoins
        assert set(joinsInDocOrder) == set(joins), "missing join in doc: %s" % (set(joins) - set(joinsInDocOrder))

        joinsInDocOrder, simpleJoins, complexJoins = self._findJoinPreds(root, joinsInDocOrder)
        # next, in reverse document order (i.e. start with the most nested joins)
        # join together simpleJoinPreds that reference each other
        # need to follow this order to ensure that the outermost joins are on the
        # left-side otherwise right outer joins (the "maybe" operator) will break

        validateTree(*joinsInDocOrder)
        joinFromLabel = {}
        for join in joinsInDocOrder:
            joinFromLabel[join.name] = join
        for i in xrange(len(joinsInDocOrder) - 1, -1, -1):
            simpleJoins = self._makeSimpleJoins(joinsInDocOrder[i:], joinFromLabel, simpleJoins)
        assert not simpleJoins

        # now join together complexJoins as crossjoins
        # and set the filter as a complex predicate
        for filter, refs, ismaybe in complexJoins:
            if ismaybe:
                raise QueryException("MAYBE operation not supported on complex join predicates")
            filter.complexPredicates = True
            # find parent join to join with: give preference to one
            # that already joined with another join
            parentjoin = None
            otherjoins = []
            for name in refs:
                candidate = joinFromLabel.get(name)
                if not candidate:
                    raise QueryException("unreferenced label '%s' in complex join" % name)
                if candidate.parent and not isinstance(candidate.parent, Select):
                    candidate = getTopJoin(candidate, root.where)
                    if parentjoin and parentjoin is not candidate:
                        raise QueryException(
                            "Not supported: complex join that" "joins more than one join that is already joined"
                        )
                    else:
                        parentjoin = candidate
                elif candidate is root.where:
                    parentjoin = candidate
                else:
                    otherjoins.append(candidate)

            for join in otherjoins:
                if not parentjoin:
                    if root.where:
                        parentjoin = root.where
                    else:
                        if filter.parent.parent is not join:
                            join.appendArg(filter)  # move filter to parentjoin
                        root.appendArg(join)
                if parentjoin:
                    assert parentjoin is not join, (join.name, root)
                    parentjoin.appendArg(JoinConditionOp(join, filter, "x"))

        # next, make any non-empty labeled joins that we haven't yet merged
        # into another join a cross-join
        for join in joinsInDocOrder:
            if join.maybe:
                raise QueryException("MAYBE operation not supported on uncorrelated filter sets")
            if not join.parent and join.args:
                if not root.where:
                    root.appendArg(join)
                else:
                    root.where.appendArg(JoinConditionOp(join, join.name, "x"))
        assert all(join.parent or not join.args for join in joinsInDocOrder)

        # finally, removed empty joins from nested selects
        for join in joinsInDocOrder:
            if isinstance(join.parent, Select) and join.parent.parent and not join.args:
                join.parent = None