Skip to content

Commit

Permalink
Fix inline functions substituting parameters from hidden nodes. (#8288)
Browse files Browse the repository at this point in the history
A function body may be a statement which contains references to a parent statement. Ensure that this parent's parameters are not substituted while inlining the function parameters.
  • Loading branch information
dnwpark authored and deepbuzin committed Feb 18, 2025
1 parent 5187a6b commit 6982527
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
12 changes: 12 additions & 0 deletions edb/common/ast/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ def generic_visit(self, node):
changes = {}

for field, old_value in base.iter_fields(node, include_meta=False):
field_spec = node._fields[field]
if self.skip_hidden and field_spec.hidden:
continue
if field in self.extra_skips:
continue

old_value = getattr(node, field, None)

if typeutils.is_container(old_value):
Expand All @@ -79,6 +85,12 @@ def generic_visit(self, node):

else:
for field, old_value in base.iter_fields(node, include_meta=False):
field_spec = node._fields[field]
if self.skip_hidden and field_spec.hidden:
continue
if field in self.extra_skips:
continue

old_value = getattr(node, field, None)

if typeutils.is_container(old_value):
Expand Down
4 changes: 4 additions & 0 deletions edb/edgeql/compiler/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,10 @@ def compile_FunctionCall(

class ArgumentInliner(ast.NodeTransformer):

# Don't look through hidden nodes, they may contain references to nodes
# which should not be modified. For example, irast.Stmt.parent_stmt.
skip_hidden = True

mapped_args: dict[irast.PathId, irast.PathId]
inlined_arg_keys: list[int | str]

Expand Down
39 changes: 39 additions & 0 deletions tests/test_edgeql_functions_inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3133,6 +3133,45 @@ async def test_edgeql_functions_inline_nested_basic_20(self):
sort=True,
)

async def test_edgeql_functions_inline_nested_basic_21(self):
# Inner function body is a statement with a parent statement
#
# A function body may be a statement which contains references to a
# parent statement. Ensure that this parent's parameters are not
# substituted while inlining the function parameters.
#
# In this case the outer function's `for` contains the parameter `x`
# which is at risk of being substituted when the inner function
# inlines its parameters.
await self.con.execute('''
create function inner(x: int64) -> int64 {
set is_inlined := true;
using (select x)
};
create function foo(x: int64) -> set of int64 {
set is_inlined := true;
using (for y in {x, x + 1, x + 2} union (inner(y)));
};
''')
await self.assert_query_result(
'select foo(<int64>{})',
[],
)
await self.assert_query_result(
'select foo(10)',
[10, 11, 12],
)
await self.assert_query_result(
'select foo({10, 20, 30})',
[10, 11, 12, 20, 21, 22, 30, 31, 32],
sort=True,
)
await self.assert_query_result(
'for x in {10, 20, 30} union (select foo(x))',
[10, 11, 12, 20, 21, 22, 30, 31, 32],
sort=True,
)

async def test_edgeql_functions_inline_nested_array_01(self):
# Return array from inner function
await self.con.execute('''
Expand Down

0 comments on commit 6982527

Please sign in to comment.