diff --git a/mayfly_go_web/src/views/ops/db/SqlExec.vue b/mayfly_go_web/src/views/ops/db/SqlExec.vue index 5ec3d6cb..369e5053 100644 --- a/mayfly_go_web/src/views/ops/db/SqlExec.vue +++ b/mayfly_go_web/src/views/ops/db/SqlExec.vue @@ -844,11 +844,20 @@ const onDeleteTable = async (data: any) => { let dialect = getDbDialect(state.nowDbInst.type); let schemaStr = schema ? `${dialect.quoteIdentifier(schema)}.` : ''; - dbApi.sqlExec.request({ id, db, sql: `drop table ${schemaStr + dialect.quoteIdentifier(tableName)}` }).then(() => { - ElMessage.success('删除成功'); - setTimeout(() => { - parentKey && reloadNode(parentKey); - }, 1000); + dbApi.sqlExec.request({ id, db, sql: `drop table ${schemaStr + dialect.quoteIdentifier(tableName)}` }).then((res) => { + let success = true; + for (let re of res) { + if (re.errorMsg) { + success = false; + ElMessage.error(`${re.sql} -> 执行失败: ${re.errorMsg}`); + } + } + if (success) { + ElMessage.success('删除成功'); + setTimeout(() => { + parentKey && reloadNode(parentKey); + }, 1000); + } }); }; diff --git a/mayfly_go_web/src/views/ops/db/component/sqleditor/DbSqlEditor.vue b/mayfly_go_web/src/views/ops/db/component/sqleditor/DbSqlEditor.vue index 0a2b1ae6..381a2dfc 100644 --- a/mayfly_go_web/src/views/ops/db/component/sqleditor/DbSqlEditor.vue +++ b/mayfly_go_web/src/views/ops/db/component/sqleditor/DbSqlEditor.vue @@ -309,18 +309,6 @@ const onRunSql = async (newTab = false) => { sqlPrefix.startsWith('drop') || sqlPrefix.startsWith('create'); - // 启用工单审批 - // if (nonQuery && getNowDbInst().flowProcdef) { - // try { - // getNowDbInst().promptExeSql(props.dbName, sql, null, () => { - // ElMessage.success('工单提交成功'); - // }); - // } catch (e) { - // ElMessage.success('工单提交失败'); - // } - // return; - // } - if (sqls.length == 1) { let execRemark; if (nonQuery) { @@ -432,28 +420,61 @@ const runSql = async (sql: string, remark = '', newTab = false) => { }; function splitSql(sql: string) { - // 移除注释 - sql = sql.replace(/\/\*[\s\S]*?\*\//g, ''); + let state = 'normal'; + let buffer = ''; + let result = []; + let inString = null; // 用于记录当前字符串的引号类型(' 或 ") - // 分割SQL语句 - const statements = sql.split(/;\s*|\/\*[\s\S]*?\*\//g); + for (let i = 0; i < sql.length; i++) { + const char = sql[i]; + const nextChar = sql[i + 1]; - // 移除空语句 - const filteredStatements = statements.filter((statement) => statement.trim() !== ''); - - // 处理包含分号的SQL语句 - const processedStatements = filteredStatements.map((statement) => { - const parts = statement.split(/[\s]+/); - const newParts = parts.map((part) => { - if (part.includes('(') && part.includes(')')) { - return part.replace(/\(/g, '[').replace(/\)/g, ']'); + if (state === 'normal') { + if (char === '-' && nextChar === '-') { + state = 'singleLineComment'; + i++; // 跳过下一个字符 + } else if (char === '/' && nextChar === '*') { + state = 'multiLineComment'; + i++; // 跳过下一个字符 + } else if (char === "'" || char === '"') { + state = 'string'; + inString = char; + buffer += char; + } else if (char === ';') { + if (buffer.trim()) { + result.push(buffer.trim()); + } + buffer = ''; + } else { + buffer += char; } - return part; - }); - return newParts.join(' '); - }); + } else if (state === 'string') { + buffer += char; + if (char === '\\') { + // 处理转义字符 + buffer += nextChar; + i++; + } else if (char === inString) { + state = 'normal'; + inString = null; + } + } else if (state === 'singleLineComment') { + if (char === '\n') { + state = 'normal'; + } + } else if (state === 'multiLineComment') { + if (char === '*' && nextChar === '/') { + state = 'normal'; + i++; // 跳过下一个字符 + } + } + } - return processedStatements; + if (buffer.trim()) { + result.push(buffer.trim()); + } + + return result; } /** diff --git a/mayfly_go_web/src/views/ops/db/enums.ts b/mayfly_go_web/src/views/ops/db/enums.ts index 599ba03a..8b89c739 100644 --- a/mayfly_go_web/src/views/ops/db/enums.ts +++ b/mayfly_go_web/src/views/ops/db/enums.ts @@ -11,6 +11,7 @@ export const DbSqlExecTypeEnum = { Delete: EnumValue.of(2, 'DELETE').setTagColor('#F9E2AE'), Insert: EnumValue.of(3, 'INSERT').setTagColor('#A8DEE0'), Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'), + Ddl: EnumValue.of(5, 'DDL').setTagColor('#F9E2AE'), Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'), }; diff --git a/server/internal/db/api/db.go b/server/internal/db/api/db.go index f481f2be..0c0da7f4 100644 --- a/server/internal/db/api/db.go +++ b/server/internal/db/api/db.go @@ -195,6 +195,9 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) { executedStatements++ _, err = dbConn.Exec(sql) + if err != nil { + return err + } return err }) diff --git a/server/internal/db/application/db_sql_exec.go b/server/internal/db/application/db_sql_exec.go index 6097dc6e..622a392b 100644 --- a/server/internal/db/application/db_sql_exec.go +++ b/server/internal/db/application/db_sql_exec.go @@ -109,6 +109,8 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) ( execRes, err = d.doInsert(ctx, sqlExec) } else if isOtherQuery(oneSql) { execRes, err = d.doOtherRead(ctx, sqlExec) + } else if isDDL(oneSql) { + execRes, err = d.doExecDDL(ctx, sqlExec) } else { execRes, err = d.doExec(ctx, dbConn, oneSql) } @@ -149,6 +151,18 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) ( execRes, err = d.doDelete(ctx, sqlExec) case *sqlstmt.InsertStmt: execRes, err = d.doInsert(ctx, sqlExec) + case *sqlstmt.DdlStmt: + execRes, err = d.doExecDDL(ctx, sqlExec) + case *sqlstmt.CreateDatabase: + execRes, err = d.doExecDDL(ctx, sqlExec) + case *sqlstmt.CreateTable: + execRes, err = d.doExecDDL(ctx, sqlExec) + case *sqlstmt.CreateIndex: + execRes, err = d.doExecDDL(ctx, sqlExec) + case *sqlstmt.AlterDatabase: + execRes, err = d.doExecDDL(ctx, sqlExec) + case *sqlstmt.AlterTable: + execRes, err = d.doExecDDL(ctx, sqlExec) default: execRes, err = d.doExec(ctx, dbConn, sql) } @@ -310,6 +324,19 @@ func (d *dbSqlExecAppImpl) doOtherRead(ctx context.Context, sqlExecParam *sqlExe return d.doQuery(ctx, sqlExecParam.DbConn, selectSql) } +func (d *dbSqlExecAppImpl) doExecDDL(ctx context.Context, sqlExecParam *sqlExecParam) (*DbSqlExecRes, error) { + selectSql := sqlExecParam.Sql + sqlExecParam.SqlExecRecord.Type = entity.DbSqlExecTypeDDL + + if procdef := sqlExecParam.Procdef; procdef != nil { + if needStartProc := procdef.MatchCondition(DbSqlExecFlowBizType, collx.Kvs("stmtType", "ddl")); needStartProc { + return nil, errorx.NewBiz("该操作需要提交工单审批执行") + } + } + + return d.doExec(ctx, sqlExecParam.DbConn, selectSql) +} + func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecParam) (*DbSqlExecRes, error) { dbConn := sqlExecParam.DbConn @@ -533,3 +560,9 @@ func isOtherQuery(sql string) bool { sqlPrefix := strings.ToLower(sql[:10]) return strings.Contains(sqlPrefix, "explain") || strings.Contains(sqlPrefix, "show") } + +func isDDL(sql string) bool { + sqlPrefix := strings.ToLower(sql[:10]) + return strings.Contains(sqlPrefix, "create") || strings.Contains(sqlPrefix, "alter") || + strings.Contains(sqlPrefix, "drop") || strings.Contains(sqlPrefix, "truncate") || strings.Contains(sqlPrefix, "rename") +} diff --git a/server/internal/db/dbm/sqlparser/mysql/visitor.go b/server/internal/db/dbm/sqlparser/mysql/visitor.go index ce21b5c1..a4743e17 100644 --- a/server/internal/db/dbm/sqlparser/mysql/visitor.go +++ b/server/internal/db/dbm/sqlparser/mysql/visitor.go @@ -50,7 +50,7 @@ func (v *MysqlVisitor) VisitEmptyStatement_(ctx *mysqlparser.EmptyStatement_Cont } func (v *MysqlVisitor) VisitDdlStatement(ctx *mysqlparser.DdlStatementContext) interface{} { - ddlStmt := sqlstmt.DdlStmt{} + ddlStmt := &sqlstmt.DdlStmt{} ddlStmt.Node = sqlstmt.NewNode(ctx.GetParser(), ctx) return ddlStmt } diff --git a/server/internal/db/dbm/sqlparser/sqlparser.go b/server/internal/db/dbm/sqlparser/sqlparser.go index 90d7a16b..654db06f 100644 --- a/server/internal/db/dbm/sqlparser/sqlparser.go +++ b/server/internal/db/dbm/sqlparser/sqlparser.go @@ -26,6 +26,7 @@ func SQLSplit(r io.Reader, callback SQLCallback) error { // SQLCallback 是解析出一条 SQL 语句后的回调函数 type SQLCallback func(sql string) error +// parseSQL 主要由阿里通义灵码提供 func parseSQL(r io.Reader, callback SQLCallback) error { reader := bufio.NewReaderSize(r, 512*1024) buffer := new(bytes.Buffer) // 使用 bytes.Buffer 来处理数据 @@ -34,6 +35,7 @@ func parseSQL(r io.Reader, callback SQLCallback) error { var inMultiLineComment bool var inSingleLineComment bool var stringDelimiter rune + var escapeNextChar bool // 用于处理转义符 for { // 读取数据到缓冲区 @@ -68,10 +70,18 @@ func parseSQL(r io.Reader, callback SQLCallback) error { } buffer.Next(size) case inString: - if r == stringDelimiter { + if escapeNextChar { + currentStatement.WriteRune(r) + escapeNextChar = false + } else if r == '\\' { + escapeNextChar = true + currentStatement.WriteRune(r) + } else if r == stringDelimiter { inString = false + currentStatement.WriteRune(r) + } else { + currentStatement.WriteRune(r) } - currentStatement.WriteRune(r) buffer.Next(size) case r == '/' && buffer.Len() >= 2 && buffer.Bytes()[1] == '*': inMultiLineComment = true diff --git a/server/internal/db/dbm/sqlparser/sqlparser_test.go b/server/internal/db/dbm/sqlparser/sqlparser_test.go index 993f8bca..36247c7a 100644 --- a/server/internal/db/dbm/sqlparser/sqlparser_test.go +++ b/server/internal/db/dbm/sqlparser/sqlparser_test.go @@ -12,7 +12,7 @@ func TestSQLSplit(t *testing.T) { /*删除*/ DELETE FROM t_sys_log WHERE - id IN (59) and name='哈哈哈';delete form tsyslog; + id IN (59) and name='哈哈哈' and name2="hahah;呵呵呵;";delete form tsyslog; -- alter sql语句 ALTER TABLE mayfly_go.t_sys_log DROP COLUMN delete_time; @@ -22,8 +22,7 @@ DROP COLUMN delete_time; INSERT INTO t_sys_log VALUES(15, 2, '用户登录', NULL, '⑴ 成孔;⑵ 砼浇筑;⑶ 桩头掏渣;⑷ 桩基检测配合; ⑸ 其他工作;⑹ 甲方现场要求乙方完成的其它临时工作。', '{"ip":"::1 ","username":"test_user"}', 'errCode: 401, errMsg: 您的密码安全等级较低,请修改后重新登录;\n信息嘻嘻嘻', '-', 0, '2024-04-23 20:00:35', 0, NULL, ''); ` - // fmt.Println(allsql) - // allsql2 := "select * from t_sys_log" + err := SQLSplit(strings.NewReader(allsql), func(sql string) error { // if strings.Contains(sql, "INSERT") { // return fmt.Errorf("不能存在INSERT语句") diff --git a/server/internal/db/domain/entity/db_sql_exec.go b/server/internal/db/domain/entity/db_sql_exec.go index 292845aa..e29b21c8 100644 --- a/server/internal/db/domain/entity/db_sql_exec.go +++ b/server/internal/db/domain/entity/db_sql_exec.go @@ -27,6 +27,7 @@ const ( DbSqlExecTypeDelete int8 = 2 // 删除类型 DbSqlExecTypeInsert int8 = 3 // 插入类型 DbSqlExecTypeQuery int8 = 4 // 查询类型,如select、show等 + DbSqlExecTypeDDL int8 = 5 // DDL DbSqlExecStatusWait = 1 DbSqlExecStatusSuccess = 2 diff --git a/server/internal/flow/api/procinst.go b/server/internal/flow/api/procinst.go index 8251e772..9ba53fa1 100644 --- a/server/internal/flow/api/procinst.go +++ b/server/internal/flow/api/procinst.go @@ -41,6 +41,7 @@ func (p *Procinst) ProcinstStart(rc *req.Ctx) { _, err := p.ProcinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{ BizType: startForm.BizType, BizForm: jsonx.ToStr(startForm.BizForm), + Remark: startForm.Remark, }) biz.ErrIsNil(err) }