From c805fc64e1ebc88d375541c224400cebc5d60619 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 7 Oct 2024 13:08:25 +0800 Subject: [PATCH] chore: create seed table separately --- internal/db/push/push_test.go | 3 ++- internal/link/link.go | 5 ++++- internal/link/link_test.go | 6 ++++-- internal/testing/helper/history.go | 9 ++++++++- pkg/migration/apply_test.go | 7 ++----- pkg/migration/history.go | 14 +++++++++++++- pkg/migration/seed.go | 5 +++++ pkg/migration/seed_test.go | 15 +++++++++++++-- 8 files changed, 51 insertions(+), 13 deletions(-) diff --git a/internal/db/push/push_test.go b/internal/db/push/push_test.go index 631c2cc7e..3a3ff3cda 100644 --- a/internal/db/push/push_test.go +++ b/internal/db/push/push_test.go @@ -180,7 +180,8 @@ func TestPushAll(t *testing.T) { Reply("SELECT 0") helper.MockMigrationHistory(conn). Query(migration.INSERT_MIGRATION_VERSION, "0", "test", nil). - Reply("INSERT 0 1"). + Reply("INSERT 0 1") + helper.MockSeedHistory(conn). Query(migration.UPSERT_SEED_FILE, seedPath, digest). ReplyError(pgerrcode.NotNullViolation, `null value in column "hash" of relation "seed_files"`) // Run test diff --git a/internal/link/link.go b/internal/link/link.go index 3a2831070..1d3824b84 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -183,7 +183,10 @@ func linkDatabase(ctx context.Context, config pgconn.Config, options ...func(*pg defer conn.Close(context.Background()) updatePostgresConfig(conn) // If `schema_migrations` doesn't exist on the remote database, create it. - return migration.CreateMigrationTable(ctx, conn) + if err := migration.CreateMigrationTable(ctx, conn); err != nil { + return err + } + return migration.CreateSeedTable(ctx, conn) } func linkDatabaseVersion(ctx context.Context, projectRef string, fsys afero.Fs) error { diff --git a/internal/link/link_test.go b/internal/link/link_test.go index af17eb9f5..edc95d8fa 100644 --- a/internal/link/link_test.go +++ b/internal/link/link_test.go @@ -46,6 +46,7 @@ func TestLinkCommand(t *testing.T) { conn := pgtest.NewConn() defer conn.Close(t) helper.MockMigrationHistory(conn) + helper.MockSeedHistory(conn) // Flush pending mocks after test execution defer gock.OffAll() gock.New(utils.DefaultApiHost). @@ -279,6 +280,7 @@ func TestLinkDatabase(t *testing.T) { }) defer conn.Close(t) helper.MockMigrationHistory(conn) + helper.MockSeedHistory(conn) // Run test err := linkDatabase(context.Background(), dbConfig, conn.Intercept) // Check error @@ -294,6 +296,7 @@ func TestLinkDatabase(t *testing.T) { }) defer conn.Close(t) helper.MockMigrationHistory(conn) + helper.MockSeedHistory(conn) // Run test err := linkDatabase(context.Background(), dbConfig, conn.Intercept) // Check error @@ -313,8 +316,7 @@ func TestLinkDatabase(t *testing.T) { Query(migration.CREATE_VERSION_TABLE). ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations"). Query(migration.ADD_STATEMENTS_COLUMN). - Query(migration.ADD_NAME_COLUMN). - Query(migration.CREATE_SEED_TABLE) + Query(migration.ADD_NAME_COLUMN) // Run test err := linkDatabase(context.Background(), dbConfig, conn.Intercept) // Check error diff --git a/internal/testing/helper/history.go b/internal/testing/helper/history.go index 30f4a0ca2..95c846b7a 100644 --- a/internal/testing/helper/history.go +++ b/internal/testing/helper/history.go @@ -14,7 +14,14 @@ func MockMigrationHistory(conn *pgtest.MockConn) *pgtest.MockConn { Query(migration.ADD_STATEMENTS_COLUMN). Reply("ALTER TABLE"). Query(migration.ADD_NAME_COLUMN). - Reply("ALTER TABLE"). + Reply("ALTER TABLE") + return conn +} + +func MockSeedHistory(conn *pgtest.MockConn) *pgtest.MockConn { + conn.Query(migration.SET_LOCK_TIMEOUT). + Query(migration.CREATE_VERSION_SCHEMA). + Reply("CREATE SCHEMA"). Query(migration.CREATE_SEED_TABLE). Reply("CREATE TABLE") return conn diff --git a/pkg/migration/apply_test.go b/pkg/migration/apply_test.go index 7415dfce2..65a75028a 100644 --- a/pkg/migration/apply_test.go +++ b/pkg/migration/apply_test.go @@ -130,8 +130,7 @@ func TestApplyMigrations(t *testing.T) { Query(CREATE_VERSION_TABLE). ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations"). Query(ADD_STATEMENTS_COLUMN). - Query(ADD_NAME_COLUMN). - Query(CREATE_SEED_TABLE) + Query(ADD_NAME_COLUMN) // Run test err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), fsys) // Check error @@ -176,8 +175,6 @@ func mockMigrationHistory(conn *pgtest.MockConn) *pgtest.MockConn { Query(ADD_STATEMENTS_COLUMN). Reply("ALTER TABLE"). Query(ADD_NAME_COLUMN). - Reply("ALTER TABLE"). - Query(CREATE_SEED_TABLE). - Reply("CREATE TABLE") + Reply("ALTER TABLE") return conn } diff --git a/pkg/migration/history.go b/pkg/migration/history.go index 832651874..5ff77ae6b 100644 --- a/pkg/migration/history.go +++ b/pkg/migration/history.go @@ -36,7 +36,6 @@ func CreateMigrationTable(ctx context.Context, conn *pgx.Conn) error { batch.ExecParams(CREATE_VERSION_TABLE, nil, nil, nil, nil) batch.ExecParams(ADD_STATEMENTS_COLUMN, nil, nil, nil, nil) batch.ExecParams(ADD_NAME_COLUMN, nil, nil, nil, nil) - batch.ExecParams(CREATE_SEED_TABLE, nil, nil, nil, nil) if _, err := conn.PgConn().ExecBatch(ctx, &batch).ReadAll(); err != nil { return errors.Errorf("failed to create migration table: %w", err) } @@ -51,6 +50,19 @@ func ReadMigrationTable(ctx context.Context, conn *pgx.Conn) ([]MigrationFile, e return pgxv5.CollectRows[MigrationFile](rows) } +func CreateSeedTable(ctx context.Context, conn *pgx.Conn) error { + // This must be run without prepared statements because each statement in the batch depends on + // the previous schema change. The lock timeout will be reset when implicit transaction ends. + batch := pgconn.Batch{} + batch.ExecParams(SET_LOCK_TIMEOUT, nil, nil, nil, nil) + batch.ExecParams(CREATE_VERSION_SCHEMA, nil, nil, nil, nil) + batch.ExecParams(CREATE_SEED_TABLE, nil, nil, nil, nil) + if _, err := conn.PgConn().ExecBatch(ctx, &batch).ReadAll(); err != nil { + return errors.Errorf("failed to create migration table: %w", err) + } + return nil +} + func ReadSeedTable(ctx context.Context, conn *pgx.Conn) ([]SeedFile, error) { rows, err := conn.Query(ctx, SELECT_SEED_TABLE) if err != nil { diff --git a/pkg/migration/seed.go b/pkg/migration/seed.go index 44ac67ae0..988e8d4bc 100644 --- a/pkg/migration/seed.go +++ b/pkg/migration/seed.go @@ -58,6 +58,11 @@ func GetPendingSeeds(ctx context.Context, locals []string, conn *pgx.Conn, fsys } func SeedData(ctx context.Context, pending []SeedFile, conn *pgx.Conn, fsys fs.FS) error { + if len(pending) > 0 { + if err := CreateSeedTable(ctx, conn); err != nil { + return err + } + } for _, seed := range pending { if seed.Dirty { fmt.Fprintf(os.Stderr, "Updating seed hash to %s...\n", seed.Path) diff --git a/pkg/migration/seed_test.go b/pkg/migration/seed_test.go index f3ba81650..7e9cd4aca 100644 --- a/pkg/migration/seed_test.go +++ b/pkg/migration/seed_test.go @@ -102,7 +102,8 @@ func TestSeedData(t *testing.T) { // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) - conn.Query(UPSERT_SEED_FILE, seed.Path, seed.Hash). + mockSeedHistory(conn). + Query(UPSERT_SEED_FILE, seed.Path, seed.Hash). Reply("INSERT 0 1") // Run test err := SeedData(context.Background(), []SeedFile{seed}, conn.MockClient(t), testMigrations) @@ -118,7 +119,8 @@ func TestSeedData(t *testing.T) { // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) - conn.Query(testSeed+`;INSERT INTO supabase_migrations.seed_files(path, hash) VALUES( 'testdata/seed.sql' , '61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3' ) ON CONFLICT (path) DO UPDATE SET hash = EXCLUDED.hash`). + mockSeedHistory(conn). + Query(testSeed+`;INSERT INTO supabase_migrations.seed_files(path, hash) VALUES( 'testdata/seed.sql' , '61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3' ) ON CONFLICT (path) DO UPDATE SET hash = EXCLUDED.hash`). ReplyError(pgerrcode.NotNullViolation, `null value in column "age" of relation "employees"`) // Run test err := SeedData(context.Background(), []SeedFile{seed}, conn.MockClient(t, func(cc *pgx.ConnConfig) { @@ -129,6 +131,15 @@ func TestSeedData(t *testing.T) { }) } +func mockSeedHistory(conn *pgtest.MockConn) *pgtest.MockConn { + conn.Query(SET_LOCK_TIMEOUT). + Query(CREATE_VERSION_SCHEMA). + Reply("CREATE SCHEMA"). + Query(CREATE_SEED_TABLE). + Reply("CREATE TABLE") + return conn +} + //go:embed testdata/1_globals.sql var testGlobals string