Repository: edgedb/edgedb

Issue:
`InternalServerError` when not using a variable defined in `with` in a query
<!-- Please search existing issues to avoid creating duplicates. -->

<!--
For the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).
For the EdgeDB CLI Version: Run `edgedb --version` from anywhere
-->

- EdgeDB Version: `3.4+301ba34`
- EdgeDB CLI Version: `EdgeDB CLI 3.5.0+907ff37`
- OS Version: ArchLinux 6.5.6-arch2-1

Steps to Reproduce:

1. Construct the following schema:
```edgeql
module platform {
  type Actor {
    required name: str;
  }
  type Post {
    required text: str;
    single link parent_post: Post;
    single link author: Actor;
    multi link posts := .<parent_post[is Post];
  }
}
```

2. Run the following queries (here as .py, but the result is the same in the repl):
```py
import edgedb
client = edgedb.create_client()
for tx in client.transaction():
    with tx:
        tx.execute(
            """
            INSERT platform::Actor {
                name := "Foo"
            };

            INSERT platform::Actor {
                name := "Bar"
            };
            """
        )
        tx.execute(
            """
            with parent := (
                INSERT platform::Post {
                    text := "Foo's Post",
                    author := (SELECT platform::Actor filter .name = "Foo" limit 1)
                }
            )

            INSERT platform::Post {
                text := "Bar's Post",
                author := (SELECT platform::Actor filter .name = "Bar" limit 1),
            };
            """
        )
client.close()
```

This triggers the following error:
```
edgedb error: InternalServerError: None
  Hint: This is most likely a bug in EdgeDB. Please consider opening an issue ticket at https://github.com/edgedb/edgedb/issues/new?template=bug_report.md
  Server traceback:
      Traceback (most recent call last):
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/__init__.py", line 135, in compile_ir_to_sql_tree
          qtree = dispatch.compile(ir_expr, ctx=ctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 60, in compile_Set
          _compile_set_impl(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 115, in _compile_set_impl
          _compile_set(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 701, in _compile_set
          relgen.get_set_rvar(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 164, in get_set_rvar
          return _process_toplevel_query(ir_set, ctx=ctx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 286, in _process_toplevel_query
          rvars = _get_set_rvar(ir_set, ctx=ctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 348, in _get_set_rvar
          return process_set_as_subquery(ir_set, ctx=ctx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 1425, in process_set_as_subquery
          dispatch.visit(expr, ctx=newctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dispatch.py", line 44, in visit
          compile(ir, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/stmt.py", line 148, in compile_InsertStmt
          parts = dml.init_dml_stmt(stmt, ctx=ctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dml.py", line 95, in init_dml_stmt
          clauses.compile_dml_bindings(ir_stmt, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/clauses.py", line 251, in compile_dml_bindings
          dispatch.compile(binding, ctx=bctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 60, in compile_Set
          _compile_set_impl(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 111, in _compile_set_impl
          _compile_set(ir_set, ctx=scopectx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 701, in _compile_set
          relgen.get_set_rvar(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 222, in get_set_rvar
          rvars = _get_set_rvar(ir_set, ctx=subctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 348, in _get_set_rvar
          return process_set_as_subquery(ir_set, ctx=ctx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 1425, in process_set_as_subquery
          dispatch.visit(expr, ctx=newctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dispatch.py", line 44, in visit
          compile(ir, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/stmt.py", line 156, in compile_InsertStmt
          dml.process_insert_body(
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dml.py", line 691, in process_insert_body
          external_inserts = process_insert_shape(
                             ^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dml.py", line 1017, in process_insert_shape
          compile_insert_shape_element(
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dml.py", line 1090, in compile_insert_shape_element
          dispatch.visit(shape_el, ctx=insvalctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 85, in visit_Set
          _compile_set_impl(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 111, in _compile_set_impl
          _compile_set(ir_set, ctx=scopectx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 701, in _compile_set
          relgen.get_set_rvar(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 222, in get_set_rvar
          rvars = _get_set_rvar(ir_set, ctx=subctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 348, in _get_set_rvar
          return process_set_as_subquery(ir_set, ctx=ctx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 1425, in process_set_as_subquery
          dispatch.visit(expr, ctx=newctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dispatch.py", line 44, in visit
          compile(ir, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/stmt.py", line 88, in compile_SelectStmt
          outvar = clauses.compile_output(stmt.result, ctx=ictx)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/clauses.py", line 225, in compile_output
          dispatch.visit(ir_set, ctx=newctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 85, in visit_Set
          _compile_set_impl(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 111, in _compile_set_impl
          _compile_set(ir_set, ctx=scopectx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/expr.py", line 701, in _compile_set
          relgen.get_set_rvar(ir_set, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 222, in get_set_rvar
          rvars = _get_set_rvar(ir_set, ctx=subctx)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 348, in _get_set_rvar
          return process_set_as_subquery(ir_set, ctx=ctx)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/relgen.py", line 1425, in process_set_as_subquery
          dispatch.visit(expr, ctx=newctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/dispatch.py", line 44, in visit
          compile(ir, ctx=ctx)
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/functools.py", line 909, in wrapper
          return dispatch(args[0].__class__)(*args, **kw)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/stmt.py", line 99, in compile_SelectStmt
          clauses.compile_filter_clause(
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/clauses.py", line 263, in compile_filter_clause
          assert cardinality != qltypes.Cardinality.UNKNOWN
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      AssertionError

      The above exception was the direct cause of the following exception:

      Traceback (most recent call last):
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler_pool/worker.py", line 161, in compile
          units, cstate = COMPILER.compile(
                          ^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 856, in compile
          unit_group = compile(ctx=ctx, source=source)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2068, in compile
          return _try_compile(ctx=ctx, source=source)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2136, in _try_compile
          comp, capabilities = _compile_dispatch_ql(
                               ^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2042, in _compile_dispatch_ql
          query = _compile_ql_query(ctx, ql, script_info=script_info)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 1588, in _compile_ql_query
          qtree, sql_text, argmap = pg_compiler.compile_ir_to_tree_and_sql(
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/__init__.py", line 170, in compile_ir_to_tree_and_sql
          qtree, _, argmap = compile_ir_to_sql_tree(
                             ^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/gregdan3/.local/share/edgedb/portable/3.4/lib/python3.11/site-packages/edb/pgsql/compiler/__init__.py", line 152, in compile_ir_to_sql_tree
          raise errors.InternalServerError(*args) from e
      edb.errors.InternalServerError: None

```

The expected behavior is that the syntax error (?) should be identified and warned against.

(?): Is it a syntax error to not use a variable, like it is in Go? Or is this not meant to be an error at all?
Either way, the error is resolved by using the `parent` variable to fill in the `parent_post` property of the second insert to `Post`.


Discussion:
> (?): Is it a syntax error to not use a variable, like it is in Go? Or is this not meant to be an error at all? Either way, the error is resolved by using the `parent` variable to fill in the `parent_post` property of the second insert to `Post`.

It *used* to be, but we started allowing it in 3.0.
I'm surprised this one doesn't work; I know there are similar tests. I'll take a look.

Resolving patch:
diff --git a/edb/edgeql/compiler/inference/cardinality.py b/edb/edgeql/compiler/inference/cardinality.py
--- a/edb/edgeql/compiler/inference/cardinality.py
+++ b/edb/edgeql/compiler/inference/cardinality.py
@@ -1152,6 +1152,9 @@ def _infer_stmt_cardinality(
     scope_tree: irast.ScopeTreeNode,
     ctx: inference_context.InfCtx,
 ) -> qltypes.Cardinality:
+    for part in (ir.bindings or []):
+        infer_cardinality(part, scope_tree=scope_tree, ctx=ctx)
+
     result = ir.subject if isinstance(ir, irast.MutatingStmt) else ir.result
     result_card = infer_cardinality(
         result,
@@ -1278,6 +1281,8 @@ def __infer_insert_stmt(
     scope_tree: irast.ScopeTreeNode,
     ctx: inference_context.InfCtx,
 ) -> qltypes.Cardinality:
+    for part in (ir.bindings or []):
+        infer_cardinality(part, scope_tree=scope_tree, ctx=ctx)
 
     infer_cardinality(
         ir.subject, is_mutation=True, scope_tree=scope_tree, ctx=ctx
diff --git a/edb/edgeql/compiler/inference/multiplicity.py b/edb/edgeql/compiler/inference/multiplicity.py
--- a/edb/edgeql/compiler/inference/multiplicity.py
+++ b/edb/edgeql/compiler/inference/multiplicity.py
@@ -553,7 +553,7 @@ def _infer_stmt_multiplicity(
     scope_tree: irast.ScopeTreeNode,
     ctx: inf_ctx.InfCtx,
 ) -> inf_ctx.MultiplicityInfo:
-    # WITH block bindings need to be validated, they don't have to
+    # WITH block bindings need to be validated; they don't have to
     # have multiplicity UNIQUE, but their sub-expressions must be valid.
     for part in (ir.bindings or []):
         infer_multiplicity(part, scope_tree=scope_tree, ctx=ctx)
@@ -649,6 +649,11 @@ def __infer_insert_stmt(
     scope_tree: irast.ScopeTreeNode,
     ctx: inf_ctx.InfCtx,
 ) -> inf_ctx.MultiplicityInfo:
+    # WITH block bindings need to be validated, they don't have to
+    # have multiplicity UNIQUE, but their sub-expressions must be valid.
+    for part in (ir.bindings or []):
+        infer_multiplicity(part, scope_tree=scope_tree, ctx=ctx)
+
     # INSERT will always return a proper set, but we still want to
     # process the sub-expressions.
     infer_multiplicity(


Test patch:
diff --git a/tests/test_edgeql_insert.py b/tests/test_edgeql_insert.py
--- a/tests/test_edgeql_insert.py
+++ b/tests/test_edgeql_insert.py
@@ -249,6 +249,35 @@ async def test_edgeql_insert_unused_01(self):
             ]
         )
 
+        await self.con.execute(r"""
+            with _ := (
+                INSERT InsertTest {
+                    name := 'insert simple 01',
+                    l2 := (select 1 filter true),
+                }
+            ),
+            INSERT InsertTest {
+                name := 'insert simple 01',
+                l2 := 2,
+            }
+        """)
+        await self.assert_query_result(
+            r"""
+                SELECT
+                    InsertTest {
+                        l2
+                    }
+                FILTER
+                    InsertTest.name = 'insert simple 01'
+                ORDER BY .l2
+            """,
+            [
+                {'l2': 0},
+                {'l2': 1},
+                {'l2': 2},
+            ]
+        )
+
     async def test_edgeql_insert_nested_01(self):
         await self.con.execute('''
             INSERT Subordinate {