Repository: edgedb/edgedb

Issue:
Migration to move a trigger to parent type fails
<!-- 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.0-dev.7435+bb3b78c
- EdgeDB CLI Version: 3.0.0-dev.968+8eef530
- OS Version: Ubuntu

Steps to Reproduce:

1. Apply the starting schema
   ```
   module default {
       type Log {
           body: str;
           timestamp: datetime {
               default := datetime_current();
           }
       }
   
   
       abstract type Named {
           required name: str;
       }
   
       type Person extending Named {
           trigger log_insert after insert for each do (
               insert Log {
                   body := __new__.__type__.name ++ ' ' ++ __new__.name,
               }
           );
       }
   }
   ```
2. Move the trigger from `Person` to `Named`.
   ```
   module default {
       type Log {
           body: str;
           timestamp: datetime {
               default := datetime_current();
           }
       }
   
   
       abstract type Named {
           required name: str;
   
           trigger log_insert after insert for each do (
               insert Log {
                   body := __new__.__type__.name ++ ' ' ++ __new__.name,
               }
           );
       }
   
       type Person extending Named {
       }
   }
   ```
3. Try to `edgedb migration create` for the new schema.

<!-- If the issue is about a query error, please also provide your schema -->

I can neither alter nor drop the trigger from `Person` in the migration workflow:
```
$ edgedb migration create
did you create trigger '__::log_insert' of object type 'default::Named'? [y,n,l,c,b,s,q,?]
> y
did you alter trigger '__::log_insert' of object type 'default::Person'? [y,n,l,c,b,s,q,?]
> y
error: EdgeQLSyntaxError: Unexpected 'OWNED'
  ┌─ <statement>:2:35
  │
2 │     ALTER TRIGGER log_insert DROP OWNED;
  │                                   ^^^^^ error

Rolling back last operation...
did you alter trigger '__::log_insert' of object type 'default::Person'? [y,n,l,c,b,s,q,?]
> n
did you drop trigger '__::log_insert' of object type 'default::Person'? [y,n,l,c,b,s,q,?]
> y
error: SchemaError: cannot drop inherited trigger '__::log_insert' of object type 'default::Person'
  ┌─ <statement>:2:5
  │
2 │     DROP TRIGGER log_insert;
  │     ^^^^^^^^^^^^^^^^^^^^^^^ error
  │
  = trigger '__::log_insert' of object type 'default::Person' is inherited from:
    - object type 'default::Named'

Rolling back last operation...
```

P.S. The problem goes away if the trigger is moved _and_ renamed at the same time, then it simply is dropped from `Person` and created in `Named`. 


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
@@ -712,6 +712,21 @@ def write_ref_deps(
                         anc_item.deps.add(('delete', ref_name_str))
 
         if isinstance(obj, referencing.ReferencedObject):
+            if tag == 'delete':
+                # If the object is being deleted and then recreated
+                # via inheritance, that deletion needs to come before
+                # an ancestor gets created (since that will cause our
+                # recreation.)
+                try:
+                    new_obj = get_object(new_schema, op)
+                except errors.InvalidReferenceError:
+                    new_obj = None
+                if isinstance(new_obj, referencing.ReferencedInheritingObject):
+                    for ancestor in new_obj.get_implicit_ancestors(new_schema):
+                        rep_item = get_deps(
+                            ('create', str(ancestor.get_name(new_schema))))
+                        rep_item.deps.add((tag, str(op.classname)))
+
             referrer = obj.get_referrer(old_schema)
             if referrer is not None:
                 assert isinstance(referrer, so.QualifiedObject)
diff --git a/edb/schema/triggers.py b/edb/schema/triggers.py
--- a/edb/schema/triggers.py
+++ b/edb/schema/triggers.py
@@ -80,6 +80,17 @@ class Trigger(
         compcoef=None,
         inheritable=False)
 
+    # We don't support SET/DROP OWNED owned on triggers so we set its
+    # compcoef to 0.0
+    owned = so.SchemaField(
+        bool,
+        default=False,
+        inheritable=False,
+        compcoef=0.0,
+        reflection_method=so.ReflectionMethod.AS_LINK,
+        special_ddl_syntax=True,
+    )
+
     def get_subject(self, schema: s_schema.Schema) -> s_objtypes.ObjectType:
         subj: s_objtypes.ObjectType = self.get_field_value(schema, 'subject')
         return subj


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
@@ -11533,6 +11533,52 @@ async def test_edgeql_migration_alias_new_computed_02(self):
             }
         ''')
 
+    async def test_edgeql_migration_trigger_shift_01(self):
+        await self.migrate(r'''
+            type Log {
+                body: str;
+                timestamp: datetime {
+                    default := datetime_current();
+                }
+            }
+
+
+            abstract type Named {
+                required name: str;
+            }
+
+            type Person extending Named {
+                trigger log_insert after insert for each do (
+                    insert Log {
+                        body := __new__.__type__.name ++ ' ' ++ __new__.name,
+                    }
+                );
+            }
+        ''')
+
+        await self.migrate(r'''
+            type Log {
+                body: str;
+                timestamp: datetime {
+                    default := datetime_current();
+                }
+            }
+
+
+            abstract type Named {
+                required name: str;
+
+                trigger log_insert after insert for each do (
+                    insert Log {
+                        body := __new__.__type__.name ++ ' ' ++ __new__.name,
+                    }
+                );
+            }
+
+            type Person extending Named {
+            }
+        ''')
+
 
 class TestEdgeQLDataMigrationNonisolated(EdgeQLDataMigrationTestCase):
     TRANSACTION_ISOLATION = False