示例#1
0
def compile_SessionStateDecl(
        decl: qlast.SessionStateDecl, *,
        ctx: context.ContextLevel) -> irast.SessionStateCmd:

    aliases = {}

    for item in decl.items:
        if isinstance(item, qlast.ModuleAliasDecl):
            try:
                module = ctx.schema.get_module(item.module)
            except LookupError:
                raise errors.EdgeQLError(
                    f'module {item.module!r} does not exist',
                    context=item.context
                )

            aliases[item.alias] = module

        else:
            raise errors.EdgeQLError(
                f'expression aliases in SET are not supported yet',
                context=item.context
            )

    return irast.SessionStateCmd(
        modaliases=aliases
    )
示例#2
0
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        cmd = super()._cmd_tree_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            if astnode.args:
                args = []

                for arg in astnode.args:
                    arg_expr = s_expr.ExpressionText(
                        edgeql.generate_source(arg.arg, pretty=False))
                    args.append(arg_expr)

                cmd.add(sd.AlterObjectProperty(property='args',
                                               new_value=args))

        elif isinstance(astnode, qlast.CreateConstraint):
            if astnode.args:
                paramnames, paramdefaults, paramtypes, paramkinds, variadic = \
                    s_func.parameters_from_ast(
                        astnode, context.modaliases, schema)

                if variadic is not None:
                    cmd.add(
                        sd.AlterObjectProperty(property='varparam',
                                               new_value=variadic))

                for pname, pdefault, ptype in zip(paramnames, paramdefaults,
                                                  paramtypes):
                    if pname is not None:
                        raise ql_errors.EdgeQLError(
                            'constraints do not support named parameters',
                            context=astnode.context)

                    if pdefault is not None:
                        raise ql_errors.EdgeQLError(
                            'constraints do not support parameters '
                            'with defaults',
                            context=astnode.context)

                    if ptype is None:
                        raise ql_errors.EdgeQLError('untyped parameter',
                                                    context=astnode.context)

                cmd.add(
                    sd.AlterObjectProperty(property='paramtypes',
                                           new_value=paramtypes))

        # 'subject' can be present in either astnode type
        if astnode.subject:
            subjectexpr = s_expr.ExpressionText(
                edgeql.generate_source(astnode.subject, pretty=False))

            cmd.add(
                sd.AlterObjectProperty(property='subjectexpr',
                                       new_value=subjectexpr))

        cls._validate_subcommands(astnode)

        return cmd
示例#3
0
def _cast_expr(ql_type: qlast.TypeName, ir_expr: irast.Base, *,
               source_context: parsing.ParserContext,
               ctx: context.ContextLevel) -> irast.Base:
    try:
        orig_type = irutils.infer_type(ir_expr, ctx.schema)
    except errors.EdgeQLError:
        # It is possible that the source expression is unresolved
        # if the expr is an empty set (or a coalesce of empty sets).
        orig_type = None

    if isinstance(orig_type, s_types.Tuple):
        # For tuple-to-tuple casts we generate a new tuple
        # to simplify things on sqlgen side.
        new_type = typegen.ql_typeref_to_type(ql_type, ctx=ctx)
        if not isinstance(new_type, s_types.Tuple):
            raise errors.EdgeQLError(f'cannot cast tuple to {new_type.name}',
                                     context=source_context)

        if len(orig_type.element_types) != len(new_type.element_types):
            raise errors.EdgeQLError(
                f'cannot cast to {new_type.name}: '
                f'number of elements is not the same',
                context=source_context)

        new_names = list(new_type.element_types)

        elements = []
        for i, n in enumerate(orig_type.element_types):
            val = setgen.generated_set(irast.TupleIndirection(expr=ir_expr,
                                                              name=n),
                                       ctx=ctx)
            val.path_id = irutils.tuple_indirection_path_id(
                ir_expr.path_id, n, orig_type.element_types[n])

            val_type = irutils.infer_type(val, ctx.schema)
            new_el_name = new_names[i]
            if val_type != new_type.element_types[new_el_name]:
                # Element cast
                val = _cast_expr(ql_type.subtypes[i],
                                 val,
                                 ctx=ctx,
                                 source_context=source_context)

            elements.append(irast.TupleElement(name=new_el_name, val=val))

        return irast.Tuple(named=new_type.named, elements=elements)

    elif isinstance(ir_expr, irast.EmptySet):
        # For the common case of casting an empty set, we simply
        # generate a new EmptySet node of the requested type.
        scls = typegen.ql_typeref_to_type(ql_type, ctx=ctx)
        return irutils.new_empty_set(ctx.schema,
                                     scls=scls,
                                     alias=ir_expr.path_id[-1].name.name)

    else:
        typ = typegen.ql_typeref_to_ir_typeref(ql_type, ctx=ctx)
        return irast.TypeCast(expr=ir_expr, type=typ)
示例#4
0
文件: types.py 项目: virajs/edgedb
def __infer_array(ir, schema):
    if ir.elements:
        element_type = _infer_common_type(ir.elements, schema)
        if element_type is None:
            raise ql_errors.EdgeQLError('could not determine array type',
                                        context=ir.context)
    else:
        raise ql_errors.EdgeQLError('could not determine type of empty array',
                                    context=ir.context)

    return s_types.Array(element_type=element_type)
示例#5
0
文件: types.py 项目: virajs/edgedb
def __infer_map(ir, schema):
    if not ir.keys:
        raise ql_errors.EdgeQLError('could not determine type of empty map',
                                    context=ir.context)

    key_type = _infer_common_type(ir.keys, schema)
    if key_type is None:
        raise ql_errors.EdgeQLError('could not determine map keys type',
                                    context=ir.context)

    element_type = _infer_common_type(ir.values, schema)
    if element_type is None:
        raise ql_errors.EdgeQLError('could not determine map values type',
                                    context=ir.context)

    return s_types.Map(key_type=key_type, element_type=element_type)
示例#6
0
def compile_UpdateQuery(
        expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Base:
    with ctx.subquery() as ictx:
        stmt = irast.UpdateStmt()
        init_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

        subject = dispatch.compile(expr.subject, ctx=ictx)
        subj_type = irutils.infer_type(subject, ictx.schema)
        if not isinstance(subj_type, s_objtypes.ObjectType):
            raise errors.EdgeQLError(
                f'cannot update non-ObjectType objects',
                context=expr.subject.context
            )

        stmt.subject = compile_query_subject(
            subject,
            shape=expr.shape,
            view_rptr=ctx.view_rptr,
            compile_views=True,
            result_alias=expr.subject_alias,
            is_update=True,
            ctx=ictx)

        stmt.result = setgen.class_set(
            stmt.subject.scls.material_type(), ctx=ctx)

        stmt.where = clauses.compile_where_clause(
            expr.where, ctx=ictx)

        result = fini_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

    return result
示例#7
0
文件: pathctx.py 项目: virajs/edgedb
def enforce_singleton(expr: irast.Base, *, ctx: context.ContextLevel) -> None:
    cardinality = infer_cardinality(expr, ctx=ctx)
    if cardinality != irast.Cardinality.ONE:
        raise errors.EdgeQLError(
            'possibly more than one element returned by an expression '
            'where only singletons are allowed',
            context=expr.context)
示例#8
0
文件: types.py 项目: virajs/edgedb
def __infer_binop(ir, schema):
    left_type, right_type = _infer_binop_args(ir.left, ir.right, schema)

    if isinstance(ir.op,
                  (ast.ops.ComparisonOperator, ast.ops.TypeCheckOperator,
                   ast.ops.MembershipOperator)):
        result = schema.get('std::bool')
    else:
        result = s_basetypes.TypeRules.get_result(ir.op,
                                                  (left_type, right_type),
                                                  schema)

        if result is None:
            result = s_basetypes.TypeRules.get_result(
                (ir.op, 'reversed'), (right_type, left_type), schema)

        if result is None:
            if right_type.implicitly_castable_to(left_type, schema):
                right_type = left_type
            elif left_type.implicitly_castable_to(right_type, schema):
                left_type = right_type

            result = s_basetypes.TypeRules.get_result(
                (ir.op, 'reversed'), (right_type, left_type), schema)

    if result is None:
        raise ql_errors.EdgeQLError(
            f'binary operator `{ir.op.upper()}` is not defined for types '
            f'{left_type.name} and {right_type.name}',
            context=ir.left.context)

    return result
示例#9
0
def compile_IfElse(expr: qlast.IfElse, *,
                   ctx: context.ContextLevel) -> irast.Base:

    condition = setgen.ensure_set(dispatch.compile(expr.condition, ctx=ctx),
                                  ctx=ctx)

    ql_if_expr = expr.if_expr
    ql_else_expr = expr.else_expr

    with ctx.newscope(fenced=True) as scopectx:
        if_expr = dispatch.compile(ql_if_expr, ctx=scopectx)

    with ctx.newscope(fenced=True) as scopectx:
        else_expr = dispatch.compile(ql_else_expr, ctx=scopectx)

    if_expr_type = irutils.infer_type(if_expr, ctx.schema)
    else_expr_type = irutils.infer_type(else_expr, ctx.schema)

    result = s_utils.get_class_nearest_common_ancestor(
        [if_expr_type, else_expr_type])

    if result is None:
        raise errors.EdgeQLError(
            'if/else clauses must be of related types, got: {}/{}'.format(
                if_expr_type.name, else_expr_type.name),
            context=expr.context)

    return setgen.generated_set(irast.IfElseExpr(if_expr=if_expr,
                                                 else_expr=else_expr,
                                                 condition=condition),
                                ctx=ctx)
示例#10
0
def compile_DeleteQuery(
        expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Base:
    with ctx.subquery() as ictx:
        stmt = irast.DeleteStmt()
        init_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

        # DELETE Expr is a delete(SET OF X), so we need a scope fence.
        with ictx.newscope(fenced=True) as scopectx:
            subject = setgen.scoped_set(
                dispatch.compile(expr.subject, ctx=scopectx), ctx=scopectx)

        subj_type = irutils.infer_type(subject, ictx.schema)
        if not isinstance(subj_type, s_objtypes.ObjectType):
            raise errors.EdgeQLError(
                f'cannot delete non-ObjectType objects',
                context=expr.subject.context
            )

        stmt.subject = compile_query_subject(
            subject, shape=None, result_alias=expr.subject_alias, ctx=ictx)

        stmt.result = setgen.class_set(
            stmt.subject.scls.material_type(), ctx=ctx)
        stmt.result.path_id = stmt.subject.path_id

        result = fini_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx)

    return result
示例#11
0
文件: types.py 项目: virajs/edgedb
def __infer_struct_indirection(ir, schema):
    struct_type = infer_type(ir.expr, schema)
    result = struct_type.element_types.get(ir.name)
    if result is None:
        raise ql_errors.EdgeQLError('could not determine struct element type',
                                    context=ir.context)

    return result
示例#12
0
文件: types.py 项目: virajs/edgedb
def __infer_coalesce(ir, schema):
    result = _infer_common_type([ir.left, ir.right], schema)
    if result is None:
        raise ql_errors.EdgeQLError(
            'coalescing operator must have operands of related types',
            context=ir.context)

    return result
示例#13
0
def get_schema_object(
        name: typing.Union[str, qlast.ObjectRef],
        module: typing.Optional[str] = None,
        *,
        item_types: typing.Optional[typing.List[s_obj.ObjectMeta]],
        ctx: context.ContextLevel,
        srcctx: typing.Optional[parsing.ParserContext] = None) -> s_obj.Object:

    if isinstance(name, qlast.ObjectRef):
        if srcctx is None:
            srcctx = name.context
        module = name.module
        name = name.name

    if module:
        name = sn.Name(name=name, module=module)

    if not module:
        result = ctx.aliased_views.get(name)
        if result is not None:
            return result

    try:
        scls = ctx.schema.get(name=name,
                              module_aliases=ctx.modaliases,
                              type=item_types)

    except s_err.ItemNotFoundError as e:
        qlerror = qlerrors.EdgeQLError(e.args[0], context=srcctx)
        s_utils.enrich_schema_lookup_error(qlerror,
                                           name,
                                           modaliases=ctx.modaliases,
                                           schema=ctx.schema,
                                           item_types=item_types)

        raise qlerror

    except s_err.SchemaError as e:
        raise qlerrors.EdgeQLError(e.args[0], context=srcctx)

    result = ctx.aliased_views.get(scls.name)
    if result is None:
        result = scls

    return result
示例#14
0
def compile_TypeFilter(expr: qlast.Base, *,
                       ctx: context.ContextLevel) -> irast.Base:
    # Expr[IS Type] expressions.
    with ctx.new() as scopectx:
        arg = setgen.ensure_set(dispatch.compile(expr.expr, ctx=scopectx),
                                ctx=scopectx)

    arg_type = irutils.infer_type(arg, ctx.schema)
    if not isinstance(arg_type, s_objtypes.ObjectType):
        raise errors.EdgeQLError(
            f'invalid type filter operand: {arg_type.name} '
            f'is not an object type',
            context=expr.expr.context)

    typ = schemactx.get_schema_type(expr.type.maintype, ctx=ctx)
    if not isinstance(typ, s_objtypes.ObjectType):
        raise errors.EdgeQLError(
            f'invalid type filter operand: {typ.name} is not an object type',
            context=expr.type.context)

    return setgen.class_indirection_set(arg, typ, optional=False, ctx=ctx)
示例#15
0
文件: types.py 项目: virajs/edgedb
def infer_type(ir, schema):
    try:
        return ir._inferred_type_
    except AttributeError:
        pass

    result = _infer_type(ir, schema)

    if (result is not None
            and not isinstance(result, (s_obj.Object, s_obj.ObjectMeta))):

        raise ql_errors.EdgeQLError(
            f'infer_type({ir!r}) retured {result!r} instead of a Object',
            context=ir.context)

    if result is None or result.name == 'std::any':
        raise ql_errors.EdgeQLError('could not determine expression type',
                                    context=ir.context)

    ir._inferred_type_ = result
    return result
示例#16
0
文件: types.py 项目: virajs/edgedb
def __infer_index(ir, schema):
    node_type = infer_type(ir.expr, schema)
    index_type = infer_type(ir.index, schema)

    str_t = schema.get('std::str')
    int_t = schema.get('std::int64')

    result = None

    if node_type.issubclass(str_t):

        if not index_type.issubclass(int_t):
            raise ql_errors.EdgeQLError(
                f'cannot index string by {index_type.name}, '
                f'{int_t.name} was expected',
                context=ir.index.context)

        result = str_t

    elif isinstance(node_type, s_types.Map):

        if not index_type.issubclass(node_type.key_type):
            raise ql_errors.EdgeQLError(
                f'cannot index {node_type.name} by {index_type.name}, '
                f'{node_type.key_type.name} was expected',
                context=ir.index.context)

        result = node_type.element_type

    elif isinstance(node_type, s_types.Array):

        if not index_type.issubclass(int_t):
            raise ql_errors.EdgeQLError(
                f'cannot index array by {index_type.name}, '
                f'{int_t.name} was expected',
                context=ir.index.context)

        result = node_type.element_type

    return result
示例#17
0
文件: func.py 项目: virajs/edgedb
def compile_FunctionCall(expr: qlast.Base, *,
                         ctx: context.ContextLevel) -> irast.Base:
    with ctx.new() as fctx:
        if isinstance(expr.func, str):
            funcname = expr.func
        else:
            funcname = sn.Name(expr.func[1], expr.func[0])

        funcs = fctx.schema.get_functions(funcname,
                                          module_aliases=fctx.modaliases)

        if funcs is None:
            raise errors.EdgeQLError(
                f'could not resolve function name {funcname}',
                context=expr.context)

        fctx.in_func_call = True
        args, kwargs, arg_types = process_func_args(expr, funcname, ctx=fctx)

        for funcobj in funcs:
            if check_function(funcobj, arg_types):
                break
        else:
            raise errors.EdgeQLError(
                f'could not find a function variant {funcname}',
                context=expr.context)

        fixup_param_scope(funcobj, args, kwargs, ctx=fctx)

        node = irast.FunctionCall(func=funcobj, args=args, kwargs=kwargs)

        if funcobj.initial_value is not None:
            rtype = irutils.infer_type(node, fctx.schema)
            iv_ql = qlast.TypeCast(expr=qlparser.parse_fragment(
                funcobj.initial_value),
                                   type=typegen.type_to_ql_typeref(rtype))
            node.initial_value = dispatch.compile(iv_ql, ctx=fctx)

    ir_set = setgen.ensure_set(node, ctx=ctx)
    return ir_set
示例#18
0
    def _add_to_schema(self, schema):
        props = super().get_struct_properties(schema)
        fullname = self._get_function_fullname(props['name'],
                                               props.get('paramtypes'))
        func = schema.get(fullname, None)
        if func:
            raise ql_errors.EdgeQLError(
                f'Cannot create a function {self.classname}: '
                f'a function with the same signature '
                f'is already defined',
                context=self.source_context)

        super()._add_to_schema(schema)
示例#19
0
文件: types.py 项目: virajs/edgedb
def __infer_unaryop(ir, schema):
    result = None
    operand_type = infer_type(ir.expr, schema)

    if ir.op == ast.ops.NOT:
        if operand_type.name == 'std::bool':
            result = operand_type

    else:
        if ir.op not in {ast.ops.UPLUS, ast.ops.UMINUS}:
            raise ql_errors.EdgeQLError(f'unknown unary operator: {ir.op}',
                                        context=ir.context)

        result = s_basetypes.TypeRules.get_result(ir.op, (operand_type, ),
                                                  schema)

    if result is None:
        raise ql_errors.EdgeQLError(
            f'unary operator `{ir.op.upper()}` is not defined '
            f'for type {operand_type.name}',
            context=ir.context)

    return result
示例#20
0
文件: types.py 项目: virajs/edgedb
def __infer_ifelse(ir, schema):
    if_expr_type = infer_type(ir.if_expr, schema)
    else_expr_type = infer_type(ir.else_expr, schema)

    result = s_utils.get_class_nearest_common_ancestor(
        [if_expr_type, else_expr_type])

    if result is None:
        raise ql_errors.EdgeQLError(
            'if/else clauses must be of related types, got: {}/{}'.format(
                if_expr_type.name, else_expr_type.name),
            context=ir.if_expr.context)

    return result
示例#21
0
文件: types.py 项目: virajs/edgedb
def _infer_common_type(irs: typing.List[irast.Base], schema):
    if not irs:
        raise ql_errors.EdgeQLError(
            'cannot determine common type of an empty set',
            context=irs[0].context)

    col_type = None
    arg_types = []
    empties = []
    for i, arg in enumerate(irs):
        if isinstance(arg, irast.EmptySet) and arg.scls is None:
            empties.append(i)
            continue

        arg_type = infer_type(arg, schema)
        arg_types.append(arg_type)

        if isinstance(arg_type, s_types.Collection):
            col_type = arg_type

    if not arg_types:
        raise ql_errors.EdgeQLError(
            'cannot determine common type of an empty set',
            context=irs[0].context)

    if col_type is not None:
        if not all(col_type.issubclass(t) for t in arg_types):
            raise ql_errors.EdgeQLError('cannot determine common type',
                                        context=irs[0].context)
        common_type = col_type
    else:
        common_type = s_utils.get_class_nearest_common_ancestor(arg_types)

    for i in empties:
        amend_empty_set_type(irs[i], common_type, schema)

    return common_type
示例#22
0
文件: func.py 项目: virajs/edgedb
def process_func_args(
        expr: qlast.FunctionCall, funcname: sn.Name, *,
        ctx: context.ContextLevel) \
        -> typing.Tuple[
            typing.List[irast.Base],            # args
            typing.Dict[str, irast.Base],       # kwargs
            typing.List[s_types.Type]]:    # arg_types
    args = []
    kwargs = {}
    arg_types = []

    for ai, a in enumerate(expr.args):
        arg_ql = a.arg

        if a.sort or a.filter:
            arg_ql = astutils.ensure_qlstmt(arg_ql)
            if a.filter:
                arg_ql.where = astutils.extend_qlbinop(arg_ql.where, a.filter)

            if a.sort:
                arg_ql.orderby = a.sort + arg_ql.orderby

        with ctx.newscope(fenced=True) as fencectx:
            # We put on a SET OF fence preemptively in case this is
            # a SET OF arg, which we don't know yet due to polymorphic
            # matching.
            arg = setgen.scoped_set(dispatch.compile(arg_ql, ctx=fencectx),
                                    ctx=fencectx)

        if a.name:
            kwargs[a.name] = arg
            aname = a.name
        else:
            args.append(arg)
            aname = ai

        arg_type = irutils.infer_type(arg, ctx.schema)
        if arg_type is None:
            raise errors.EdgeQLError(
                f'could not resolve the type of argument '
                f'${aname} of function {funcname}',
                context=a.context)
        arg_types.append(arg_type)

    return args, kwargs, arg_types
示例#23
0
def ensure_set(expr: irast.Base,
               *,
               typehint: typing.Optional[s_types.Type] = None,
               path_id: typing.Optional[irast.PathId] = None,
               ctx: context.ContextLevel) -> irast.Set:
    if not isinstance(expr, irast.Set):
        expr = generated_set(expr, typehint=typehint, path_id=path_id, ctx=ctx)

    if (isinstance(expr, irast.EmptySet) and expr.scls is None
            and typehint is not None):
        irutils.amend_empty_set_type(expr, typehint, schema=ctx.schema)

    if typehint is not None and not expr.scls.issubclass(typehint):
        raise errors.EdgeQLError(
            f'expecting expression of type {typehint.name}, '
            f'got {expr.scls.name}',
            context=expr.context)
    return expr
示例#24
0
文件: types.py 项目: virajs/edgedb
def _infer_binop_args(left, right, schema):
    if not isinstance(left, irast.EmptySet) or left.scls is not None:
        left_type = infer_type(left, schema)
    else:
        left_type = None

    if not isinstance(right, irast.EmptySet) or right.scls is not None:
        right_type = infer_type(right, schema)
    else:
        right_type = None

    if left_type is None and right_type is None:
        raise ql_errors.EdgeQLError(
            'cannot determine the type of an empty set', context=left.context)
    elif left_type is None:
        amend_empty_set_type(left, right_type, schema)
        left_type = right_type
    elif right_type is None:
        amend_empty_set_type(right, left_type, schema)
        right_type = left_type

    return left_type, right_type
示例#25
0
def infer_cardinality(ir, singletons, schema):
    try:
        return ir._inferred_cardinality_[frozenset(singletons)]
    except (AttributeError, KeyError):
        pass

    result = _infer_cardinality(ir, singletons, schema)

    if result not in {ONE, MANY}:
        raise ql_errors.EdgeQLError(
            'could not determine the cardinality of '
            'set produced by expression',
            context=ir.context)

    try:
        cache = ir._inferred_cardinality_
    except AttributeError:
        cache = ir._inferred_cardinality_ = {}

    cache[frozenset(singletons)] = result

    return result
示例#26
0
def _normalize_view_ptr_expr(
        shape_el: qlast.ShapeElement,
        view_scls: s_nodes.Node,
        *,
        path_id: irast.PathId,
        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_mutation = is_insert or is_update
    # Pointers may be qualified by the explicit source
    # class, which is equivalent to Expr[IS Type].
    is_polymorphic = len(steps) == 2
    scls = view_scls.peel_view()
    ptrsource = scls
    qlexpr = None

    if is_polymorphic:
        source = qlast.TypeFilter(expr=qlast.Path(steps=[qlast.Source()]),
                                  type=qlast.TypeName(maintype=steps[0]))
        lexpr = steps[1]
        ptrsource = schemactx.get_schema_type(steps[0], ctx=ctx)
    elif len(steps) == 1:
        lexpr = steps[0]
        is_linkprop = lexpr.type == 'property'
        if is_linkprop:
            if view_rptr is None:
                raise errors.EdgeQLError(
                    'invalid reference to link property '
                    'in top level shape',
                    context=lexpr.context)
            ptrsource = scls = view_rptr.ptrcls
        source = qlast.Source()
    else:
        raise RuntimeError(
            f'unexpected path length in view shape: {len(steps)}')

    ptrname = (lexpr.ptr.module, lexpr.ptr.name)
    ptrcls_is_derived = False

    compexpr = shape_el.compexpr
    if compexpr is None and is_insert and shape_el.elements:
        # Nested insert short form:
        #     INSERT Foo { bar: Spam { name := 'name' }}
        # Expand to:
        #     INSERT Foo { bar := (INSERT Spam { name := 'name' }) }
        if lexpr.target is not None:
            ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx)
        else:
            ptr_target = None

        base_ptrcls = ptrcls = setgen.resolve_ptr(
            ptrsource,
            ptrname,
            s_pointers.PointerDirection.Outbound,
            target=ptr_target,
            ctx=ctx)

        compexpr = qlast.InsertQuery(subject=qlast.Path(steps=[
            qlast.ObjectRef(name=ptrcls.target.name.name,
                            module=ptrcls.target.name.module)
        ]),
                                     shape=shape_el.elements)

    if compexpr is None:
        if lexpr.target is not None:
            ptr_target = schemactx.get_schema_type(lexpr.target, ctx=ctx)
        else:
            ptr_target = None

        base_ptrcls = ptrcls = setgen.resolve_ptr(
            ptrsource,
            ptrname,
            s_pointers.PointerDirection.Outbound,
            target=ptr_target,
            ctx=ctx)

        base_ptr_is_computable = ptrcls in ctx.source_map

        if ptr_target is not None and ptr_target != base_ptrcls.target:
            # This happens when a union type target is narrowed by an
            # [IS Type] construct.  Since the derived pointer will have
            # the correct target, we don't need to do anything, but
            # remove the [IS] qualifier to prevent recursion.
            lexpr.target = None
        else:
            ptr_target = ptrcls.target

        ptr_cardinality = ptrcls.cardinality

        if shape_el.elements:
            sub_view_rptr = context.ViewRPtr(view_scls,
                                             ptrcls,
                                             is_insert=is_insert,
                                             is_update=is_update)

            sub_path_id = path_id.extend(ptrcls, target=ptrcls.target)
            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.EdgeQLError(
                            'only references to link properties are allowed '
                            'in nested UPDATE shapes',
                            context=subel.context)

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

            ptrcls = sub_view_rptr.derived_ptrcls
            if ptrcls is None:
                ptrcls_is_derived = False
                ptrcls = sub_view_rptr.ptrcls
            else:
                ptrcls_is_derived = True

        if (shape_el.where or shape_el.orderby or shape_el.offset
                or shape_el.limit or base_ptr_is_computable or is_polymorphic):

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

            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
    else:
        try:
            base_ptrcls = ptrcls = setgen.resolve_ptr(
                ptrsource,
                ptrname,
                s_pointers.PointerDirection.Outbound,
                ctx=ctx)
        except errors.EdgeQLReferenceError:
            if is_mutation:
                raise

            ptr_module = (ptrname[0] or ctx.derived_target_module
                          or scls.name.module)

            if is_linkprop:
                ptr_metacls = s_props.Property
            else:
                ptr_metacls = s_links.Link

            ptr_name = sn.SchemaName(module=ptr_module, name=ptrname[1])
            base_ptrcls = ptrcls = ptr_metacls(name=ptr_name)

        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(view_scls,
                                                        ptrcls,
                                                        is_insert=is_insert,
                                                        is_update=is_update)

            shape_expr_ctx.path_scope.unnest_fence = True

            if is_mutation:
                shape_expr_ctx.expr_exposed = True

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

            irexpr.context = compexpr.context
            derived_ptrcls = shape_expr_ctx.view_rptr.derived_ptrcls
            if derived_ptrcls is not None:
                ptrcls_is_derived = True
                ptrcls = derived_ptrcls

        inferred_cardinality = pathctx.infer_cardinality(irexpr, ctx=ctx)
        if inferred_cardinality == irast.Cardinality.MANY:
            ptr_cardinality = s_pointers.PointerCardinality.ManyToMany
        else:
            ptr_cardinality = s_pointers.PointerCardinality.ManyToOne

        ptr_target = irutils.infer_type(irexpr, ctx.schema)
        if ptr_target is None:
            msg = 'cannot determine expression result type'
            raise errors.EdgeQLError(msg, context=shape_el.context)

        if is_mutation and not ptr_target.assignment_castable_to(
                base_ptrcls.target, schema=ctx.schema):
            # Validate that the insert/update expression is
            # of the correct class.
            lname = f'({ptrsource.name}).{ptrcls.shortname.name}'
            expected = [repr(str(base_ptrcls.target.name))]
            raise edgedb_error.InvalidPointerTargetError(
                f'invalid target for link {str(lname)!r}: '
                f'{str(ptr_target.name)!r} (expecting '
                f'{" or ".join(expected)})')

        if is_mutation and base_ptrcls.singular():
            pathctx.enforce_singleton(irexpr, ctx=ctx)

    if qlexpr is not None or ptr_target is not ptrcls.target:
        if not ptrcls_is_derived:
            if ptrcls.is_link_property():
                rptrcls = view_rptr.derived_ptrcls
                if rptrcls is None:
                    rptrcls = schemactx.derive_view(
                        view_rptr.ptrcls,
                        view_rptr.source,
                        view_scls,
                        is_insert=view_rptr.is_insert,
                        is_update=view_rptr.is_update,
                        ctx=ctx)
                    view_rptr.derived_ptrcls = rptrcls

                src_scls = rptrcls
            else:
                src_scls = view_scls

            ptrcls = schemactx.derive_view(ptrcls,
                                           src_scls,
                                           ptr_target,
                                           is_insert=is_insert,
                                           is_update=is_update,
                                           derived_name_quals=[view_scls.name],
                                           ctx=ctx)

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

    if not is_mutation:
        ptrcls.cardinality = ptr_cardinality

    if ptrcls.shortname == 'std::__type__' and qlexpr is not None:
        msg = 'cannot assign to __type__'
        raise errors.EdgeQLError(msg, context=shape_el.context)

    return ptrcls
示例#27
0
def compile_EmptyCollection(expr: qlast.Base, *,
                            ctx: context.ContextLevel) -> irast.Base:
    raise errors.EdgeQLError(f'could not determine type of empty collection',
                             context=expr.context)
示例#28
0
def compile_path(expr: qlast.Path, *, ctx: context.ContextLevel) -> irast.Set:
    """Create an ir.Set representing the given EdgeQL path expression."""
    anchors = ctx.anchors

    path_tip = None

    if expr.partial:
        if ctx.partial_path_prefix is not None:
            path_tip = ctx.partial_path_prefix
        else:
            raise errors.EdgeQLError('could not resolve partial path ',
                                     context=expr.context)

    extra_scopes = {}
    computables = []
    path_sets = []

    for i, step in enumerate(expr.steps):
        if isinstance(step, qlast.Source):
            # 'self' can only appear as the starting path label
            # syntactically and is a known anchor
            path_tip = anchors.get(step.__class__)

        elif isinstance(step, qlast.Subject):
            # '__subject__' can only appear as the starting path label
            # syntactically and is a known anchor
            path_tip = anchors.get(step.__class__)

        elif isinstance(step, qlast.ObjectRef):
            if i > 0:
                raise RuntimeError(
                    'unexpected ObjectRef as a non-first path item')

            refnode = None

            if not step.module:
                # Check if the starting path label is a known anchor
                refnode = anchors.get(step.name)

            if refnode is not None:
                path_tip = copy.copy(refnode)
            else:
                scls = schemactx.get_schema_type(
                    step, item_types=(s_objtypes.ObjectType, ), ctx=ctx)

                if (scls.view_type is not None
                        and scls.name not in ctx.view_nodes):
                    # This is a schema-level view, as opposed to
                    # a WITH-block or inline alias view.
                    scls = stmtctx.declare_view_from_schema(scls, ctx=ctx)

                path_tip = class_set(scls, ctx=ctx)
                view_set = ctx.view_sets.get(scls)
                if view_set is not None:
                    path_tip = new_set_from_set(view_set, ctx=ctx)
                    path_scope = ctx.path_scope_map.get(view_set)
                    extra_scopes[path_tip] = path_scope.copy()

                view_scls = ctx.class_view_overrides.get(scls.name)
                if view_scls is not None:
                    path_tip.scls = view_scls

        elif isinstance(step, qlast.Ptr):
            # Pointer traversal step
            ptr_expr = step
            ptr_target = None

            direction = (ptr_expr.direction
                         or s_pointers.PointerDirection.Outbound)
            if ptr_expr.target:
                # ... link [IS Target]
                ptr_target = schemactx.get_schema_type(ptr_expr.target,
                                                       ctx=ctx)
                if not isinstance(ptr_target, s_objtypes.ObjectType):
                    raise errors.EdgeQLError(
                        f'invalid type filter operand: {ptr_target.name} '
                        f'is not an object type',
                        context=ptr_expr.target.context)

            ptr_name = (ptr_expr.ptr.module, ptr_expr.ptr.name)

            if ptr_expr.type == 'property':
                # Link property reference; the source is the
                # link immediately preceding this step in the path.
                source = path_tip.rptr.ptrcls
            else:
                source = path_tip.scls

            with ctx.newscope(fenced=True, temporary=True) as subctx:
                if isinstance(source, s_types.Tuple):
                    path_tip = tuple_indirection_set(
                        path_tip,
                        source=source,
                        ptr_name=ptr_name,
                        source_context=step.context,
                        ctx=subctx)

                else:
                    path_tip = ptr_step_set(path_tip,
                                            source=source,
                                            ptr_name=ptr_name,
                                            direction=direction,
                                            ptr_target=ptr_target,
                                            ignore_computable=True,
                                            source_context=step.context,
                                            ctx=subctx)

                    ptrcls = path_tip.rptr.ptrcls
                    if _is_computable_ptr(ptrcls, ctx=ctx):
                        computables.append(path_tip)

        else:
            # Arbitrary expression
            if i > 0:
                raise RuntimeError(
                    'unexpected expression as a non-first path item')

            with ctx.newscope(fenced=True, temporary=True) as subctx:
                path_tip = ensure_set(dispatch.compile(step, ctx=subctx),
                                      ctx=subctx)

                if path_tip.path_id.is_type_indirection_path():
                    scope_set = path_tip.rptr.source
                else:
                    scope_set = path_tip

                extra_scopes[scope_set] = subctx.path_scope

        mapped = ctx.view_map.get(path_tip.path_id)
        if mapped is not None:
            path_tip = new_set(path_id=mapped.path_id,
                               scls=path_tip.scls,
                               expr=mapped.expr,
                               ctx=ctx)

        path_sets.append(path_tip)

    path_tip.context = expr.context
    pathctx.register_set_in_scope(path_tip, ctx=ctx)

    for ir_set in computables:
        scope = ctx.path_scope.find_descendant(ir_set.path_id)
        if scope is None:
            # The path is already in the scope, no point
            # in recompiling the computable expression.
            continue

        with ctx.new() as subctx:
            subctx.path_scope = scope
            comp_ir_set = computable_ptr_set(ir_set.rptr, ctx=subctx)
            i = path_sets.index(ir_set)
            if i != len(path_sets) - 1:
                path_sets[i + 1].rptr.source = comp_ir_set
            else:
                path_tip = comp_ir_set
            path_sets[i] = comp_ir_set

    for ir_set, scope in extra_scopes.items():
        node = ctx.path_scope.find_descendant(ir_set.path_id)
        if node is None:
            # The path portion not being a descendant means
            # that is is already present in the scope above us,
            # along with the view scope.
            continue

        fuse_scope_branch(ir_set, node, scope, ctx=ctx)
        if ir_set.path_scope_id is None:
            pathctx.assign_set_scope(ir_set, node, ctx=ctx)

    return path_tip
示例#29
0
 def _get_view_expr(cls, astnode):
     expr = cls._maybe_get_view_expr(astnode)
     if expr is None:
         raise ql_errors.EdgeQLError(
             f'Missing required view expression', context=astnode.context)
     return expr