Repository: edgedb/edgedb

Issue:
ISE: there is no range var for (__derived__::expr~2) source in <pg.SelectStmt at ...>
<!-- Please search existing issues to avoid creating duplicates. -->
- EdgeDB Version: `1.2+85bef76`
- OS Version: running in docker, `latest` / `sha256:91770f634b4dbcd9466fdc7d5b52f1a966a78f81cb37b2f3a1e0ea3acb683cd1`

Steps to Reproduce:

1. I wrote a reproducer using the Go driver: https://github.com/das7pad/edgedb-go/commit/b500bd62e231b3b141ad367c56acdc8177c990b7

<!-- If the issue is about a query error, please also provide your schema -->
Schema:

```
module default {
	abstract type Node {
		required property path -> str;
	}
	type RootNode extending Node {}
	type VisibleNode extending Node {
		required link parent -> Node;
		multi link children := .<parent[is VisibleNode];
	}
}
```

Minimal query that triggers this ISE:
```
select (update VisibleNode set { }) { children }
```


The full schema has more levels of inheritance and I can query another computed back-link on the same level as _VisibleNode.children_ that points at a different implementation of _Node_ just fine. So it's not computed back-links per-se that do not work in a filter from an update. Could this be caused by type self-reference?


Discussion:

Resolving patch:
diff --git a/edb/pgsql/compiler/relctx.py b/edb/pgsql/compiler/relctx.py
--- a/edb/pgsql/compiler/relctx.py
+++ b/edb/pgsql/compiler/relctx.py
@@ -442,6 +442,35 @@ def new_free_object_rvar(
     return rvar_for_rel(qry, typeref=typeref, lateral=lateral, ctx=ctx)
 
 
+def _deep_copy_primitive_rvar_path_var(
+    orig_id: irast.PathId, new_id: irast.PathId,
+    rvar: pgast.PathRangeVar, *,
+    env: context.Environment
+) -> None:
+    """Copy one identity path to another in a primitive rvar.
+
+    The trickiness here is because primitive rvars might have an
+    overlay stack, which means if they are joined on, it might be
+    using _lateral_union_join, which requires every component of
+    the union to have all the path bonds.
+    """
+
+    def _copy_id(component: pgast.Query) -> None:
+        rref = pathctx.get_path_var(
+            component, orig_id, aspect='identity', env=env)
+        pathctx.put_path_var(
+            component, new_id, rref, aspect='identity',
+            env=env)
+
+    if isinstance(rvar, pgast.RangeSubselect):
+        astutils.for_each_query_in_set(rvar.query, _copy_id)
+    else:
+        rref = pathctx.get_path_output(
+            rvar.query, orig_id, aspect='identity', env=env)
+        pathctx.put_rvar_path_output(
+            rvar, new_id, aspect='identity', var=rref, env=env)
+
+
 def new_primitive_rvar(
     ir_set: irast.Set,
     *,
@@ -483,13 +512,16 @@ def new_primitive_rvar(
             assert prefix_path_id is not None, 'expected a path'
 
             flipped_id = path_id.extend(ptrref=rptrref)
-            rref = pathctx.get_path_output(
-                set_rvar.query, flipped_id, aspect='identity', env=ctx.env)
 
+            # Unfortunately we can't necessarily just install the
+            # prefix path id path---the rvar from range_from_typeref
+            # might be a DML overlay, which means joins on it will try
+            # to use _lateral_union_join; this means that all of the
+            # path bonds need to be valid on each *subquery*, so we
+            # need to set them up in each subquery.
+            _deep_copy_primitive_rvar_path_var(
+                flipped_id, prefix_path_id, set_rvar, env=ctx.env)
             pathctx.put_rvar_path_bond(set_rvar, prefix_path_id)
-            pathctx.put_rvar_path_output(
-                set_rvar, prefix_path_id,
-                aspect='identity', var=rref, env=ctx.env)
 
     return set_rvar
 


Test patch:
diff --git a/tests/test_edgeql_tree.py b/tests/test_edgeql_tree.py
--- a/tests/test_edgeql_tree.py
+++ b/tests/test_edgeql_tree.py
@@ -1065,3 +1065,31 @@ async def test_edgeql_tree_update_08(self):
                 },
             ],
         )
+
+    async def test_edgeql_tree_update_09(self):
+        # A trivial update that accesses children
+        await self.assert_query_result(
+            r"""
+                select (update Tree filter .val = "00" set { }) {
+                    children: {val}
+                }
+            """,
+            [{"children": [{"val": "000"}]}],
+        )
+
+    async def test_edgeql_tree_update_10(self):
+        # A real update that accesses children
+        await self.assert_query_result(
+            r"""
+               select (
+                    update Tree filter .val = {"0", "00"}
+                    set { parent := {} }
+               ) {
+                   val, children: {val} order by .val
+               } order by .val;
+            """,
+            [
+                {"children": [{"val": "01"}, {"val": "02"}], "val": "0"},
+                {"children": [{"val": "000"}], "val": "00"}
+            ]
+        )