2020-11-16 23:30:47 +08:00
|
|
|
|
package setup
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2021-08-02 15:23:02 +08:00
|
|
|
|
"fmt"
|
2020-11-25 21:19:22 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
2020-11-16 23:30:47 +08:00
|
|
|
|
"github.com/iwind/TeaGo/dbs"
|
2022-08-13 23:55:48 +08:00
|
|
|
|
"github.com/iwind/TeaGo/lists"
|
2020-11-16 23:30:47 +08:00
|
|
|
|
"github.com/iwind/TeaGo/types"
|
|
|
|
|
|
"regexp"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var recordsTables = []*SQLRecordsTable{
|
|
|
|
|
|
{
|
|
|
|
|
|
TableName: "edgeRegionCities",
|
|
|
|
|
|
UniqueFields: []string{"name", "provinceId"},
|
2022-08-13 23:55:48 +08:00
|
|
|
|
ExceptFields: []string{"customName", "customCodes"},
|
2020-11-16 23:30:47 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
TableName: "edgeRegionCountries",
|
|
|
|
|
|
UniqueFields: []string{"name"},
|
2022-08-13 23:55:48 +08:00
|
|
|
|
ExceptFields: []string{"customName", "customCodes"},
|
2020-11-16 23:30:47 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
TableName: "edgeRegionProvinces",
|
|
|
|
|
|
UniqueFields: []string{"name", "countryId"},
|
2022-08-13 23:55:48 +08:00
|
|
|
|
ExceptFields: []string{"customName", "customCodes"},
|
2020-11-16 23:30:47 +08:00
|
|
|
|
},
|
2021-01-25 16:40:03 +08:00
|
|
|
|
{
|
|
|
|
|
|
TableName: "edgeRegionProviders",
|
|
|
|
|
|
UniqueFields: []string{"name"},
|
2022-08-13 23:55:48 +08:00
|
|
|
|
ExceptFields: []string{"customName", "customCodes"},
|
2021-01-25 16:40:03 +08:00
|
|
|
|
},
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type SQLDump struct {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewSQLDump() *SQLDump {
|
|
|
|
|
|
return &SQLDump{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-18 16:32:08 +08:00
|
|
|
|
// Dump 导出数据
|
2020-11-16 23:30:47 +08:00
|
|
|
|
func (this *SQLDump) Dump(db *dbs.DB) (result *SQLDumpResult, err error) {
|
|
|
|
|
|
result = &SQLDumpResult{}
|
|
|
|
|
|
|
|
|
|
|
|
tableNames, err := db.TableNames()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return result, err
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, tableName := range tableNames {
|
|
|
|
|
|
// 忽略一些分表
|
2022-03-08 19:55:39 +08:00
|
|
|
|
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeHTTPAccessLogs_")) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeNSAccessLogs_")) {
|
2020-11-16 23:30:47 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
table, err := db.FindFullTable(tableName)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
sqlTable := &SQLTable{
|
|
|
|
|
|
Name: table.Name,
|
|
|
|
|
|
Engine: table.Engine,
|
|
|
|
|
|
Charset: table.Collation,
|
|
|
|
|
|
Definition: regexp.MustCompile(" AUTO_INCREMENT=\\d+").ReplaceAllString(table.Code, ""),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 字段
|
|
|
|
|
|
fields := []*SQLField{}
|
|
|
|
|
|
for _, field := range table.Fields {
|
|
|
|
|
|
fields = append(fields, &SQLField{
|
|
|
|
|
|
Name: field.Name,
|
|
|
|
|
|
Definition: field.Definition(),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
sqlTable.Fields = fields
|
|
|
|
|
|
|
|
|
|
|
|
// 索引
|
|
|
|
|
|
indexes := []*SQLIndex{}
|
|
|
|
|
|
for _, index := range table.Indexes {
|
|
|
|
|
|
indexes = append(indexes, &SQLIndex{
|
|
|
|
|
|
Name: index.Name,
|
|
|
|
|
|
Definition: index.Definition(),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
sqlTable.Indexes = indexes
|
|
|
|
|
|
|
|
|
|
|
|
// Records
|
|
|
|
|
|
records := []*SQLRecord{}
|
|
|
|
|
|
recordsTable := this.findRecordsTable(tableName)
|
|
|
|
|
|
if recordsTable != nil {
|
|
|
|
|
|
ones, _, err := db.FindOnes("SELECT * FROM " + tableName + " ORDER BY id ASC")
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return result, err
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, one := range ones {
|
|
|
|
|
|
record := &SQLRecord{
|
|
|
|
|
|
Id: one.GetInt64("id"),
|
|
|
|
|
|
Values: map[string]string{},
|
|
|
|
|
|
UniqueFields: recordsTable.UniqueFields,
|
2022-08-13 23:55:48 +08:00
|
|
|
|
ExceptFields: recordsTable.ExceptFields,
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
for k, v := range one {
|
2022-08-13 23:55:48 +08:00
|
|
|
|
// 需要排除的字段
|
|
|
|
|
|
if lists.ContainsString(record.ExceptFields, k) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-16 23:30:47 +08:00
|
|
|
|
record.Values[k] = types.String(v)
|
|
|
|
|
|
}
|
|
|
|
|
|
records = append(records, record)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
sqlTable.Records = records
|
|
|
|
|
|
|
|
|
|
|
|
result.Tables = append(result.Tables, sqlTable)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-18 16:32:08 +08:00
|
|
|
|
// Apply 应用数据
|
2021-08-02 15:23:02 +08:00
|
|
|
|
func (this *SQLDump) Apply(db *dbs.DB, newResult *SQLDumpResult, showLog bool) (ops []string, err error) {
|
2020-11-16 23:30:47 +08:00
|
|
|
|
currentResult, err := this.Dump(db)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新增表格
|
|
|
|
|
|
for _, newTable := range newResult.Tables {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var oldTable = currentResult.FindTable(newTable.Name)
|
2020-11-16 23:30:47 +08:00
|
|
|
|
if oldTable == nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "+ table " + newTable.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
_, err = db.Exec(newTable.Definition)
|
|
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if oldTable.Definition != newTable.Definition {
|
|
|
|
|
|
// 对比字段
|
|
|
|
|
|
// +
|
|
|
|
|
|
for _, newField := range newTable.Fields {
|
|
|
|
|
|
oldField := oldTable.FindField(newField.Name)
|
|
|
|
|
|
if oldField == nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "+ " + newTable.Name + " " + newField.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-12-09 20:44:05 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD `" + newField.Name + "` " + newField.Definition)
|
2020-11-16 23:30:47 +08:00
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
2020-12-09 20:44:05 +08:00
|
|
|
|
} else if !newField.EqualDefinition(oldField.Definition) {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "* " + newTable.Name + " " + newField.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-12-09 20:44:05 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + newTable.Name + " MODIFY `" + newField.Name + "` " + newField.Definition)
|
2020-11-16 23:30:47 +08:00
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 对比索引
|
|
|
|
|
|
// +
|
|
|
|
|
|
for _, newIndex := range newTable.Indexes {
|
|
|
|
|
|
oldIndex := oldTable.FindIndex(newIndex.Name)
|
|
|
|
|
|
if oldIndex == nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "+ index " + newTable.Name + " " + newIndex.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD " + newIndex.Definition)
|
|
|
|
|
|
if err != nil {
|
2021-08-06 14:22:17 +08:00
|
|
|
|
err = this.tryCreateIndex(err, db, newTable.Name, newIndex.Definition)
|
|
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2021-08-06 14:22:17 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if oldIndex.Definition != newIndex.Definition {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "* index " + newTable.Name + " " + newIndex.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + newTable.Name + " DROP KEY " + newIndex.Name)
|
|
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' drop old key failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD " + newIndex.Definition)
|
|
|
|
|
|
if err != nil {
|
2021-08-06 14:22:17 +08:00
|
|
|
|
err = this.tryCreateIndex(err, db, newTable.Name, newIndex.Definition)
|
|
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2021-08-06 14:22:17 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// -
|
|
|
|
|
|
for _, oldIndex := range oldTable.Indexes {
|
|
|
|
|
|
newIndex := newTable.FindIndex(oldIndex.Name)
|
|
|
|
|
|
if newIndex == nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "- index " + oldTable.Name + " " + oldIndex.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + oldTable.Name + " DROP KEY " + oldIndex.Name)
|
|
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 对比字段
|
|
|
|
|
|
// -
|
|
|
|
|
|
for _, oldField := range oldTable.Fields {
|
|
|
|
|
|
newField := newTable.FindField(oldField.Name)
|
|
|
|
|
|
if newField == nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
var op = "- field " + oldTable.Name + " " + oldField.Name
|
|
|
|
|
|
ops = append(ops, op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
fmt.Println(op)
|
2021-08-02 15:23:02 +08:00
|
|
|
|
}
|
2020-12-09 20:44:05 +08:00
|
|
|
|
_, err = db.Exec("ALTER TABLE " + oldTable.Name + " DROP COLUMN `" + oldField.Name + "`")
|
2020-11-16 23:30:47 +08:00
|
|
|
|
if err != nil {
|
2022-01-30 10:11:45 +08:00
|
|
|
|
return nil, errors.New("'" + op + "' failed: " + err.Error())
|
2020-11-16 23:30:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 对比记录
|
|
|
|
|
|
// +
|
|
|
|
|
|
for _, record := range newTable.Records {
|
2022-08-13 23:55:48 +08:00
|
|
|
|
var queryArgs = []string{}
|
|
|
|
|
|
var queryValues = []interface{}{}
|
|
|
|
|
|
var valueStrings = []string{}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
for _, field := range record.UniqueFields {
|
|
|
|
|
|
queryArgs = append(queryArgs, field+"=?")
|
|
|
|
|
|
queryValues = append(queryValues, record.Values[field])
|
|
|
|
|
|
valueStrings = append(valueStrings, record.Values[field])
|
|
|
|
|
|
}
|
|
|
|
|
|
one, err := db.FindOne("SELECT * FROM "+newTable.Name+" WHERE "+strings.Join(queryArgs, " AND "), queryValues...)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
if one == nil {
|
|
|
|
|
|
ops = append(ops, "+ record "+newTable.Name+" "+strings.Join(valueStrings, ", "))
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
|
|
|
|
|
fmt.Println("+ record " + newTable.Name + " " + strings.Join(valueStrings, ", "))
|
|
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
params := []string{}
|
|
|
|
|
|
args := []string{}
|
|
|
|
|
|
values := []interface{}{}
|
|
|
|
|
|
for k, v := range record.Values {
|
2022-08-13 23:55:48 +08:00
|
|
|
|
// 需要排除的字段
|
|
|
|
|
|
if lists.ContainsString(record.ExceptFields, k) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-17 10:26:31 +08:00
|
|
|
|
// ID需要保留,因为各个表格之间需要有对应关系
|
2020-11-16 23:30:47 +08:00
|
|
|
|
params = append(params, "`"+k+"`")
|
|
|
|
|
|
args = append(args, "?")
|
|
|
|
|
|
values = append(values, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
_, err = db.Exec("INSERT INTO "+newTable.Name+" ("+strings.Join(params, ", ")+") VALUES ("+strings.Join(args, ", ")+")", values...)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if !record.ValuesEquals(one) {
|
|
|
|
|
|
ops = append(ops, "* record "+newTable.Name+" "+strings.Join(valueStrings, ", "))
|
2021-08-02 15:23:02 +08:00
|
|
|
|
if showLog {
|
|
|
|
|
|
fmt.Println("* record " + newTable.Name + " " + strings.Join(valueStrings, ", "))
|
|
|
|
|
|
}
|
2020-11-16 23:30:47 +08:00
|
|
|
|
args := []string{}
|
|
|
|
|
|
values := []interface{}{}
|
|
|
|
|
|
for k, v := range record.Values {
|
|
|
|
|
|
if k == "id" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2022-08-13 23:55:48 +08:00
|
|
|
|
|
|
|
|
|
|
// 需要排除的字段
|
|
|
|
|
|
if lists.ContainsString(record.ExceptFields, k) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-16 23:30:47 +08:00
|
|
|
|
args = append(args, k+"=?")
|
|
|
|
|
|
values = append(values, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
values = append(values, one.GetInt("id"))
|
2020-11-17 10:26:31 +08:00
|
|
|
|
_, err = db.Exec("UPDATE "+newTable.Name+" SET "+strings.Join(args, ", ")+" WHERE id=?", values...)
|
2020-11-16 23:30:47 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 减少表格
|
|
|
|
|
|
// 由于我们不删除任何表格,所以这里什么都不做
|
|
|
|
|
|
|
2020-11-25 21:19:22 +08:00
|
|
|
|
// 升级数据
|
|
|
|
|
|
err = UpgradeSQLData(db)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, errors.New("upgrade data failed: " + err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-16 23:30:47 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找有记录的表
|
|
|
|
|
|
func (this *SQLDump) findRecordsTable(tableName string) *SQLRecordsTable {
|
|
|
|
|
|
for _, table := range recordsTables {
|
|
|
|
|
|
if table.TableName == tableName {
|
|
|
|
|
|
return table
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2021-08-06 14:22:17 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建索引
|
|
|
|
|
|
func (this *SQLDump) tryCreateIndex(err error, db *dbs.DB, tableName string, indexDefinition string) error {
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理Duplicate entry
|
2021-08-07 16:11:35 +08:00
|
|
|
|
if strings.Contains(err.Error(), "Error 1062: Duplicate entry") && (strings.HasSuffix(tableName, "Stats") || strings.HasSuffix(tableName, "Values")) {
|
2021-08-06 14:22:17 +08:00
|
|
|
|
var tries = 5 // 尝试次数
|
|
|
|
|
|
for i := 0; i < tries; i++ {
|
|
|
|
|
|
_, err = db.Exec("TRUNCATE TABLE " + tableName)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if i == tries-1 {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
_, err = db.Exec("ALTER TABLE " + tableName + " ADD " + indexDefinition)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
if i == tries-1 {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|