From b880b5416f7e48ab3da647be8af5e8f54c1d103e Mon Sep 17 00:00:00 2001 From: Syngnat Date: Mon, 18 May 2026 20:14:31 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(connection):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20IRIS=20=E8=BF=9E=E6=8E=A5=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=90=8E=E5=9B=9E=E9=80=80=E4=B8=BA=20MySQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 IRIS 纳入前端连接类型白名单与默认端口配置 - 补齐常见数据源类型别名归一化,避免未知别名回退为 MySQL - 增加 IRIS 连接保存、导入、自动 Limit 和表数据清空回归测试 - 补齐前后端 IRIS truncate 支持 Refs #476 --- .../components/tableDataDangerActions.test.ts | 2 + .../src/components/tableDataDangerActions.ts | 4 ++ frontend/src/store.test.ts | 56 +++++++++++++++++++ frontend/src/store.ts | 31 ++++++++++ frontend/src/utils/queryAutoLimit.test.ts | 2 + internal/app/methods_file.go | 2 +- internal/app/methods_file_clear_test.go | 13 +++++ 7 files changed, 109 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/tableDataDangerActions.test.ts b/frontend/src/components/tableDataDangerActions.test.ts index 65f5579..bc40082 100644 --- a/frontend/src/components/tableDataDangerActions.test.ts +++ b/frontend/src/components/tableDataDangerActions.test.ts @@ -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', () => { diff --git a/frontend/src/components/tableDataDangerActions.ts b/frontend/src/components/tableDataDangerActions.ts index 7169f78..e5160e3 100644 --- a/frontend/src/components/tableDataDangerActions.ts +++ b/frontend/src/components/tableDataDangerActions.ts @@ -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': diff --git a/frontend/src/store.test.ts b/frontend/src/store.test.ts index fc2fb9c..8c83d4a 100644 --- a/frontend/src/store.test.ts +++ b/frontend/src/store.test.ts @@ -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(); diff --git a/frontend/src/store.ts b/frontend/src/store.ts index a6e3283..7548e5c 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -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; }; diff --git a/frontend/src/utils/queryAutoLimit.test.ts b/frontend/src/utils/queryAutoLimit.test.ts index e561e7e..5e05d63 100644 --- a/frontend/src/utils/queryAutoLimit.test.ts +++ b/frontend/src/utils/queryAutoLimit.test.ts @@ -18,6 +18,8 @@ describe('applyQueryAutoLimit', () => { 'highgo', 'vastbase', 'opengauss', + 'iris', + 'intersystemsiris', 'sqlite', 'sqlite3', 'duckdb', diff --git a/internal/app/methods_file.go b/internal/app/methods_file.go index bbc1825..3bfccbd 100644 --- a/internal/app/methods_file.go +++ b/internal/app/methods_file.go @@ -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 diff --git a/internal/app/methods_file_clear_test.go b/internal/app/methods_file_clear_test.go index 4f0cd9c..dfcd9a6 100644 --- a/internal/app/methods_file_clear_test.go +++ b/internal/app/methods_file_clear_test.go @@ -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()