Files
MyGoNavi/frontend/src/utils/sqlServerObjectDefinition.ts
Syngnat 54195e0591 🐛 fix(sqlserver): 修复对象 SQL 定义获取失败
- SQL Server 对象定义改为通过 sys.all_sql_modules 按库、schema、对象名精确查询
- 增加 sp_helptext 兼容候选,支持拼接多行 Text 返回完整定义
- 统一修复视图、函数/存储过程、触发器定义查看与对象修改入口
- 补充 SQL Server 对象定义查询和组件回归测试
2026-06-16 12:54:39 +08:00

82 lines
3.3 KiB
TypeScript

import { splitQualifiedNameSegments } from './qualifiedName';
export type SqlServerObjectDefinitionKind = 'view' | 'routine' | 'trigger';
const SQL_SERVER_OBJECT_TYPES: Record<SqlServerObjectDefinitionKind, string[]> = {
view: ['V'],
routine: ['P', 'PC', 'RF', 'FN', 'FS', 'FT', 'IF', 'TF'],
trigger: ['TR', 'TA'],
};
const escapeSqlServerLiteral = (raw: string): string => String(raw || '').replace(/'/g, "''");
const quoteSqlServerIdentifier = (raw: string): string => `[${String(raw || '').replace(/]/g, ']]')}]`;
const buildSqlServerObjectRef = (schemaName: string, objectName: string): string => {
const object = String(objectName || '').trim();
const schema = String(schemaName || '').trim();
if (!object) return '';
if (!schema) return object;
return `${quoteSqlServerIdentifier(schema)}.${quoteSqlServerIdentifier(object)}`;
};
const resolveSqlServerObjectTarget = (rawObjectName: string, fallbackDbName: string) => {
const segments = splitQualifiedNameSegments(rawObjectName).filter(Boolean);
const objectName = String(segments[segments.length - 1] || '').trim();
let schemaName = '';
let databaseName = String(fallbackDbName || '').trim();
if (segments.length >= 3) {
databaseName = String(segments[segments.length - 3] || databaseName).trim();
schemaName = String(segments[segments.length - 2] || '').trim();
} else if (segments.length === 2) {
schemaName = String(segments[0] || '').trim();
}
return { databaseName, schemaName, objectName };
};
export const buildSqlServerObjectDefinitionQueries = (
kind: SqlServerObjectDefinitionKind,
objectName: string,
dbName: string,
resultAlias: string,
): string[] => {
const target = resolveSqlServerObjectTarget(objectName, dbName);
if (!target.objectName) return [];
const catalogPrefix = target.databaseName ? `${quoteSqlServerIdentifier(target.databaseName)}.` : '';
const safeObjectName = escapeSqlServerLiteral(target.objectName);
const safeSchemaName = escapeSqlServerLiteral(target.schemaName);
const objectTypes = SQL_SERVER_OBJECT_TYPES[kind] || [];
const typeFilter = objectTypes.length > 0
? ` AND o.type IN (${objectTypes.map((type) => `'${type}'`).join(', ')})\n`
: '';
const schemaFilter = target.schemaName
? ` AND s.name = N'${safeSchemaName}'\n`
: '';
const schemaOrder = target.schemaName
? 's.name'
: "CASE WHEN s.name = N'dbo' THEN 0 WHEN s.name = N'sys' THEN 1 ELSE 2 END, s.name";
const moduleQuery = [
`SELECT TOP (1)`,
` m.definition AS ${resultAlias}`,
`FROM ${catalogPrefix}sys.all_sql_modules AS m`,
`JOIN ${catalogPrefix}sys.all_objects AS o ON o.object_id = m.object_id`,
`JOIN ${catalogPrefix}sys.schemas AS s ON s.schema_id = o.schema_id`,
`WHERE o.name = N'${safeObjectName}'`,
typeFilter.trimEnd(),
schemaFilter.trimEnd(),
` AND m.definition IS NOT NULL`,
`ORDER BY ${schemaOrder}, o.name`,
].filter(Boolean).join('\n');
const objectRef = buildSqlServerObjectRef(target.schemaName, target.objectName);
const helpTextProcedure = target.databaseName
? `EXEC ${quoteSqlServerIdentifier(target.databaseName)}.sys.sp_helptext @objname = N'${escapeSqlServerLiteral(objectRef)}'`
: `EXEC sys.sp_helptext @objname = N'${escapeSqlServerLiteral(objectRef)}'`;
return [moduleQuery, helpTextProcedure];
};