feat(iris): 新增 InterSystems IRIS 数据源支持

- 后端新增 IRIS 连接、查询、DDL、索引元数据和 DataGrid 编辑能力
- 接入 optional driver-agent、构建标签、revision 生成和变更检测流程
- 前端新增 IRIS 连接入口、方言映射、能力配置和图标展示
- 修复 IRIS 主键识别、事务开启错误处理和驱动连接关闭问题
- 补充后端、前端和构建脚本相关回归测试
Refs #408
This commit is contained in:
Syngnat
2026-05-17 10:32:08 +08:00
parent 0cde96844d
commit 992d2dee45
57 changed files with 4391 additions and 16 deletions

View File

@@ -224,6 +224,8 @@ const getDefaultPortByType = (type: string) => {
return 54321;
case "sqlserver":
return 1433;
case "iris":
return 1972;
case "mongodb":
return 27017;
case "highgo":
@@ -247,6 +249,7 @@ const singleHostUriSchemesByType: Record<string, string[]> = {
clickhouse: ["clickhouse"],
oracle: ["oracle"],
sqlserver: ["sqlserver"],
iris: ["iris", "intersystems"],
redis: ["redis"],
tdengine: ["tdengine"],
dameng: ["dameng", "dm"],
@@ -368,6 +371,7 @@ const supportsConnectionParamsForType = (type: string) =>
type === "opengauss" ||
type === "oracle" ||
type === "sqlserver" ||
type === "iris" ||
type === "clickhouse" ||
type === "mongodb" ||
type === "dameng" ||
@@ -390,6 +394,13 @@ const normalizeDriverType = (value: string): string => {
.toLowerCase();
if (normalized === "postgresql") return "postgres";
if (normalized === "doris") return "diros";
if (
normalized === "intersystems" ||
normalized === "intersystemsiris" ||
normalized === "inter-systems-iris" ||
normalized === "inter-systems"
)
return "iris";
if (
normalized === "open_gauss" ||
normalized === "open-gauss" ||
@@ -1980,6 +1991,9 @@ const ConnectionModal: React.FC<{
if (dbType === "oracle") {
return "oracle://user:pass@127.0.0.1:1521/ORCLPDB1";
}
if (dbType === "iris") {
return "iris://user:pass@127.0.0.1:1972/USER";
}
if (dbType === "opengauss") {
return "opengauss://user:pass@127.0.0.1:5432/db_name";
}
@@ -2006,6 +2020,8 @@ const ConnectionModal: React.FC<{
return "PREFETCH_ROWS=5000&TRACE FILE=/tmp/go-ora.trc";
case "sqlserver":
return "app name=GoNavi&packet size=32767";
case "iris":
return "timeout=30";
case "clickhouse":
return "max_execution_time=60&compress=lz4";
case "mongodb":
@@ -3869,6 +3885,11 @@ const ConnectionModal: React.FC<{
name: "SQL Server",
icon: getDbIcon("sqlserver", undefined, 36),
},
{
key: "iris",
name: "InterSystems IRIS",
icon: getDbIcon("iris", undefined, 36),
},
{
key: "sqlite",
name: "SQLite",

View File

@@ -0,0 +1,10 @@
import { describe, expect, it } from 'vitest';
import { DB_ICON_TYPES, getDbIconLabel } from './DatabaseIcons';
describe('DatabaseIcons', () => {
it('includes InterSystems IRIS in the selectable database icons', () => {
expect(DB_ICON_TYPES).toContain('iris');
expect(getDbIconLabel('iris')).toBe('InterSystems IRIS');
});
});

View File

@@ -27,6 +27,7 @@ const DB_DEFAULT_COLORS: Record<string, string> = {
vastbase: '#0066CC',
opengauss: '#2446A8',
highgo: '#00A86B',
iris: '#1F6FEB',
tdengine: '#2962FF',
diros: '#0050B3',
starrocks: '#00A6A6',
@@ -146,6 +147,9 @@ const OpenGaussIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
const HighGoIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.highgo} label="HG" />
);
const IrisIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.iris} label="IR" />
);
const TDengineIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.tdengine} label="TD" />
);
@@ -195,6 +199,7 @@ const DB_ICON_MAP: Record<string, React.FC<DbIconProps>> = {
vastbase: VastBaseIcon,
opengauss: OpenGaussIcon,
highgo: HighGoIcon,
iris: IrisIcon,
tdengine: TDengineIcon,
custom: CustomIcon,
};
@@ -203,7 +208,7 @@ const DB_ICON_MAP: Record<string, React.FC<DbIconProps>> = {
export const DB_ICON_TYPES: string[] = [
'mysql', 'mariadb', 'oceanbase', 'postgres', 'redis', 'mongodb', 'jvm',
'oracle', 'sqlserver', 'sqlite', 'duckdb', 'clickhouse', 'starrocks',
'kingbase', 'dameng', 'vastbase', 'opengauss', 'highgo', 'tdengine', 'custom',
'kingbase', 'dameng', 'vastbase', 'opengauss', 'highgo', 'iris', 'tdengine', 'custom',
];
/** 该类型是否有品牌 SVG 文件 */
@@ -225,7 +230,7 @@ export const getDbIconLabel = (type: string): string => {
sqlserver: 'SQL Server', clickhouse: 'ClickHouse', sqlite: 'SQLite',
starrocks: 'StarRocks',
duckdb: 'DuckDB', kingbase: '金仓', dameng: '达梦',
vastbase: 'VastBase', opengauss: 'OpenGauss', highgo: '瀚高', tdengine: 'TDengine',
vastbase: 'VastBase', opengauss: 'OpenGauss', highgo: '瀚高', iris: 'InterSystems IRIS', tdengine: 'TDengine',
custom: '自定义',
};
return labels[type?.toLowerCase()] || type;

View File

@@ -131,6 +131,12 @@ const normalizeDriverType = (value: string): string => {
normalized === 'open-gauss' ||
normalized === 'opengauss'
) return 'opengauss';
if (
normalized === 'intersystems' ||
normalized === 'intersystemsiris' ||
normalized === 'inter-systems' ||
normalized === 'inter-systems-iris'
) return 'iris';
return normalized;
};
@@ -648,6 +654,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
'open_gauss',
'open-gauss',
'sqlserver',
'iris',
'oracle',
'dameng',
]);
@@ -661,6 +668,7 @@ const Sidebar: React.FC<{ onEditConnection?: (conn: SavedConnection) => void }>
'open_gauss',
'open-gauss',
'sqlserver',
'iris',
'oracle',
'dm',
]);

View File

@@ -38,6 +38,10 @@ const resolveCustomDriverDialect = (driver: string): string => {
return 'highgo';
case 'vastbase':
return 'vastbase';
case 'iris':
case 'intersystems':
case 'intersystemsiris':
return 'iris';
default:
break;
}