Skip to content
Open
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
4 changes: 3 additions & 1 deletion backend/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ func (mb *mysqlBackend) Migrate() error {
return fmt.Errorf("opening schema database: %w", err)
}

dbi, err := mysql.WithInstance(db, &mysql.Config{})
dbi, err := mysql.WithInstance(db, &mysql.Config{
MigrationsTable: mb.options.MigrationsTable,
})
if err != nil {
return fmt.Errorf("creating migration instance: %w", err)
}
Expand Down
62 changes: 62 additions & 0 deletions backend/mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,68 @@ func TestMySqlBackendE2E(t *testing.T) {
})
}

func Test_MysqlBackendWithCustomMigrationsTable(t *testing.T) {
if testing.Short() {
t.Skip()
}

adminDB, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/?parseTime=true&interpolateParams=true", testUser, testPassword))
if err != nil {
t.Fatal(err)
}

dbName := "test_migration_table_" + strings.ReplaceAll(uuid.NewString(), "-", "")
if _, err := adminDB.Exec("CREATE DATABASE " + dbName); err != nil {
t.Fatal(err)
}
defer func() {
adminDB.Exec("DROP DATABASE IF EXISTS " + dbName)
adminDB.Close()
}()

dsn := fmt.Sprintf("%s:%s@tcp(localhost:3306)/%s?parseTime=true&interpolateParams=true", testUser, testPassword, dbName)
db, err := sql.Open("mysql", dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()

if _, err := db.Exec("CREATE TABLE schema_migrations (version bigint not null primary key, dirty boolean not null)"); err != nil {
t.Fatal(err)
}
if _, err := db.Exec("INSERT INTO schema_migrations (version, dirty) VALUES (10, false)"); err != nil {
t.Fatal(err)
}

backend := NewMysqlBackendWithDB(
db,
WithApplyMigrations(true),
WithMigrationDSN(dsn+"&multiStatements=true"),
WithMigrationsTable("go_workflows_schema_migrations"),
)
defer backend.Close()

if _, err := db.Exec("SELECT 1 FROM instances LIMIT 1"); err != nil {
t.Fatalf("table should exist after migrations: %v", err)
}

var defaultVersion int
if err := db.QueryRow("SELECT version FROM schema_migrations").Scan(&defaultVersion); err != nil {
t.Fatal(err)
}
if defaultVersion != 10 {
t.Fatalf("expected default migration table version 10, got %d", defaultVersion)
}

var workflowsVersion int
if err := db.QueryRow("SELECT version FROM go_workflows_schema_migrations").Scan(&workflowsVersion); err != nil {
t.Fatal(err)
}
if workflowsVersion != 4 {
t.Fatalf("expected workflows migration table version 4, got %d", workflowsVersion)
}
}

var _ test.TestBackend = (*mysqlBackend)(nil)

func (mb *mysqlBackend) GetFutureEvents(ctx context.Context) ([]*history.Event, error) {
Expand Down
11 changes: 11 additions & 0 deletions backend/mysql/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type options struct {
// using NewMysqlBackendWithDB where no DSN is available. The DSN must support
// multi-statement queries (e.g., include &multiStatements=true).
MigrationDSN string

// MigrationsTable is the table used to track applied migrations. If empty,
// golang-migrate uses its default table.
MigrationsTable string
}

type option func(*options)
Expand All @@ -44,6 +48,13 @@ func WithMigrationDSN(dsn string) option {
}
}

// WithMigrationsTable sets the table used to track applied migrations.
func WithMigrationsTable(migrationsTable string) option {
return func(o *options) {
o.MigrationsTable = migrationsTable
}
}

// WithBackendOptions allows to pass generic backend options.
func WithBackendOptions(opts ...backend.BackendOption) option {
return func(o *options) {
Expand Down
11 changes: 11 additions & 0 deletions backend/postgres/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ type options struct {

// ApplyMigrations automatically applies database migrations on startup.
ApplyMigrations bool

// MigrationsTable is the table used to track applied migrations. If empty,
// golang-migrate uses its default table.
MigrationsTable string
}

type option func(*options)
Expand All @@ -24,6 +28,13 @@ func WithApplyMigrations(applyMigrations bool) option {
}
}

// WithMigrationsTable sets the table used to track applied migrations.
func WithMigrationsTable(migrationsTable string) option {
return func(o *options) {
o.MigrationsTable = migrationsTable
}
}

func WithPostgresOptions(f func(db *sql.DB)) option {
return func(o *options) {
o.PostgresOptions = f
Expand Down
4 changes: 3 additions & 1 deletion backend/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ func (pb *postgresBackend) Migrate() error {
needsClose = false
}

dbi, err := postgres.WithInstance(db, &postgres.Config{})
dbi, err := postgres.WithInstance(db, &postgres.Config{
MigrationsTable: pb.options.MigrationsTable,
})
if err != nil {
return fmt.Errorf("creating migration instance: %w", err)
}
Expand Down
47 changes: 47 additions & 0 deletions backend/postgres/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/cschleiden/go-workflows/backend/test"
"github.com/google/uuid"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/stretchr/testify/require"
)

const testUser = "root"
Expand Down Expand Up @@ -102,6 +103,52 @@ func TestPostgresBackendE2E(t *testing.T) {
})
}

func Test_PostgresBackendWithCustomMigrationsTable(t *testing.T) {
if testing.Short() {
t.Skip()
}

adminDB, err := sql.Open("pgx", fmt.Sprintf("host=localhost port=5432 user=%s password=%s dbname=postgres sslmode=disable", testUser, testPassword))
require.NoError(t, err)

dbName := "test_migration_table_" + strings.ReplaceAll(uuid.NewString(), "-", "")
_, err = adminDB.Exec("CREATE DATABASE " + dbName)
require.NoError(t, err)
defer func() {
_, _ = adminDB.Exec("DROP DATABASE IF EXISTS " + dbName + " WITH (FORCE)")
_ = adminDB.Close()
}()

db, err := sql.Open("pgx", fmt.Sprintf("host=localhost port=5432 user=%s password=%s dbname=%s sslmode=disable", testUser, testPassword, dbName))
require.NoError(t, err)
defer db.Close()

_, err = db.Exec("CREATE TABLE schema_migrations (version bigint NOT NULL PRIMARY KEY, dirty boolean NOT NULL)")
require.NoError(t, err)
_, err = db.Exec("INSERT INTO schema_migrations (version, dirty) VALUES (10, false)")
require.NoError(t, err)

backend := NewPostgresBackendWithDB(
db,
WithApplyMigrations(true),
WithMigrationsTable("go_workflows_schema_migrations"),
)
defer backend.Close()

_, err = db.Exec("SELECT 1 FROM instances LIMIT 1")
require.NoError(t, err)

var defaultVersion int
err = db.QueryRow("SELECT version FROM schema_migrations").Scan(&defaultVersion)
require.NoError(t, err)
require.Equal(t, 10, defaultVersion)

var workflowsVersion int
err = db.QueryRow("SELECT version FROM go_workflows_schema_migrations").Scan(&workflowsVersion)
require.NoError(t, err)
require.Equal(t, 1, workflowsVersion)
}

var _ test.TestBackend = (*postgresBackend)(nil)

// GetFutureEvents returns all pending events that have a non-null visible_at (timers / scheduled future events).
Expand Down
11 changes: 11 additions & 0 deletions backend/sqlite/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ type options struct {

// ApplyMigrations automatically applies database migrations on startup.
ApplyMigrations bool

// MigrationsTable is the table used to track applied migrations. If empty,
// golang-migrate uses its default table.
MigrationsTable string
}

type option func(*options)
Expand All @@ -20,6 +24,13 @@ func WithApplyMigrations(applyMigrations bool) option {
}
}

// WithMigrationsTable sets the table used to track applied migrations.
func WithMigrationsTable(migrationsTable string) option {
return func(o *options) {
o.MigrationsTable = migrationsTable
}
}

// WithBackendOptions allows to pass generic backend options.
func WithBackendOptions(opts ...backend.BackendOption) option {
return func(o *options) {
Expand Down
4 changes: 3 additions & 1 deletion backend/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ func (sb *sqliteBackend) Close() error {
func (sb *sqliteBackend) Migrate() error {
sb.options.Logger.Info("Applying migrations...")

dbi, err := sqlite.WithInstance(sb.db, &sqlite.Config{})
dbi, err := sqlite.WithInstance(sb.db, &sqlite.Config{
MigrationsTable: sb.options.MigrationsTable,
})
if err != nil {
return fmt.Errorf("creating migration instance: %w", err)
}
Expand Down
38 changes: 38 additions & 0 deletions backend/sqlite/sqlite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/cschleiden/go-workflows/backend"
"github.com/cschleiden/go-workflows/backend/test"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

Expand All @@ -24,6 +25,43 @@ func Test_SqliteBackend(t *testing.T) {
})
}

func Test_SqliteBackendWithCustomMigrationsTable(t *testing.T) {
db, err := sql.Open("sqlite", "file:"+uuid.NewString()+"?mode=memory&cache=shared")
require.NoError(t, err)
defer db.Close()

_, err = db.Exec("PRAGMA journal_mode=WAL;")
require.NoError(t, err)
db.SetMaxOpenConns(1)

_, err = db.Exec("CREATE TABLE schema_migrations (version uint64, dirty bool)")
require.NoError(t, err)
_, err = db.Exec("CREATE UNIQUE INDEX version_unique ON schema_migrations (version)")
require.NoError(t, err)
_, err = db.Exec("INSERT INTO schema_migrations (version, dirty) VALUES (10, false)")
require.NoError(t, err)

backend := NewSqliteBackendWithDB(
db,
WithApplyMigrations(true),
WithMigrationsTable("go_workflows_schema_migrations"),
)
defer backend.Close()

_, err = db.Exec("SELECT 1 FROM instances LIMIT 1")
require.NoError(t, err)

var defaultVersion int
err = db.QueryRow("SELECT version FROM schema_migrations").Scan(&defaultVersion)
require.NoError(t, err)
require.Equal(t, 10, defaultVersion)

var workflowsVersion int
err = db.QueryRow("SELECT version FROM go_workflows_schema_migrations").Scan(&workflowsVersion)
require.NoError(t, err)
require.Equal(t, 3, workflowsVersion)
}

func Test_EndToEndSqliteBackend(t *testing.T) {
if testing.Short() {
t.Skip()
Expand Down
5 changes: 4 additions & 1 deletion docs/source/includes/_backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ backend := sqlite.NewSqliteBackendWithDB(db, sqlite.WithApplyMigrations(true))
### Options

- `WithApplyMigrations(applyMigrations bool)` - Set whether migrations should be applied on startup. Defaults to `true` for `NewSqliteBackend`, `false` for `NewSqliteBackendWithDB`
- `WithMigrationsTable(migrationsTable string)` - Set the table used to track applied migrations. Defaults to golang-migrate's standard migration table.
- `WithBackendOptions(opts ...backend.BackendOption)` - Apply generic backend options
Comment on lines +48 to 49

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry about that. My codebase didn't use MySQL so my agent must've missed that. Happy to push a fix if you want.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I pushed a change with the mysql support.


### Schema
Expand Down Expand Up @@ -94,6 +95,7 @@ backend := mysql.NewMysqlBackendWithDB(db,
- `WithMySQLOptions(f func(db *sql.DB))` - Apply custom options to the MySQL database connection
- `WithApplyMigrations(applyMigrations bool)` - Set whether migrations should be applied on startup. Defaults to `true` for `NewMysqlBackend`, `false` for `NewMysqlBackendWithDB`
- `WithMigrationDSN(dsn string)` - Set the DSN to use for migrations. Required when using `NewMysqlBackendWithDB` with `ApplyMigrations` enabled. The DSN must support multi-statement queries.
- `WithMigrationsTable(migrationsTable string)` - Set the table used to track applied migrations. Defaults to golang-migrate's standard migration table.
- `WithBackendOptions(opts ...backend.BackendOption)` - Apply generic backend options


Expand Down Expand Up @@ -136,6 +138,7 @@ backend := postgres.NewPostgresBackendWithDB(db, postgres.WithApplyMigrations(tr

- `WithPostgresOptions(f func(db *sql.DB))` - Apply custom options to the PostgreSQL database connection
- `WithApplyMigrations(applyMigrations bool)` - Set whether migrations should be applied on startup. Defaults to `true` for `NewPostgresBackend`, `false` for `NewPostgresBackendWithDB`
- `WithMigrationsTable(migrationsTable string)` - Set the table used to track applied migrations. Defaults to golang-migrate's standard migration table.
- `WithBackendOptions(opts ...backend.BackendOption)` - Apply generic backend options


Expand Down Expand Up @@ -261,4 +264,4 @@ type Backend interface {
// Close closes any underlying resources
Close() error
}
```
```