diff --git a/pkg/migrations/op_alter_column.go b/pkg/migrations/op_alter_column.go index de81fbc7..d076fccf 100644 --- a/pkg/migrations/op_alter_column.go +++ b/pkg/migrations/op_alter_column.go @@ -5,28 +5,155 @@ package migrations import ( "context" "database/sql" + "fmt" + "github.com/lib/pq" "github.com/xataio/pgroll/pkg/schema" ) var _ Operation = (*OpAlterColumn)(nil) func (o *OpAlterColumn) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { + table := s.GetTable(o.Table) + column := table.GetColumn(o.Column) + op := o.innerOperation() - return op.Start(ctx, conn, stateSchema, tr, s, cbs...) + if _, ok := op.(*OpRenameColumn); !ok { + // Duplicate the column on the underlying table. + d := duplicatorForOperation(o.innerOperation(), conn, table, column) + if err := d.Duplicate(ctx); err != nil { + return nil, fmt.Errorf("failed to duplicate column: %w", err) + } + } + + // perform any operation specific start steps + tbl, err := op.Start(ctx, conn, stateSchema, tr, s, cbs...) + if err != nil { + return nil, err + } + + // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. + // Rename column operations do not require this trigger. + if _, ok := op.(*OpRenameColumn); !ok { + err = createTrigger(ctx, conn, tr, triggerConfig{ + Name: TriggerName(o.Table, o.Column), + Direction: TriggerDirectionUp, + Columns: table.Columns, + SchemaName: s.Name, + TableName: o.Table, + PhysicalColumn: TemporaryName(o.Column), + StateSchema: stateSchema, + SQL: o.upSQLForOperation(op), + }) + if err != nil { + return nil, fmt.Errorf("failed to create up trigger: %w", err) + } + + // Add the new column to the internal schema representation. This is done + // here, before creation of the down trigger, so that the trigger can declare + // a variable for the new column. + table.AddColumn(o.Column, schema.Column{ + Name: TemporaryName(o.Column), + }) + + // Add a trigger to copy values from the new column to the old. + err = createTrigger(ctx, conn, tr, triggerConfig{ + Name: TriggerName(o.Table, TemporaryName(o.Column)), + Direction: TriggerDirectionDown, + Columns: table.Columns, + SchemaName: s.Name, + TableName: o.Table, + PhysicalColumn: o.Column, + StateSchema: stateSchema, + SQL: o.downSQLForOperation(op), + }) + if err != nil { + return nil, fmt.Errorf("failed to create down trigger: %w", err) + } + } + + return tbl, nil } func (o *OpAlterColumn) Complete(ctx context.Context, conn *sql.DB, tr SQLTransformer, s *schema.Schema) error { op := o.innerOperation() - return op.Complete(ctx, conn, tr, s) + // Perform any operation specific completion steps + if err := op.Complete(ctx, conn, tr, s); err != nil { + return err + } + + if _, ok := op.(*OpRenameColumn); !ok { + // Drop the old column + _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", + pq.QuoteIdentifier(o.Table), + pq.QuoteIdentifier(o.Column))) + if err != nil { + return err + } + + // Remove the up function and trigger + _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", + pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) + if err != nil { + return err + } + + // Remove the down function and trigger + _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", + pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) + if err != nil { + return err + } + + // Rename the new column to the old column name + table := s.GetTable(o.Table) + column := table.GetColumn(o.Column) + if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { + return err + } + } + + return nil } func (o *OpAlterColumn) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { op := o.innerOperation() - return op.Rollback(ctx, conn, tr) + // Perform any operation specific rollback steps + if err := op.Rollback(ctx, conn, tr); err != nil { + return err + } + + if _, ok := op.(*OpRenameColumn); !ok { + // Drop the new column + _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", + pq.QuoteIdentifier(o.Table), + pq.QuoteIdentifier(TemporaryName(o.Column)), + )) + if err != nil { + return err + } + + // Remove the up function and trigger + _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", + pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), + )) + if err != nil { + return err + } + + // Remove the down function and trigger + _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", + pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), + )) + if err != nil { + return err + } + } + + return nil } func (o *OpAlterColumn) Validate(ctx context.Context, s *schema.Schema) error { @@ -155,3 +282,45 @@ func (o *OpAlterColumn) numChanges() int { return fieldsSet } + +// duplicatorForOperation returns a Duplicator for the given operation. +func duplicatorForOperation(op Operation, conn *sql.DB, table *schema.Table, column *schema.Column) *Duplicator { + d := NewColumnDuplicator(conn, table, column) + + switch op := (op).(type) { + case *OpDropNotNull: + d = d.WithoutNotNull() + case *OpChangeType: + d = d.WithType(op.Type) + } + return d +} + +// downSQLForOperation returns the down SQL for the given operation, applying +// an appropriate default if none is provided. +func (o *OpAlterColumn) downSQLForOperation(op Operation) string { + if o.Down != "" { + return o.Down + } + + switch (op).(type) { + case *OpSetUnique, *OpSetNotNull: + return pq.QuoteIdentifier(o.Column) + } + + return "" +} + +// upSQLForOperation returns the up SQL for the given operation, applying +// an appropriate default if none is provided. +func (o *OpAlterColumn) upSQLForOperation(op Operation) string { + if o.Up != "" { + return o.Up + } + + if _, ok := op.(*OpDropNotNull); ok { + return pq.QuoteIdentifier(o.Column) + } + + return "" +} diff --git a/pkg/migrations/op_change_type.go b/pkg/migrations/op_change_type.go index b8612dd5..ea2c9038 100644 --- a/pkg/migrations/op_change_type.go +++ b/pkg/migrations/op_change_type.go @@ -5,9 +5,7 @@ package migrations import ( "context" "database/sql" - "fmt" - "github.com/lib/pq" "github.com/xataio/pgroll/pkg/schema" ) @@ -23,111 +21,16 @@ var _ Operation = (*OpChangeType)(nil) func (o *OpChangeType) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // Create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column).WithType(o.Type) - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } - - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.Up, - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old, rewriting values using the `down` SQL. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.Down, - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } return table, nil } func (o *OpChangeType) Complete(ctx context.Context, conn *sql.DB, tr SQLTransformer, s *schema.Schema) error { - // Remove the up function and trigger - _, err := conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) - if err != nil { - return err - } - - // Drop the old column - _, err = conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - return nil } func (o *OpChangeType) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpChangeType) Validate(ctx context.Context, s *schema.Schema) error { diff --git a/pkg/migrations/op_drop_not_null.go b/pkg/migrations/op_drop_not_null.go index b13180d7..3d3b2b38 100644 --- a/pkg/migrations/op_drop_not_null.go +++ b/pkg/migrations/op_drop_not_null.go @@ -5,9 +5,7 @@ package migrations import ( "context" "database/sql" - "fmt" - "github.com/lib/pq" "github.com/xataio/pgroll/pkg/schema" ) @@ -22,111 +20,16 @@ var _ Operation = (*OpDropNotNull)(nil) func (o *OpDropNotNull) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // Create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column).WithoutNotNull() - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } - - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.upSQL(), - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.Down, - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } return table, nil } func (o *OpDropNotNull) Complete(ctx context.Context, conn *sql.DB, tr SQLTransformer, s *schema.Schema) error { - // Drop the old column - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - return nil } func (o *OpDropNotNull) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpDropNotNull) Validate(ctx context.Context, s *schema.Schema) error { @@ -141,12 +44,3 @@ func (o *OpDropNotNull) Validate(ctx context.Context, s *schema.Schema) error { return nil } - -// When removing `NOT NULL` from a column, up SQL is either user-specified or -// defaults to copying the value from the old column to the new. -func (o *OpDropNotNull) upSQL() string { - if o.Up == "" { - return pq.QuoteIdentifier(o.Column) - } - return o.Up -} diff --git a/pkg/migrations/op_set_check.go b/pkg/migrations/op_set_check.go index 2a32623b..36910fd1 100644 --- a/pkg/migrations/op_set_check.go +++ b/pkg/migrations/op_set_check.go @@ -24,55 +24,12 @@ var _ Operation = (*OpSetCheckConstraint)(nil) func (o *OpSetCheckConstraint) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // Create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column) - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } // Add the check constraint to the new column as NOT VALID. if err := o.addCheckConstraint(ctx, conn); err != nil { return nil, fmt.Errorf("failed to add check constraint: %w", err) } - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.Up, - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old, rewriting values using the `down` SQL. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.Down, - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } return table, nil } @@ -85,62 +42,11 @@ func (o *OpSetCheckConstraint) Complete(ctx context.Context, conn *sql.DB, tr SQ return err } - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) - if err != nil { - return err - } - - // Drop the old column - _, err = conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - - return err + return nil } func (o *OpSetCheckConstraint) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column, taking the constraint on the column with it - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpSetCheckConstraint) Validate(ctx context.Context, s *schema.Schema) error { diff --git a/pkg/migrations/op_set_fk.go b/pkg/migrations/op_set_fk.go index aca44ae8..6cbe7115 100644 --- a/pkg/migrations/op_set_fk.go +++ b/pkg/migrations/op_set_fk.go @@ -24,56 +24,12 @@ var _ Operation = (*OpSetForeignKey)(nil) func (o *OpSetForeignKey) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // Create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column) - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } // Create a NOT VALID foreign key constraint on the new column. if err := o.addForeignKeyConstraint(ctx, conn); err != nil { return nil, fmt.Errorf("failed to add foreign key constraint: %w", err) } - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.Up, - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old, rewriting values using the `down` SQL. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.Down, - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } - return table, nil } @@ -86,62 +42,11 @@ func (o *OpSetForeignKey) Complete(ctx context.Context, conn *sql.DB, tr SQLTran return err } - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) - if err != nil { - return err - } - - // Drop the old column - _, err = conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - - return err + return nil } func (o *OpSetForeignKey) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column, taking the constraint on the column with it - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpSetForeignKey) Validate(ctx context.Context, s *schema.Schema) error { diff --git a/pkg/migrations/op_set_notnull.go b/pkg/migrations/op_set_notnull.go index 78b5ec54..066b56ae 100644 --- a/pkg/migrations/op_set_notnull.go +++ b/pkg/migrations/op_set_notnull.go @@ -22,56 +22,12 @@ var _ Operation = (*OpSetNotNull)(nil) func (o *OpSetNotNull) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // Create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column) - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } // Add an unchecked NOT NULL constraint to the new column. if err := addNotNullConstraint(ctx, conn, o.Table, o.Column, TemporaryName(o.Column)); err != nil { return nil, fmt.Errorf("failed to add not null constraint: %w", err) } - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.Up, - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.downSQL(), - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } - return table, nil } @@ -103,62 +59,11 @@ func (o *OpSetNotNull) Complete(ctx context.Context, conn *sql.DB, tr SQLTransfo return err } - // Drop the old column - _, err = conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)))) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - return nil } func (o *OpSetNotNull) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpSetNotNull) Validate(ctx context.Context, s *schema.Schema) error { @@ -174,11 +79,3 @@ func (o *OpSetNotNull) Validate(ctx context.Context, s *schema.Schema) error { return nil } - -// Down SQL is either user-specified or defaults to copying the value from the new column to the old. -func (o *OpSetNotNull) downSQL() string { - if o.Down == "" { - return fmt.Sprintf("NEW.%s", pq.QuoteIdentifier(TemporaryName(o.Column))) - } - return o.Down -} diff --git a/pkg/migrations/op_set_unique.go b/pkg/migrations/op_set_unique.go index eeafd531..a19614ed 100644 --- a/pkg/migrations/op_set_unique.go +++ b/pkg/migrations/op_set_unique.go @@ -23,56 +23,12 @@ var _ Operation = (*OpSetUnique)(nil) func (o *OpSetUnique) Start(ctx context.Context, conn *sql.DB, stateSchema string, tr SQLTransformer, s *schema.Schema, cbs ...CallbackFn) (*schema.Table, error) { table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - - // create a copy of the column on the underlying table. - d := NewColumnDuplicator(conn, table, column) - if err := d.Duplicate(ctx); err != nil { - return nil, fmt.Errorf("failed to duplicate column: %w", err) - } // Add a unique index to the new column if err := o.addUniqueIndex(ctx, conn); err != nil { return nil, fmt.Errorf("failed to add unique index: %w", err) } - // Add a trigger to copy values from the old column to the new, rewriting values using the `up` SQL. - err := createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, o.Column), - Direction: TriggerDirectionUp, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: TemporaryName(o.Column), - StateSchema: stateSchema, - SQL: o.Up, - }) - if err != nil { - return nil, fmt.Errorf("failed to create up trigger: %w", err) - } - - // Add the new column to the internal schema representation. This is done - // here, before creation of the down trigger, so that the trigger can declare - // a variable for the new column. - table.AddColumn(o.Column, schema.Column{ - Name: TemporaryName(o.Column), - }) - - // Add a trigger to copy values from the new column to the old, rewriting values using the `down` SQL. - err = createTrigger(ctx, conn, tr, triggerConfig{ - Name: TriggerName(o.Table, TemporaryName(o.Column)), - Direction: TriggerDirectionDown, - Columns: table.Columns, - SchemaName: s.Name, - TableName: o.Table, - PhysicalColumn: o.Column, - StateSchema: stateSchema, - SQL: o.downSQL(), - }) - if err != nil { - return nil, fmt.Errorf("failed to create down trigger: %w", err) - } - return table, nil } @@ -86,64 +42,11 @@ func (o *OpSetUnique) Complete(ctx context.Context, conn *sql.DB, tr SQLTransfor return err } - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - if err != nil { - return err - } - - // Drop the old column - _, err = conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE IF EXISTS %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(o.Column))) - if err != nil { - return err - } - - // Rename the new column to the old column name - table := s.GetTable(o.Table) - column := table.GetColumn(o.Column) - if err := RenameDuplicatedColumn(ctx, conn, table, column); err != nil { - return err - } - return err } func (o *OpSetUnique) Rollback(ctx context.Context, conn *sql.DB, tr SQLTransformer) error { - // Drop the new column, taking the unique index on the column with it - _, err := conn.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s DROP COLUMN IF EXISTS %s", - pq.QuoteIdentifier(o.Table), - pq.QuoteIdentifier(TemporaryName(o.Column)), - )) - if err != nil { - return err - } - - // Remove the up function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, o.Column)), - )) - if err != nil { - return err - } - - // Remove the down function and trigger - _, err = conn.ExecContext(ctx, fmt.Sprintf("DROP FUNCTION IF EXISTS %s CASCADE", - pq.QuoteIdentifier(TriggerFunctionName(o.Table, TemporaryName(o.Column))), - )) - - return err + return nil } func (o *OpSetUnique) Validate(ctx context.Context, s *schema.Schema) error { @@ -172,12 +75,3 @@ func (o *OpSetUnique) addUniqueIndex(ctx context.Context, conn *sql.DB) error { return err } - -// Down SQL is either user-specified or defaults to copying the value from the new column to the old. -func (o *OpSetUnique) downSQL() string { - if o.Down != "" { - return o.Down - } - - return o.Column -}