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
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ all:
go generate
go test
golangci-lint run
@ echo any output from the following command indicates an out-of-date direct dependency
go list -u -m -f '{{if (and (not .Indirect) .Update)}}{{.}}{{end}}' all

coverageO:
go install github.com/eltorocorp/drygopher/drygopher@latest
Expand All @@ -28,3 +30,8 @@ golanglint:
# binary will be $(go env GOPATH)/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin v1.45.2
golangci-lint --version

misspell:;
go install github.com/client9/misspell/cmd/misspell@latest
misspell -w `find . -name \*.md`

12 changes: 6 additions & 6 deletions apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
var err error
d.db, err = OpenAnyDB(s.options.Overrides.MigrateDSN)
if err != nil {
return errors.Wrap(err, "Could not open database")
return errors.Wrap(err, "could not open database")
}
}
err = d.prepare(ctx)
Expand All @@ -67,7 +67,7 @@
}
}()
if s.options.Overrides.ErrorIfMigrateNeeded && !d.done(s) {
return errors.Errorf("Migrations required for %s", d.DBName)
return errors.Errorf("migrations required for %s", d.DBName)
}
return d.migrate(ctx, s)
}(d)
Expand All @@ -85,7 +85,7 @@
for _, ref := range migration.Base().rawAfter {
after, ok := d.migrationIndex[ref]
if !ok {
return errors.Errorf("Migration %s for %s is supposed to be after %s for %s but that cannot be found",
return errors.Errorf("migration %s for %s is supposed to be after %s for %s but that cannot be found",
migration.Base().Name.Name, migration.Base().Name.Library, ref.Name, ref.Library)
}
nodes[after.Base().order].Blocking = append(nodes[after.Base().order].Blocking, migration.Base().order)
Expand Down Expand Up @@ -229,7 +229,7 @@
if m.Base().skipIf != nil {
skip, err := m.Base().skipIf()
if err != nil {
return false, errors.Wrapf(err, "SkipIf %s", m.Base().Name)
return false, errors.Wrapf(err, "skipIf %s", m.Base().Name)
}
if skip {
d.log.Debug(" skipping migration")
Expand All @@ -240,7 +240,7 @@
if m.Base().skipRemainingIf != nil {
skip, err := m.Base().skipRemainingIf()
if err != nil {
return false, errors.Wrapf(err, "SkipRemainingIf %s", m.Base().Name)
return false, errors.Wrapf(err, "skipRemainingIf %s", m.Base().Name)

Check warning on line 243 in apply.go

View check run for this annotation

Codecov / codecov/patch

apply.go#L243

Added line #L243 was not covered by tests
}
if skip {
d.log.Debug(" skipping remaining migrations")
Expand Down Expand Up @@ -294,7 +294,7 @@
// allDone reports status
func (d *Database) allDone(m Migration, err error) {
if err != nil && m != nil {
err = errors.Wrapf(err, "Migration %s", m.Base().Name)
err = errors.Wrapf(err, "migration %s", m.Base().Name)

Check warning on line 297 in apply.go

View check run for this annotation

Codecov / codecov/patch

apply.go#L297

Added line #L297 was not covered by tests
}
if d.Options.OnMigrationsComplete != nil {
d.Options.OnMigrationsComplete(d, err)
Expand Down
4 changes: 2 additions & 2 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func OpenAnyDB(dsn string) (*sql.DB, error) {
return sql.Open(driver, dsn)
}
}
return nil, fmt.Errorf("Could not find database driver matching %s", wanted)
return nil, fmt.Errorf("could not find database driver matching %s", wanted)
}
return nil, fmt.Errorf("Could not find appropriate database driver for DSN")
return nil, fmt.Errorf("could not find appropriate database driver for DSN")
}
4 changes: 2 additions & 2 deletions driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
func TestOpenAnyDB(t *testing.T) {
_, err := libschema.OpenAnyDB("http://foo/bar")
require.Error(t, err)
assert.Contains(t, err.Error(), "Could not find database driver matching http")
assert.Contains(t, err.Error(), "could not find database driver matching http")
_, err = libschema.OpenAnyDB("postgres")
require.Error(t, err)
assert.Contains(t, err.Error(), "Could not find appropriate database driver for DSN")
assert.Contains(t, err.Error(), "could not find appropriate database driver for DSN")
}
20 changes: 11 additions & 9 deletions lsmysql/bad_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestBadMigrationsMysql(t *testing.T) {
},
{
name: "wrong db",
error: `Non-mysql`,
error: `non-mysql`,
define: func(dbase *libschema.Database) {
dbase.Migrations("L2",
lspostgres.Script("T4", `INSERT INTO T1 (id) VALUES ('T4')`),
Expand All @@ -49,28 +49,28 @@ func TestBadMigrationsMysql(t *testing.T) {
},
{
name: "bad table1",
error: `Tracking table 'foo.bar.baz' is not valid`,
error: `tracking table 'foo.bar.baz' is not valid`,
reopt: func(o *libschema.Options) {
o.TrackingTable = "foo.bar.baz"
},
},
{
name: "bad table2",
error: `Tracking table schema name must be a simple identifier, not 'foo'bar'`,
error: `tracking table schema name must be a simple identifier, not 'foo'bar'`,
reopt: func(o *libschema.Options) {
o.TrackingTable = "foo'bar.baz"
},
},
{
name: "non idempotent",
error: `Unconditional migration has non-idempotent DDL (Data Definition Language [schema changes]`,
error: `unconditional migration has non-idempotent DDL (Data Definition Language [schema changes]`,
define: func(dbase *libschema.Database) {
dbase.Migrations("L2", lsmysql.Script("T4", `CREATE TABLE T1 (id text) TYPE = InnoDB`))
},
},
{
name: "combines data & ddl",
error: `Migration combines DDL (Data Definition Language [schema changes]) and data manipulation`,
error: `migration combines DDL (Data Definition Language [schema changes]) and data manipulation`,
define: func(dbase *libschema.Database) {
dbase.Migrations("L2", lsmysql.Script("T4", `
CREATE TABLE IF NOT EXISTST1 (id text) TYPE = InnoDB;
Expand All @@ -80,21 +80,21 @@ func TestBadMigrationsMysql(t *testing.T) {
},
{
name: "bad table3",
error: `Tracking table table name must be a simple identifier, not 'bar'baz'`,
error: `tracking table table name must be a simple identifier, not 'bar'baz'`,
reopt: func(o *libschema.Options) {
o.TrackingTable = "foo.bar'baz"
},
},
{
name: "bad table4",
error: `Tracking table table name must be a simple identifier, not 'bar'baz'`,
error: `tracking table table name must be a simple identifier, not 'bar'baz'`,
reopt: func(o *libschema.Options) {
o.TrackingTable = "bar'baz"
},
},
{
name: "bad schema",
error: `Options.SchemaOverride must be a simple identifier`,
error: `options.SchemaOverride must be a simple identifier`,
reopt: func(o *libschema.Options) {
o.SchemaOverride = `"foo."bar.baz"`
},
Expand All @@ -120,7 +120,9 @@ func testBadMigration(t *testing.T, expected string, define func(*libschema.Data

db, err := sql.Open("mysql", dsn)
require.NoError(t, err, "open database")
defer db.Close()
defer func() {
assert.NoError(t, db.Close())
}()
defer cleanup(db)

if reopt != nil {
Expand Down
12 changes: 9 additions & 3 deletions lsmysql/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import (
"github.com/pkg/errors"
)

var ErrDataAndDDL = errors.New("Migration combines DDL (Data Definition Language [schema changes]) and data manipulation")
var ErrNonIdempotentDDL = errors.New("Unconditional migration has non-idempotent DDL (Data Definition Language [schema changes])")
var ifExistsRE = regexp.MustCompile(`(?i)\bIF (?:NOT )?EXISTS\b`)
var (
// ErrDataAndDDL is returned when a migration combines DML and DDL
ErrDataAndDDL = errors.New("migration combines DDL (Data Definition Language [schema changes]) and data manipulation")

// ErrNonIdempotentDDL is returned when a migration has non-idempotent DDL
ErrNonIdempotentDDL = errors.New("unconditional migration has non-idempotent DDL (Data Definition Language [schema changes])")

ifExistsRE = regexp.MustCompile(`(?i)\bIF (?:NOT )?EXISTS\b`)
)

// CheckScript attempts to validate that an SQL command does not do
// both schema changes (DDL) and data changes. The returned error
Expand Down
49 changes: 28 additions & 21 deletions lsmysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@
func Generate(
name string,
generator func(context.Context, *sql.Tx) string,
opts ...libschema.MigrationOption) libschema.Migration {
opts ...libschema.MigrationOption,
) libschema.Migration {
return mmigration{
MigrationBase: libschema.MigrationBase{
Name: libschema.MigrationName{
Expand All @@ -127,7 +128,8 @@
func Computed(
name string,
action func(context.Context, *sql.Tx) error,
opts ...libschema.MigrationOption) libschema.Migration {
opts ...libschema.MigrationOption,
) libschema.Migration {
return mmigration{
MigrationBase: libschema.MigrationBase{
Name: libschema.MigrationName{
Expand Down Expand Up @@ -172,11 +174,11 @@
}()
if d.Options.SchemaOverride != "" {
if !simpleIdentifierRE.MatchString(d.Options.SchemaOverride) {
return nil, errors.Errorf("Options.SchemaOverride must be a simple identifier, not '%s'", d.Options.SchemaOverride)
return nil, errors.Errorf("options.SchemaOverride must be a simple identifier, not '%s'", d.Options.SchemaOverride)
}
_, err := tx.Exec(`USE ` + d.Options.SchemaOverride)
if err != nil {
return nil, errors.Wrapf(err, "Set search path to %s for %s", d.Options.SchemaOverride, m.Base().Name)
return nil, errors.Wrapf(err, "set search path to %s for %s", d.Options.SchemaOverride, m.Base().Name)

Check warning on line 181 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L181

Added line #L181 was not covered by tests
}
}
pm := m.(*mmigration)
Expand All @@ -196,11 +198,11 @@
err = pm.computed(ctx, tx)
}
if err != nil {
err = errors.Wrapf(err, "Problem with migration %s", m.Base().Name)
err = errors.Wrapf(err, "problem with migration %s", m.Base().Name)
_ = tx.Rollback()
ntx, txerr := d.DB().BeginTx(ctx, d.Options.MigrationTxOptions)
if txerr != nil {
return nil, errors.Wrapf(err, "Tx for saving status for %s also failed with %s", m.Base().Name, txerr)
return nil, errors.Wrapf(err, "tx for saving status for %s also failed with %s", m.Base().Name, txerr)

Check warning on line 205 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L205

Added line #L205 was not covered by tests
}
tx = ntx
}
Expand All @@ -209,7 +211,7 @@
if err == nil {
err = txerr
} else {
err = errors.Wrapf(err, "Save status for %s also failed: %s", m.Base().Name, txerr)
err = errors.Wrapf(err, "save status for %s also failed: %s", m.Base().Name, txerr)

Check warning on line 214 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L214

Added line #L214 was not covered by tests
}
}
return
Expand All @@ -229,7 +231,7 @@
CREATE SCHEMA IF NOT EXISTS %s
`, schema))
if err != nil {
return errors.Wrapf(err, "Could not create libschema schema '%s'", schema)
return errors.Wrapf(err, "could not create libschema schema '%s'", schema)

Check warning on line 234 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L234

Added line #L234 was not covered by tests
}
}
_, err = d.DB().ExecContext(ctx, fmt.Sprintf(`
Expand All @@ -242,7 +244,7 @@
PRIMARY KEY (library, migration)
) ENGINE = InnoDB`, tableName))
if err != nil {
return errors.Wrapf(err, "Could not create libschema migrations table '%s'", tableName)
return errors.Wrapf(err, "could not create libschema migrations table '%s'", tableName)

Check warning on line 247 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L247

Added line #L247 was not covered by tests
}
return nil
}
Expand Down Expand Up @@ -270,20 +272,20 @@
case 2:
schema := s[0]
if !simpleIdentifierRE.MatchString(schema) {
return "", "", "", errors.Errorf("Tracking table schema name must be a simple identifier, not '%s'", schema)
return "", "", "", errors.Errorf("tracking table schema name must be a simple identifier, not '%s'", schema)
}
table := s[1]
if !simpleIdentifierRE.MatchString(table) {
return "", "", "", errors.Errorf("Tracking table table name must be a simple identifier, not '%s'", table)
return "", "", "", errors.Errorf("tracking table table name must be a simple identifier, not '%s'", table)
}
return schema, schema + "." + table, table, nil
case 1:
if !simpleIdentifierRE.MatchString(tableName) {
return "", "", "", errors.Errorf("Tracking table table name must be a simple identifier, not '%s'", tableName)
return "", "", "", errors.Errorf("tracking table table name must be a simple identifier, not '%s'", tableName)
}
return "", tableName, tableName, nil
default:
return "", "", "", errors.Errorf("Tracking table '%s' is not valid", tableName)
return "", "", "", errors.Errorf("tracking table '%s' is not valid", tableName)
}
}

Expand All @@ -309,7 +311,7 @@
VALUES (?, ?, ?, ?, ?, now())`, p.trackingTable(d))
_, err := tx.Exec(q, p.databaseName, m.Base().Name.Library, m.Base().Name.Name, done, estr)
if err != nil {
return errors.Wrapf(err, "Save status for %s", m.Base().Name)
return errors.Wrapf(err, "save status for %s", m.Base().Name)

Check warning on line 314 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L314

Added line #L314 was not covered by tests
}
return nil
}
Expand Down Expand Up @@ -345,7 +347,7 @@
var gotLock int
err = tx.QueryRow(`SELECT GET_LOCK(?, -1)`, p.lockStr).Scan(&gotLock)
if err != nil {
return errors.Wrapf(err, "Could not get lock for libschema migrations")
return errors.Wrapf(err, "could not get lock for libschema migrations")

Check warning on line 350 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L350

Added line #L350 was not covered by tests
}
p.lockTx = tx

Expand Down Expand Up @@ -429,17 +431,22 @@
// It is expected to be called by libschema and is not
// called internally which means that is safe to override
// in types that embed MySQL.
func (p *MySQL) LoadStatus(ctx context.Context, _ *internal.Log, d *libschema.Database) ([]libschema.MigrationName, error) {
func (p *MySQL) LoadStatus(ctx context.Context, _ *internal.Log, d *libschema.Database) (_ []libschema.MigrationName, err error) {
// TODO: DRY
tableName := p.trackingTable(d)
rows, err := d.DB().QueryContext(ctx, fmt.Sprintf(`
SELECT library, migration, done
FROM %s
WHERE db_name = ?`, tableName), p.databaseName)
if err != nil {
return nil, errors.Wrap(err, "Cannot query migration status")
return nil, errors.Wrap(err, "cannot query migration status")

Check warning on line 442 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L442

Added line #L442 was not covered by tests
}
defer rows.Close()
defer func() {
e := rows.Close()
if e != nil && err == nil {
err = errors.Wrap(e, "close scan on migration table")
}

Check warning on line 448 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L447-L448

Added lines #L447 - L448 were not covered by tests
}()
var unknowns []libschema.MigrationName
for rows.Next() {
var (
Expand All @@ -448,7 +455,7 @@
)
err := rows.Scan(&name.Library, &name.Name, &status.Done)
if err != nil {
return nil, errors.Wrap(err, "Cannot scan migration status")
return nil, errors.Wrap(err, "cannot scan migration status")

Check warning on line 458 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L458

Added line #L458 was not covered by tests
}
if m, ok := d.Lookup(name); ok {
m.Base().SetStatus(status)
Expand All @@ -468,13 +475,13 @@
func (p *MySQL) IsMigrationSupported(d *libschema.Database, _ *internal.Log, migration libschema.Migration) error {
m, ok := migration.(*mmigration)
if !ok {
return fmt.Errorf("Non-mysql migration %s registered with mysql migrations", migration.Base().Name)
return fmt.Errorf("non-mysql migration %s registered with mysql migrations", migration.Base().Name)
}
if m.script != nil {
return nil
}
if m.computed != nil {
return nil
}
return errors.Errorf("Migration %s is not supported", m.Name)
return errors.Errorf("migration %s is not supported", m.Name)

Check warning on line 486 in lsmysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

lsmysql/mysql.go#L486

Added line #L486 was not covered by tests
}
2 changes: 1 addition & 1 deletion lsmysql/mysql_repeat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestRepeat(t *testing.T) {
db, err := sql.Open("mysql", dsn)
require.NoError(t, err)
defer func() {
db.Close()
assert.NoError(t, db.Close())
}()

options, cleanup := lstesting.FakeSchema(t, "")
Expand Down
Loading
Loading