diff --git a/.golangci.yml b/.golangci.yml index 48350ee6e..2d4d89158 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,6 @@ +run: + # timeout for analysis, e.g. 30s, 5m, default is 1m + deadline: 2m linters: enable: #- golint @@ -7,14 +10,11 @@ linters: - goconst - gofmt - misspell - #- maligned + - maligned - unparam - nakedret - prealloc #- gosec - disable: - - errcheck - - staticcheck linters-settings: misspell: locale: US diff --git a/database/cassandra/cassandra_test.go b/database/cassandra/cassandra_test.go index 3389f109e..91cd31bf5 100644 --- a/database/cassandra/cassandra_test.go +++ b/database/cassandra/cassandra_test.go @@ -68,9 +68,13 @@ func Test(t *testing.T) { p := &Cassandra{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT table_name from system_schema.tables")) }) } @@ -85,13 +89,17 @@ func TestMigrate(t *testing.T) { p := &Cassandra{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "testks", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT table_name from system_schema.tables")) }) diff --git a/database/clickhouse/clickhouse.go b/database/clickhouse/clickhouse.go index d14e1c543..384742e0f 100644 --- a/database/clickhouse/clickhouse.go +++ b/database/clickhouse/clickhouse.go @@ -204,15 +204,18 @@ func (ch *ClickHouse) ensureVersionTable() (err error) { return nil } -func (ch *ClickHouse) Drop() error { - var ( - query = "SHOW TABLES FROM " + ch.config.DatabaseName - tables, err = ch.conn.Query(query) - ) +func (ch *ClickHouse) Drop() (err error) { + query := "SHOW TABLES FROM " + ch.config.DatabaseName + tables, err := ch.conn.Query(query) + if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() for tables.Next() { var table string if err := tables.Scan(&table); err != nil { diff --git a/database/cockroachdb/cockroachdb.go b/database/cockroachdb/cockroachdb.go index c18edcd7a..8d6c47075 100644 --- a/database/cockroachdb/cockroachdb.go +++ b/database/cockroachdb/cockroachdb.go @@ -150,7 +150,7 @@ func (c *CockroachDb) Close() error { // Locking is done manually with a separate lock table. Implementing advisory locks in CRDB is being discussed // See: https://github.com/cockroachdb/cockroach/issues/13546 func (c *CockroachDb) Lock() error { - err := crdb.ExecuteTx(context.Background(), c.db, nil, func(tx *sql.Tx) error { + err := crdb.ExecuteTx(context.Background(), c.db, nil, func(tx *sql.Tx) (err error) { aid, err := database.GenerateAdvisoryLockId(c.config.DatabaseName) if err != nil { return err @@ -161,7 +161,11 @@ func (c *CockroachDb) Lock() error { if err != nil { return database.Error{OrigErr: err, Err: "failed to fetch migration lock", Query: []byte(query)} } - defer rows.Close() + defer func() { + if errClose := rows.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // If row exists at all, lock is present locked := rows.Next() @@ -267,14 +271,18 @@ func (c *CockroachDb) Version() (version int, dirty bool, err error) { } } -func (c *CockroachDb) Drop() error { +func (c *CockroachDb) Drop() (err error) { // select all tables in current schema query := `SELECT table_name FROM information_schema.tables WHERE table_schema=(SELECT current_schema())` tables, err := c.db.Query(query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // delete one table after another tableNames := make([]string, 0) diff --git a/database/cockroachdb/cockroachdb_test.go b/database/cockroachdb/cockroachdb_test.go index 4f5570e0b..0e7c3b537 100644 --- a/database/cockroachdb/cockroachdb_test.go +++ b/database/cockroachdb/cockroachdb_test.go @@ -7,6 +7,7 @@ import ( "database/sql" "fmt" "github.com/golang-migrate/migrate/v4" + "log" "strings" "testing" ) @@ -38,20 +39,22 @@ var ( func isReady(ctx context.Context, c dktest.ContainerInfo) bool { ip, port, err := c.Port(defaultPort) if err != nil { - fmt.Println("port error:", err) + log.Println("port error:", err) return false } db, err := sql.Open("postgres", fmt.Sprintf("postgres://root@%v:%v?sslmode=disable", ip, port)) if err != nil { - fmt.Println("open error:", err) + log.Println("open error:", err) return false } if err := db.PingContext(ctx); err != nil { - fmt.Println("ping error:", err) + log.Println("ping error:", err) return false } - db.Close() + if err := db.Close(); err != nil { + log.Println("close error:", err) + } return true } @@ -68,7 +71,11 @@ func createDB(t *testing.T, c dktest.ContainerInfo) { if err = db.Ping(); err != nil { t.Fatal(err) } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + t.Error(err) + } + }() if _, err = db.Exec("CREATE DATABASE migrate"); err != nil { t.Fatal(err) @@ -88,7 +95,7 @@ func Test(t *testing.T) { c := &CockroachDb{} d, err := c.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("SELECT 1")) }) @@ -107,12 +114,12 @@ func TestMigrate(t *testing.T) { c := &CockroachDb{} d, err := c.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "migrate", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT 1")) }) @@ -131,7 +138,7 @@ func TestMultiStatement(t *testing.T) { c := &CockroachDb{} d, err := c.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } if err := d.Run(strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);")); err != nil { t.Fatalf("expected err to be nil, got %v", err) @@ -161,7 +168,7 @@ func TestFilterCustomQuery(t *testing.T) { c := &CockroachDb{} _, err = c.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } }) } diff --git a/database/firebird/firebird.go b/database/firebird/firebird.go index 54ac9d0ee..a02fc45ba 100644 --- a/database/firebird/firebird.go +++ b/database/firebird/firebird.go @@ -166,14 +166,18 @@ func (f *Firebird) Version() (version int, dirty bool, err error) { } } -func (f *Firebird) Drop() error { +func (f *Firebird) Drop() (err error) { // select all tables query := `SELECT rdb$relation_name FROM rdb$relations WHERE rdb$view_blr IS NULL AND (rdb$system_flag IS NULL OR rdb$system_flag = 0);` tables, err := f.conn.QueryContext(context.Background(), query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // delete one table after another tableNames := make([]string, 0) diff --git a/database/firebird/firebird_test.go b/database/firebird/firebird_test.go index 91ef8af7c..c6a8f1f1c 100644 --- a/database/firebird/firebird_test.go +++ b/database/firebird/firebird_test.go @@ -5,6 +5,8 @@ import ( "database/sql" sqldriver "database/sql/driver" "fmt" + "log" + "github.com/golang-migrate/migrate/v4" "io" "strings" @@ -36,7 +38,6 @@ var ( }, } specs = []dktesting.ContainerSpec{ - {ImageName: "jacobalberty/firebird:2.5-ss", Options: opts}, {ImageName: "jacobalberty/firebird:3.0", Options: opts}, } ) @@ -54,15 +55,20 @@ func isReady(ctx context.Context, c dktest.ContainerInfo) bool { db, err := sql.Open("firebirdsql", fbConnectionString(ip, port)) if err != nil { + log.Println("open error:", err) return false } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Println("close error:", err) + } + }() if err = db.PingContext(ctx); err != nil { switch err { case sqldriver.ErrBadConn, io.EOF: return false default: - fmt.Println(err) + log.Println(err) } return false } @@ -81,9 +87,13 @@ func Test(t *testing.T) { p := &Firebird{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT Count(*) FROM rdb$relations")) }) } @@ -99,12 +109,16 @@ func TestMigrate(t *testing.T) { p := &Firebird{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "firebirdsql", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT Count(*) FROM rdb$relations")) }) @@ -121,9 +135,13 @@ func TestErrorParsing(t *testing.T) { p := &Firebird{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() wantErr := `migration failed in line 0: CREATE TABLEE foo (foo varchar(40)); (details: Dynamic SQL Error SQL error code = -104 @@ -151,9 +169,13 @@ func TestFilterCustomQuery(t *testing.T) { p := &Firebird{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() }) } @@ -168,8 +190,13 @@ func Test_Lock(t *testing.T) { p := &Firebird{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT Count(*) FROM rdb$relations")) diff --git a/database/mongodb/mongodb_test.go b/database/mongodb/mongodb_test.go index 60721ec84..d2ba64535 100644 --- a/database/mongodb/mongodb_test.go +++ b/database/mongodb/mongodb_test.go @@ -4,6 +4,9 @@ import ( "bytes" "context" "fmt" + + "log" + "github.com/golang-migrate/migrate/v4" "io" "os" @@ -50,13 +53,18 @@ func isReady(ctx context.Context, c dktest.ContainerInfo) bool { if err != nil { return false } - defer client.Disconnect(ctx) + defer func() { + if err := client.Disconnect(ctx); err != nil { + log.Println("close error:", err) + } + }() + if err = client.Ping(ctx, nil); err != nil { switch err { case io.EOF: return false default: - fmt.Println(err) + log.Println(err) } return false } @@ -74,9 +82,13 @@ func Test(t *testing.T) { p := &Mongo{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.TestNilVersion(t, d) //TestLockAndUnlock(t, d) driver doesn't support lock on database level dt.TestRun(t, d, bytes.NewReader([]byte(`[{"insert":"hello","documents":[{"wild":"world"}]}]`))) @@ -96,12 +108,16 @@ func TestMigrate(t *testing.T) { p := &Mongo{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte(`[{"insert":"hello","documents":[{"wild":"world"}]}]`)) }) @@ -118,13 +134,17 @@ func TestWithAuth(t *testing.T) { p := &Mongo{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() createUserCMD := []byte(`[{"createUser":"deminem","pwd":"gogo","roles":[{"role":"readWrite","db":"testMigration"}]}]`) err = d.Run(bytes.NewReader(createUserCMD)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } testcases := []struct { name string @@ -144,9 +164,13 @@ func TestWithAuth(t *testing.T) { mc := &Mongo{} d, err := mc.Open(fmt.Sprintf(tcase.connectUri, ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() err = d.Run(bytes.NewReader(insertCMD)) switch { case tcase.isErrorExpected && err == nil: @@ -172,20 +196,20 @@ func TestTransaction(t *testing.T) { client, err := mongo.Connect(context.TODO(), mongoConnectionString(ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } err = client.Ping(context.TODO(), nil) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } //rs.initiate() err = client.Database("admin").RunCommand(context.TODO(), bson.D{bson.E{Key: "replSetInitiate", Value: bson.D{}}}).Err() if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } err = waitForReplicaInit(client) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } d, err := WithInstance(client, &Config{ DatabaseName: "testMigration", @@ -193,7 +217,11 @@ func TestTransaction(t *testing.T) { if err != nil { t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() //We have to create collection //transactions don't support operations with creating new dbs, collections //Unique index need for checking transaction aborting @@ -211,7 +239,7 @@ func TestTransaction(t *testing.T) { }]`) err = d.Run(bytes.NewReader(insertCMD)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } testcases := []struct { name string @@ -248,29 +276,33 @@ func TestTransaction(t *testing.T) { t.Run(tcase.name, func(t *testing.T) { client, err := mongo.Connect(context.TODO(), mongoConnectionString(ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } err = client.Ping(context.TODO(), nil) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } d, err := WithInstance(client, &Config{ DatabaseName: "testMigration", TransactionMode: true, }) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() runErr := d.Run(bytes.NewReader(tcase.cmds)) if runErr != nil { if !tcase.isErrorExpected { - t.Fatalf("%v", runErr) + t.Fatal(runErr) } } documentsCount, err := client.Database("testMigration").Collection("hello").Count(context.TODO(), bson.M{}) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } if tcase.documentsCount != documentsCount { t.Fatalf("expected %d and actual %d documents count not equal. run migration error:%s", tcase.documentsCount, documentsCount, runErr) diff --git a/database/mysql/mysql.go b/database/mysql/mysql.go index ccd6f3703..dbb81a347 100644 --- a/database/mysql/mysql.go +++ b/database/mysql/mysql.go @@ -164,11 +164,14 @@ func (m *Mysql) Open(url string) (database.Driver, error) { insecureSkipVerify = x } - mysql.RegisterTLSConfig(ctls, &tls.Config{ + err = mysql.RegisterTLSConfig(ctls, &tls.Config{ RootCAs: rootCertPool, Certificates: clientCert, InsecureSkipVerify: insecureSkipVerify, }) + if err != nil { + return nil, err + } } } @@ -272,14 +275,18 @@ func (m *Mysql) SetVersion(version int, dirty bool) error { query := "TRUNCATE `" + m.config.MigrationsTable + "`" if _, err := tx.ExecContext(context.Background(), query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } if version >= 0 { query := "INSERT INTO `" + m.config.MigrationsTable + "` (version, dirty) VALUES (?, ?)" if _, err := tx.ExecContext(context.Background(), query, version, dirty); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } } @@ -311,14 +318,18 @@ func (m *Mysql) Version() (version int, dirty bool, err error) { } } -func (m *Mysql) Drop() error { +func (m *Mysql) Drop() (err error) { // select all tables query := `SHOW TABLES LIKE '%'` tables, err := m.conn.QueryContext(context.Background(), query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // delete one table after another tableNames := make([]string, 0) diff --git a/database/mysql/mysql_test.go b/database/mysql/mysql_test.go index a71383484..e5f73ff2a 100644 --- a/database/mysql/mysql_test.go +++ b/database/mysql/mysql_test.go @@ -5,6 +5,8 @@ import ( "database/sql" sqldriver "database/sql/driver" "fmt" + "log" + "github.com/golang-migrate/migrate/v4" "net/url" "testing" @@ -47,7 +49,11 @@ func isReady(ctx context.Context, c dktest.ContainerInfo) bool { if err != nil { return false } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Println("close error:", err) + } + }() if err = db.PingContext(ctx); err != nil { switch err { case sqldriver.ErrBadConn, mysql.ErrInvalidConn: @@ -74,9 +80,13 @@ func Test(t *testing.T) { p := &Mysql{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT 1")) // check ensureVersionTable @@ -103,13 +113,17 @@ func TestMigrate(t *testing.T) { p := &Mysql{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "public", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT 1")) @@ -135,7 +149,7 @@ func TestLockWorks(t *testing.T) { p := &Mysql{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("SELECT 1")) diff --git a/database/postgres/postgres.go b/database/postgres/postgres.go index 96fc69023..4c0cc1c40 100644 --- a/database/postgres/postgres.go +++ b/database/postgres/postgres.go @@ -255,14 +255,18 @@ func (p *Postgres) SetVersion(version int, dirty bool) error { query := `TRUNCATE ` + pq.QuoteIdentifier(p.config.MigrationsTable) if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } if version >= 0 { query = `INSERT INTO ` + pq.QuoteIdentifier(p.config.MigrationsTable) + ` (version, dirty) VALUES ($1, $2)` if _, err := tx.Exec(query, version, dirty); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } } @@ -294,14 +298,18 @@ func (p *Postgres) Version() (version int, dirty bool, err error) { } } -func (p *Postgres) Drop() error { +func (p *Postgres) Drop() (err error) { // select all tables in current schema query := `SELECT table_name FROM information_schema.tables WHERE table_schema=(SELECT current_schema()) AND table_type='BASE TABLE'` tables, err := p.conn.QueryContext(context.Background(), query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // delete one table after another tableNames := make([]string, 0) diff --git a/database/postgres/postgres_test.go b/database/postgres/postgres_test.go index ad2d91f6b..960896801 100644 --- a/database/postgres/postgres_test.go +++ b/database/postgres/postgres_test.go @@ -7,6 +7,8 @@ import ( "database/sql" sqldriver "database/sql/driver" "fmt" + "log" + "github.com/golang-migrate/migrate/v4" "io" "strconv" @@ -47,13 +49,17 @@ func isReady(ctx context.Context, c dktest.ContainerInfo) bool { if err != nil { return false } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Println("close error:", err) + } + }() if err = db.PingContext(ctx); err != nil { switch err { case sqldriver.ErrBadConn, io.EOF: return false default: - fmt.Println(err) + log.Println(err) } return false } @@ -72,9 +78,13 @@ func Test(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT 1")) }) } @@ -90,12 +100,16 @@ func TestMigrate(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "postgres", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT 1")) }) @@ -112,9 +126,13 @@ func TestMultiStatement(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() if err := d.Run(strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);")); err != nil { t.Fatalf("expected err to be nil, got %v", err) } @@ -141,9 +159,13 @@ func TestErrorParsing(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() wantErr := `migration failed: syntax error at or near "TABLEE" (column 37) in line 1: CREATE TABLE foo ` + `(foo text); CREATE TABLEE bar (bar text); (details: pq: syntax error at or near "TABLEE")` @@ -166,9 +188,13 @@ func TestFilterCustomQuery(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() }) } @@ -183,9 +209,13 @@ func TestWithSchema(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Fatal(err) + } + }() // create foobar schema if err := d.Run(strings.NewReader("CREATE SCHEMA foobar AUTHORIZATION postgres")); err != nil { @@ -198,9 +228,13 @@ func TestWithSchema(t *testing.T) { // re-connect using that schema d2, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=foobar", ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d2.Close() + defer func() { + if err := d2.Close(); err != nil { + t.Fatal(err) + } + }() version, _, err := d2.Version() if err != nil { @@ -244,9 +278,13 @@ func TestParallelSchema(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() // create foo and bar schemas if err := d.Run(strings.NewReader("CREATE SCHEMA foo AUTHORIZATION postgres")); err != nil { @@ -259,15 +297,23 @@ func TestParallelSchema(t *testing.T) { // re-connect using that schemas dfoo, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=foo", ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer dfoo.Close() + defer func() { + if err := dfoo.Close(); err != nil { + t.Error(err) + } + }() dbar, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=bar", ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer dbar.Close() + defer func() { + if err := dbar.Close(); err != nil { + t.Error(err) + } + }() if err := dfoo.Lock(); err != nil { t.Fatal(err) @@ -302,7 +348,7 @@ func TestPostgres_Lock(t *testing.T) { p := &Postgres{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("SELECT 1")) @@ -349,7 +395,11 @@ func TestWithInstance_Concurrent(t *testing.T) { if err != nil { t.Fatal(err) } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + t.Error(err) + } + }() db.SetMaxIdleConns(concurrency) db.SetMaxOpenConns(concurrency) diff --git a/database/ql/ql.go b/database/ql/ql.go index 004efc4fa..e67e2335d 100644 --- a/database/ql/ql.go +++ b/database/ql/ql.go @@ -125,13 +125,17 @@ func (m *Ql) Open(url string) (database.Driver, error) { func (m *Ql) Close() error { return m.db.Close() } -func (m *Ql) Drop() error { +func (m *Ql) Drop() (err error) { query := `SELECT Name FROM __Table` tables, err := m.db.Query(query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() tableNames := make([]string, 0) for tables.Next() { var tableName string @@ -185,7 +189,9 @@ func (m *Ql) executeQuery(query string) error { return &database.Error{OrigErr: err, Err: "transaction start failed"} } if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } if err := tx.Commit(); err != nil { @@ -207,7 +213,9 @@ func (m *Ql) SetVersion(version int, dirty bool) error { if version >= 0 { query := fmt.Sprintf(`INSERT INTO %s (version, dirty) VALUES (%d, %t)`, m.config.MigrationsTable, version, dirty) if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } } diff --git a/database/ql/ql_test.go b/database/ql/ql_test.go index 49e629cce..57be27009 100644 --- a/database/ql/ql_test.go +++ b/database/ql/ql_test.go @@ -20,14 +20,16 @@ func Test(t *testing.T) { return } defer func() { - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + t.Fatal(err) + } }() t.Logf("DB path : %s\n", filepath.Join(dir, "ql.db")) p := &Ql{} addr := fmt.Sprintf("ql://%s", filepath.Join(dir, "ql.db")) d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } db, err := sql.Open("ql", filepath.Join(dir, "ql.db")) @@ -48,7 +50,9 @@ func TestMigrate(t *testing.T) { return } defer func() { - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } }() t.Logf("DB path : %s\n", filepath.Join(dir, "ql.db")) @@ -64,14 +68,14 @@ func TestMigrate(t *testing.T) { driver, err := WithInstance(db, &Config{}) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } m, err := migrate.NewWithDatabaseInstance( "file://./examples/migrations", "ql", driver) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("CREATE TABLE t (Qty int, Name string);")) } diff --git a/database/redshift/redshift.go b/database/redshift/redshift.go index b772bba4f..3d018bde1 100644 --- a/database/redshift/redshift.go +++ b/database/redshift/redshift.go @@ -212,14 +212,18 @@ func (p *Redshift) SetVersion(version int, dirty bool) error { query := `DELETE FROM "` + p.config.MigrationsTable + `"` if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } if version >= 0 { query = `INSERT INTO "` + p.config.MigrationsTable + `" (version, dirty) VALUES ($1, $2)` if _, err := tx.Exec(query, version, dirty); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } } @@ -251,14 +255,18 @@ func (p *Redshift) Version() (version int, dirty bool, err error) { } } -func (p *Redshift) Drop() error { +func (p *Redshift) Drop() (err error) { // select all tables in current schema query := `SELECT table_name FROM information_schema.tables WHERE table_schema=(SELECT current_schema()) AND table_type='BASE TABLE'` tables, err := p.conn.QueryContext(context.Background(), query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() // delete one table after another tableNames := make([]string, 0) diff --git a/database/redshift/redshift_test.go b/database/redshift/redshift_test.go index 0fa58651d..3cacfbcd8 100644 --- a/database/redshift/redshift_test.go +++ b/database/redshift/redshift_test.go @@ -8,6 +8,8 @@ import ( "database/sql" sqldriver "database/sql/driver" "fmt" + "log" + "github.com/golang-migrate/migrate/v4" "io" "strconv" @@ -54,13 +56,17 @@ func isReady(ctx context.Context, c dktest.ContainerInfo) bool { if err != nil { return false } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Println("close error:", err) + } + }() if err = db.PingContext(ctx); err != nil { switch err { case sqldriver.ErrBadConn, io.EOF: return false default: - fmt.Println(err) + log.Println(err) } return false } @@ -79,9 +85,13 @@ func Test(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() dt.Test(t, d, []byte("SELECT 1")) }) } @@ -97,12 +107,16 @@ func TestMigrate(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "postgres", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT 1")) }) @@ -119,9 +133,13 @@ func TestMultiStatement(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() if err := d.Run(bytes.NewReader([]byte("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);"))); err != nil { t.Fatalf("expected err to be nil, got %v", err) } @@ -148,9 +166,13 @@ func TestErrorParsing(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() wantErr := `migration failed: syntax error at or near "TABLEE" (column 37) in line 1: CREATE TABLE foo ` + `(foo text); CREATE TABLEE bar (bar text); (details: pq: syntax error at or near "TABLEE")` @@ -173,9 +195,13 @@ func TestFilterCustomQuery(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() }) } @@ -190,9 +216,13 @@ func TestWithSchema(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d.Close() + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() // create foobar schema if err := d.Run(bytes.NewReader([]byte("CREATE SCHEMA foobar AUTHORIZATION postgres"))); err != nil { @@ -205,9 +235,13 @@ func TestWithSchema(t *testing.T) { // re-connect using that schema d2, err := p.Open(fmt.Sprintf("postgres://postgres@%v:%v/postgres?sslmode=disable&search_path=foobar", ip, port)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } - defer d2.Close() + defer func() { + if err := d2.Close(); err != nil { + t.Error(err) + } + }() version, _, err := d2.Version() if err != nil { @@ -255,7 +289,7 @@ func TestRedshift_Lock(t *testing.T) { p := &Redshift{} d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("SELECT 1")) diff --git a/database/spanner/spanner_test.go b/database/spanner/spanner_test.go index b9cb52b13..2e04fc6af 100644 --- a/database/spanner/spanner_test.go +++ b/database/spanner/spanner_test.go @@ -24,7 +24,7 @@ func Test(t *testing.T) { addr := fmt.Sprintf("spanner://%s", db) d, err := s.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("SELECT 1")) } @@ -43,11 +43,11 @@ func TestMigrate(t *testing.T) { addr := fmt.Sprintf("spanner://%s", db) d, err := s.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", db, d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("SELECT 1")) } diff --git a/database/sqlite3/sqlite3.go b/database/sqlite3/sqlite3.go index 4826448a5..9cedc6bf2 100644 --- a/database/sqlite3/sqlite3.go +++ b/database/sqlite3/sqlite3.go @@ -118,13 +118,17 @@ func (m *Sqlite) Close() error { return m.db.Close() } -func (m *Sqlite) Drop() error { +func (m *Sqlite) Drop() (err error) { query := `SELECT name FROM sqlite_master WHERE type = 'table';` tables, err := m.db.Query(query) if err != nil { return &database.Error{OrigErr: err, Query: []byte(query)} } - defer tables.Close() + defer func() { + if errClose := tables.Close(); errClose != nil { + err = multierror.Append(err, errClose) + } + }() tableNames := make([]string, 0) for tables.Next() { var tableName string @@ -185,7 +189,9 @@ func (m *Sqlite) executeQuery(query string) error { return &database.Error{OrigErr: err, Err: "transaction start failed"} } if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } if err := tx.Commit(); err != nil { @@ -208,7 +214,9 @@ func (m *Sqlite) SetVersion(version int, dirty bool) error { if version >= 0 { query := fmt.Sprintf(`INSERT INTO %s (version, dirty) VALUES (%d, '%t')`, m.config.MigrationsTable, version, dirty) if _, err := tx.Exec(query); err != nil { - tx.Rollback() + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } return &database.Error{OrigErr: err, Query: []byte(query)} } } diff --git a/database/sqlite3/sqlite3_test.go b/database/sqlite3/sqlite3_test.go index 648141c92..3c3f24d4d 100644 --- a/database/sqlite3/sqlite3_test.go +++ b/database/sqlite3/sqlite3_test.go @@ -20,14 +20,16 @@ func Test(t *testing.T) { return } defer func() { - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } }() t.Logf("DB path : %s\n", filepath.Join(dir, "sqlite3.db")) p := &Sqlite{} addr := fmt.Sprintf("sqlite3://%s", filepath.Join(dir, "sqlite3.db")) d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.Test(t, d, []byte("CREATE TABLE t (Qty int, Name string);")) } @@ -38,14 +40,16 @@ func TestMigrate(t *testing.T) { return } defer func() { - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } }() t.Logf("DB path : %s\n", filepath.Join(dir, "sqlite3.db")) p := &Sqlite{} addr := fmt.Sprintf("sqlite3://%s", filepath.Join(dir, "sqlite3.db")) d, err := p.Open(addr) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } db, err := sql.Open("sqlite3", filepath.Join(dir, "sqlite3.db")) @@ -59,7 +63,7 @@ func TestMigrate(t *testing.T) { }() driver, err := WithInstance(db, &Config{}) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } if err := d.Drop(); err != nil { t.Fatal(err) @@ -69,7 +73,7 @@ func TestMigrate(t *testing.T) { "file://./examples/migrations", "ql", driver) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("CREATE TABLE t (Qty int, Name string);")) } @@ -80,7 +84,9 @@ func TestMigrationTable(t *testing.T) { return } defer func() { - os.RemoveAll(dir) + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } }() t.Logf("DB path : %s\n", filepath.Join(dir, "sqlite3.db")) @@ -100,22 +106,22 @@ func TestMigrationTable(t *testing.T) { } driver, err := WithInstance(db, config) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } m, err := migrate.NewWithDatabaseInstance( "file://./examples/migrations", "ql", driver) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } t.Log("UP") err = m.Up() if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } _, err = db.Query(fmt.Sprintf("SELECT * FROM %s", config.MigrationsTable)) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } } diff --git a/database/stub/stub_test.go b/database/stub/stub_test.go index fdf74c748..aa95ac0d1 100644 --- a/database/stub/stub_test.go +++ b/database/stub/stub_test.go @@ -36,7 +36,7 @@ func TestMigrate(t *testing.T) { srcDrv.(*stub.Stub).Migrations = stubMigrations m, err := migrate.NewWithInstance("stub", srcDrv, "", d) if err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } dt.TestMigrate(t, m, []byte("/* foobar migration */")) diff --git a/database/testing/migrate_testing.go b/database/testing/migrate_testing.go index 944468ef4..fd05851e8 100644 --- a/database/testing/migrate_testing.go +++ b/database/testing/migrate_testing.go @@ -34,6 +34,6 @@ func TestMigrateDrop(t *testing.T, m *migrate.Migrate) { func TestMigrateUp(t *testing.T, m *migrate.Migrate) { t.Log("UP") if err := m.Up(); err != nil { - t.Fatalf("%v", err) + t.Fatal(err) } } diff --git a/database/testing/testing.go b/database/testing/testing.go index b227c6088..3304443fa 100644 --- a/database/testing/testing.go +++ b/database/testing/testing.go @@ -5,6 +5,7 @@ package testing import ( "bytes" + "errors" "fmt" "io" "testing" @@ -39,7 +40,9 @@ func TestNilVersion(t *testing.T, d database.Driver) { func TestLockAndUnlock(t *testing.T, d database.Driver) { // add a timeout, in case there is a deadlock - done := make(chan bool, 1) + done := make(chan struct{}) + errs := make(chan error) + go func() { timeout := time.After(15 * time.Second) for { @@ -47,36 +50,52 @@ func TestLockAndUnlock(t *testing.T, d database.Driver) { case <-done: return case <-timeout: - t.Fatal(fmt.Sprintf("Timeout after 15 seconds. Looks like a deadlock in Lock/UnLock.\n%#v", d)) + errs <- fmt.Errorf("Timeout after 15 seconds. Looks like a deadlock in Lock/UnLock.\n%#v", d) + return } } }() - defer func() { - done <- true - }() // run the locking test ... + go func() { + if err := d.Lock(); err != nil { + errs <- err + return + } - if err := d.Lock(); err != nil { - t.Fatal(err) - } + // try to acquire lock again + if err := d.Lock(); err == nil { + errs <- errors.New("lock: expected err not to be nil") + return + } - // try to acquire lock again - if err := d.Lock(); err == nil { - t.Fatal("Lock: expected err not to be nil") - } + // unlock + if err := d.Unlock(); err != nil { + errs <- err + return + } - // unlock - if err := d.Unlock(); err != nil { - t.Fatal(err) - } + // try to lock + if err := d.Lock(); err != nil { + errs <- err + return + } + if err := d.Unlock(); err != nil { + errs <- err + return + } + // notify everyone + close(done) + }() - // try to lock - if err := d.Lock(); err != nil { - t.Fatal(err) - } - if err := d.Unlock(); err != nil { - t.Fatal(err) + // wait for done or any error + for { + select { + case <-done: + return + case err := <-errs: + t.Fatal(err) + } } } diff --git a/database/util.go b/database/util.go index f0ffd615f..976ad3534 100644 --- a/database/util.go +++ b/database/util.go @@ -6,14 +6,14 @@ import ( "strings" ) -const advisoryLockIdSalt uint = 1486364155 +const advisoryLockIDSalt uint = 1486364155 // GenerateAdvisoryLockId inspired by rails migrations, see https://goo.gl/8o9bCT -func GenerateAdvisoryLockId(databaseName string, additionalNames ...string) (string, error) { +func GenerateAdvisoryLockId(databaseName string, additionalNames ...string) (string, error) { // nolint: golint if len(additionalNames) > 0 { databaseName = strings.Join(append(additionalNames, databaseName), "\x00") } sum := crc32.ChecksumIEEE([]byte(databaseName)) - sum = sum * uint32(advisoryLockIdSalt) - return fmt.Sprintf("%v", sum), nil + sum = sum * uint32(advisoryLockIDSalt) + return fmt.Sprint(sum), nil } diff --git a/internal/cli/commands.go b/internal/cli/commands.go index fabd8bfb5..ab2b0952d 100644 --- a/internal/cli/commands.go +++ b/internal/cli/commands.go @@ -80,7 +80,10 @@ func createCmd(dir string, startTime time.Time, format string, name string, ext } } - os.MkdirAll(dir, os.ModePerm) + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + log.fatalErr(err) + } + createFile(base + "up" + ext) createFile(base + "down" + ext) } diff --git a/internal/cli/main.go b/internal/cli/main.go index 361ae8eca..c975f6ed8 100644 --- a/internal/cli/main.go +++ b/internal/cli/main.go @@ -89,7 +89,9 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n") migrater, migraterErr := migrate.New(*sourcePtr, *databasePtr) defer func() { if migraterErr == nil { - migrater.Close() + if _, err := migrater.Close(); err != nil { + log.Println(err) + } } }() if migraterErr == nil { @@ -123,7 +125,9 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n") formatPtr := createFlagSet.String("format", defaultTimeFormat, `The Go time format string to use. If the string "unix" or "unixNano" is specified, then the seconds or nanoseconds since January 1, 1970 UTC respectively will be used. Caution, due to the behavior of time.Time.Format(), invalid format strings will not error`) createFlagSet.BoolVar(&seq, "seq", seq, "Use sequential numbers instead of timestamps (default: false)") createFlagSet.IntVar(&seqDigits, "digits", seqDigits, "The number of digits to use in sequences (default: 6)") - createFlagSet.Parse(args) + if err := createFlagSet.Parse(args); err != nil { + log.Println(err) + } if createFlagSet.NArg() == 0 { log.fatal("error: please specify name") diff --git a/migrate.go b/migrate.go index ba7d329fd..a4a694b40 100644 --- a/migrate.go +++ b/migrate.go @@ -7,6 +7,7 @@ package migrate import ( "errors" "fmt" + "github.com/hashicorp/go-multierror" "os" "sync" "time" @@ -64,11 +65,11 @@ type Migrate struct { // GracefulStop accepts `true` and will stop executing migrations // as soon as possible at a safe break point, so that the database // is not corrupted. - GracefulStop chan bool - isGracefulStop bool + GracefulStop chan bool + isLockedMu *sync.Mutex - isLockedMu *sync.Mutex - isLocked bool + isGracefulStop bool + isLocked bool // PrefetchMigrations defaults to DefaultPrefetchMigrations, // but can be set per Migrate instance. @@ -81,28 +82,28 @@ type Migrate struct { // New returns a new Migrate instance from a source URL and a database URL. // The URL scheme is defined by each driver. -func New(sourceUrl, databaseUrl string) (*Migrate, error) { +func New(sourceURL, databaseURL string) (*Migrate, error) { m := newCommon() - sourceName, err := sourceSchemeFromUrl(sourceUrl) + sourceName, err := sourceSchemeFromURL(sourceURL) if err != nil { return nil, err } m.sourceName = sourceName - databaseName, err := databaseSchemeFromUrl(databaseUrl) + databaseName, err := databaseSchemeFromURL(databaseURL) if err != nil { return nil, err } m.databaseName = databaseName - sourceDrv, err := source.Open(sourceUrl) + sourceDrv, err := source.Open(sourceURL) if err != nil { return nil, err } m.sourceDrv = sourceDrv - databaseDrv, err := database.Open(databaseUrl) + databaseDrv, err := database.Open(databaseURL) if err != nil { return nil, err } @@ -115,10 +116,10 @@ func New(sourceUrl, databaseUrl string) (*Migrate, error) { // and an existing database instance. The source URL scheme is defined by each driver. // Use any string that can serve as an identifier during logging as databaseName. // You are responsible for closing the underlying database client if necessary. -func NewWithDatabaseInstance(sourceUrl string, databaseName string, databaseInstance database.Driver) (*Migrate, error) { +func NewWithDatabaseInstance(sourceURL string, databaseName string, databaseInstance database.Driver) (*Migrate, error) { m := newCommon() - sourceName, err := schemeFromUrl(sourceUrl) + sourceName, err := schemeFromURL(sourceURL) if err != nil { return nil, err } @@ -126,7 +127,7 @@ func NewWithDatabaseInstance(sourceUrl string, databaseName string, databaseInst m.databaseName = databaseName - sourceDrv, err := source.Open(sourceUrl) + sourceDrv, err := source.Open(sourceURL) if err != nil { return nil, err } @@ -141,10 +142,10 @@ func NewWithDatabaseInstance(sourceUrl string, databaseName string, databaseInst // and a database URL. The database URL scheme is defined by each driver. // Use any string that can serve as an identifier during logging as sourceName. // You are responsible for closing the underlying source client if necessary. -func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, databaseUrl string) (*Migrate, error) { +func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, databaseURL string) (*Migrate, error) { m := newCommon() - databaseName, err := schemeFromUrl(databaseUrl) + databaseName, err := schemeFromURL(databaseURL) if err != nil { return nil, err } @@ -152,7 +153,7 @@ func NewWithSourceInstance(sourceName string, sourceInstance source.Driver, data m.sourceName = sourceName - databaseDrv, err := database.Open(databaseUrl) + databaseDrv, err := database.Open(databaseURL) if err != nil { return nil, err } @@ -347,7 +348,11 @@ func (m *Migrate) Run(migration ...*Migration) error { } ret <- migr - go migr.Buffer() + go func(migr *Migration) { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }(migr) } }() @@ -434,7 +439,12 @@ func (m *Migrate) read(from int, to int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() + from = int(firstVersion) } @@ -457,7 +467,12 @@ func (m *Migrate) read(from int, to int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() + from = int(next) } @@ -478,7 +493,12 @@ func (m *Migrate) read(from int, to int, ret chan<- interface{}) { return } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() + return } else if err != nil { @@ -493,7 +513,12 @@ func (m *Migrate) read(from int, to int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() + from = int(prev) } } @@ -541,7 +566,11 @@ func (m *Migrate) readUp(from int, limit int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() from = int(firstVersion) count++ continue @@ -585,7 +614,11 @@ func (m *Migrate) readUp(from int, limit int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() from = int(next) count++ } @@ -646,7 +679,11 @@ func (m *Migrate) readDown(from int, limit int, ret chan<- interface{}) { return } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() count++ } @@ -667,7 +704,11 @@ func (m *Migrate) readDown(from int, limit int, ret chan<- interface{}) { } ret <- migr - go migr.Buffer() + go func() { + if err := migr.Buffer(); err != nil { + m.logErr(err) + } + }() from = int(prev) count++ } @@ -732,11 +773,15 @@ func (m *Migrate) runMigrations(ret <-chan interface{}) error { // versionExists checks the source if either the up or down migration for // the specified migration version exists. -func (m *Migrate) versionExists(version uint) error { +func (m *Migrate) versionExists(version uint) (result error) { // try up migration first up, _, err := m.sourceDrv.ReadUp(version) if err == nil { - defer up.Close() + defer func() { + if errClose := up.Close(); errClose != nil { + result = multierror.Append(result, errClose) + } + }() } if os.IsExist(err) { return nil @@ -747,7 +792,11 @@ func (m *Migrate) versionExists(version uint) error { // then try down migration down, _, err := m.sourceDrv.ReadDown(version) if err == nil { - defer down.Close() + defer func() { + if errClose := down.Close(); errClose != nil { + result = multierror.Append(result, errClose) + } + }() } if os.IsExist(err) { return nil @@ -919,3 +968,10 @@ func (m *Migrate) logVerbosePrintf(format string, v ...interface{}) { m.Log.Printf(format, v...) } } + +// logErr writes error to m.Log if not nil +func (m *Migrate) logErr(err error) { + if m.Log != nil { + m.Log.Printf("error: %v", err) + } +} diff --git a/migrate_test.go b/migrate_test.go index 15891774c..0cb5f236b 100644 --- a/migrate_test.go +++ b/migrate_test.go @@ -108,7 +108,11 @@ func ExampleNewWithDatabaseInstance() { if err != nil { log.Fatal(err) } - defer db.Close() + defer func() { + if err := db.Close(); err != nil { + log.Fatal(err) + } + }() // Create driver instance from db. // Check each driver if it supports the WithInstance function. diff --git a/migration.go b/migration.go index 069e7f038..704fef49e 100644 --- a/migration.go +++ b/migration.go @@ -129,7 +129,9 @@ func (m *Migration) Buffer() error { // start reading from body, peek won't move the read pointer though // poor man's solution? - b.Peek(int(m.BufferSize)) + if _, err := b.Peek(int(m.BufferSize)); err != nil && err != io.EOF { + return err + } m.FinishedBuffering = time.Now() @@ -145,10 +147,14 @@ func (m *Migration) Buffer() error { // close bufferWriter so Buffer knows that there is no // more data coming - m.bufferWriter.Close() + if err := m.bufferWriter.Close(); err != nil { + return err + } // it's safe to close the Body too - m.Body.Close() + if err := m.Body.Close(); err != nil { + return err + } return nil } diff --git a/source/file/file.go b/source/file/file.go index 1f5b88282..a0c641931 100644 --- a/source/file/file.go +++ b/source/file/file.go @@ -84,27 +84,24 @@ func (f *File) Close() error { } func (f *File) First() (version uint, err error) { - if v, ok := f.migrations.First(); !ok { - return 0, &os.PathError{Op: "first", Path: f.path, Err: os.ErrNotExist} - } else { + if v, ok := f.migrations.First(); ok { return v, nil } + return 0, &os.PathError{Op: "first", Path: f.path, Err: os.ErrNotExist} } func (f *File) Prev(version uint) (prevVersion uint, err error) { - if v, ok := f.migrations.Prev(version); !ok { - return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: f.path, Err: os.ErrNotExist} - } else { + if v, ok := f.migrations.Prev(version); ok { return v, nil } + return 0, &os.PathError{Op: fmt.Sprintf("prev for version %v", version), Path: f.path, Err: os.ErrNotExist} } func (f *File) Next(version uint) (nextVersion uint, err error) { - if v, ok := f.migrations.Next(version); !ok { - return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: f.path, Err: os.ErrNotExist} - } else { + if v, ok := f.migrations.Next(version); ok { return v, nil } + return 0, &os.PathError{Op: fmt.Sprintf("next for version %v", version), Path: f.path, Err: os.ErrNotExist} } func (f *File) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) { diff --git a/source/file/file_test.go b/source/file/file_test.go index c9cfe61cf..6536faaea 100644 --- a/source/file/file_test.go +++ b/source/file/file_test.go @@ -16,7 +16,11 @@ func Test(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpDir) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() // write files that meet driver test requirements mustWriteFile(t, tmpDir, "1_foobar.up.sql", "1 up") @@ -46,7 +50,11 @@ func TestOpen(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpDir) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() mustWriteFile(t, tmpDir, "1_foobar.up.sql", "") mustWriteFile(t, tmpDir, "1_foobar.down.sql", "") @@ -67,13 +75,22 @@ func TestOpenWithRelativePath(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpDir) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() wd, err := os.Getwd() if err != nil { t.Fatal(err) } - defer os.Chdir(wd) // rescue working dir after we are done + defer func() { + // rescue working dir after we are done + if err := os.Chdir(wd); err != nil { + t.Log(err) + } + }() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) @@ -130,7 +147,11 @@ func TestOpenWithDuplicateVersion(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpDir) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() mustWriteFile(t, tmpDir, "1_foo.up.sql", "") // 1 up mustWriteFile(t, tmpDir, "1_bar.up.sql", "") // 1 up @@ -147,7 +168,11 @@ func TestClose(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.RemoveAll(tmpDir) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() f := &File{} d, err := f.Open("file://" + tmpDir) @@ -182,18 +207,29 @@ func mustCreateBenchmarkDir(t *testing.B) (dir string) { func BenchmarkOpen(b *testing.B) { dir := mustCreateBenchmarkDir(b) - defer os.RemoveAll(dir) + defer func() { + if err := os.RemoveAll(dir); err != nil { + b.Error(err) + } + }() b.ResetTimer() for n := 0; n < b.N; n++ { f := &File{} - f.Open("file://" + dir) + _, err := f.Open("file://" + dir) + if err != nil { + b.Error(err) + } } b.StopTimer() } func BenchmarkNext(b *testing.B) { dir := mustCreateBenchmarkDir(b) - defer os.RemoveAll(dir) + defer func() { + if err := os.RemoveAll(dir); err != nil { + b.Error(err) + } + }() f := &File{} d, _ := f.Open("file://" + dir) b.ResetTimer() diff --git a/source/go_bindata/examples/migrations/bindata.go b/source/go_bindata/examples/migrations/bindata.go index 282d5ef54..d0a57e24e 100644 --- a/source/go_bindata/examples/migrations/bindata.go +++ b/source/go_bindata/examples/migrations/bindata.go @@ -204,9 +204,9 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "1085649617_create_users_table.down.sql": _1085649617_create_users_tableDownSql, - "1085649617_create_users_table.up.sql": _1085649617_create_users_tableUpSql, - "1185749658_add_city_to_users.down.sql": _1185749658_add_city_to_usersDownSql, - "1185749658_add_city_to_users.up.sql": _1185749658_add_city_to_usersUpSql, + "1085649617_create_users_table.up.sql": _1085649617_create_users_tableUpSql, + "1185749658_add_city_to_users.down.sql": _1185749658_add_city_to_usersDownSql, + "1185749658_add_city_to_users.up.sql": _1185749658_add_city_to_usersUpSql, } // AssetDir returns the file names below a certain @@ -248,11 +248,12 @@ type bintree struct { Func func() (*asset, error) Children map[string]*bintree } + var _bintree = &bintree{nil, map[string]*bintree{ "1085649617_create_users_table.down.sql": &bintree{_1085649617_create_users_tableDownSql, map[string]*bintree{}}, - "1085649617_create_users_table.up.sql": &bintree{_1085649617_create_users_tableUpSql, map[string]*bintree{}}, - "1185749658_add_city_to_users.down.sql": &bintree{_1185749658_add_city_to_usersDownSql, map[string]*bintree{}}, - "1185749658_add_city_to_users.up.sql": &bintree{_1185749658_add_city_to_usersUpSql, map[string]*bintree{}}, + "1085649617_create_users_table.up.sql": &bintree{_1085649617_create_users_tableUpSql, map[string]*bintree{}}, + "1185749658_add_city_to_users.down.sql": &bintree{_1185749658_add_city_to_usersDownSql, map[string]*bintree{}}, + "1185749658_add_city_to_users.up.sql": &bintree{_1185749658_add_city_to_usersUpSql, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory @@ -301,4 +302,3 @@ func _filePath(dir, name string) string { cannonicalName := strings.Replace(name, "\\", "/", -1) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } - diff --git a/source/godoc_vfs/vfs_example_test.go b/source/godoc_vfs/vfs_example_test.go index 9a57290d2..ae178294d 100644 --- a/source/godoc_vfs/vfs_example_test.go +++ b/source/godoc_vfs/vfs_example_test.go @@ -26,5 +26,8 @@ func Example_mapfs() { if err != nil { panic("error creating the migrations") } - m.Up() + err = m.Up() + if err != nil { + panic("up failed") + } } diff --git a/source/godoc_vfs/vfs_test.go b/source/godoc_vfs/vfs_test.go index f49360b14..30bced1ed 100644 --- a/source/godoc_vfs/vfs_test.go +++ b/source/godoc_vfs/vfs_test.go @@ -34,5 +34,7 @@ func TestOpen(t *testing.T) { } }() b := &godoc_vfs.VFS{} - b.Open("") + if _, err := b.Open(""); err != nil { + t.Error(err) + } } diff --git a/testing/docker.go b/testing/docker.go index 4c43e0811..e5b9ac90c 100644 --- a/testing/docker.go +++ b/testing/docker.go @@ -11,6 +11,7 @@ import ( dockercontainer "github.com/docker/docker/api/types/container" dockernetwork "github.com/docker/docker/api/types/network" dockerclient "github.com/docker/docker/client" + "github.com/hashicorp/go-multierror" "io" "math/rand" "strconv" @@ -62,7 +63,7 @@ type DockerContainer struct { keepForDebugging bool } -func (d *DockerContainer) PullImage() error { +func (d *DockerContainer) PullImage() (err error) { if d == nil { return errors.New("Cannot pull image on a nil *DockerContainer") } @@ -71,7 +72,11 @@ func (d *DockerContainer) PullImage() error { if err != nil { return err } - defer r.Close() + defer func() { + if errClose := r.Close(); errClose != nil { + err = multierror.Append(errClose) + } + }() // read output and log relevant lines bf := bufio.NewScanner(r) @@ -208,7 +213,7 @@ func (d *DockerContainer) portMapping(selectFirst bool, cPort int) (containerPor return 0, "", 0, err } - return uint(port.Int()), binding.HostIP, uint(hostPortUint), nil + return uint(port.Int()), binding.HostIP, uint(hostPortUint), nil // nolint: staticcheck } } diff --git a/testing/testing.go b/testing/testing.go index a65fa181d..d7a2c2472 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -45,8 +45,11 @@ func ParallelTest(t *testing.T, versions []Version, readyFn IsReadyFunc, testFn } // make sure to remove container once done - defer container.Remove() - + defer func() { + if err := container.Remove(); err != nil { + t.Error(err) + } + }() // wait until database is ready tick := time.NewTicker(1000 * time.Millisecond) defer tick.Stop() @@ -78,7 +81,11 @@ func containerLogs(t *testing.T, c *DockerContainer) []byte { t.Error(err) return nil } - defer r.Close() + defer func() { + if err := r.Close(); err != nil { + t.Error(err) + } + }() b, err := ioutil.ReadAll(r) if err != nil { t.Error(err) diff --git a/util.go b/util.go index b6829aafb..4aeb0f221 100644 --- a/util.go +++ b/util.go @@ -47,24 +47,24 @@ func suint(n int) uint { var errNoScheme = errors.New("no scheme") var errEmptyURL = errors.New("URL cannot be empty") -func sourceSchemeFromUrl(url string) (string, error) { - u, err := schemeFromUrl(url) +func sourceSchemeFromURL(url string) (string, error) { + u, err := schemeFromURL(url) if err != nil { return "", fmt.Errorf("source: %v", err) } return u, nil } -func databaseSchemeFromUrl(url string) (string, error) { - u, err := schemeFromUrl(url) +func databaseSchemeFromURL(url string) (string, error) { + u, err := schemeFromURL(url) if err != nil { return "", fmt.Errorf("database: %v", err) } return u, nil } -// schemeFromUrl returns the scheme from a URL string -func schemeFromUrl(url string) (string, error) { +// schemeFromURL returns the scheme from a URL string +func schemeFromURL(url string) (string, error) { if url == "" { return "", errEmptyURL } diff --git a/util_test.go b/util_test.go index b4843410b..6543b28f8 100644 --- a/util_test.go +++ b/util_test.go @@ -36,7 +36,7 @@ func TestSourceSchemeFromUrlSuccess(t *testing.T) { urlStr := "protocol://path" expected := "protocol" - u, err := sourceSchemeFromUrl(urlStr) + u, err := sourceSchemeFromURL(urlStr) if err != nil { t.Fatalf("expected no error, but received %q", err) } @@ -65,7 +65,7 @@ func TestSourceSchemeFromUrlFailure(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - _, err := sourceSchemeFromUrl(tc.urlStr) + _, err := sourceSchemeFromURL(tc.urlStr) if err.Error() != tc.expectErr.Error() { t.Fatalf("expected %q, but received %q", tc.expectErr, err) } @@ -77,7 +77,7 @@ func TestDatabaseSchemeFromUrlSuccess(t *testing.T) { urlStr := "protocol://path" expected := "protocol" - u, err := databaseSchemeFromUrl(urlStr) + u, err := databaseSchemeFromURL(urlStr) if err != nil { t.Fatalf("expected no error, but received %q", err) } @@ -106,7 +106,7 @@ func TestDatabaseSchemeFromUrlFailure(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - _, err := databaseSchemeFromUrl(tc.urlStr) + _, err := databaseSchemeFromURL(tc.urlStr) if err.Error() != tc.expectErr.Error() { t.Fatalf("expected %q, but received %q", tc.expectErr, err) }