Skip to content

Commit

Permalink
Add rudimentary support for inline function calls (#7173)
Browse files Browse the repository at this point in the history
If the IR produces a `FunctionCall` with non-empty `body` attribute, we
use that instead of compiling an SQL function call.  It is currently the
responsibility of the IR producer to make sure that any arguments are
wired properly via anchors.
  • Loading branch information
elprans authored Apr 10, 2024
1 parent 33f9716 commit 411d23f
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 7 deletions.
25 changes: 23 additions & 2 deletions edb/edgeql/compiler/inference/cardinality.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,10 +749,31 @@ def __infer_func_call(
upper = (CB_MANY if force_multi
else max(arg_upper) if ir.preserves_upper_cardinality
else ret_upper_bound)
return _bounds_to_card(lower, upper)
call_card = _bounds_to_card(lower, upper)

else:
return _standard_call_cardinality(ir, cards, ctx=ctx)
call_card = _standard_call_cardinality(ir, cards, ctx=ctx)

if ir.body is not None:
body_card = infer_cardinality(ir.body, scope_tree=scope_tree, ctx=ctx)
# Check that inline body cardinality does not disagree with
# declared function cardinality.
if body_card.can_be_zero() and not call_card.can_be_zero():
raise errors.QueryError(
'inline function body expression returns a possibly empty '
'result while the function is not declared as returning '
'OPTIONAL',
span=ir.span,
)
if body_card.is_multi() and not call_card.is_multi():
raise errors.QueryError(
'inline function body expression possibly returns more '
'than one element, while the function is not declared as '
'returning SET OF',
span=ir.span,
)

return call_card


@_infer_cardinality.register
Expand Down
9 changes: 9 additions & 0 deletions edb/edgeql/compiler/inference/volatility.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ def __infer_func_call(
ir: irast.FunctionCall,
env: context.Environment,
) -> InferredVolatility:
if ir.body is not None:
body_volatility = infer_volatility(ir.body, env=env)
if body_volatility > ir.volatility:
raise errors.QueryError(
f'inline function body expression is {body_volatility} '
f'while the function is declared as {ir.volatility}',
span=ir.span,
)

if ir.args:
return _max_volatility([
_common_volatility((arg.expr for arg in ir.args), env),
Expand Down
8 changes: 8 additions & 0 deletions edb/ir/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,8 @@ class Call(ImmutableExpr):

class FunctionCall(Call):

__ast_mutable_fields__ = frozenset(('extras', 'body'))

# If the bound callable is a "USING SQL" callable, this
# attribute will be set to the name of the SQL function.
func_sql_function: typing.Optional[str]
Expand Down Expand Up @@ -1020,6 +1022,12 @@ class FunctionCall(Call):
# Additional arguments representing global variables
global_args: typing.Optional[typing.List[Set]] = None

# Any extra information useful for compilation of special-case callables.
extras: typing.Optional[dict[str, typing.Any]] = None

# Inline body of the callable.
body: typing.Optional[Set] = None


class OperatorCall(Call):

Expand Down
14 changes: 9 additions & 5 deletions edb/pgsql/compiler/relgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -3351,13 +3351,17 @@ def process_set_as_func_expr(
with ctx.subrel() as newctx:
newctx.expr_exposed = False
args = _compile_call_args(ir_set, ctx=newctx)
name = get_func_call_backend_name(expr, ctx=newctx)

if expr.typemod is qltypes.TypeModifier.SetOfType:
set_expr = _process_set_func(
ir_set, func_name=name, args=args, ctx=newctx)
if expr.body is not None:
set_expr = dispatch.compile(expr.body, ctx=newctx)
else:
set_expr = pgast.FuncCall(name=name, args=args)
name = get_func_call_backend_name(expr, ctx=newctx)

if expr.typemod is qltypes.TypeModifier.SetOfType:
set_expr = _process_set_func(
ir_set, func_name=name, args=args, ctx=newctx)
else:
set_expr = pgast.FuncCall(name=name, args=args)

if expr.error_on_null_result:
set_expr = pgast.FuncCall(
Expand Down

0 comments on commit 411d23f

Please sign in to comment.