diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..a13ffba --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,30 @@ +name: PostgreSQL service example +on: push + +jobs: + container-job: + runs-on: ubuntu-latest + strategy: + fail-fast: true + services: + postgres: + image: postgres:14.4 + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Checkout code + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # latest + + - name: Install Go + uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # latest + with: + go-version: 1.17 + + - name: Test Postgres pgx + run: cd dbump_pgx && go test -v -race -shuffle=on -coverprofile=coverage.txt ./... diff --git a/dbump.go b/dbump.go index 7fd4df3..3add7bf 100644 --- a/dbump.go +++ b/dbump.go @@ -25,7 +25,7 @@ type Config struct { Mode MigratorMode // UseForce to get a lock on a database. MUST be used with the caution. - // Should be used when previos migration run didn't unlock the database, + // Should be used when previous migration run didn't unlock the database, // and this blocks subsequent runs. UseForce bool diff --git a/dbump_pgx/go.mod b/dbump_pgx/go.mod index d5697a2..ee57e44 100644 --- a/dbump_pgx/go.mod +++ b/dbump_pgx/go.mod @@ -1,8 +1,22 @@ module github.com/cristalhq/dbump/dbump_pgx -go 1.16 +go 1.17 require ( github.com/cristalhq/dbump v0.2.0 github.com/jackc/pgx/v4 v4.16.1 ) + +replace github.com/cristalhq/dbump => ../ + +require ( + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.12.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.11.0 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/dbump_pgx/go.sum b/dbump_pgx/go.sum index 2e9aa60..c329a3d 100644 --- a/dbump_pgx/go.sum +++ b/dbump_pgx/go.sum @@ -5,8 +5,6 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/cristalhq/dbump v0.2.0 h1:zwk0d4UFBGtXswYh6lAZ5KDCEgnn9px9HMzLCUQSDso= -github.com/cristalhq/dbump v0.2.0/go.mod h1:rAjULuStbuNPCLrJT62Eu7Sp/2gVt/4URUvsnPK1yFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/dbump_pgx/pgx.go b/dbump_pgx/pgx.go index 8df482a..011d7f2 100644 --- a/dbump_pgx/pgx.go +++ b/dbump_pgx/pgx.go @@ -26,8 +26,8 @@ func NewMigrator(conn *pgx.Conn) *Migrator { // Init migrator. func (pg *Migrator) Init(ctx context.Context) error { - query := `CREATE TABLE IF NOT EXISTS _dbump_schema_version ( - version BIGINT NOT NULL PRIMARY KEY, + query := `CREATE TABLE IF NOT EXISTS _dbump_log ( + version BIGINT NOT NULL PRIMARY KEY, created_at TIMESTAMP WITH TIME ZONE NOT NULL );` _, err := pg.conn.Exec(ctx, query) @@ -48,7 +48,7 @@ func (pg *Migrator) UnlockDB(ctx context.Context) error { // Version is a method for Migrator interface. func (pg *Migrator) Version(ctx context.Context) (version int, err error) { - query := "SELECT COUNT(*) FROM _dbump_schema_version;" + query := "SELECT COUNT(*) FROM _dbump_log;" row := pg.conn.QueryRow(ctx, query) err = row.Scan(&version) return version, err @@ -56,10 +56,9 @@ func (pg *Migrator) Version(ctx context.Context) (version int, err error) { // SetVersion is a method for Migrator interface. func (pg *Migrator) SetVersion(ctx context.Context, version int) error { - query := `INSERT INTO _dbump_schema_version (version, created_at) -VALUES ($1, NOW()) -ON CONFLICT (version) DO UPDATE -SET created_at = NOW();` + query := `DELETE FROM _dbump_log WHERE version >= $1; +INSERT INTO _dbump_log (version, created_at) VALUES ($1, NOW()) +ON CONFLICT (version) DO UPDATE SET created_at = NOW();` _, err := pg.conn.Exec(ctx, query, version) return err } diff --git a/dbump_pgx/pgx_test.go b/dbump_pgx/pgx_test.go new file mode 100644 index 0000000..c986841 --- /dev/null +++ b/dbump_pgx/pgx_test.go @@ -0,0 +1,73 @@ +package dbump_pgx + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/cristalhq/dbump" + "github.com/jackc/pgx/v4" +) + +var conn *pgx.Conn + +func init() { + host := envOrDef("DBUMP_PG_HOST", "127.0.0.1") + port := envOrDef("DBUMP_PG_PORT", "5432") + username := envOrDef("DBUMP_PG_USER", "postgres") + password := envOrDef("DBUMP_PG_PASS", "postgres") + db := envOrDef("DBUMP_PG_DB", "postgres") + sslmode := envOrDef("DBUMP_PG_SSL", "disable") + + dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", + host, port, username, password, db, sslmode) + + var err error + conn, err = pgx.Connect(context.Background(), dsn) + if err != nil { + panic(err) + } +} + +func TestPGX_Simple(t *testing.T) { + m := NewMigrator(conn) + l := dbump.NewSliceLoader([]*dbump.Migration{ + { + ID: 1, + Apply: "SELECT 1;", + Revert: "SELECT 1;", + }, + { + ID: 2, + Apply: "SELECT 1;", + Revert: "SELECT 1;", + }, + { + ID: 3, + Apply: "SELECT 1;", + Revert: "SELECT 1;", + }, + }) + + errRun := dbump.Run(context.Background(), dbump.Config{ + Migrator: m, + Loader: l, + Mode: dbump.ModeUp, + }) + failIfErr(t, errRun) +} + +func envOrDef(env, def string) string { + if val := os.Getenv(env); val != "" { + return val + } + return def +} + +func failIfErr(tb testing.TB, err error) { + tb.Helper() + if err != nil { + tb.Fatal(err) + } +} diff --git a/dbump_test.go b/dbump_test.go index 39889e1..89567ef 100644 --- a/dbump_test.go +++ b/dbump_test.go @@ -47,9 +47,6 @@ func TestRunCheck(t *testing.T) { } } -func TestLoaderError(t *testing.T) { -} - func TestMigrateUp(t *testing.T) { wantLog := []string{ "init", "lockdb", "getversion", diff --git a/load.go b/load.go index 70742a7..e3efdb1 100644 --- a/load.go +++ b/load.go @@ -34,21 +34,21 @@ func (dl *DiskLoader) Load() ([]*Migration, error) { // FileSysLoader can load migrations from fs.FS. type FileSysLoader struct { - fs FS + fsys FS path string } // NewFileSysLoader instantiates a new FileSysLoader. -func NewFileSysLoader(fs FS, path string) *FileSysLoader { +func NewFileSysLoader(fsys FS, path string) *FileSysLoader { return &FileSysLoader{ - fs: fs, + fsys: fsys, path: strings.TrimRight(path, string(os.PathSeparator)), } } // Load is a method for Loader interface. func (el *FileSysLoader) Load() ([]*Migration, error) { - return loadMigrationsFromFS(el.fs, el.path) + return loadMigrationsFromFS(el.fsys, el.path) } // SliceLoader loads given migrations. @@ -78,8 +78,8 @@ func (sl *SliceLoader) AddMigration(m *Migration) { var migrationRE = regexp.MustCompile(`^(\d+)_.+\.sql$`) -func loadMigrationsFromFS(fs FS, path string) ([]*Migration, error) { - files, err := fs.ReadDir(path) +func loadMigrationsFromFS(fsys FS, path string) ([]*Migration, error) { + files, err := fsys.ReadDir(path) if err != nil { return nil, err } @@ -95,7 +95,7 @@ func loadMigrationsFromFS(fs FS, path string) ([]*Migration, error) { continue } - m, err := loadMigrationFromFS(fs, path, matches[1], fi.Name()) + m, err := loadMigrationFromFS(fsys, path, matches[1], fi.Name()) if err != nil { return nil, err } @@ -105,13 +105,13 @@ func loadMigrationsFromFS(fs FS, path string) ([]*Migration, error) { return migs, nil } -func loadMigrationFromFS(fs FS, path, id, name string) (*Migration, error) { +func loadMigrationFromFS(fsys FS, path, id, name string) (*Migration, error) { n, err := strconv.ParseInt(id, 10, 32) if err != nil { return nil, err } - body, err := fs.ReadFile(filepath.Join(path, name)) + body, err := fsys.ReadFile(filepath.Join(path, name)) if err != nil { return nil, err }