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
118 changes: 107 additions & 11 deletions internal/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (
DiffTypeTableIndexComment
DiffTypeView
DiffTypeViewComment
DiffTypeViewIndex
DiffTypeViewIndexComment
DiffTypeFunction
DiffTypeProcedure
DiffTypeSequence
Expand Down Expand Up @@ -60,6 +62,10 @@ func (d DiffType) String() string {
return "view"
case DiffTypeViewComment:
return "view.comment"
case DiffTypeViewIndex:
return "view.index"
case DiffTypeViewIndexComment:
return "view.index.comment"
case DiffTypeFunction:
return "function"
case DiffTypeProcedure:
Expand Down Expand Up @@ -114,6 +120,10 @@ func (d *DiffType) UnmarshalJSON(data []byte) error {
*d = DiffTypeView
case "view.comment":
*d = DiffTypeViewComment
case "view.index":
*d = DiffTypeViewIndex
case "view.index.comment":
*d = DiffTypeViewIndexComment
case "function":
*d = DiffTypeFunction
case "procedure":
Expand Down Expand Up @@ -262,11 +272,15 @@ type triggerDiff struct {

// viewDiff represents changes to a view
type viewDiff struct {
Old *ir.View
New *ir.View
CommentChanged bool
OldComment string
NewComment string
Old *ir.View
New *ir.View
CommentChanged bool
OldComment string
NewComment string
AddedIndexes []*ir.Index // For materialized views
DroppedIndexes []*ir.Index // For materialized views
ModifiedIndexes []*IndexDiff // For materialized views
RequiresRecreate bool // For materialized views with structural changes that require DROP + CREATE
}

// tableDiff represents changes to a table
Expand Down Expand Up @@ -676,13 +690,53 @@ func GenerateMigration(oldIR, newIR *ir.IR, targetSchema string) []Diff {
structurallyDifferent := !viewsEqual(oldView, newView)
commentChanged := oldView.Comment != newView.Comment

if structurallyDifferent || commentChanged {
// For materialized views with structural changes, use DROP + CREATE approach
// Check if indexes changed for materialized views
indexesChanged := false
if newView.Materialized {
oldIndexCount := 0
newIndexCount := 0
if oldView.Indexes != nil {
oldIndexCount = len(oldView.Indexes)
}
if newView.Indexes != nil {
newIndexCount = len(newView.Indexes)
}
indexesChanged = oldIndexCount != newIndexCount

// If counts are same, check if any indexes are different (added/removed/modified)
if !indexesChanged && oldIndexCount > 0 {
// Check for added or removed indexes
for indexName := range newView.Indexes {
if _, exists := oldView.Indexes[indexName]; !exists {
indexesChanged = true
break
}
}

// Check for modified indexes (structure or comments)
if !indexesChanged {
for indexName, newIndex := range newView.Indexes {
if oldIndex, exists := oldView.Indexes[indexName]; exists {
structurallyEqual := indexesStructurallyEqual(oldIndex, newIndex)
commentChanged := oldIndex.Comment != newIndex.Comment
if !structurallyEqual || commentChanged {
indexesChanged = true
break
}
}
}
}
}
}

if structurallyDifferent || commentChanged || indexesChanged {
// For materialized views with structural changes, mark for recreation
if newView.Materialized && structurallyDifferent {
// Add old materialized view to dropped views
diff.droppedViews = append(diff.droppedViews, oldView)
// Add new materialized view to added views
diff.addedViews = append(diff.addedViews, newView)
diff.modifiedViews = append(diff.modifiedViews, &viewDiff{
Old: oldView,
New: newView,
RequiresRecreate: true,
})
} else {
// For regular views or comment-only changes, use the modify approach
viewDiff := &viewDiff{
Expand All @@ -697,6 +751,48 @@ func GenerateMigration(oldIR, newIR *ir.IR, targetSchema string) []Diff {
viewDiff.NewComment = newView.Comment
}

// For materialized views, also diff indexes
if newView.Materialized {
oldIndexes := oldView.Indexes
newIndexes := newView.Indexes
if oldIndexes == nil {
oldIndexes = make(map[string]*ir.Index)
}
if newIndexes == nil {
newIndexes = make(map[string]*ir.Index)
}

// Find added indexes
for indexName, index := range newIndexes {
if _, exists := oldIndexes[indexName]; !exists {
viewDiff.AddedIndexes = append(viewDiff.AddedIndexes, index)
}
}

// Find dropped indexes
for indexName, index := range oldIndexes {
if _, exists := newIndexes[indexName]; !exists {
viewDiff.DroppedIndexes = append(viewDiff.DroppedIndexes, index)
}
}

// Find modified indexes
for indexName, newIndex := range newIndexes {
if oldIndex, exists := oldIndexes[indexName]; exists {
structurallyEqual := indexesStructurallyEqual(oldIndex, newIndex)
commentChanged := oldIndex.Comment != newIndex.Comment

// If either structure changed or comment changed, treat as modification
if !structurallyEqual || commentChanged {
viewDiff.ModifiedIndexes = append(viewDiff.ModifiedIndexes, &IndexDiff{
Old: oldIndex,
New: newIndex,
})
}
}
}
}

diff.modifiedViews = append(diff.modifiedViews, viewDiff)
}
}
Expand Down
16 changes: 13 additions & 3 deletions internal/diff/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,18 @@ import (
"github.com/pgschema/pgschema/ir"
)

// generateCreateIndexesSQL generates CREATE INDEX statements
// generateCreateIndexesSQL generates CREATE INDEX statements for table indexes
func generateCreateIndexesSQL(indexes []*ir.Index, targetSchema string, collector *diffCollector) {
generateCreateIndexesSQLWithType(indexes, targetSchema, collector, DiffTypeTableIndex, DiffTypeTableIndexComment)
}

// generateCreateViewIndexesSQL generates CREATE INDEX statements for materialized view indexes
func generateCreateViewIndexesSQL(indexes []*ir.Index, targetSchema string, collector *diffCollector) {
generateCreateIndexesSQLWithType(indexes, targetSchema, collector, DiffTypeViewIndex, DiffTypeViewIndexComment)
}

// generateCreateIndexesSQLWithType generates CREATE INDEX statements with specified diff types
func generateCreateIndexesSQLWithType(indexes []*ir.Index, targetSchema string, collector *diffCollector, indexType DiffType, commentType DiffType) {
// Sort indexes by name for consistent ordering
sortedIndexes := make([]*ir.Index, len(indexes))
copy(sortedIndexes, indexes)
Expand All @@ -27,7 +37,7 @@ func generateCreateIndexesSQL(indexes []*ir.Index, targetSchema string, collecto

// Create context for this statement
context := &diffContext{
Type: DiffTypeTableIndex,
Type: indexType,
Operation: DiffOperationCreate,
Path: fmt.Sprintf("%s.%s.%s", index.Schema, index.Table, index.Name),
Source: index,
Expand All @@ -43,7 +53,7 @@ func generateCreateIndexesSQL(indexes []*ir.Index, targetSchema string, collecto

// Create context for this statement
context := &diffContext{
Type: DiffTypeTableIndexComment,
Type: commentType,
Operation: DiffOperationCreate,
Path: fmt.Sprintf("%s.%s.%s", index.Schema, index.Table, index.Name),
Source: index,
Expand Down
Loading