mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
feat: 支持执行多条sql
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
|
||||||
如需执行多条sql,需要在【数据库管理】配置连接参数:multiStatements=true
|
|
||||||
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue"
|
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue"
|
||||||
:options="cmOptions" />
|
:options="cmOptions" />
|
||||||
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
|
||||||
@@ -90,14 +89,22 @@ const runSql = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
state.btnLoading = true;
|
state.btnLoading = true;
|
||||||
await dbApi.sqlExec.request({
|
const res = await dbApi.sqlExec.request({
|
||||||
id: state.dbId,
|
id: state.dbId,
|
||||||
db: state.db,
|
db: state.db,
|
||||||
remark: state.remark,
|
remark: state.remark,
|
||||||
sql: state.sqlValue.trim(),
|
sql: state.sqlValue.trim(),
|
||||||
});
|
});
|
||||||
ElMessage.success('执行成功');
|
|
||||||
|
for (let re of res.res) {
|
||||||
|
if (re.result !== 'success') {
|
||||||
|
ElMessage.error(`${re.sql} \n执行失败: ${re.result}`);
|
||||||
|
throw new Error(re.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runSuccess = true;
|
runSuccess = true;
|
||||||
|
ElMessage.success('执行成功');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
runSuccess = false;
|
runSuccess = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<el-table-column label="操作" width>
|
<el-table-column label="操作" width>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-link type="primary" @click="showDatabases(scope.row.id, scope.row)" plain size="small"
|
<el-link type="primary" @click="showDatabases(scope.row.id)" plain size="small"
|
||||||
:underline="false">数据库</el-link>
|
:underline="false">数据库</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|||||||
@@ -128,27 +128,43 @@ func (d *Db) ExecSql(rc *ctx.ReqCtx) {
|
|||||||
dbInstance := d.DbApp.GetDbInstance(id, db)
|
dbInstance := d.DbApp.GetDbInstance(id, db)
|
||||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.LoginAccount.Id, dbInstance.TagPath), "%s")
|
||||||
|
|
||||||
|
rc.ReqParam = fmt.Sprintf("db: %d:%s | sql: %s", id, db, form.Sql)
|
||||||
|
biz.NotEmpty(form.Sql, "sql不能为空")
|
||||||
|
|
||||||
// 去除前后空格及换行符
|
// 去除前后空格及换行符
|
||||||
sql := strings.TrimFunc(form.Sql, func(r rune) bool {
|
sql := utils.StrTrimSpaceAndBr(form.Sql)
|
||||||
s := string(r)
|
|
||||||
return s == " " || s == "\n"
|
|
||||||
})
|
|
||||||
|
|
||||||
rc.ReqParam = fmt.Sprintf("db: %d:%s | sql: %s", id, db, sql)
|
execReq := &application.DbSqlExecReq{
|
||||||
biz.NotEmpty(sql, "sql不能为空")
|
|
||||||
|
|
||||||
execRes, err := d.DbSqlExecApp.Exec(&application.DbSqlExecReq{
|
|
||||||
DbId: id,
|
DbId: id,
|
||||||
Db: db,
|
Db: db,
|
||||||
Sql: sql,
|
|
||||||
Remark: form.Remark,
|
Remark: form.Remark,
|
||||||
DbInstance: dbInstance,
|
DbInstance: dbInstance,
|
||||||
LoginAccount: rc.LoginAccount,
|
LoginAccount: rc.LoginAccount,
|
||||||
})
|
}
|
||||||
biz.ErrIsNilAppendErr(err, "执行失败: %s")
|
|
||||||
|
sqls := strings.Split(sql, ";\n")
|
||||||
|
isMulti := len(sqls) > 1
|
||||||
|
var execResAll *application.DbSqlExecRes
|
||||||
|
for _, s := range sqls {
|
||||||
|
s = utils.StrTrimSpaceAndBr(s)
|
||||||
|
// 多条执行,如果有查询语句,则跳过
|
||||||
|
if isMulti && strings.HasPrefix(strings.ToLower(s), "select") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
execReq.Sql = s
|
||||||
|
execRes, err := d.DbSqlExecApp.Exec(execReq)
|
||||||
|
biz.ErrIsNilAppendErr(err, "执行失败: %s")
|
||||||
|
|
||||||
|
if execResAll == nil {
|
||||||
|
execResAll = execRes
|
||||||
|
} else {
|
||||||
|
execResAll.Merge(execRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
colAndRes := make(map[string]interface{})
|
colAndRes := make(map[string]interface{})
|
||||||
colAndRes["colNames"] = execRes.ColNames
|
colAndRes["colNames"] = execResAll.ColNames
|
||||||
colAndRes["res"] = execRes.Res
|
colAndRes["res"] = execResAll.Res
|
||||||
rc.ResData = colAndRes
|
rc.ResData = colAndRes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ func getDsn(d *entity.Db, db string) string {
|
|||||||
var dsn string
|
var dsn string
|
||||||
if d.Type == entity.DbTypeMysql {
|
if d.Type == entity.DbTypeMysql {
|
||||||
// 更多参数参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
// 更多参数参考:https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||||
dsn = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s&multiStatements=true", d.Username, d.Password, d.Network, d.Host, d.Port, db)
|
dsn = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?timeout=8s", d.Username, d.Password, d.Network, d.Host, d.Port, db)
|
||||||
if d.Params != "" {
|
if d.Params != "" {
|
||||||
dsn = fmt.Sprintf("%s&%s", dsn, d.Params)
|
dsn = fmt.Sprintf("%s&%s", dsn, d.Params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ type DbSqlExecRes struct {
|
|||||||
Res []map[string]interface{}
|
Res []map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 合并执行结果,主要用于执行多条sql使用
|
||||||
|
func (d *DbSqlExecRes) Merge(execRes *DbSqlExecRes) {
|
||||||
|
canMerge := len(d.ColNames) == len(execRes.ColNames)
|
||||||
|
if !canMerge {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 列名不一致,则不合并
|
||||||
|
for i, colName := range d.ColNames {
|
||||||
|
if execRes.ColNames[i] != colName {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.Res = append(d.Res, execRes.Res...)
|
||||||
|
}
|
||||||
|
|
||||||
type DbSqlExec interface {
|
type DbSqlExec interface {
|
||||||
// 执行sql
|
// 执行sql
|
||||||
Exec(execSqlReq *DbSqlExecReq) (*DbSqlExecRes, error)
|
Exec(execSqlReq *DbSqlExecReq) (*DbSqlExecRes, error)
|
||||||
@@ -187,16 +202,19 @@ func doInsert(insert *sqlparser.Insert, execSqlReq *DbSqlExecReq, dbSqlExec *ent
|
|||||||
|
|
||||||
func doExec(sql string, dbInstance *DbInstance) (*DbSqlExecRes, error) {
|
func doExec(sql string, dbInstance *DbInstance) (*DbSqlExecRes, error) {
|
||||||
rowsAffected, err := dbInstance.Exec(sql)
|
rowsAffected, err := dbInstance.Exec(sql)
|
||||||
|
execRes := "success"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
execRes = err.Error()
|
||||||
}
|
}
|
||||||
res := make([]map[string]interface{}, 0)
|
res := make([]map[string]interface{}, 0)
|
||||||
resData := make(map[string]interface{})
|
resData := make(map[string]interface{})
|
||||||
resData["影响条数"] = rowsAffected
|
resData["rowsAffected"] = rowsAffected
|
||||||
|
resData["sql"] = sql
|
||||||
|
resData["result"] = execRes
|
||||||
res = append(res, resData)
|
res = append(res, resData)
|
||||||
|
|
||||||
return &DbSqlExecRes{
|
return &DbSqlExecRes{
|
||||||
ColNames: []string{"影响条数"},
|
ColNames: []string{"sql", "rowsAffected", "result"},
|
||||||
Res: res,
|
Res: res,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ func StrTrim(str string) string {
|
|||||||
return strings.Trim(str, " ")
|
return strings.Trim(str, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去除字符串左右空字符与\n换行
|
||||||
|
func StrTrimSpaceAndBr(str string) string {
|
||||||
|
return strings.TrimFunc(str, func(r rune) bool {
|
||||||
|
s := string(r)
|
||||||
|
return s == " " || s == "\n"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func SubString(str string, begin, end int) (substr string) {
|
func SubString(str string, begin, end int) (substr string) {
|
||||||
// 将字符串的转换成[]rune
|
// 将字符串的转换成[]rune
|
||||||
rs := []rune(str)
|
rs := []rune(str)
|
||||||
|
|||||||
Reference in New Issue
Block a user