示例#1
0
文件: expr.py 项目: virajs/edgedb
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
    )
示例#2
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)
示例#3
0
文件: stmtctx.py 项目: virajs/edgedb
def fini_expression(
        ir: irast.Base, *,
        ctx: context.ContextLevel) -> irast.Command:
    for ir_set in ctx.all_sets:
        if ir_set.path_id.namespace:
            ir_set.path_id = ir_set.path_id.strip_weak_namespaces()

    if isinstance(ir, irast.Command):
        # IR is already a Command
        return ir

    if ctx.path_scope is not None:
        # Simple expressions have no scope.
        for node in ctx.path_scope.get_all_path_nodes(include_subpaths=True):
            if node.path_id.namespace:
                node.path_id = node.path_id.strip_weak_namespaces()

        cardinality = pathctx.infer_cardinality(ir, ctx=ctx)
    else:
        cardinality = irast.Cardinality.ONE

    result = irast.Statement(
        expr=ir,
        params=ctx.arguments,
        views=ctx.view_nodes,
        source_map=ctx.source_map,
        scope_tree=ctx.path_scope,
        cardinality=cardinality,
    )
    irutils.infer_type(result, schema=ctx.schema)
    return result
示例#4
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)
示例#5
0
def compile_UnaryOp(expr: qlast.Base, *,
                    ctx: context.ContextLevel) -> irast.Set:
    if expr.op == qlast.DISTINCT:
        return compile_distinct_op(expr, ctx=ctx)

    operand = dispatch.compile(expr.operand, ctx=ctx)
    if astutils.is_exists_expr_set(operand):
        operand.expr.negated = not operand.expr.negated
        return operand

    unop = irast.UnaryOp(expr=operand, op=expr.op)
    result_type = irutils.infer_type(unop, ctx.schema)

    real_t = ctx.schema.get('std::anyreal')

    if (isinstance(operand.expr, irast.Constant)
            and result_type.issubclass(real_t)):
        # Fold the operation to constant if possible
        if expr.op == ast.ops.UMINUS:
            return setgen.ensure_set(irast.Constant(value=-operand.expr.value,
                                                    type=result_type),
                                     ctx=ctx)
        elif expr.op == ast.ops.UPLUS:
            return operand

    return setgen.generated_set(unop, ctx=ctx)
示例#6
0
文件: expr.py 项目: virajs/edgedb
def compile_UnaryOp(
        expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Set:
    if expr.op == qlast.DISTINCT:
        return compile_distinct_op(expr, ctx=ctx)

    operand = dispatch.compile(expr.operand, ctx=ctx)
    if astutils.is_exists_expr_set(operand):
        operand.expr.negated = not operand.expr.negated
        return operand

    unop = irast.UnaryOp(expr=operand, op=expr.op)
    result_type = irutils.infer_type(unop, ctx.schema)

    real_t = ctx.schema.get('std::anyreal')

    if (isinstance(operand.expr, irast.Constant) and
            result_type.issubclass(real_t)):
        # Fold the operation to constant if possible
        if expr.op == ast.ops.UMINUS:
            return setgen.ensure_set(
                irast.Constant(value=-operand.expr.value, type=result_type),
                ctx=ctx)
        elif expr.op == ast.ops.UPLUS:
            return operand

    return setgen.generated_set(unop, ctx=ctx)
示例#7
0
def fini_stmt(
        irstmt: irast.Base, qlstmt: qlast.Statement, *,
        ctx: context.ContextLevel,
        parent_ctx: context.ContextLevel) -> irast.Set:
    irstmt.cardinality = qlstmt.cardinality

    view_name = parent_ctx.toplevel_result_view_name
    t = irutils.infer_type(irstmt, ctx.schema)

    if t.name == view_name:
        # The view statement did contain a view declaration and
        # generated a view class with the requested name.
        view = t
        path_id = pathctx.get_path_id(view, ctx=parent_ctx)
    elif view_name is not None:
        # The view statement did _not_ contain a view declaration,
        # but we still want the correct path_id.
        view = schemactx.derive_view(t, derived_name=view_name, ctx=parent_ctx)
        path_id = pathctx.get_path_id(view, ctx=parent_ctx)
    else:
        view = None
        path_id = None

    result = setgen.scoped_set(irstmt, path_id=path_id, ctx=ctx)

    if view is not None:
        parent_ctx.view_sets[view] = result
        result.scls = view

    return result
示例#8
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
示例#9
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
示例#10
0
    def normalize_constraint_expr(cls,
                                  schema,
                                  module_aliases,
                                  expr,
                                  *,
                                  subject=None,
                                  constraint,
                                  expr_context=None,
                                  enforce_boolean=False):
        from edgedb.lang.ir import utils as irutils

        if subject is None:
            subject = cls._dummy_subject()

        edgeql_tree, ir_result = cls._normalize_constraint_expr(
            schema, module_aliases, expr, subject)

        if enforce_boolean:
            bool_t = schema.get('std::bool')
            expr_type = irutils.infer_type(ir_result, schema)
            if not expr_type.issubclass(bool_t):
                raise s_errors.SchemaDefinitionError(
                    f'{constraint.displayname} constraint expression expected '
                    f'to return a bool value, got {expr_type.name.name!r}',
                    context=expr_context)

        expr = edgeql.generate_source(edgeql_tree, pretty=False)
        # XXX: check that expr has boolean result
        return expr
示例#11
0
文件: expr.py 项目: virajs/edgedb
def try_fold_arithmetic_binop(
        op: ast.ops.Operator, left: irast.Set, right: irast.Set, *,
        ctx: context.ContextLevel) -> typing.Optional[irast.Set]:
    """Try folding an arithmetic expr into a constant."""
    schema = ctx.schema

    real_t = schema.get('std::anyreal')
    float_t = schema.get('std::anyfloat')
    int_t = schema.get('std::anyint')

    left_type = irutils.infer_type(left, schema)
    right_type = irutils.infer_type(right, schema)

    if not left_type.issubclass(real_t) or not right_type.issubclass(real_t):
        return

    result_type = left_type
    if right_type.issubclass(float_t):
        result_type = right_type

    left = left.expr
    right = right.expr

    if op == ast.ops.ADD:
        value = left.value + right.value
    elif op == ast.ops.SUB:
        value = left.value - right.value
    elif op == ast.ops.MUL:
        value = left.value * right.value
    elif op == ast.ops.DIV:
        if left_type.issubclass(int_t) and right_type.issubclass(int_t):
            value = left.value // right.value
        else:
            value = left.value / right.value
    elif op == ast.ops.POW:
        value = left.value ** right.value
    elif op == ast.ops.MOD:
        value = left.value % right.value
    else:
        value = None

    if value is not None:
        return setgen.ensure_set(
            irast.Constant(value=value, type=result_type), ctx=ctx)
示例#12
0
def try_fold_arithmetic_binop(
        op: ast.ops.Operator, left: irast.Set, right: irast.Set, *,
        ctx: context.ContextLevel) -> typing.Optional[irast.Set]:
    """Try folding an arithmetic expr into a constant."""
    schema = ctx.schema

    real_t = schema.get('std::anyreal')
    float_t = schema.get('std::anyfloat')
    int_t = schema.get('std::anyint')

    left_type = irutils.infer_type(left, schema)
    right_type = irutils.infer_type(right, schema)

    if not left_type.issubclass(real_t) or not right_type.issubclass(real_t):
        return

    result_type = left_type
    if right_type.issubclass(float_t):
        result_type = right_type

    left = left.expr
    right = right.expr

    if op == ast.ops.ADD:
        value = left.value + right.value
    elif op == ast.ops.SUB:
        value = left.value - right.value
    elif op == ast.ops.MUL:
        value = left.value * right.value
    elif op == ast.ops.DIV:
        if left_type.issubclass(int_t) and right_type.issubclass(int_t):
            value = left.value // right.value
        else:
            value = left.value / right.value
    elif op == ast.ops.POW:
        value = left.value**right.value
    elif op == ast.ops.MOD:
        value = left.value % right.value
    else:
        value = None

    if value is not None:
        return setgen.ensure_set(irast.Constant(value=value, type=result_type),
                                 ctx=ctx)
示例#13
0
文件: expr.py 项目: virajs/edgedb
def try_fold_binop(
        binop: irast.BinOp, *,
        ctx: context.ContextLevel) -> typing.Optional[irast.Set]:
    """Try folding a binary operator expression."""
    schema = ctx.schema
    real_t = schema.get('std::anyreal')

    result_type = irutils.infer_type(binop, schema)
    folded = None

    left = binop.left
    right = binop.right
    op = binop.op

    if (isinstance(left.expr, irast.Constant) and
            isinstance(right.expr, irast.Constant) and
            result_type.issubclass(real_t)):

        # Left and right nodes are constants.
        folded = try_fold_arithmetic_binop(op, left, right, ctx=ctx)

    elif op in {ast.ops.ADD, ast.ops.MUL}:
        # Let's check if we have (CONST + (OTHER_CONST + X))
        # tree, which can be optimized to ((CONST + OTHER_CONST) + X)

        my_const = left
        other_binop = right
        if isinstance(right.expr, irast.Constant):
            my_const, other_binop = other_binop, my_const

        if (isinstance(my_const.expr, irast.Constant) and
                isinstance(other_binop.expr, irast.BinOp) and
                other_binop.expr.op == op):

            other_const = other_binop.expr.left
            other_binop_node = other_binop.expr.right
            if isinstance(other_binop_node.expr, irast.Constant):
                other_binop_node, other_const = \
                    other_const, other_binop_node

            if isinstance(other_const.expr, irast.Constant):
                new_const = try_fold_arithmetic_binop(
                    op, other_const, my_const, ctx=ctx)

                if new_const is not None:
                    folded_binop = irast.BinOp(
                        left=new_const,
                        right=other_binop_node,
                        op=op)
                    folded = setgen.ensure_set(folded_binop, ctx=ctx)

    return folded
示例#14
0
def try_fold_binop(binop: irast.BinOp, *,
                   ctx: context.ContextLevel) -> typing.Optional[irast.Set]:
    """Try folding a binary operator expression."""
    schema = ctx.schema
    real_t = schema.get('std::anyreal')

    result_type = irutils.infer_type(binop, schema)
    folded = None

    left = binop.left
    right = binop.right
    op = binop.op

    if (isinstance(left.expr, irast.Constant)
            and isinstance(right.expr, irast.Constant)
            and result_type.issubclass(real_t)):

        # Left and right nodes are constants.
        folded = try_fold_arithmetic_binop(op, left, right, ctx=ctx)

    elif op in {ast.ops.ADD, ast.ops.MUL}:
        # Let's check if we have (CONST + (OTHER_CONST + X))
        # tree, which can be optimized to ((CONST + OTHER_CONST) + X)

        my_const = left
        other_binop = right
        if isinstance(right.expr, irast.Constant):
            my_const, other_binop = other_binop, my_const

        if (isinstance(my_const.expr, irast.Constant)
                and isinstance(other_binop.expr, irast.BinOp)
                and other_binop.expr.op == op):

            other_const = other_binop.expr.left
            other_binop_node = other_binop.expr.right
            if isinstance(other_binop_node.expr, irast.Constant):
                other_binop_node, other_const = \
                    other_const, other_binop_node

            if isinstance(other_const.expr, irast.Constant):
                new_const = try_fold_arithmetic_binop(op,
                                                      other_const,
                                                      my_const,
                                                      ctx=ctx)

                if new_const is not None:
                    folded_binop = irast.BinOp(left=new_const,
                                               right=other_binop_node,
                                               op=op)
                    folded = setgen.ensure_set(folded_binop, ctx=ctx)

    return folded
示例#15
0
文件: views.py 项目: virajs/edgedb
    def _command_for_ast_node(cls, astnode, schema, context):
        classname = cls._classname_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateView):
            expr = cls._get_view_expr(astnode)
            ir = cls._compile_view_expr(expr, classname, schema, context)
            scls = irutils.infer_type(ir, schema)
        else:
            scls = schema.get(classname)

        if isinstance(scls, s_scalars.ScalarType):
            mapping = cls._scalar_cmd_map
        else:
            mapping = cls._objtype_cmd_map

        return mapping[type(astnode)]
示例#16
0
文件: views.py 项目: virajs/edgedb
    def _command_for_ast_node(cls, astnode, schema, context):
        classname = cls._classname_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateView):
            expr = cls._get_view_expr(astnode)
            ir = cls._compile_view_expr(expr, classname, schema, context)
            scls = irutils.infer_type(ir, schema)
        else:
            scls = schema.get(classname)

        if isinstance(scls, s_scalars.ScalarType):
            mapping = cls._scalar_cmd_map
        else:
            mapping = cls._objtype_cmd_map

        return mapping[type(astnode)]
示例#17
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
示例#18
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
示例#19
0
    def _handle_view_op(cls, cmd, astnode, context, schema):
        from edgedb.lang.ir import utils as irutils

        view_expr = cls._maybe_get_view_expr(astnode)
        if view_expr is not None:
            ir = cls._compile_view_expr(view_expr, cmd.classname,
                                        schema, context)
            rt = irutils.infer_type(ir, schema)

            if rt.is_view():
                # The expression itself declares a view, use it.
                rt.name = cmd.classname

            view_schema = cls._view_schema_from_ir(cmd.classname, ir, schema)
            if isinstance(astnode, qlast.AlterObjectType):
                prev = schema.get(cmd.classname)
                prev_ir = cls._compile_view_expr(
                    prev.expr, cmd.classname, schema, context)
                prev_view_schema = cls._view_schema_from_ir(
                    cmd.classname, prev_ir, schema)
            else:
                prev_view_schema = cls._view_schema_from_ir(
                    cmd.classname, None, schema)

            derived_delta = sd.delta_schemas(
                view_schema, prev_view_schema, include_derived=True)

            if rt.is_view():
                for op in list(derived_delta.get_subcommands()):
                    if op.classname == cmd.classname:
                        for subop in op.get_subcommands():
                            if isinstance(subop, sd.AlterObjectProperty):
                                cmd.discard_attribute(subop.property)
                            cmd.add(subop)

                        derived_delta.discard(op)

            cmd.update(derived_delta.get_subcommands())
            cmd.discard_attribute('view_type')
            cmd.add(sd.AlterObjectProperty(
                property='view_type', new_value=s_types.ViewType.Select))

        return cmd
示例#20
0
文件: expr.py 项目: virajs/edgedb
def compile_type_check_op(
        expr: qlast.BinOp, *, ctx: context.ContextLevel) -> irast.BinOp:
    # <Expr> IS <Type>
    left = dispatch.compile(expr.left, ctx=ctx)
    with ctx.new() as subctx:
        subctx.path_as_type = True
        right = dispatch.compile(expr.right, ctx=subctx)

    ltype = irutils.infer_type(left, ctx.schema)
    left = setgen.ptr_step_set(
        left, source=ltype, ptr_name=('std', '__type__'),
        direction=s_pointers.PointerDirection.Outbound,
        source_context=expr.context, ctx=ctx)

    pathctx.register_set_in_scope(left, ctx=ctx)

    right = typegen.process_type_ref_expr(right)

    return irast.BinOp(left=left, right=right, op=expr.op)
示例#21
0
def new_expression_set(ir_expr,
                       path_id=None,
                       alias=None,
                       typehint: typing.Optional[irast.TypeRef] = None,
                       *,
                       ctx: context.ContextLevel) -> irast.Set:
    if isinstance(ir_expr, irast.EmptySet) and typehint is not None:
        ir_expr = irast.TypeCast(expr=ir_expr, type=typehint)

    result_type = irutils.infer_type(ir_expr, ctx.schema)

    if path_id is None:
        path_id = getattr(ir_expr, 'path_id', None)

        if not path_id:
            if alias is None:
                raise ValueError('either path_id or alias are required')
            path_id = get_expression_path_id(result_type, alias, ctx=ctx)

    return new_set(path_id=path_id, scls=result_type, expr=ir_expr, ctx=ctx)
示例#22
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
示例#23
0
def compile_type_check_op(expr: qlast.BinOp, *,
                          ctx: context.ContextLevel) -> irast.BinOp:
    # <Expr> IS <Type>
    left = dispatch.compile(expr.left, ctx=ctx)
    with ctx.new() as subctx:
        subctx.path_as_type = True
        right = dispatch.compile(expr.right, ctx=subctx)

    ltype = irutils.infer_type(left, ctx.schema)
    left = setgen.ptr_step_set(left,
                               source=ltype,
                               ptr_name=('std', '__type__'),
                               direction=s_pointers.PointerDirection.Outbound,
                               source_context=expr.context,
                               ctx=ctx)

    pathctx.register_set_in_scope(left, ctx=ctx)

    right = typegen.process_type_ref_expr(right)

    return irast.BinOp(left=left, right=right, op=expr.op)
示例#24
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)
示例#25
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
示例#26
0
文件: expr.py 项目: virajs/edgedb
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)
示例#27
0
    def _normalize_ptr_default(self, expr, source, ptr, ptrdecl):
        module_aliases = {None: source.name.module}

        ir, _, expr_text = edgeql.utils.normalize_tree(
            expr,
            self._schema,
            modaliases=module_aliases,
            anchors={qlast.Source: source})

        self_set = ast.find_children(
            ir,
            lambda n: getattr(n, 'anchor', None) == qlast.Source,
            terminate_early=True)

        try:
            expr_type = ir_utils.infer_type(ir, self._schema)
        except edgeql.EdgeQLError as e:
            raise s_err.SchemaError(
                'could not determine the result type of the default '
                'expression on {!s}.{!s}'.format(source.name, ptr.shortname),
                context=expr.context) from e

        ptr.default = expr_text
        ptr.normalize_defaults()

        if ptr.is_pure_computable():
            # Pure computable without explicit target.
            # Fixup pointer target and target property.
            ptr.target = expr_type

            if isinstance(ptr, s_links.Link):
                if not isinstance(expr_type, s_objtypes.ObjectType):
                    raise s_err.SchemaDefinitionError(
                        f'invalid link target, expected object type, got '
                        f'{expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)
            else:
                if not isinstance(expr_type,
                                  (s_scalars.ScalarType, s_types.Collection)):
                    raise s_err.SchemaDefinitionError(
                        f'invalid property target, expected primitive type, '
                        f'got {expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)

            if isinstance(ptr, s_links.Link):
                pname = s_name.Name('std::target')
                tgt_prop = ptr.pointers[pname]
                tgt_prop.target = expr_type

            cardinality = self._get_literal_attribute(ptrdecl, 'cardinality')
            if cardinality is not None:
                raise s_err.SchemaError(
                    'computable links must not define explicit cardinality',
                    context=expr.context)

            singletons = set()
            if self_set is not None:
                singletons.add(self_set.path_id)

            cardinality = \
                ir_inference.infer_cardinality(ir, singletons, self._schema)

            if cardinality == qlast.Cardinality.MANY:
                ptr.cardinality = s_pointers.PointerCardinality.ManyToMany
            else:
                ptr.cardinality = s_pointers.PointerCardinality.ManyToOne

        if (not isinstance(expr_type, s_types.Type) or
            (ptr.target is not None and not expr_type.issubclass(ptr.target))):
            raise s_err.SchemaError(
                'default value query must yield a single result of '
                'type {!r}'.format(ptr.target.name),
                context=expr.context)

        if not isinstance(ptr.target, s_scalars.ScalarType):
            many_mapping = (s_pointers.PointerCardinality.ManyToOne,
                            s_pointers.PointerCardinality.ManyToMany)
            if ptr.cardinality not in many_mapping:
                raise s_err.SchemaError(
                    'type links with query defaults '
                    'must have either a "*1" or "**" cardinality',
                    context=expr.context)
示例#28
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
示例#29
0
文件: links.py 项目: virajs/edgedb
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        from edgedb.lang.edgeql import utils as ql_utils
        from edgedb.lang.ir import ast as irast
        from edgedb.lang.ir import inference as ir_inference
        from edgedb.lang.ir import utils as ir_utils
        from . import objtypes as s_objtypes

        cmd = super()._cmd_tree_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateConcreteLink):
            cmd.add(
                sd.AlterObjectProperty(
                    property='required',
                    new_value=astnode.is_required
                )
            )

            # "source" attribute is set automatically as a refdict back-attr
            parent_ctx = context.get(LinkSourceCommandContext)
            source_name = parent_ctx.op.classname
            target_type = None

            if len(astnode.targets) > 1:
                cmd.add(
                    sd.AlterObjectProperty(
                        property='spectargets',
                        new_value=so.ObjectList([
                            utils.ast_to_typeref(
                                t, modaliases=context.modaliases,
                                schema=schema)
                            for t in astnode.targets
                        ])
                    )
                )

                target_name = sources.Source.gen_virt_parent_name(
                    (sn.Name(module=t.maintype.module, name=t.maintype.name)
                     for t in astnode.targets),
                    module=source_name.module
                )

                target = so.ObjectRef(classname=target_name)

                create_virt_parent = s_objtypes.CreateObjectType(
                    classname=target_name,
                    metaclass=s_objtypes.ObjectType
                )

                create_virt_parent.update((
                    sd.AlterObjectProperty(
                        property='bases',
                        new_value=so.ObjectList([
                            so.ObjectRef(classname=sn.Name(
                                module='std', name='Object'
                            ))
                        ])
                    ),
                    sd.AlterObjectProperty(
                        property='name',
                        new_value=target_name
                    ),
                    sd.AlterObjectProperty(
                        property='is_virtual',
                        new_value=True
                    )
                ))

                alter_db_ctx = context.get(s_db.DatabaseCommandContext)

                for cc in alter_db_ctx.op.get_subcommands(
                        type=s_objtypes.CreateObjectType):
                    if cc.classname == create_virt_parent.classname:
                        break
                else:
                    alter_db_ctx.op.add(create_virt_parent)
            else:
                target_expr = astnode.targets[0]
                if isinstance(target_expr, qlast.TypeName):
                    target = utils.ast_to_typeref(
                        target_expr, modaliases=context.modaliases,
                        schema=schema)
                else:
                    # computable
                    source = schema.get(source_name, default=None)
                    if source is None:
                        raise s_err.SchemaDefinitionError(
                            f'cannot define link computables in CREATE TYPE',
                            hint='Perform a CREATE TYPE without the link '
                                 'followed by ALTER TYPE defining the '
                                 'computable',
                            context=target_expr.context
                        )

                    ir, _, target_expr = ql_utils.normalize_tree(
                        target_expr, schema,
                        anchors={qlast.Source: source})

                    try:
                        target_type = ir_utils.infer_type(ir, schema)
                    except edgeql.EdgeQLError as e:
                        raise s_err.SchemaDefinitionError(
                            'could not determine the result type of '
                            'computable expression',
                            context=target_expr.context) from e

                    target = utils.reduce_to_typeref(target_type)

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

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

                    singletons = {
                        irast.PathId(source)
                    }

                    cardinality = ir_inference.infer_cardinality(
                        ir, singletons, schema)

                    if cardinality == qlast.Cardinality.ONE:
                        link_card = pointers.PointerCardinality.ManyToOne
                    else:
                        link_card = pointers.PointerCardinality.ManyToMany

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

            if (isinstance(target, so.ObjectRef) and
                    target.classname == source_name):
                # Special case for loop links.  Since the target
                # is the same as the source, we know it's a proper
                # type.
                pass
            else:
                if target_type is None:
                    target_type = utils.resolve_typeref(target, schema=schema)

                if not isinstance(target_type, s_objtypes.ObjectType):
                    raise s_err.SchemaDefinitionError(
                        f'invalid link target, expected object type, got '
                        f'{target_type.__class__.__name__}',
                        context=astnode.targets[0].context
                    )

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

            base_prop_name = sn.Name('std::source')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            src_prop_name = sn.Name(name=s_name,
                                    module=cmd.classname.module)

            src_prop = lproperties.CreateProperty(
                classname=src_prop_name,
                metaclass=lproperties.Property
            )
            src_prop.update((
                sd.AlterObjectProperty(
                    property='name',
                    new_value=src_prop_name
                ),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[
                        so.ObjectRef(
                            classname=base_prop_name
                        )
                    ]
                ),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(
                        classname=cmd.classname
                    )
                ),
                sd.AlterObjectProperty(
                    property='target',
                    new_value=so.ObjectRef(
                        classname=source_name
                    )
                ),
                sd.AlterObjectProperty(
                    property='required',
                    new_value=True
                ),
                sd.AlterObjectProperty(
                    property='readonly',
                    new_value=True
                ),
            ))

            cmd.add(src_prop)

            base_prop_name = sn.Name('std::target')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            tgt_prop_name = sn.Name(name=s_name,
                                    module=cmd.classname.module)

            tgt_prop = lproperties.CreateProperty(
                classname=tgt_prop_name,
                metaclass=lproperties.Property
            )
            tgt_prop.update((
                sd.AlterObjectProperty(
                    property='name',
                    new_value=tgt_prop_name
                ),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[
                        so.ObjectRef(
                            classname=base_prop_name
                        )
                    ]
                ),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(
                        classname=cmd.classname
                    )
                ),
                sd.AlterObjectProperty(
                    property='target',
                    new_value=target
                ),
                sd.AlterObjectProperty(
                    property='required',
                    new_value=False
                ),
                sd.AlterObjectProperty(
                    property='readonly',
                    new_value=True
                ),
            ))

            cmd.add(tgt_prop)

            cls._parse_default(cmd)

        return cmd
示例#30
0
文件: expr.py 项目: virajs/edgedb
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)
示例#31
0
文件: viewgen.py 项目: virajs/edgedb
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
示例#32
0
文件: expr.py 项目: virajs/edgedb
def _infer_type(expr: irast.Base, *,
                ctx: context.CompilerContextLevel) -> s_obj.Object:
    return irutils.infer_type(expr, schema=ctx.env.schema)