mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 07:50:25 +08:00
fix: sql切割转义等问题处理
This commit is contained in:
@@ -844,11 +844,20 @@ const onDeleteTable = async (data: any) => {
|
|||||||
let dialect = getDbDialect(state.nowDbInst.type);
|
let dialect = getDbDialect(state.nowDbInst.type);
|
||||||
let schemaStr = schema ? `${dialect.quoteIdentifier(schema)}.` : '';
|
let schemaStr = schema ? `${dialect.quoteIdentifier(schema)}.` : '';
|
||||||
|
|
||||||
dbApi.sqlExec.request({ id, db, sql: `drop table ${schemaStr + dialect.quoteIdentifier(tableName)}` }).then(() => {
|
dbApi.sqlExec.request({ id, db, sql: `drop table ${schemaStr + dialect.quoteIdentifier(tableName)}` }).then((res) => {
|
||||||
ElMessage.success('删除成功');
|
let success = true;
|
||||||
setTimeout(() => {
|
for (let re of res) {
|
||||||
parentKey && reloadNode(parentKey);
|
if (re.errorMsg) {
|
||||||
}, 1000);
|
success = false;
|
||||||
|
ElMessage.error(`${re.sql} -> 执行失败: ${re.errorMsg}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
ElMessage.success('删除成功');
|
||||||
|
setTimeout(() => {
|
||||||
|
parentKey && reloadNode(parentKey);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -309,18 +309,6 @@ const onRunSql = async (newTab = false) => {
|
|||||||
sqlPrefix.startsWith('drop') ||
|
sqlPrefix.startsWith('drop') ||
|
||||||
sqlPrefix.startsWith('create');
|
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) {
|
if (sqls.length == 1) {
|
||||||
let execRemark;
|
let execRemark;
|
||||||
if (nonQuery) {
|
if (nonQuery) {
|
||||||
@@ -432,28 +420,61 @@ const runSql = async (sql: string, remark = '', newTab = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function splitSql(sql: string) {
|
function splitSql(sql: string) {
|
||||||
// 移除注释
|
let state = 'normal';
|
||||||
sql = sql.replace(/\/\*[\s\S]*?\*\//g, '');
|
let buffer = '';
|
||||||
|
let result = [];
|
||||||
|
let inString = null; // 用于记录当前字符串的引号类型(' 或 ")
|
||||||
|
|
||||||
// 分割SQL语句
|
for (let i = 0; i < sql.length; i++) {
|
||||||
const statements = sql.split(/;\s*|\/\*[\s\S]*?\*\//g);
|
const char = sql[i];
|
||||||
|
const nextChar = sql[i + 1];
|
||||||
|
|
||||||
// 移除空语句
|
if (state === 'normal') {
|
||||||
const filteredStatements = statements.filter((statement) => statement.trim() !== '');
|
if (char === '-' && nextChar === '-') {
|
||||||
|
state = 'singleLineComment';
|
||||||
// 处理包含分号的SQL语句
|
i++; // 跳过下一个字符
|
||||||
const processedStatements = filteredStatements.map((statement) => {
|
} else if (char === '/' && nextChar === '*') {
|
||||||
const parts = statement.split(/[\s]+/);
|
state = 'multiLineComment';
|
||||||
const newParts = parts.map((part) => {
|
i++; // 跳过下一个字符
|
||||||
if (part.includes('(') && part.includes(')')) {
|
} else if (char === "'" || char === '"') {
|
||||||
return part.replace(/\(/g, '[').replace(/\)/g, ']');
|
state = 'string';
|
||||||
|
inString = char;
|
||||||
|
buffer += char;
|
||||||
|
} else if (char === ';') {
|
||||||
|
if (buffer.trim()) {
|
||||||
|
result.push(buffer.trim());
|
||||||
|
}
|
||||||
|
buffer = '';
|
||||||
|
} else {
|
||||||
|
buffer += char;
|
||||||
}
|
}
|
||||||
return part;
|
} else if (state === 'string') {
|
||||||
});
|
buffer += char;
|
||||||
return newParts.join(' ');
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const DbSqlExecTypeEnum = {
|
|||||||
Delete: EnumValue.of(2, 'DELETE').setTagColor('#F9E2AE'),
|
Delete: EnumValue.of(2, 'DELETE').setTagColor('#F9E2AE'),
|
||||||
Insert: EnumValue.of(3, 'INSERT').setTagColor('#A8DEE0'),
|
Insert: EnumValue.of(3, 'INSERT').setTagColor('#A8DEE0'),
|
||||||
Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'),
|
Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'),
|
||||||
|
Ddl: EnumValue.of(5, 'DDL').setTagColor('#F9E2AE'),
|
||||||
Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'),
|
Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,9 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
|
|||||||
|
|
||||||
executedStatements++
|
executedStatements++
|
||||||
_, err = dbConn.Exec(sql)
|
_, err = dbConn.Exec(sql)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ func (d *dbSqlExecAppImpl) Exec(ctx context.Context, execSqlReq *DbSqlExecReq) (
|
|||||||
execRes, err = d.doInsert(ctx, sqlExec)
|
execRes, err = d.doInsert(ctx, sqlExec)
|
||||||
} else if isOtherQuery(oneSql) {
|
} else if isOtherQuery(oneSql) {
|
||||||
execRes, err = d.doOtherRead(ctx, sqlExec)
|
execRes, err = d.doOtherRead(ctx, sqlExec)
|
||||||
|
} else if isDDL(oneSql) {
|
||||||
|
execRes, err = d.doExecDDL(ctx, sqlExec)
|
||||||
} else {
|
} else {
|
||||||
execRes, err = d.doExec(ctx, dbConn, oneSql)
|
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)
|
execRes, err = d.doDelete(ctx, sqlExec)
|
||||||
case *sqlstmt.InsertStmt:
|
case *sqlstmt.InsertStmt:
|
||||||
execRes, err = d.doInsert(ctx, sqlExec)
|
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:
|
default:
|
||||||
execRes, err = d.doExec(ctx, dbConn, sql)
|
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)
|
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) {
|
func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, sqlExecParam *sqlExecParam) (*DbSqlExecRes, error) {
|
||||||
dbConn := sqlExecParam.DbConn
|
dbConn := sqlExecParam.DbConn
|
||||||
|
|
||||||
@@ -533,3 +560,9 @@ func isOtherQuery(sql string) bool {
|
|||||||
sqlPrefix := strings.ToLower(sql[:10])
|
sqlPrefix := strings.ToLower(sql[:10])
|
||||||
return strings.Contains(sqlPrefix, "explain") || strings.Contains(sqlPrefix, "show")
|
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")
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func (v *MysqlVisitor) VisitEmptyStatement_(ctx *mysqlparser.EmptyStatement_Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *MysqlVisitor) VisitDdlStatement(ctx *mysqlparser.DdlStatementContext) interface{} {
|
func (v *MysqlVisitor) VisitDdlStatement(ctx *mysqlparser.DdlStatementContext) interface{} {
|
||||||
ddlStmt := sqlstmt.DdlStmt{}
|
ddlStmt := &sqlstmt.DdlStmt{}
|
||||||
ddlStmt.Node = sqlstmt.NewNode(ctx.GetParser(), ctx)
|
ddlStmt.Node = sqlstmt.NewNode(ctx.GetParser(), ctx)
|
||||||
return ddlStmt
|
return ddlStmt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func SQLSplit(r io.Reader, callback SQLCallback) error {
|
|||||||
// SQLCallback 是解析出一条 SQL 语句后的回调函数
|
// SQLCallback 是解析出一条 SQL 语句后的回调函数
|
||||||
type SQLCallback func(sql string) error
|
type SQLCallback func(sql string) error
|
||||||
|
|
||||||
|
// parseSQL 主要由阿里通义灵码提供
|
||||||
func parseSQL(r io.Reader, callback SQLCallback) error {
|
func parseSQL(r io.Reader, callback SQLCallback) error {
|
||||||
reader := bufio.NewReaderSize(r, 512*1024)
|
reader := bufio.NewReaderSize(r, 512*1024)
|
||||||
buffer := new(bytes.Buffer) // 使用 bytes.Buffer 来处理数据
|
buffer := new(bytes.Buffer) // 使用 bytes.Buffer 来处理数据
|
||||||
@@ -34,6 +35,7 @@ func parseSQL(r io.Reader, callback SQLCallback) error {
|
|||||||
var inMultiLineComment bool
|
var inMultiLineComment bool
|
||||||
var inSingleLineComment bool
|
var inSingleLineComment bool
|
||||||
var stringDelimiter rune
|
var stringDelimiter rune
|
||||||
|
var escapeNextChar bool // 用于处理转义符
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// 读取数据到缓冲区
|
// 读取数据到缓冲区
|
||||||
@@ -68,10 +70,18 @@ func parseSQL(r io.Reader, callback SQLCallback) error {
|
|||||||
}
|
}
|
||||||
buffer.Next(size)
|
buffer.Next(size)
|
||||||
case inString:
|
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
|
inString = false
|
||||||
|
currentStatement.WriteRune(r)
|
||||||
|
} else {
|
||||||
|
currentStatement.WriteRune(r)
|
||||||
}
|
}
|
||||||
currentStatement.WriteRune(r)
|
|
||||||
buffer.Next(size)
|
buffer.Next(size)
|
||||||
case r == '/' && buffer.Len() >= 2 && buffer.Bytes()[1] == '*':
|
case r == '/' && buffer.Len() >= 2 && buffer.Bytes()[1] == '*':
|
||||||
inMultiLineComment = true
|
inMultiLineComment = true
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func TestSQLSplit(t *testing.T) {
|
|||||||
/*删除*/
|
/*删除*/
|
||||||
DELETE FROM t_sys_log
|
DELETE FROM t_sys_log
|
||||||
WHERE
|
WHERE
|
||||||
id IN (59) and name='哈哈哈';delete form tsyslog;
|
id IN (59) and name='哈哈哈' and name2="hahah;呵呵呵;";delete form tsyslog;
|
||||||
-- alter sql语句
|
-- alter sql语句
|
||||||
ALTER TABLE mayfly_go.t_sys_log
|
ALTER TABLE mayfly_go.t_sys_log
|
||||||
DROP COLUMN delete_time;
|
DROP COLUMN delete_time;
|
||||||
@@ -22,8 +22,7 @@ DROP COLUMN delete_time;
|
|||||||
INSERT INTO t_sys_log VALUES(15, 2, '用户登录', NULL, '⑴ 成孔;⑵ 砼浇筑;⑶ 桩头掏渣;⑷ 桩基检测配合;
|
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, '');
|
⑸ 其他工作;⑹ 甲方现场要求乙方完成的其它临时工作。', '{"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 {
|
err := SQLSplit(strings.NewReader(allsql), func(sql string) error {
|
||||||
// if strings.Contains(sql, "INSERT") {
|
// if strings.Contains(sql, "INSERT") {
|
||||||
// return fmt.Errorf("不能存在INSERT语句")
|
// return fmt.Errorf("不能存在INSERT语句")
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const (
|
|||||||
DbSqlExecTypeDelete int8 = 2 // 删除类型
|
DbSqlExecTypeDelete int8 = 2 // 删除类型
|
||||||
DbSqlExecTypeInsert int8 = 3 // 插入类型
|
DbSqlExecTypeInsert int8 = 3 // 插入类型
|
||||||
DbSqlExecTypeQuery int8 = 4 // 查询类型,如select、show等
|
DbSqlExecTypeQuery int8 = 4 // 查询类型,如select、show等
|
||||||
|
DbSqlExecTypeDDL int8 = 5 // DDL
|
||||||
|
|
||||||
DbSqlExecStatusWait = 1
|
DbSqlExecStatusWait = 1
|
||||||
DbSqlExecStatusSuccess = 2
|
DbSqlExecStatusSuccess = 2
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func (p *Procinst) ProcinstStart(rc *req.Ctx) {
|
|||||||
_, err := p.ProcinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
|
_, err := p.ProcinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
|
||||||
BizType: startForm.BizType,
|
BizType: startForm.BizType,
|
||||||
BizForm: jsonx.ToStr(startForm.BizForm),
|
BizForm: jsonx.ToStr(startForm.BizForm),
|
||||||
|
Remark: startForm.Remark,
|
||||||
})
|
})
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user