Repository: edgedb/edgedb

Issue:
SchemaDefinitionError when migrating to an empty schema
- EdgeDB Version: `2.7`
- EdgeDB CLI Version: Irrelevant
- OS Version: Irrelevant

Steps to Reproduce:

1. Migrate to the schema below using `start migration to ...; populate migration; commit migration;`.
2. Migrate to an empty schema in the same way.
3. If step 2 does not fail, retry steps 1 and 2. This bug appears to be nondeterministic.
4. At some iteration, step 2 will fail with `SchemaDefinitionError: cannot alter link 'x' of object type 'default::C' because this affects expression of link 'y' of object type 'default::E'`.

We have seen at least 3 completely different errors (e.g. `constraint 'std::exclusive' does not exist`) with more complex schemas. This is a minimal, or near-minimal, reproduction.

Schema:

```
module default {           
    abstract type C {
        link x -> E {
            constraint exclusive;
        }
    }
    
    abstract type A;
    
    type B extending A;
    
    type D extending C, A;
    
    type E extending A {
        link y := assert_single(.<`x`[IS C]);
    }
}
```



Discussion:

Resolving patch:
diff --git a/edb/schema/ordering.py b/edb/schema/ordering.py
--- a/edb/schema/ordering.py
+++ b/edb/schema/ordering.py
@@ -915,10 +915,13 @@ def _get_referrers(
         if not ref.is_blocking_ref(schema, obj):
             continue
 
+        referrer: so.Object | None = None
+
         parent_ref = strongrefs.get(ref.get_name(schema))
         if parent_ref is not None:
-            referrer: so.Object = schema.get(parent_ref)
-        else:
+            referrer = schema.get(parent_ref, default=None)
+
+        if not referrer or obj == referrer:
             referrer = ref
 
         result.add(referrer)
diff --git a/edb/schema/properties.py b/edb/schema/properties.py
--- a/edb/schema/properties.py
+++ b/edb/schema/properties.py
@@ -189,7 +189,7 @@ def init_delta_command(
             **kwargs,
         )
         assert isinstance(delta, referencing.ReferencedObjectCommandBase)
-        delta.is_strong_ref = self.is_endpoint_pointer(schema)
+        delta.is_strong_ref = self.is_special_pointer(schema)
         return delta  # type: ignore
 
 


Test patch:
diff --git a/tests/test_edgeql_data_migration.py b/tests/test_edgeql_data_migration.py
--- a/tests/test_edgeql_data_migration.py
+++ b/tests/test_edgeql_data_migration.py
@@ -5922,6 +5922,8 @@ async def test_edgeql_migration_rebase_02(self):
         await self.interact([
             "did you create object type 'test::HasContent'?",
             "did you alter object type 'test::Post'?",
+            "did you alter constraint 'std::max_len_value' of property "
+            "'content'?",
             "did you create object type 'test::Reply'?",
             "did you alter property 'content' of object type 'test::Post'?",
         ])
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -4726,6 +4726,26 @@ def test_schema_migrations_equivalence_57b(self):
             global TwoUsers := (User);
         """])
 
+    def test_schema_migrations_equivalence_58(self):
+        self._assert_migration_equivalence([r"""
+            abstract type C {
+                link x -> E {
+                    constraint exclusive;
+                }
+            }
+
+            abstract type A;
+
+            type B extending A;
+
+            type D extending C, A;
+
+            type E extending A {
+                link y := assert_single(.<`x`[IS C]);
+            }
+        """, r"""
+        """])
+
     def test_schema_migrations_equivalence_compound_01(self):
         # Check that union types can be referenced in computables
         # Bug #2002.