Files
EdgeAPI/internal/setup/sql_dump.go
2022-08-13 23:55:48 +08:00

363 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package setup
import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
)
var recordsTables = []*SQLRecordsTable{
{
TableName: "edgeRegionCities",
UniqueFields: []string{"name", "provinceId"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionCountries",
UniqueFields: []string{"name"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionProvinces",
UniqueFields: []string{"name", "countryId"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionProviders",
UniqueFields: []string{"name"},
ExceptFields: []string{"customName", "customCodes"},
},
}
type SQLDump struct {
}
func NewSQLDump() *SQLDump {
return &SQLDump{}
}
// Dump 导出数据
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 {
// 忽略一些分表
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeHTTPAccessLogs_")) {
continue
}
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeNSAccessLogs_")) {
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,
ExceptFields: recordsTable.ExceptFields,
}
for k, v := range one {
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
record.Values[k] = types.String(v)
}
records = append(records, record)
}
}
sqlTable.Records = records
result.Tables = append(result.Tables, sqlTable)
}
return
}
// Apply 应用数据
func (this *SQLDump) Apply(db *dbs.DB, newResult *SQLDumpResult, showLog bool) (ops []string, err error) {
currentResult, err := this.Dump(db)
if err != nil {
return nil, err
}
// 新增表格
for _, newTable := range newResult.Tables {
var oldTable = currentResult.FindTable(newTable.Name)
if oldTable == nil {
var op = "+ table " + newTable.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec(newTable.Definition)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
} else if oldTable.Definition != newTable.Definition {
// 对比字段
// +
for _, newField := range newTable.Fields {
oldField := oldTable.FindField(newField.Name)
if oldField == nil {
var op = "+ " + newTable.Name + " " + newField.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD `" + newField.Name + "` " + newField.Definition)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
} else if !newField.EqualDefinition(oldField.Definition) {
var op = "* " + newTable.Name + " " + newField.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + newTable.Name + " MODIFY `" + newField.Name + "` " + newField.Definition)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
}
}
// 对比索引
// +
for _, newIndex := range newTable.Indexes {
oldIndex := oldTable.FindIndex(newIndex.Name)
if oldIndex == nil {
var op = "+ index " + newTable.Name + " " + newIndex.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD " + newIndex.Definition)
if err != nil {
err = this.tryCreateIndex(err, db, newTable.Name, newIndex.Definition)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
}
} else if oldIndex.Definition != newIndex.Definition {
var op = "* index " + newTable.Name + " " + newIndex.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + newTable.Name + " DROP KEY " + newIndex.Name)
if err != nil {
return nil, errors.New("'" + op + "' drop old key failed: " + err.Error())
}
_, err = db.Exec("ALTER TABLE " + newTable.Name + " ADD " + newIndex.Definition)
if err != nil {
err = this.tryCreateIndex(err, db, newTable.Name, newIndex.Definition)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
}
}
}
// -
for _, oldIndex := range oldTable.Indexes {
newIndex := newTable.FindIndex(oldIndex.Name)
if newIndex == nil {
var op = "- index " + oldTable.Name + " " + oldIndex.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + oldTable.Name + " DROP KEY " + oldIndex.Name)
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
}
}
// 对比字段
// -
for _, oldField := range oldTable.Fields {
newField := newTable.FindField(oldField.Name)
if newField == nil {
var op = "- field " + oldTable.Name + " " + oldField.Name
ops = append(ops, op)
if showLog {
fmt.Println(op)
}
_, err = db.Exec("ALTER TABLE " + oldTable.Name + " DROP COLUMN `" + oldField.Name + "`")
if err != nil {
return nil, errors.New("'" + op + "' failed: " + err.Error())
}
}
}
}
// 对比记录
// +
for _, record := range newTable.Records {
var queryArgs = []string{}
var queryValues = []interface{}{}
var valueStrings = []string{}
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, ", "))
if showLog {
fmt.Println("+ record " + newTable.Name + " " + strings.Join(valueStrings, ", "))
}
params := []string{}
args := []string{}
values := []interface{}{}
for k, v := range record.Values {
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
// ID需要保留因为各个表格之间需要有对应关系
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, ", "))
if showLog {
fmt.Println("* record " + newTable.Name + " " + strings.Join(valueStrings, ", "))
}
args := []string{}
values := []interface{}{}
for k, v := range record.Values {
if k == "id" {
continue
}
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
args = append(args, k+"=?")
values = append(values, v)
}
values = append(values, one.GetInt("id"))
_, err = db.Exec("UPDATE "+newTable.Name+" SET "+strings.Join(args, ", ")+" WHERE id=?", values...)
if err != nil {
return nil, err
}
}
}
}
// 减少表格
// 由于我们不删除任何表格,所以这里什么都不做
// 升级数据
err = UpgradeSQLData(db)
if err != nil {
return nil, errors.New("upgrade data failed: " + err.Error())
}
return
}
// 查找有记录的表
func (this *SQLDump) findRecordsTable(tableName string) *SQLRecordsTable {
for _, table := range recordsTables {
if table.TableName == tableName {
return table
}
}
return nil
}
// 创建索引
func (this *SQLDump) tryCreateIndex(err error, db *dbs.DB, tableName string, indexDefinition string) error {
if err == nil {
return nil
}
// 处理Duplicate entry
if strings.Contains(err.Error(), "Error 1062: Duplicate entry") && (strings.HasSuffix(tableName, "Stats") || strings.HasSuffix(tableName, "Values")) {
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
}