Commit 68a77c40 by Torkel Ödegaard

More progress on db schema setup and migrations, tricky stuff

parent 38f237ef
db: mysql:
image: mysql:latest image: mysql:latest
environment: environment:
MYSQL_ROOT_PASSWORD: rootpass MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: grafana MYSQL_DATABASE: grafana
MYSQL_USER: grafana MYSQL_USER: grafana
MYSQL_PASSWORD: password MYSQL_PASSWORD: password
ports:
- "3306:3306"
mysqltests:
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: grafana_tests
MYSQL_USER: grafana
MYSQL_PASSWORD: password
ports:
- "3306:3306"
openldap: mysqltests:
image: cnry/openldap image: mysql:latest
environment: environment:
SLAPD_PASSWORD: grafana MYSQL_ROOT_PASSWORD: rootpass
SLAPD_DOMAIN: grafana.org MYSQL_DATABASE: grafana_tests
MYSQL_USER: grafana
MYSQL_PASSWORD: password
ports: ports:
- "389:389" - "3306:3306"
...@@ -8,9 +8,8 @@ import ( ...@@ -8,9 +8,8 @@ import (
func GetAccount(c *middleware.Context) { func GetAccount(c *middleware.Context) {
query := m.GetAccountInfoQuery{Id: c.AccountId} query := m.GetAccountInfoQuery{Id: c.AccountId}
err := bus.Dispatch(&query)
if err != nil { if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(500, "Failed to fetch collaboratos", err) c.JsonApiErr(500, "Failed to fetch collaboratos", err)
return return
} }
...@@ -31,9 +30,8 @@ func UpdateAccount(c *middleware.Context, cmd m.UpdateAccountCommand) { ...@@ -31,9 +30,8 @@ func UpdateAccount(c *middleware.Context, cmd m.UpdateAccountCommand) {
func GetOtherAccounts(c *middleware.Context) { func GetOtherAccounts(c *middleware.Context) {
query := m.GetOtherAccountsQuery{AccountId: c.AccountId} query := m.GetOtherAccountsQuery{AccountId: c.AccountId}
err := bus.Dispatch(&query)
if err != nil { if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(500, "Failed to get other accounts", err) c.JsonApiErr(500, "Failed to get other accounts", err)
return return
} }
......
...@@ -3,9 +3,27 @@ package migrations ...@@ -3,9 +3,27 @@ package migrations
type migration struct { type migration struct {
desc string desc string
sqlite string sqlite string
mysql string
verifyTable string verifyTable string
} }
type columnType string
const (
DB_TYPE_STRING columnType = "String"
)
func (m *migration) getSql(dbType string) string {
switch dbType {
case "mysql":
return m.mysql
case "sqlite3":
return m.sqlite
}
panic("db type not supported")
}
type migrationBuilder struct { type migrationBuilder struct {
migration *migration migration *migration
} }
...@@ -15,6 +33,11 @@ func (b *migrationBuilder) sqlite(sql string) *migrationBuilder { ...@@ -15,6 +33,11 @@ func (b *migrationBuilder) sqlite(sql string) *migrationBuilder {
return b return b
} }
func (b *migrationBuilder) mysql(sql string) *migrationBuilder {
b.migration.mysql = sql
return b
}
func (b *migrationBuilder) verifyTable(name string) *migrationBuilder { func (b *migrationBuilder) verifyTable(name string) *migrationBuilder {
b.migration.verifyTable = name b.migration.verifyTable = name
return b return b
......
...@@ -4,8 +4,12 @@ import ( ...@@ -4,8 +4,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/go-xorm/xorm"
"github.com/torkelo/grafana-pro/pkg/services/sqlstore/sqlsyntax" "github.com/torkelo/grafana-pro/pkg/services/sqlstore/sqlsyntax"
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
) )
var x *xorm.Engine var x *xorm.Engine
...@@ -29,9 +33,18 @@ func getSchemaVersion() (int, error) { ...@@ -29,9 +33,18 @@ func getSchemaVersion() (int, error) {
return v.Version, err return v.Version, err
} }
func StartMigration(engine *xorm.Engine) error { func setEngineAndDialect(engine *xorm.Engine) {
x = engine x = engine
dialect = new(sqlsyntax.Sqlite3) switch x.DriverName() {
case "mysql":
dialect = new(sqlsyntax.Mysql)
case "sqlite3":
dialect = new(sqlsyntax.Sqlite3)
}
}
func StartMigration(engine *xorm.Engine) error {
setEngineAndDialect(engine)
_, err := getSchemaVersion() _, err := getSchemaVersion()
if err != nil { if err != nil {
...@@ -49,16 +62,21 @@ func StartMigration(engine *xorm.Engine) error { ...@@ -49,16 +62,21 @@ func StartMigration(engine *xorm.Engine) error {
func execMigration(m *migration) error { func execMigration(m *migration) error {
err := inTransaction(func(sess *xorm.Session) error { err := inTransaction(func(sess *xorm.Session) error {
_, err := sess.Exec(m.sqlite) _, err := sess.Exec(m.getSql(x.DriverName()))
if err != nil { if err != nil {
return err return err
} }
return nil return nil
}) })
if err != nil { if err != nil {
return err return err
} }
// verify
return verifyMigration(m)
}
func verifyMigration(m *migration) error {
if m.verifyTable != "" { if m.verifyTable != "" {
sqlStr, args := dialect.TableCheckSql(m.verifyTable) sqlStr, args := dialect.TableCheckSql(m.verifyTable)
results, err := x.Query(sqlStr, args...) results, err := x.Query(sqlStr, args...)
...@@ -66,7 +84,6 @@ func execMigration(m *migration) error { ...@@ -66,7 +84,6 @@ func execMigration(m *migration) error {
return errors.New(fmt.Sprintf("Verify failed: table %v does not exist", m.verifyTable)) return errors.New(fmt.Sprintf("Verify failed: table %v does not exist", m.verifyTable))
} }
} }
return nil return nil
} }
......
...@@ -4,13 +4,24 @@ var migrationList []*migration ...@@ -4,13 +4,24 @@ var migrationList []*migration
func init() { func init() {
new(migrationBuilder). new(migrationBuilder).
// ------------------------------
desc("Create account table"). desc("Create account table").
sqlite(` sqlite(`
CREATE TABLE account ( CREATE TABLE account (
id INTEGER PRIMARY KEY id INTEGER PRIMARY KEY AUTOINCREMENT
) )
`). `).
verifyTable("account") mysql(`
CREATE TABLE account (
id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)
)
`).
verifyTable("account").add()
// ------------------------------
// desc("Add name column to account table").
// table("account").addColumn("name").colType(DB_TYPE_STRING)
// sqlite("ALTER TABLE account ADD COLUMN name TEXT").
// mysql("ALTER TABLE account ADD COLUMN name NVARCHAR(255)").
} }
type SchemaVersion struct { type SchemaVersion struct {
......
package migrations package migrations
// import ( import (
// "testing" "fmt"
// "testing"
// "github.com/go-xorm/xorm"
// "github.com/go-xorm/xorm"
// . "github.com/smartystreets/goconvey/convey"
// ) . "github.com/smartystreets/goconvey/convey"
// )
// func TestMigrationsSqlite(t *testing.T) {
// func cleanDB(x *xorm.Engine) {
// Convey("Initial SQLite3 migration", t, func() { tables, _ := x.DBMetas()
// x, err := xorm.NewEngine("sqlite3", ":memory:") sess := x.NewSession()
// StartMigration(x) defer sess.Close()
//
// tables, err := x.DBMetas() for _, table := range tables {
// So(err, ShouldBeNil) if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil {
// panic("Failed to disable foreign key checks")
// So(len(tables), ShouldEqual, 1) }
// }) if _, err := sess.Exec("DROP TABLE " + table.Name); err != nil {
// } panic(fmt.Sprintf("Failed to delete table: %v, err: %v", table.Name, err))
}
if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 1"); err != nil {
panic("Failed to disable foreign key checks")
}
}
}
func TestMigrationsSqlite(t *testing.T) {
testDBs := [][]string{
[]string{"sqlite3", ":memory:"},
[]string{"mysql", "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"},
}
for _, testDB := range testDBs {
Convey("Initial "+testDB[0]+" migration", t, func() {
x, err := xorm.NewEngine(testDB[0], testDB[1])
So(err, ShouldBeNil)
if testDB[0] == "mysql" {
cleanDB(x)
}
StartMigration(x)
tables, err := x.DBMetas()
So(err, ShouldBeNil)
So(len(tables), ShouldEqual, 2)
})
}
}
...@@ -8,11 +8,24 @@ type Dialect interface { ...@@ -8,11 +8,24 @@ type Dialect interface {
type Sqlite3 struct { type Sqlite3 struct {
} }
type Mysql struct {
}
func (db *Sqlite3) DBType() string { func (db *Sqlite3) DBType() string {
return "sqlite3" return "sqlite3"
} }
func (db *Mysql) DBType() string {
return "mysql"
}
func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) { func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{tableName} args := []interface{}{tableName}
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
} }
func (db *Mysql) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{"grafana", tableName}
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
return sql, args
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment