Skip to content

Commit e2bef36

Browse files
feat: implement extension detection and management in schema diff
1 parent e4f3a12 commit e2bef36

13 files changed

Lines changed: 1412 additions & 55 deletions

File tree

internal/diff/diff.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
DiffTypePrivilege
4343
DiffTypeRevokedDefaultPrivilege
4444
DiffTypeColumnPrivilege
45+
DiffTypeExtension
4546
)
4647

4748
// String returns the string representation of DiffType
@@ -103,6 +104,8 @@ func (d DiffType) String() string {
103104
return "revoked_default_privilege"
104105
case DiffTypeColumnPrivilege:
105106
return "column_privilege"
107+
case DiffTypeExtension:
108+
return "extension"
106109
default:
107110
return "unknown"
108111
}
@@ -177,6 +180,8 @@ func (d *DiffType) UnmarshalJSON(data []byte) error {
177180
*d = DiffTypeRevokedDefaultPrivilege
178181
case "column_privilege":
179182
*d = DiffTypeColumnPrivilege
183+
case "extension":
184+
*d = DiffTypeExtension
180185
default:
181186
return fmt.Errorf("unknown diff type: %s", s)
182187
}
@@ -260,6 +265,8 @@ type Diff struct {
260265
}
261266

262267
type ddlDiff struct {
268+
addedExtensions []string
269+
droppedExtensions []string
263270
addedSchemas []*ir.Schema
264271
droppedSchemas []*ir.Schema
265272
modifiedSchemas []*schemaDiff
@@ -462,6 +469,26 @@ func GenerateMigration(oldIR, newIR *ir.IR, targetSchema string) []Diff {
462469
modifiedColumnPrivileges: []*columnPrivilegeDiff{},
463470
}
464471

472+
// Compare extensions
473+
oldExtSet := make(map[string]bool)
474+
for _, ext := range oldIR.Extensions {
475+
oldExtSet[ext] = true
476+
}
477+
for _, ext := range newIR.Extensions {
478+
if !oldExtSet[ext] {
479+
diff.addedExtensions = append(diff.addedExtensions, ext)
480+
}
481+
}
482+
newExtSet := make(map[string]bool)
483+
for _, ext := range newIR.Extensions {
484+
newExtSet[ext] = true
485+
}
486+
for _, ext := range oldIR.Extensions {
487+
if !newExtSet[ext] {
488+
diff.droppedExtensions = append(diff.droppedExtensions, ext)
489+
}
490+
}
491+
465492
// Compare schemas first in deterministic order
466493
schemaNames := sortedKeys(newIR.Schemas)
467494
for _, name := range schemaNames {
@@ -1497,6 +1524,18 @@ func (d *ddlDiff) generatePreDropMaterializedViewsSQL(targetSchema string, colle
14971524

14981525
// generateCreateSQL generates CREATE statements in dependency order
14991526
func (d *ddlDiff) generateCreateSQL(targetSchema string, collector *diffCollector) {
1527+
// Create extensions first - must be installed before any schema objects that depend on them
1528+
for _, ext := range d.addedExtensions {
1529+
context := &diffContext{
1530+
Type: DiffTypeExtension,
1531+
Operation: DiffOperationCreate,
1532+
Path: ext,
1533+
Source: &ir.Extension{Name: ext},
1534+
CanRunInTransaction: true,
1535+
}
1536+
collector.collect(context, fmt.Sprintf("CREATE EXTENSION IF NOT EXISTS %s;", ir.QuoteIdentifier(ext)))
1537+
}
1538+
15001539
// Note: Schema creation is out of scope for schema-level comparisons
15011540

15021541
// Build function lookup early - needed for both domain and table dependency checks

internal/dump/formatter.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (f *DumpFormatter) FormatMultiFile(diffs []diff.Diff, outputPath string) er
116116
}
117117

118118
// Create files in dependency order
119-
orderedDirs := []string{"types", "domains", "sequences", "functions", "procedures", "tables", "views", "materialized_views", "default_privileges", "privileges"}
119+
orderedDirs := []string{"extensions", "types", "domains", "sequences", "functions", "procedures", "tables", "views", "materialized_views", "default_privileges", "privileges"}
120120

121121
for _, dir := range orderedDirs {
122122
if objects, exists := filesByType[dir]; exists {
@@ -235,6 +235,8 @@ func (f *DumpFormatter) writeObjectFile(filePath string, diffs []diff.Diff) erro
235235
// getObjectDirectory returns the directory name for an object type
236236
func (f *DumpFormatter) getObjectDirectory(objectType string) string {
237237
switch objectType {
238+
case "extension":
239+
return "extensions"
238240
case "type":
239241
return "types"
240242
case "domain":

0 commit comments

Comments
 (0)