mirror of https://github.com/grafana/grafana.git
Worked on database agnostic table creation for db migrations
This commit is contained in:
parent
7d70ffe201
commit
d8e5be5782
|
|
@ -4,4 +4,8 @@ app_mode = development
|
||||||
router_logging = false
|
router_logging = false
|
||||||
static_root_path = grafana/src
|
static_root_path = grafana/src
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = Trace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,10 @@ type Account struct {
|
||||||
Login string `xorm:"UNIQUE NOT NULL"`
|
Login string `xorm:"UNIQUE NOT NULL"`
|
||||||
Email string `xorm:"UNIQUE NOT NULL"`
|
Email string `xorm:"UNIQUE NOT NULL"`
|
||||||
Name string
|
Name string
|
||||||
FullName string
|
|
||||||
Password string
|
Password string
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
Salt string `xorm:"VARCHAR(10)"`
|
Salt string `xorm:"VARCHAR(10)"`
|
||||||
Company string
|
Company string
|
||||||
NextDashboardId int
|
|
||||||
UsingAccountId int64
|
UsingAccountId int64
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,5 @@
|
||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
POSTGRES = "postgres"
|
|
||||||
SQLITE = "sqlite3"
|
|
||||||
MYSQL = "mysql"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migration interface {
|
|
||||||
Sql(dialect Dialect) string
|
|
||||||
Id() string
|
|
||||||
SetId(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ColumnType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
DB_TYPE_STRING ColumnType = "String"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MigrationBase struct {
|
type MigrationBase struct {
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +43,7 @@ func (m *RawSqlMigration) Mysql(sql string) *RawSqlMigration {
|
||||||
type AddColumnMigration struct {
|
type AddColumnMigration struct {
|
||||||
MigrationBase
|
MigrationBase
|
||||||
tableName string
|
tableName string
|
||||||
columnName string
|
column *Column
|
||||||
columnType ColumnType
|
|
||||||
length int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddColumnMigration) Table(tableName string) *AddColumnMigration {
|
func (m *AddColumnMigration) Table(tableName string) *AddColumnMigration {
|
||||||
|
|
@ -76,35 +51,23 @@ func (m *AddColumnMigration) Table(tableName string) *AddColumnMigration {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddColumnMigration) Length(length int) *AddColumnMigration {
|
func (m *AddColumnMigration) Column(col *Column) *AddColumnMigration {
|
||||||
m.length = length
|
m.column = col
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *AddColumnMigration) Column(columnName string) *AddColumnMigration {
|
|
||||||
m.columnName = columnName
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *AddColumnMigration) Type(columnType ColumnType) *AddColumnMigration {
|
|
||||||
m.columnType = columnType
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddColumnMigration) Sql(dialect Dialect) string {
|
func (m *AddColumnMigration) Sql(dialect Dialect) string {
|
||||||
return fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s", m.tableName, m.columnName, dialect.ToDBTypeSql(m.columnType, m.length))
|
return dialect.AddColumnSql(m.tableName, m.column)
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddIndexMigration struct {
|
type AddIndexMigration struct {
|
||||||
MigrationBase
|
MigrationBase
|
||||||
tableName string
|
tableName string
|
||||||
columns string
|
index Index
|
||||||
indexName string
|
|
||||||
unique string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddIndexMigration) Name(name string) *AddIndexMigration {
|
func (m *AddIndexMigration) Name(name string) *AddIndexMigration {
|
||||||
m.indexName = name
|
m.index.Name = name
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,15 +77,47 @@ func (m *AddIndexMigration) Table(tableName string) *AddIndexMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddIndexMigration) Unique() *AddIndexMigration {
|
func (m *AddIndexMigration) Unique() *AddIndexMigration {
|
||||||
m.unique = "UNIQUE"
|
m.index.Type = UniqueIndex
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddIndexMigration) Columns(columns ...string) *AddIndexMigration {
|
func (m *AddIndexMigration) Columns(columns ...string) *AddIndexMigration {
|
||||||
m.columns = strings.Join(columns, ",")
|
m.index.Cols = columns
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AddIndexMigration) Sql(dialect Dialect) string {
|
func (m *AddIndexMigration) Sql(dialect Dialect) string {
|
||||||
return fmt.Sprintf("CREATE %s INDEX %s ON %s(%s)", m.unique, m.indexName, m.tableName, m.columns)
|
return dialect.CreateIndexSql(m.tableName, &m.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddTableMigration struct {
|
||||||
|
MigrationBase
|
||||||
|
table Table
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddTableMigration) Sql(d Dialect) string {
|
||||||
|
return d.CreateTableSql(&m.table)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddTableMigration) Name(name string) *AddTableMigration {
|
||||||
|
m.table.Name = name
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddTableMigration) WithColumns(columns ...*Column) *AddTableMigration {
|
||||||
|
for _, col := range columns {
|
||||||
|
m.table.Columns = append(m.table.Columns, col)
|
||||||
|
if col.IsPrimaryKey {
|
||||||
|
m.table.PrimaryKeys = append(m.table.PrimaryKeys, col.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AddTableMigration) WithColumn(col *Column) *AddTableMigration {
|
||||||
|
m.table.Columns = append(m.table.Columns, col)
|
||||||
|
if col.IsPrimaryKey {
|
||||||
|
m.table.PrimaryKeys = append(m.table.PrimaryKeys, col.Name)
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
// Notice
|
||||||
|
// code based on parts from from https://github.com/go-xorm/core/blob/3e0fa232ab5c90996406c0cd7ae86ad0e5ecf85f/column.go
|
||||||
|
|
||||||
|
type Column struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Length int
|
||||||
|
Length2 int
|
||||||
|
Nullable bool
|
||||||
|
IsPrimaryKey bool
|
||||||
|
IsAutoIncrement bool
|
||||||
|
Default string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (col *Column) String(d Dialect) string {
|
||||||
|
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||||
|
|
||||||
|
sql += d.SqlType(col) + " "
|
||||||
|
|
||||||
|
if col.IsPrimaryKey {
|
||||||
|
sql += "PRIMARY KEY "
|
||||||
|
if col.IsAutoIncrement {
|
||||||
|
sql += d.AutoIncrStr() + " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.ShowCreateNull() {
|
||||||
|
if col.Nullable {
|
||||||
|
sql += "NULL "
|
||||||
|
} else {
|
||||||
|
sql += "NOT NULL "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if col.Default != "" {
|
||||||
|
sql += "DEFAULT " + col.Default + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (col *Column) StringNoPk(d Dialect) string {
|
||||||
|
sql := d.QuoteStr() + col.Name + d.QuoteStr() + " "
|
||||||
|
|
||||||
|
sql += d.SqlType(col) + " "
|
||||||
|
|
||||||
|
if d.ShowCreateNull() {
|
||||||
|
if col.Nullable {
|
||||||
|
sql += "NULL "
|
||||||
|
} else {
|
||||||
|
sql += "NOT NULL "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if col.Default != "" {
|
||||||
|
sql += "DEFAULT " + col.Default + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
@ -1,52 +1,97 @@
|
||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Dialect interface {
|
type Dialect interface {
|
||||||
DriverName() string
|
DriverName() string
|
||||||
ToDBTypeSql(columnType ColumnType, length int) string
|
QuoteStr() string
|
||||||
|
Quote(string) string
|
||||||
|
AndStr() string
|
||||||
|
AutoIncrStr() string
|
||||||
|
OrStr() string
|
||||||
|
EqStr() string
|
||||||
|
ShowCreateNull() bool
|
||||||
|
SqlType(col *Column) string
|
||||||
|
|
||||||
|
CreateIndexSql(tableName string, index *Index) string
|
||||||
|
CreateTableSql(table *Table) string
|
||||||
|
AddColumnSql(tableName string, Col *Column) string
|
||||||
|
|
||||||
TableCheckSql(tableName string) (string, []interface{})
|
TableCheckSql(tableName string) (string, []interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sqlite3 struct {
|
type BaseDialect struct {
|
||||||
|
dialect Dialect
|
||||||
|
driverName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mysql struct {
|
func (d *BaseDialect) DriverName() string {
|
||||||
|
return d.driverName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Sqlite3) DriverName() string {
|
func (b *BaseDialect) ShowCreateNull() bool {
|
||||||
return SQLITE
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Mysql) DriverName() string {
|
func (b *BaseDialect) AndStr() string {
|
||||||
return MYSQL
|
return "AND"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Sqlite3) ToDBTypeSql(columnType ColumnType, length int) string {
|
func (b *BaseDialect) OrStr() string {
|
||||||
switch columnType {
|
return "OR"
|
||||||
case DB_TYPE_STRING:
|
|
||||||
return "TEXT"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("Unsupported db type")
|
func (b *BaseDialect) EqStr() string {
|
||||||
|
return "="
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Mysql) ToDBTypeSql(columnType ColumnType, length int) string {
|
func (b *BaseDialect) CreateTableSql(table *Table) string {
|
||||||
switch columnType {
|
var sql string
|
||||||
case DB_TYPE_STRING:
|
sql = "CREATE TABLE IF NOT EXISTS "
|
||||||
return fmt.Sprintf("NVARCHAR(%d)", length)
|
sql += b.dialect.Quote(table.Name) + " (\n"
|
||||||
|
|
||||||
|
pkList := table.PrimaryKeys
|
||||||
|
|
||||||
|
for _, col := range table.Columns {
|
||||||
|
if col.IsPrimaryKey && len(pkList) == 1 {
|
||||||
|
sql += col.String(b.dialect)
|
||||||
|
} else {
|
||||||
|
sql += col.StringNoPk(b.dialect)
|
||||||
|
}
|
||||||
|
sql = strings.TrimSpace(sql)
|
||||||
|
sql += "\n, "
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("Unsupported db type")
|
if len(pkList) > 1 {
|
||||||
|
sql += "PRIMARY KEY ( "
|
||||||
|
sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
|
||||||
|
sql += " ), "
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
|
sql = sql[:len(sql)-2] + ")"
|
||||||
args := []interface{}{tableName}
|
sql += ";"
|
||||||
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
|
return sql
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Mysql) TableCheckSql(tableName string) (string, []interface{}) {
|
func (db *BaseDialect) AddColumnSql(tableName string, col *Column) string {
|
||||||
args := []interface{}{"grafana", tableName}
|
return fmt.Sprintf("alter table %s ADD COLUMN %s", tableName, col.StringNoPk(db.dialect))
|
||||||
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
|
}
|
||||||
return sql, args
|
|
||||||
|
func (db *BaseDialect) CreateIndexSql(tableName string, index *Index) string {
|
||||||
|
quote := db.dialect.Quote
|
||||||
|
var unique string
|
||||||
|
var idxName string
|
||||||
|
if index.Type == UniqueIndex {
|
||||||
|
unique = " UNIQUE"
|
||||||
|
idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
|
||||||
|
} else {
|
||||||
|
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v);", unique,
|
||||||
|
quote(idxName), quote(tableName),
|
||||||
|
quote(strings.Join(index.Cols, quote(","))))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,61 +2,40 @@ package migrations
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
// Id int64
|
|
||||||
// Login string `xorm:"UNIQUE NOT NULL"`
|
|
||||||
// Email string `xorm:"UNIQUE NOT NULL"`
|
|
||||||
// Name string
|
|
||||||
// FullName string
|
|
||||||
// Password string
|
|
||||||
// IsAdmin bool
|
|
||||||
// Salt string `xorm:"VARCHAR(10)"`
|
|
||||||
// Company string
|
|
||||||
// NextDashboardId int
|
|
||||||
// UsingAccountId int64
|
|
||||||
// Created time.Time
|
|
||||||
// Updated time.Time
|
|
||||||
|
|
||||||
func AddMigrations(mg *Migrator) {
|
func AddMigrations(mg *Migrator) {
|
||||||
|
|
||||||
// TABLE Account
|
//------- migration_log table -------------------
|
||||||
// -------------------------------
|
mg.AddMigration("create migration_log table", new(AddTableMigration).
|
||||||
mg.AddMigration("create account table", new(RawSqlMigration).
|
Name("migration_log").WithColumns(
|
||||||
Sqlite(`
|
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
CREATE TABLE account (
|
&Column{Name: "migration_id", Type: DB_NVarchar, Length: 255},
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
&Column{Name: "sql", Type: DB_Text},
|
||||||
login TEXT NOT NULL,
|
&Column{Name: "success", Type: DB_Bool},
|
||||||
email TEXT NOT NULL,
|
&Column{Name: "error", Type: DB_Text},
|
||||||
name TEXT NULL,
|
&Column{Name: "timestamp", Type: DB_DateTime},
|
||||||
password TEXT NULL,
|
))
|
||||||
salt TEXT NULL,
|
|
||||||
company TEXT NULL,
|
//------- account table -------------------
|
||||||
using_account_id INTEGER NULL,
|
mg.AddMigration("create account table", new(AddTableMigration).
|
||||||
is_admin INTEGER NOT NULL,
|
Name("account").WithColumns(
|
||||||
created INTEGER NOT NULL,
|
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
updated INTEGER NOT NULL
|
&Column{Name: "login", Type: DB_NVarchar, Length: 255},
|
||||||
)
|
&Column{Name: "email", Type: DB_NVarchar, Length: 255},
|
||||||
`).
|
&Column{Name: "name", Type: DB_NVarchar, Length: 255},
|
||||||
Mysql(`
|
&Column{Name: "password", Type: DB_NVarchar, Length: 50},
|
||||||
CREATE TABLE account (
|
&Column{Name: "salt", Type: DB_NVarchar, Length: 50},
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id),
|
&Column{Name: "company", Type: DB_NVarchar, Length: 255},
|
||||||
login VARCHAR(255) NOT NULL,
|
&Column{Name: "using_account_id", Type: DB_BigInt},
|
||||||
email VARCHAR(255) NOT NULL,
|
&Column{Name: "is_admin", Type: DB_Bool},
|
||||||
name VARCHAR(255) NULL,
|
&Column{Name: "created", Type: DB_DateTime},
|
||||||
password VARCHAR(50) NULL,
|
&Column{Name: "updated", Type: DB_DateTime},
|
||||||
salt VARCHAR(50) NULL,
|
))
|
||||||
company VARCHAR(255) NULL,
|
|
||||||
using_account_id BIGINT NULL,
|
//------- account table indexes ------------------
|
||||||
is_admin BOOL NOT NULL,
|
mg.AddMigration("add unique index UIX_account.login", new(AddIndexMigration).
|
||||||
created DATETIME NOT NULL,
|
|
||||||
update DATETIME NOT NULL
|
|
||||||
)
|
|
||||||
`))
|
|
||||||
// ------------------------------
|
|
||||||
mg.AddMigration("add index UIX_account.login", new(AddIndexMigration).
|
|
||||||
Name("UIX_account_login").Table("account").Columns("login"))
|
Name("UIX_account_login").Table("account").Columns("login"))
|
||||||
// ------------------------------
|
mg.AddMigration("add unique index UIX_account.email", new(AddIndexMigration).
|
||||||
// mg.AddMigration("add column", new(AddColumnMigration).
|
Name("UIX_account_email").Table("account").Columns("email"))
|
||||||
// Table("account").Column("name").Type(DB_TYPE_STRING).Length(255))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MigrationLog struct {
|
type MigrationLog struct {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func TestMigrations(t *testing.T) {
|
||||||
log.NewLogger(0, "console", `{"level": 0}`)
|
log.NewLogger(0, "console", `{"level": 0}`)
|
||||||
|
|
||||||
testDBs := [][]string{
|
testDBs := [][]string{
|
||||||
//[]string{"mysql", "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"},
|
[]string{"mysql", "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"},
|
||||||
[]string{"sqlite3", ":memory:"},
|
[]string{"sqlite3", ":memory:"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ func NewMigrator(engine *xorm.Engine) *Migrator {
|
||||||
|
|
||||||
switch mg.x.DriverName() {
|
switch mg.x.DriverName() {
|
||||||
case MYSQL:
|
case MYSQL:
|
||||||
mg.dialect = new(Mysql)
|
mg.dialect = NewMysqlDialect()
|
||||||
case SQLITE:
|
case SQLITE:
|
||||||
mg.dialect = new(Sqlite3)
|
mg.dialect = NewSqlite3Dialect()
|
||||||
}
|
}
|
||||||
|
|
||||||
return mg
|
return mg
|
||||||
|
|
@ -37,20 +37,18 @@ func (mg *Migrator) AddMigration(id string, m Migration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
|
func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
|
||||||
|
logMap := make(map[string]MigrationLog)
|
||||||
|
logItems := make([]MigrationLog, 0)
|
||||||
|
|
||||||
exists, err := mg.x.IsTableExist(new(MigrationLog))
|
exists, err := mg.x.IsTableExist(new(MigrationLog))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
if err := mg.x.CreateTables(new(MigrationLog)); err != nil {
|
return logMap, nil
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logMap := make(map[string]MigrationLog)
|
|
||||||
logItems := make([]MigrationLog, 0)
|
|
||||||
if err = mg.x.Find(&logItems); err != nil {
|
if err = mg.x.Find(&logItems); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +64,7 @@ func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mg *Migrator) Start() error {
|
func (mg *Migrator) Start() error {
|
||||||
log.Info("Migrator::Start DB migration")
|
log.Info("Migrator::Starting DB migration")
|
||||||
|
|
||||||
logMap, err := mg.GetMigrationLog()
|
logMap, err := mg.GetMigrationLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -76,13 +74,15 @@ func (mg *Migrator) Start() error {
|
||||||
for _, m := range mg.migrations {
|
for _, m := range mg.migrations {
|
||||||
_, exists := logMap[m.Id()]
|
_, exists := logMap[m.Id()]
|
||||||
if exists {
|
if exists {
|
||||||
log.Info("Migrator:: Skipping migration: %v, Already executed", m.Id())
|
log.Debug("Migrator:: Skipping migration: %v, Already executed", m.Id())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sql := m.Sql(mg.dialect)
|
||||||
|
|
||||||
record := MigrationLog{
|
record := MigrationLog{
|
||||||
MigrationId: m.Id(),
|
MigrationId: m.Id(),
|
||||||
Sql: m.Sql(mg.dialect),
|
Sql: sql,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
package migrations
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
)
|
|
||||||
|
|
||||||
// func cleanDB(x *xorm.Engine) {
|
|
||||||
// tables, _ := x.DBMetas()
|
|
||||||
// sess := x.NewSession()
|
|
||||||
// defer sess.Close()
|
|
||||||
//
|
|
||||||
// for _, table := range tables {
|
|
||||||
// if _, err := sess.Exec("SET FOREIGN_KEY_CHECKS = 0"); err != nil {
|
|
||||||
// panic("Failed to disable foreign key checks")
|
|
||||||
// }
|
|
||||||
// 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")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var indexTypes = []string{"Unknown", "", "UNIQUE"}
|
|
||||||
//
|
|
||||||
|
|
||||||
func TestMigrator(t *testing.T) {
|
|
||||||
|
|
||||||
Convey("Migrator", t, func() {
|
|
||||||
x, err := xorm.NewEngine(SQLITE, ":memory:")
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
mg := NewMigrator(x)
|
|
||||||
|
|
||||||
Convey("Given one migration", func() {
|
|
||||||
mg.AddMigration("test migration", new(RawSqlMigration).
|
|
||||||
Sqlite(`
|
|
||||||
CREATE TABLE account (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
||||||
)`).
|
|
||||||
Mysql(`
|
|
||||||
CREATE TABLE account (
|
|
||||||
id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)
|
|
||||||
)`))
|
|
||||||
|
|
||||||
err := mg.Start()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
log, err := mg.GetMigrationLog()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(len(log), ShouldEqual, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// So(err, ShouldBeNil)
|
|
||||||
//
|
|
||||||
// So(len(tables), ShouldEqual, 2)
|
|
||||||
// fmt.Printf("\nDB Schema after migration: table count: %v\n", len(tables))
|
|
||||||
//
|
|
||||||
// for _, table := range tables {
|
|
||||||
// fmt.Printf("\nTable: %v \n", table.Name)
|
|
||||||
// for _, column := range table.Columns() {
|
|
||||||
// fmt.Printf("\t %v \n", column.String(x.Dialect()))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if len(table.Indexes) > 0 {
|
|
||||||
// fmt.Printf("\n\tIndexes:\n")
|
|
||||||
// for _, index := range table.Indexes {
|
|
||||||
// fmt.Printf("\t %v (%v) %v \n", index.Name, strings.Join(index.Cols, ","), indexTypes[index.Type])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Mysql struct {
|
||||||
|
BaseDialect
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMysqlDialect() *Mysql {
|
||||||
|
d := Mysql{}
|
||||||
|
d.BaseDialect.dialect = &d
|
||||||
|
d.BaseDialect.driverName = MYSQL
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Mysql) Quote(name string) string {
|
||||||
|
return "`" + name + "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Mysql) QuoteStr() string {
|
||||||
|
return "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Mysql) AutoIncrStr() string {
|
||||||
|
return "AUTO_INCREMENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Mysql) SqlType(c *Column) string {
|
||||||
|
var res string
|
||||||
|
switch c.Type {
|
||||||
|
case DB_Bool:
|
||||||
|
res = DB_TinyInt
|
||||||
|
c.Length = 1
|
||||||
|
case DB_Serial:
|
||||||
|
c.IsAutoIncrement = true
|
||||||
|
c.IsPrimaryKey = true
|
||||||
|
c.Nullable = false
|
||||||
|
res = DB_Int
|
||||||
|
case DB_BigSerial:
|
||||||
|
c.IsAutoIncrement = true
|
||||||
|
c.IsPrimaryKey = true
|
||||||
|
c.Nullable = false
|
||||||
|
res = DB_BigInt
|
||||||
|
case DB_Bytea:
|
||||||
|
res = DB_Blob
|
||||||
|
case DB_TimeStampz:
|
||||||
|
res = DB_Char
|
||||||
|
c.Length = 64
|
||||||
|
case DB_NVarchar:
|
||||||
|
res = DB_Varchar
|
||||||
|
default:
|
||||||
|
res = c.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasLen1 bool = (c.Length > 0)
|
||||||
|
var hasLen2 bool = (c.Length2 > 0)
|
||||||
|
|
||||||
|
if res == DB_BigInt && !hasLen1 && !hasLen2 {
|
||||||
|
c.Length = 20
|
||||||
|
hasLen1 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasLen2 {
|
||||||
|
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
|
||||||
|
} else if hasLen1 {
|
||||||
|
res += "(" + strconv.Itoa(c.Length) + ")"
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Mysql) ToDBTypeSql(columnType ColumnType, length int) string {
|
||||||
|
switch columnType {
|
||||||
|
case DB_TYPE_STRING:
|
||||||
|
return fmt.Sprintf("NVARCHAR(%d)", length)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Unsupported db type")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import "github.com/go-xorm/core"
|
||||||
|
|
||||||
|
type Sqlite3 struct {
|
||||||
|
BaseDialect
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqlite3Dialect() *Sqlite3 {
|
||||||
|
d := Sqlite3{}
|
||||||
|
d.BaseDialect.dialect = &d
|
||||||
|
d.BaseDialect.driverName = SQLITE
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Sqlite3) Quote(name string) string {
|
||||||
|
return "`" + name + "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Sqlite3) QuoteStr() string {
|
||||||
|
return "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Sqlite3) AutoIncrStr() string {
|
||||||
|
return "AUTOINCREMENT"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Sqlite3) SqlType(c *Column) string {
|
||||||
|
switch c.Type {
|
||||||
|
case DB_Date, DB_DateTime, DB_TimeStamp, DB_Time:
|
||||||
|
return DB_DateTime
|
||||||
|
case DB_TimeStampz:
|
||||||
|
return DB_Text
|
||||||
|
case DB_Char, DB_Varchar, DB_NVarchar, DB_TinyText, DB_Text, DB_MediumText, DB_LongText:
|
||||||
|
return core.Text
|
||||||
|
case DB_Bit, DB_TinyInt, DB_SmallInt, DB_MediumInt, DB_Int, DB_Integer, DB_BigInt, DB_Bool:
|
||||||
|
return DB_Integer
|
||||||
|
case DB_Float, DB_Double, DB_Real:
|
||||||
|
return DB_Real
|
||||||
|
case DB_Decimal, DB_Numeric:
|
||||||
|
return DB_Numeric
|
||||||
|
case DB_TinyBlob, DB_Blob, DB_MediumBlob, DB_LongBlob, DB_Bytea, DB_Binary, DB_VarBinary:
|
||||||
|
return DB_Blob
|
||||||
|
case DB_Serial, DB_BigSerial:
|
||||||
|
c.IsPrimaryKey = true
|
||||||
|
c.IsAutoIncrement = true
|
||||||
|
c.Nullable = false
|
||||||
|
return core.Integer
|
||||||
|
default:
|
||||||
|
return c.Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
|
||||||
|
args := []interface{}{tableName}
|
||||||
|
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
const (
|
||||||
|
POSTGRES = "postgres"
|
||||||
|
SQLITE = "sqlite3"
|
||||||
|
MYSQL = "mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Migration interface {
|
||||||
|
Sql(dialect Dialect) string
|
||||||
|
Id() string
|
||||||
|
SetId(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLType string
|
||||||
|
|
||||||
|
type ColumnType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DB_TYPE_STRING ColumnType = "String"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Table struct {
|
||||||
|
Name string
|
||||||
|
Columns []*Column
|
||||||
|
PrimaryKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IndexType = iota + 1
|
||||||
|
UniqueIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
type Index struct {
|
||||||
|
Name string
|
||||||
|
Type int
|
||||||
|
Cols []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
DB_Bit = "BIT"
|
||||||
|
DB_TinyInt = "TINYINT"
|
||||||
|
DB_SmallInt = "SMALLINT"
|
||||||
|
DB_MediumInt = "MEDIUMINT"
|
||||||
|
DB_Int = "INT"
|
||||||
|
DB_Integer = "INTEGER"
|
||||||
|
DB_BigInt = "BIGINT"
|
||||||
|
|
||||||
|
DB_Enum = "ENUM"
|
||||||
|
DB_Set = "SET"
|
||||||
|
|
||||||
|
DB_Char = "CHAR"
|
||||||
|
DB_Varchar = "VARCHAR"
|
||||||
|
DB_NVarchar = "NVARCHAR"
|
||||||
|
DB_TinyText = "TINYTEXT"
|
||||||
|
DB_Text = "TEXT"
|
||||||
|
DB_MediumText = "MEDIUMTEXT"
|
||||||
|
DB_LongText = "LONGTEXT"
|
||||||
|
DB_Uuid = "UUID"
|
||||||
|
|
||||||
|
DB_Date = "DATE"
|
||||||
|
DB_DateTime = "DATETIME"
|
||||||
|
DB_Time = "TIME"
|
||||||
|
DB_TimeStamp = "TIMESTAMP"
|
||||||
|
DB_TimeStampz = "TIMESTAMPZ"
|
||||||
|
|
||||||
|
DB_Decimal = "DECIMAL"
|
||||||
|
DB_Numeric = "NUMERIC"
|
||||||
|
|
||||||
|
DB_Real = "REAL"
|
||||||
|
DB_Float = "FLOAT"
|
||||||
|
DB_Double = "DOUBLE"
|
||||||
|
|
||||||
|
DB_Binary = "BINARY"
|
||||||
|
DB_VarBinary = "VARBINARY"
|
||||||
|
DB_TinyBlob = "TINYBLOB"
|
||||||
|
DB_Blob = "BLOB"
|
||||||
|
DB_MediumBlob = "MEDIUMBLOB"
|
||||||
|
DB_LongBlob = "LONGBLOB"
|
||||||
|
DB_Bytea = "BYTEA"
|
||||||
|
|
||||||
|
DB_Bool = "BOOL"
|
||||||
|
|
||||||
|
DB_Serial = "SERIAL"
|
||||||
|
DB_BigSerial = "BIGSERIAL"
|
||||||
|
)
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
"github.com/torkelo/grafana-pro/pkg/log"
|
"github.com/torkelo/grafana-pro/pkg/log"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
|
"github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrations"
|
||||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||||
"github.com/torkelo/grafana-pro/pkg/util"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ var (
|
||||||
func init() {
|
func init() {
|
||||||
tables = make([]interface{}, 0)
|
tables = make([]interface{}, 0)
|
||||||
|
|
||||||
tables = append(tables, new(m.Account), new(m.Dashboard),
|
tables = append(tables, new(m.Dashboard),
|
||||||
new(m.Collaborator), new(m.DataSource), new(DashboardTag),
|
new(m.Collaborator), new(m.DataSource), new(DashboardTag),
|
||||||
new(m.Token))
|
new(m.Token))
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +78,13 @@ func NewEngine() {
|
||||||
func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
|
func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
|
||||||
x = engine
|
x = engine
|
||||||
|
|
||||||
|
migrator := migrations.NewMigrator(x)
|
||||||
|
migrations.AddMigrations(migrator)
|
||||||
|
|
||||||
|
if err := migrator.Start(); err != nil {
|
||||||
|
return fmt.Errorf("Sqlstore::Migration failed err: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := x.Sync2(tables...); err != nil {
|
if err := x.Sync2(tables...); err != nil {
|
||||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue