diff --git a/internal/diff/column.go b/internal/diff/column.go index 1b769767..4954426e 100644 --- a/internal/diff/column.go +++ b/internal/diff/column.go @@ -138,6 +138,72 @@ func needsUsingClause(oldType, newType string) bool { return false } +// columnsMatchForRename checks if two columns are the same column with a different name. +// Both position AND all properties (except name and comment) must match for a rename to be detected. +func columnsMatchForRename(old, new *ir.Column, targetSchema string) bool { + // Names must differ (otherwise it's not a rename) + if old.Name == new.Name { + return false + } + + // Position must match + if old.Position != new.Position { + return false + } + + // Data type must match (normalize schema prefix) + oldType := stripSchemaPrefix(old.DataType, targetSchema) + newType := stripSchemaPrefix(new.DataType, targetSchema) + if oldType != newType { + return false + } + + // Nullability must match + if old.IsNullable != new.IsNullable { + return false + } + + // Default values must match + if (old.DefaultValue == nil) != (new.DefaultValue == nil) { + return false + } + if old.DefaultValue != nil && new.DefaultValue != nil && *old.DefaultValue != *new.DefaultValue { + return false + } + + // Max length must match + if (old.MaxLength == nil) != (new.MaxLength == nil) { + return false + } + if old.MaxLength != nil && new.MaxLength != nil && *old.MaxLength != *new.MaxLength { + return false + } + + // Identity must match + if (old.Identity == nil) != (new.Identity == nil) { + return false + } + if old.Identity != nil && new.Identity != nil { + if old.Identity.Generation != new.Identity.Generation { + return false + } + } + + // Generated column must match + if old.IsGenerated != new.IsGenerated { + return false + } + if (old.GeneratedExpr == nil) != (new.GeneratedExpr == nil) { + return false + } + if old.GeneratedExpr != nil && new.GeneratedExpr != nil && *old.GeneratedExpr != *new.GeneratedExpr { + return false + } + + // Comment changes are OK — rename still applies, comment change emitted separately + return true +} + // columnsEqual compares two columns for equality // targetSchema is used to normalize type names before comparison func columnsEqual(old, new *ir.Column, targetSchema string) bool { diff --git a/internal/diff/column_test.go b/internal/diff/column_test.go new file mode 100644 index 00000000..2e91403a --- /dev/null +++ b/internal/diff/column_test.go @@ -0,0 +1,84 @@ +package diff + +import ( + "testing" + + "github.com/pgplex/pgschema/ir" +) + +func TestColumnsMatchForRename(t *testing.T) { + boolDefault := "false" + + tests := []struct { + name string + old *ir.Column + new *ir.Column + schema string + expected bool + }{ + { + name: "same type and position, different name = match", + old: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + new: &ir.Column{Name: "active_n", Position: 3, DataType: "boolean", IsNullable: false}, + expected: true, + }, + { + name: "different type = no match", + old: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + new: &ir.Column{Name: "active_n", Position: 3, DataType: "integer", IsNullable: false}, + expected: false, + }, + { + name: "different position = no match", + old: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + new: &ir.Column{Name: "active_n", Position: 4, DataType: "boolean", IsNullable: false}, + expected: false, + }, + { + name: "different nullability = no match", + old: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + new: &ir.Column{Name: "active_n", Position: 3, DataType: "boolean", IsNullable: true}, + expected: false, + }, + { + name: "same name = no match (not a rename)", + old: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + new: &ir.Column{Name: "active", Position: 3, DataType: "boolean", IsNullable: false}, + expected: false, + }, + { + name: "with matching defaults = match", + old: &ir.Column{Name: "old_col", Position: 2, DataType: "boolean", DefaultValue: &boolDefault}, + new: &ir.Column{Name: "new_col", Position: 2, DataType: "boolean", DefaultValue: &boolDefault}, + expected: true, + }, + { + name: "with matching identity = match", + old: &ir.Column{Name: "old_id", Position: 1, DataType: "integer", Identity: &ir.Identity{Generation: "ALWAYS"}}, + new: &ir.Column{Name: "new_id", Position: 1, DataType: "integer", Identity: &ir.Identity{Generation: "ALWAYS"}}, + expected: true, + }, + { + name: "different identity generation = no match", + old: &ir.Column{Name: "old_id", Position: 1, DataType: "integer", Identity: &ir.Identity{Generation: "ALWAYS"}}, + new: &ir.Column{Name: "new_id", Position: 1, DataType: "integer", Identity: &ir.Identity{Generation: "BY DEFAULT"}}, + expected: false, + }, + { + name: "schema-qualified type normalized = match", + old: &ir.Column{Name: "old_col", Position: 1, DataType: "public.my_type"}, + new: &ir.Column{Name: "new_col", Position: 1, DataType: "my_type"}, + schema: "public", + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := columnsMatchForRename(tt.old, tt.new, tt.schema) + if result != tt.expected { + t.Errorf("columnsMatchForRename() = %v, want %v", result, tt.expected) + } + }) + } +} diff --git a/internal/diff/constraint.go b/internal/diff/constraint.go index 4846d624..96473ada 100644 --- a/internal/diff/constraint.go +++ b/internal/diff/constraint.go @@ -223,3 +223,34 @@ func constraintsEqual(old, new *ir.Constraint) bool { return true } + +// applyRenameMapToConstraint returns a shallow copy of the constraint with +// column names updated according to the rename map. This is used to compare +// constraints after column renames — PostgreSQL automatically updates constraint +// column references when a column is renamed. +func applyRenameMapToConstraint(c *ir.Constraint, renameMap map[string]string) *ir.Constraint { + needsUpdate := false + for _, col := range c.Columns { + if _, ok := renameMap[col.Name]; ok { + needsUpdate = true + break + } + } + if !needsUpdate { + return c + } + + // Shallow copy the constraint and update column names + copy := *c + copy.Columns = make([]*ir.ConstraintColumn, len(c.Columns)) + for i, col := range c.Columns { + if newName, ok := renameMap[col.Name]; ok { + colCopy := *col + colCopy.Name = newName + copy.Columns[i] = &colCopy + } else { + copy.Columns[i] = col + } + } + return © +} diff --git a/internal/diff/diff.go b/internal/diff/diff.go index 7870cba6..983e68bf 100644 --- a/internal/diff/diff.go +++ b/internal/diff/diff.go @@ -25,6 +25,7 @@ const ( DiffTypeTableColumnComment DiffTypeTableIndexComment DiffTypeTablePersistence + DiffTypeTableColumnRename DiffTypeView DiffTypeViewTrigger DiffTypeViewComment @@ -69,6 +70,8 @@ func (d DiffType) String() string { return "table.index.comment" case DiffTypeTablePersistence: return "table.persistence" + case DiffTypeTableColumnRename: + return "table.column_rename" case DiffTypeView: return "view" case DiffTypeViewTrigger: @@ -143,6 +146,8 @@ func (d *DiffType) UnmarshalJSON(data []byte) error { *d = DiffTypeTableIndexComment case "table.persistence": *d = DiffTypeTablePersistence + case "table.column_rename": + *d = DiffTypeTableColumnRename case "view": *d = DiffTypeView case "view.trigger": @@ -375,6 +380,7 @@ type tableDiff struct { AddedColumns []*ir.Column DroppedColumns []*ir.Column ModifiedColumns []*ColumnDiff + RenamedColumns []*ColumnRename AddedConstraints []*ir.Constraint DroppedConstraints []*ir.Constraint ModifiedConstraints []*ConstraintDiff @@ -400,6 +406,12 @@ type ColumnDiff struct { New *ir.Column } +// ColumnRename represents a column that was renamed +type ColumnRename struct { + Old *ir.Column + New *ir.Column +} + // ConstraintDiff represents changes to a constraint type ConstraintDiff struct { Old *ir.Constraint @@ -1857,6 +1869,10 @@ func sortTableObjects(tables []*tableDiff) { sort.Slice(tableDiff.ModifiedColumns, func(i, j int) bool { return tableDiff.ModifiedColumns[i].New.Position < tableDiff.ModifiedColumns[j].New.Position }) + + sort.Slice(tableDiff.RenamedColumns, func(i, j int) bool { + return tableDiff.RenamedColumns[i].Old.Position < tableDiff.RenamedColumns[j].Old.Position + }) } } @@ -2166,6 +2182,7 @@ func (d *triggerDiff) GetObjectName() string { return d.New.Name } func (d *viewDiff) GetObjectName() string { return d.New.Name } func (d *tableDiff) GetObjectName() string { return d.Table.Name } func (d *ColumnDiff) GetObjectName() string { return d.New.Name } +func (d *ColumnRename) GetObjectName() string { return d.New.Name } func (d *ConstraintDiff) GetObjectName() string { return d.New.Name } func (d *IndexDiff) GetObjectName() string { return d.New.Name } func (d *policyDiff) GetObjectName() string { return d.New.Name } diff --git a/internal/diff/table.go b/internal/diff/table.go index cb4d6d94..b6a388b3 100644 --- a/internal/diff/table.go +++ b/internal/diff/table.go @@ -111,6 +111,50 @@ func diffTriggers(oldTable, newTable *ir.Table, diff *tableDiff) { } } +// detectColumnRenames finds column pairs that were renamed (same position + properties, different name). +// Returns the detected renames and the remaining added/dropped columns that are not renames. +func detectColumnRenames(added, dropped []*ir.Column, targetSchema string) ( + renames []*ColumnRename, + remainingAdded []*ir.Column, + remainingDropped []*ir.Column, +) { + // Build a map of dropped columns keyed by position + droppedByPosition := make(map[int]*ir.Column) + for _, col := range dropped { + droppedByPosition[col.Position] = col + } + + matchedDropped := make(map[string]bool) + matchedAdded := make(map[string]bool) + + for _, addedCol := range added { + if droppedCol, exists := droppedByPosition[addedCol.Position]; exists { + if !matchedDropped[droppedCol.Name] && columnsMatchForRename(droppedCol, addedCol, targetSchema) { + renames = append(renames, &ColumnRename{ + Old: droppedCol, + New: addedCol, + }) + matchedDropped[droppedCol.Name] = true + matchedAdded[addedCol.Name] = true + } + } + } + + // Build remaining lists + for _, col := range added { + if !matchedAdded[col.Name] { + remainingAdded = append(remainingAdded, col) + } + } + for _, col := range dropped { + if !matchedDropped[col.Name] { + remainingDropped = append(remainingDropped, col) + } + } + + return renames, remainingAdded, remainingDropped +} + // diffTables compares two tables and returns the differences // targetSchema is used to normalize type names before comparison func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { @@ -119,6 +163,7 @@ func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { AddedColumns: []*ir.Column{}, DroppedColumns: []*ir.Column{}, ModifiedColumns: []*ColumnDiff{}, + RenamedColumns: []*ColumnRename{}, AddedConstraints: []*ir.Constraint{}, DroppedConstraints: []*ir.Constraint{}, ModifiedConstraints: []*ConstraintDiff{}, @@ -160,6 +205,14 @@ func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { } } + // Detect column renames from added/dropped pairs + renames, remainingAdded, remainingDropped := detectColumnRenames(diff.AddedColumns, diff.DroppedColumns, targetSchema) + if len(renames) > 0 { + diff.RenamedColumns = renames + diff.AddedColumns = remainingAdded + diff.DroppedColumns = remainingDropped + } + // Find modified columns for name, newColumn := range newColumns { if oldColumn, exists := oldColumns[name]; exists { @@ -172,6 +225,12 @@ func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { } } + // Build rename map (old name -> new name) for constraint comparison + renameMap := make(map[string]string, len(diff.RenamedColumns)) + for _, rename := range diff.RenamedColumns { + renameMap[rename.Old.Name] = rename.New.Name + } + // Compare constraints oldConstraints := make(map[string]*ir.Constraint) newConstraints := make(map[string]*ir.Constraint) @@ -205,7 +264,14 @@ func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { // Find modified constraints for name, newConstraint := range newConstraints { if oldConstraint, exists := oldConstraints[name]; exists { - if !constraintsEqual(oldConstraint, newConstraint) { + // When columns are renamed, apply the rename map to the old constraint's + // column names before comparing, since PostgreSQL automatically updates + // constraint column references when a column is renamed. + oldForComparison := oldConstraint + if len(renameMap) > 0 { + oldForComparison = applyRenameMapToConstraint(oldConstraint, renameMap) + } + if !constraintsEqual(oldForComparison, newConstraint) { diff.ModifiedConstraints = append(diff.ModifiedConstraints, &ConstraintDiff{ Old: oldConstraint, New: newConstraint, @@ -335,7 +401,8 @@ func diffTables(oldTable, newTable *ir.Table, targetSchema string) *tableDiff { // Return nil if no changes if len(diff.AddedColumns) == 0 && len(diff.DroppedColumns) == 0 && - len(diff.ModifiedColumns) == 0 && len(diff.AddedConstraints) == 0 && + len(diff.ModifiedColumns) == 0 && len(diff.RenamedColumns) == 0 && + len(diff.AddedConstraints) == 0 && len(diff.DroppedConstraints) == 0 && len(diff.ModifiedConstraints) == 0 && len(diff.AddedIndexes) == 0 && len(diff.DroppedIndexes) == 0 && len(diff.ModifiedIndexes) == 0 && len(diff.AddedTriggers) == 0 && @@ -357,6 +424,7 @@ func diffExternalTable(oldTable, newTable *ir.Table) *tableDiff { AddedColumns: []*ir.Column{}, DroppedColumns: []*ir.Column{}, ModifiedColumns: []*ColumnDiff{}, + RenamedColumns: []*ColumnRename{}, AddedConstraints: []*ir.Constraint{}, DroppedConstraints: []*ir.Constraint{}, ModifiedConstraints: []*ConstraintDiff{}, @@ -718,6 +786,22 @@ func (td *tableDiff) generateAlterTableStatements(targetSchema string, collector collector.collect(context, sql) } + // Rename columns before any drops/adds to preserve data + for _, rename := range td.RenamedColumns { + tableName := getTableNameWithSchema(td.Table.Schema, td.Table.Name, targetSchema) + sql := fmt.Sprintf("ALTER TABLE %s RENAME COLUMN %s TO %s;", + tableName, ir.QuoteIdentifier(rename.Old.Name), ir.QuoteIdentifier(rename.New.Name)) + + context := &diffContext{ + Type: DiffTypeTableColumnRename, + Operation: DiffOperationAlter, + Path: fmt.Sprintf("%s.%s.%s", td.Table.Schema, td.Table.Name, rename.New.Name), + Source: rename, + CanRunInTransaction: true, + } + collector.collect(context, sql) + } + // Drop constraints first (before dropping columns) - already sorted by the Diff operation for _, constraint := range td.DroppedConstraints { // Skip constraints already removed by a dropped column. (#384) @@ -1323,6 +1407,28 @@ func (td *tableDiff) generateAlterTableStatements(targetSchema string, collector } } + // Handle comment changes on renamed columns + for _, rename := range td.RenamedColumns { + if rename.Old.Comment != rename.New.Comment { + tableName := getTableNameWithSchema(td.Table.Schema, td.Table.Name, targetSchema) + var sql string + if rename.New.Comment == "" { + sql = fmt.Sprintf("COMMENT ON COLUMN %s.%s IS NULL;", tableName, ir.QuoteIdentifier(rename.New.Name)) + } else { + sql = fmt.Sprintf("COMMENT ON COLUMN %s.%s IS %s;", tableName, ir.QuoteIdentifier(rename.New.Name), quoteString(rename.New.Comment)) + } + + context := &diffContext{ + Type: DiffTypeTableColumnComment, + Operation: DiffOperationAlter, + Path: fmt.Sprintf("%s.%s.%s", td.Table.Schema, td.Table.Name, rename.New.Name), + Source: rename, + CanRunInTransaction: true, + } + collector.collect(context, sql) + } + } + // Handle index modifications using shared function generateIndexModifications( td.DroppedIndexes, diff --git a/testdata/diff/create_table/issue_384_rename_column_constraint/diff.sql b/testdata/diff/create_table/issue_384_rename_column_constraint/diff.sql index e072decb..71f5d700 100644 --- a/testdata/diff/create_table/issue_384_rename_column_constraint/diff.sql +++ b/testdata/diff/create_table/issue_384_rename_column_constraint/diff.sql @@ -1,6 +1 @@ -ALTER TABLE a DROP COLUMN revision; - -ALTER TABLE a ADD COLUMN current_revision bigint; - -ALTER TABLE a -ADD CONSTRAINT a_revision_fkey FOREIGN KEY (current_revision) REFERENCES b (id); +ALTER TABLE a RENAME COLUMN revision TO current_revision; diff --git a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.json b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.json index dd166540..da098dc2 100644 --- a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.json +++ b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.json @@ -9,28 +9,10 @@ { "steps": [ { - "sql": "ALTER TABLE a DROP COLUMN revision;", - "type": "table.column", - "operation": "drop", - "path": "public.a.revision" - }, - { - "sql": "ALTER TABLE a ADD COLUMN current_revision bigint;", - "type": "table.column", - "operation": "create", + "sql": "ALTER TABLE a RENAME COLUMN revision TO current_revision;", + "type": "table.column_rename", + "operation": "alter", "path": "public.a.current_revision" - }, - { - "sql": "ALTER TABLE a\nADD CONSTRAINT a_revision_fkey FOREIGN KEY (current_revision) REFERENCES b (id) NOT VALID;", - "type": "table.constraint", - "operation": "create", - "path": "public.a.a_revision_fkey" - }, - { - "sql": "ALTER TABLE a VALIDATE CONSTRAINT a_revision_fkey;", - "type": "table.constraint", - "operation": "create", - "path": "public.a.a_revision_fkey" } ] } diff --git a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.sql b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.sql index f81300a1..71f5d700 100644 --- a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.sql +++ b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.sql @@ -1,8 +1 @@ -ALTER TABLE a DROP COLUMN revision; - -ALTER TABLE a ADD COLUMN current_revision bigint; - -ALTER TABLE a -ADD CONSTRAINT a_revision_fkey FOREIGN KEY (current_revision) REFERENCES b (id) NOT VALID; - -ALTER TABLE a VALIDATE CONSTRAINT a_revision_fkey; +ALTER TABLE a RENAME COLUMN revision TO current_revision; diff --git a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.txt b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.txt index 753a60a9..191fed85 100644 --- a/testdata/diff/create_table/issue_384_rename_column_constraint/plan.txt +++ b/testdata/diff/create_table/issue_384_rename_column_constraint/plan.txt @@ -5,18 +5,9 @@ Summary by type: Tables: ~ a - + current_revision (column) - - revision (column) - + a_revision_fkey (constraint) + ~ current_revision (column_rename) DDL to be executed: -------------------------------------------------- -ALTER TABLE a DROP COLUMN revision; - -ALTER TABLE a ADD COLUMN current_revision bigint; - -ALTER TABLE a -ADD CONSTRAINT a_revision_fkey FOREIGN KEY (current_revision) REFERENCES b (id) NOT VALID; - -ALTER TABLE a VALIDATE CONSTRAINT a_revision_fkey; +ALTER TABLE a RENAME COLUMN revision TO current_revision; diff --git a/testdata/diff/create_table/rename_column/diff.sql b/testdata/diff/create_table/rename_column/diff.sql new file mode 100644 index 00000000..86e91075 --- /dev/null +++ b/testdata/diff/create_table/rename_column/diff.sql @@ -0,0 +1 @@ +ALTER TABLE users RENAME COLUMN active TO is_active; diff --git a/testdata/diff/create_table/rename_column/new.sql b/testdata/diff/create_table/rename_column/new.sql new file mode 100644 index 00000000..7dc7cce3 --- /dev/null +++ b/testdata/diff/create_table/rename_column/new.sql @@ -0,0 +1,4 @@ +CREATE TABLE public.users ( + id integer NOT NULL, + is_active boolean NOT NULL +); diff --git a/testdata/diff/create_table/rename_column/old.sql b/testdata/diff/create_table/rename_column/old.sql new file mode 100644 index 00000000..972037b0 --- /dev/null +++ b/testdata/diff/create_table/rename_column/old.sql @@ -0,0 +1,4 @@ +CREATE TABLE public.users ( + id integer NOT NULL, + active boolean NOT NULL +); diff --git a/testdata/diff/create_table/rename_column/plan.json b/testdata/diff/create_table/rename_column/plan.json new file mode 100644 index 00000000..78a0d4ec --- /dev/null +++ b/testdata/diff/create_table/rename_column/plan.json @@ -0,0 +1,20 @@ +{ + "version": "1.0.0", + "pgschema_version": "1.9.0", + "created_at": "1970-01-01T00:00:00Z", + "source_fingerprint": { + "hash": "2269947753a914ccaf56d792f2240cbe65cb6aea022eab92bb484aff584e22f4" + }, + "groups": [ + { + "steps": [ + { + "sql": "ALTER TABLE users RENAME COLUMN active TO is_active;", + "type": "table.column_rename", + "operation": "alter", + "path": "public.users.is_active" + } + ] + } + ] +} diff --git a/testdata/diff/create_table/rename_column/plan.sql b/testdata/diff/create_table/rename_column/plan.sql new file mode 100644 index 00000000..86e91075 --- /dev/null +++ b/testdata/diff/create_table/rename_column/plan.sql @@ -0,0 +1 @@ +ALTER TABLE users RENAME COLUMN active TO is_active; diff --git a/testdata/diff/create_table/rename_column/plan.txt b/testdata/diff/create_table/rename_column/plan.txt new file mode 100644 index 00000000..a4f20911 --- /dev/null +++ b/testdata/diff/create_table/rename_column/plan.txt @@ -0,0 +1,13 @@ +Plan: 1 to modify. + +Summary by type: + tables: 1 to modify + +Tables: + ~ users + ~ is_active (column_rename) + +DDL to be executed: +-------------------------------------------------- + +ALTER TABLE users RENAME COLUMN active TO is_active; diff --git a/testdata/diff/create_table/rename_column_type_change/diff.sql b/testdata/diff/create_table/rename_column_type_change/diff.sql new file mode 100644 index 00000000..2da349d2 --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/diff.sql @@ -0,0 +1,3 @@ +ALTER TABLE orders DROP COLUMN status; + +ALTER TABLE orders ADD COLUMN order_status integer NOT NULL; diff --git a/testdata/diff/create_table/rename_column_type_change/new.sql b/testdata/diff/create_table/rename_column_type_change/new.sql new file mode 100644 index 00000000..48707990 --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/new.sql @@ -0,0 +1,4 @@ +CREATE TABLE public.orders ( + id integer NOT NULL, + order_status integer NOT NULL +); diff --git a/testdata/diff/create_table/rename_column_type_change/old.sql b/testdata/diff/create_table/rename_column_type_change/old.sql new file mode 100644 index 00000000..7b03192d --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/old.sql @@ -0,0 +1,4 @@ +CREATE TABLE public.orders ( + id integer NOT NULL, + status text NOT NULL +); diff --git a/testdata/diff/create_table/rename_column_type_change/plan.json b/testdata/diff/create_table/rename_column_type_change/plan.json new file mode 100644 index 00000000..fbfca484 --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/plan.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0", + "pgschema_version": "1.9.0", + "created_at": "1970-01-01T00:00:00Z", + "source_fingerprint": { + "hash": "257331bc006ad99a91cdba3a4c7fe7a0cedd09b17fb127ed74d1b4892f93463b" + }, + "groups": [ + { + "steps": [ + { + "sql": "ALTER TABLE orders DROP COLUMN status;", + "type": "table.column", + "operation": "drop", + "path": "public.orders.status" + }, + { + "sql": "ALTER TABLE orders ADD COLUMN order_status integer NOT NULL;", + "type": "table.column", + "operation": "create", + "path": "public.orders.order_status" + } + ] + } + ] +} diff --git a/testdata/diff/create_table/rename_column_type_change/plan.sql b/testdata/diff/create_table/rename_column_type_change/plan.sql new file mode 100644 index 00000000..2da349d2 --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/plan.sql @@ -0,0 +1,3 @@ +ALTER TABLE orders DROP COLUMN status; + +ALTER TABLE orders ADD COLUMN order_status integer NOT NULL; diff --git a/testdata/diff/create_table/rename_column_type_change/plan.txt b/testdata/diff/create_table/rename_column_type_change/plan.txt new file mode 100644 index 00000000..15b1a558 --- /dev/null +++ b/testdata/diff/create_table/rename_column_type_change/plan.txt @@ -0,0 +1,16 @@ +Plan: 1 to modify. + +Summary by type: + tables: 1 to modify + +Tables: + ~ orders + + order_status (column) + - status (column) + +DDL to be executed: +-------------------------------------------------- + +ALTER TABLE orders DROP COLUMN status; + +ALTER TABLE orders ADD COLUMN order_status integer NOT NULL;