Files
MyGoNavi/frontend/src/utils/sqlErrorSemantics.ts
Syngnat 9acb1c69f7 feat(sql-editor): 增加SQL错误中文语义提示
- 新增 SQL 执行错误语义化规则,覆盖语法、对象、字段、约束和连接类错误

- 执行失败和刷新失败展示中文语义、处理建议与原始错误

- 补充工具函数与 QueryEditor 回归测试,确保英文报错可读化
2026-06-04 10:48:17 +08:00

178 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export type SqlExecutionErrorFormatOptions = {
prefix?: string;
};
type SqlErrorSemanticRule = {
label: string;
explanation: string;
suggestion: string;
patterns: RegExp[];
};
const SQL_ERROR_RULES: SqlErrorSemanticRule[] = [
{
label: 'SQL 语法错误',
explanation: '通常是关键字、逗号、括号、引号、语句顺序或当前数据库方言不匹配。',
suggestion: '检查报错位置附近的 SQL 片段,并确认当前连接的数据源类型与 SQL 方言一致。',
patterns: [
/syntax error/i,
/sql syntax/i,
/sqlstate\s*42601/i,
/near\s+["'`].+["'`]\s*:?\s*syntax error/i,
/ora-00933/i,
/ora-00936/i,
/you have an error in your sql syntax/i,
],
},
{
label: '表或对象不存在',
explanation: 'SQL 引用了当前库或 schema 中找不到的表、视图、序列或其他数据库对象。',
suggestion: '确认对象名称、大小写、schema/database 前缀,以及当前查询所选数据库是否正确。',
patterns: [
/relation\s+["'`].+["'`]\s+does not exist/i,
/table\s+.+doesn'?t exist/i,
/no such table/i,
/invalid object name/i,
/ora-00942/i,
/object\s+.+does not exist/i,
],
},
{
label: '字段不存在',
explanation: 'SQL 引用了结果集中不存在、拼写不一致或当前表没有的字段。',
suggestion: '检查字段名、别名、大小写、引用表别名,以及字段是否属于当前 FROM/JOIN 的对象。',
patterns: [
/column\s+["'`].+["'`]\s+does not exist/i,
/unknown column/i,
/invalid column name/i,
/ora-00904/i,
/no such column/i,
],
},
{
label: '唯一约束或主键冲突',
explanation: '插入或更新的数据与唯一索引、主键或唯一约束中的已有数据重复。',
suggestion: '检查重复键值,必要时改为 UPDATE、UPSERT或调整唯一键字段值。',
patterns: [
/duplicate key/i,
/duplicate entry/i,
/unique constraint failed/i,
/violates unique constraint/i,
/ora-00001/i,
],
},
{
label: '权限不足',
explanation: '当前数据库账号没有执行该 SQL 或访问相关对象的权限。',
suggestion: '确认账号权限、schema 授权、只读连接限制,以及是否需要由管理员授权。',
patterns: [
/permission denied/i,
/access denied/i,
/not authorized/i,
/insufficient privileges/i,
/ora-01031/i,
],
},
{
label: '数据类型或格式不匹配',
explanation: '写入、比较或转换的数据格式不符合目标字段或表达式要求。',
suggestion: '检查日期、数字、布尔值、枚举值、隐式转换和字段类型,必要时显式 CAST。',
patterns: [
/invalid input syntax/i,
/incorrect\s+.+\s+value/i,
/data truncated/i,
/truncated incorrect/i,
/conversion failed/i,
/invalid number/i,
/ora-01722/i,
],
},
{
label: '约束校验失败',
explanation: '数据不满足外键、非空、检查约束或引用完整性规则。',
suggestion: '检查关联父表记录、必填字段、CHECK 条件,以及写入顺序是否正确。',
patterns: [
/foreign key constraint/i,
/violates foreign key constraint/i,
/cannot be null/i,
/not null constraint failed/i,
/check constraint/i,
/constraint failed/i,
],
},
{
label: '查询超时或被取消',
explanation: 'SQL 执行时间超过超时限制,或执行过程被手动取消。',
suggestion: '检查 SQL 执行计划、过滤条件和索引,必要时缩小查询范围或调整超时时间。',
patterns: [
/context deadline exceeded/i,
/statement canceled/i,
/statement cancelled/i,
/context canceled/i,
/context cancelled/i,
/timeout/i,
/timed out/i,
],
},
{
label: '数据库连接或认证失败',
explanation: '客户端无法连接数据库,或认证信息、网络、实例状态存在问题。',
suggestion: '检查主机、端口、账号密码、网络连通性、代理/SSH 隧道和数据库服务状态。',
patterns: [
/password authentication failed/i,
/connection refused/i,
/no route to host/i,
/server has gone away/i,
/too many connections/i,
/connection reset/i,
/connection timeout/i,
],
},
];
const normalizeErrorText = (raw: unknown): string => {
if (raw instanceof Error) {
return raw.message || String(raw);
}
if (typeof raw === 'string') {
return raw;
}
if (raw == null) {
return '';
}
try {
return JSON.stringify(raw);
} catch {
return String(raw);
}
};
const findSqlErrorSemantic = (message: string): SqlErrorSemanticRule | null => {
const text = String(message || '');
return SQL_ERROR_RULES.find((rule) => rule.patterns.some((pattern) => pattern.test(text))) || null;
};
export const formatSqlExecutionError = (
raw: unknown,
options: SqlExecutionErrorFormatOptions = {},
): string => {
const rawMessage = normalizeErrorText(raw).trim() || '未知错误';
if (/中文语义:/.test(rawMessage) && /原始错误:/.test(rawMessage)) {
return rawMessage;
}
const semantic = findSqlErrorSemantic(rawMessage) || {
label: '数据库执行错误',
explanation: '数据库返回了执行失败信息,当前未匹配到更具体的错误类型。',
suggestion: '结合原始错误、SQL 片段和当前数据库方言继续排查。',
};
const prefix = String(options.prefix || '').trim();
return [
prefix,
`中文语义:${semantic.label}${semantic.explanation}`,
`处理建议:${semantic.suggestion}`,
`原始错误:${rawMessage}`,
].filter(Boolean).join('\n');
};