2021-09-11 14:04:09 +08:00
|
|
|
|
package api
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2022-06-30 16:42:25 +08:00
|
|
|
|
"io"
|
2023-10-12 21:50:55 +08:00
|
|
|
|
"mayfly-go/pkg/utils/collx"
|
2023-10-10 09:24:49 +08:00
|
|
|
|
"mayfly-go/pkg/utils/uniqueid"
|
|
|
|
|
|
"mayfly-go/pkg/ws"
|
|
|
|
|
|
|
2023-10-12 21:50:55 +08:00
|
|
|
|
"github.com/lib/pq"
|
|
|
|
|
|
|
2022-09-09 18:26:08 +08:00
|
|
|
|
"mayfly-go/internal/db/api/form"
|
|
|
|
|
|
"mayfly-go/internal/db/api/vo"
|
|
|
|
|
|
"mayfly-go/internal/db/application"
|
|
|
|
|
|
"mayfly-go/internal/db/domain/entity"
|
2023-07-03 21:42:04 +08:00
|
|
|
|
msgapp "mayfly-go/internal/msg/application"
|
2023-10-10 17:39:46 +08:00
|
|
|
|
msgdto "mayfly-go/internal/msg/application/dto"
|
2022-10-26 20:49:29 +08:00
|
|
|
|
tagapp "mayfly-go/internal/tag/application"
|
2022-06-02 17:41:11 +08:00
|
|
|
|
"mayfly-go/pkg/biz"
|
|
|
|
|
|
"mayfly-go/pkg/ginx"
|
2023-07-01 14:34:42 +08:00
|
|
|
|
"mayfly-go/pkg/gormx"
|
2022-06-02 17:41:11 +08:00
|
|
|
|
"mayfly-go/pkg/model"
|
2023-01-14 16:29:52 +08:00
|
|
|
|
"mayfly-go/pkg/req"
|
2023-09-26 22:47:19 +08:00
|
|
|
|
"mayfly-go/pkg/sqlparser"
|
2023-07-21 17:07:04 +08:00
|
|
|
|
"mayfly-go/pkg/utils/stringx"
|
2021-01-08 15:37:32 +08:00
|
|
|
|
"strconv"
|
2021-04-21 10:22:09 +08:00
|
|
|
|
"strings"
|
2022-06-30 16:42:25 +08:00
|
|
|
|
"time"
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
2021-04-16 15:10:07 +08:00
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
|
)
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
2021-05-08 18:00:33 +08:00
|
|
|
|
type Db struct {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
InstanceApp application.Instance
|
2022-06-16 15:55:18 +08:00
|
|
|
|
DbApp application.Db
|
|
|
|
|
|
DbSqlExecApp application.DbSqlExec
|
2023-07-03 21:42:04 +08:00
|
|
|
|
MsgApp msgapp.Msg
|
2022-10-26 20:49:29 +08:00
|
|
|
|
TagApp tagapp.TagTree
|
2021-05-08 18:00:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-08 15:37:32 +08:00
|
|
|
|
// @router /api/dbs [get]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) Dbs(rc *req.Ctx) {
|
2023-07-08 20:05:55 +08:00
|
|
|
|
queryCond, page := ginx.BindQueryAndPage[*entity.DbQuery](rc.GinCtx, new(entity.DbQuery))
|
2022-10-26 20:49:29 +08:00
|
|
|
|
|
|
|
|
|
|
// 不存在可访问标签id,即没有可操作数据
|
|
|
|
|
|
tagIds := d.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
|
|
|
|
|
|
if len(tagIds) == 0 {
|
2023-07-01 14:34:42 +08:00
|
|
|
|
rc.ResData = model.EmptyPageResult[any]()
|
2022-10-26 20:49:29 +08:00
|
|
|
|
return
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
2023-07-08 20:05:55 +08:00
|
|
|
|
|
|
|
|
|
|
queryCond.TagIds = tagIds
|
|
|
|
|
|
rc.ResData = d.DbApp.GetPageList(queryCond, page, new([]vo.SelectDataDbVO))
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-20 22:41:13 +08:00
|
|
|
|
func (d *Db) DbTags(rc *req.Ctx) {
|
|
|
|
|
|
rc.ResData = d.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Db))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) Save(rc *req.Ctx) {
|
2021-07-28 18:03:19 +08:00
|
|
|
|
form := &form.DbForm{}
|
2023-07-08 20:05:55 +08:00
|
|
|
|
db := ginx.BindJsonAndCopyTo[*entity.Db](rc.GinCtx, form, new(entity.Db))
|
2022-07-18 20:36:31 +08:00
|
|
|
|
|
2022-07-14 11:39:12 +08:00
|
|
|
|
rc.ReqParam = form
|
|
|
|
|
|
|
2021-07-28 18:03:19 +08:00
|
|
|
|
db.SetBaseInfo(rc.LoginAccount)
|
|
|
|
|
|
d.DbApp.Save(db)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) DeleteDb(rc *req.Ctx) {
|
2023-07-01 14:34:42 +08:00
|
|
|
|
idsStr := ginx.PathParam(rc.GinCtx, "dbId")
|
|
|
|
|
|
rc.ReqParam = idsStr
|
|
|
|
|
|
ids := strings.Split(idsStr, ",")
|
|
|
|
|
|
|
|
|
|
|
|
for _, v := range ids {
|
|
|
|
|
|
value, err := strconv.Atoi(v)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
|
|
|
|
|
|
dbId := uint64(value)
|
|
|
|
|
|
d.DbApp.Delete(dbId)
|
|
|
|
|
|
// 删除该库的sql执行记录
|
|
|
|
|
|
d.DbSqlExecApp.DeleteBy(&entity.DbSqlExec{DbId: dbId})
|
|
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-05 08:13:32 +08:00
|
|
|
|
func (d *Db) getDbConnection(g *gin.Context) *application.DbConnection {
|
2023-10-10 09:24:49 +08:00
|
|
|
|
return d.DbApp.GetDbConnection(getDbId(g), getDbName(g))
|
2023-08-30 22:41:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) TableInfos(rc *req.Ctx) {
|
2023-09-05 08:13:32 +08:00
|
|
|
|
rc.ResData = d.getDbConnection(rc.GinCtx).GetMeta().GetTableInfos()
|
2021-08-18 17:57:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) TableIndex(rc *req.Ctx) {
|
2021-08-18 17:57:33 +08:00
|
|
|
|
tn := rc.GinCtx.Query("tableName")
|
|
|
|
|
|
biz.NotEmpty(tn, "tableName不能为空")
|
2023-09-05 08:13:32 +08:00
|
|
|
|
rc.ResData = d.getDbConnection(rc.GinCtx).GetMeta().GetTableIndex(tn)
|
2021-08-18 17:57:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
|
2021-08-18 17:57:33 +08:00
|
|
|
|
tn := rc.GinCtx.Query("tableName")
|
|
|
|
|
|
biz.NotEmpty(tn, "tableName不能为空")
|
2023-09-05 08:13:32 +08:00
|
|
|
|
rc.ResData = d.getDbConnection(rc.GinCtx).GetMeta().GetCreateTableDdl(tn)
|
2021-08-18 17:57:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) ExecSql(rc *req.Ctx) {
|
2021-04-16 15:10:07 +08:00
|
|
|
|
g := rc.GinCtx
|
2022-06-16 15:55:18 +08:00
|
|
|
|
form := &form.DbSqlExecForm{}
|
|
|
|
|
|
ginx.BindJsonAndValid(g, form)
|
2022-04-22 17:49:21 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbId := getDbId(g)
|
2023-09-05 14:41:12 +08:00
|
|
|
|
dbConn := d.DbApp.GetDbConnection(dbId, form.Db)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbConn.Info.TagPath), "%s")
|
2022-04-22 17:49:21 +08:00
|
|
|
|
|
2023-09-05 14:41:12 +08:00
|
|
|
|
rc.ReqParam = fmt.Sprintf("%s\n-> %s", dbConn.Info.GetLogDesc(), form.Sql)
|
2022-11-02 19:27:40 +08:00
|
|
|
|
biz.NotEmpty(form.Sql, "sql不能为空")
|
2022-06-16 15:55:18 +08:00
|
|
|
|
|
2022-11-02 19:27:40 +08:00
|
|
|
|
// 去除前后空格及换行符
|
2023-07-21 17:07:04 +08:00
|
|
|
|
sql := stringx.TrimSpaceAndBr(form.Sql)
|
2022-09-22 11:56:21 +08:00
|
|
|
|
|
2022-11-02 19:27:40 +08:00
|
|
|
|
execReq := &application.DbSqlExecReq{
|
2023-08-30 22:41:42 +08:00
|
|
|
|
DbId: dbId,
|
|
|
|
|
|
Db: form.Db,
|
2022-09-22 11:56:21 +08:00
|
|
|
|
Remark: form.Remark,
|
2023-09-05 14:41:12 +08:00
|
|
|
|
DbConn: dbConn,
|
2022-09-22 11:56:21 +08:00
|
|
|
|
LoginAccount: rc.LoginAccount,
|
2022-11-02 19:27:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-17 08:59:37 +08:00
|
|
|
|
sqls, err := sqlparser.SplitStatementToPieces(sql)
|
|
|
|
|
|
biz.ErrIsNil(err, "SQL解析错误,请检查您的执行SQL")
|
2022-11-02 19:27:40 +08:00
|
|
|
|
isMulti := len(sqls) > 1
|
|
|
|
|
|
var execResAll *application.DbSqlExecRes
|
|
|
|
|
|
for _, s := range sqls {
|
2023-07-21 17:07:04 +08:00
|
|
|
|
s = stringx.TrimSpaceAndBr(s)
|
2022-11-02 19:27:40 +08:00
|
|
|
|
// 多条执行,如果有查询语句,则跳过
|
|
|
|
|
|
if isMulti && strings.HasPrefix(strings.ToLower(s), "select") {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
execReq.Sql = s
|
|
|
|
|
|
execRes, err := d.DbSqlExecApp.Exec(execReq)
|
2022-12-17 22:24:21 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, fmt.Sprintf("[%s] -> 执行失败: ", s)+"%s")
|
|
|
|
|
|
}
|
2022-11-02 19:27:40 +08:00
|
|
|
|
|
|
|
|
|
|
if execResAll == nil {
|
|
|
|
|
|
execResAll = execRes
|
|
|
|
|
|
} else {
|
|
|
|
|
|
execResAll.Merge(execRes)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-01 12:31:32 +08:00
|
|
|
|
colAndRes := make(map[string]any)
|
2022-11-02 19:27:40 +08:00
|
|
|
|
colAndRes["colNames"] = execResAll.ColNames
|
|
|
|
|
|
colAndRes["res"] = execResAll.Res
|
2022-09-22 11:56:21 +08:00
|
|
|
|
rc.ResData = colAndRes
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
// progressCategory sql文件执行进度消息类型
|
|
|
|
|
|
const progressCategory = "execSqlFileProgress"
|
|
|
|
|
|
|
|
|
|
|
|
// progressMsg sql文件执行进度消息
|
|
|
|
|
|
type progressMsg struct {
|
|
|
|
|
|
Id uint64 `json:"id"`
|
|
|
|
|
|
SqlFileName string `json:"sqlFileName"`
|
|
|
|
|
|
ExecutedStatements int `json:"executedStatements"`
|
|
|
|
|
|
Terminated bool `json:"terminated"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-25 14:34:15 +08:00
|
|
|
|
// 执行sql文件
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
2021-11-25 14:34:15 +08:00
|
|
|
|
g := rc.GinCtx
|
2023-10-10 09:24:49 +08:00
|
|
|
|
multipart, err := g.Request.MultipartReader()
|
2021-11-25 14:34:15 +08:00
|
|
|
|
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
2023-10-10 09:24:49 +08:00
|
|
|
|
file, err := multipart.NextPart()
|
|
|
|
|
|
biz.ErrIsNilAppendErr(err, "读取sql文件失败: %s")
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
filename := file.FileName()
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbId := getDbId(g)
|
|
|
|
|
|
dbName := getDbName(g)
|
2021-11-25 14:34:15 +08:00
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
dbConn := d.DbApp.GetDbConnection(dbId, dbName)
|
2023-09-05 14:41:12 +08:00
|
|
|
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbConn.Info.TagPath), "%s")
|
|
|
|
|
|
rc.ReqParam = fmt.Sprintf("%s -> filename: %s", dbConn.Info.GetLogDesc(), filename)
|
2022-01-12 16:00:31 +08:00
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
defer func() {
|
2023-09-26 22:47:19 +08:00
|
|
|
|
var errInfo string
|
|
|
|
|
|
switch t := recover().(type) {
|
2023-10-12 21:50:55 +08:00
|
|
|
|
case error:
|
2023-09-26 22:47:19 +08:00
|
|
|
|
errInfo = t.Error()
|
|
|
|
|
|
case string:
|
|
|
|
|
|
errInfo = t
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(errInfo) > 0 {
|
|
|
|
|
|
d.MsgApp.CreateAndSend(rc.LoginAccount, msgdto.ErrSysMsg("sql脚本执行失败", fmt.Sprintf("[%s]%s执行失败: [%s]", filename, dbConn.Info.GetLogDesc(), errInfo)))
|
2023-10-10 09:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
execReq := &application.DbSqlExecReq{
|
|
|
|
|
|
DbId: dbId,
|
|
|
|
|
|
Db: dbName,
|
|
|
|
|
|
Remark: filename,
|
|
|
|
|
|
DbConn: dbConn,
|
|
|
|
|
|
LoginAccount: rc.LoginAccount,
|
2022-12-17 22:24:21 +08:00
|
|
|
|
}
|
2022-01-12 16:00:31 +08:00
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
progressId := uniqueid.IncrementID()
|
|
|
|
|
|
executedStatements := 0
|
|
|
|
|
|
defer ws.SendJsonMsg(rc.LoginAccount.Id, msgdto.InfoSysMsg("sql脚本执行进度", &progressMsg{
|
|
|
|
|
|
Id: progressId,
|
|
|
|
|
|
SqlFileName: filename,
|
|
|
|
|
|
ExecutedStatements: executedStatements,
|
|
|
|
|
|
Terminated: true,
|
|
|
|
|
|
}).WithCategory(progressCategory))
|
|
|
|
|
|
|
2023-09-26 22:47:19 +08:00
|
|
|
|
var parser sqlparser.Parser
|
|
|
|
|
|
if dbConn.Info.Type == entity.DbTypeMysql {
|
|
|
|
|
|
parser = sqlparser.NewMysqlParser(file)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
parser = sqlparser.NewPostgresParser(file)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
ticker := time.NewTicker(time.Second * 1)
|
|
|
|
|
|
defer ticker.Stop()
|
2023-09-26 22:47:19 +08:00
|
|
|
|
for {
|
2023-10-10 09:24:49 +08:00
|
|
|
|
select {
|
|
|
|
|
|
case <-ticker.C:
|
|
|
|
|
|
ws.SendJsonMsg(rc.LoginAccount.Id, msgdto.InfoSysMsg("sql脚本执行进度", &progressMsg{
|
|
|
|
|
|
Id: progressId,
|
|
|
|
|
|
SqlFileName: filename,
|
|
|
|
|
|
ExecutedStatements: executedStatements,
|
|
|
|
|
|
Terminated: false,
|
|
|
|
|
|
}).WithCategory(progressCategory))
|
|
|
|
|
|
default:
|
|
|
|
|
|
}
|
2023-09-26 22:47:19 +08:00
|
|
|
|
err = parser.Next()
|
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
d.MsgApp.CreateAndSend(rc.LoginAccount, msgdto.ErrSysMsg("sql脚本执行失败", fmt.Sprintf("[%s][%s] 解析SQL失败: [%s]", filename, dbConn.Info.GetLogDesc(), err.Error())))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
sql := parser.Current()
|
2023-10-10 09:24:49 +08:00
|
|
|
|
const prefixUse = "use "
|
|
|
|
|
|
if strings.HasPrefix(sql, prefixUse) {
|
|
|
|
|
|
dbNameExec := strings.Trim(sql[len(prefixUse):], " `;\n")
|
|
|
|
|
|
if len(dbNameExec) > 0 {
|
|
|
|
|
|
dbConn = d.DbApp.GetDbConnection(dbId, dbNameExec)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbConn.Info.TagPath), "%s")
|
|
|
|
|
|
execReq.DbConn = dbConn
|
2022-12-17 22:24:21 +08:00
|
|
|
|
}
|
2023-10-10 09:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 需要记录执行记录
|
|
|
|
|
|
const maxRecordStatements = 64
|
|
|
|
|
|
if executedStatements < maxRecordStatements {
|
|
|
|
|
|
execReq.Sql = sql
|
|
|
|
|
|
_, err = d.DbSqlExecApp.Exec(execReq)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_, err = dbConn.Exec(sql)
|
|
|
|
|
|
}
|
2022-12-17 22:24:21 +08:00
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
d.MsgApp.CreateAndSend(rc.LoginAccount, msgdto.ErrSysMsg("sql脚本执行失败", fmt.Sprintf("[%s][%s] -> sql=[%s] 执行失败: [%s]", filename, dbConn.Info.GetLogDesc(), sql, err.Error())))
|
|
|
|
|
|
return
|
2021-11-25 14:34:15 +08:00
|
|
|
|
}
|
2023-10-10 09:24:49 +08:00
|
|
|
|
executedStatements++
|
|
|
|
|
|
}
|
|
|
|
|
|
d.MsgApp.CreateAndSend(rc.LoginAccount, msgdto.SuccessSysMsg("sql脚本执行成功", fmt.Sprintf("[%s]执行完成 -> %s", filename, dbConn.Info.GetLogDesc())))
|
2021-11-25 14:34:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-30 16:42:25 +08:00
|
|
|
|
// 数据库dump
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) DumpSql(rc *req.Ctx) {
|
2022-06-30 16:42:25 +08:00
|
|
|
|
g := rc.GinCtx
|
2023-08-31 20:05:38 +08:00
|
|
|
|
dbId := getDbId(g)
|
|
|
|
|
|
dbNamesStr := g.Query("db")
|
2022-06-30 16:42:25 +08:00
|
|
|
|
dumpType := g.Query("type")
|
|
|
|
|
|
tablesStr := g.Query("tables")
|
2023-09-05 10:55:53 +08:00
|
|
|
|
extName := g.Query("extName")
|
|
|
|
|
|
switch extName {
|
|
|
|
|
|
case ".gz", ".gzip", "gz", "gzip":
|
|
|
|
|
|
extName = ".gz"
|
|
|
|
|
|
default:
|
|
|
|
|
|
extName = ""
|
|
|
|
|
|
}
|
2022-06-30 16:42:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 是否需要导出表结构
|
|
|
|
|
|
needStruct := dumpType == "1" || dumpType == "3"
|
|
|
|
|
|
// 是否需要导出数据
|
|
|
|
|
|
needData := dumpType == "2" || dumpType == "3"
|
|
|
|
|
|
|
2023-08-31 20:05:38 +08:00
|
|
|
|
db := d.DbApp.GetById(dbId)
|
|
|
|
|
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, db.TagPath), "%s")
|
2022-07-10 12:14:06 +08:00
|
|
|
|
|
2022-06-30 16:42:25 +08:00
|
|
|
|
now := time.Now()
|
2023-09-05 10:55:53 +08:00
|
|
|
|
filename := fmt.Sprintf("%s.%s.sql%s", db.Name, now.Format("20060102150405"), extName)
|
2022-06-30 16:42:25 +08:00
|
|
|
|
g.Header("Content-Type", "application/octet-stream")
|
|
|
|
|
|
g.Header("Content-Disposition", "attachment; filename="+filename)
|
2023-09-05 10:55:53 +08:00
|
|
|
|
if extName != ".gz" {
|
|
|
|
|
|
g.Header("Content-Encoding", "gzip")
|
|
|
|
|
|
}
|
2022-06-30 16:42:25 +08:00
|
|
|
|
|
2023-08-31 20:05:38 +08:00
|
|
|
|
var dbNames, tables []string
|
|
|
|
|
|
if len(dbNamesStr) > 0 {
|
|
|
|
|
|
dbNames = strings.Split(dbNamesStr, ",")
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(dbNames) == 1 && len(tablesStr) > 0 {
|
|
|
|
|
|
tables = strings.Split(tablesStr, ",")
|
|
|
|
|
|
}
|
2023-09-07 11:15:11 +08:00
|
|
|
|
writer := newGzipWriter(g.Writer)
|
2023-09-05 22:35:46 +08:00
|
|
|
|
defer func() {
|
|
|
|
|
|
var msg string
|
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
|
switch t := err.(type) {
|
2023-10-08 12:14:19 +08:00
|
|
|
|
case error:
|
2023-09-05 22:35:46 +08:00
|
|
|
|
msg = t.Error()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(msg) > 0 {
|
|
|
|
|
|
msg = "数据库导出失败: " + msg
|
|
|
|
|
|
writer.WriteString(msg)
|
2023-10-10 17:39:46 +08:00
|
|
|
|
d.MsgApp.CreateAndSend(rc.LoginAccount, msgdto.ErrSysMsg("数据库导出失败", msg))
|
2023-09-05 22:35:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
writer.Close()
|
|
|
|
|
|
}()
|
2023-08-31 20:05:38 +08:00
|
|
|
|
for _, dbName := range dbNames {
|
2023-09-05 15:22:16 +08:00
|
|
|
|
d.dumpDb(writer, dbId, dbName, tables, needStruct, needData, len(dbNames) > 1)
|
2023-08-31 20:05:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-12 21:50:55 +08:00
|
|
|
|
rc.ReqParam = collx.Kvs("db", db, "databases", dbNamesStr, "tables", tablesStr, "dumpType", dumpType)
|
2023-08-31 20:05:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-10 09:24:49 +08:00
|
|
|
|
func escapeSql(dbType string, sql string) string {
|
|
|
|
|
|
if dbType == entity.DbTypePostgres {
|
|
|
|
|
|
return pq.QuoteLiteral(sql)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sql = strings.ReplaceAll(sql, `\`, `\\`)
|
|
|
|
|
|
sql = strings.ReplaceAll(sql, `'`, `''`)
|
|
|
|
|
|
return "'" + sql + "'"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func quoteTable(dbType string, table string) string {
|
|
|
|
|
|
if dbType == entity.DbTypePostgres {
|
|
|
|
|
|
return "\"" + table + "\""
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return "`" + table + "`"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-07 11:15:11 +08:00
|
|
|
|
func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []string, needStruct bool, needData bool, switchDb bool) {
|
2023-09-05 15:22:16 +08:00
|
|
|
|
dbConn := d.DbApp.GetDbConnection(dbId, dbName)
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.WriteString("\n-- ----------------------------")
|
2022-06-30 16:42:25 +08:00
|
|
|
|
writer.WriteString("\n-- 导出平台: mayfly-go")
|
2023-08-31 20:05:38 +08:00
|
|
|
|
writer.WriteString(fmt.Sprintf("\n-- 导出时间: %s ", time.Now().Format("2006-01-02 15:04:05")))
|
|
|
|
|
|
writer.WriteString(fmt.Sprintf("\n-- 导出数据库: %s ", dbName))
|
2022-06-30 16:42:25 +08:00
|
|
|
|
writer.WriteString("\n-- ----------------------------\n")
|
|
|
|
|
|
|
2023-09-05 14:15:40 +08:00
|
|
|
|
if switchDb {
|
2023-09-05 15:22:16 +08:00
|
|
|
|
switch dbConn.Info.Type {
|
2023-09-05 14:15:40 +08:00
|
|
|
|
case entity.DbTypeMysql:
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.WriteString(fmt.Sprintf("USE `%s`;\n", dbName))
|
2023-09-05 14:15:40 +08:00
|
|
|
|
default:
|
2023-10-10 09:24:49 +08:00
|
|
|
|
biz.IsTrue(false, "同时导出多个数据库,数据库类型必须为 %s", entity.DbTypeMysql)
|
2023-09-05 14:15:40 +08:00
|
|
|
|
}
|
2023-08-31 20:05:38 +08:00
|
|
|
|
}
|
2023-10-10 09:24:49 +08:00
|
|
|
|
if dbConn.Info.Type == entity.DbTypeMysql {
|
|
|
|
|
|
writer.WriteString("\nSET FOREIGN_KEY_CHECKS = 0;\n")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-05 15:22:16 +08:00
|
|
|
|
dbMeta := dbConn.GetMeta()
|
2023-08-31 20:05:38 +08:00
|
|
|
|
if len(tables) == 0 {
|
|
|
|
|
|
ti := dbMeta.GetTableInfos()
|
|
|
|
|
|
tables = make([]string, len(ti))
|
|
|
|
|
|
for i, table := range ti {
|
|
|
|
|
|
tables[i] = table.TableName
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-30 16:42:25 +08:00
|
|
|
|
for _, table := range tables {
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.TryFlush()
|
|
|
|
|
|
quotedTable := quoteTable(dbConn.Info.Type, table)
|
2022-06-30 16:42:25 +08:00
|
|
|
|
if needStruct {
|
|
|
|
|
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS %s;\n", quotedTable))
|
2023-08-31 20:05:38 +08:00
|
|
|
|
writer.WriteString(dbMeta.GetCreateTableDdl(table) + ";\n")
|
2022-06-30 16:42:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
if !needData {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", table))
|
|
|
|
|
|
writer.WriteString("BEGIN;\n")
|
2023-10-10 09:24:49 +08:00
|
|
|
|
insertSql := "INSERT INTO %s VALUES (%s);\n"
|
2023-09-07 11:15:11 +08:00
|
|
|
|
dbMeta.WalkTableRecord(table, func(record map[string]any, columns []string) {
|
|
|
|
|
|
var values []string
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.TryFlush()
|
2023-09-07 11:15:11 +08:00
|
|
|
|
for _, column := range columns {
|
|
|
|
|
|
value := record[column]
|
|
|
|
|
|
if value == nil {
|
|
|
|
|
|
values = append(values, "NULL")
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
strValue, ok := value.(string)
|
|
|
|
|
|
if ok {
|
2023-10-10 09:24:49 +08:00
|
|
|
|
strValue = escapeSql(dbConn.Info.Type, strValue)
|
|
|
|
|
|
values = append(values, strValue)
|
2023-09-07 11:15:11 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
values = append(values, stringx.AnyToStr(value))
|
2022-06-30 16:42:25 +08:00
|
|
|
|
}
|
2022-12-17 22:24:21 +08:00
|
|
|
|
}
|
2023-10-10 09:24:49 +08:00
|
|
|
|
writer.WriteString(fmt.Sprintf(insertSql, quotedTable, strings.Join(values, ", ")))
|
2023-09-07 11:15:11 +08:00
|
|
|
|
})
|
2022-06-30 16:42:25 +08:00
|
|
|
|
writer.WriteString("COMMIT;\n")
|
2023-10-10 09:24:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
if dbConn.Info.Type == entity.DbTypeMysql {
|
|
|
|
|
|
writer.WriteString("\nSET FOREIGN_KEY_CHECKS = 1;\n")
|
2022-06-30 16:42:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-08 15:37:32 +08:00
|
|
|
|
// @router /api/db/:dbId/t-metadata [get]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) TableMA(rc *req.Ctx) {
|
2023-09-05 08:13:32 +08:00
|
|
|
|
dbi := d.getDbConnection(rc.GinCtx)
|
2022-08-10 19:46:17 +08:00
|
|
|
|
rc.ResData = dbi.GetMeta().GetTables()
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// @router /api/db/:dbId/c-metadata [get]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) ColumnMA(rc *req.Ctx) {
|
2021-04-16 15:10:07 +08:00
|
|
|
|
g := rc.GinCtx
|
|
|
|
|
|
tn := g.Query("tableName")
|
|
|
|
|
|
biz.NotEmpty(tn, "tableName不能为空")
|
2022-04-22 17:49:21 +08:00
|
|
|
|
|
2023-09-05 08:13:32 +08:00
|
|
|
|
dbi := d.getDbConnection(rc.GinCtx)
|
2022-08-10 19:46:17 +08:00
|
|
|
|
rc.ResData = dbi.GetMeta().GetColumns(tn)
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// @router /api/db/:dbId/hint-tables [get]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) HintTables(rc *req.Ctx) {
|
2023-09-05 08:13:32 +08:00
|
|
|
|
dbi := d.getDbConnection(rc.GinCtx)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2022-08-10 19:46:17 +08:00
|
|
|
|
dm := dbi.GetMeta()
|
|
|
|
|
|
// 获取所有表
|
|
|
|
|
|
tables := dm.GetTables()
|
2021-07-28 18:03:19 +08:00
|
|
|
|
tableNames := make([]string, 0)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
for _, v := range tables {
|
2023-05-24 12:32:17 +08:00
|
|
|
|
tableNames = append(tableNames, v.TableName)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
// key = 表名,value = 列名数组
|
|
|
|
|
|
res := make(map[string][]string)
|
|
|
|
|
|
|
2022-03-24 17:50:44 +08:00
|
|
|
|
// 表为空,则直接返回
|
|
|
|
|
|
if len(tableNames) == 0 {
|
|
|
|
|
|
rc.ResData = res
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有表下的所有列信息
|
2022-08-10 19:46:17 +08:00
|
|
|
|
columnMds := dm.GetColumns(tableNames...)
|
2021-07-28 18:03:19 +08:00
|
|
|
|
for _, v := range columnMds {
|
2023-05-24 12:32:17 +08:00
|
|
|
|
tName := v.TableName
|
2021-07-28 18:03:19 +08:00
|
|
|
|
if res[tName] == nil {
|
|
|
|
|
|
res[tName] = make([]string, 0)
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
2021-07-28 18:03:19 +08:00
|
|
|
|
|
2023-05-24 12:32:17 +08:00
|
|
|
|
columnName := fmt.Sprintf("%s [%s]", v.ColumnName, v.ColumnType)
|
|
|
|
|
|
comment := v.ColumnComment
|
2021-07-28 18:03:19 +08:00
|
|
|
|
// 如果字段备注不为空,则加上备注信息
|
2023-05-24 12:32:17 +08:00
|
|
|
|
if comment != "" {
|
2021-07-28 18:03:19 +08:00
|
|
|
|
columnName = fmt.Sprintf("%s[%s]", columnName, comment)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res[tName] = append(res[tName], columnName)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
rc.ResData = res
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// @router /api/db/:dbId/sql [post]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) SaveSql(rc *req.Ctx) {
|
2021-04-16 15:10:07 +08:00
|
|
|
|
g := rc.GinCtx
|
|
|
|
|
|
account := rc.LoginAccount
|
|
|
|
|
|
dbSqlForm := &form.DbSqlSaveForm{}
|
|
|
|
|
|
ginx.BindJsonAndValid(g, dbSqlForm)
|
|
|
|
|
|
rc.ReqParam = dbSqlForm
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbId := getDbId(g)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
// 判断dbId是否存在
|
2023-07-01 14:34:42 +08:00
|
|
|
|
err := gormx.GetById(new(entity.Db), dbId)
|
2021-05-08 18:00:33 +08:00
|
|
|
|
biz.ErrIsNil(err, "该数据库信息不存在")
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
2021-04-16 15:10:07 +08:00
|
|
|
|
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
2022-05-08 14:10:57 +08:00
|
|
|
|
dbSql := &entity.DbSql{Type: dbSqlForm.Type, DbId: dbId, Name: dbSqlForm.Name, Db: dbSqlForm.Db}
|
2021-04-16 15:10:07 +08:00
|
|
|
|
dbSql.CreatorId = account.Id
|
2023-07-01 14:34:42 +08:00
|
|
|
|
e := gormx.GetBy(dbSql)
|
2021-01-08 15:37:32 +08:00
|
|
|
|
|
2021-04-16 15:10:07 +08:00
|
|
|
|
dbSql.SetBaseInfo(account)
|
|
|
|
|
|
// 更新sql信息
|
|
|
|
|
|
dbSql.Sql = dbSqlForm.Sql
|
|
|
|
|
|
if e == nil {
|
2023-07-01 14:34:42 +08:00
|
|
|
|
gormx.UpdateById(dbSql)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
} else {
|
2023-07-01 14:34:42 +08:00
|
|
|
|
gormx.Insert(dbSql)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
}
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-20 20:42:52 +08:00
|
|
|
|
// 获取所有保存的sql names
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) GetSqlNames(rc *req.Ctx) {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbId := getDbId(rc.GinCtx)
|
|
|
|
|
|
dbName := getDbName(rc.GinCtx)
|
2021-12-20 20:42:52 +08:00
|
|
|
|
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbSql := &entity.DbSql{Type: 1, DbId: dbId, Db: dbName}
|
2021-12-20 20:42:52 +08:00
|
|
|
|
dbSql.CreatorId = rc.LoginAccount.Id
|
|
|
|
|
|
var sqls []entity.DbSql
|
2023-07-01 14:34:42 +08:00
|
|
|
|
gormx.ListBy(dbSql, &sqls, "id", "name")
|
2021-12-20 20:42:52 +08:00
|
|
|
|
|
|
|
|
|
|
rc.ResData = sqls
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除保存的sql
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) DeleteSql(rc *req.Ctx) {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbSql := &entity.DbSql{Type: 1, DbId: getDbId(rc.GinCtx)}
|
2021-12-20 20:42:52 +08:00
|
|
|
|
dbSql.CreatorId = rc.LoginAccount.Id
|
|
|
|
|
|
dbSql.Name = rc.GinCtx.Query("name")
|
2023-02-13 21:11:16 +08:00
|
|
|
|
dbSql.Db = rc.GinCtx.Query("db")
|
2021-12-20 20:42:52 +08:00
|
|
|
|
|
2023-07-01 14:34:42 +08:00
|
|
|
|
gormx.DeleteByCondition(dbSql)
|
2021-12-20 20:42:52 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-08 15:37:32 +08:00
|
|
|
|
// @router /api/db/:dbId/sql [get]
|
2023-01-14 16:29:52 +08:00
|
|
|
|
func (d *Db) GetSql(rc *req.Ctx) {
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbId := getDbId(rc.GinCtx)
|
|
|
|
|
|
dbName := getDbName(rc.GinCtx)
|
2021-12-20 20:42:52 +08:00
|
|
|
|
// 根据创建者id, 数据库id,以及sql模板名称查询保存的sql信息
|
2023-08-30 22:41:42 +08:00
|
|
|
|
dbSql := &entity.DbSql{Type: 1, DbId: dbId, Db: dbName}
|
2021-04-16 15:10:07 +08:00
|
|
|
|
dbSql.CreatorId = rc.LoginAccount.Id
|
2021-12-20 20:42:52 +08:00
|
|
|
|
dbSql.Name = rc.GinCtx.Query("name")
|
|
|
|
|
|
|
2023-07-01 14:34:42 +08:00
|
|
|
|
e := gormx.GetBy(dbSql)
|
2021-04-16 15:10:07 +08:00
|
|
|
|
if e != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
rc.ResData = dbSql
|
2021-01-08 15:37:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
func getDbId(g *gin.Context) uint64 {
|
2021-04-16 15:10:07 +08:00
|
|
|
|
dbId, _ := strconv.Atoi(g.Param("dbId"))
|
2021-03-24 17:18:39 +08:00
|
|
|
|
biz.IsTrue(dbId > 0, "dbId错误")
|
2021-01-08 15:37:32 +08:00
|
|
|
|
return uint64(dbId)
|
|
|
|
|
|
}
|
2022-05-08 14:10:57 +08:00
|
|
|
|
|
2023-08-30 22:41:42 +08:00
|
|
|
|
func getDbName(g *gin.Context) string {
|
2022-05-08 14:10:57 +08:00
|
|
|
|
db := g.Query("db")
|
|
|
|
|
|
biz.NotEmpty(db, "db不能为空")
|
2023-08-30 22:41:42 +08:00
|
|
|
|
return db
|
2022-05-08 14:10:57 +08:00
|
|
|
|
}
|