Repository: edgedb/edgedb

Issue:
Insert-or-select fails when it's combined with a shape.
- EdgeDB Version: EdgeDB 1.0-alpha.6+dev.5050.gb8aa1c99
- OS Version: Linux

Steps to Reproduce:
```
CREATE TYPE default::Foo {
    CREATE OPTIONAL SINGLE PROPERTY name -> std::str {
        CREATE CONSTRAINT std::exclusive;
    };
};
CREATE TYPE default::Bar {
    CREATE REQUIRED SINGLE LINK foo -> default::Foo;
};
INSERT Foo {name := 'foo0'};
```
Then the following happens:
```
victor> SELECT (INSERT Bar {foo := (INSERT Foo {name := 'foo0'} UNLESS CONFLICT ON .name ELSE (SELECT Foo))}) {id};
{Object {id: 948f4c8e-ed81-11ea-955a-5b32015497c5}}
victor> SELECT (INSERT Bar {foo := (INSERT Foo {name := 'foo0'} UNLESS CONFLICT ON .name ELSE (SELECT Foo))}) {id, foo};
ERROR: InternalServerError: more than one row returned by a subquery used as an expression
  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:
      edb.errors.InternalServerError: more than one row returned by a subquery used as an expression
```
So it's not that the cardinality check fails, it's that the SQL causes an effect similar to a cardinality problem if foo is included in the final shape to be returned.


Discussion:
@msullivan, this appears to be due to `add_type_rel_overlay` erroneously adding the `UNION` of both `ELSE` sides instead of only the `INSERT` part.

Resolving patch:
diff --git a/edb/pgsql/compiler/dml.py b/edb/pgsql/compiler/dml.py
--- a/edb/pgsql/compiler/dml.py
+++ b/edb/pgsql/compiler/dml.py
@@ -372,8 +372,12 @@ def fini_dml_stmt(
     # referencing this class yields the expected results.
     dml_stack = get_dml_stmt_stack(ir_stmt, ctx=ctx)
     if isinstance(ir_stmt, irast.InsertStmt):
+        # The union CTE might have a SELECT from an ELSE clause, which
+        # we don't actually want to include.
+        assert len(parts.dml_ctes) == 1
+        cte = next(iter(parts.dml_ctes.values()))[0]
         relctx.add_type_rel_overlay(
-            ir_stmt.subject.typeref, 'union', union_cte,
+            ir_stmt.subject.typeref, 'union', cte,
             dml_stmts=dml_stack, path_id=ir_stmt.subject.path_id, ctx=ctx)
     elif isinstance(ir_stmt, irast.DeleteStmt):
         relctx.add_type_rel_overlay(


Test patch:
diff --git a/tests/schemas/insert.esdl b/tests/schemas/insert.esdl
--- a/tests/schemas/insert.esdl
+++ b/tests/schemas/insert.esdl
@@ -56,6 +56,10 @@ type Person {
     };
 }
 
+type PersonWrapper {
+    required single link person -> Person;
+}
+
 type DefaultTest1 {
     property num -> int64 {
         default := 42;
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
@@ -2227,6 +2227,25 @@ async def test_edgeql_insert_unless_conflict_07(self):
              {"name": "Phil Emarg", "tag": None}]
         )
 
+    async def test_edgeql_insert_unless_conflict_08(self):
+        query = r'''
+            WITH MODULE test
+            SELECT (
+                INSERT PersonWrapper {
+                    person := (
+                        INSERT Person { name := "foo" }
+                        UNLESS CONFLICT ON .name ELSE (SELECT Person)
+                    )
+                }
+            ) {id, person};
+        '''
+
+        res1 = await self.con.query_one(query)
+        res2 = await self.con.query_one(query)
+
+        self.assertNotEqual(res1.id, res2.id)
+        self.assertEqual(res1.person, res2.person)
+
     async def test_edgeql_insert_dependent_01(self):
         query = r'''
             WITH MODULE test