Skip to content

Commit 23a86cd

Browse files
committed
fix: 修改远程数据库依赖
1 parent 7a67377 commit 23a86cd

File tree

11 files changed

+581
-35
lines changed

11 files changed

+581
-35
lines changed

backend/utils/mysql/client/remote.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"database/sql"
66
"fmt"
77
"os"
8-
"path"
8+
"os/exec"
99
"strings"
1010
"time"
1111

@@ -14,8 +14,7 @@ import (
1414
"github.com/1Panel-dev/1Panel/backend/global"
1515
"github.com/1Panel-dev/1Panel/backend/utils/common"
1616
"github.com/1Panel-dev/1Panel/backend/utils/files"
17-
18-
"github.com/jarvanstack/mysqldump"
17+
"github.com/1Panel-dev/1Panel/backend/utils/mysql/helper"
1918
)
2019

2120
type Remote struct {
@@ -222,23 +221,26 @@ func (r *Remote) Backup(info BackupInfo) error {
222221

223222
f, _ := os.OpenFile(fileNameItem, os.O_RDWR|os.O_CREATE, 0755)
224223
defer f.Close()
225-
if err := mysqldump.Dump(dns, mysqldump.WithData(), mysqldump.WithDropTable(), mysqldump.WithWriter(f)); err != nil {
224+
if err := helper.Dump(dns, helper.WithData(), helper.WithDropTable(), helper.WithWriter(f)); err != nil {
226225
return err
227226
}
228227

229-
if err := fileOp.Compress([]string{fileNameItem}, info.TargetDir, info.FileName, files.Gz); err != nil {
230-
return err
228+
gzipCmd := exec.Command("gzip", fileNameItem)
229+
stdout, err := gzipCmd.CombinedOutput()
230+
if err != nil {
231+
return fmt.Errorf("gzip file %s failed, stdout: %v, err: %v", strings.TrimSuffix(info.FileName, ".gz"), string(stdout), err)
231232
}
232233
return nil
233234
}
234235

235236
func (r *Remote) Recover(info RecoverInfo) error {
236-
fileOp := files.NewFileOp()
237237
fileName := info.SourceFile
238238
if strings.HasSuffix(info.SourceFile, ".sql.gz") {
239239
fileName = strings.TrimSuffix(info.SourceFile, ".gz")
240-
if err := fileOp.Decompress(info.SourceFile, path.Dir(fileName), files.Gz); err != nil {
241-
return err
240+
gzipCmd := exec.Command("gunzip", info.SourceFile)
241+
stdout, err := gzipCmd.CombinedOutput()
242+
if err != nil {
243+
return fmt.Errorf("gunzip file %s failed, stdout: %v, err: %v", info.SourceFile, string(stdout), err)
242244
}
243245
}
244246
dns := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=%s&parseTime=true&loc=Asia%sShanghai", r.User, r.Password, r.Address, r.Port, info.Name, info.Format, "%2F")
@@ -247,7 +249,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
247249
return err
248250
}
249251
defer f.Close()
250-
if err := mysqldump.Source(dns, f, mysqldump.WithMergeInsert(1000)); err != nil {
252+
if err := helper.Source(dns, f, helper.WithMergeInsert(1000)); err != nil {
251253
return err
252254
}
253255
return nil

backend/utils/mysql/helper/dump.go

+332
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
package helper
2+
3+
import (
4+
"bufio"
5+
"database/sql"
6+
"fmt"
7+
"io"
8+
"log"
9+
"os"
10+
"strings"
11+
"time"
12+
13+
_ "github.com/go-sql-driver/mysql"
14+
)
15+
16+
func init() {
17+
log.SetFlags(log.Lshortfile | log.LstdFlags)
18+
}
19+
20+
type dumpOption struct {
21+
isData bool
22+
23+
tables []string
24+
isAllTable bool
25+
isDropTable bool
26+
writer io.Writer
27+
}
28+
29+
type DumpOption func(*dumpOption)
30+
31+
func WithDropTable() DumpOption {
32+
return func(option *dumpOption) {
33+
option.isDropTable = true
34+
}
35+
}
36+
37+
func WithData() DumpOption {
38+
return func(option *dumpOption) {
39+
option.isData = true
40+
}
41+
}
42+
43+
// 导出指定表, 与 WithAllTables 互斥, WithAllTables 优先级高
44+
func WithTables(tables ...string) DumpOption {
45+
return func(option *dumpOption) {
46+
option.tables = tables
47+
}
48+
}
49+
50+
// 导出全部表
51+
func WithAllTable() DumpOption {
52+
return func(option *dumpOption) {
53+
option.isAllTable = true
54+
}
55+
}
56+
57+
// 导出到指定 writer
58+
func WithWriter(writer io.Writer) DumpOption {
59+
return func(option *dumpOption) {
60+
option.writer = writer
61+
}
62+
}
63+
64+
func Dump(dns string, opts ...DumpOption) error {
65+
start := time.Now()
66+
log.Printf("[info] [dump] start at %s\n", start.Format("2006-01-02 15:04:05"))
67+
defer func() {
68+
end := time.Now()
69+
log.Printf("[info] [dump] end at %s, cost %s\n", end.Format("2006-01-02 15:04:05"), end.Sub(start))
70+
}()
71+
72+
var err error
73+
74+
var o dumpOption
75+
76+
for _, opt := range opts {
77+
opt(&o)
78+
}
79+
80+
if len(o.tables) == 0 {
81+
o.isAllTable = true
82+
}
83+
84+
if o.writer == nil {
85+
o.writer = os.Stdout
86+
}
87+
88+
buf := bufio.NewWriter(o.writer)
89+
defer buf.Flush()
90+
91+
_, _ = buf.WriteString("-- ----------------------------\n")
92+
_, _ = buf.WriteString("-- MySQL Database Dump\n")
93+
_, _ = buf.WriteString("-- Start Time: " + start.Format("2006-01-02 15:04:05") + "\n")
94+
_, _ = buf.WriteString("-- ----------------------------\n")
95+
_, _ = buf.WriteString("\n\n")
96+
97+
db, err := sql.Open("mysql", dns)
98+
if err != nil {
99+
log.Printf("[error] %v \n", err)
100+
return err
101+
}
102+
defer db.Close()
103+
104+
dbName, err := getDBNameFromDNS(dns)
105+
if err != nil {
106+
log.Printf("[error] %v \n", err)
107+
return err
108+
}
109+
_, err = db.Exec(fmt.Sprintf("USE `%s`", dbName))
110+
if err != nil {
111+
log.Printf("[error] %v \n", err)
112+
return err
113+
}
114+
115+
var tables []string
116+
if o.isAllTable {
117+
tmp, err := getAllTables(db)
118+
if err != nil {
119+
log.Printf("[error] %v \n", err)
120+
return err
121+
}
122+
tables = tmp
123+
} else {
124+
tables = o.tables
125+
}
126+
127+
for _, table := range tables {
128+
if o.isDropTable {
129+
_, _ = buf.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS `%s`;\n", table))
130+
}
131+
132+
err = writeTableStruct(db, table, buf)
133+
if err != nil {
134+
log.Printf("[error] %v \n", err)
135+
return err
136+
}
137+
138+
if o.isData {
139+
err = writeTableData(db, table, buf)
140+
if err != nil {
141+
log.Printf("[error] %v \n", err)
142+
return err
143+
}
144+
}
145+
}
146+
147+
_, _ = buf.WriteString("-- ----------------------------\n")
148+
_, _ = buf.WriteString("-- Dumped by mysqldump\n")
149+
_, _ = buf.WriteString("-- Cost Time: " + time.Since(start).String() + "\n")
150+
_, _ = buf.WriteString("-- ----------------------------\n")
151+
_ = buf.Flush()
152+
153+
return nil
154+
}
155+
156+
func getCreateTableSQL(db *sql.DB, table string) (string, error) {
157+
var createTableSQL string
158+
err := db.QueryRow(fmt.Sprintf("SHOW CREATE TABLE `%s`", table)).Scan(&table, &createTableSQL)
159+
if err != nil {
160+
return "", err
161+
}
162+
createTableSQL = strings.Replace(createTableSQL, "CREATE TABLE", "CREATE TABLE IF NOT EXISTS", 1)
163+
return createTableSQL, nil
164+
}
165+
166+
func getAllTables(db *sql.DB) ([]string, error) {
167+
var tables []string
168+
rows, err := db.Query("SHOW TABLES")
169+
if err != nil {
170+
return nil, err
171+
}
172+
defer rows.Close()
173+
174+
for rows.Next() {
175+
var table string
176+
err = rows.Scan(&table)
177+
if err != nil {
178+
return nil, err
179+
}
180+
tables = append(tables, table)
181+
}
182+
return tables, nil
183+
}
184+
185+
func writeTableStruct(db *sql.DB, table string, buf *bufio.Writer) error {
186+
_, _ = buf.WriteString("-- ----------------------------\n")
187+
_, _ = buf.WriteString(fmt.Sprintf("-- Table structure for %s\n", table))
188+
_, _ = buf.WriteString("-- ----------------------------\n")
189+
190+
createTableSQL, err := getCreateTableSQL(db, table)
191+
if err != nil {
192+
log.Printf("[error] %v \n", err)
193+
return err
194+
}
195+
_, _ = buf.WriteString(createTableSQL)
196+
_, _ = buf.WriteString(";")
197+
198+
_, _ = buf.WriteString("\n\n")
199+
_, _ = buf.WriteString("\n\n")
200+
return nil
201+
}
202+
203+
func writeTableData(db *sql.DB, table string, buf *bufio.Writer) error {
204+
_, _ = buf.WriteString("-- ----------------------------\n")
205+
_, _ = buf.WriteString(fmt.Sprintf("-- Records of %s\n", table))
206+
_, _ = buf.WriteString("-- ----------------------------\n")
207+
208+
lineRows, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`", table))
209+
if err != nil {
210+
log.Printf("[error] %v \n", err)
211+
return err
212+
}
213+
defer lineRows.Close()
214+
215+
var columns []string
216+
columns, err = lineRows.Columns()
217+
if err != nil {
218+
log.Printf("[error] %v \n", err)
219+
return err
220+
}
221+
columnTypes, err := lineRows.ColumnTypes()
222+
if err != nil {
223+
log.Printf("[error] %v \n", err)
224+
return err
225+
}
226+
227+
var values [][]interface{}
228+
for lineRows.Next() {
229+
row := make([]interface{}, len(columns))
230+
rowPointers := make([]interface{}, len(columns))
231+
for i := range columns {
232+
rowPointers[i] = &row[i]
233+
}
234+
err = lineRows.Scan(rowPointers...)
235+
if err != nil {
236+
log.Printf("[error] %v \n", err)
237+
return err
238+
}
239+
values = append(values, row)
240+
}
241+
242+
for _, row := range values {
243+
ssql := "INSERT INTO `" + table + "` VALUES ("
244+
245+
for i, col := range row {
246+
if col == nil {
247+
ssql += "NULL"
248+
} else {
249+
Type := columnTypes[i].DatabaseTypeName()
250+
Type = strings.Replace(Type, "UNSIGNED", "", -1)
251+
Type = strings.Replace(Type, " ", "", -1)
252+
switch Type {
253+
case "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER", "BIGINT":
254+
if bs, ok := col.([]byte); ok {
255+
ssql += string(bs)
256+
} else {
257+
ssql += fmt.Sprintf("%d", col)
258+
}
259+
case "FLOAT", "DOUBLE":
260+
if bs, ok := col.([]byte); ok {
261+
ssql += string(bs)
262+
} else {
263+
ssql += fmt.Sprintf("%f", col)
264+
}
265+
case "DECIMAL", "DEC":
266+
ssql += fmt.Sprintf("%s", col)
267+
268+
case "DATE":
269+
t, ok := col.(time.Time)
270+
if !ok {
271+
log.Println("DATE 类型转换错误")
272+
return err
273+
}
274+
ssql += fmt.Sprintf("'%s'", t.Format("2006-01-02"))
275+
case "DATETIME":
276+
t, ok := col.(time.Time)
277+
if !ok {
278+
log.Println("DATETIME 类型转换错误")
279+
return err
280+
}
281+
ssql += fmt.Sprintf("'%s'", t.Format("2006-01-02 15:04:05"))
282+
case "TIMESTAMP":
283+
t, ok := col.(time.Time)
284+
if !ok {
285+
log.Println("TIMESTAMP 类型转换错误")
286+
return err
287+
}
288+
ssql += fmt.Sprintf("'%s'", t.Format("2006-01-02 15:04:05"))
289+
case "TIME":
290+
t, ok := col.([]byte)
291+
if !ok {
292+
log.Println("TIME 类型转换错误")
293+
return err
294+
}
295+
ssql += fmt.Sprintf("'%s'", string(t))
296+
case "YEAR":
297+
t, ok := col.([]byte)
298+
if !ok {
299+
log.Println("YEAR 类型转换错误")
300+
return err
301+
}
302+
ssql += string(t)
303+
case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT":
304+
ssql += fmt.Sprintf("'%s'", strings.Replace(fmt.Sprintf("%s", col), "'", "''", -1))
305+
case "BIT", "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB":
306+
ssql += fmt.Sprintf("0x%X", col)
307+
case "ENUM", "SET":
308+
ssql += fmt.Sprintf("'%s'", col)
309+
case "BOOL", "BOOLEAN":
310+
if col.(bool) {
311+
ssql += "true"
312+
} else {
313+
ssql += "false"
314+
}
315+
case "JSON":
316+
ssql += fmt.Sprintf("'%s'", col)
317+
default:
318+
log.Printf("unsupported type: %s", Type)
319+
return fmt.Errorf("unsupported type: %s", Type)
320+
}
321+
}
322+
if i < len(row)-1 {
323+
ssql += ","
324+
}
325+
}
326+
ssql += ");\n"
327+
_, _ = buf.WriteString(ssql)
328+
}
329+
330+
_, _ = buf.WriteString("\n\n")
331+
return nil
332+
}

0 commit comments

Comments
 (0)