Пример #1
0
def type_intersection_set(
    source_set: irast.Set,
    stype: s_types.Type,
    *,
    optional: bool,
    ctx: context.ContextLevel,
) -> irast.Set:
    """Return an interesection of *source_set* with type *stype*."""

    arg_type = get_set_type(source_set, ctx=ctx)

    result = schemactx.apply_intersection(arg_type, stype, ctx=ctx)
    if result.stype is arg_type:
        return source_set

    poly_set = new_set(stype=result.stype, ctx=ctx)
    rptr = source_set.rptr
    rptr_specialization = []

    if rptr is not None and rptr.ptrref.union_components:
        # This is a type intersection of a union pointer, most likely
        # a reverse link path specification.  If so, test the union
        # components against the type expression and record which
        # components match.  This information will be used later
        # when evaluating the path cardinality, as well as to
        # route link property references accordingly.
        for component in rptr.ptrref.union_components:
            component_endpoint_ref = component.dir_target
            ctx.env.schema, component_endpoint = irtyputils.ir_typeref_to_type(
                ctx.env.schema, component_endpoint_ref)
            if component_endpoint.issubclass(ctx.env.schema, stype):
                assert isinstance(component, irast.PointerRef)
                rptr_specialization.append(component)

    ptrcls = irast.TypeIntersectionLink(
        arg_type,
        result.stype,
        optional=optional,
        is_empty=result.is_empty,
        is_subtype=result.is_subtype,
        rptr_specialization=rptr_specialization,
        # The type intersection cannot increase the cardinality
        # of the input set, so semantically, the cardinality
        # of the type intersection "link" is, at most, ONE.
        cardinality=qltypes.SchemaCardinality.ONE,
    )

    ptrref = irtyputils.ptrref_from_ptrcls(
        schema=ctx.env.schema,
        ptrcls=ptrcls,
        cache=ctx.env.ptr_ref_cache,
        typeref_cache=ctx.env.type_ref_cache,
    )

    poly_set.path_id = source_set.path_id.extend(
        schema=ctx.env.schema,
        ptrref=ptrref,
    )

    ptr = irast.TypeIntersectionPointer(
        source=source_set,
        target=poly_set,
        ptrref=ptrref,
        direction=poly_set.path_id.rptr_dir(),
        optional=optional,
    )

    poly_set.rptr = ptr

    return poly_set
Пример #2
0
def computable_ptr_set(
        rptr: irast.Pointer, *,
        unnest_fence: bool=False,
        same_computable_scope: bool=False,
        from_default_expr: bool=False,
        ctx: context.ContextLevel) -> irast.Set:
    """Return ir.Set for a pointer defined as a computable."""
    ptrcls = irtyputils.ptrcls_from_ptrref(rptr.ptrref, schema=ctx.env.schema)
    source_set = rptr.source
    source_scls = get_set_type(source_set, ctx=ctx)
    # process_view() may generate computable pointer expressions
    # in the form "self.linkname".  To prevent infinite recursion,
    # self must resolve to the parent type of the view NOT the view
    # type itself.  Similarly, when resolving computable link properties
    # make sure that we use ptrcls.derived_from.
    if source_scls.is_view(ctx.env.schema):
        source_set_stype = source_scls.peel_view(ctx.env.schema)
        source_set = new_set_from_set(
            source_set, stype=source_set_stype,
            preserve_scope_ns=True, ctx=ctx)
        source_set.shape = []

        if source_set.rptr is not None:
            schema = ctx.env.schema
            source_rptrref = source_set.rptr.ptrref
            source_rptrcls = irtyputils.ptrcls_from_ptrref(
                source_rptrref, schema=schema)
            bases = source_rptrcls.get_bases(schema)
            if bases:
                base = bases.first(schema)
                if (not base.generic(schema)
                        and ptrcls.is_link_property(schema)):
                    source_rptrref = irtyputils.ptrref_from_ptrcls(
                        source_ref=source_rptrref.dir_source,
                        target_ref=source_rptrref.dir_target,
                        direction=source_rptrref.direction,
                        parent_ptr=source_rptrref.parent_ptr,
                        ptrcls=base,
                        schema=schema,
                    )

                    source_set.rptr = irast.Pointer(
                        source=source_set.rptr.source,
                        target=source_set,
                        ptrref=source_rptrref,
                        direction=source_set.rptr.direction,
                    )

                    stmtctx.ensure_ptrref_cardinality(
                        base, source_set.rptr.ptrref, ctx=ctx)

    qlctx: typing.Optional[context.ContextLevel]
    inner_source_path_id: typing.Optional[irast.PathId]

    try:
        qlexpr, qlctx, inner_source_path_id, path_id_ns = \
            ctx.source_map[ptrcls]
    except KeyError:
        if from_default_expr:
            comp_expr = ptrcls.get_default(ctx.env.schema)
        else:
            comp_expr = ptrcls.get_expr(ctx.env.schema)
        if comp_expr is None:
            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            raise ValueError(
                f'{ptrcls_sn!r} is not a computable pointer')

        qlexpr = qlparser.parse(comp_expr.text)
        # NOTE: Validation of the expression type is not the concern
        # of this function. For any non-object pointer target type,
        # the default expression must be assignment-cast into that
        # type.
        target_scls = ptrcls.get_target(ctx.env.schema)
        assert target_scls is not None
        if not target_scls.is_object_type():
            qlexpr = qlast.TypeCast(
                type=astutils.type_to_ql_typeref(
                    target_scls, schema=ctx.env.schema),
                expr=qlexpr,
            )
        qlexpr = astutils.ensure_qlstmt(qlexpr)
        qlctx = None
        inner_source_path_id = None
        path_id_ns = None

    if qlctx is None:
        # Schema-level computable, completely detached context
        newctx = ctx.detached
    else:
        newctx = _get_computable_ctx(
            rptr=rptr,
            source=source_set,
            source_scls=source_scls,
            inner_source_path_id=inner_source_path_id,
            path_id_ns=path_id_ns,
            same_scope=same_computable_scope,
            qlctx=qlctx,
            ctx=ctx)

    if ptrcls.is_link_property(ctx.env.schema):
        source_path_id = rptr.source.path_id.ptr_path()
    else:
        source_path_id = rptr.target.path_id.src_path()

    result_stype = ptrcls.get_target(ctx.env.schema)
    result_path_id = pathctx.extend_path_id(
        source_path_id,
        ptrcls=ptrcls,
        direction=s_pointers.PointerDirection.Outbound,
        target=result_stype,
        ns=ctx.path_id_namespace,
        ctx=ctx)

    with newctx() as subctx:
        subctx.view_scls = result_stype
        subctx.view_rptr = context.ViewRPtr(
            source_scls, ptrcls=ptrcls, rptr=rptr)
        subctx.anchors[qlast.Source] = source_set
        subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema)
        subctx.partial_path_prefix = source_set

        if isinstance(qlexpr, qlast.Statement) and unnest_fence:
            subctx.stmt_metadata[qlexpr] = context.StatementMetadata(
                is_unnest_fence=True)

        comp_ir_set = ensure_set(
            dispatch.compile(qlexpr, ctx=subctx), ctx=subctx)

    comp_ir_set_copy = new_set_from_set(comp_ir_set, ctx=ctx)
    pending_cardinality = ctx.pending_cardinality.get(ptrcls)
    if pending_cardinality is not None and not pending_cardinality.from_parent:
        stmtctx.get_pointer_cardinality_later(
            ptrcls=ptrcls, irexpr=comp_ir_set_copy,
            specified_card=pending_cardinality.specified_cardinality,
            source_ctx=pending_cardinality.source_ctx,
            ctx=ctx)

    stmtctx.enforce_pointer_cardinality(ptrcls, comp_ir_set_copy, ctx=ctx)

    comp_ir_set = new_set_from_set(
        comp_ir_set, path_id=result_path_id, rptr=rptr, ctx=ctx)

    rptr.target = comp_ir_set

    return comp_ir_set
Пример #3
0
def computable_ptr_set(rptr: irast.Pointer,
                       *,
                       unnest_fence: bool = False,
                       same_computable_scope: bool = False,
                       ctx: context.ContextLevel) -> irast.Set:
    """Return ir.Set for a pointer defined as a computable."""
    ptrcls = irtyputils.ptrcls_from_ptrref(rptr.ptrref, schema=ctx.env.schema)
    source_set = rptr.source
    source_scls = get_set_type(source_set, ctx=ctx)
    # process_view() may generate computable pointer expressions
    # in the form "self.linkname".  To prevent infinite recursion,
    # self must resolve to the parent type of the view NOT the view
    # type itself.  Similarly, when resolving computable link properties
    # make sure that we use ptrcls.derived_from.
    if source_scls.is_view(ctx.env.schema):
        source_set_stype = source_scls.peel_view(ctx.env.schema)
        source_set = new_set_from_set(source_set,
                                      stype=source_set_stype,
                                      preserve_scope_ns=True,
                                      ctx=ctx)
        source_set.shape = []

        if source_set.rptr is not None:
            schema = ctx.env.schema
            source_rptrref = source_set.rptr.ptrref
            source_rptrcls = irtyputils.ptrcls_from_ptrref(source_rptrref,
                                                           schema=schema)
            derived_from = source_rptrcls.get_derived_from(schema)
            if (derived_from is not None and not derived_from.generic(schema)
                    and derived_from.get_derived_from(schema) is not None
                    and ptrcls.is_link_property(schema)):
                source_set.rptr.ptrref = irtyputils.ptrref_from_ptrcls(
                    source_ref=source_rptrref.dir_source,
                    target_ref=source_rptrref.dir_target,
                    direction=source_rptrref.direction,
                    parent_ptr=source_rptrref.parent_ptr,
                    ptrcls=derived_from,
                    schema=schema,
                )

                stmtctx.ensure_ptrref_cardinality(derived_from,
                                                  source_set.rptr.ptrref,
                                                  ctx=ctx)

    try:
        qlexpr, qlctx, inner_source_path_id, path_id_ns = \
            ctx.source_map[ptrcls]
    except KeyError:
        ptrcls_default = ptrcls.get_default(ctx.env.schema)
        if not ptrcls_default:
            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            raise ValueError(f'{ptrcls_sn!r} is not a computable pointer')

        qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls_default.text))
        qlctx = None
        inner_source_path_id = None
        path_id_ns = None

    if qlctx is None:
        # Schema-level computable, completely detached context
        newctx = ctx.detached
    else:
        newctx = _get_computable_ctx(rptr=rptr,
                                     source=source_set,
                                     source_scls=source_scls,
                                     inner_source_path_id=inner_source_path_id,
                                     path_id_ns=path_id_ns,
                                     same_scope=same_computable_scope,
                                     qlctx=qlctx,
                                     ctx=ctx)

    if ptrcls.is_link_property(ctx.env.schema):
        source_path_id = rptr.source.path_id.ptr_path()
    else:
        source_path_id = rptr.target.path_id.src_path()

    result_stype = ptrcls.get_target(ctx.env.schema)
    result_path_id = pathctx.extend_path_id(
        source_path_id,
        ptrcls=ptrcls,
        direction=s_pointers.PointerDirection.Outbound,
        target=result_stype,
        ns=ctx.path_id_namespace,
        ctx=ctx)

    with newctx() as subctx:
        subctx.view_scls = result_stype
        subctx.view_rptr = context.ViewRPtr(source_scls,
                                            ptrcls=ptrcls,
                                            rptr=rptr)
        subctx.anchors[qlast.Source] = source_set
        subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema)
        subctx.partial_path_prefix = source_set

        if isinstance(qlexpr, qlast.Statement) and unnest_fence:
            subctx.stmt_metadata[qlexpr] = context.StatementMetadata(
                is_unnest_fence=True)

        comp_ir_set = dispatch.compile(qlexpr, ctx=subctx)

    pending_cardinality = ctx.pending_cardinality.get(ptrcls)
    if pending_cardinality is not None and not pending_cardinality.from_parent:
        comp_ir_set_copy = new_set_from_set(comp_ir_set, ctx=ctx)
        stmtctx.get_pointer_cardinality_later(
            ptrcls=ptrcls,
            irexpr=comp_ir_set_copy,
            specified_card=pending_cardinality.specified_cardinality,
            source_ctx=pending_cardinality.source_ctx,
            ctx=ctx)

        def _check_cardinality(ctx):
            if ptrcls.singular(ctx.env.schema):
                stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx)

        stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx)

    comp_ir_set = new_set_from_set(comp_ir_set,
                                   path_id=result_path_id,
                                   rptr=rptr,
                                   ctx=ctx)

    rptr.target = comp_ir_set

    return comp_ir_set