🐛 fix(connection): 修复 IRIS 连接类型保存后回退为 MySQL

- 将 IRIS 纳入前端连接类型白名单与默认端口配置

- 补齐常见数据源类型别名归一化,避免未知别名回退为 MySQL

- 增加 IRIS 连接保存、导入、自动 Limit 和表数据清空回归测试

- 补齐前后端 IRIS truncate 支持

Refs #476
This commit is contained in:
Syngnat
2026-05-18 20:14:31 +08:00
parent 7b895474ef
commit b880b5416f
7 changed files with 109 additions and 1 deletions

View File

@@ -8,8 +8,10 @@ describe('tableDataDangerActions', () => {
expect(supportsTableTruncateAction('oceanbase')).toBe(true);
expect(supportsTableTruncateAction('postgres')).toBe(true);
expect(supportsTableTruncateAction('opengauss')).toBe(true);
expect(supportsTableTruncateAction('iris')).toBe(true);
expect(supportsTableTruncateAction('custom', 'postgresql')).toBe(true);
expect(supportsTableTruncateAction('custom', 'kingbase8')).toBe(true);
expect(supportsTableTruncateAction('custom', 'intersystemsiris')).toBe(true);
});
it('rejects truncate for unsupported or document-style backends', () => {

View File

@@ -41,6 +41,8 @@ const resolveCustomDriverDialect = (driver: string): string => {
case 'iris':
case 'intersystems':
case 'intersystemsiris':
case 'inter-systems':
case 'inter-systems-iris':
return 'iris';
default:
break;
@@ -53,6 +55,7 @@ const resolveCustomDriverDialect = (driver: string): string => {
if (normalized.includes('highgo')) return 'highgo';
if (normalized.includes('vastbase')) return 'vastbase';
if (normalized.includes('sqlite')) return 'sqlite';
if (normalized.includes('iris') || normalized.includes('intersystems')) return 'iris';
if (normalized.includes('sphinx')) return 'sphinx';
if (normalized.includes('diros') || normalized.includes('doris')) return 'diros';
if (normalized.includes('starrocks')) return 'starrocks';
@@ -79,6 +82,7 @@ export const supportsTableTruncateAction = (type: string, driver?: string): bool
case 'vastbase':
case 'opengauss':
case 'sqlserver':
case 'iris':
case 'oracle':
case 'dameng':
case 'clickhouse':

View File

@@ -262,6 +262,62 @@ describe('store appearance persistence', () => {
expect(config?.port).toBe(9030);
});
it('keeps InterSystems IRIS saved connections as independent datasource type', async () => {
const { useStore } = await importStore();
useStore.getState().replaceConnections([
{
id: 'iris-user',
name: 'IRIS USER',
config: {
id: 'iris-user',
type: 'iris',
host: 'iris.local',
port: 1972,
user: '_SYSTEM',
database: 'USER',
},
},
{
id: 'iris-alias',
name: 'IRIS Alias',
config: {
id: 'iris-alias',
type: 'InterSystemsIRIS',
host: 'iris-alias.local',
port: 1972,
user: '_SYSTEM',
database: 'USER',
},
},
]);
const connections = useStore.getState().connections;
expect(connections[0]?.config.type).toBe('iris');
expect(connections[0]?.config.port).toBe(1972);
expect(connections[1]?.config.type).toBe('iris');
});
it('normalizes saved connection type aliases without falling back to mysql', async () => {
const { useStore } = await importStore();
useStore.getState().replaceConnections([
{ id: 'pg', name: 'Postgres', config: { id: 'pg', type: 'PostgreSQL', host: 'pg.local', port: 5432, user: 'postgres' } },
{ id: 'mssql', name: 'MSSQL', config: { id: 'mssql', type: 'mssql', host: 'sql.local', port: 1433, user: 'sa' } },
{ id: 'kingbase', name: 'Kingbase', config: { id: 'kingbase', type: 'kingbase8', host: 'kingbase.local', port: 54321, user: 'system' } },
{ id: 'dm', name: 'Dameng', config: { id: 'dm', type: 'dm8', host: 'dm.local', port: 5236, user: 'SYSDBA' } },
{ id: 'sqlite', name: 'SQLite', config: { id: 'sqlite', type: 'sqlite3', host: 'D:/db/app.sqlite', port: 0, user: '' } },
]);
expect(useStore.getState().connections.map((conn) => conn.config.type)).toEqual([
'postgres',
'sqlserver',
'kingbase',
'dameng',
'sqlite',
]);
});
it('preserves SSL certificate paths for SSL-capable saved connections', async () => {
const { useStore } = await importStore();

View File

@@ -120,6 +120,7 @@ const SUPPORTED_CONNECTION_TYPES = new Set([
"dameng",
"kingbase",
"sqlserver",
"iris",
"mongodb",
"highgo",
"vastbase",
@@ -185,6 +186,8 @@ const getDefaultPortByType = (type: string): number => {
return 54321;
case "sqlserver":
return 1433;
case "iris":
return 1972;
case "mongodb":
return 27017;
case "highgo":
@@ -311,6 +314,24 @@ const normalizeConnectionType = (value: unknown): string => {
if (type === "doris") {
return "diros";
}
if (type === "postgresql") {
return "postgres";
}
if (type === "mssql" || type === "sql_server" || type === "sql-server") {
return "sqlserver";
}
if (type === "kingbase8" || type === "kingbasees" || type === "kingbasev8") {
return "kingbase";
}
if (type === "dm" || type === "dm8") {
return "dameng";
}
if (type === "sqlite3") {
return "sqlite";
}
if (type === "sphinxql") {
return "sphinx";
}
if (
type === "open_gauss" ||
type === "open-gauss" ||
@@ -318,6 +339,16 @@ const normalizeConnectionType = (value: unknown): string => {
) {
return "opengauss";
}
if (
type === "inter-systems" ||
type === "inter-systems-iris" ||
type === "intersystems" ||
type === "intersystems iris" ||
type === "intersystemsiris" ||
type.includes("iris")
) {
return "iris";
}
return SUPPORTED_CONNECTION_TYPES.has(type) ? type : DEFAULT_CONNECTION_TYPE;
};

View File

@@ -18,6 +18,8 @@ describe('applyQueryAutoLimit', () => {
'highgo',
'vastbase',
'opengauss',
'iris',
'intersystemsiris',
'sqlite',
'sqlite3',
'duckdb',

View File

@@ -1424,7 +1424,7 @@ const (
func supportsTruncateTableForDBType(dbType string) bool {
switch strings.ToLower(strings.TrimSpace(dbType)) {
case "mysql", "mariadb", "oceanbase", "starrocks", "postgres", "kingbase", "highgo", "vastbase", "opengauss", "sqlserver", "oracle", "dameng", "clickhouse", "duckdb":
case "mysql", "mariadb", "oceanbase", "starrocks", "postgres", "kingbase", "highgo", "vastbase", "opengauss", "sqlserver", "iris", "oracle", "dameng", "clickhouse", "duckdb":
return true
default:
return false

View File

@@ -85,6 +85,19 @@ func TestBuildTableDataClearSQL_KingbaseClearNormalizesQuotedQualifiedTable(t *t
}
}
func TestBuildTableDataClearSQL_IRISTruncateUsesNativeStatement(t *testing.T) {
t.Parallel()
sql, err := buildTableDataClearSQL(connection.ConnectionConfig{Type: "InterSystemsIRIS"}, "Sample.Person", tableDataClearModeTruncate)
if err != nil {
t.Fatalf("buildTableDataClearSQL() unexpected error: %v", err)
}
if sql != `TRUNCATE TABLE "Sample"."Person"` {
t.Fatalf("unexpected iris truncate sql: %s", sql)
}
}
func TestBuildTableDataClearSQL_TruncateRejectsUnsupportedDialect(t *testing.T) {
t.Parallel()