Ejemplo n.º 1
0
def evaluate_TypeCast(
        ir_cast: irast.TypeCast,
        schema: s_schema.Schema) -> EvaluationResult:

    schema, from_type = irtyputils.ir_typeref_to_type(
        schema, ir_cast.from_type)
    schema, to_type = irtyputils.ir_typeref_to_type(
        schema, ir_cast.from_type)
    schema_type_to_python_type(from_type, schema)
    schema_type_to_python_type(to_type, schema)
    evaluate(ir_cast.expr, schema)
    return ir_cast
Ejemplo n.º 2
0
    def _get_ref_storage_info(cls, schema, refs):
        link_biased = {}
        objtype_biased = {}

        ref_ptrs = {}
        for ref in refs:
            rptr = ref.rptr
            if rptr is not None:
                ptrref = ref.rptr.ptrref
                ptr = irtyputils.ptrcls_from_ptrref(ptrref, schema=schema)
                if ptr.is_link_property(schema):
                    srcref = ref.rptr.source.rptr.ptrref
                    src = irtyputils.ptrcls_from_ptrref(srcref, schema=schema)
                    if src.get_is_derived(schema):
                        # This specialized pointer was derived specifically
                        # for the purposes of constraint expr compilation.
                        src = src.get_bases(schema).first(schema)
                else:
                    src = irtyputils.ir_typeref_to_type(
                        schema, ref.rptr.source.typeref)
                ref_ptrs[ref] = (ptr, src)

        for ref, (ptr, src) in ref_ptrs.items():
            ptr_info = types.get_pointer_storage_info(
                ptr, source=src, resolve_type=False, schema=schema)

            # See if any of the refs are hosted in pointer tables and others
            # are not...
            if ptr_info.table_type == 'link':
                link_biased[ref] = ptr_info
            else:
                objtype_biased[ref] = ptr_info

            if link_biased and objtype_biased:
                break

        if link_biased and objtype_biased:
            for ref in objtype_biased.copy():
                ptr, src = ref_ptrs[ref]
                ptr_info = types.get_pointer_storage_info(
                    ptr, source=src, resolve_type=False, link_bias=True,
                    schema=schema)

                if ptr_info.table_type == 'link':
                    link_biased[ref] = ptr_info
                    objtype_biased.pop(ref)

        ref_tables = {}

        for ref, ptr_info in itertools.chain(
                objtype_biased.items(), link_biased.items()):
            ptr, src = ref_ptrs[ref]

            try:
                ref_tables[ptr_info.table_name].append(
                    (ref, ptr, src, ptr_info))
            except KeyError:
                ref_tables[ptr_info.table_name] = [(ref, ptr, src, ptr_info)]

        return ref_tables
Ejemplo n.º 3
0
def compile_type_check_op(expr: qlast.IsOp, *,
                          ctx: context.ContextLevel) -> irast.TypeCheckOp:
    # <Expr> IS <TypeExpr>
    left = setgen.ensure_set(dispatch.compile(expr.left, ctx=ctx), ctx=ctx)
    ltype = setgen.get_set_type(left, ctx=ctx)
    typeref = typegen.ql_typeexpr_to_ir_typeref(expr.right, ctx=ctx)

    if ltype.is_object_type():
        left = setgen.ptr_step_set(left,
                                   source=ltype,
                                   ptr_name='__type__',
                                   source_context=expr.context,
                                   ctx=ctx)
        pathctx.register_set_in_scope(left, ctx=ctx)
        result = None
    else:
        if (ltype.is_collection() and typing.cast(
                s_types.Collection, ltype).contains_object(ctx.env.schema)):
            raise errors.QueryError(
                f'type checks on non-primitive collections are not supported')

        ctx.env.schema, test_type = (irtyputils.ir_typeref_to_type(
            ctx.env.schema, typeref))
        result = ltype.issubclass(ctx.env.schema, test_type)

    return irast.TypeCheckOp(left=left,
                             right=typeref,
                             op=expr.op,
                             result=result)
Ejemplo n.º 4
0
def cast_const_to_python(
        ir: irast.TypeCast,
        schema: s_schema.Schema) -> Any:

    stype = irtyputils.ir_typeref_to_type(schema, ir.to_type)
    pytype = scalar_type_to_python_type(stype, schema)
    sval = evaluate_to_python_val(ir.expr, schema=schema)
    return pytype(sval)
Ejemplo n.º 5
0
def cast_const_to_python(ir: irast.TypeCast, schema: s_schema.Schema) -> Any:

    schema, stype = irtyputils.ir_typeref_to_type(schema, ir.to_type)
    pytype = scalar_type_to_python_type(stype, schema)
    sval = evaluate_to_python_val(ir.expr, schema=schema)
    if sval is None:
        return None
    elif isinstance(sval, tuple):
        return tuple(pytype(elem) for elem in sval)
    else:
        return pytype(sval)
Ejemplo n.º 6
0
def __infer_array(ir, env):
    if ir.typeref is not None:
        return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
    elif ir.elements:
        element_type = _infer_common_type(ir.elements, env)
        if element_type is None:
            raise errors.QueryError('could not determine array type',
                                    context=ir.context)
    else:
        element_type = s_pseudo.Any.instance

    return s_types.Array.create(env.schema, element_type=element_type)
Ejemplo n.º 7
0
    def _visit_path(self, node):
        steps = []

        while node.rptr:
            if node.show_as_anchor and not self.context.inline_anchors:
                break
            if isinstance(node.rptr.ptrref, irast.TypeIndirectionPointerRef):
                ttype = irtyputils.ir_typeref_to_type(self.context.schema,
                                                      node.typeref)
                steps.append(
                    qlast.TypeIndirection(type=typegen.type_to_ql_typeref(
                        ttype, ctx=self.context)))
            else:
                rptr = node.rptr
                ptrref = rptr.ptrref
                pname = ptrref.shortname

                link = qlast.Ptr(
                    ptr=qlast.ObjectRef(name=pname.name, ),
                    direction=rptr.direction,
                )
                if ptrref.parent_ptr is not None:
                    link.type = 'property'
                steps.append(link)

            node = node.rptr.source

        if node.show_as_anchor and not self.context.inline_anchors:
            if issubclass(node.show_as_anchor, qlast.Expr):
                step = node.show_as_anchor()
            else:
                step = qlast.ObjectRef(name=node.show_as_anchor)
        else:
            if node.typeref.material_type is not None:
                typeref = node.typeref.material_type
            else:
                typeref = node.typeref

            stype = self.context.schema.get_by_id(typeref.id)
            scls_shortname = stype.get_shortname(self.context.schema)
            step = qlast.ObjectRef(name=scls_shortname.name,
                                   module=scls_shortname.module)

        return qlast.Path(steps=[step] + list(reversed(steps)))
Ejemplo n.º 8
0
def get_tuple_indirection_path_id(
        tuple_path_id: irast.PathId, element_name: str,
        element_type: s_types.Type, *,
        ctx: context.ContextLevel) -> irast.PathId:

    ptrcls = irast.TupleIndirectionLink(
        irtyputils.ir_typeref_to_type(ctx.env.schema, tuple_path_id.target),
        element_type,
        element_name=element_name,
    )

    ptrref = irtyputils.ptrref_from_ptrcls(
        schema=ctx.env.schema,
        ptrcls=ptrcls,
        # FIXME: caching disabled here since it breaks tests
        # cache=ctx.env.ptr_ref_cache,
        # typeref_cache=ctx.env.type_ref_cache,
    )

    return tuple_path_id.extend(schema=ctx.env.schema, ptrref=ptrref)
Ejemplo n.º 9
0
def __infer_array(
    ir: irast.Array,
    env: context.Environment,
) -> s_types.Type:
    if ir.typeref is not None:
        env.schema, t = irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
        return t
    elif ir.elements:
        element_type = _infer_common_type(ir.elements, env)
        if element_type is None:
            raise errors.QueryError('could not determine array type',
                                    context=ir.context)
    else:
        element_type = s_pseudo.PseudoType.get(env.schema, 'anytype')

    env.schema, arr_t = s_types.Array.create(
        env.schema,
        element_type=element_type,
    )

    return arr_t
Ejemplo n.º 10
0
def compile_insert_unless_conflict(
    stmt: irast.InsertStmt,
    insert_subject: qlast.Path,
    *, ctx: context.ContextLevel,
) -> irast.OnConflictClause:
    """Compile an UNLESS CONFLICT clause with no ON

    This requires synthesizing a conditional based on all the exclusive
    constraints on the object.
    """

    schema = ctx.env.schema

    schema, typ = typeutils.ir_typeref_to_type(schema, stmt.subject.typeref)
    assert isinstance(typ, s_objtypes.ObjectType)
    real_typ = typ.get_nearest_non_derived_parent(schema)
    pointers = {}

    exclusive_constr = schema.get('std::exclusive', type=s_constr.Constraint)
    for ptr in typ.get_pointers(schema).objects(schema):
        ptr = ptr.get_nearest_non_derived_parent(schema)
        ex_cnstrs = [c for c in ptr.get_constraints(schema).objects(schema)
                     if c.issubclass(schema, exclusive_constr)]
        if ex_cnstrs:
            name = ptr.get_shortname(schema).name
            if name != 'id':
                pointers[name] = ptr, ex_cnstrs

    ctx.env.schema = schema

    obj_constrs = real_typ.get_constraints(schema).objects(schema)

    select_ir = compile_insert_unless_conflict_select(
        stmt, insert_subject, real_typ,
        constrs=pointers,
        obj_constrs=obj_constrs,
        parser_context=stmt.context, ctx=ctx)

    return irast.OnConflictClause(
        constraint=None, select_ir=select_ir, else_ir=None)
Ejemplo n.º 11
0
def _normalize_view_ptr_expr(
        shape_el: qlast.ShapeElement,
        view_scls: s_objtypes.ObjectType, *,
        path_id: irast.PathId,
        path_id_namespace: Optional[irast.WeakNamespace]=None,
        is_insert: bool=False,
        is_update: bool=False,
        from_default: bool=False,
        view_rptr: Optional[context.ViewRPtr]=None,
        ctx: context.ContextLevel) -> s_pointers.Pointer:
    steps = shape_el.expr.steps
    is_linkprop = False
    is_polymorphic = False
    is_mutation = is_insert or is_update
    # Pointers may be qualified by the explicit source
    # class, which is equivalent to Expr[IS Type].
    plen = len(steps)
    ptrsource: s_sources.Source = view_scls
    qlexpr: Optional[qlast.Expr] = None
    target_typexpr = None
    source: qlast.Base
    base_ptrcls_is_alias = False

    if plen >= 2 and isinstance(steps[-1], qlast.TypeIntersection):
        # Target type intersection: foo: Type
        target_typexpr = steps[-1].type
        plen -= 1
        steps = steps[:-1]

    if plen == 1:
        # regular shape
        lexpr = steps[0]
        assert isinstance(lexpr, qlast.Ptr)
        is_linkprop = lexpr.type == 'property'
        if is_linkprop:
            if view_rptr is None or view_rptr.ptrcls is None:
                raise errors.QueryError(
                    'invalid reference to link property '
                    'in top level shape', context=lexpr.context)
            assert isinstance(view_rptr.ptrcls, s_links.Link)
            ptrsource = view_rptr.ptrcls
        source = qlast.Source()
    elif plen == 2 and isinstance(steps[0], qlast.TypeIntersection):
        # Source type intersection: [IS Type].foo
        source = qlast.Path(steps=[
            qlast.Source(),
            steps[0],
        ])
        lexpr = steps[1]
        ptype = steps[0].type
        if not isinstance(ptype, qlast.TypeName):
            raise errors.QueryError(
                'complex type expressions are not supported here',
                context=ptype.context,
            )
        source_spec = schemactx.get_schema_type(ptype.maintype, ctx=ctx)
        if not isinstance(source_spec, s_objtypes.ObjectType):
            raise errors.QueryError(
                f'expected object type, got '
                f'{source_spec.get_verbosename(ctx.env.schema)}',
                context=ptype.context,
            )
        ptrsource = source_spec
        is_polymorphic = True
    else:  # pragma: no cover
        raise RuntimeError(
            f'unexpected path length in view shape: {len(steps)}')

    assert isinstance(lexpr, qlast.Ptr)
    ptrname = lexpr.ptr.name

    compexpr: Optional[qlast.Expr] = shape_el.compexpr
    if compexpr is None and is_insert and shape_el.elements:
        # Short shape form in INSERT, e.g
        #     INSERT Foo { bar: Spam { name := 'name' }}
        # is prohibited.
        raise errors.EdgeQLSyntaxError(
            "unexpected ':'", context=steps[-1].context)

    ptrcls: Optional[s_pointers.Pointer]

    if compexpr is None:
        ptrcls = setgen.resolve_ptr(
            ptrsource, ptrname, track_ref=lexpr, ctx=ctx)
        if is_polymorphic:
            ptrcls = schemactx.derive_ptr(
                ptrcls, view_scls,
                is_insert=is_insert,
                is_update=is_update,
                ctx=ctx)

        base_ptrcls = ptrcls.get_bases(ctx.env.schema).first(ctx.env.schema)
        base_ptr_is_computable = base_ptrcls in ctx.source_map
        ptr_name = sn.QualName(
            module='__',
            name=ptrcls.get_shortname(ctx.env.schema).name,
        )

        base_cardinality = _get_base_ptr_cardinality(base_ptrcls, ctx=ctx)
        base_is_singleton = False
        if base_cardinality is not None and base_cardinality.is_known():
            base_is_singleton = base_cardinality.is_single()

        if (
            shape_el.where
            or shape_el.orderby
            or shape_el.offset
            or shape_el.limit
            or base_ptr_is_computable
            or is_polymorphic
            or target_typexpr is not None
            or (ctx.implicit_limit and not base_is_singleton)
        ):

            if target_typexpr is None:
                qlexpr = qlast.Path(steps=[source, lexpr])
            else:
                qlexpr = qlast.Path(steps=[
                    source,
                    lexpr,
                    qlast.TypeIntersection(type=target_typexpr),
                ])

            qlexpr = astutils.ensure_qlstmt(qlexpr)
            assert isinstance(qlexpr, qlast.SelectQuery)
            qlexpr.where = shape_el.where
            qlexpr.orderby = shape_el.orderby

            if shape_el.offset or shape_el.limit:
                qlexpr = qlast.SelectQuery(result=qlexpr, implicit=True)
                qlexpr.offset = shape_el.offset
                qlexpr.limit = shape_el.limit

            if (
                (ctx.expr_exposed or ctx.stmt is ctx.toplevel_stmt)
                and not qlexpr.limit
                and ctx.implicit_limit
                and not base_is_singleton
            ):
                qlexpr.limit = qlast.IntegerConstant(
                    value=str(ctx.implicit_limit),
                )

        if target_typexpr is not None:
            assert isinstance(target_typexpr, qlast.TypeName)
            intersector_type = schemactx.get_schema_type(
                target_typexpr.maintype, ctx=ctx)

            int_result = schemactx.apply_intersection(
                ptrcls.get_target(ctx.env.schema),  # type: ignore
                intersector_type,
                ctx=ctx,
            )

            ptr_target = int_result.stype
        else:
            _ptr_target = ptrcls.get_target(ctx.env.schema)
            assert _ptr_target
            ptr_target = _ptr_target

        ptr_cardinality = base_cardinality
        if ptr_cardinality is None or not ptr_cardinality.is_known():
            # We do not know the parent's pointer cardinality yet.
            ctx.env.pointer_derivation_map[base_ptrcls].append(ptrcls)
            ctx.env.pointer_specified_info[ptrcls] = (
                shape_el.cardinality, shape_el.required, shape_el.context)

        implicit_tid = has_implicit_type_computables(
            ptr_target,
            is_mutation=is_mutation,
            ctx=ctx,
        )

        if shape_el.elements or implicit_tid:
            sub_view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                is_insert=is_insert,
                is_update=is_update)

            sub_path_id = pathctx.extend_path_id(
                path_id,
                ptrcls=base_ptrcls,
                ns=ctx.path_id_namespace,
                ctx=ctx)

            ctx.path_scope.attach_path(sub_path_id,
                                       context=shape_el.context)

            if not isinstance(ptr_target, s_objtypes.ObjectType):
                raise errors.QueryError(
                    f'shapes cannot be applied to '
                    f'{ptr_target.get_verbosename(ctx.env.schema)}',
                    context=shape_el.context,
                )

            if is_update:
                for subel in shape_el.elements or []:
                    is_prop = (
                        isinstance(subel.expr.steps[0], qlast.Ptr) and
                        subel.expr.steps[0].type == 'property'
                    )
                    if not is_prop:
                        raise errors.QueryError(
                            'only references to link properties are allowed '
                            'in nested UPDATE shapes', context=subel.context)

                ptr_target = _process_view(
                    stype=ptr_target, path_id=sub_path_id,
                    path_id_namespace=path_id_namespace,
                    view_rptr=sub_view_rptr,
                    elements=shape_el.elements, is_update=True,
                    parser_context=shape_el.context,
                    ctx=ctx)
            else:
                ptr_target = _process_view(
                    stype=ptr_target, path_id=sub_path_id,
                    path_id_namespace=path_id_namespace,
                    view_rptr=sub_view_rptr,
                    elements=shape_el.elements,
                    parser_context=shape_el.context,
                    ctx=ctx)

    else:
        base_ptrcls = ptrcls = None

        if (is_mutation
                and ptrname not in ctx.special_computables_in_mutation_shape):
            # If this is a mutation, the pointer must exist.
            ptrcls = setgen.resolve_ptr(
                ptrsource, ptrname, track_ref=lexpr, ctx=ctx)

            base_ptrcls = ptrcls.get_bases(
                ctx.env.schema).first(ctx.env.schema)

            ptr_name = sn.QualName(
                module='__',
                name=ptrcls.get_shortname(ctx.env.schema).name,
            )

        else:
            ptr_name = sn.QualName(
                module='__',
                name=ptrname,
            )

            try:
                ptrcls = setgen.resolve_ptr(
                    ptrsource,
                    ptrname,
                    track_ref=False,
                    ctx=ctx,
                )

                base_ptrcls = ptrcls.get_bases(
                    ctx.env.schema).first(ctx.env.schema)
            except errors.InvalidReferenceError:
                # This is a NEW computable pointer, it's fine.
                pass

        qlexpr = astutils.ensure_qlstmt(compexpr)

        if ((ctx.expr_exposed or ctx.stmt is ctx.toplevel_stmt)
                and ctx.implicit_limit
                and isinstance(qlexpr, qlast.OffsetLimitMixin)
                and not qlexpr.limit):
            qlexpr.limit = qlast.IntegerConstant(value=str(ctx.implicit_limit))

        with ctx.newscope(fenced=True) as shape_expr_ctx:
            # Put current pointer class in context, so
            # that references to link properties in sub-SELECT
            # can be resolved.  This is necessary for proper
            # evaluation of link properties on computable links,
            # most importantly, in INSERT/UPDATE context.
            shape_expr_ctx.view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                ptrcls_name=ptr_name,
                ptrcls_is_linkprop=is_linkprop,
                is_insert=is_insert,
                is_update=is_update,
            )

            shape_expr_ctx.defining_view = view_scls
            shape_expr_ctx.path_scope.unnest_fence = True
            shape_expr_ctx.partial_path_prefix = setgen.class_set(
                view_scls.get_bases(ctx.env.schema).first(ctx.env.schema),
                path_id=path_id, ctx=shape_expr_ctx)
            prefix_rptrref = path_id.rptr()
            if prefix_rptrref is not None:
                # Source path seems to contain multiple steps,
                # so set up a rptr for abbreviated link property
                # paths.
                src_path_id = path_id.src_path()
                assert src_path_id is not None
                ctx.env.schema, src_t = irtyputils.ir_typeref_to_type(
                    shape_expr_ctx.env.schema,
                    src_path_id.target,
                )
                prefix_rptr = irast.Pointer(
                    source=setgen.class_set(
                        src_t,
                        path_id=src_path_id,
                        ctx=shape_expr_ctx,
                    ),
                    target=shape_expr_ctx.partial_path_prefix,
                    ptrref=prefix_rptrref,
                    direction=s_pointers.PointerDirection.Outbound,
                )
                shape_expr_ctx.partial_path_prefix.rptr = prefix_rptr

            if is_mutation and ptrcls is not None:
                shape_expr_ctx.expr_exposed = True
                shape_expr_ctx.empty_result_type_hint = \
                    ptrcls.get_target(ctx.env.schema)

            shape_expr_ctx.stmt_metadata[qlexpr] = context.StatementMetadata(
                iterator_target=True,
            )
            irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)

            if (
                shape_el.operation.op is qlast.ShapeOp.APPEND
                or shape_el.operation.op is qlast.ShapeOp.SUBTRACT
            ):
                if not is_update:
                    op = (
                        '+=' if shape_el.operation.op is qlast.ShapeOp.APPEND
                        else '-='
                    )
                    raise errors.EdgeQLSyntaxError(
                        f"unexpected '{op}'",
                        context=shape_el.operation.context,
                    )

            irexpr.context = compexpr.context

            if base_ptrcls is None:
                base_ptrcls = shape_expr_ctx.view_rptr.base_ptrcls
                base_ptrcls_is_alias = shape_expr_ctx.view_rptr.ptrcls_is_alias

            if ptrcls is not None:
                ctx.env.schema = ptrcls.set_field_value(
                    ctx.env.schema, 'owned', True)

        ptr_cardinality = None
        ptr_target = inference.infer_type(irexpr, ctx.env)

        if (
            isinstance(ptr_target, s_types.Collection)
            and not ctx.env.orig_schema.get_by_id(ptr_target.id, default=None)
        ):
            # Record references to implicitly defined collection types,
            # so that the alias delta machinery can pick them up.
            ctx.env.created_schema_objects.add(ptr_target)

        anytype = ptr_target.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                context=ctx.env.type_origins.get(anytype),
            )

        # Validate that the insert/update expression is
        # of the correct class.
        if is_mutation and ptrcls is not None:
            base_target = ptrcls.get_target(ctx.env.schema)
            assert base_target is not None
            if ptr_target.assignment_castable_to(
                    base_target,
                    schema=ctx.env.schema):
                # Force assignment casts if the target type is not a
                # subclass of the base type and the cast is not to an
                # object type.
                if not (
                    base_target.is_object_type()
                    or s_types.is_type_compatible(
                        base_target, ptr_target, schema=ctx.env.schema
                    )
                ):
                    qlexpr = astutils.ensure_qlstmt(qlast.TypeCast(
                        type=typegen.type_to_ql_typeref(base_target, ctx=ctx),
                        expr=compexpr,
                    ))
                    ptr_target = base_target

            else:
                expected = [
                    repr(str(base_target.get_displayname(ctx.env.schema)))
                ]

                ercls: Type[errors.EdgeDBError]
                if ptrcls.is_property(ctx.env.schema):
                    ercls = errors.InvalidPropertyTargetError
                else:
                    ercls = errors.InvalidLinkTargetError

                ptr_vn = ptrcls.get_verbosename(ctx.env.schema,
                                                with_parent=True)

                raise ercls(
                    f'invalid target for {ptr_vn}: '
                    f'{str(ptr_target.get_displayname(ctx.env.schema))!r} '
                    f'(expecting {" or ".join(expected)})'
                )

    if qlexpr is not None or ptrcls is None:
        src_scls: s_sources.Source

        if is_linkprop:
            # Proper checking was done when is_linkprop is defined.
            assert view_rptr is not None
            assert isinstance(view_rptr.ptrcls, s_links.Link)
            src_scls = view_rptr.ptrcls
        else:
            src_scls = view_scls

        if ptr_target.is_object_type():
            base = ctx.env.get_track_schema_object(
                sn.QualName('std', 'link'), expr=None)
        else:
            base = ctx.env.get_track_schema_object(
                sn.QualName('std', 'property'), expr=None)

        if base_ptrcls is not None:
            derive_from = base_ptrcls
        else:
            derive_from = base

        derived_name = schemactx.derive_view_name(
            base_ptrcls,
            derived_name_base=ptr_name,
            derived_name_quals=[str(src_scls.get_name(ctx.env.schema))],
            ctx=ctx,
        )

        existing = ctx.env.schema.get(
            derived_name, default=None, type=s_pointers.Pointer)
        if existing is not None:
            existing_target = existing.get_target(ctx.env.schema)
            assert existing_target is not None
            if ctx.recompiling_schema_alias:
                ptr_cardinality = existing.get_cardinality(ctx.env.schema)
            if ptr_target == existing_target:
                ptrcls = existing
            elif ptr_target.implicitly_castable_to(
                    existing_target, ctx.env.schema):
                ctx.env.schema = existing.set_target(
                    ctx.env.schema, ptr_target)
                ptrcls = existing
            else:
                vnp = existing.get_verbosename(
                    ctx.env.schema, with_parent=True)

                t1_vn = existing_target.get_verbosename(ctx.env.schema)
                t2_vn = ptr_target.get_verbosename(ctx.env.schema)

                if compexpr is not None:
                    source_context = compexpr.context
                else:
                    source_context = shape_el.expr.steps[-1].context
                raise errors.SchemaError(
                    f'cannot redefine {vnp} as {t2_vn}',
                    details=f'{vnp} is defined as {t1_vn}',
                    context=source_context,
                )
        else:
            ptrcls = schemactx.derive_ptr(
                derive_from, src_scls, ptr_target,
                is_insert=is_insert,
                is_update=is_update,
                derived_name=derived_name,
                ctx=ctx)

    elif ptrcls.get_target(ctx.env.schema) != ptr_target:
        ctx.env.schema = ptrcls.set_target(ctx.env.schema, ptr_target)

    assert ptrcls is not None

    if qlexpr is None:
        # This is not a computable, just a pointer
        # to a nested shape.  Have it reuse the original
        # pointer name so that in `Foo.ptr.name` and
        # `Foo { ptr: {name}}` are the same path.
        path_id_name = base_ptrcls.get_name(ctx.env.schema)
        ctx.env.schema = ptrcls.set_field_value(
            ctx.env.schema, 'path_id_name', path_id_name
        )

    if qlexpr is not None:
        ctx.source_map[ptrcls] = irast.ComputableInfo(
            qlexpr=qlexpr,
            context=ctx,
            path_id=path_id,
            path_id_ns=path_id_namespace,
            shape_op=shape_el.operation.op,
        )

    if compexpr is not None or is_polymorphic:
        ctx.env.schema = ptrcls.set_field_value(
            ctx.env.schema,
            'computable',
            True,
        )

        ctx.env.schema = ptrcls.set_field_value(
            ctx.env.schema,
            'owned',
            True,
        )

    if ptr_cardinality is not None:
        ctx.env.schema = ptrcls.set_field_value(
            ctx.env.schema, 'cardinality', ptr_cardinality)
    else:
        if qlexpr is None and ptrcls is not base_ptrcls:
            ctx.env.pointer_derivation_map[base_ptrcls].append(ptrcls)

        base_cardinality = None
        base_required = False
        if base_ptrcls is not None and not base_ptrcls_is_alias:
            base_cardinality = _get_base_ptr_cardinality(base_ptrcls, ctx=ctx)
            base_required = base_ptrcls.get_required(ctx.env.schema)

        if base_cardinality is None or not base_cardinality.is_known():
            specified_cardinality = shape_el.cardinality
            specified_required = shape_el.required
        else:
            specified_cardinality = base_cardinality
            specified_required = base_required

            if (shape_el.cardinality is not None
                    and base_ptrcls is not None
                    and shape_el.cardinality != base_cardinality):
                base_src = base_ptrcls.get_source(ctx.env.schema)
                assert base_src is not None
                base_src_name = base_src.get_verbosename(ctx.env.schema)
                raise errors.SchemaError(
                    f'cannot redefine the cardinality of '
                    f'{ptrcls.get_verbosename(ctx.env.schema)}: '
                    f'it is defined as {base_cardinality.as_ptr_qual()!r} '
                    f'in the base {base_src_name}',
                    context=compexpr and compexpr.context,
                )
            # The required flag may be inherited from the base
            specified_required = shape_el.required or base_required

        ctx.env.pointer_specified_info[ptrcls] = (
            specified_cardinality, specified_required, shape_el.context)

        ctx.env.schema = ptrcls.set_field_value(
            ctx.env.schema, 'cardinality', qltypes.SchemaCardinality.Unknown)

    if (
        ptrcls.is_protected_pointer(ctx.env.schema)
        and qlexpr is not None
        and not from_default
        and not ctx.env.options.allow_writing_protected_pointers
    ):
        ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
        if is_polymorphic:
            msg = (f'cannot access {ptrcls_sn.name} on a polymorphic '
                   f'shape element')
        else:
            msg = f'cannot assign to {ptrcls_sn.name}'
        raise errors.QueryError(msg, context=shape_el.context)

    if is_update and ptrcls.get_readonly(ctx.env.schema):
        raise errors.QueryError(
            f'cannot update {ptrcls.get_verbosename(ctx.env.schema)}: '
            f'it is declared as read-only',
            context=compexpr and compexpr.context,
        )

    return ptrcls
Ejemplo n.º 12
0
def __infer_param(
    ir: irast.Parameter,
    env: context.Environment,
) -> s_types.Type:
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 13
0
def __infer_const_set(
    ir: irast.ConstantSet,
    env: context.Environment,
) -> s_types.Type:
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 14
0
def __infer_oper_call(
    ir: irast.OperatorCall,
    env: context.Environment,
) -> s_types.Type:
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 15
0
def __infer_func_call(
    ir: irast.FunctionCall,
    env: context.Environment,
) -> s_types.Type:
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 16
0
def __infer_const_or_param(ir, env):
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 17
0
def _compile_qlexpr(
    qlexpr: qlast.Base,
    view_scls: s_objtypes.ObjectType,
    *,
    ptrcls: Optional[s_pointers.Pointer],
    ptrsource: s_sources.Source,
    path_id: irast.PathId,
    ptr_name: sn.QualName,
    is_insert: bool,
    is_update: bool,
    is_linkprop: bool,

    ctx: context.ContextLevel,
) -> Tuple[irast.Set, context.ViewRPtr]:

    is_mutation = is_insert or is_update

    with ctx.newscope(fenced=True) as shape_expr_ctx:
        # Put current pointer class in context, so
        # that references to link properties in sub-SELECT
        # can be resolved.  This is necessary for proper
        # evaluation of link properties on computable links,
        # most importantly, in INSERT/UPDATE context.
        shape_expr_ctx.view_rptr = context.ViewRPtr(
            ptrsource if is_linkprop else view_scls,
            ptrcls=ptrcls,
            ptrcls_name=ptr_name,
            ptrcls_is_linkprop=is_linkprop,
            is_insert=is_insert,
            is_update=is_update,
        )

        shape_expr_ctx.defining_view = view_scls
        shape_expr_ctx.path_scope.unnest_fence = True
        shape_expr_ctx.partial_path_prefix = setgen.class_set(
            view_scls.get_bases(ctx.env.schema).first(ctx.env.schema),
            path_id=path_id, ctx=shape_expr_ctx)

        prefix_rptrref = path_id.rptr()
        if prefix_rptrref is not None:
            # Source path seems to contain multiple steps,
            # so set up a rptr for abbreviated link property
            # paths.
            src_path_id = path_id.src_path()
            assert src_path_id is not None
            ctx.env.schema, src_t = irtyputils.ir_typeref_to_type(
                shape_expr_ctx.env.schema,
                src_path_id.target,
            )
            prefix_rptr = irast.Pointer(
                source=setgen.class_set(
                    src_t,
                    path_id=src_path_id,
                    ctx=shape_expr_ctx,
                ),
                target=shape_expr_ctx.partial_path_prefix,
                ptrref=prefix_rptrref,
                direction=s_pointers.PointerDirection.Outbound,
            )
            shape_expr_ctx.partial_path_prefix.rptr = prefix_rptr

        if is_mutation and ptrcls is not None:
            shape_expr_ctx.expr_exposed = True
            shape_expr_ctx.empty_result_type_hint = \
                ptrcls.get_target(ctx.env.schema)

        irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)

    return irexpr, shape_expr_ctx.view_rptr
Ejemplo n.º 18
0
def __infer_const(
    ir: irast.BaseConstant,
    env: context.Environment,
) -> s_types.Type:
    env.schema, t = irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
    return t
Ejemplo n.º 19
0
def _normalize_view_ptr_expr(shape_el: qlast.ShapeElement,
                             view_scls: s_types.Type,
                             *,
                             path_id: irast.PathId,
                             path_id_namespace: Optional[
                                 irast.WeakNamespace] = None,
                             is_insert: bool = False,
                             is_update: bool = False,
                             view_rptr: Optional[context.ViewRPtr] = None,
                             ctx: context.ContextLevel) -> s_pointers.Pointer:
    steps = shape_el.expr.steps
    is_linkprop = False
    is_polymorphic = False
    is_mutation = is_insert or is_update
    # Pointers may be qualified by the explicit source
    # class, which is equivalent to Expr[IS Type].
    plen = len(steps)
    ptrsource = view_scls
    qlexpr = None
    target_typexpr = None
    source: qlast.Base

    if plen >= 2 and isinstance(steps[-1], qlast.TypeIndirection):
        # Target type indirection: foo: Type
        target_typexpr = steps[-1].type
        plen -= 1
        steps = steps[:-1]

    if plen == 1:
        # regular shape
        lexpr = steps[0]
        assert isinstance(lexpr, qlast.Ptr)
        is_linkprop = lexpr.type == 'property'
        if is_linkprop:
            if view_rptr is None:
                raise errors.QueryError(
                    'invalid reference to link property '
                    'in top level shape',
                    context=lexpr.context)
            ptrsource = view_rptr.ptrcls
        source = qlast.Source()
    elif plen == 2 and isinstance(steps[0], qlast.TypeIndirection):
        # Source type indirection: [IS Type].foo
        source = qlast.Path(steps=[
            qlast.Source(),
            steps[0],
        ])
        lexpr = steps[1]
        ptype = steps[0].type
        if not isinstance(ptype, qlast.TypeName):
            raise errors.QueryError(
                'complex type expressions are not supported here',
                context=ptype.context,
            )
        ptrsource = schemactx.get_schema_type(ptype.maintype, ctx=ctx)
        is_polymorphic = True
    else:  # pragma: no cover
        raise RuntimeError(
            f'unexpected path length in view shape: {len(steps)}')

    assert isinstance(lexpr, qlast.Ptr)
    ptrname = lexpr.ptr.name

    compexpr = shape_el.compexpr
    if compexpr is None and is_insert and shape_el.elements:
        # Short shape form in INSERT, e.g
        #     INSERT Foo { bar: Spam { name := 'name' }}
        # is prohibited.
        raise errors.EdgeQLSyntaxError("unexpected ':'",
                                       context=steps[-1].context)

    if compexpr is None:
        ptrcls = setgen.resolve_ptr(ptrsource, ptrname, ctx=ctx)
        if is_polymorphic:
            ptrcls = schemactx.derive_ptr(ptrcls,
                                          view_scls,
                                          is_insert=is_insert,
                                          is_update=is_update,
                                          ctx=ctx)

        base_ptrcls = ptrcls.get_bases(ctx.env.schema).first(ctx.env.schema)
        base_ptr_is_computable = base_ptrcls in ctx.source_map
        ptr_name = sn.Name(
            module='__',
            name=ptrcls.get_shortname(ctx.env.schema).name,
        )

        if (shape_el.where or shape_el.orderby or shape_el.offset
                or shape_el.limit or base_ptr_is_computable or is_polymorphic
                or target_typexpr is not None):

            if target_typexpr is None:
                qlexpr = qlast.Path(steps=[source, lexpr])
            else:
                qlexpr = qlast.Path(steps=[
                    source,
                    lexpr,
                    qlast.TypeIndirection(type=target_typexpr),
                ])

            qlexpr = astutils.ensure_qlstmt(qlexpr)
            qlexpr.where = shape_el.where
            qlexpr.orderby = shape_el.orderby

            if shape_el.offset or shape_el.limit:
                qlexpr = qlast.SelectQuery(result=qlexpr, implicit=True)
                qlexpr.offset = shape_el.offset
                qlexpr.limit = shape_el.limit

        if target_typexpr is not None:
            ptr_target = schemactx.get_schema_type(target_typexpr.maintype,
                                                   ctx=ctx)
        else:
            ptr_target = ptrcls.get_target(ctx.env.schema)

        if base_ptrcls in ctx.pending_cardinality:
            # We do not know the parent's pointer cardinality yet.
            ptr_cardinality = None
            ctx.pointer_derivation_map[base_ptrcls].append(ptrcls)
            stmtctx.pend_pointer_cardinality_inference(
                ptrcls=ptrcls,
                specified_card=shape_el.cardinality,
                source_ctx=shape_el.context,
                ctx=ctx)
        else:
            ptr_cardinality = base_ptrcls.get_cardinality(ctx.env.schema)

        implicit_tid = has_implicit_tid(
            ptr_target,
            is_mutation=is_mutation,
            ctx=ctx,
        )

        if shape_el.elements or implicit_tid:
            sub_view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                is_insert=is_insert,
                is_update=is_update)

            sub_path_id = pathctx.extend_path_id(path_id,
                                                 ptrcls=base_ptrcls,
                                                 target=ptrcls.get_target(
                                                     ctx.env.schema),
                                                 ns=ctx.path_id_namespace,
                                                 ctx=ctx)

            ctx.path_scope.attach_path(sub_path_id)

            if is_update:
                for subel in shape_el.elements or []:
                    is_prop = (isinstance(subel.expr.steps[0], qlast.Ptr)
                               and subel.expr.steps[0].type == 'property')
                    if not is_prop:
                        raise errors.QueryError(
                            'only references to link properties are allowed '
                            'in nested UPDATE shapes',
                            context=subel.context)

                ptr_target = _process_view(stype=ptr_target,
                                           path_id=sub_path_id,
                                           path_id_namespace=path_id_namespace,
                                           view_rptr=sub_view_rptr,
                                           elements=shape_el.elements,
                                           is_update=True,
                                           ctx=ctx)
            else:
                ptr_target = _process_view(stype=ptr_target,
                                           path_id=sub_path_id,
                                           path_id_namespace=path_id_namespace,
                                           view_rptr=sub_view_rptr,
                                           elements=shape_el.elements,
                                           ctx=ctx)

    else:
        base_ptrcls = ptrcls = None

        if (is_mutation
                and ptrname not in ctx.special_computables_in_mutation_shape):
            # If this is a mutation, the pointer must exist.
            ptrcls = setgen.resolve_ptr(ptrsource, ptrname, ctx=ctx)

            base_ptrcls = ptrcls.get_bases(ctx.env.schema).first(
                ctx.env.schema)

            ptr_name = sn.Name(
                module='__',
                name=ptrcls.get_shortname(ctx.env.schema).name,
            )

        else:
            # Otherwise, assume no pointer inheritance.
            # Every computable is a new pointer derived from
            # std::link or std::property.  There is one exception:
            # pointer aliases (Foo {some := Foo.other}), where `foo`
            # gets derived from `Foo.other`.  This logic is applied
            # in compile_query_subject() by populating the base_ptrcls.
            ptr_name = sn.Name(
                module='__',
                name=ptrname,
            )

        qlexpr = astutils.ensure_qlstmt(compexpr)

        with ctx.newscope(fenced=True) as shape_expr_ctx:
            # Put current pointer class in context, so
            # that references to link properties in sub-SELECT
            # can be resolved.  This is necessary for proper
            # evaluation of link properties on computable links,
            # most importantly, in INSERT/UPDATE context.
            shape_expr_ctx.view_rptr = context.ViewRPtr(
                ptrsource if is_linkprop else view_scls,
                ptrcls=ptrcls,
                ptrcls_name=ptr_name,
                ptrcls_is_linkprop=is_linkprop,
                is_insert=is_insert,
                is_update=is_update)

            shape_expr_ctx.defining_view = True
            shape_expr_ctx.path_scope.unnest_fence = True
            shape_expr_ctx.partial_path_prefix = setgen.class_set(
                view_scls, path_id=path_id, ctx=shape_expr_ctx)
            prefix_rptrref = path_id.rptr()
            if prefix_rptrref is not None:
                # Source path seems to contain multiple steps,
                # so set up a rptr for abbreviated link property
                # paths.
                src_path_id = path_id.src_path()
                prefix_rptr = irast.Pointer(
                    source=setgen.class_set(
                        irtyputils.ir_typeref_to_type(
                            shape_expr_ctx.env.schema,
                            src_path_id.target,
                        ),
                        path_id=src_path_id,
                        ctx=shape_expr_ctx,
                    ),
                    target=shape_expr_ctx.partial_path_prefix,
                    ptrref=prefix_rptrref,
                    direction=s_pointers.PointerDirection.Outbound,
                )
                shape_expr_ctx.partial_path_prefix.rptr = prefix_rptr

            if is_mutation and ptrcls is not None:
                shape_expr_ctx.expr_exposed = True
                shape_expr_ctx.empty_result_type_hint = \
                    ptrcls.get_target(ctx.env.schema)

            irexpr = dispatch.compile(qlexpr, ctx=shape_expr_ctx)

            irexpr.context = compexpr.context

            if base_ptrcls is None:
                base_ptrcls = shape_expr_ctx.view_rptr.base_ptrcls

        ptr_cardinality = None
        ptr_target = inference.infer_type(irexpr, ctx.env)

        anytype = ptr_target.find_any(ctx.env.schema)
        if anytype is not None:
            raise errors.QueryError(
                'expression returns value of indeterminate type',
                context=ctx.env.type_origins.get(anytype),
            )

        # Validate that the insert/update expression is
        # of the correct class.
        if is_mutation and ptrcls is not None:
            base_target = ptrcls.get_target(ctx.env.schema)
            assert base_target is not None
            if ptr_target.assignment_castable_to(base_target,
                                                 schema=ctx.env.schema):
                # Force assignment casts if the target type is not a
                # subclass of the base type and the cast is not to an
                # object type.
                if not (base_target.is_object_type()
                        or ptr_target.issubclass(ctx.env.schema, base_target)):
                    qlexpr = astutils.ensure_qlstmt(
                        qlast.TypeCast(
                            type=astutils.type_to_ql_typeref(
                                base_target, schema=ctx.env.schema),
                            expr=compexpr,
                        ))
                    ptr_target = base_target

            else:
                expected = [
                    repr(str(base_target.get_displayname(ctx.env.schema)))
                ]

                ercls: Type[errors.EdgeDBError]
                if ptrcls.is_property(ctx.env.schema):
                    ercls = errors.InvalidPropertyTargetError
                else:
                    ercls = errors.InvalidLinkTargetError

                ptr_vn = ptrcls.get_verbosename(ctx.env.schema,
                                                with_parent=True)

                raise ercls(
                    f'invalid target for {ptr_vn}: '
                    f'{str(ptr_target.get_displayname(ctx.env.schema))!r} '
                    f'(expecting {" or ".join(expected)})')

    if qlexpr is not None or ptrcls is None:
        if is_linkprop:
            # Proper checking was done when is_linkprop is defined.
            assert view_rptr is not None
            src_scls = view_rptr.ptrcls
        else:
            src_scls = view_scls

        if ptr_target.is_object_type():
            base = ctx.env.get_track_schema_object('std::link')
        else:
            base = ctx.env.get_track_schema_object('std::property')

        if base_ptrcls is not None:
            derive_from = base_ptrcls
        else:
            derive_from = base

        derived_name = schemactx.derive_view_name(
            base_ptrcls,
            derived_name_base=ptr_name,
            derived_name_quals=[src_scls.get_name(ctx.env.schema)],
            ctx=ctx)

        existing = ctx.env.schema.get(derived_name, None)
        if existing is not None:
            existing_target = existing.get_target(ctx.env.schema)
            if ptr_target == existing_target:
                ptrcls = existing
            elif ptr_target.implicitly_castable_to(existing_target,
                                                   ctx.env.schema):
                ctx.env.schema = existing.set_target(ctx.env.schema,
                                                     ptr_target)
                ptrcls = existing
            else:
                target_rptr_set = (ptr_target.get_rptr(ctx.env.schema)
                                   is not None)

                if target_rptr_set:
                    ctx.env.schema = ptr_target.set_field_value(
                        ctx.env.schema,
                        'rptr',
                        None,
                    )

                ctx.env.schema = existing.delete(ctx.env.schema)
                ptrcls = schemactx.derive_ptr(derive_from,
                                              src_scls,
                                              ptr_target,
                                              is_insert=is_insert,
                                              is_update=is_update,
                                              derived_name=derived_name,
                                              inheritance_merge=False,
                                              ctx=ctx)

                if target_rptr_set:
                    ctx.env.schema = ptr_target.set_field_value(
                        ctx.env.schema,
                        'rptr',
                        ptrcls,
                    )
        else:
            ptrcls = schemactx.derive_ptr(derive_from,
                                          src_scls,
                                          ptr_target,
                                          is_insert=is_insert,
                                          is_update=is_update,
                                          derived_name=derived_name,
                                          ctx=ctx)

    elif ptrcls.get_target(ctx.env.schema) != ptr_target:
        ctx.env.schema = ptrcls.set_target(ctx.env.schema, ptr_target)

    assert ptrcls is not None

    if qlexpr is None:
        # This is not a computable, just a pointer
        # to a nested shape.  Have it reuse the original
        # pointer name so that in `Foo.ptr.name` and
        # `Foo { ptr: {name}}` are the same path.
        path_id_name = base_ptrcls.get_name(ctx.env.schema)
        ctx.env.schema = ptrcls.set_field_value(ctx.env.schema, 'path_id_name',
                                                path_id_name)

    if qlexpr is not None:
        ctx.source_map[ptrcls] = (qlexpr, ctx, path_id, path_id_namespace)

    if not is_mutation:
        if ptr_cardinality is None:
            if qlexpr is None and ptrcls is not base_ptrcls:
                ctx.pointer_derivation_map[base_ptrcls].append(ptrcls)

            stmtctx.pend_pointer_cardinality_inference(
                ptrcls=ptrcls,
                specified_card=shape_el.cardinality,
                source_ctx=shape_el.context,
                ctx=ctx)

            ctx.env.schema = ptrcls.set_field_value(ctx.env.schema,
                                                    'cardinality', None)
        else:
            ctx.env.schema = ptrcls.set_field_value(ctx.env.schema,
                                                    'cardinality',
                                                    ptr_cardinality)

    if ptrcls.is_protected_pointer(ctx.env.schema) and qlexpr is not None:
        ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
        if is_polymorphic:
            msg = (f'cannot access {ptrcls_sn.name} on a polymorphic '
                   f'shape element')
        else:
            msg = f'cannot assign to {ptrcls_sn.name}'
        raise errors.QueryError(msg, context=shape_el.context)

    return ptrcls
Ejemplo n.º 20
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 == 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)
            elif stype.issubclass(ctx.env.schema, component_endpoint):
                assert isinstance(stype, s_objtypes.ObjectType)
                narrow_ptr = stype.getptr(
                    ctx.env.schema,
                    component.shortname.get_local_name(),
                )
                rptr_specialization.append(
                    irtyputils.ptrref_from_ptrcls(
                        schema=ctx.env.schema,
                        ptrcls=narrow_ptr,
                        direction=rptr.direction,
                        cache=ctx.env.ptr_ref_cache,
                        typeref_cache=ctx.env.type_ref_cache,
                    ), )

    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(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
Ejemplo n.º 21
0
def __infer_func_call(ir, env):
    return irtyputils.ir_typeref_to_type(env.schema, ir.typeref)
Ejemplo n.º 22
0
def compile_insert_unless_conflict_on(
    stmt: irast.InsertStmt,
    insert_subject: qlast.Path,
    constraint_spec: qlast.Expr,
    else_branch: Optional[qlast.Expr],
    *, ctx: context.ContextLevel,
) -> irast.OnConflictClause:

    with ctx.new() as constraint_ctx:
        constraint_ctx.partial_path_prefix = stmt.subject

        # We compile the name here so we can analyze it, but we don't do
        # anything else with it.
        cspec_res = setgen.ensure_set(dispatch.compile(
            constraint_spec, ctx=constraint_ctx), ctx=constraint_ctx)

    if not cspec_res.rptr:
        raise errors.QueryError(
            'UNLESS CONFLICT argument must be a property',
            context=constraint_spec.context,
        )

    if cspec_res.rptr.source.path_id != stmt.subject.path_id:
        raise errors.QueryError(
            'UNLESS CONFLICT argument must be a property of the '
            'type being inserted',
            context=constraint_spec.context,
        )

    schema = ctx.env.schema
    schema, typ = typeutils.ir_typeref_to_type(schema, stmt.subject.typeref)
    assert isinstance(typ, s_objtypes.ObjectType)
    real_typ = typ.get_nearest_non_derived_parent(schema)

    schema, ptr = (
        typeutils.ptrcls_from_ptrref(cspec_res.rptr.ptrref,
                                     schema=schema))
    if not isinstance(ptr, s_pointers.Pointer):
        raise errors.QueryError(
            'UNLESS CONFLICT property must be a property',
            context=constraint_spec.context,
        )

    ptr = ptr.get_nearest_non_derived_parent(schema)
    ptr_card = ptr.get_cardinality(schema)
    if not ptr_card.is_single():
        raise errors.QueryError(
            'UNLESS CONFLICT property must be a SINGLE property',
            context=constraint_spec.context,
        )

    exclusive_constr = schema.get('std::exclusive', type=s_constr.Constraint)
    ex_cnstrs = [c for c in ptr.get_constraints(schema).objects(schema)
                 if c.issubclass(schema, exclusive_constr)]

    if len(ex_cnstrs) != 1:
        raise errors.QueryError(
            'UNLESS CONFLICT property must have a single exclusive constraint',
            context=constraint_spec.context,
        )

    module_id = schema.get_global(
        s_mod.Module, ptr.get_name(schema).module).id

    field_name = cspec_res.rptr.ptrref.shortname
    ds = {field_name.name: (ptr, ex_cnstrs)}
    select_ir = compile_insert_unless_conflict_select(
        stmt, insert_subject, real_typ, constrs=ds, obj_constrs=[],
        parser_context=stmt.context, ctx=ctx)

    # Compile an else branch
    else_ir = None
    if else_branch:
        # The ELSE needs to be able to reference the subject in an
        # UPDATE, even though that would normally be prohibited.
        ctx.path_scope.factoring_allowlist.add(stmt.subject.path_id)

        # Compile else
        else_ir = dispatch.compile(
            astutils.ensure_qlstmt(else_branch), ctx=ctx)
        assert isinstance(else_ir, irast.Set)

    return irast.OnConflictClause(
        constraint=irast.ConstraintRef(
            id=ex_cnstrs[0].id, module_id=module_id),
        select_ir=select_ir,
        else_ir=else_ir
    )