Beispiel #1
0
def apply_intersection(
    left: s_types.Type,
    right: s_types.Type,
    *,
    ctx: context.ContextLevel
) -> TypeIntersectionResult:
    """Compute an intersection of two types: *left* and *right*.

    In theory, this should handle all combinations of unions and intersections
    recursively, but currently this handles only the common case of
    intersecting a regular type or a union type with a regular type.

    Returns:
        A :class:`~TypeIntersectionResult` named tuple containing the
        result intersection type, whether the type system considers
        the intersection empty and whether *left* is related to *right*
        (i.e either is a subtype of another).
    """

    if left.issubclass(ctx.env.schema, right):
        # The intersection type is a proper *superclass*
        # of the argument, then this is, effectively, a NOP.
        return TypeIntersectionResult(stype=left)

    is_subtype = False
    empty_intersection = False
    union = left.get_union_of(ctx.env.schema)
    if union:
        # If the argument type is a union type, then we
        # narrow it by the intersection type.
        narrowed_union = []
        for component_type in union.objects(ctx.env.schema):
            if component_type.issubclass(ctx.env.schema, right):
                narrowed_union.append(component_type)
            elif right.issubclass(ctx.env.schema, component_type):
                narrowed_union.append(right)

        if len(narrowed_union) == 0:
            int_type = get_intersection_type((left, right), ctx=ctx)
            is_subtype = int_type.issubclass(ctx.env.schema, left)
            assert isinstance(right, s_obj.InheritingObject)
            empty_intersection = not any(
                c.issubclass(ctx.env.schema, left)
                for c in right.descendants(ctx.env.schema)
            )
        elif len(narrowed_union) == 1:
            int_type = narrowed_union[0]
            is_subtype = int_type.issubclass(ctx.env.schema, left)
        else:
            int_type = get_union_type(narrowed_union, ctx=ctx)
    else:
        is_subtype = right.issubclass(ctx.env.schema, left)
        empty_intersection = not is_subtype
        int_type = get_intersection_type((left, right), ctx=ctx)

    return TypeIntersectionResult(
        stype=int_type,
        is_empty=empty_intersection,
        is_subtype=is_subtype,
    )
Beispiel #2
0
def type_to_typeref(
    schema: s_schema.Schema,
    t: s_types.Type,
    *,
    cache: Optional[Dict[TypeRefCacheKey, irast.TypeRef]] = None,
    typename: Optional[s_name.QualName] = None,
    include_descendants: bool = False,
    include_ancestors: bool = False,
    _name: Optional[str] = None,
) -> irast.TypeRef:
    """Return an instance of :class:`ir.ast.TypeRef` for a given type.

    An IR TypeRef is an object that fully describes a schema type for
    the purposes of query compilation.

    Args:
        schema:
            A schema instance, in which the type *t* is defined.
        t:
            A schema type instance.
        cache:
            Optional mapping from (type UUID, typename) to cached IR TypeRefs.
        typename:
            Optional name hint to use for the type in the returned
            TypeRef.  If ``None``, the type name is used.
        include_descendants:
            Whether to include the description of all material type descendants
            of *t*.
        include_ancestors:
            Whether to include the description of all material type ancestors
            of *t*.
        _name:
            Optional subtype element name if this type is a collection within
            a Tuple,

    Returns:
        A ``TypeRef`` instance corresponding to the given schema type.
    """

    result: irast.TypeRef
    material_type: s_types.Type

    key = (t.id, include_descendants, include_ancestors)

    if cache is not None and typename is None:
        cached_result = cache.get(key)
        if cached_result is not None:
            # If the schema changed due to an ongoing compilation, the name
            # hint might be outdated.
            if cached_result.name_hint == t.get_name(schema):
                return cached_result

    if t.is_anytuple(schema):
        result = irast.AnyTupleRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif t.is_any(schema):
        result = irast.AnyTypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif not isinstance(t, s_types.Collection):
        assert isinstance(t, s_types.InheritingType)
        union_of = t.get_union_of(schema)
        if union_of:
            non_overlapping, union_is_concrete = (
                s_utils.get_non_overlapping_union(
                    schema,
                    union_of.objects(schema),
                )
            )
            union = frozenset(
                type_to_typeref(schema, c, cache=cache)
                for c in non_overlapping
            )
        else:
            union_is_concrete = False
            union = frozenset()

        intersection_of = t.get_intersection_of(schema)
        if intersection_of:
            intersection = frozenset(
                type_to_typeref(schema, c, cache=cache)
                for c in intersection_of.objects(schema)
            )
        else:
            intersection = frozenset()

        schema, material_type = t.material_type(schema)

        material_typeref: Optional[irast.TypeRef]
        if material_type != t:
            material_typeref = type_to_typeref(
                schema,
                material_type,
                include_descendants=include_descendants,
                include_ancestors=include_ancestors,
                cache=cache,
            )
        else:
            material_typeref = None

        if (isinstance(material_type, s_scalars.ScalarType)
                and not material_type.get_abstract(schema)):
            base_type = material_type.get_topmost_concrete_base(schema)
            if base_type == material_type:
                base_typeref = None
            else:
                assert isinstance(base_type, s_types.Type)
                base_typeref = type_to_typeref(
                    schema, base_type, cache=cache
                )
        else:
            base_typeref = None

        tname = t.get_name(schema)
        if typename is not None:
            name = typename
        else:
            name = tname

        common_parent_ref: Optional[irast.TypeRef]
        if union_of:
            common_parent = s_utils.get_class_nearest_common_ancestor(
                schema, union_of.objects(schema))
            assert isinstance(common_parent, s_types.Type)
            common_parent_ref = type_to_typeref(
                schema, common_parent, cache=cache
            )
        else:
            common_parent_ref = None

        descendants: Optional[FrozenSet[irast.TypeRef]]

        if material_typeref is None and include_descendants:
            descendants = frozenset(
                type_to_typeref(
                    schema,
                    child,
                    cache=cache,
                    include_descendants=True,
                    include_ancestors=include_ancestors,
                )
                for child in t.children(schema)
                if not child.get_is_derived(schema)
            )
        else:
            descendants = None

        ancestors: Optional[FrozenSet[irast.TypeRef]]
        if material_typeref is None and include_ancestors:
            ancestors = frozenset(
                type_to_typeref(
                    schema,
                    ancestor,
                    cache=cache,
                    include_descendants=include_descendants,
                    include_ancestors=False
                )
                for ancestor in t.get_ancestors(schema).objects(schema)
            )
        else:
            ancestors = None

        result = irast.TypeRef(
            id=t.id,
            name_hint=name,
            material_type=material_typeref,
            base_type=base_typeref,
            descendants=descendants,
            ancestors=ancestors,
            union=union,
            union_is_concrete=union_is_concrete,
            intersection=intersection,
            common_parent=common_parent_ref,
            element_name=_name,
            is_scalar=t.is_scalar(),
            is_abstract=t.get_abstract(schema),
            is_view=t.is_view(schema),
            is_opaque_union=t.get_is_opaque_union(schema),
        )
    elif isinstance(t, s_types.Tuple) and t.is_named(schema):
        schema, material_type = t.material_type(schema)

        if material_type != t:
            material_typeref = type_to_typeref(
                schema, material_type, cache=cache
            )
        else:
            material_typeref = None

        result = irast.TypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
            material_type=material_typeref,
            element_name=_name,
            collection=t.schema_name,
            in_schema=t.get_is_persistent(schema),
            subtypes=tuple(
                type_to_typeref(schema, st, _name=sn)  # note: no cache
                for sn, st in t.iter_subtypes(schema)
            )
        )
    else:
        schema, material_type = t.material_type(schema)

        if material_type != t:
            material_typeref = type_to_typeref(
                schema, material_type, cache=cache
            )
        else:
            material_typeref = None

        result = irast.TypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
            material_type=material_typeref,
            element_name=_name,
            collection=t.schema_name,
            in_schema=t.get_is_persistent(schema),
            subtypes=tuple(
                type_to_typeref(schema, st, cache=cache)
                for st in t.get_subtypes(schema)
            )
        )

    if cache is not None and typename is None and _name is None:
        # Note: there is no cache for `_name` variants since they are only used
        # for Tuple subtypes and thus they will be cached on the outer level
        # anyway.
        # There's also no variant for types with custom typenames since they
        # proved to have a very low hit rate.
        # This way we save on the size of the key tuple.
        cache[key] = result
    return result
Beispiel #3
0
def type_to_typeref(
    schema: s_schema.Schema,
    t: s_types.Type,
    *,
    cache: Optional[Dict[uuid.UUID, irast.TypeRef]] = None,
    typename: Optional[s_name.Name] = None,
    _name: Optional[str] = None,
) -> irast.TypeRef:
    """Return an instance of :class:`ir.ast.TypeRef` for a given type.

    An IR TypeRef is an object that fully describes a schema type for
    the purposes of query compilation.

    Args:
        schema:
            A schema instance, in which the type *t* is defined.
        t:
            A schema type instance.
        cache:
            Optional mapping from (type UUID, typename) to cached IR TypeRefs.
        typename:
            Optional name hint to use for the type in the returned
            TypeRef.  If ``None``, the type name is used.
        _name:
            Optional subtype element name if this type is a collection within
            a Tuple,

    Returns:
        A ``TypeRef`` instance corresponding to the given schema type.
    """

    result: irast.TypeRef

    if cache is not None and typename is None:
        cached_result = cache.get(t.id)
        if cached_result is not None:
            # If the schema changed due to an ongoing compilation, the name
            # hint might be outdated.
            if cached_result.name_hint == t.get_name(schema):
                return cached_result

    if t.is_anytuple():
        result = irast.AnyTupleRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif t.is_any():
        result = irast.AnyTypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif not isinstance(t, s_abc.Collection):
        union_of = t.get_union_of(schema)
        if union_of:
            non_overlapping, union_is_concrete = (
                s_utils.get_non_overlapping_union(
                    schema,
                    union_of.objects(schema),
                ))
            union = frozenset(
                type_to_typeref(schema, c, cache=cache)
                for c in non_overlapping)
        else:
            union_is_concrete = False
            union = frozenset()

        intersection_of = t.get_intersection_of(schema)
        if intersection_of:
            intersection = frozenset(
                type_to_typeref(schema, c, cache=cache)
                for c in intersection_of.objects(schema))
        else:
            intersection = frozenset()

        material_type = t.material_type(schema)

        material_typeref: Optional[irast.TypeRef]
        if material_type is not t:
            material_typeref = type_to_typeref(schema,
                                               material_type,
                                               cache=cache)
        else:
            material_typeref = None

        if (isinstance(material_type, s_scalars.ScalarType)
                and not material_type.get_is_abstract(schema)):
            base_type = material_type.get_topmost_concrete_base(schema)
            if base_type is material_type:
                base_typeref = None
            else:
                assert isinstance(base_type, s_types.Type)
                base_typeref = type_to_typeref(schema, base_type, cache=cache)
        else:
            base_typeref = None

        if typename is not None:
            name = typename
        else:
            name = t.get_name(schema)
        module = schema.get_global(s_mod.Module, name.module)

        common_parent_ref: Optional[irast.TypeRef]
        if union_of:
            common_parent = s_utils.get_class_nearest_common_ancestor(
                schema, union_of.objects(schema))
            assert isinstance(common_parent, s_types.Type)
            common_parent_ref = type_to_typeref(schema,
                                                common_parent,
                                                cache=cache)
        else:
            common_parent_ref = None

        result = irast.TypeRef(
            id=t.id,
            module_id=module.id,
            name_hint=name,
            material_type=material_typeref,
            base_type=base_typeref,
            union=union,
            union_is_concrete=union_is_concrete,
            intersection=intersection,
            common_parent=common_parent_ref,
            element_name=_name,
            is_scalar=t.is_scalar(),
            is_abstract=t.get_is_abstract(schema),
            is_view=t.is_view(schema),
            is_opaque_union=t.get_is_opaque_union(schema),
        )
    elif isinstance(t, s_abc.Tuple) and t.named:
        result = irast.TypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
            element_name=_name,
            collection=t.schema_name,
            in_schema=schema.get_by_id(t.id, None) is not None,
            subtypes=tuple(
                type_to_typeref(schema, st, _name=sn)  # note: no cache
                for sn, st in t.iter_subtypes(schema)))
    else:
        result = irast.TypeRef(id=t.id,
                               name_hint=typename or t.get_name(schema),
                               element_name=_name,
                               collection=t.schema_name,
                               in_schema=schema.get_by_id(t.id, None)
                               is not None,
                               subtypes=tuple(
                                   type_to_typeref(schema, st, cache=cache)
                                   for st in t.get_subtypes(schema)))

    if cache is not None and typename is None and _name is None:
        # Note: there is no cache for `_name` variants since they are only used
        # for Tuple subtypes and thus they will be cached on the outer level
        # anyway.
        # There's also no variant for types with custom typenames since they
        # proved to have a very low hit rate.
        # This way we save on the size of the key tuple.
        cache[t.id] = result
    return result
Beispiel #4
0
def type_to_typeref(schema,
                    t: s_types.Type,
                    *,
                    _name=None,
                    typename=None) -> irast.TypeRef:

    if t.is_anytuple():
        result = irast.AnyTupleRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif t.is_any():
        result = irast.AnyTypeRef(
            id=t.id,
            name_hint=typename or t.get_name(schema),
        )
    elif not isinstance(t, s_abc.Collection):
        union_of = t.get_union_of(schema)
        if union_of:
            children = frozenset(
                type_to_typeref(schema, c) for c in union_of.objects(schema))
        else:
            children = frozenset()

        material_type = t.material_type(schema)
        if material_type is not t:
            material_typeref = type_to_typeref(schema, material_type)
        else:
            material_typeref = None

        if (material_type.is_scalar()
                and not material_type.get_is_abstract(schema)):
            base_type = material_type.get_topmost_concrete_base(schema)
            if base_type is material_type:
                base_typeref = None
            else:
                base_typeref = type_to_typeref(schema, base_type)
        else:
            base_typeref = None

        if typename is not None:
            name = typename
        else:
            name = t.get_name(schema)
        module = schema.get_global(s_mod.Module, name.module)

        if union_of:
            common_parent = s_utils.get_class_nearest_common_ancestor(
                schema, union_of.objects(schema))
            common_parent_ref = type_to_typeref(schema, common_parent)
        else:
            common_parent_ref = None

        result = irast.TypeRef(
            id=t.id,
            module_id=module.id,
            name_hint=name,
            material_type=material_typeref,
            base_type=base_typeref,
            children=children,
            common_parent=common_parent_ref,
            element_name=_name,
            is_scalar=t.is_scalar(),
            is_abstract=t.get_is_abstract(schema),
            is_view=t.is_view(schema),
            is_opaque_union=t.get_is_opaque_union(schema),
        )
    elif isinstance(t, s_abc.Tuple) and t.named:
        result = irast.TypeRef(id=t.id,
                               name_hint=typename or t.get_name(schema),
                               element_name=_name,
                               collection=t.schema_name,
                               in_schema=schema.get_by_id(t.id, None)
                               is not None,
                               subtypes=tuple(
                                   type_to_typeref(schema, st, _name=sn)
                                   for sn, st in t.iter_subtypes(schema)))
    else:
        result = irast.TypeRef(id=t.id,
                               name_hint=typename or t.get_name(schema),
                               element_name=_name,
                               collection=t.schema_name,
                               in_schema=schema.get_by_id(t.id, None)
                               is not None,
                               subtypes=tuple(
                                   type_to_typeref(schema, st)
                                   for st in t.get_subtypes(schema)))

    return result