Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to disable version schemas #233

Merged
merged 2 commits into from
Jan 15, 2024
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
35 changes: 22 additions & 13 deletions pkg/roll/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ func (m *Roll) Start(ctx context.Context, migration *migrations.Migration, cbs .
}
}

if m.disableVersionSchemas {
// skip creating version schemas
return nil
}

// create schema for the new version
versionSchema := VersionedSchemaName(m.schema, migration.Name)
_, err = m.pgConn.ExecContext(ctx, fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", pq.QuoteIdentifier(versionSchema)))
Expand Down Expand Up @@ -86,15 +91,17 @@ func (m *Roll) Complete(ctx context.Context) error {
}

// Drop the old schema
prevVersion, err := m.state.PreviousVersion(ctx, m.schema)
if err != nil {
return fmt.Errorf("unable to get name of previous version: %w", err)
}
if prevVersion != nil {
versionSchema := VersionedSchemaName(m.schema, *prevVersion)
_, err = m.pgConn.ExecContext(ctx, fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(versionSchema)))
if !m.disableVersionSchemas {
prevVersion, err := m.state.PreviousVersion(ctx, m.schema)
if err != nil {
return fmt.Errorf("unable to drop previous version: %w", err)
return fmt.Errorf("unable to get name of previous version: %w", err)
}
if prevVersion != nil {
versionSchema := VersionedSchemaName(m.schema, *prevVersion)
_, err = m.pgConn.ExecContext(ctx, fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(versionSchema)))
if err != nil {
return fmt.Errorf("unable to drop previous version: %w", err)
}
}
}

Expand Down Expand Up @@ -122,11 +129,13 @@ func (m *Roll) Rollback(ctx context.Context) error {
return fmt.Errorf("unable to get active migration: %w", err)
}

// delete the schema and view for the new version
versionSchema := VersionedSchemaName(m.schema, migration.Name)
_, err = m.pgConn.ExecContext(ctx, fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(versionSchema)))
if err != nil {
return err
if !m.disableVersionSchemas {
// delete the schema and view for the new version
versionSchema := VersionedSchemaName(m.schema, migration.Name)
_, err = m.pgConn.ExecContext(ctx, fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", pq.QuoteIdentifier(versionSchema)))
if err != nil {
return err
}
}

// execute operations
Expand Down
116 changes: 57 additions & 59 deletions pkg/roll/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestMain(m *testing.M) {
testutils.SharedTestMain(m)
}

func TestSchemaIsCreatedfterMigrationStart(t *testing.T) {
func TestSchemaIsCreatedAfterMigrationStart(t *testing.T) {
t.Parallel()

testutils.WithMigratorAndConnectionToContainer(t, func(mig *roll.Roll, db *sql.DB) {
Expand All @@ -39,23 +39,64 @@ func TestSchemaIsCreatedfterMigrationStart(t *testing.T) {
//
// Check that the schema exists
//
var exists bool
err := db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, roll.VersionedSchemaName(schema, version)).Scan(&exists)
if err != nil {
t.Fatal(err)
if !schemaExists(t, db, roll.VersionedSchemaName(schema, version)) {
t.Errorf("Expected schema %q to exist", version)
}
})
}

if !exists {
t.Errorf("Expected schema %q to exist", version)
func TestDisabledSchemaManagement(t *testing.T) {
t.Parallel()

testutils.WithMigratorInSchemaAndConnectionToContainerWithOptions(t, "public", []roll.Option{roll.WithDisableViewsManagement()}, func(mig *roll.Roll, db *sql.DB) {
ctx := context.Background()
version := "1_create_table"

if err := mig.Start(ctx, &migrations.Migration{Name: version, Operations: migrations.Operations{createTableOp("table1")}}); err != nil {
t.Fatalf("Failed to start migration: %v", err)
}

//
// Check that the schema doesn't get created
//
if schemaExists(t, db, roll.VersionedSchemaName(schema, version)) {
t.Errorf("Expected schema %q to not exist", version)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth testing the behaviour on rollback here too? Rolling back the migration shouldn't try to delete the version schema if it wasn't created, so attempting a rollback and asserting no error here might be worth adding.

if err := mig.Rollback(ctx); err != nil {
t.Fatalf("Failed to rollback migration: %v", err)
}

if err := mig.Start(ctx, &migrations.Migration{Name: version, Operations: migrations.Operations{createTableOp("table1")}}); err != nil {
t.Fatalf("Failed to start migration again: %v", err)
}

// complete the migration, check that the schema still doesn't exist
if err := mig.Complete(ctx); err != nil {
t.Fatalf("Failed to complete migration: %v", err)
}

if schemaExists(t, db, roll.VersionedSchemaName(schema, version)) {
t.Errorf("Expected schema %q to not exist", version)
}
})
}

func schemaExists(t *testing.T, db *sql.DB, schema string) bool {
t.Helper()
var exists bool
err := db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, schema).Scan(&exists)
if err != nil {
t.Fatal(err)
}
return exists
}

func TestPreviousVersionIsDroppedAfterMigrationCompletion(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -83,18 +124,7 @@ func TestPreviousVersionIsDroppedAfterMigrationCompletion(t *testing.T) {
//
// Check that the schema for the first version has been dropped
//
var exists bool
err := db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, roll.VersionedSchemaName(schema, firstVersion)).Scan(&exists)
if err != nil {
t.Fatal(err)
}

if exists {
if schemaExists(t, db, roll.VersionedSchemaName(schema, firstVersion)) {
t.Errorf("Expected schema %q to not exist", firstVersion)
}
})
Expand Down Expand Up @@ -133,18 +163,7 @@ func TestPreviousVersionIsDroppedAfterMigrationCompletion(t *testing.T) {
//
// Check that the schema for the first version has been dropped
//
var exists bool
err = db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, roll.VersionedSchemaName(schema, firstVersion)).Scan(&exists)
if err != nil {
t.Fatal(err)
}

if exists {
if schemaExists(t, db, roll.VersionedSchemaName(schema, firstVersion)) {
t.Errorf("Expected schema %q to not exist", firstVersion)
}
})
Expand All @@ -168,18 +187,7 @@ func TestSchemaIsDroppedAfterMigrationRollback(t *testing.T) {
//
// Check that the schema has been dropped
//
var exists bool
err := db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, roll.VersionedSchemaName(schema, version)).Scan(&exists)
if err != nil {
t.Fatal(err)
}

if exists {
if schemaExists(t, db, roll.VersionedSchemaName(schema, version)) {
t.Errorf("Expected schema %q to not exist", version)
}
})
Expand Down Expand Up @@ -234,17 +242,7 @@ func TestSchemaOptionIsRespected(t *testing.T) {
}

// Ensure that the versioned schema for the first migration has been dropped
err = db.QueryRow(`
SELECT EXISTS(
SELECT 1
FROM pg_catalog.pg_namespace
WHERE nspname = $1
)`, roll.VersionedSchemaName("schema1", version1)).Scan(&exists)
if err != nil {
t.Fatal(err)
}

if exists {
if schemaExists(t, db, roll.VersionedSchemaName("schema1", version1)) {
t.Errorf("Expected schema %q to not exist", version1)
}
})
Expand Down
11 changes: 11 additions & 0 deletions pkg/roll/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ type options struct {

// optional role to set before executing migrations
role string

// disable pgroll version schemas creation and deletion
disableVersionSchemas bool
}

type Option func(*options)
Expand All @@ -25,3 +28,11 @@ func WithRole(role string) Option {
o.role = role
}
}

// WithDisableViewsManagement disables pgroll version schemas management
// when passed, pgroll will not create or drop version schemas
func WithDisableViewsManagement() Option {
return func(o *options) {
o.disableVersionSchemas = true
}
}
12 changes: 8 additions & 4 deletions pkg/roll/roll.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ type Roll struct {
// schema we are acting on
schema string

// disable pgroll version schemas creation and deletion
disableVersionSchemas bool

state *state.State
pgVersion PGVersion
}
Expand Down Expand Up @@ -74,10 +77,11 @@ func New(ctx context.Context, pgURL, schema string, state *state.State, opts ...
}

return &Roll{
pgConn: conn,
schema: schema,
state: state,
pgVersion: PGVersion(pgMajorVersion),
pgConn: conn,
schema: schema,
state: state,
pgVersion: PGVersion(pgMajorVersion),
disableVersionSchemas: options.disableVersionSchemas,
}, nil
}

Expand Down
Loading