fix: sql切割转义等问题处理

This commit is contained in:
meilin.huang
2024-10-18 17:15:58 +08:00
parent a726927a28
commit 6837a9c867
10 changed files with 119 additions and 41 deletions

View File

@@ -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);
}
});
};

View File

@@ -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;
}
/**

View File

@@ -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'),
};

View File

@@ -195,6 +195,9 @@ func (d *Db) ExecSqlFile(rc *req.Ctx) {
executedStatements++
_, err = dbConn.Exec(sql)
if err != nil {
return err
}
return err
})

View File

@@ -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")
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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语句")

View File

@@ -27,6 +27,7 @@ const (
DbSqlExecTypeDelete int8 = 2 // 删除类型
DbSqlExecTypeInsert int8 = 3 // 插入类型
DbSqlExecTypeQuery int8 = 4 // 查询类型如select、show等
DbSqlExecTypeDDL int8 = 5 // DDL
DbSqlExecStatusWait = 1
DbSqlExecStatusSuccess = 2

View File

@@ -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)
}