Exemplo n.º 1
0
def compile_Parameter(expr: qlast.Base, *,
                      ctx: context.ContextLevel) -> irast.Set:

    if ctx.env.options.func_params is not None:
        if ctx.env.options.schema_object_context is s_constr.Constraint:
            raise errors.InvalidConstraintDefinitionError(
                f'dollar-prefixed "$parameters" cannot be used here',
                context=expr.context)
        else:
            raise errors.InvalidFunctionDefinitionError(
                f'dollar-prefixed "$parameters" cannot be used here',
                context=expr.context)

    raise errors.QueryError(f'missing a type cast before the parameter',
                            context=expr.context)
Exemplo n.º 2
0
def compile_func_to_ir(func,
                       schema,
                       *,
                       anchors=None,
                       security_context=None,
                       modaliases=None,
                       implicit_id_in_shapes=False,
                       implicit_tid_in_shapes=False):
    """Compile an EdgeQL function into EdgeDB IR."""

    if debug.flags.edgeql_compile:
        debug.header('EdgeQL Function')
        debug.print(func.get_code(schema))

    trees = ql_parser.parse_block(func.get_code(schema) + ';')
    if len(trees) != 1:
        raise errors.InvalidFunctionDefinitionError(
            'functions can only contain one statement')

    tree = trees[0]
    if modaliases:
        ql_parser.append_module_aliases(tree, modaliases)

    param_anchors, param_aliases = get_param_anchors_for_callable(
        func.get_params(schema), schema)

    if anchors is None:
        anchors = {}

    anchors.update(param_anchors)
    tree.aliases.extend(param_aliases)

    ir = compile_ast_to_ir(
        tree,
        schema,
        anchors=anchors,
        func_params=func.get_params(schema),
        security_context=security_context,
        modaliases=modaliases,
        implicit_id_in_shapes=implicit_id_in_shapes,
        implicit_tid_in_shapes=implicit_tid_in_shapes,
        # the body of a session_only function can contain calls to
        # other session_only functions
        session_mode=func.get_session_only(schema))

    return ir
Exemplo n.º 3
0
    def _create_begin(self, schema, context):
        from edb.ir import utils as irutils

        fullname = self.classname
        shortname = sn.shortname_from_fullname(fullname)
        schema, cp = self._get_param_desc_from_delta(schema, context, self)
        signature = f'{shortname}({", ".join(p.as_str(schema) for p in cp)})'

        func = schema.get(fullname, None)
        if func:
            raise errors.DuplicateFunctionDefinitionError(
                f'cannot create the `{signature}` function: '
                f'a function with the same signature '
                f'is already defined',
                context=self.source_context)

        schema = super()._create_begin(schema, context)

        params: FuncParameterList = self.scls.get_params(schema)
        language = self.scls.get_language(schema)
        return_type = self.scls.get_return_type(schema)
        return_typemod = self.scls.get_return_typemod(schema)
        from_function = self.scls.get_from_function(schema)
        has_polymorphic = params.has_polymorphic(schema)
        polymorphic_return_type = return_type.is_polymorphic(schema)
        named_only = params.find_named_only(schema)
        session_only = self.scls.get_session_only(schema)

        # Certain syntax is only allowed in "EdgeDB developer" mode,
        # i.e. when populating std library, etc.
        if not context.stdmode and not context.testmode:
            if has_polymorphic or polymorphic_return_type:
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create `{signature}` function: '
                    f'generic types are not supported in '
                    f'user-defined functions',
                    context=self.source_context)
            elif from_function:
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create `{signature}` function: '
                    f'"FROM SQL FUNCTION" is not supported in '
                    f'user-defined functions',
                    context=self.source_context)
            elif language != qlast.Language.EdgeQL:
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create `{signature}` function: '
                    f'"FROM {language}" is not supported in '
                    f'user-defined functions',
                    context=self.source_context)

        if polymorphic_return_type and not has_polymorphic:
            raise errors.InvalidFunctionDefinitionError(
                f'cannot create `{signature}` function: '
                f'function returns a generic type but has no '
                f'generic parameters',
                context=self.source_context)

        overloaded_funcs = schema.get_functions(shortname, ())
        has_from_function = from_function

        for func in overloaded_funcs:
            func_params = func.get_params(schema)
            func_named_only = func_params.find_named_only(schema)
            func_from_function = func.get_from_function(schema)

            if func_named_only.keys() != named_only.keys():
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create `{signature}` function: '
                    f'overloading another function with different '
                    f'named only parameters: '
                    f'"{func.get_shortname(schema)}'
                    f'{func_params.as_str(schema)}"',
                    context=self.source_context)

            if ((has_polymorphic or func_params.has_polymorphic(schema))
                    and (func.get_return_typemod(schema) != return_typemod)):

                func_return_typemod = func.get_return_typemod(schema)
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create the polymorphic `{signature} -> '
                    f'{return_typemod.to_edgeql()} '
                    f'{return_type.get_displayname(schema)}` '
                    f'function: overloading another function with different '
                    f'return type {func_return_typemod.to_edgeql()} '
                    f'{func.get_return_type(schema).get_displayname(schema)}',
                    context=self.source_context)

            if session_only != func.get_session_only(schema):
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create `{signature}` function: '
                    f'overloading another function with different '
                    f'`session_only` flag',
                    context=self.source_context)

            if func_from_function:
                has_from_function = func_from_function

        if has_from_function:
            if (from_function != has_from_function or any(
                    f.get_from_function(schema) != has_from_function
                    for f in overloaded_funcs)):
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create the `{signature}` function: '
                    f'overloading "FROM SQL FUNCTION" functions is '
                    f'allowed only when all functions point to the same '
                    f'SQL function',
                    context=self.source_context)

        if (language == qlast.Language.EdgeQL and any(
                p.get_typemod(schema) is ft.TypeModifier.SET_OF
                for p in params.objects(schema))):
            raise errors.UnsupportedFeatureError(
                f'cannot create the `{signature}` function: '
                f'SET OF parameters in user-defined EdgeQL functions are '
                f'not supported',
                context=self.source_context)

        # check that params of type 'anytype' don't have defaults
        for p in params.objects(schema):
            p_default = p.get_default(schema)
            if p_default is None:
                continue

            p_type = p.get_type(schema)

            try:
                ir_default = p.get_ir_default(schema=schema)
            except Exception as ex:
                raise errors.InvalidFunctionDefinitionError(
                    f'cannot create the `{signature}` function: '
                    f'invalid default value {p_default.text!r} of parameter '
                    f'{p.get_displayname(schema)!r}: {ex}',
                    context=self.source_context)

            check_default_type = True
            if p_type.is_polymorphic(schema):
                if irutils.is_empty(ir_default.expr):
                    check_default_type = False
                else:
                    raise errors.InvalidFunctionDefinitionError(
                        f'cannot create the `{signature}` function: '
                        f'polymorphic parameter of type '
                        f'{p_type.get_displayname(schema)} cannot '
                        f'have a non-empty default value',
                        context=self.source_context)
            elif (p.get_typemod(schema) is ft.TypeModifier.OPTIONAL
                  and irutils.is_empty(ir_default.expr)):
                check_default_type = False

            if check_default_type:
                default_type = ir_default.stype
                if not default_type.assignment_castable_to(p_type, schema):
                    raise errors.InvalidFunctionDefinitionError(
                        f'cannot create the `{signature}` function: '
                        f'invalid declaration of parameter '
                        f'{p.get_displayname(schema)!r}: '
                        f'unexpected type of the default expression: '
                        f'{default_type.get_displayname(schema)}, expected '
                        f'{p_type.get_displayname(schema)}',
                        context=self.source_context)

        return schema
Exemplo n.º 4
0
def compile_func_to_ir(func,
                       schema,
                       *,
                       anchors=None,
                       security_context=None,
                       modaliases=None,
                       implicit_id_in_shapes=False,
                       implicit_tid_in_shapes=False):
    """Compile an EdgeQL function into EdgeDB IR."""

    if debug.flags.edgeql_compile:
        debug.header('EdgeQL Function')
        debug.print(func.get_code(schema))

    trees = ql_parser.parse_block(func.get_code(schema) + ';')
    if len(trees) != 1:
        raise errors.InvalidFunctionDefinitionError(
            'functions can only contain one statement')

    tree = trees[0]
    if modaliases:
        ql_parser.append_module_aliases(tree, modaliases)

    param_anchors, param_aliases = get_param_anchors_for_callable(
        func.get_params(schema),
        schema,
        inlined_defaults=func.has_inlined_defaults(schema))

    if anchors is None:
        anchors = {}

    anchors.update(param_anchors)
    tree.aliases.extend(param_aliases)

    ir = compile_ast_to_ir(
        tree,
        schema,
        anchors=anchors,
        func_params=func.get_params(schema),
        security_context=security_context,
        modaliases=modaliases,
        implicit_id_in_shapes=implicit_id_in_shapes,
        implicit_tid_in_shapes=implicit_tid_in_shapes,
        # the body of a session_only function can contain calls to
        # other session_only functions
        session_mode=func.get_session_only(schema))

    return_type = func.get_return_type(schema)
    if (not ir.stype.issubclass(schema, return_type)
            and not ir.stype.implicitly_castable_to(return_type, schema)):
        raise errors.InvalidFunctionDefinitionError(
            f'return type mismatch in function declared to return '
            f'{return_type.get_verbosename(schema)}',
            details=f'Actual return type is '
            f'{ir.stype.get_verbosename(schema)}',
            context=tree.context,
        )

    return_typemod = func.get_return_typemod(schema)
    if (return_typemod is not qltypes.TypeModifier.SET_OF
            and ir.cardinality is qltypes.Cardinality.MANY):
        raise errors.InvalidFunctionDefinitionError(
            f'return cardinality mismatch in function declared to return '
            f'a singleton',
            details=f'Function may return a set with more than one element.',
            context=tree.context,
        )

    return ir
Exemplo n.º 5
0
def compile_func_to_ir(func, schema, *,
                       anchors=None,
                       security_context=None,
                       modaliases=None,
                       implicit_id_in_shapes=False,
                       implicit_tid_in_shapes=False):
    """Compile an EdgeQL function into EdgeDB IR."""

    if debug.flags.edgeql_compile:
        debug.header('EdgeQL Function')
        debug.print(func.get_code(schema))

    trees = ql_parser.parse_block(func.get_code(schema) + ';')
    if len(trees) != 1:
        raise errors.InvalidFunctionDefinitionError(
            'functions can only contain one statement')

    tree = trees[0]
    if modaliases:
        ql_parser.append_module_aliases(tree, modaliases)

    if anchors is None:
        anchors = {}

    anchors['__defaults_mask__'] = irast.Parameter(
        name='__defaults_mask__',
        typeref=irtyputils.type_to_typeref(schema, schema.get('std::bytes')))

    func_params = func.get_params(schema)
    pg_params = s_func.PgParams.from_params(schema, func_params)
    for pi, p in enumerate(pg_params.params):
        p_shortname = p.get_shortname(schema)
        anchors[p_shortname] = irast.Parameter(
            name=p_shortname,
            typeref=irtyputils.type_to_typeref(schema, p.get_type(schema)))

        if p.get_default(schema) is None:
            continue

        tree.aliases.append(
            qlast.AliasedExpr(
                alias=p_shortname,
                expr=qlast.IfElse(
                    condition=qlast.BinOp(
                        left=qlast.FunctionCall(
                            func=('std', 'bytes_get_bit'),
                            args=[
                                qlast.FuncArg(
                                    arg=qlast.Path(steps=[
                                        qlast.ObjectRef(
                                            name='__defaults_mask__')
                                    ])),
                                qlast.FuncArg(
                                    arg=qlast.IntegerConstant(value=str(pi)))
                            ]),
                        right=qlast.IntegerConstant(value='0'),
                        op='='),
                    if_expr=qlast.Path(
                        steps=[qlast.ObjectRef(name=p_shortname)]),
                    else_expr=qlast._Optional(expr=p.get_ql_default(schema)))))

    ir = compile_ast_to_ir(
        tree, schema, anchors=anchors, func=func,
        security_context=security_context, modaliases=modaliases,
        implicit_id_in_shapes=implicit_id_in_shapes,
        implicit_tid_in_shapes=implicit_tid_in_shapes)

    return ir
Exemplo n.º 6
0
    def compile_function(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
        body: expr.Expression,
    ) -> expr.Expression:
        from edb.ir import ast as irast

        params = self._get_params(schema, context)
        session_only = self._get_attribute_value(schema, context,
                                                 'session_only')

        language = self._get_attribute_value(schema, context, 'language')
        assert language is qlast.Language.EdgeQL

        has_inlined_defaults = bool(params.find_named_only(schema))

        param_anchors = get_params_symtable(
            params,
            schema,
            inlined_defaults=has_inlined_defaults,
        )

        compiled = type(body).compiled(
            body,
            schema,
            anchors=param_anchors,
            func_params=params,
            # the body of a session_only function can contain calls to
            # other session_only functions
            session_mode=session_only,
        )

        ir = compiled.irast
        assert isinstance(ir, irast.Statement)
        schema = ir.schema

        return_type = self._get_attribute_value(schema, context, 'return_type')
        if (not ir.stype.issubclass(schema, return_type)
                and not ir.stype.implicitly_castable_to(return_type, schema)):
            raise errors.InvalidFunctionDefinitionError(
                f'return type mismatch in function declared to return '
                f'{return_type.get_verbosename(schema)}',
                details=f'Actual return type is '
                f'{ir.stype.get_verbosename(schema)}',
                context=body.qlast.context,
            )

        return_typemod = self._get_attribute_value(schema, context,
                                                   'return_typemod')
        if (return_typemod is not ft.TypeModifier.SET_OF
                and ir.cardinality.is_multi()):
            raise errors.InvalidFunctionDefinitionError(
                f'return cardinality mismatch in function declared to return '
                f'a singleton',
                details=(
                    f'Function may return a set with more than one element.'),
                context=body.qlast.context,
            )

        return compiled
Exemplo n.º 7
0
def compile_func_to_ir(
    func: s_func.Function,
    schema: s_schema.Schema,
) -> irast.Statement:
    """Compile an EdgeQL function into EdgeDB IR.

    Args:
        func:
            A function object.

        schema:
            A schema instance where the function is defined.

    Returns:
        An instance of :class:`ir.ast.Statement` representing the
        function body.
    """
    if debug.flags.edgeql_compile:
        debug.header('EdgeQL Function')
        debug.print(func.get_code(schema))

    code = func.get_code(schema)
    assert code is not None
    trees = ql_parser.parse_block(code + ';')
    if len(trees) != 1:
        raise errors.InvalidFunctionDefinitionError(
            'functions can only contain one statement')

    tree = trees[0]

    param_anchors, param_aliases = get_param_anchors_for_callable(
        func.get_params(schema),
        schema,
        inlined_defaults=func.has_inlined_defaults(schema))

    tree.aliases.extend(param_aliases)

    ir = compile_ast_to_ir(
        tree,
        schema,
        anchors=param_anchors,  # type: ignore
        # (typing#273)
        func_params=func.get_params(schema),
        # the body of a session_only function can contain calls to
        # other session_only functions
        session_mode=func.get_session_only(schema),
    )

    assert isinstance(ir, irast.Statement)

    return_type = func.get_return_type(schema)
    if (not ir.stype.issubclass(schema, return_type)
            and not ir.stype.implicitly_castable_to(return_type, schema)):
        raise errors.InvalidFunctionDefinitionError(
            f'return type mismatch in function declared to return '
            f'{return_type.get_verbosename(schema)}',
            details=f'Actual return type is '
            f'{ir.stype.get_verbosename(schema)}',
            context=tree.context,
        )

    return_typemod = func.get_return_typemod(schema)
    if (return_typemod is not qltypes.TypeModifier.SET_OF
            and ir.cardinality is qltypes.Cardinality.MANY):
        raise errors.InvalidFunctionDefinitionError(
            f'return cardinality mismatch in function declared to return '
            f'a singleton',
            details=f'Function may return a set with more than one element.',
            context=tree.context,
        )

    return ir