🐛 fix(ai-safety): 修正完全模式执行口径与本地工具失败判定

- 修正完全模式下 DML 与过程调用的安全提示和限制说明
- 区分连接探针失败与可恢复 SQL 执行错误,避免数据探针被误终止
- 修复本地 execute_sql 写语句结果返回 affectedRows
- 补充 AI 安全、本地工具执行与 SQL 限制回归测试
This commit is contained in:
Syngnat
2026-06-17 09:49:59 +08:00
parent 7ff3e00759
commit 3e140c1bc6
14 changed files with 254 additions and 24 deletions

View File

@@ -22,6 +22,11 @@ describe('buildAIReadonlyPreviewSQL', () => {
.toBe('SELECT * FROM (SELECT 1 FROM DUAL) WHERE ROWNUM <= 50');
});
it('treats OceanBase Oracle as Oracle dialect when building readonly preview SQL', () => {
expect(buildAIReadonlyPreviewSQL('oceanbase', 'SELECT 1 FROM DUAL;', 50, 'oceanbase', { oceanBaseProtocol: 'oracle' }))
.toBe('SELECT * FROM (SELECT 1 FROM DUAL) WHERE ROWNUM <= 50');
});
it('keeps MySQL-family SQL on LIMIT syntax', () => {
expect(buildAIReadonlyPreviewSQL('mysql', 'SELECT * FROM users', 50))
.toBe('SELECT * FROM users LIMIT 50 OFFSET 0');

View File

@@ -20,10 +20,16 @@ const hasExistingRowLimit = (dialect: string, sql: string): boolean => {
return (dialect === 'oracle' || dialect === 'dameng') && /\brownum\b/.test(text);
};
export const buildAIReadonlyPreviewSQL = (dbType: string, sql: string, limit = 50, driver = ''): string => {
export const buildAIReadonlyPreviewSQL = (
dbType: string,
sql: string,
limit = 50,
driver = '',
options?: { oceanBaseProtocol?: unknown },
): string => {
const baseSQL = trimSQLStatement(sql);
const safeLimit = Math.max(0, Math.floor(Number(limit) || 0));
const dialect = resolveSqlDialect(dbType, driver);
const dialect = resolveSqlDialect(dbType, driver, options);
if (!baseSQL || safeLimit <= 0 || !isAIReadonlySQL(baseSQL) || hasExistingRowLimit(dialect, baseSQL)) {
return baseSQL;
}