mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-06 22:53:35 +08:00
- SQL 执行:移除 AI 工具和代码块预览中硬编码的 LIMIT 50 - 方言适配:按连接类型和自定义驱动别名生成只读 SQL 预览限流语句 - Oracle 兼容:Oracle、自定义 Oracle 和达梦改用 ROWNUM 语法限制行数 - 权限降级:获取表 DDL 失败时自动降级为字段元数据摘要 - 上下文优化:手动添加表结构上下文时复用同一套 DDL 降级逻辑 - 测试覆盖:新增 AI SQL 限流和表结构降级单元测试 Refs #418
32 lines
1.4 KiB
TypeScript
32 lines
1.4 KiB
TypeScript
import { buildPaginatedSelectSQL } from './sql';
|
|
import { resolveSqlDialect } from './sqlDialect';
|
|
|
|
const AI_READONLY_SQL_KEYWORDS = new Set(['select', 'show', 'describe', 'desc', 'explain', 'with', 'pragma', 'values']);
|
|
|
|
const trimSQLStatement = (sql: string): string => String(sql || '').trim().replace(/;\s*$/, '').trim();
|
|
|
|
const isAIReadonlySQL = (sql: string): boolean => {
|
|
const firstWord = trimSQLStatement(sql).trimStart().split(/\s+/)[0]?.toLowerCase() || '';
|
|
return AI_READONLY_SQL_KEYWORDS.has(firstWord);
|
|
};
|
|
|
|
const hasExistingRowLimit = (dialect: string, sql: string): boolean => {
|
|
const text = trimSQLStatement(sql).toLowerCase();
|
|
if (!text) return false;
|
|
if (/\blimit\s+\d+\b/.test(text)) return true;
|
|
if (/\bfetch\s+(first|next)\s+\d+\s+rows?\b/.test(text)) return true;
|
|
if (/\btop\s*\(?\s*\d+\s*\)?\b/.test(text)) return true;
|
|
|
|
return (dialect === 'oracle' || dialect === 'dameng') && /\brownum\b/.test(text);
|
|
};
|
|
|
|
export const buildAIReadonlyPreviewSQL = (dbType: string, sql: string, limit = 50, driver = ''): string => {
|
|
const baseSQL = trimSQLStatement(sql);
|
|
const safeLimit = Math.max(0, Math.floor(Number(limit) || 0));
|
|
const dialect = resolveSqlDialect(dbType, driver);
|
|
if (!baseSQL || safeLimit <= 0 || !isAIReadonlySQL(baseSQL) || hasExistingRowLimit(dialect, baseSQL)) {
|
|
return baseSQL;
|
|
}
|
|
return buildPaginatedSelectSQL(dialect, baseSQL, '', safeLimit, 0);
|
|
};
|