319 lines
6.9 KiB
Go
319 lines
6.9 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
type Cfg struct {
|
|
Settings Settings `yaml:"Settings"`
|
|
DBConfig DBConfig `yaml:"DBConfig"`
|
|
FormatColumns FormatColumns `yaml:"FormatColumns"`
|
|
}
|
|
|
|
type Settings struct {
|
|
Condition string `yaml:"Condition"`
|
|
PrimaryKey string `yaml:"PrimaryKey"`
|
|
DeleteSource bool `yaml:"DeleteSource"`
|
|
}
|
|
|
|
type FormatColumns struct {
|
|
Enablement bool `yaml:"Enablement"`
|
|
Reference string `yaml:"Reference"`
|
|
Year string `yaml:"Year"`
|
|
Month string `yaml:"Month"`
|
|
Day string `yaml:"Day"`
|
|
Week string `yaml:"Week"`
|
|
}
|
|
|
|
type DBConfig struct {
|
|
Username string `yaml:"Username"`
|
|
Password string `yaml:"Password"`
|
|
Server string `yaml:"Server"`
|
|
Port string `yaml:"Port"`
|
|
Database string `yaml:"Database"`
|
|
SourceTable string `yaml:"SourceTable"`
|
|
DestinationTable string `yaml:"DestinationTable"`
|
|
SourceColumns []string `yaml:"SourceColumns"`
|
|
DestinationColumns []string `yaml:"DestinationColumns"`
|
|
}
|
|
|
|
var cfg = Cfg{}
|
|
var dsn string
|
|
|
|
func main() {
|
|
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
|
log.SetOutput(os.Stdout)
|
|
|
|
self := strings.TrimSuffix(os.Args[0], ".exe")
|
|
file := self + ".yml"
|
|
txt, err := os.ReadFile(file)
|
|
if err != nil {
|
|
log.Printf("[ERR] %s\r\n", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
err = yaml.Unmarshal(txt, &cfg)
|
|
if err != nil {
|
|
log.Printf("[ERR] %s\r\n", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
|
|
// srcCols := cfg.DBConfig.Columns
|
|
// dstCols := cfg.DBConfig.Columns
|
|
// if len(srcCols) != len(dstCols) {
|
|
// log.Println("[ERR] Mismatched Length Of Source Columns And Destination Columns.")
|
|
// os.Exit(1)
|
|
// }
|
|
|
|
condition := cfg.Settings.Condition
|
|
log.Printf("[MSG] Condition: %s\r\n", condition)
|
|
|
|
if condition == "" {
|
|
log.Println("[ERR] Missing Query Condition.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s",
|
|
cfg.DBConfig.Username,
|
|
cfg.DBConfig.Password,
|
|
cfg.DBConfig.Server,
|
|
cfg.DBConfig.Port,
|
|
cfg.DBConfig.Database,
|
|
)
|
|
|
|
qry, err := retrieveData(condition)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
os.Exit(1)
|
|
}
|
|
log.Printf("[MSG] Selected Rows: %d\r\n", len(qry))
|
|
if len(qry) == 0 {
|
|
os.Exit(0)
|
|
}
|
|
|
|
err = backupData(qry)
|
|
if err != nil {
|
|
log.Printf("[ERR] %v\r\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func retrieveData(condition string) ([]map[string]interface{}, error) {
|
|
rst := make([]map[string]interface{}, 0)
|
|
dbo, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return rst, err
|
|
}
|
|
defer dbo.Close()
|
|
err = dbo.Ping()
|
|
if err != nil {
|
|
return rst, err
|
|
}
|
|
|
|
cols := ""
|
|
for _, c := range cfg.DBConfig.SourceColumns {
|
|
cols = cols + c + ","
|
|
}
|
|
cols = strings.TrimSuffix(cols, ",")
|
|
|
|
qry := fmt.Sprintf("SELECT %s FROM %s.%s",
|
|
cols,
|
|
cfg.DBConfig.Database,
|
|
cfg.DBConfig.SourceTable,
|
|
)
|
|
flt := fmt.Sprintf("WHERE %s;", condition)
|
|
if condition != "" {
|
|
qry = qry + " " + flt
|
|
}
|
|
|
|
lnth := len(cfg.DBConfig.SourceColumns)
|
|
vals := make([]interface{}, lnth)
|
|
valsPtr := make([]interface{}, lnth)
|
|
for i := 0; i < lnth; i++ {
|
|
valsPtr[i] = &vals[i]
|
|
}
|
|
|
|
rows, err := dbo.Query(qry)
|
|
if err != nil {
|
|
return rst, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
err := rows.Scan(valsPtr...)
|
|
if err != nil {
|
|
break
|
|
}
|
|
row := make(map[string]interface{}, 0)
|
|
for i, val := range vals {
|
|
key := cfg.DBConfig.SourceColumns[i]
|
|
var v interface{}
|
|
b, ok := val.([]byte)
|
|
if ok {
|
|
v = string(b)
|
|
} else {
|
|
v = val
|
|
}
|
|
row[key] = v
|
|
}
|
|
rst = append(rst, row)
|
|
}
|
|
return rst, nil
|
|
}
|
|
|
|
func backupData(data []map[string]interface{}) error {
|
|
dbo, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dbo.Close()
|
|
err = dbo.Ping()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cols := ""
|
|
for _, c := range cfg.DBConfig.DestinationColumns {
|
|
cols = cols + c + ","
|
|
}
|
|
|
|
// Append Formated Column Keys
|
|
if cfg.FormatColumns.Enablement {
|
|
cols = cols + strings.Join(
|
|
[]string{
|
|
cfg.FormatColumns.Year,
|
|
cfg.FormatColumns.Month,
|
|
cfg.FormatColumns.Day,
|
|
cfg.FormatColumns.Week,
|
|
},
|
|
",",
|
|
)
|
|
}
|
|
cols = strings.TrimSuffix(cols, ",")
|
|
|
|
var timeStr string = ""
|
|
var timeVal time.Time
|
|
for i, d := range data {
|
|
log.Printf("[MSG] #%d: %#v\r\n", i+1, d)
|
|
vals := ""
|
|
for _, c := range cfg.DBConfig.DestinationColumns {
|
|
for k, v := range d {
|
|
if k == c {
|
|
vals = vals + fmt.Sprintf("%#v,", v)
|
|
}
|
|
if k == cfg.FormatColumns.Reference {
|
|
timeStr = v.(string)
|
|
timeVal, err = time.Parse("2006-01-02 15:04:05", timeStr)
|
|
if err != nil {
|
|
log.Printf("[ERR] #%d: %#v\r\n", i+1, err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append Formated Column Values
|
|
if cfg.FormatColumns.Enablement {
|
|
vals = vals + strings.Join(
|
|
[]string{
|
|
fmt.Sprintf("%#v", timeVal.Format("2006")),
|
|
fmt.Sprintf("%#v", timeVal.Format("01")),
|
|
fmt.Sprintf("%#v", timeVal.Format("02")),
|
|
fmt.Sprintf("%#v", calculateWeek(timeVal)),
|
|
},
|
|
",",
|
|
)
|
|
}
|
|
vals = strings.TrimSuffix(vals, ",")
|
|
|
|
tx, err := dbo.Begin()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txBegin: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
|
|
stmt, err := tx.Prepare(fmt.Sprintf("INSERT INTO %s.%s (%s) VALUES (%s);",
|
|
cfg.DBConfig.Database,
|
|
cfg.DBConfig.DestinationTable,
|
|
cols,
|
|
vals,
|
|
))
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txPrepare_Insert: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txExecute_Insert: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
log.Printf("[MSG] Backed Up: %s=%s\r\n", cfg.Settings.PrimaryKey, d[cfg.Settings.PrimaryKey])
|
|
|
|
if cfg.Settings.DeleteSource {
|
|
stmt, err = tx.Prepare(fmt.Sprintf("DELETE FROM %s.%s WHERE %s=%s;",
|
|
cfg.DBConfig.Database,
|
|
cfg.DBConfig.SourceTable,
|
|
cfg.Settings.PrimaryKey,
|
|
fmt.Sprintf("%#v", d[cfg.Settings.PrimaryKey]),
|
|
))
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txPrepare_Delete: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txExecute_Delete: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
log.Printf("[MSG] Deleted Souce: %s=%s\r\n", cfg.Settings.PrimaryKey, d[cfg.Settings.PrimaryKey])
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
tx.Rollback()
|
|
log.Printf("[ERR] txCommit: %v\r\n\r\n", err)
|
|
continue
|
|
}
|
|
log.Printf("[MSG] ********** Transaction Commited.\r\n\r\n")
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func calculateWeek(t time.Time) string {
|
|
yearDay := t.YearDay()
|
|
lastYearEndDay := t.AddDate(0, 0, -yearDay)
|
|
lastYearEndDayWeek := int(lastYearEndDay.Weekday())
|
|
firstWeekDays := 7
|
|
if lastYearEndDayWeek != 0 {
|
|
firstWeekDays = 7 - lastYearEndDayWeek
|
|
}
|
|
week := 0
|
|
if yearDay <= firstWeekDays {
|
|
week = 1
|
|
} else {
|
|
plusDay := 0
|
|
if (yearDay-firstWeekDays)%7 > 0 {
|
|
plusDay = 1
|
|
}
|
|
week = (yearDay-firstWeekDays)/7 + 1 + plusDay
|
|
}
|
|
return fmt.Sprintf("%02d", week)
|
|
}
|