b6bb3098bc
many other updates
627 lines
20 KiB
Go
627 lines
20 KiB
Go
package db
|
||
|
||
import (
|
||
"apigo.cloud/git/apigo/plugin"
|
||
_ "embed"
|
||
"fmt"
|
||
"github.com/ssgo/db"
|
||
"github.com/ssgo/log"
|
||
"github.com/ssgo/u"
|
||
"os"
|
||
"regexp"
|
||
"strings"
|
||
"text/template"
|
||
)
|
||
|
||
type TableFieldDesc struct {
|
||
Field string
|
||
Type string
|
||
Null string
|
||
Key string
|
||
Default string
|
||
Extra string
|
||
After string
|
||
}
|
||
|
||
type TableKeyDesc struct {
|
||
Key_name string
|
||
Column_name string
|
||
}
|
||
|
||
type TableField struct {
|
||
Name string
|
||
Type string
|
||
Index string
|
||
IndexGroup string
|
||
Default string
|
||
Comment string
|
||
IsNull bool
|
||
Extra string
|
||
desc string
|
||
Null string
|
||
}
|
||
|
||
type ERGroup struct {
|
||
Group string
|
||
Comment string
|
||
Tables []TableStruct
|
||
}
|
||
|
||
type TableStruct struct {
|
||
Name string
|
||
Comment string
|
||
Fields []TableField
|
||
}
|
||
|
||
//go:embed er.html
|
||
var erTpl string
|
||
|
||
func (field *TableField) Parse(tableType string) {
|
||
//if field.Index == "autoId" {
|
||
// field.Type += " unsigned"
|
||
//}
|
||
|
||
if field.IsNull {
|
||
field.Null = "NULL"
|
||
} else {
|
||
field.Null = "NOT NULL"
|
||
}
|
||
|
||
if tableType == "sqlite3" {
|
||
// sqlite3 不能修改字段,统一使用NULL
|
||
field.Null = "NULL"
|
||
if field.Extra == "AUTO_INCREMENT" {
|
||
field.Extra = "PRIMARY KEY AUTOINCREMENT"
|
||
field.Type = "integer"
|
||
field.Null = "NOT NULL"
|
||
}
|
||
}
|
||
|
||
a := make([]string, 0)
|
||
a = append(a, fmt.Sprintf("`%s` %s", field.Name, field.Type))
|
||
|
||
if tableType == "mysql" {
|
||
lowerType := strings.ToLower(field.Type)
|
||
if strings.Contains(lowerType, "varchar") || strings.Contains(lowerType, "text") {
|
||
a = append(a, " COLLATE utf8mb4_general_ci")
|
||
}
|
||
}
|
||
//if field.Index == "autoId" {
|
||
// a = append(a, " AUTO_INCREMENT")
|
||
// field.Index = "pk"
|
||
// //a = append(a, " NOT NULL")
|
||
//}
|
||
|
||
//if field.Index == "uniqueId" {
|
||
// field.Index = "pk"
|
||
// //a = append(a, " NOT NULL")
|
||
//}
|
||
|
||
if field.Extra != "" {
|
||
a = append(a, " "+field.Extra)
|
||
}
|
||
a = append(a, " "+field.Null)
|
||
|
||
if field.Default != "" {
|
||
if strings.Contains(field.Default, "CURRENT_TIMESTAMP") {
|
||
a = append(a, " DEFAULT "+field.Default)
|
||
} else {
|
||
a = append(a, " DEFAULT '"+field.Default+"'")
|
||
}
|
||
}
|
||
if tableType == "sqlite3" {
|
||
field.Comment = ""
|
||
field.Type = "numeric"
|
||
} else if tableType == "mysql" {
|
||
if field.Comment != "" {
|
||
a = append(a, " COMMENT '"+field.Comment+"'")
|
||
}
|
||
}
|
||
field.desc = strings.Join(a, "")
|
||
}
|
||
|
||
var ddlTableMatcher = regexp.MustCompile("(?is)^\\s*CREATE\\s+TABLE\\s+`?([\\w]+)`?\\s*\\(\\s*(.*?)\\s*\\);?\\s*$")
|
||
var ddlFieldMatcher = regexp.MustCompile("(?s)\\s*[`\\[]?([\\w]+)[`\\]]?\\s+\\[?([\\w() ]+)\\]?\\s*(.*?)(,|$)")
|
||
var ddlKeyMatcher = regexp.MustCompile("[`\\[]?([\\w]+)[`\\]]?\\s*(,|\\))")
|
||
var ddlNotNullMatcher = regexp.MustCompile("(?i)\\s+NOT NULL")
|
||
var ddlNullMatcher = regexp.MustCompile("(?i)\\s+NULL")
|
||
|
||
// var ddlDefaultMatcher = regexp.MustCompile("(?i)\\s+DEFAULT\\s+(.*?)$")
|
||
var ddlIndexMatcher = regexp.MustCompile("(?is)^\\s*CREATE\\s+([A-Za-z ]+)\\s+`?([\\w]+)`?\\s+ON\\s+`?([\\w]+)`?\\s*\\(\\s*(.*?)\\s*\\);?\\s*$")
|
||
var ddlIndexFieldMatcher = regexp.MustCompile("[`\\[]?([\\w]+)[`\\]]?\\s*(,|$)")
|
||
|
||
// MakeER 创建ER图文件
|
||
func MakeER(groups []ERGroup, outputFile *string, tplFile *string, ctx *plugin.Context) error {
|
||
logger := ctx.GetInject("*log.Logger").(*log.Logger)
|
||
|
||
tplStr := ""
|
||
if tplFile == nil || *tplFile == "" {
|
||
tplStr = erTpl
|
||
} else {
|
||
tplStr = *tplFile
|
||
}
|
||
erOutputFile := "er.html"
|
||
if outputFile != nil && *outputFile != "" {
|
||
erOutputFile = *outputFile
|
||
}
|
||
tpl := template.New(erOutputFile).Funcs(template.FuncMap{
|
||
"short": func(in string) string {
|
||
switch in {
|
||
case "NULL":
|
||
return "n"
|
||
case "NOT NULL":
|
||
return "nn"
|
||
case "AUTO_INCREMENT":
|
||
return "ai"
|
||
case "CURRENT_TIMESTAMP":
|
||
return "ct"
|
||
case "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP":
|
||
return "ctu"
|
||
}
|
||
return in
|
||
},
|
||
})
|
||
var err error
|
||
tpl, err = tpl.Parse(tplStr)
|
||
if err == nil {
|
||
var fp *os.File
|
||
fp, err = os.OpenFile(erOutputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||
if err == nil {
|
||
err = tpl.Execute(fp, map[string]interface{}{
|
||
"title": "ER",
|
||
"groups": groups,
|
||
})
|
||
_ = fp.Close()
|
||
}
|
||
}
|
||
if err != nil {
|
||
logger.Error(err.Error())
|
||
}
|
||
return err
|
||
}
|
||
|
||
func MakeTable(conn *db.DB, table *TableStruct, ctx *plugin.Context) ([]string, error) {
|
||
logger := ctx.GetInject("*log.Logger").(*log.Logger)
|
||
//fmt.Println(u.JsonP(ddlKeyMatcher.FindAllStringSubmatch("(`key`,id, `name` )", 100)), "====================")
|
||
fieldSets := make([]string, 0)
|
||
//fieldSetBy := make(map[string]string)
|
||
pks := make([]string, 0)
|
||
keySets := make([]string, 0)
|
||
keySetBy := make(map[string]string)
|
||
keySetFields := make(map[string]string)
|
||
for i, field := range table.Fields {
|
||
field.Parse(conn.Config.Type)
|
||
table.Fields[i] = field
|
||
|
||
if conn.Config.Type == "sqlite3" {
|
||
if field.Index == "pk" && field.Extra != "PRIMARY KEY AUTOINCREMENT" {
|
||
// sqlite3 用 unique 代替 pk
|
||
field.Index = "unique"
|
||
field.IndexGroup = "0"
|
||
field.Null = "NULL"
|
||
}
|
||
}
|
||
|
||
switch field.Index {
|
||
case "pk":
|
||
if conn.Config.Type == "sqlite3" {
|
||
if field.Extra != "PRIMARY KEY AUTOINCREMENT" {
|
||
pks = append(pks, field.Name)
|
||
}
|
||
} else {
|
||
pks = append(pks, field.Name)
|
||
}
|
||
case "unique":
|
||
keyName := fmt.Sprint("uk_", table.Name, "_", field.Name)
|
||
if field.IndexGroup != "" {
|
||
keyName = fmt.Sprint("uk_", table.Name, "_", field.IndexGroup)
|
||
}
|
||
if keySetBy[keyName] != "" {
|
||
keySetFields[keyName] += " " + field.Name
|
||
// 复合索引
|
||
if conn.Config.Type == "sqlite3" {
|
||
keySetBy[keyName] = strings.Replace(keySetBy[keyName], ")", ", `"+field.Name+"`)", 1)
|
||
} else if conn.Config.Type == "mysql" {
|
||
keySetBy[keyName] = strings.Replace(keySetBy[keyName], ") COMMENT", ", `"+field.Name+"`) COMMENT", 1)
|
||
}
|
||
} else {
|
||
keySetFields[keyName] = field.Name
|
||
keySet := ""
|
||
if conn.Config.Type == "sqlite3" {
|
||
keySet = fmt.Sprintf("CREATE UNIQUE INDEX `%s` ON `%s` (`%s`)", keyName, table.Name, field.Name)
|
||
} else if conn.Config.Type == "mysql" {
|
||
keySet = fmt.Sprintf("UNIQUE KEY `%s` (`%s`) COMMENT '%s'", keyName, field.Name, field.Comment)
|
||
}
|
||
keySets = append(keySets, keySet)
|
||
keySetBy[keyName] = keySet
|
||
}
|
||
case "fulltext":
|
||
if conn.Config.Type == "mysql" {
|
||
keyName := fmt.Sprint("tk_", table.Name, "_", field.Name)
|
||
keySet := fmt.Sprintf("FULLTEXT KEY `%s` (`%s`) COMMENT '%s'", keyName, field.Name, field.Comment)
|
||
keySets = append(keySets, keySet)
|
||
keySetBy[keyName] = keySet
|
||
}
|
||
case "index":
|
||
keyName := fmt.Sprint("ik_", table.Name, "_", field.Name)
|
||
if field.IndexGroup != "" {
|
||
keyName = fmt.Sprint("ik_", table.Name, "_", field.IndexGroup)
|
||
}
|
||
if keySetBy[keyName] != "" {
|
||
keySetFields[keyName] += " " + field.Name
|
||
// 复合索引
|
||
if conn.Config.Type == "sqlite3" {
|
||
keySetBy[keyName] = strings.Replace(keySetBy[keyName], ")", ", `"+field.Name+"`)", 1)
|
||
} else if conn.Config.Type == "mysql" {
|
||
keySetBy[keyName] = strings.Replace(keySetBy[keyName], ") COMMENT", ", `"+field.Name+"`) COMMENT", 1)
|
||
}
|
||
} else {
|
||
keySetFields[keyName] = field.Name
|
||
keySet := ""
|
||
if conn.Config.Type == "sqlite3" {
|
||
keySet = fmt.Sprintf("CREATE INDEX `%s` ON `%s` (`%s`)", keyName, table.Name, field.Name)
|
||
} else if conn.Config.Type == "mysql" {
|
||
keySet = fmt.Sprintf("KEY `%s` (`%s`) COMMENT '%s'", keyName, field.Name, field.Comment)
|
||
}
|
||
keySets = append(keySets, keySet)
|
||
keySetBy[keyName] = keySet
|
||
}
|
||
}
|
||
|
||
fieldSets = append(fieldSets, field.desc)
|
||
//fieldSetBy[field.Name] = field.desc
|
||
}
|
||
//fmt.Println(u.JsonP(table.Fields))
|
||
//fmt.Println(u.JsonP(keySetBy), 3)
|
||
//fmt.Println(u.JsonP(keySets), 4)
|
||
|
||
outSql := make([]string, 0)
|
||
|
||
var result *db.ExecResult
|
||
var tableInfo map[string]interface{}
|
||
if conn.Config.Type == "sqlite3" {
|
||
tableInfo = conn.Query("SELECT `name`, `sql` FROM `sqlite_master` WHERE `type`='table' AND `name`='" + table.Name + "'").MapOnR1()
|
||
tableInfo["comment"] = ""
|
||
} else if conn.Config.Type == "mysql" {
|
||
tableInfo = conn.Query("SELECT TABLE_NAME name, TABLE_COMMENT comment FROM information_schema.TABLES WHERE TABLE_SCHEMA='" + conn.Config.DB + "' AND TABLE_NAME='" + table.Name + "'").MapOnR1()
|
||
}
|
||
oldTableComment := u.String(tableInfo["comment"])
|
||
|
||
if tableInfo["name"] != nil && tableInfo["name"] != "" {
|
||
// 合并字段
|
||
oldFieldList := make([]*TableFieldDesc, 0)
|
||
oldFields := make(map[string]*TableFieldDesc)
|
||
oldIndexes := make(map[string]string)
|
||
oldIndexInfos := make([]*TableKeyDesc, 0)
|
||
|
||
oldComments := map[string]string{}
|
||
if conn.Config.Type == "sqlite3" {
|
||
tableM := ddlTableMatcher.FindStringSubmatch(u.String(tableInfo["sql"]))
|
||
if tableM != nil {
|
||
fieldsM := ddlFieldMatcher.FindAllStringSubmatch(tableM[2], 2000)
|
||
if fieldsM != nil {
|
||
for _, m := range fieldsM {
|
||
if m[1] == "PRIMARY" && m[2] == "KEY" {
|
||
keysM := ddlKeyMatcher.FindAllStringSubmatch(m[3], 20)
|
||
if keysM != nil {
|
||
for _, km := range keysM {
|
||
oldIndexInfos = append(oldIndexInfos, &TableKeyDesc{
|
||
Key_name: "PRIMARY",
|
||
Column_name: km[1],
|
||
})
|
||
}
|
||
}
|
||
} else {
|
||
Null := "NULL"
|
||
//fmt.Println(" =====", m[0], m[1], m[2])
|
||
if ddlNotNullMatcher.MatchString(m[2]) {
|
||
m[2] = ddlNotNullMatcher.ReplaceAllString(m[2], "")
|
||
Null = "NOT NULL"
|
||
} else if ddlNullMatcher.MatchString(m[2]) {
|
||
m[2] = ddlNullMatcher.ReplaceAllString(m[2], "")
|
||
Null = "NULL"
|
||
}
|
||
//fmt.Println(" =====", m[2], "|", Null)
|
||
|
||
oldFieldList = append(oldFieldList, &TableFieldDesc{
|
||
Field: m[1],
|
||
Type: m[2],
|
||
//Null: u.StringIf(strings.Contains(m[3], "NOT NULL"), "NO", "YES"),
|
||
Null: u.StringIf(Null == "NOT NULL", "NO", "YES"),
|
||
Key: "",
|
||
Default: "",
|
||
Extra: "",
|
||
After: "",
|
||
})
|
||
}
|
||
}
|
||
}
|
||
//fmt.Println(u.JsonP(fieldsM), 222)
|
||
}
|
||
|
||
// 读取索引信息
|
||
for _, indexInfo := range conn.Query("SELECT `name`, `sql` FROM `sqlite_master` WHERE `type`='index' AND `tbl_name`='" + table.Name + "'").StringMapResults() {
|
||
//fmt.Println(u.JsonP(indexInfo), 777)
|
||
indexM := ddlIndexMatcher.FindStringSubmatch(indexInfo["sql"])
|
||
if indexM != nil {
|
||
//fmt.Println(u.JsonP(indexM), 666)
|
||
indexFieldM := ddlIndexFieldMatcher.FindAllStringSubmatch(indexM[4], 20)
|
||
//fmt.Println(u.JsonP(indexFieldM), 555)
|
||
if indexFieldM != nil {
|
||
for _, km := range indexFieldM {
|
||
oldIndexInfos = append(oldIndexInfos, &TableKeyDesc{
|
||
Key_name: indexInfo["name"],
|
||
Column_name: km[1],
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//fmt.Println(u.JsonP(oldFieldList), 1)
|
||
//fmt.Println(u.JsonP(oldIndexInfos), 2)
|
||
} else if conn.Config.Type == "mysql" {
|
||
conn.Query("SELECT column_name, column_comment FROM information_schema.columns WHERE TABLE_SCHEMA='" + conn.Config.DB + "' AND TABLE_NAME='" + table.Name + "'").ToKV(&oldComments)
|
||
_ = conn.Query("DESC `" + table.Name + "`").To(&oldFieldList)
|
||
_ = conn.Query("SHOW INDEX FROM `" + table.Name + "`").To(&oldIndexInfos)
|
||
}
|
||
//fmt.Println(u.JsonP(oldComments), 111)
|
||
|
||
for _, indexInfo := range oldIndexInfos {
|
||
if oldIndexes[indexInfo.Key_name] == "" {
|
||
oldIndexes[indexInfo.Key_name] = indexInfo.Column_name
|
||
} else {
|
||
oldIndexes[indexInfo.Key_name] += " " + indexInfo.Column_name
|
||
}
|
||
}
|
||
//fmt.Println(u.JsonP(oldIndexes), 111)
|
||
//fmt.Println(u.JsonP(keySetFields), 222)
|
||
//fmt.Println(u.JsonP(keySetBy), 333)
|
||
|
||
// 先后顺序
|
||
prevFieldId := ""
|
||
for _, field := range oldFieldList {
|
||
if conn.Config.Type == "sqlite3" {
|
||
field.Type = "numeric"
|
||
} else if conn.Config.Type == "mysql" {
|
||
field.After = prevFieldId
|
||
}
|
||
prevFieldId = field.Field
|
||
oldFields[field.Field] = field
|
||
}
|
||
//fmt.Println(111, u.JsonP(oldFields), 111)
|
||
|
||
actions := make([]string, 0)
|
||
for keyId := range oldIndexes {
|
||
if keyId != "PRIMARY" && strings.ToLower(keySetFields[keyId]) != strings.ToLower(oldIndexes[keyId]) {
|
||
if conn.Config.Type == "sqlite3" {
|
||
actions = append(actions, "DROP INDEX `"+keyId+"`")
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "DROP KEY `"+keyId+"`")
|
||
}
|
||
}
|
||
}
|
||
//fmt.Println(" =>>>>>>>>", oldIndexes, pks)
|
||
if oldIndexes["PRIMARY"] != "" && strings.ToLower(oldIndexes["PRIMARY"]) != strings.ToLower(strings.Join(pks, " ")) {
|
||
if conn.Config.Type == "sqlite3" {
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "DROP PRIMARY KEY")
|
||
}
|
||
}
|
||
//for fieldId, fieldSet := range fieldSetBy {
|
||
newFieldExists := map[string]bool{}
|
||
prevFieldId = ""
|
||
for _, field := range table.Fields {
|
||
newFieldExists[field.Name] = true
|
||
oldField := oldFields[field.Name]
|
||
|
||
// 修复部分数据库的特殊性
|
||
if oldField == nil {
|
||
if conn.Config.Type == "sqlite3" {
|
||
actions = append(actions, "ALTER TABLE `"+table.Name+"` ADD COLUMN "+field.desc)
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "ADD COLUMN "+field.desc)
|
||
}
|
||
} else {
|
||
oldField.Type = strings.TrimSpace(strings.ReplaceAll(oldField.Type, " (", "("))
|
||
fixedOldDefault := u.String(oldField.Default)
|
||
if fixedOldDefault == "CURRENT_TIMESTAMP" && strings.Contains(oldField.Extra, "on update CURRENT_TIMESTAMP") {
|
||
fixedOldDefault = "CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||
}
|
||
fixedOldNull := "NOT NULL"
|
||
if oldField.Null == "YES" {
|
||
fixedOldNull = "NULL"
|
||
}
|
||
//fmt.Println(" ==", field.Type, "!=", oldField.Type, "||", field.Default, "!=", fixedOldDefault, "||", field.Null, "!=", fixedOldNull, "||", oldField.After, "!=", prevFieldId, "||", oldComments[field.Name], "!=", field.Comment)
|
||
//fmt.Println(" ==", strings.ToLower(field.Type) != strings.ToLower(oldField.Type), strings.ToLower(field.Default) != strings.ToLower(fixedOldDefault), strings.ToLower(field.Null) != strings.ToLower(fixedOldNull), strings.ToLower(oldField.After) != strings.ToLower(prevFieldId), strings.ToLower(oldComments[field.Name]) != strings.ToLower(field.Comment))
|
||
if strings.ToLower(field.Type) != strings.ToLower(oldField.Type) || strings.ToLower(field.Default) != strings.ToLower(fixedOldDefault) || strings.ToLower(field.Null) != strings.ToLower(fixedOldNull) || strings.ToLower(oldField.After) != strings.ToLower(prevFieldId) || strings.ToLower(oldComments[field.Name]) != strings.ToLower(field.Comment) {
|
||
//fmt.Println(" > > > > ", u.JsonP(oldField), 1111)
|
||
// `t4f34` varchar(100) COLLATE utf8mb4_general_ci COMMENT ''
|
||
// f34, varchar(100), YES, , ,
|
||
//fmt.Println(111111, u.JsonP(field), 1111)
|
||
// 为什么Desc是空????
|
||
after := ""
|
||
if conn.Config.Type == "mysql" {
|
||
if oldField.After != prevFieldId {
|
||
if prevFieldId == "" {
|
||
after = " FIRST"
|
||
} else {
|
||
after = " AFTER `" + prevFieldId + "`"
|
||
}
|
||
}
|
||
}
|
||
//DROP INDEX `uk_config_key`;
|
||
//ALTER TABLE `config` RENAME COLUMN `key` TO `keyOld`;
|
||
//ALTER TABLE `config` ADD COLUMN `key` varchar(30) NULL;
|
||
//UPDATE `config` SET `key`=`keyOld`;
|
||
//ALTER TABLE `config` DROP COLUMN `keyOld`;
|
||
//CREATE INDEX `uk_config_key` ON `config` (`key`);
|
||
if conn.Config.Type == "sqlite3" {
|
||
// 不支持修改字段,所以要先创建然后复制数据再删除
|
||
|
||
// 方案一(已放弃)重新创建表实现修改
|
||
//actions = append(actions, fmt.Sprintf("CREATE TABLE `%s_temp` (\n%s\n)", table.Name, strings.Join(fieldSets, ",\n")))
|
||
//actions = append(actions, fmt.Sprintf("INSERT INTO `%s_temp` SELECT * FROM `%s`", table.Name, table.Name))
|
||
//actions = append(actions, fmt.Sprintf("DROP TABLE `%s`", table.Name))
|
||
//actions = append(actions, fmt.Sprintf("ALTER TABLE `%s_temp` RENAME TO `%s`", table.Name, table.Name))
|
||
//INSERT INTO t1_new SELECT foo, bar, baz FROM t1;
|
||
//DROP TABLE t1;
|
||
//ALTER TABLE t1_new RENAME TO t1;
|
||
|
||
// 方案二(已放弃)创建新字段复制数据后删除(部分Sqlite不支持DROP COLUMN)
|
||
//redoIndexes := make([]string, 0)
|
||
//for oldIndexName, oldIndex := range oldIndexes {
|
||
// if u.StringIn(strings.Split(oldIndex, " "), field.Name) {
|
||
// indexSql := conn.Query("SELECT `sql` FROM `sqlite_master` WHERE `type`='index' AND `name`='" + oldIndexName + "'").StringOnR1C1()
|
||
// redoIndexes = append(redoIndexes, indexSql)
|
||
// actions = append(actions, "DROP INDEX `"+oldIndexName+"`")
|
||
// }
|
||
//}
|
||
//oldPostfix := u.UniqueId()
|
||
//actions = append(actions, "ALTER TABLE `"+table.Name+"` RENAME COLUMN `"+field.Name+"` TO `d_"+field.Name+"_"+oldPostfix+"`")
|
||
//actions = append(actions, "ALTER TABLE `"+table.Name+"` ADD COLUMN "+field.Desc+after)
|
||
//actions = append(actions, "UPDATE `"+table.Name+"` SET `"+field.Name+"`=`d_"+field.Name+"_"+oldPostfix+"`")
|
||
////actions = append(actions, "ALTER TABLE `"+table.Name+"` DROP COLUMN `"+field.Name+"Old`")
|
||
//for _, redoIndex := range redoIndexes {
|
||
// actions = append(actions, redoIndex)
|
||
//}
|
||
|
||
// 方案三 不修改字段类型,Sqlite可以兼容
|
||
|
||
//actions = append(actions, "ALTER TABLE `"+table.Name+"` ADD COLUMN "+field.desc)
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "CHANGE `"+field.Name+"` "+field.desc+after)
|
||
}
|
||
}
|
||
}
|
||
|
||
if conn.Config.Type == "mysql" {
|
||
prevFieldId = field.Name
|
||
}
|
||
}
|
||
|
||
for oldFieldName := range oldFields {
|
||
if newFieldExists[oldFieldName] != true {
|
||
if conn.Config.Type == "sqlite3" {
|
||
//actions = append(actions, "ALTER TABLE `"+table.Name+"` DROP COLUMN `"+oldFieldName+"`")
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "DROP COLUMN `"+oldFieldName+"`")
|
||
}
|
||
}
|
||
}
|
||
|
||
// sqlite3 不支持添加主键
|
||
if conn.Config.Type == "mysql" {
|
||
if len(pks) > 0 && strings.ToLower(oldIndexes["PRIMARY"]) != strings.ToLower(strings.Join(pks, " ")) {
|
||
actions = append(actions, "ADD PRIMARY KEY(`"+strings.Join(pks, "`,`")+"`)")
|
||
}
|
||
}
|
||
|
||
//fmt.Println(111, u.JsonP(oldIndexes), 222 )
|
||
//fmt.Println(222, u.JsonP(keySetBy), 222 )
|
||
for keyId, keySet := range keySetBy {
|
||
if oldIndexes[keyId] == "" || strings.ToLower(oldIndexes[keyId]) != strings.ToLower(keySetFields[keyId]) {
|
||
if conn.Config.Type == "sqlite3" {
|
||
actions = append(actions, keySet)
|
||
} else if conn.Config.Type == "mysql" {
|
||
actions = append(actions, "ADD "+keySet)
|
||
}
|
||
}
|
||
}
|
||
|
||
//fmt.Println(" =>", table.Comment, "|", oldTableComment )
|
||
if conn.Config.Type == "mysql" {
|
||
if table.Comment != oldTableComment {
|
||
actions = append(actions, "COMMENT '"+table.Comment+"'")
|
||
}
|
||
}
|
||
|
||
if len(actions) == 0 {
|
||
// 不需要更新
|
||
return outSql, nil
|
||
}
|
||
|
||
tx := conn.Begin()
|
||
defer tx.CheckFinished()
|
||
if conn.Config.Type == "sqlite3" {
|
||
for _, action := range actions {
|
||
//fmt.Println(u.Dim("\t" + strings.ReplaceAll(action, "\n", "\n\t")))
|
||
outSql = append(outSql, action)
|
||
result = tx.Exec(action)
|
||
if result.Error != nil {
|
||
break
|
||
}
|
||
}
|
||
} else if conn.Config.Type == "mysql" {
|
||
sql := "ALTER TABLE `" + table.Name + "` " + strings.Join(actions, "\n,") + ";"
|
||
//fmt.Println(u.Dim("\t" + strings.ReplaceAll(sql, "\n", "\n\t")))
|
||
outSql = append(outSql, sql)
|
||
result = tx.Exec(sql)
|
||
}
|
||
if result.Error != nil {
|
||
_ = tx.Rollback()
|
||
} else {
|
||
_ = tx.Commit()
|
||
}
|
||
} else {
|
||
// 创建新表
|
||
if len(pks) > 0 {
|
||
fieldSets = append(fieldSets, "PRIMARY KEY (`"+strings.Join(pks, "`,`")+"`)")
|
||
}
|
||
|
||
indexSets := make([]string, 0) // sqlite3 额外创建索引的sql
|
||
if conn.Config.Type == "sqlite3" {
|
||
for _, indexSql := range keySetBy {
|
||
indexSets = append(indexSets, indexSql)
|
||
}
|
||
} else if conn.Config.Type == "mysql" {
|
||
for _, key := range keySets {
|
||
fieldSets = append(fieldSets, key)
|
||
}
|
||
}
|
||
|
||
sql := ""
|
||
|
||
if conn.Config.Type == "sqlite3" {
|
||
sql = fmt.Sprintf("CREATE TABLE `%s` (\n %s\n);", table.Name, strings.Join(fieldSets, ",\n "))
|
||
} else if conn.Config.Type == "mysql" {
|
||
sql = fmt.Sprintf("CREATE TABLE `%s` (\n %s\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='%s';", table.Name, strings.Join(fieldSets, ",\n "), table.Comment)
|
||
}
|
||
tx := conn.Begin()
|
||
defer tx.CheckFinished()
|
||
//fmt.Println(u.Dim("\t" + strings.ReplaceAll(sql, "\n", "\n\t")))
|
||
outSql = append(outSql, sql)
|
||
result = tx.Exec(sql)
|
||
|
||
if result.Error == nil {
|
||
if conn.Config.Type == "sqlite3" {
|
||
for _, indexSet := range indexSets {
|
||
//fmt.Println(indexSet)
|
||
//fmt.Println(u.Dim("\t" + strings.ReplaceAll(indexSet, "\n", "\n\t")))
|
||
outSql = append(outSql, indexSet)
|
||
r := tx.Exec(indexSet)
|
||
if r.Error != nil {
|
||
result = r
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if result.Error != nil {
|
||
_ = tx.Rollback()
|
||
} else {
|
||
_ = tx.Commit()
|
||
}
|
||
|
||
}
|
||
|
||
if result == nil {
|
||
return outSql, nil
|
||
}
|
||
|
||
if result.Error != nil {
|
||
logger.Error(result.Error.Error())
|
||
}
|
||
|
||
return outSql, result.Error
|
||
}
|