Example #1
0
    def in_expr(self, expr: Expression):
        assert expr.operator == "In"
        (left, right) = expr.args
        right_path = dot_path(right)

        if left == "_this" and right_path:
            if right_path[1:]:
                # _this in _this.foo.bar
                # _this in _some_var.foo.bar
                path = self.translate_path_to_field(right_path)
                # path = "__".join(right_path[1:])
                self.filter &= COMPARISONS["Unify"]("pk", path)
            else:
                # _this in _this
                # _this in _some_var
                raise UnsupportedError(
                    f"Unsupported partial expression: {expr}")
        elif isinstance(left, Variable) and right_path:
            if right_path[1:]:
                # var in _this.foo.bar
                # var in other_var.foo.bar

                # get the base query for the RHS of the `in`
                root = self
                while root.parent:
                    root = root.parent
                base_query = root.get_query_from_var(right_path[0]) or root

                # Left is a variable => apply constraints to the subquery.
                if left not in base_query.variables:
                    subquery_path = right_path[1:]
                    model = get_model_by_path(base_query.model, subquery_path)
                    base_query.variables[left] = FilterBuilder(
                        model, parent=base_query, name=left)
                else:
                    # This means we have two paths for the same variable
                    # the subquery will handle the intersection
                    pass

                # Get the model for the subfield

                subquery = base_query.variables[left]
                # <var> in <partial>
                # => set up <var> as a new filtered query over the model
                # filtered to the entries of right_path
                path = base_query.translate_path_to_field(right_path)
                field = OuterRef(path.name) if isinstance(
                    path, F) else OuterRef(path)
                subquery.filter &= COMPARISONS["Unify"]("pk", field)
                # Maybe redundant, but want to be sure
                base_query.variables[left] = subquery
            else:
                # var in _this
                # var in other_var
                raise UnsupportedError(
                    f"Unsupported partial expression: {expr}")
        else:
            # <value> in <partial>
            self.filter &= COMPARISONS["Unify"]("__".join(right_path[1:]),
                                                left)
Example #2
0
def translate_compare(expression: Expression, session: Session, model, get_model):
    (left, right) = expression.args
    left_path = dot_path(left)
    if left_path:
        path, field_name = left_path[:-1], left_path[-1]
        return translate_dot(
            path,
            session,
            model,
            functools.partial(emit_compare, field_name, right, expression.operator),
        )
    else:
        assert left == Variable("_this")
        if not isinstance(right, model):
            return sql.false()

        if expression.operator not in ("Eq", "Unify"):
            raise UnsupportedError(
                f"Unsupported comparison: {expression}. Models can only be compared"
                " with `=` or `==`"
            )

        primary_keys = [pk.name for pk in inspect(model).primary_key]
        pk_filter = sql.true()
        for key in primary_keys:
            pk_filter &= getattr(model, key) == getattr(right, key)
        return pk_filter
Example #3
0
    def compare_expr(self, expr: Expression):
        assert expr.operator in COMPARISONS
        (left, right) = expr.args
        left_path = dot_path(left)
        right_path = dot_path(right)

        # Normalize partial to LHS.
        if right_path:
            expr = reflect_expr(expr)
            left, right = right, left
            left_path, right_path = right_path, left_path

        left_field = "__".join(left_path[1:]) if left_path[1:] else "pk"

        if left_path and right_path:
            raise UnsupportedError(f"Unsupported partial expression: {expr}")
            # compare partials
            # self.filter &= COMPARISONS[expr.operator](
            #     left_field, self.translate_path_to_field(right_path)
            # )
        else:
            # partial cmp grounded
            assert left_path
            if isinstance(right, Model):
                right = right.pk
            self.filter &= COMPARISONS[expr.operator](left_field, right)
Example #4
0
def _runtime_error(subkind, message, details):
    runtime_errors = {
        "Serialization": SerializationError(message, details),
        "Unsupported": UnsupportedError(message, details),
        "TypeError": PolarTypeError(message, details),
        "StackOverflow": StackOverflowError(message, details),
        "FileLoading": FileLoadingError(message, details),
    }
    return runtime_errors.get(subkind, PolarRuntimeError(message, details))
Example #5
0
def translate_expr(expression: Expression, session: Session, model, get_model):
    assert isinstance(expression, Expression)
    if expression.operator in COMPARISONS:
        return translate_compare(expression, session, model, get_model)
    elif expression.operator == "Isa":
        return translate_isa(expression, session, model, get_model)
    elif expression.operator == "In":
        return translate_in(expression, session, model, get_model)
    elif expression.operator == "And":
        return translate_and(expression, session, model, get_model)
    else:
        raise UnsupportedError(f"Unsupported {expression}")
Example #6
0
def and_expr(expr, type_name):
    q = Q()

    assert expr.operator == "And"
    for expression in expr.args:
        assert isinstance(expression, Expression)
        if expression.operator in COMPARISONS:
            q = q & compare_expr(expression, type_name)
        elif expression.operator == "And":
            q = q & and_expr(expression, type_name)
        elif expression.operator == "Isa":
            try:
                assert expression.args[1].tag == type_name
            except (AssertionError, IndexError, AttributeError, TypeError):
                raise UnsupportedError(
                    f"Unimplemented partial isa operation {expression}.")
        else:
            raise UnsupportedError(
                f"Unimplemented partial operator {expression.operator}")

    return q
Example #7
0
def translate_expr(expr: Expression, model: Model, **kwargs):
    """Translate a Polar expression to a Django Q object."""
    assert isinstance(expr, Expression), "expected a Polar expression"

    if expr.operator in COMPARISONS:
        return compare_expr(expr, model, **kwargs)
    elif expr.operator == "And":
        return and_expr(expr, model, **kwargs)
    elif expr.operator == "Isa":
        return isa_expr(expr, model, **kwargs)
    elif expr.operator == "In":
        return in_expr(expr, model, **kwargs)
    elif expr.operator == "Not":
        return not_expr(expr, model, **kwargs)
    else:
        raise UnsupportedError(f"Unsupported partial expression: {expr}")
Example #8
0
def compare_expr(expr: Expression, model: Model, path=(), **kwargs):
    assert expr.operator in COMPARISONS
    (left, right) = expr.args
    left_path = dot_path(left)
    if left_path:
        return COMPARISONS[expr.operator]("__".join(path + left_path), right)
    else:
        assert left == Variable("_this")
        if not isinstance(right, model):
            return FALSE_FILTER

        if expr.operator not in ("Eq", "Unify"):
            raise UnsupportedError(
                f"Unsupported comparison: {expr}. Models can only be compared"
                " with `=` or `==`")

        return COMPARISONS[expr.operator]("__".join(path + ("pk", )), right.pk)
Example #9
0
def preprocess_expression(
    expression: Expression, variables: TGroupedExpressions
) -> Optional[Expression]:
    """Collect expressions over variables into ``variables``.

    Return the expression with those removed.
    """
    # Walk expression and collect variable expressions
    new_expr: Optional[Expression] = expression
    if expression.operator == "And":
        new_expr = preprocess_and(expression, variables)
    elif expression.operator in (
        "Or",
        "Not",
    ):  # Or and Not are not supported by SQLAlchemy translation.
        raise UnsupportedError(f"{expression.operator}")
    else:
        new_expr = preprocess_leaf(expression, variables)

    return new_expr
Example #10
0
    def translate_expr(self, expr: Expression):
        """Translate a Polar expression to a Django Q object."""
        assert isinstance(expr, Expression), "expected a Polar expression"

        # Check if either side of the expression starts with a lookup on
        # a variable. In which case, enter the subquery for that variable
        # instead and proceed as usual.
        if len(expr.args) == 2:
            left, right = expr.args
            left_path = dot_path(left)
            right_path = dot_path(right)

            if left_path:
                query = self.get_query_from_var(left_path[0])
                if query and query != self:
                    query.translate_expr(expr)
                    return self
            if right_path:
                query = self.get_query_from_var(right_path[0])
                if query and query != self:
                    query.translate_expr(expr)
                    return self

        if expr.operator in COMPARISONS:
            self.compare_expr(expr)
        elif expr.operator == "And":
            self.and_expr(expr)
        elif expr.operator == "Isa":
            self.isa_expr(expr)
        elif expr.operator == "In":
            self.in_expr(expr)
        elif expr.operator == "Not":
            self.not_expr(expr)
        else:
            raise UnsupportedError(f"Unsupported partial expression: {expr}")
        return self