mirror of https://github.com/grafana/grafana
parent
7d70ffe201
commit
d8e5be5782
@ -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 |
||||
|
||||
import "fmt" |
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
type Dialect interface { |
||||
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{}) |
||||
} |
||||
|
||||
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 { |
||||
return SQLITE |
||||
func (b *BaseDialect) ShowCreateNull() bool { |
||||
return true |
||||
} |
||||
|
||||
func (db *Mysql) DriverName() string { |
||||
return MYSQL |
||||
func (b *BaseDialect) AndStr() string { |
||||
return "AND" |
||||
} |
||||
|
||||
func (db *Sqlite3) ToDBTypeSql(columnType ColumnType, length int) string { |
||||
switch columnType { |
||||
case DB_TYPE_STRING: |
||||
return "TEXT" |
||||
} |
||||
func (b *BaseDialect) OrStr() string { |
||||
return "OR" |
||||
} |
||||
|
||||
panic("Unsupported db type") |
||||
func (b *BaseDialect) EqStr() string { |
||||
return "=" |
||||
} |
||||
|
||||
func (db *Mysql) ToDBTypeSql(columnType ColumnType, length int) string { |
||||
switch columnType { |
||||
case DB_TYPE_STRING: |
||||
return fmt.Sprintf("NVARCHAR(%d)", length) |
||||
func (b *BaseDialect) CreateTableSql(table *Table) string { |
||||
var sql string |
||||
sql = "CREATE TABLE IF NOT EXISTS " |
||||
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, " |
||||
} |
||||
|
||||
if len(pkList) > 1 { |
||||
sql += "PRIMARY KEY ( " |
||||
sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(","))) |
||||
sql += " ), " |
||||
} |
||||
|
||||
panic("Unsupported db type") |
||||
sql = sql[:len(sql)-2] + ")" |
||||
sql += ";" |
||||
return sql |
||||
} |
||||
|
||||
func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) { |
||||
args := []interface{}{tableName} |
||||
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args |
||||
func (db *BaseDialect) AddColumnSql(tableName string, col *Column) string { |
||||
return fmt.Sprintf("alter table %s ADD COLUMN %s", tableName, col.StringNoPk(db.dialect)) |
||||
} |
||||
|
||||
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 |
||||
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(",")))) |
||||
} |
||||
|
||||
@ -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" |
||||
) |
||||
Loading…
Reference in new issue