diff --git a/sqldb/migrations.go b/sqldb/migrations.go index f81436733..92893fa87 100644 --- a/sqldb/migrations.go +++ b/sqldb/migrations.go @@ -112,6 +112,9 @@ type MigrationExecutor interface { // order. Migration details are stored in the global variable // migrationConfig. ExecuteMigrations(target MigrationTarget) error + + // GetSchemaVersion returns the current schema version of the database. + GetSchemaVersion() (int, bool, error) } var ( @@ -360,6 +363,22 @@ func ApplyMigrations(ctx context.Context, db *BaseDB, } currentVersion = int(version) + } else { + // Since we don't have a version tracked by our own table yet, + // we'll use the schema version reported by sqlc to determine + // the current version. + // + // NOTE: This is safe because the first in-code migration was + // introduced in version 7. This is only possible if the user + // has a schema version <= 4. + var dirty bool + currentVersion, dirty, err = migrator.GetSchemaVersion() + if err != nil { + return err + } + + log.Infof("No database version found, using schema version %d "+ + "(dirty=%v) as base version", currentVersion, dirty) } for _, migration := range migrations { diff --git a/sqldb/postgres.go b/sqldb/postgres.go index d271d214e..a1754fd4f 100644 --- a/sqldb/postgres.go +++ b/sqldb/postgres.go @@ -158,6 +158,10 @@ func (s *PostgresStore) ApplyAllMigrations(ctx context.Context, return ApplyMigrations(ctx, s.BaseDB, s, migrations) } +func errPostgresMigration(err error) error { + return fmt.Errorf("error creating postgres migration: %w", err) +} + // ExecuteMigrations runs migrations for the Postgres database, depending on the // target given, either all migrations or up to a given version. func (s *PostgresStore) ExecuteMigrations(target MigrationTarget) error { @@ -168,7 +172,7 @@ func (s *PostgresStore) ExecuteMigrations(target MigrationTarget) error { driver, err := pgx_migrate.WithInstance(s.DB, &pgx_migrate.Config{}) if err != nil { - return fmt.Errorf("error creating postgres migration: %w", err) + return errPostgresMigration(err) } // Populate the database with our set of schemas based on our embedded @@ -178,3 +182,19 @@ func (s *PostgresStore) ExecuteMigrations(target MigrationTarget) error { postgresFS, driver, "sqlc/migrations", dbName, target, ) } + +// GetSchemaVersion returns the current schema version of the Postgres database. +func (s *PostgresStore) GetSchemaVersion() (int, bool, error) { + driver, err := pgx_migrate.WithInstance(s.DB, &pgx_migrate.Config{}) + if err != nil { + return 0, false, errPostgresMigration(err) + + } + + version, dirty, err := driver.Version() + if err != nil { + return 0, false, err + } + + return version, dirty, nil +} diff --git a/sqldb/sqlite.go b/sqldb/sqlite.go index 81e1f26b3..24a36870d 100644 --- a/sqldb/sqlite.go +++ b/sqldb/sqlite.go @@ -158,6 +158,10 @@ func (s *SqliteStore) ApplyAllMigrations(ctx context.Context, return ApplyMigrations(ctx, s.BaseDB, s, migrations) } +func errSqliteMigration(err error) error { + return fmt.Errorf("error creating sqlite migration: %w", err) +} + // ExecuteMigrations runs migrations for the sqlite database, depending on the // target given, either all migrations or up to a given version. func (s *SqliteStore) ExecuteMigrations(target MigrationTarget) error { @@ -165,7 +169,7 @@ func (s *SqliteStore) ExecuteMigrations(target MigrationTarget) error { s.DB, &sqlite_migrate.Config{}, ) if err != nil { - return fmt.Errorf("error creating sqlite migration: %w", err) + return errSqliteMigration(err) } // Populate the database with our set of schemas based on our embedded @@ -176,6 +180,23 @@ func (s *SqliteStore) ExecuteMigrations(target MigrationTarget) error { ) } +// GetSchemaVersion returns the current schema version of the SQLite database. +func (s *SqliteStore) GetSchemaVersion() (int, bool, error) { + driver, err := sqlite_migrate.WithInstance( + s.DB, &sqlite_migrate.Config{}, + ) + if err != nil { + return 0, false, errSqliteMigration(err) + } + + version, dirty, err := driver.Version() + if err != nil { + return 0, dirty, err + } + + return version, dirty, nil +} + // NewTestSqliteDB is a helper function that creates an SQLite database for // testing. func NewTestSqliteDB(t *testing.T) *SqliteStore {