Beispiel #1
0
def type_to_ql_typeref(t: s_types.Type,
                       *,
                       _name=None,
                       schema: s_schema.Schema) -> qlast.TypeName:
    if t.is_any():
        result = qlast.TypeName(name=_name, maintype=qlast.AnyType())
    elif t.is_anytuple():
        result = qlast.TypeName(name=_name, maintype=qlast.AnyTuple())
    elif not isinstance(t, s_abc.Collection):
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(
                                    module=t.get_name(schema).module,
                                    name=t.get_name(schema).name))
    elif isinstance(t, s_abc.Tuple) and t.named:
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    type_to_ql_typeref(st,
                                                       _name=sn,
                                                       schema=schema)
                                    for sn, st in t.iter_subtypes(schema)
                                ])
    else:
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    type_to_ql_typeref(st, schema=schema)
                                    for st in t.get_subtypes(schema)
                                ])

    return result
Beispiel #2
0
def new_set(
    *,
    stype: s_types.Type,
    ctx: context.ContextLevel,
    ircls: Type[irast.Set] = irast.Set,
    **kwargs: Any,
) -> irast.Set:
    """Create a new ir.Set instance with given attributes.

    Absolutely all ir.Set instances must be created using this
    constructor.
    """
    if (stype not in ctx.type_rewrites
            and isinstance(stype, s_objtypes.ObjectType)
            and ctx.env.options.apply_query_rewrites
            and (filters := stype.get_access_policy_filters(ctx.env.schema))):
        qry = qlast.SelectQuery(result=qlast.Path(
            steps=[s_utils.name_to_ast_ref(stype.get_name(ctx.env.schema))]), )
        for f in filters:
            assert isinstance(f.qlast, qlast.Expr)
            qry.where = astutils.extend_binop(qry.where, f.qlast)

        with ctx.detached() as subctx:
            subctx.expr_exposed = False
            # This is a global rewrite operation that is done once
            # per type, and so we don't really care if we're in a
            # temporary scope or not.
            subctx.path_scope = subctx.env.path_scope.root
            subctx.in_temp_scope = False
            # Put a placeholder to prevent recursion.
            subctx.type_rewrites[stype] = irast.Set()
            filtered_set = dispatch.compile(qry, ctx=subctx)
            assert isinstance(filtered_set, irast.Set)
            subctx.type_rewrites[stype] = filtered_set
Beispiel #3
0
def declare_view_from_schema(viewcls: s_types.Type, *,
                             ctx: context.ContextLevel) -> s_types.Type:
    vc = ctx.env.schema_view_cache.get(viewcls)
    if vc is not None:
        return vc

    with ctx.detached() as subctx:
        subctx.expr_exposed = False
        view_expr = viewcls.get_expr(ctx.env.schema)
        assert view_expr is not None
        view_ql = qlparser.parse(view_expr.text)
        viewcls_name = viewcls.get_name(ctx.env.schema)
        assert isinstance(view_ql, qlast.Expr), 'expected qlast.Expr'
        view_set = declare_view(view_ql,
                                alias=viewcls_name,
                                fully_detached=True,
                                ctx=subctx)
        # The view path id _itself_ should not be in the nested namespace.
        view_set.path_id = view_set.path_id.replace_namespace(
            ctx.path_id_namespace)

        vc = subctx.aliased_views[viewcls_name]
        assert vc is not None
        ctx.env.schema_view_cache[viewcls] = vc
        ctx.source_map.update(subctx.source_map)
        ctx.aliased_views[viewcls_name] = subctx.aliased_views[viewcls_name]
        ctx.view_nodes[vc.get_name(ctx.env.schema)] = vc
        ctx.view_sets[vc] = subctx.view_sets[vc]

    return vc
Beispiel #4
0
def declare_view_from_schema(viewcls: s_types.Type, *,
                             ctx: context.ContextLevel) -> s_types.Type:
    vc = ctx.env.schema_view_cache.get(viewcls)
    if vc is not None:
        return vc

    with ctx.detached() as subctx:
        subctx.expr_exposed = False
        view_expr = viewcls.get_expr(ctx.env.schema)
        assert view_expr is not None
        view_ql = qlparser.parse(view_expr.text)
        viewcls_name = viewcls.get_name(ctx.env.schema)
        assert isinstance(view_ql, qlast.Expr), 'expected qlast.Expr'
        view_set = declare_view(view_ql,
                                alias=viewcls_name,
                                fully_detached=True,
                                ctx=subctx)
        # The view path id _itself_ should not be in the nested namespace.
        view_set.path_id = view_set.path_id.replace_namespace(
            ctx.path_id_namespace)

        vc = subctx.aliased_views[viewcls_name]
        assert vc is not None
        if not ctx.in_temp_scope:
            ctx.env.schema_view_cache[viewcls] = vc
        ctx.source_map.update(subctx.source_map)
        ctx.aliased_views[viewcls_name] = subctx.aliased_views[viewcls_name]
        ctx.view_nodes[vc.get_name(ctx.env.schema)] = vc
        ctx.view_sets[vc] = subctx.view_sets[vc]

        # XXX: The current cardinality inference machine does not look
        # into unreferenced expression parts, which includes computables
        # that may be declared on an alias that another alias is referencing,
        # leaving Unknown cardinalities in place.  To fix this, copy
        # cardinalities for computed pointers from the alias object in the
        # schema.
        view_type = setgen.get_set_type(view_set, ctx=subctx)
        if isinstance(view_type, s_objtypes.ObjectType):
            assert isinstance(viewcls, s_objtypes.ObjectType)
            _fixup_cardinalities(
                view_type,
                viewcls,
                ctx=ctx,
            )

    return vc
Beispiel #5
0
def object_type_to_python_type(
        objtype: s_types.Type,
        schema: s_schema.Schema,
        *,
        base_class: typing.Optional[type] = None,
        _memo: typing.Optional[typing.Mapping[s_types.Type,
                                              type]] = None) -> type:

    if _memo is None:
        _memo = {}

    fields = []
    subclasses = []

    for pn, p in objtype.get_pointers(schema).items(schema):
        if pn in ('id', '__type__'):
            continue

        ptype = p.get_target(schema)

        if ptype.is_object_type():
            pytype = _memo.get(ptype)
            if pytype is None:
                pytype = object_type_to_python_type(ptype,
                                                    schema,
                                                    base_class=base_class,
                                                    _memo=_memo)
                _memo[ptype] = pytype

                for subtype in ptype.children(schema):
                    subclasses.append(
                        object_type_to_python_type(subtype,
                                                   schema,
                                                   base_class=pytype,
                                                   _memo=_memo))
        else:
            pytype = scalar_type_to_python_type(ptype, schema)

        is_multi = p.get_cardinality(schema) is qltypes.Cardinality.MANY
        if is_multi:
            pytype = typing.FrozenSet[pytype]

        default = p.get_default(schema)
        if default is None:
            if p.get_required(schema):
                default = dataclasses.MISSING
        else:
            default = ql_compiler.evaluate_to_python_val(default.text,
                                                         schema=schema)
            if is_multi and not isinstance(default, frozenset):
                default = frozenset((default, ))

        constraints = p.get_constraints(schema).objects(schema)
        exclusive = schema.get('std::exclusive')
        unique = (not ptype.is_object_type() and any(
            c.issubclass(schema, exclusive) for c in constraints))
        field = dataclasses.field(
            compare=unique,
            hash=unique,
            repr=True,
            default=default,
        )
        fields.append((pn, pytype, field))

    return dataclasses.make_dataclass(
        objtype.get_name(schema).name,
        fields=fields,
        bases=(base_class, ) if base_class is not None else (),
        frozen=True,
        namespace={'_subclasses': subclasses},
    )
Beispiel #6
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 #7
0
def derive_view(
        stype: s_types.Type, *,
        derived_name: typing.Optional[sn.SchemaName]=None,
        derived_name_quals: typing.Optional[typing.Sequence[str]]=(),
        derived_name_base: typing.Optional[str]=None,
        preserve_shape: bool=False,
        preserve_path_id: bool=False,
        is_insert: bool=False,
        is_update: bool=False,
        inheritance_merge: bool=True,
        attrs: typing.Optional[dict]=None,
        ctx: context.ContextLevel) -> s_types.Type:

    if derived_name is None:
        derived_name = derive_view_name(
            stype=stype, derived_name_quals=derived_name_quals,
            derived_name_base=derived_name_base, ctx=ctx)

    if is_insert:
        vtype = s_types.ViewType.Insert
    elif is_update:
        vtype = s_types.ViewType.Update
    else:
        vtype = s_types.ViewType.Select

    if attrs is None:
        attrs = {}
    else:
        attrs = dict(attrs)

    attrs['view_type'] = vtype

    derived: s_types.Type

    if isinstance(stype, s_abc.Collection):
        ctx.env.schema, derived = stype.derive_subtype(
            ctx.env.schema, name=derived_name)

    elif isinstance(stype, s_inh.InheritingObject):
        qualifiers: typing.Tuple[str, ...] = ()
        if stype.get_name(ctx.env.schema) == derived_name:
            qualifiers = (ctx.aliases.get('d'),)

        ctx.env.schema, derived = stype.derive(
            ctx.env.schema,
            stype,
            *qualifiers,
            name=derived_name,
            inheritance_merge=inheritance_merge,
            refdict_whitelist={'pointers'},
            mark_derived=True,
            preserve_path_id=preserve_path_id,
            attrs=attrs)

        if (not stype.generic(ctx.env.schema)
                and isinstance(derived, s_sources.Source)):
            scls_pointers = stype.get_pointers(ctx.env.schema)
            derived_own_pointers = derived.get_pointers(ctx.env.schema)

            for pn, ptr in derived_own_pointers.items(ctx.env.schema):
                # This is a view of a view.  Make sure query-level
                # computable expressions for pointers are carried over.
                src_ptr = scls_pointers.get(ctx.env.schema, pn)
                computable_data = ctx.source_map.get(src_ptr)
                if computable_data is not None:
                    ctx.source_map[ptr] = computable_data

    ctx.view_nodes[derived.get_name(ctx.env.schema)] = derived

    if preserve_shape and stype in ctx.env.view_shapes:
        ctx.env.view_shapes[derived] = ctx.env.view_shapes[stype]

    return derived
Beispiel #8
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 #9
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