Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2101,6 +2101,12 @@ func (d *ddlDiff) generateDropSQL(targetSchema string, collector *diffCollector,
generateDropTriggersFromModifiedTables(d.modifiedTables, targetSchema, collector)
generateDropTriggersFromModifiedViews(d.modifiedViews, targetSchema, collector, preDroppedViews)

// Drop triggers from tables and views that are themselves being dropped.
// This must happen before DROP FUNCTION so that trigger→function
// dependencies are removed first (#505).
generateDropTriggersFromDroppedTables(d.droppedTables, targetSchema, collector)
generateDropTriggersFromDroppedViews(d.droppedViews, targetSchema, collector, preDroppedViews)

// Drop aggregates before functions, since an aggregate depends on its
// transition/final functions (issue #416).
generateDropAggregatesSQL(d.droppedAggregates, targetSchema, collector)
Expand Down
77 changes: 77 additions & 0 deletions internal/diff/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,83 @@ func generateDropTriggersFromModifiedViews(views []*viewDiff, targetSchema strin
}
}

// generateDropTriggersFromDroppedTables emits DROP TRIGGER for all triggers on
// tables that are being dropped. This must run before DROP FUNCTION so that
// trigger→function dependencies are removed first (#505).
func generateDropTriggersFromDroppedTables(tables []*ir.Table, targetSchema string, collector *diffCollector) {
var allTriggers []*ir.Trigger

for _, table := range tables {
for _, trigger := range table.Triggers {
allTriggers = append(allTriggers, trigger)
}
}

sort.Slice(allTriggers, func(i, j int) bool {
if allTriggers[i].Schema != allTriggers[j].Schema {
return allTriggers[i].Schema < allTriggers[j].Schema
}
if allTriggers[i].Table != allTriggers[j].Table {
return allTriggers[i].Table < allTriggers[j].Table
}
return allTriggers[i].Name < allTriggers[j].Name
})

for _, trigger := range allTriggers {
tableName := getTableNameWithSchema(trigger.Schema, trigger.Table, targetSchema)
sql := fmt.Sprintf("DROP TRIGGER IF EXISTS %s ON %s;", ir.QuoteIdentifier(trigger.Name), tableName)

context := &diffContext{
Type: DiffTypeTableTrigger,
Operation: DiffOperationDrop,
Path: fmt.Sprintf("%s.%s.%s", trigger.Schema, trigger.Table, trigger.Name),
Source: trigger,
CanRunInTransaction: true,
}
collector.collect(context, sql)
}
}

// generateDropTriggersFromDroppedViews emits DROP TRIGGER for all triggers on
Comment thread
tianzhou marked this conversation as resolved.
// views that are being dropped (same rationale as dropped tables, #505).
func generateDropTriggersFromDroppedViews(views []*ir.View, targetSchema string, collector *diffCollector, preDroppedViews map[string]bool) {
var allTriggers []*ir.Trigger

for _, view := range views {
viewKey := view.Schema + "." + view.Name
if preDroppedViews != nil && preDroppedViews[viewKey] {
continue
Comment thread
tianzhou marked this conversation as resolved.
}
for _, trigger := range view.Triggers {
allTriggers = append(allTriggers, trigger)
}
}

sort.Slice(allTriggers, func(i, j int) bool {
if allTriggers[i].Schema != allTriggers[j].Schema {
return allTriggers[i].Schema < allTriggers[j].Schema
}
if allTriggers[i].Table != allTriggers[j].Table {
return allTriggers[i].Table < allTriggers[j].Table
}
return allTriggers[i].Name < allTriggers[j].Name
})

for _, trigger := range allTriggers {
tableName := getTableNameWithSchema(trigger.Schema, trigger.Table, targetSchema)
sql := fmt.Sprintf("DROP TRIGGER IF EXISTS %s ON %s;", ir.QuoteIdentifier(trigger.Name), tableName)

context := &diffContext{
Type: DiffTypeViewTrigger,
Operation: DiffOperationDrop,
Path: fmt.Sprintf("%s.%s.%s", trigger.Schema, trigger.Table, trigger.Name),
Source: trigger,
CanRunInTransaction: true,
}
collector.collect(context, sql)
}
}

// generateTriggerSQLWithMode generates CREATE [OR REPLACE] TRIGGER or CREATE CONSTRAINT TRIGGER statement
Comment thread
tianzhou marked this conversation as resolved.
func generateTriggerSQLWithMode(trigger *ir.Trigger, targetSchema string, qualifySchema bool) string {
// Build event list in standard order: INSERT, UPDATE, DELETE, TRUNCATE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DROP TRIGGER IF EXISTS events_notify ON events;

DROP FUNCTION IF EXISTS notify_event();

DROP TABLE IF EXISTS events CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- empty: drop everything
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE events (
id serial PRIMARY KEY,
payload jsonb NOT NULL,
created_at timestamp DEFAULT CURRENT_TIMESTAMP
);

CREATE FUNCTION notify_event()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
PERFORM pg_notify('events', NEW.id::text);
RETURN NEW;
END;
$$;

CREATE TRIGGER events_notify
AFTER INSERT ON events
FOR EACH ROW
EXECUTE FUNCTION notify_event();
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": "1.0.0",
"pgschema_version": "1.12.0",
"created_at": "1970-01-01T00:00:00Z",
"source_fingerprint": {
"hash": "b830414412e5c92476a85d494c5017028c5adcc5f7ead7b1322e85d7514349bc"
},
"groups": [
{
"steps": [
{
"sql": "DROP TRIGGER IF EXISTS events_notify ON events;",
"type": "table.trigger",
"operation": "drop",
"path": "public.events.events_notify"
},
{
"sql": "DROP FUNCTION IF EXISTS notify_event();",
"type": "function",
"operation": "drop",
"path": "public.notify_event"
},
{
"sql": "DROP TABLE IF EXISTS events CASCADE;",
"type": "table",
"operation": "drop",
"path": "public.events"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DROP TRIGGER IF EXISTS events_notify ON events;

DROP FUNCTION IF EXISTS notify_event();

DROP TABLE IF EXISTS events CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Plan: 2 to drop.

Summary by type:
functions: 1 to drop
tables: 1 to drop

Functions:
- notify_event

Tables:
- events
- events_notify (trigger)

DDL to be executed:
--------------------------------------------------

DROP TRIGGER IF EXISTS events_notify ON events;

DROP FUNCTION IF EXISTS notify_event();

DROP TABLE IF EXISTS events CASCADE;
Comment thread
tianzhou marked this conversation as resolved.