예제 #1
0
    def _get_general_offset_limit(self, after, before, first, last):
        # convert any static values to corresponding qlast
        if after is not None:
            if isinstance(after, qlast.Base):
                after = qlast.TypeCast(
                    type=qlast.TypeName(
                        maintype=qlast.ObjectRef(name='int64')),
                    expr=after
                )
            else:
                after = qlast.BaseConstant.from_python(after)
        if before is not None:
            if isinstance(before, qlast.Base):
                before = qlast.TypeCast(
                    type=qlast.TypeName(
                        maintype=qlast.ObjectRef(name='int64')),
                    expr=before
                )
            else:
                before = qlast.BaseConstant.from_python(before)
        if first is not None and not isinstance(first, qlast.Base):
            first = qlast.BaseConstant.from_python(first)
        if last is not None and not isinstance(last, qlast.Base):
            last = qlast.BaseConstant.from_python(last)

        offset = limit = None
        # convert before, after, first and last into offset and limit
        if after is not None:
            # The +1 is to make 'after' into an appropriate index.
            #
            # 0--a--1--b--2--c--3-- ... we call element at
            # index 0 (or "element 0" for short), the element
            # immediately after the mark 0. So after "element
            # 0" really means after "index 1".
            offset = qlast.BinOp(
                left=after,
                op='+',
                right=qlast.IntegerConstant(value='1')
            )

        if before is not None:
            # limit = before - (after or 0)
            if after:
                limit = qlast.BinOp(
                    left=before,
                    op='-',
                    right=after
                )
            else:
                limit = before

        if first is not None:
            if limit is None:
                limit = first
            else:
                limit = qlast.IfElse(
                    if_expr=first,
                    condition=qlast.BinOp(
                        left=first,
                        op='<',
                        right=limit
                    ),
                    else_expr=limit
                )

        if last is not None:
            if limit is not None:
                if offset:
                    offset = qlast.BinOp(
                        left=offset,
                        op='+',
                        right=qlast.BinOp(
                            left=limit,
                            op='-',
                            right=last
                        )
                    )
                else:
                    offset = qlast.BinOp(
                        left=limit,
                        op='-',
                        right=last
                    )

                limit = qlast.IfElse(
                    if_expr=last,
                    condition=qlast.BinOp(
                        left=last,
                        op='<',
                        right=limit
                    ),
                    else_expr=limit
                )

            else:
                # FIXME: there wasn't any limit, so we can define last
                # in terms of offset alone without negative OFFSET
                # implementation
                raise g_errors.GraphQLTranslationError(
                    f'last translates to a negative OFFSET in '
                    f'EdgeQL which is currently unsupported')

        return offset, limit
예제 #2
0
def computable_ptr_set(
    rptr: irast.Pointer,
    *,
    unnest_fence: bool=False,
    same_computable_scope: bool=False,
    srcctx: Optional[parsing.ParserContext]=None,
    ctx: context.ContextLevel,
) -> irast.Set:
    """Return ir.Set for a pointer defined as a computable."""
    ptrcls = typegen.ptrcls_from_ptrref(rptr.ptrref, ctx=ctx)
    source_set = rptr.source
    source_scls = get_set_type(source_set, ctx=ctx)
    # process_view() may generate computable pointer expressions
    # in the form "self.linkname".  To prevent infinite recursion,
    # self must resolve to the parent type of the view NOT the view
    # type itself.  Similarly, when resolving computable link properties
    # make sure that we use the parent of derived ptrcls.
    if source_scls.is_view(ctx.env.schema):
        source_set_stype = source_scls.peel_view(ctx.env.schema)
        source_set = new_set_from_set(
            source_set, stype=source_set_stype,
            preserve_scope_ns=True, ctx=ctx)
        source_set.shape = []
        if source_set.rptr is not None:
            source_rptrref = source_set.rptr.ptrref
            if source_rptrref.base_ptr is not None:
                source_rptrref = source_rptrref.base_ptr
            source_set.rptr = irast.Pointer(
                source=source_set.rptr.source,
                target=source_set,
                ptrref=source_rptrref,
                direction=source_set.rptr.direction,
            )

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

    try:
        comp_info = ctx.source_map[ptrcls]
        qlexpr = comp_info.qlexpr
        assert isinstance(comp_info.context, context.ContextLevel)
        qlctx = comp_info.context
        inner_source_path_id = comp_info.path_id
        path_id_ns = comp_info.path_id_ns
    except KeyError:
        comp_expr = ptrcls.get_expr(ctx.env.schema)
        schema_qlexpr: Optional[qlast.Expr] = None
        if comp_expr is None and ctx.env.options.apply_query_rewrites:
            schema_deflt = ptrcls.get_schema_reflection_default(ctx.env.schema)
            if schema_deflt is not None:
                assert isinstance(ptrcls, s_pointers.Pointer)
                ptrcls_n = ptrcls.get_shortname(ctx.env.schema).name
                schema_qlexpr = qlast.BinOp(
                    left=qlast.Path(
                        steps=[
                            qlast.Source(),
                            qlast.Ptr(
                                ptr=qlast.ObjectRef(name=ptrcls_n),
                                direction=s_pointers.PointerDirection.Outbound,
                                type=(
                                    'property'
                                    if ptrcls.is_link_property(ctx.env.schema)
                                    else None
                                )
                            )
                        ],
                    ),
                    right=qlparser.parse_fragment(schema_deflt),
                    op='??',
                )

        if schema_qlexpr is None:
            if comp_expr is None:
                ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
                raise errors.InternalServerError(
                    f'{ptrcls_sn!r} is not a computable pointer')

            comp_qlexpr = qlparser.parse(comp_expr.text)
            assert isinstance(comp_qlexpr, qlast.Expr), 'expected qlast.Expr'
            schema_qlexpr = comp_qlexpr

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

    newctx: Callable[[], ContextManager[context.ContextLevel]]

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

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

    result_path_id = pathctx.extend_path_id(
        source_path_id,
        ptrcls=ptrcls,
        ns=ctx.path_id_namespace,
        ctx=ctx,
    )

    result_stype = ptrcls.get_target(ctx.env.schema)
    base_object = ctx.env.schema.get('std::BaseObject', type=s_types.Type)
    with newctx() as subctx:
        subctx.disable_shadowing.add(ptrcls)
        if result_stype != base_object:
            subctx.view_scls = result_stype
        subctx.view_rptr = context.ViewRPtr(
            source_scls, ptrcls=ptrcls, rptr=rptr)  # type: ignore
        subctx.anchors[qlast.Source().name] = source_set
        subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema)
        subctx.partial_path_prefix = source_set
        # On a mutation, make the expr_exposed. This corresponds with
        # a similar check on is_mutation in _normalize_view_ptr_expr.
        if (source_scls.get_expr_type(ctx.env.schema)
                != s_types.ExprType.Select):
            subctx.expr_exposed = True

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

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

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

    rptr.target = comp_ir_set

    return comp_ir_set
예제 #3
0
def _process_view(
    *,
    stype: s_objtypes.ObjectType,
    path_id: irast.PathId,
    path_id_namespace: Optional[irast.WeakNamespace] = None,
    elements: List[qlast.ShapeElement],
    view_rptr: Optional[context.ViewRPtr] = None,
    view_name: Optional[sn.SchemaName] = None,
    is_insert: bool = False,
    is_update: bool = False,
    is_delete: bool = False,
    parser_context: pctx.ParserContext,
    ctx: context.ContextLevel,
) -> s_objtypes.ObjectType:

    if (view_name is None and ctx.env.options.schema_view_mode
            and view_rptr is not None):
        # Make sure persistent schema expression aliases have properly formed
        # names as opposed to the usual mangled form of the ephemeral
        # aliases.  This is needed for introspection readability, as well
        # as helps in maintaining proper type names for schema
        # representations that require alphanumeric names, such as
        # GraphQL.
        #
        # We use the name of the source together with the name
        # of the inbound link to form the name, so in e.g.
        #    CREATE ALIAS V := (SELECT Foo { bar: { baz: { ... } })
        # The name of the innermost alias would be "__V__bar__baz".
        source_name = view_rptr.source.get_name(ctx.env.schema).name
        if not source_name.startswith('__'):
            source_name = f'__{source_name}'
        if view_rptr.ptrcls_name is not None:
            ptr_name = view_rptr.ptrcls_name.name
        elif view_rptr.ptrcls is not None:
            ptr_name = view_rptr.ptrcls.get_shortname(ctx.env.schema).name
        else:
            raise errors.InternalServerError(
                '_process_view in schema mode received view_rptr with '
                'neither ptrcls_name, not ptrcls'
            )

        name = f'{source_name}__{ptr_name}'
        view_name = sn.Name(
            module=ctx.derived_target_module or '__derived__',
            name=name,
        )

    view_scls = schemactx.derive_view(
        stype,
        is_insert=is_insert,
        is_update=is_update,
        is_delete=is_delete,
        derived_name=view_name,
        ctx=ctx,
    )
    assert isinstance(view_scls, s_objtypes.ObjectType), view_scls
    is_mutation = is_insert or is_update
    is_defining_shape = ctx.expr_exposed or is_mutation

    if view_rptr is not None and view_rptr.ptrcls is None:
        derive_ptrcls(
            view_rptr, target_scls=view_scls,
            transparent=True, ctx=ctx)

    pointers = []

    for shape_el in elements:
        with ctx.newscope(fenced=True) as scopectx:
            pointer = _normalize_view_ptr_expr(
                shape_el, view_scls, path_id=path_id,
                path_id_namespace=path_id_namespace,
                is_insert=is_insert, is_update=is_update,
                view_rptr=view_rptr,
                ctx=scopectx)

            if pointer in pointers:
                schema = ctx.env.schema
                vnp = pointer.get_verbosename(schema, with_parent=True)

                raise errors.QueryError(
                    f'duplicate definition of {vnp}',
                    context=shape_el.context)

            pointers.append(pointer)

    if is_insert:
        explicit_ptrs = {
            ptrcls.get_shortname(ctx.env.schema).name
            for ptrcls in pointers
        }
        scls_pointers = stype.get_pointers(ctx.env.schema)
        for pn, ptrcls in scls_pointers.items(ctx.env.schema):
            if (pn in explicit_ptrs or
                    ptrcls.is_pure_computable(ctx.env.schema)):
                continue

            default_expr = ptrcls.get_default(ctx.env.schema)
            if not default_expr:
                if ptrcls.get_required(ctx.env.schema):
                    if ptrcls.is_property(ctx.env.schema):
                        # If the target is a sequence, there's no need
                        # for an explicit value.
                        ptrcls_target = ptrcls.get_target(ctx.env.schema)
                        assert ptrcls_target is not None
                        if ptrcls_target.issubclass(
                                ctx.env.schema,
                                ctx.env.schema.get('std::sequence')):
                            continue

                        what = 'property'
                    else:
                        what = 'link'
                    raise errors.MissingRequiredError(
                        f'missing value for required {what} '
                        f'{stype.get_displayname(ctx.env.schema)}.'
                        f'{ptrcls.get_displayname(ctx.env.schema)}')
                else:
                    continue

            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            default_ql = qlast.ShapeElement(
                expr=qlast.Path(
                    steps=[
                        qlast.Ptr(
                            ptr=qlast.ObjectRef(
                                name=ptrcls_sn.name,
                                module=ptrcls_sn.module,
                            ),
                        ),
                    ],
                ),
                compexpr=qlast.DetachedExpr(
                    expr=default_expr.qlast,
                ),
            )

            with ctx.newscope(fenced=True) as scopectx:
                pointers.append(
                    _normalize_view_ptr_expr(
                        default_ql,
                        view_scls,
                        path_id=path_id,
                        path_id_namespace=path_id_namespace,
                        is_insert=is_insert,
                        is_update=is_update,
                        from_default=True,
                        view_rptr=view_rptr,
                        ctx=scopectx,
                    ),
                )

    elif (
        stype.get_name(ctx.env.schema).module == 'schema'
        and ctx.env.options.introspection_schema_rewrites
    ):
        explicit_ptrs = {
            ptrcls.get_shortname(ctx.env.schema).name
            for ptrcls in pointers
        }
        scls_pointers = stype.get_pointers(ctx.env.schema)
        for pn, ptrcls in scls_pointers.items(ctx.env.schema):
            if (
                pn in explicit_ptrs
                or ptrcls.is_pure_computable(ctx.env.schema)
            ):
                continue

            schema_deflt = ptrcls.get_schema_reflection_default(ctx.env.schema)
            if schema_deflt is None:
                continue

            with ctx.newscope(fenced=True) as scopectx:
                implicit_ql = qlast.ShapeElement(
                    expr=qlast.Path(
                        steps=[
                            qlast.Ptr(
                                ptr=qlast.ObjectRef(
                                    name=pn,
                                ),
                            ),
                        ],
                    ),
                    compexpr=qlast.BinOp(
                        left=qlast.Path(
                            partial=True,
                            steps=[
                                qlast.Ptr(
                                    ptr=qlast.ObjectRef(name=pn),
                                    direction=(
                                        s_pointers.PointerDirection.Outbound
                                    ),
                                )
                            ],
                        ),
                        right=qlparser.parse_fragment(schema_deflt),
                        op='??',
                    ),
                )

                # Note: we only need to record the schema default
                # as a computable, but not include it in the type
                # shape, so we ignore the return value.
                _normalize_view_ptr_expr(
                    implicit_ql,
                    view_scls,
                    path_id=path_id,
                    path_id_namespace=path_id_namespace,
                    is_insert=is_insert,
                    is_update=is_update,
                    view_rptr=view_rptr,
                    ctx=scopectx,
                )

    for ptrcls in pointers:
        source: Union[s_types.Type, s_pointers.PointerLike]

        if ptrcls.is_link_property(ctx.env.schema):
            assert view_rptr is not None and view_rptr.ptrcls is not None
            source = view_rptr.ptrcls
        else:
            source = view_scls

        if is_defining_shape:
            cinfo = ctx.source_map.get(ptrcls)
            if cinfo is not None:
                shape_op = cinfo.shape_op
            else:
                shape_op = qlast.ShapeOp.ASSIGN

            ctx.env.view_shapes[source].append((ptrcls, shape_op))

    if (view_rptr is not None and view_rptr.ptrcls is not None and
            view_scls is not stype):
        ctx.env.schema = view_scls.set_field_value(
            ctx.env.schema, 'rptr', view_rptr.ptrcls)

    return view_scls
예제 #4
0
def sdl_to_ddl(
    schema: s_schema.Schema,
    documents: Mapping[str, List[qlast.DDL]],
) -> Tuple[qlast.DDLCommand, ...]:

    ddlgraph: DDLGraph = {}
    mods: List[qlast.DDLCommand] = []

    ctx = LayoutTraceContext(
        schema,
        local_modules=frozenset(mod for mod in documents),
    )

    for module_name, declarations in documents.items():
        ctx.set_module(module_name)
        for decl_ast in declarations:
            if isinstance(decl_ast, qlast.CreateObject):
                _, fq_name = ctx.get_fq_name(decl_ast)

                if isinstance(decl_ast,
                              (qlast.CreateObjectType, qlast.CreateAlias)):
                    ctx.objects[fq_name] = qltracer.ObjectType(fq_name)

                elif isinstance(decl_ast, qlast.CreateScalarType):
                    ctx.objects[fq_name] = qltracer.Type(fq_name)

                elif isinstance(decl_ast,
                                (qlast.CreateLink, qlast.CreateProperty)):
                    ctx.objects[fq_name] = qltracer.Pointer(fq_name,
                                                            source=None,
                                                            target=None)
                elif isinstance(decl_ast, qlast.CreateFunction):
                    ctx.objects[fq_name] = qltracer.Function(fq_name)
                elif isinstance(decl_ast, qlast.CreateConstraint):
                    ctx.objects[fq_name] = qltracer.Constraint(fq_name)
                elif isinstance(decl_ast, qlast.CreateAnnotation):
                    ctx.objects[fq_name] = qltracer.Annotation(fq_name)
                else:
                    raise AssertionError(
                        f'unexpected SDL declaration: {decl_ast}')

    for module_name, declarations in documents.items():
        ctx.set_module(module_name)
        for decl_ast in declarations:
            trace_layout(decl_ast, ctx=ctx)

    # compute the ancestors graph
    for obj_name in ctx.parents.keys():
        ctx.ancestors[obj_name] = get_ancestors(obj_name, ctx.ancestors,
                                                ctx.parents)

    topological.normalize(
        ctx.inh_graph,
        merger=_graph_merge_cb,  # type: ignore
        schema=schema,
    )

    tracectx = DepTraceContext(schema, ddlgraph, ctx.objects, ctx.parents,
                               ctx.ancestors, ctx.defdeps, ctx.constraints)
    for module_name, declarations in documents.items():
        tracectx.set_module(module_name)
        # module needs to be created regardless of whether its
        # contents are empty or not
        mods.append(qlast.CreateModule(name=qlast.ObjectRef(name=module_name)))
        for decl_ast in declarations:
            trace_dependencies(decl_ast, ctx=tracectx)

    ordered = topological.sort(ddlgraph, allow_unresolved=False)
    return tuple(mods) + tuple(ordered)
예제 #5
0
 def reduce_AnyIdentifier(self, *kids):
     self.val = qlast.ObjectRef(module=None, name=kids[0].val)
예제 #6
0
파일: stmt.py 프로젝트: xing0713/edgedb
def compile_result_clause(
        result: qlast.Base, *,
        view_scls: typing.Optional[s_types.Type]=None,
        view_rptr: typing.Optional[context.ViewRPtr]=None,
        view_name: typing.Optional[s_name.SchemaName]=None,
        result_alias: typing.Optional[str]=None,
        forward_rptr: bool=False,
        ctx: context.ContextLevel) -> irast.Set:
    with ctx.new() as sctx:
        sctx.clause = 'result'
        if sctx.stmt is ctx.toplevel_stmt:
            sctx.toplevel_clause = sctx.clause
            sctx.expr_exposed = True

        if forward_rptr:
            sctx.view_rptr = view_rptr
            # sctx.view_scls = view_scls

        if isinstance(result, qlast.Shape):
            result_expr = result.expr
            shape = result.elements
        else:
            result_expr = result
            shape = None

        if result_alias:
            # `SELECT foo := expr` is largely equivalent to
            # `WITH foo := expr SELECT foo` with one important exception:
            # the scope namespace does not get added to the current query
            # path scope.  This is needed to handle FOR queries correctly.
            with sctx.newscope(temporary=True, fenced=True) as scopectx:
                stmtctx.declare_view(
                    result_expr, alias=result_alias,
                    temporary_scope=False, ctx=scopectx)

            result_expr = qlast.Path(
                steps=[qlast.ObjectRef(name=result_alias)]
            )

        if (view_rptr is not None and
                (view_rptr.is_insert or view_rptr.is_update) and
                view_rptr.ptrcls is not None) and False:
            # If we have an empty set assigned to a pointer in an INSERT
            # or UPDATE, there's no need to explicitly specify the
            # empty set type and it can be assumed to match the pointer
            # target type.
            target_t = view_rptr.ptrcls.get_target(ctx.env.schema)

            if astutils.is_ql_empty_set(result_expr):
                expr = setgen.new_empty_set(
                    stype=target_t,
                    alias=ctx.aliases.get('e'),
                    ctx=sctx,
                    srcctx=result_expr.context,
                )
            else:
                with sctx.new() as exprctx:
                    exprctx.empty_result_type_hint = target_t
                    expr = setgen.ensure_set(
                        dispatch.compile(result_expr, ctx=exprctx),
                        ctx=exprctx)
        else:
            if astutils.is_ql_empty_set(result_expr):
                expr = setgen.new_empty_set(
                    stype=sctx.empty_result_type_hint,
                    alias=ctx.aliases.get('e'),
                    ctx=sctx,
                    srcctx=result_expr.context,
                )
            else:
                expr = setgen.ensure_set(
                    dispatch.compile(result_expr, ctx=sctx), ctx=sctx)

        result = compile_query_subject(
            expr, shape=shape, view_rptr=view_rptr, view_name=view_name,
            result_alias=result_alias,
            view_scls=view_scls,
            compile_views=ctx.stmt is ctx.toplevel_stmt,
            ctx=sctx)

        ctx.partial_path_prefix = result

    return result
예제 #7
0
 def reduce_INDEX_OnExpr(self, *kids):
     self.val = qlast.CreateIndex(
         name=qlast.ObjectRef(name='idx'),
         expr=kids[1].val,
     )
예제 #8
0
 def reduce_DUNDERTYPE(self, *kids):
     self.val = qlast.ObjectRef(name=kids[0].val)
예제 #9
0
def _get_shape_configuration(
        ir_set: irast.Set, *,
        rptr: typing.Optional[irast.Pointer]=None,
        parent_view_type: typing.Optional[s_types.ViewType]=None,
        ctx: context.ContextLevel) \
        -> typing.List[typing.Tuple[irast.Set, s_pointers.Pointer]]:
    """Return a list of (source_set, ptrcls) pairs as a shape for a given set.
    """

    stype = setgen.get_set_type(ir_set, ctx=ctx)

    sources = []
    link_view = False
    is_objtype = ir_set.path_id.is_objtype_path()

    if rptr is None:
        rptr = ir_set.rptr

    if rptr is not None:
        rptrcls = irtyputils.ptrcls_from_ptrref(rptr.ptrref,
                                                schema=ctx.env.schema)
    else:
        rptrcls = None

    link_view = (rptrcls is not None
                 and not rptrcls.is_link_property(ctx.env.schema)
                 and _link_has_shape(rptrcls, ctx=ctx))

    if is_objtype or not link_view:
        sources.append(stype)

    if link_view:
        sources.append(rptrcls)

    shape_ptrs = []

    id_present_in_shape = False

    for source in sources:
        for ptr in ctx.env.view_shapes[source]:
            if (ptr.is_link_property(ctx.env.schema)
                    and ir_set.path_id != rptr.target.path_id):
                path_tip = rptr.target
            else:
                path_tip = ir_set

            shape_ptrs.append((path_tip, ptr))

            if source is stype and ptr.is_id_pointer(ctx.env.schema):
                id_present_in_shape = True

    if is_objtype and not id_present_in_shape:
        view_type = stype.get_view_type(ctx.env.schema)
        is_mutation = view_type in (s_types.ViewType.Insert,
                                    s_types.ViewType.Update)
        is_parent_update = parent_view_type is s_types.ViewType.Update

        implicit_id = (
            # shape is not specified at all
            not shape_ptrs
            # implicit ids are always wanted
            or (ctx.implicit_id_in_shapes and not is_mutation)
            # we are inside an UPDATE shape and this is
            # an explicit expression (link target update)
            or (is_parent_update and ir_set.expr is not None))

        if implicit_id:
            # We want the id in this shape and it's not already there,
            # so insert it in the first position.
            pointers = stype.get_pointers(ctx.env.schema).objects(
                ctx.env.schema)
            for ptr in pointers:
                if ptr.is_id_pointer(ctx.env.schema):
                    view_shape = ctx.env.view_shapes[stype]
                    if ptr not in view_shape:
                        shape_metadata = ctx.env.view_shapes_metadata[stype]
                        view_shape.insert(0, ptr)
                        shape_metadata.has_implicit_id = True
                        shape_ptrs.insert(0, (ir_set, ptr))
                    break

    if (ir_set.typeref is not None and irtyputils.is_object(ir_set.typeref)
            and parent_view_type is not s_types.ViewType.Insert
            and parent_view_type is not s_types.ViewType.Update
            and ctx.implicit_tid_in_shapes):
        ql = qlast.ShapeElement(
            expr=qlast.Path(steps=[
                qlast.Ptr(
                    ptr=qlast.ObjectRef(name='__tid__'),
                    direction=s_pointers.PointerDirection.Outbound,
                )
            ], ),
            compexpr=qlast.Path(steps=[
                qlast.Source(),
                qlast.Ptr(
                    ptr=qlast.ObjectRef(name='__type__'),
                    direction=s_pointers.PointerDirection.Outbound,
                ),
                qlast.Ptr(
                    ptr=qlast.ObjectRef(name='id'),
                    direction=s_pointers.PointerDirection.Outbound,
                )
            ]))
        with ctx.newscope(fenced=True) as scopectx:
            scopectx.anchors = scopectx.anchors.copy()
            scopectx.anchors[qlast.Source] = ir_set
            ptr = _normalize_view_ptr_expr(ql,
                                           stype,
                                           path_id=ir_set.path_id,
                                           ctx=scopectx)
            view_shape = ctx.env.view_shapes[stype]
            if ptr not in view_shape:
                view_shape.insert(0, ptr)
                shape_ptrs.insert(0, (ir_set, ptr))

    return shape_ptrs
예제 #10
0
def _process_view(*,
                  stype: s_nodes.Node,
                  path_id: irast.PathId,
                  path_id_namespace: typing.Optional[
                      irast.WeakNamespace] = None,
                  elements: typing.List[qlast.ShapeElement],
                  view_rptr: typing.Optional[context.ViewRPtr] = None,
                  view_name: typing.Optional[sn.SchemaName] = None,
                  is_insert: bool = False,
                  is_update: bool = False,
                  ctx: context.CompilerContext) -> s_nodes.Node:
    view_scls = schemactx.derive_view(stype,
                                      is_insert=is_insert,
                                      is_update=is_update,
                                      derived_name=view_name,
                                      ctx=ctx)
    is_mutation = is_insert or is_update
    is_defining_shape = ctx.expr_exposed or is_mutation

    pointers = []

    for shape_el in elements:
        with ctx.newscope(fenced=True) as scopectx:
            pointers.append(
                _normalize_view_ptr_expr(shape_el,
                                         view_scls,
                                         path_id=path_id,
                                         path_id_namespace=path_id_namespace,
                                         is_insert=is_insert,
                                         is_update=is_update,
                                         view_rptr=view_rptr,
                                         ctx=scopectx))

    if is_insert:
        explicit_ptrs = {
            ptrcls.get_shortname(ctx.env.schema).name
            for ptrcls in pointers
        }

        scls_pointers = stype.get_pointers(ctx.env.schema)
        for pn, ptrcls in scls_pointers.items(ctx.env.schema):
            if (pn in explicit_ptrs
                    or ptrcls.is_pure_computable(ctx.env.schema)):
                continue

            if not ptrcls.get_default(ctx.env.schema):
                if ptrcls.get_required(ctx.env.schema):
                    if ptrcls.is_property(ctx.env.schema):
                        what = 'property'
                    else:
                        what = 'link'
                    raise errors.MissingRequiredError(
                        f'missing value for required {what} '
                        f'{stype.get_displayname(ctx.env.schema)}.'
                        f'{ptrcls.get_displayname(ctx.env.schema)}')
                else:
                    continue

            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            default_ql = qlast.ShapeElement(expr=qlast.Path(steps=[
                qlast.Ptr(ptr=qlast.ObjectRef(name=ptrcls_sn.name,
                                              module=ptrcls_sn.module))
            ]))

            with ctx.newscope(fenced=True) as scopectx:
                pointers.append(
                    _normalize_view_ptr_expr(
                        default_ql,
                        view_scls,
                        path_id=path_id,
                        path_id_namespace=path_id_namespace,
                        is_insert=is_insert,
                        is_update=is_update,
                        view_rptr=view_rptr,
                        ctx=scopectx))

    for ptrcls in pointers:
        if ptrcls.is_link_property(ctx.env.schema):
            source = view_rptr.derived_ptrcls
        else:
            source = view_scls

        if ptrcls.get_source(ctx.env.schema) is source:
            ctx.env.schema = source.add_pointer(ctx.env.schema,
                                                ptrcls,
                                                replace=True)

        if is_defining_shape:
            if source is None:
                # The nested shape is merely selecting the pointer,
                # so the link class has not been derived.  But for
                # the purposes of shape tracking, we must derive it
                # still.  The derived pointer must be treated the same
                # as the original, as this is not a new computable,
                # and both `Foo.ptr` and `Foo { ptr }` are the same path,
                # hence the `transparent` modifier.
                source = derive_ptrcls(view_rptr,
                                       target_scls=view_scls,
                                       transparent=True,
                                       ctx=ctx)

            ctx.env.view_shapes[source].append(ptrcls)

    if (view_rptr is not None and view_rptr.derived_ptrcls is not None
            and view_scls is not stype):
        ctx.env.schema = view_scls.set_field_value(ctx.env.schema, 'rptr',
                                                   view_rptr.derived_ptrcls)

    return view_scls
예제 #11
0
파일: cast.py 프로젝트: willingc/edgedb
def _cast_array(ir_set: irast.Set, orig_stype: s_types.Type,
                new_stype: s_types.Type, *,
                srcctx: typing.Optional[parsing.ParserContext],
                ctx: context.ContextLevel) -> irast.Set:

    assert isinstance(orig_stype, s_types.Array)

    direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx)

    if direct_cast is None:
        if not new_stype.is_array():
            raise errors.QueryError(
                f'cannot cast {orig_stype.get_displayname(ctx.env.schema)!r} '
                f'to {new_stype.get_displayname(ctx.env.schema)!r}',
                context=srcctx)
        assert isinstance(new_stype, s_types.Array)
        el_type = new_stype.get_subtypes(ctx.env.schema)[0]
    else:
        el_type = new_stype

    orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0]

    el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx)

    if el_cast is not None and el_cast.get_from_cast(ctx.env.schema):
        # Simple cast
        return _cast_to_ir(ir_set, el_cast, orig_stype, new_stype, ctx=ctx)
    else:
        pathctx.register_set_in_scope(ir_set, ctx=ctx)

        with ctx.new() as subctx:
            subctx.anchors = subctx.anchors.copy()
            source_alias = subctx.aliases.get('a')
            subctx.anchors[source_alias] = ir_set

            unpacked = qlast.FunctionCall(
                func=('std', 'array_unpack'),
                args=[
                    qlast.Path(steps=[qlast.ObjectRef(name=source_alias)], ),
                ],
            )

            elements = qlast.FunctionCall(
                func=('std', 'array_agg'),
                args=[
                    qlast.TypeCast(
                        expr=unpacked,
                        type=typegen.type_to_ql_typeref(el_type, ctx=subctx),
                    ),
                ],
            )

            array_ir = dispatch.compile(elements, ctx=subctx)
            assert isinstance(array_ir, irast.Set)

            if direct_cast is not None:
                array_stype = s_types.Array.from_subtypes(
                    ctx.env.schema, [el_type])
                return _cast_to_ir(array_ir,
                                   direct_cast,
                                   array_stype,
                                   new_stype,
                                   ctx=ctx)
            else:
                return array_ir
예제 #12
0
def sdl_to_ddl(schema, documents):
    ddlgraph = {}
    mods = []

    ctx = LayoutTraceContext(
        schema,
        local_modules=frozenset(mod for mod, schema_decl in documents),
    )

    for module_name, declarations in documents:
        ctx.set_module(module_name)
        for decl_ast in declarations:
            if isinstance(decl_ast, qlast.CreateObject):
                _, fq_name = ctx.get_fq_name(decl_ast)
                if isinstance(decl_ast, (qlast.CreateObjectType,
                                         qlast.CreateAlias)):
                    ctx.objects[fq_name] = qltracer.ObjectType(fq_name)

                elif isinstance(decl_ast, qlast.CreateScalarType):
                    ctx.objects[fq_name] = qltracer.Type(fq_name)

                elif isinstance(decl_ast, (qlast.CreateLink,
                                           qlast.CreateProperty)):
                    ctx.objects[fq_name] = qltracer.Pointer(
                        fq_name, source=None, target=None)
                elif isinstance(decl_ast, qlast.CreateFunction):
                    ctx.objects[fq_name] = qltracer.Function(fq_name)
                elif isinstance(decl_ast, qlast.CreateConstraint):
                    ctx.objects[fq_name] = qltracer.Constraint(fq_name)
                elif isinstance(decl_ast, qlast.CreateAnnotation):
                    ctx.objects[fq_name] = qltracer.Annotation(fq_name)
                else:
                    raise AssertionError(
                        f'unexpected SDL declaration: {decl_ast}')

    for module_name, declarations in documents:
        ctx.set_module(module_name)
        for decl_ast in declarations:
            trace_layout(decl_ast, ctx=ctx)

    # compute the ancestors graph
    for fq_name in ctx.parents.keys():
        ctx.ancestors[fq_name] = get_ancestors(
            fq_name, ctx.ancestors, ctx.parents)

    topological.normalize(ctx.inh_graph, _merge_items)

    ctx = DepTraceContext(
        schema, ddlgraph, ctx.objects, ctx.parents, ctx.ancestors,
        ctx.defdeps
    )
    for module_name, declarations in documents:
        ctx.set_module(module_name)
        # module needs to be created regardless of whether its
        # contents are empty or not
        mods.append(qlast.CreateModule(
            name=qlast.ObjectRef(name=module_name)))
        for decl_ast in declarations:
            trace_dependencies(decl_ast, ctx=ctx)

    return mods + list(topological.sort(ddlgraph, allow_unresolved=False))
예제 #13
0
파일: viewgen.py 프로젝트: willingc/edgedb
def _process_view(
        *,
        stype: s_types.Type,
        path_id: irast.PathId,
        path_id_namespace: typing.Optional[irast.WeakNamespace]=None,
        elements: typing.List[qlast.ShapeElement],
        view_rptr: typing.Optional[context.ViewRPtr]=None,
        view_name: typing.Optional[sn.SchemaName]=None,
        is_insert: bool=False,
        is_update: bool=False,
        ctx: context.ContextLevel) -> s_types.Type:

    if (view_name is None and ctx.env.schema_view_mode
            and view_rptr is not None):
        # Make sure persistent schema views have properly formed
        # names as opposed to the usual mangled form of the ephemeral
        # views.  This is needed for introspection readability, as well
        # as helps in maintaining proper type names for schema
        # representations that require alphanumeric names, such as
        # GraphQL.
        #
        # We use the name of the source together with the name
        # of the inbound link to form the name, so in e.g.
        #    CREATE VIEW V := (SELECT Foo { bar: { baz: { ... } })
        # The name of the innermost view would be "__V__bar__baz".
        source_name = view_rptr.source.get_name(ctx.env.schema).name
        if not source_name.startswith('__'):
            source_name = f'__{source_name}'
        if view_rptr.ptrcls_name is not None:
            ptr_name = view_rptr.ptrcls_name.name
        elif view_rptr.ptrcls is not None:
            ptr_name = view_rptr.ptrcls.get_shortname(ctx.env.schema).name
        else:
            raise errors.InternalServerError(
                '_process_view in schema mode received view_rptr with '
                'neither ptrcls_name, not ptrcls'
            )

        name = f'{source_name}__{ptr_name}'
        view_name = sn.Name(
            module=ctx.derived_target_module or '__derived__',
            name=name,
        )

    view_scls = schemactx.derive_view(
        stype, is_insert=is_insert, is_update=is_update,
        derived_name=view_name, ctx=ctx)
    is_mutation = is_insert or is_update
    is_defining_shape = ctx.expr_exposed or is_mutation

    if view_rptr is not None and view_rptr.ptrcls is None:
        derive_ptrcls(
            view_rptr, target_scls=view_scls,
            transparent=True, ctx=ctx)

    pointers = []

    for shape_el in elements:
        with ctx.newscope(fenced=True) as scopectx:
            pointers.append(_normalize_view_ptr_expr(
                shape_el, view_scls, path_id=path_id,
                path_id_namespace=path_id_namespace,
                is_insert=is_insert, is_update=is_update,
                view_rptr=view_rptr, ctx=scopectx))

    if is_insert:
        assert isinstance(stype, s_objtypes.ObjectType)
        explicit_ptrs = {ptrcls.get_shortname(ctx.env.schema).name
                         for ptrcls in pointers}

        scls_pointers = stype.get_pointers(ctx.env.schema)
        for pn, ptrcls in scls_pointers.items(ctx.env.schema):
            if (pn in explicit_ptrs or
                    ptrcls.is_pure_computable(ctx.env.schema)):
                continue

            if not ptrcls.get_default(ctx.env.schema):
                if ptrcls.get_required(ctx.env.schema):
                    if ptrcls.is_property(ctx.env.schema):
                        what = 'property'
                    else:
                        what = 'link'
                    raise errors.MissingRequiredError(
                        f'missing value for required {what} '
                        f'{stype.get_displayname(ctx.env.schema)}.'
                        f'{ptrcls.get_displayname(ctx.env.schema)}')
                else:
                    continue

            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            default_ql = qlast.ShapeElement(expr=qlast.Path(steps=[
                qlast.Ptr(ptr=qlast.ObjectRef(name=ptrcls_sn.name,
                                              module=ptrcls_sn.module))
            ]))

            with ctx.newscope(fenced=True) as scopectx:
                pointers.append(_normalize_view_ptr_expr(
                    default_ql, view_scls, path_id=path_id,
                    path_id_namespace=path_id_namespace,
                    is_insert=is_insert, is_update=is_update,
                    view_rptr=view_rptr, ctx=scopectx))

    for ptrcls in pointers:
        if ptrcls.is_link_property(ctx.env.schema):
            assert view_rptr is not None
            source = view_rptr.ptrcls
        else:
            source = view_scls

        if is_defining_shape:
            ctx.env.view_shapes[source].append(ptrcls)

    if (view_rptr is not None and view_rptr.ptrcls is not None and
            view_scls is not stype):
        ctx.env.schema = view_scls.set_field_value(
            ctx.env.schema, 'rptr', view_rptr.ptrcls)

    return view_scls
예제 #14
0
    def reduce_DOT_ICONST(self, *kids):
        # this is a valid link-like syntax for accessing unnamed tuples
        from edb.schema import pointers as s_pointers

        self.val = qlast.Ptr(ptr=qlast.ObjectRef(name=kids[1].val),
                             direction=s_pointers.PointerDirection.Outbound)
예제 #15
0
def _cast_array(ir_set: irast.Set, orig_stype: s_types.Type,
                new_stype: s_types.Type, *,
                srcctx: Optional[parsing.ParserContext],
                ctx: context.ContextLevel) -> irast.Set:

    assert isinstance(orig_stype, s_types.Array)

    direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx)

    if direct_cast is None:
        if not new_stype.is_array():
            raise errors.QueryError(
                f'cannot cast {orig_stype.get_displayname(ctx.env.schema)!r} '
                f'to {new_stype.get_displayname(ctx.env.schema)!r}',
                context=srcctx)
        assert isinstance(new_stype, s_types.Array)
        el_type = new_stype.get_subtypes(ctx.env.schema)[0]
    else:
        el_type = new_stype

    orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0]

    el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx)

    if el_cast is not None and el_cast.get_from_cast(ctx.env.schema):
        # Simple cast
        return _cast_to_ir(ir_set, el_cast, orig_stype, new_stype, ctx=ctx)
    else:
        pathctx.register_set_in_scope(ir_set, ctx=ctx)

        with ctx.new() as subctx:
            subctx.anchors = subctx.anchors.copy()
            source_alias = subctx.aliases.get('a')
            subctx.anchors[source_alias] = ir_set

            unpacked = qlast.FunctionCall(
                func=('__std__', 'array_unpack'),
                args=[
                    qlast.Path(steps=[qlast.ObjectRef(name=source_alias)], ),
                ],
            )

            enumerated = setgen.ensure_set(
                dispatch.compile(
                    qlast.FunctionCall(
                        func=('__std__', 'enumerate'),
                        args=[unpacked],
                    ),
                    ctx=subctx,
                ),
                ctx=subctx,
            )

            enumerated_alias = subctx.aliases.get('e')
            subctx.anchors[enumerated_alias] = enumerated
            enumerated_ref = qlast.Path(
                steps=[qlast.ObjectRef(name=enumerated_alias)], )

            elements = qlast.FunctionCall(
                func=('__std__', 'array_agg'),
                args=[
                    qlast.SelectQuery(
                        result=qlast.TypeCast(
                            expr=qlast.Path(steps=[
                                enumerated_ref,
                                qlast.Ptr(ptr=qlast.ObjectRef(
                                    name='1',
                                    direction='>',
                                ), ),
                            ], ),
                            type=typegen.type_to_ql_typeref(
                                el_type,
                                ctx=subctx,
                            ),
                            cardinality_mod=qlast.CardinalityModifier.Required,
                        ),
                        orderby=[
                            qlast.SortExpr(
                                path=qlast.Path(steps=[
                                    enumerated_ref,
                                    qlast.Ptr(ptr=qlast.ObjectRef(
                                        name='0',
                                        direction='>',
                                    ), ),
                                ], ),
                                direction=qlast.SortOrder.Asc,
                            ),
                        ],
                    ),
                ],
            )

            if el_type.contains_json(subctx.env.schema):
                subctx.inhibit_implicit_limit = True

            array_ir = dispatch.compile(elements, ctx=subctx)
            assert isinstance(array_ir, irast.Set)

            if direct_cast is not None:
                ctx.env.schema, array_stype = s_types.Array.from_subtypes(
                    ctx.env.schema, [el_type])
                return _cast_to_ir(array_ir,
                                   direct_cast,
                                   array_stype,
                                   new_stype,
                                   ctx=ctx)
            else:
                return array_ir
예제 #16
0
 def reduce_Identifier(self, *kids):
     self.val = qlast.Path(steps=[qlast.ObjectRef(name=kids[0].val)])
예제 #17
0
def name_path(name: str) -> qlast.Path:
    return qlast.Path(steps=[qlast.ObjectRef(name=name)])
예제 #18
0
 def reduce_BaseName(self, *kids):
     self.val = qlast.ObjectRef(module='.'.join(kids[0].val[:-1]) or None,
                                name=kids[0].val[-1])
예제 #19
0
def typeref_to_ast(
    schema: s_schema.Schema,
    ref: Union[so.Object, so.ObjectShell],
    *,
    _name: Optional[str] = None,
    disambiguate_std: bool = False,
) -> qlast.TypeExpr:
    from . import types as s_types

    if isinstance(ref, so.ObjectShell):
        return shell_to_ast(schema, ref)
    else:
        t = ref

    result: qlast.TypeExpr
    components: Tuple[so.Object, ...]

    if t.is_type() and cast(s_types.Type, t).is_any(schema):
        result = qlast.TypeName(name=_name, maintype=qlast.AnyType())
    elif t.is_type() and cast(s_types.Type, t).is_anytuple(schema):
        result = qlast.TypeName(name=_name, maintype=qlast.AnyTuple())
    elif isinstance(t, s_types.Tuple) and t.is_named(schema):
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    typeref_to_ast(
                                        schema,
                                        st,
                                        _name=sn,
                                        disambiguate_std=disambiguate_std)
                                    for sn, st in t.iter_subtypes(schema)
                                ])
    elif isinstance(t, (s_types.Array, s_types.Tuple)):
        # Here the concrete type Array is used because t.schema_name is used,
        # which is not defined for more generic collections and abcs
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    typeref_to_ast(
                                        schema,
                                        st,
                                        disambiguate_std=disambiguate_std)
                                    for st in t.get_subtypes(schema)
                                ])
    elif t.is_type() and cast(s_types.Type, t).is_union_type(schema):
        object_set: Optional[so.ObjectSet[s_types.Type]] = \
            cast(s_types.Type, t).get_union_of(schema)
        assert object_set is not None

        component_objects = tuple(object_set.objects(schema))
        result = typeref_to_ast(schema,
                                component_objects[0],
                                disambiguate_std=disambiguate_std)
        for component_object in component_objects[1:]:
            result = qlast.TypeOp(
                left=result,
                op='|',
                right=typeref_to_ast(schema,
                                     component_object,
                                     disambiguate_std=disambiguate_std),
            )
    elif isinstance(t, so.QualifiedObject):
        t_name = t.get_name(schema)
        module = t_name.module
        if disambiguate_std and module == 'std':
            # If the type is defined in 'std::', replace the module to
            # '__std__' to handle cases where 'std' name is aliased to
            # another module.
            module = '__std__'
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(module=module,
                                                         name=t_name.name))
    else:
        raise NotImplementedError(f'cannot represent {t!r} as a shell')

    return result
예제 #20
0
 def reduce_NodeName(self, *kids):
     self.val = qlast.Path(steps=[
         qlast.ObjectRef(name=kids[0].val.name, module=kids[0].val.module)
     ])
예제 #21
0
def shell_to_ast(
    schema: s_schema.Schema,
    t: so.ObjectShell,
    *,
    _name: Optional[str] = None,
) -> qlast.TypeExpr:
    from . import pseudo as s_pseudo
    from . import types as s_types

    result: qlast.TypeExpr
    qlref: qlast.BaseObjectRef

    if isinstance(t, s_pseudo.PseudoTypeShell):
        if t.name == 'anytype':
            qlref = qlast.AnyType()
        elif t.name == 'anytuple':
            qlref = qlast.AnyTuple()
        else:
            raise AssertionError(f'unexpected pseudo type shell: {t.name!r}')
        result = qlast.TypeName(name=_name, maintype=qlref)
    elif isinstance(t, s_types.TupleTypeShell):
        if t.is_named():
            result = qlast.TypeName(name=_name,
                                    maintype=qlast.ObjectRef(name='tuple', ),
                                    subtypes=[
                                        shell_to_ast(schema, st, _name=sn)
                                        for sn, st in t.iter_subtypes(schema)
                                    ])
        else:
            result = qlast.TypeName(name=_name,
                                    maintype=qlast.ObjectRef(name='tuple', ),
                                    subtypes=[
                                        shell_to_ast(schema, st)
                                        for st in t.get_subtypes(schema)
                                    ])
    elif isinstance(t, s_types.ArrayTypeShell):
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name='array', ),
                                subtypes=[
                                    shell_to_ast(schema, st)
                                    for st in t.get_subtypes(schema)
                                ])
    elif isinstance(t, s_types.UnionTypeShell):
        components = t.get_components(schema)
        result = typeref_to_ast(schema, components[0])
        for component in components[1:]:
            result = qlast.TypeOp(
                left=result,
                op='|',
                right=typeref_to_ast(schema, component),
            )
    elif isinstance(t, so.ObjectShell):
        name = t.name
        if isinstance(name, sn.SchemaName):
            qlref = qlast.ObjectRef(
                module=name.module,
                name=name.name,
            )
        else:
            qlref = qlast.ObjectRef(
                module='',
                name=name,
            )
        result = qlast.TypeName(
            name=_name,
            maintype=qlref,
        )
    else:
        raise NotImplementedError(f'cannot represent {t!r} as a shell')

    return result
예제 #22
0
def _get_shape_configuration(
    ir_set: irast.Set,
    *,
    rptr: Optional[irast.Pointer]=None,
    parent_view_type: Optional[s_types.ExprType]=None,
    ctx: context.ContextLevel
) -> List[Tuple[irast.Set, s_pointers.Pointer, qlast.ShapeOp]]:

    """Return a list of (source_set, ptrcls) pairs as a shape for a given set.
    """

    stype = setgen.get_set_type(ir_set, ctx=ctx)

    sources: List[
        Union[s_types.Type, s_pointers.PointerLike]] = []
    link_view = False
    is_objtype = ir_set.path_id.is_objtype_path()

    if rptr is None:
        rptr = ir_set.rptr

    if rptr is not None:
        rptrcls = typegen.ptrcls_from_ptrref(rptr.ptrref, ctx=ctx)
    else:
        rptrcls = None

    link_view = (
        rptrcls is not None and
        not rptrcls.is_link_property(ctx.env.schema) and
        _link_has_shape(rptrcls, ctx=ctx)
    )

    if is_objtype or not link_view:
        sources.append(stype)

    if link_view:
        sources.append(rptrcls)

    shape_ptrs = []

    for source in sources:
        for ptr, shape_op in ctx.env.view_shapes[source]:
            if (ptr.is_link_property(ctx.env.schema) and
                    ir_set.path_id != rptr.target.path_id):
                path_tip = rptr.target
            else:
                path_tip = ir_set

            shape_ptrs.append((path_tip, ptr, shape_op))

    if is_objtype:
        assert isinstance(stype, s_objtypes.ObjectType)

        view_type = stype.get_expr_type(ctx.env.schema)
        is_mutation = view_type in (s_types.ExprType.Insert,
                                    s_types.ExprType.Update)
        is_parent_update = parent_view_type is s_types.ExprType.Update

        implicit_id = (
            # shape is not specified at all
            not shape_ptrs
            # implicit ids are always wanted
            or (ctx.implicit_id_in_shapes and not is_mutation)
            # we are inside an UPDATE shape and this is
            # an explicit expression (link target update)
            or (is_parent_update and ir_set.expr is not None)
        )

        if implicit_id:
            # We want the id in this shape and it's not already there,
            # so insert it in the first position.
            pointers = stype.get_pointers(ctx.env.schema).objects(
                ctx.env.schema)
            view_shape = ctx.env.view_shapes[stype]
            view_shape_ptrs = {p for p, _ in view_shape}
            for ptr in pointers:
                if ptr.is_id_pointer(ctx.env.schema):
                    if ptr not in view_shape_ptrs:
                        shape_metadata = ctx.env.view_shapes_metadata[stype]
                        view_shape.insert(0, (ptr, qlast.ShapeOp.ASSIGN))
                        shape_metadata.has_implicit_id = True
                        shape_ptrs.insert(
                            0, (ir_set, ptr, qlast.ShapeOp.ASSIGN))
                    break

    is_mutation = parent_view_type in {
        s_types.ExprType.Insert,
        s_types.ExprType.Update
    }

    implicit_tid = (
        stype is not None
        and has_implicit_tid(stype, is_mutation=is_mutation, ctx=ctx)
    )

    if implicit_tid:
        assert isinstance(stype, s_objtypes.ObjectType)

        try:
            ptr = setgen.resolve_ptr(stype, '__tid__', ctx=ctx)
        except errors.InvalidReferenceError:
            ql = qlast.ShapeElement(
                expr=qlast.Path(
                    steps=[qlast.Ptr(
                        ptr=qlast.ObjectRef(name='__tid__'),
                        direction=s_pointers.PointerDirection.Outbound,
                    )],
                ),
                compexpr=qlast.Path(
                    steps=[
                        qlast.Source(),
                        qlast.Ptr(
                            ptr=qlast.ObjectRef(name='__type__'),
                            direction=s_pointers.PointerDirection.Outbound,
                        ),
                        qlast.Ptr(
                            ptr=qlast.ObjectRef(name='id'),
                            direction=s_pointers.PointerDirection.Outbound,
                        )
                    ]
                )
            )
            with ctx.newscope(fenced=True) as scopectx:
                scopectx.anchors = scopectx.anchors.copy()
                scopectx.anchors[qlast.Source().name] = ir_set
                ptr = _normalize_view_ptr_expr(
                    ql, stype, path_id=ir_set.path_id,
                    ctx=scopectx)

        view_shape = ctx.env.view_shapes[stype]
        view_shape_ptrs = {p for p, _ in view_shape}
        if ptr not in view_shape_ptrs:
            view_shape.insert(0, (ptr, qlast.ShapeOp.ASSIGN))
            shape_ptrs.insert(0, (ir_set, ptr, qlast.ShapeOp.ASSIGN))

    return shape_ptrs
예제 #23
0
    def visit_Field(self, node):
        if self._is_duplicate_field(node):
            return

        is_top, path, prevt, target, steps = \
            self._prepare_field(node)

        json_mode = False
        is_shadowed = prevt.is_field_shadowed(node.name.value)

        # determine if there needs to be extra subqueries
        if not prevt.dummy and target.dummy:
            json_mode = True

            # this is a special introspection type
            eql, shape, filterable = target.get_template()

            spec = qlast.ShapeElement(
                expr=qlast.Path(
                    steps=[qlast.Ptr(
                        ptr=qlast.ObjectRef(
                            name=(node.alias or node.name).value
                        )
                    )]
                ),
                compexpr=eql,
            )

        elif is_shadowed and not node.alias:
            # shadowed field that doesn't need an alias
            spec = filterable = shape = qlast.ShapeElement(
                expr=qlast.Path(steps=steps),
            )

        elif not node.selection_set or is_shadowed and node.alias:
            # this is either an unshadowed terminal field or an aliased
            # shadowed field
            prefix = qlast.Path(steps=self.get_path_prefix(-1))
            eql, shape, filterable = prevt.get_field_template(
                node.name.value,
                parent=prefix,
                has_shape=bool(node.selection_set)
            )
            spec = qlast.ShapeElement(
                expr=qlast.Path(
                    steps=[qlast.Ptr(
                        ptr=qlast.ObjectRef(
                            # this is already a sub-query
                            name=(node.alias or node.name).value
                        )
                    )]
                ),
                compexpr=eql,
                # preserve the original cardinality of the computable
                # aliased fields
                cardinality=prevt.get_field_cardinality(node.name.value),
            )

        else:
            # if the parent is NOT a shadowed type, we need an explicit SELECT
            eql, shape, filterable = target.get_template()
            spec = qlast.ShapeElement(
                expr=qlast.Path(
                    steps=[qlast.Ptr(
                        ptr=qlast.ObjectRef(
                            # this is already a sub-query
                            name=(node.alias or node.name).value
                        )
                    )]
                ),
                compexpr=eql,
                # preserve the original cardinality of the computable,
                # which is basically one of the top-level query
                # fields, all of which are returning lists
                cardinality=qltypes.Cardinality.MANY,
            )

        if node.selection_set is not None:
            if not json_mode:
                # a single recursion target, so we can process
                # selection set now
                self._context.fields.append({})
                vals = self.visit(node.selection_set)
                self._context.fields.pop()

                if shape:
                    shape.elements = vals
                if filterable:
                    where, orderby, offset, limit = \
                        self._visit_arguments(node.arguments)
                    filterable.where = where
                    filterable.orderby = orderby
                    filterable.offset = offset
                    filterable.limit = limit

        path.pop()
        return spec
예제 #24
0
 def reduce_INDEX_OnExpr_CreateIndexSDLCommandsBlock(self, *kids):
     self.val = qlast.CreateIndex(
         name=qlast.ObjectRef(name='idx'),
         expr=kids[1].val,
         commands=kids[2].val,
     )
예제 #25
0
파일: utils.py 프로젝트: mrs45p/edgedb
def typeref_to_ast(schema: s_schema.Schema,
                   t: so.Object,
                   *,
                   _name: Optional[str] = None) -> qlast.TypeExpr:
    from . import types as s_types

    if isinstance(t, so.ObjectRef):
        # We want typenames like 'anytype` that are wrapped in an
        # ObjectRef to be unwrapped to proper types, so that we
        # can generate proper AST nodes for them (e.g. for `anytype` it
        # is `qlast.AnyType()`).
        t = t._resolve_ref(schema)

    result: qlast.TypeExpr
    components: Tuple[so.ObjectRef, ...]

    if t.is_type() and cast(s_types.Type, t).is_any():
        result = qlast.TypeName(name=_name, maintype=qlast.AnyType())
    elif t.is_type() and cast(s_types.Type, t).is_anytuple():
        result = qlast.TypeName(name=_name, maintype=qlast.AnyTuple())
    elif isinstance(t, s_types.Tuple) and t.named:
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    typeref_to_ast(schema,
                                                   cast(so.ObjectRef, st),
                                                   _name=sn)
                                    for sn, st in t.iter_subtypes(schema)
                                ])
    elif isinstance(t, (s_types.Array, s_types.Tuple)):
        # Here the concrete type Array is used because t.schema_name is used,
        # which is not defined for more generic collections and abcs
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(name=t.schema_name),
                                subtypes=[
                                    typeref_to_ast(schema, st)
                                    for st in t.get_subtypes(schema)
                                ])
    elif isinstance(t, s_types.UnionTypeRef):
        components = t.get_union_of(schema)
        result = typeref_to_ast(schema, components[0])
        for component in components[1:]:
            result = qlast.TypeOp(
                left=result,
                op='|',
                right=typeref_to_ast(schema, component),
            )
    elif t.is_type() and cast(s_types.Type, t).is_union_type(schema):
        object_set: Optional[so.ObjectSet[s_types.Type]] = \
            cast(s_types.Type, t).get_union_of(schema)
        assert object_set is not None

        components = tuple(object_set.objects(schema))
        result = typeref_to_ast(schema, components[0])
        for component in components[1:]:
            result = qlast.TypeOp(
                left=result,
                op='|',
                right=typeref_to_ast(schema, component),
            )
    else:
        result = qlast.TypeName(name=_name,
                                maintype=qlast.ObjectRef(
                                    module=t.get_name(schema).module,
                                    name=t.get_name(schema).name))

    return result