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