mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-20 23:59:56 +08:00
- SQL 执行:移除 AI 工具和代码块预览中硬编码的 LIMIT 50 - 方言适配:按连接类型和自定义驱动别名生成只读 SQL 预览限流语句 - Oracle 兼容:Oracle、自定义 Oracle 和达梦改用 ROWNUM 语法限制行数 - 权限降级:获取表 DDL 失败时自动降级为字段元数据摘要 - 上下文优化:手动添加表结构上下文时复用同一套 DDL 降级逻辑 - 测试覆盖:新增 AI SQL 限流和表结构降级单元测试 Refs #418
70 lines
2.9 KiB
TypeScript
70 lines
2.9 KiB
TypeScript
type ToolQueryResult = {
|
|
success?: boolean;
|
|
data?: unknown;
|
|
message?: string;
|
|
};
|
|
|
|
type ResolveAITableSchemaToolResultParams = {
|
|
tableName: string;
|
|
fetchDDL: () => Promise<ToolQueryResult>;
|
|
fetchColumns: () => Promise<ToolQueryResult>;
|
|
};
|
|
|
|
const stringifyToolData = (data: unknown): string => (
|
|
typeof data === 'string' ? data : JSON.stringify(data)
|
|
);
|
|
|
|
const firstStringValue = (row: Record<string, unknown>, keys: string[]): string => {
|
|
for (const key of keys) {
|
|
const value = row[key];
|
|
if (value !== undefined && value !== null) {
|
|
return String(value);
|
|
}
|
|
}
|
|
return '';
|
|
};
|
|
|
|
const normalizeAIColumn = (raw: unknown) => {
|
|
const row = (raw && typeof raw === 'object') ? raw as Record<string, unknown> : {};
|
|
const keys = Object.keys(row);
|
|
return {
|
|
field: firstStringValue(row, ['Field', 'field', 'COLUMN_NAME', 'column_name', 'Name', 'name']) || (keys.length > 0 ? String(row[keys[0]] ?? '') : ''),
|
|
type: firstStringValue(row, ['Type', 'type', 'DATA_TYPE', 'data_type']) || (keys.length > 1 ? String(row[keys[1]] ?? '') : ''),
|
|
nullable: firstStringValue(row, ['Null', 'null', 'IS_NULLABLE', 'is_nullable', 'Nullable', 'nullable']),
|
|
default: firstStringValue(row, ['Default', 'default', 'COLUMN_DEFAULT', 'column_default', 'DefaultValue']),
|
|
comment: firstStringValue(row, ['Comment', 'comment', 'COLUMN_COMMENT', 'column_comment', 'Description']),
|
|
};
|
|
};
|
|
|
|
const buildColumnFallbackContent = (tableName: string, ddlError: string, columns: unknown[]): string => {
|
|
const normalizedColumns = columns.map(normalizeAIColumn).filter((column) => column.field.trim());
|
|
const fieldNames = normalizedColumns.map((column) => column.field).join(', ');
|
|
return [
|
|
`⚠️ 表 ${tableName} 的 DDL 获取失败,已降级为字段元数据摘要。`,
|
|
`DDL 错误:${ddlError || '未知错误'}`,
|
|
'该结果不包含完整索引、约束、触发器等 DDL 信息;请基于字段列表继续分析,不要因为 DDL 权限失败而停止。',
|
|
`可用字段:${fieldNames || '无'}`,
|
|
`详细信息:${JSON.stringify(normalizedColumns)}`,
|
|
].join('\n');
|
|
};
|
|
|
|
export const resolveAITableSchemaToolResult = async ({
|
|
tableName,
|
|
fetchDDL,
|
|
fetchColumns,
|
|
}: ResolveAITableSchemaToolResultParams): Promise<{ success: boolean; content: string }> => {
|
|
const ddlResult = await fetchDDL();
|
|
if (ddlResult?.success) {
|
|
return { success: true, content: stringifyToolData(ddlResult.data) };
|
|
}
|
|
|
|
const ddlError = ddlResult?.message || 'Failed to fetch DDL';
|
|
const columnResult = await fetchColumns();
|
|
if (columnResult?.success && Array.isArray(columnResult.data)) {
|
|
return { success: true, content: buildColumnFallbackContent(tableName, ddlError, columnResult.data) };
|
|
}
|
|
|
|
const columnError = columnResult?.message || 'Failed to fetch columns';
|
|
return { success: false, content: `获取建表语句失败:${ddlError};降级获取字段列表也失败:${columnError}` };
|
|
};
|