Repository: edgedb/edgedb

Issue:
`SET SINGLE USING` on a `required multi property` spuriously fails
```
edgedb> create type X { create required multi property foo -> str };
OK: CREATE TYPE
edgedb> insert X { foo := "test" };
[{"id": "e2b1ea82-ac12-11ed-8ac2-43e409d70c9b"}]
```

Trying to `set single` the property fails:
```
edgedb> alter type X alter property foo set single using (assert_single(.foo));
Error: MissingRequiredError: missing value for required property 'foo' of object type 'default::X'
```

I also tried with a constant in the using, still fails:
```
edgedb> alter type X alter property foo set single using ('x');
Error: MissingRequiredError: missing value for required property 'foo' of object type 'default::X'
```

Fails on 2.9 and on master. (I had thought maybe it was a regression introduced by #4873, but doesn't look like it)


Discussion:

Resolving patch:
diff --git a/edb/pgsql/delta.py b/edb/pgsql/delta.py
--- a/edb/pgsql/delta.py
+++ b/edb/pgsql/delta.py
@@ -19,6 +19,7 @@
 
 from __future__ import annotations
 from typing import *
+from copy import copy
 
 import collections.abc
 import itertools
@@ -3760,14 +3761,20 @@ def _alter_pointer_cardinality(
         if not is_multi:
             # Moving from pointer table to source table.
             cols = self.get_columns(ptr, schema)
+
+            # create columns
             alter_table = source_op.get_alter_table(
                 schema, context, manual=True)
-
+            cols_required: List[dbops.Column] = []
             for col in cols:
                 cond = dbops.ColumnExists(
                     ptr_stor_info.table_name,
                     column_name=col.name,
                 )
+                if col.required:
+                    cols_required.append(copy(col))
+
+                col.required = False
                 op = (dbops.AlterTableAddColumn(col), None, (cond, ))
                 alter_table.add_operation(op)
 
@@ -3779,6 +3786,16 @@ def _alter_pointer_cardinality(
             ''')
             self.pgops.add(dbops.Query(update_qry))
 
+            # set NOT NULL
+            if cols_required:
+                alter_table = source_op.get_alter_table(
+                    schema, context, manual=True
+                )
+                for col in cols_required:
+                    op2 = dbops.AlterTableAlterColumnNull(col.name, False)
+                    alter_table.add_operation(op2)
+                self.pgops.add(alter_table)
+
             # A link might still own a table if it has properties.
             if not has_table(ptr, schema):
                 self.drop_inhview(orig_schema, context, ptr)


Test patch:
diff --git a/tests/test_edgeql_ddl.py b/tests/test_edgeql_ddl.py
--- a/tests/test_edgeql_ddl.py
+++ b/tests/test_edgeql_ddl.py
@@ -3128,6 +3128,17 @@ async def test_edgeql_ddl_ptr_set_required_01(self):
                 }
             """)
 
+    async def test_edgeql_ddl_ptr_set_required_02(self):
+        await self.con.execute(
+            r"""
+            create type X { create required multi property foo -> str };
+            insert X { foo := "test" };
+            alter type X alter property foo set single
+                using (assert_single(.foo));
+            """
+        )
+        await self.assert_query_result("select X { foo }", [{"foo": "test"}])
+
     async def test_edgeql_ddl_link_property_01(self):
         with self.assertRaisesRegex(
                 edgedb.InvalidPropertyDefinitionError,