From 51acd3f3adae0accb950b99cb6b36ca30e0fc138 Mon Sep 17 00:00:00 2001 From: Jens Rantil Date: Sun, 17 Sep 2017 13:13:45 +0200 Subject: [PATCH 1/4] style: order imports lexicographically Best-practise according to `gofmt`. --- cli/main.go | 2 +- database/cassandra/cassandra.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/main.go b/cli/main.go index f019b5cb..4c727a97 100644 --- a/cli/main.go +++ b/cli/main.go @@ -3,10 +3,10 @@ package main import ( "flag" "fmt" - "strings" "os" "os/signal" "strconv" + "strings" "syscall" "time" diff --git a/database/cassandra/cassandra.go b/database/cassandra/cassandra.go index 3dd93793..35d980c9 100644 --- a/database/cassandra/cassandra.go +++ b/database/cassandra/cassandra.go @@ -5,10 +5,11 @@ import ( "io" "io/ioutil" nurl "net/url" - "github.com/gocql/gocql" + "strconv" "time" + + "github.com/gocql/gocql" "github.com/mattes/migrate/database" - "strconv" ) func init() { From 701bd6873162a37e2faacc84305a02ff43145b24 Mon Sep 17 00:00:00 2001 From: Jens Rantil Date: Sun, 17 Sep 2017 13:25:43 +0200 Subject: [PATCH 2/4] style(cassandra): whitespace fixes According to `gofmt`. --- database/cassandra/cassandra.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/database/cassandra/cassandra.go b/database/cassandra/cassandra.go index 35d980c9..0be7cf54 100644 --- a/database/cassandra/cassandra.go +++ b/database/cassandra/cassandra.go @@ -21,8 +21,8 @@ var DefaultMigrationsTable = "schema_migrations" var dbLocked = false var ( - ErrNilConfig = fmt.Errorf("no config") - ErrNoKeyspace = fmt.Errorf("no keyspace provided") + ErrNilConfig = fmt.Errorf("no config") + ErrNoKeyspace = fmt.Errorf("no keyspace provided") ErrDatabaseDirty = fmt.Errorf("database is dirty") ) @@ -36,7 +36,7 @@ type Cassandra struct { isLocked bool // Open and WithInstance need to guarantee that config is never nil - config *Config + config *Config } func (p *Cassandra) Open(url string) (database.Driver, error) { @@ -154,7 +154,6 @@ func (p *Cassandra) SetVersion(version int, dirty bool) error { return nil } - // Return current keyspace version func (p *Cassandra) Version() (version int, dirty bool, err error) { query := `SELECT version, dirty FROM "` + p.config.MigrationsTable + `" LIMIT 1` @@ -192,7 +191,6 @@ func (p *Cassandra) Drop() error { return nil } - // Ensure version table exists func (p *Cassandra) ensureVersionTable() error { err := p.session.Query(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (version bigint, dirty boolean, PRIMARY KEY(version))", p.config.MigrationsTable)).Exec() @@ -205,7 +203,6 @@ func (p *Cassandra) ensureVersionTable() error { return nil } - // ParseConsistency wraps gocql.ParseConsistency // to return an error instead of a panicking. func parseConsistency(consistencyStr string) (consistency gocql.Consistency, err error) { From d25732a53a3aa0505e237a58521bcd8e6d0795b7 Mon Sep 17 00:00:00 2001 From: Jens Rantil Date: Sun, 17 Sep 2017 13:30:08 +0200 Subject: [PATCH 3/4] sty: avoid unnecessary parenthesis Golang best-practise. Cleanup made by `gofmt`. --- database/cassandra/cassandra.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/cassandra/cassandra.go b/database/cassandra/cassandra.go index 0be7cf54..041a4682 100644 --- a/database/cassandra/cassandra.go +++ b/database/cassandra/cassandra.go @@ -112,7 +112,7 @@ func (p *Cassandra) Close() error { } func (p *Cassandra) Lock() error { - if (dbLocked) { + if dbLocked { return database.ErrLocked } dbLocked = true From da9a5dd1cf0f5a8a68deb8cd0f2d4885abadd5fe Mon Sep 17 00:00:00 2001 From: Jens Rantil Date: Sun, 17 Sep 2017 13:38:04 +0200 Subject: [PATCH 4/4] feat(mysql): try creating database if not exist **Background:** Our scenario is that we'd like to be able to create schema from scratch for a brand new staging environment. This new staging environment doesn't have the database created. **Problem:** Having a first migration doing `CREATE DATABASE xxx` doesn't work because a connection to `mysql://tcp(a.b.c.d)/xxx` fails when database driver is trying to switch to the database `xxx`. **Proposed solution:** When `Open(...)`ing a MySQL `Driver`, we always try to create the database if it doesn't exist and _then_ we reestablish a new database connection and ask the MySQL `database/sql/driver.Driver` to make the switch to the database. --- database/mysql/mysql.go | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/database/mysql/mysql.go b/database/mysql/mysql.go index f00f886e..f4a75808 100644 --- a/database/mysql/mysql.go +++ b/database/mysql/mysql.go @@ -23,10 +23,11 @@ func init() { var DefaultMigrationsTable = "schema_migrations" var ( - ErrDatabaseDirty = fmt.Errorf("database is dirty") - ErrNilConfig = fmt.Errorf("no config") - ErrNoDatabaseName = fmt.Errorf("no database name") - ErrAppendPEM = fmt.Errorf("failed to append PEM") + ErrDatabaseDirty = fmt.Errorf("database is dirty") + ErrNilConfig = fmt.Errorf("no config") + ErrNoDatabaseName = fmt.Errorf("no database name") + ErrAppendPEM = fmt.Errorf("failed to append PEM") + ErrDatabaseCouldNotBeCreated = fmt.Errorf("Database could not be created") ) type Config struct { @@ -89,6 +90,10 @@ func (m *Mysql) Open(url string) (database.Driver, error) { q.Set("multiStatements", "true") purl.RawQuery = q.Encode() + if err = ensureDatabaseExist(purl); err != nil { + return nil, err + } + db, err := sql.Open("mysql", strings.Replace( migrate.FilterCustomQuery(purl).String(), "mysql://", "", 1)) if err != nil { @@ -147,6 +152,28 @@ func (m *Mysql) Open(url string) (database.Driver, error) { return mx, nil } +func ensureDatabaseExist(url *nurl.URL) error { + urlCopy, _ := nurl.Parse(url.String()) // Copy + + database := urlCopy.Path[1:] + urlCopy.Path = "/" + + dbstring := strings.Replace(migrate.FilterCustomQuery(urlCopy).String(), "mysql://", "", 1) + db, err := sql.Open("mysql", dbstring) + if err != nil { + return err + } + + // Unfortunately placeholder parameters did not work here, so there might + // be a risk of SQL injection here if database URL isn't properly + // saniticed. + if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + database); err != nil { + return err + } + + return db.Close() +} + func (m *Mysql) Close() error { return m.db.Close() }