Ejemplo n.º 1
0
    def _prepare_field(self, node):
        path = self._context.path[-1]
        include_base = self._context.include_base[-1]

        is_top = self._is_top_level_field(node)

        spath = self._context.path[-1]
        prevt, target = self._get_parent_and_current_type()

        # insert normal or specialized link
        steps = []
        if include_base:
            base = spath[0].type
            steps.append(qlast.TypeIndirection(
                type=qlast.TypeName(
                    maintype=qlast.ObjectRef(
                        module=base.module,
                        name=base.short_name
                    ),
                ),
            ))
        steps.append(qlast.Ptr(
            ptr=qlast.ObjectRef(
                name=node.name.value
            )
        ))

        return is_top, path, prevt, target, steps
Ejemplo n.º 2
0
    def reduce_ShapePathPtr_DOT_PathStepName(self, *kids):
        from edb.schema import pointers as s_pointers

        self.val = qlast.Path(steps=[
            qlast.TypeIndirection(type=kids[0].val, ),
            qlast.Ptr(ptr=kids[2].val,
                      direction=s_pointers.PointerDirection.Outbound),
        ])
Ejemplo n.º 3
0
def get_config_type_shape(schema, stype,
                          path) -> typing.List[qlast.ShapeElement]:
    shape = []
    seen = set()

    stypes = [stype] + list(stype.descendants(schema))

    for t in stypes:
        t_name = t.get_name(schema)

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

            elem_path = []

            if t is not stype:
                elem_path.append(
                    qlast.TypeIndirection(type=qlast.TypeName(
                        maintype=qlast.ObjectRef(
                            module=t_name.module,
                            name=t_name.name,
                        ), ), ), )

            elem_path.append(qlast.Ptr(ptr=qlast.ObjectRef(name=pn)))

            ptype = p.get_target(schema)

            if ptype.is_object_type():
                subshape = get_config_type_shape(schema, ptype,
                                                 path + elem_path)
                subshape.append(
                    qlast.ShapeElement(
                        expr=qlast.Path(steps=[
                            qlast.Ptr(ptr=qlast.ObjectRef(name='_tname'), ),
                        ], ),
                        compexpr=qlast.Path(steps=path + elem_path + [
                            qlast.Ptr(ptr=qlast.ObjectRef(name='__type__')),
                            qlast.Ptr(ptr=qlast.ObjectRef(name='name')),
                        ], ),
                    ), )
            else:
                subshape = []

            shape.append(
                qlast.ShapeElement(
                    expr=qlast.Path(steps=elem_path),
                    elements=subshape,
                ), )

            seen.add(pn)

    return shape
Ejemplo n.º 4
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.º 5
0
    def reduce_ShapeElementWithSubShape(self, *kids):
        r"""%reduce ShapePointer \
             OptAnySubShape OptFilterClause OptSortClause OptSelectLimit \
        """
        self.val = kids[0].val

        shape = kids[1].val

        # shape elements can have a path starting with a TypeExpr,
        # this indicates a polymorphic shape and TypeExpr must be
        # extracted from the path steps
        if shape and isinstance(shape[0], qlast.TypeExpr):
            self.val.expr.steps.append(
                qlast.TypeIndirection(type=shape[0], context=shape[0].context))
            self.val.elements = shape[1:]
        else:
            self.val.elements = shape or []

        self.val.where = kids[2].val
        self.val.orderby = kids[3].val
        self.val.offset = kids[4].val[0]
        self.val.limit = kids[4].val[1]
Ejemplo n.º 6
0
def _normalize_view_ptr_expr(
        shape_el: qlast.ShapeElement,
        view_scls: s_nodes.Node,
        *,
        path_id: irast.PathId,
        path_id_namespace: typing.Optional[irast.WeakNamespace] = None,
        is_insert: bool = False,
        is_update: bool = False,
        view_rptr: typing.Optional[context.ViewRPtr] = None,
        ctx: context.CompilerContext) -> 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

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

    if plen == 1:
        # regular shape
        lexpr = steps[0]
        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
        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)}')

    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_view(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
            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,
                from_parent=True,
                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:
        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.
            base_ptrcls = ptrcls = None

            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.path_scope.unnest_fence = True
            shape_expr_ctx.partial_path_prefix = setgen.class_set(
                view_scls, path_id=path_id, ctx=shape_expr_ctx)

            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)
            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)))
                ]

                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:
            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_view(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_view(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)

    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)
        ctx.env.schema = ptrcls.set_field_value(ctx.env.schema, 'computable',
                                                True)

    if not is_mutation:
        if ptr_cardinality is None:
            if ptrcls not in ctx.pending_cardinality:
                if qlexpr is not None:
                    from_parent = False
                elif ptrcls is not base_ptrcls:
                    ctx.pointer_derivation_map[base_ptrcls].append(ptrcls)
                    from_parent = True
                else:
                    from_parent = False

                stmtctx.pend_pointer_cardinality_inference(
                    ptrcls=ptrcls,
                    specified_card=shape_el.cardinality,
                    from_parent=from_parent,
                    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.º 7
0
 def reduce_LBRACKET_IS_FullTypeExpr_RBRACKET(self, *kids):
     self.val = qlast.TypeIndirection(type=kids[2].val, )