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
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ func recoverAndExit() {
!viper.GetBool("DEBUG") {
utils.CmdSuggestion = utils.SuggestDebugFlag
}
if e, ok := err.(*errors.Error); ok && len(utils.Version) == 0 {
fmt.Fprintln(os.Stderr, string(e.Stack()))
}
msg = err.Error()
default:
msg = fmt.Sprintf("%#v", err)
Expand Down
87 changes: 38 additions & 49 deletions internal/db/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,6 @@ import (
type DiffFunc func(context.Context, string, string, []string) (string, error)

func Run(ctx context.Context, schema []string, file string, config pgconn.Config, differ DiffFunc, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (err error) {
// Sanity checks.
if utils.IsLocalDatabase(config) {
if declared, err := loadDeclaredSchemas(fsys); err != nil {
return err
} else if container, err := createShadowIfNotExists(ctx, declared); err != nil {
return err
} else if len(container) > 0 {
defer utils.DockerRemove(container)
if err := start.WaitForHealthyService(ctx, start.HealthTimeout, container); err != nil {
return err
}
if err := migrateBaseDatabase(ctx, container, declared, fsys, options...); err != nil {
return err
}
}
}
// 1. Load all user defined schemas
if len(schema) == 0 {
schema, err = loadSchema(ctx, config, options...)
Expand All @@ -72,22 +56,6 @@ func Run(ctx context.Context, schema []string, file string, config pgconn.Config
return nil
}

func createShadowIfNotExists(ctx context.Context, migrations []string) (string, error) {
if len(migrations) == 0 {
return "", nil
}
if err := utils.AssertSupabaseDbIsRunning(); !errors.Is(err, utils.ErrNotRunning) {
return "", err
}
fmt.Fprintln(os.Stderr, "Creating local database from declarative schemas:")
msg := make([]string, len(migrations))
for i, m := range migrations {
msg[i] = fmt.Sprintf(" • %s", utils.Bold(m))
}
fmt.Fprintln(os.Stderr, strings.Join(msg, "\n"))
return CreateShadowDatabase(ctx, utils.Config.Db.Port)
}

func loadDeclaredSchemas(fsys afero.Fs) ([]string, error) {
if schemas := utils.Config.Db.Migrations.SchemaPaths; len(schemas) > 0 {
return schemas.Files(afero.NewIOFS(fsys))
Expand Down Expand Up @@ -140,15 +108,15 @@ func loadSchema(ctx context.Context, config pgconn.Config, options ...func(*pgx.
}

func CreateShadowDatabase(ctx context.Context, port uint16) (string, error) {
config := start.NewContainerConfig()
// Disable background workers in shadow database
config := start.NewContainerConfig("-c", "max_worker_processes=0")
hostPort := strconv.FormatUint(uint64(port), 10)
hostConfig := container.HostConfig{
PortBindings: nat.PortMap{"5432/tcp": []nat.PortBinding{{HostPort: hostPort}}},
AutoRemove: true,
}
networkingConfig := network.NetworkingConfig{}
if utils.Config.Db.MajorVersion <= 14 {
config.Entrypoint = nil
hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""}
}
return utils.DockerStart(ctx, config, hostConfig, networkingConfig, "")
Expand All @@ -164,6 +132,9 @@ func ConnectShadowDatabase(ctx context.Context, timeout time.Duration, options .
return backoff.RetryWithData(connect, policy)
}

// Required to bypass pg_cron check: https://github.com/citusdata/pg_cron/blob/main/pg_cron.sql#L3
const CREATE_TEMPLATE = "CREATE DATABASE contrib_regression TEMPLATE postgres"

func MigrateShadowDatabase(ctx context.Context, container string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
migrations, err := migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys))
if err != nil {
Expand All @@ -177,19 +148,10 @@ func MigrateShadowDatabase(ctx context.Context, container string, fsys afero.Fs,
if err := start.SetupDatabase(ctx, conn, container[:12], os.Stderr, fsys); err != nil {
return err
}
return migration.ApplyMigrations(ctx, migrations, conn, afero.NewIOFS(fsys))
}

func migrateBaseDatabase(ctx context.Context, container string, migrations []string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{}, options...)
if err != nil {
return err
}
defer conn.Close(context.Background())
if err := start.SetupDatabase(ctx, conn, container[:12], os.Stderr, fsys); err != nil {
return err
if _, err := conn.Exec(ctx, CREATE_TEMPLATE); err != nil {
return errors.Errorf("failed to create template database: %w", err)
}
return migration.SeedGlobals(ctx, migrations, conn, afero.NewIOFS(fsys))
return migration.ApplyMigrations(ctx, migrations, conn, afero.NewIOFS(fsys))
}

func DiffDatabase(ctx context.Context, schema []string, config pgconn.Config, w io.Writer, fsys afero.Fs, differ func(context.Context, string, string, []string) (string, error), options ...func(*pgx.ConnConfig)) (string, error) {
Expand All @@ -205,14 +167,41 @@ func DiffDatabase(ctx context.Context, schema []string, config pgconn.Config, w
if err := MigrateShadowDatabase(ctx, shadow, fsys, options...); err != nil {
return "", err
}
fmt.Fprintln(w, "Diffing schemas:", strings.Join(schema, ","))
source := utils.ToPostgresURL(pgconn.Config{
shadowConfig := pgconn.Config{
Host: utils.Config.Hostname,
Port: utils.Config.Db.ShadowPort,
User: "postgres",
Password: utils.Config.Db.Password,
Database: "postgres",
})
}
if utils.IsLocalDatabase(config) {
if declared, err := loadDeclaredSchemas(fsys); err != nil {
return "", err
} else if len(declared) > 0 {
config = shadowConfig
config.Database = "contrib_regression"
if err := migrateBaseDatabase(ctx, config, declared, fsys, options...); err != nil {
return "", err
}
}
}
fmt.Fprintln(w, "Diffing schemas:", strings.Join(schema, ","))
source := utils.ToPostgresURL(shadowConfig)
target := utils.ToPostgresURL(config)
return differ(ctx, source, target, schema)
}

func migrateBaseDatabase(ctx context.Context, config pgconn.Config, migrations []string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
fmt.Fprintln(os.Stderr, "Creating local database from declarative schemas:")
msg := make([]string, len(migrations))
for i, m := range migrations {
msg[i] = fmt.Sprintf(" • %s", utils.Bold(m))
}
fmt.Fprintln(os.Stderr, strings.Join(msg, "\n"))
conn, err := utils.ConnectLocalPostgres(ctx, config, options...)
if err != nil {
return err
}
defer conn.Close(context.Background())
return migration.SeedGlobals(ctx, migrations, conn, afero.NewIOFS(fsys))
}
10 changes: 8 additions & 2 deletions internal/db/diff/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func TestRun(t *testing.T) {
require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-migra", diff))
// Setup mock postgres
conn := pgtest.NewConn()
conn.Query(CREATE_TEMPLATE).
Reply("CREATE DATABASE")
defer conn.Close(t)
// Run test
err := Run(context.Background(), []string{"public"}, "file", dbConfig, DiffSchemaMigra, fsys, conn.Intercept)
Expand Down Expand Up @@ -134,7 +136,9 @@ func TestMigrateShadow(t *testing.T) {
conn.Query(utils.GlobalsSql).
Reply("CREATE SCHEMA").
Query(utils.InitialSchemaPg14Sql).
Reply("CREATE SCHEMA")
Reply("CREATE SCHEMA").
Query(CREATE_TEMPLATE).
Reply("CREATE DATABASE")
helper.MockMigrationHistory(conn).
Query(sql).
Reply("CREATE SCHEMA").
Expand Down Expand Up @@ -308,7 +312,9 @@ create schema public`)
conn.Query(utils.GlobalsSql).
Reply("CREATE SCHEMA").
Query(utils.InitialSchemaPg14Sql).
Reply("CREATE SCHEMA")
Reply("CREATE SCHEMA").
Query(CREATE_TEMPLATE).
Reply("CREATE DATABASE")
helper.MockMigrationHistory(conn).
Query(sql).
Reply("CREATE SCHEMA").
Expand Down
37 changes: 18 additions & 19 deletions internal/db/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ func Run(ctx context.Context, fromBackup string, fsys afero.Fs) error {
return err
}

func NewContainerConfig() container.Config {
func NewContainerConfig(args ...string) container.Config {
if utils.Config.Db.MajorVersion >= 14 {
// Extensions schema does not exist on PG13 and below
args = append(args, "-c", "search_path='$user,public,extensions'")
}
env := []string{
"POSTGRES_PASSWORD=" + utils.Config.Db.Password,
"POSTGRES_HOST=/var/run/postgresql",
Expand Down Expand Up @@ -92,7 +96,7 @@ func NewContainerConfig() container.Config {
cat <<'EOF' > /etc/postgresql.schema.sql && \
cat <<'EOF' > /etc/postgresql-custom/pgsodium_root.key && \
cat <<'EOF' >> /etc/postgresql/postgresql.conf && \
docker-entrypoint.sh postgres -D /etc/postgresql
docker-entrypoint.sh postgres -D /etc/postgresql ` + strings.Join(args, " ") + `
` + initialSchema + `
` + webhookSchema + `
` + _supabaseSchema + `
Expand All @@ -102,12 +106,15 @@ EOF
` + utils.Config.Db.Settings.ToPostgresConfig() + `
EOF`},
}
if utils.Config.Db.MajorVersion >= 14 {
config.Cmd = []string{"postgres",
"-c", "config_file=/etc/postgresql/postgresql.conf",
// Ref: https://postgrespro.com/list/thread-id/2448092
"-c", `search_path="$user",public,extensions`,
}
if utils.Config.Db.MajorVersion <= 14 {
config.Entrypoint = []string{"sh", "-c", `
cat <<'EOF' > /docker-entrypoint-initdb.d/supabase_schema.sql && \
cat <<'EOF' >> /etc/postgresql/postgresql.conf && \
docker-entrypoint.sh postgres -D /etc/postgresql ` + strings.Join(args, " ") + `
` + _supabaseSchema + `
EOF
` + utils.Config.Db.Settings.ToPostgresConfig() + `
EOF`}
}
return config
}
Expand All @@ -122,6 +129,9 @@ func NewHostConfig() container.HostConfig {
utils.ConfigId + ":/etc/postgresql-custom",
},
}
if utils.Config.Db.MajorVersion <= 14 {
hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""}
}
return hostConfig
}

Expand All @@ -135,17 +145,6 @@ func StartDatabase(ctx context.Context, fromBackup string, fsys afero.Fs, w io.W
},
},
}
if utils.Config.Db.MajorVersion <= 14 {
config.Entrypoint = []string{"sh", "-c", `
cat <<'EOF' > /docker-entrypoint-initdb.d/supabase_schema.sql && \
cat <<'EOF' >> /etc/postgresql/postgresql.conf && \
docker-entrypoint.sh postgres -D /etc/postgresql
` + _supabaseSchema + `
EOF
` + utils.Config.Db.Settings.ToPostgresConfig() + `
EOF`}
hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""}
}
if len(fromBackup) > 0 {
config.Entrypoint = []string{"sh", "-c", `
cat <<'EOF' > /etc/postgresql.schema.sql && \
Expand Down