mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-31 13:39:48 +08:00
✨ feat(iris): 新增 InterSystems IRIS 数据源支持
- 后端新增 IRIS 连接、查询、DDL、索引元数据和 DataGrid 编辑能力 - 接入 optional driver-agent、构建标签、revision 生成和变更检测流程 - 前端新增 IRIS 连接入口、方言映射、能力配置和图标展示 - 修复 IRIS 主键识别、事务开启错误处理和驱动连接关闭问题 - 补充后端、前端和构建脚本相关回归测试 Refs #408
This commit is contained in:
@@ -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",
|
||||
|
||||
10
frontend/src/components/DatabaseIcons.test.tsx
Normal file
10
frontend/src/components/DatabaseIcons.test.tsx
Normal 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');
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
]);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ describe('connectionModalPresentation', () => {
|
||||
'highgo',
|
||||
'vastbase',
|
||||
'opengauss',
|
||||
'iris',
|
||||
'mongodb',
|
||||
'redis',
|
||||
'tdengine',
|
||||
@@ -139,6 +140,13 @@ describe('connectionModalPresentation', () => {
|
||||
'customDriver',
|
||||
'customDsn',
|
||||
]);
|
||||
expect(resolveConnectionConfigLayout('iris').sections).toEqual([
|
||||
'identity',
|
||||
'uri',
|
||||
'target',
|
||||
'credentials',
|
||||
'databaseScope',
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses localized labels for layout kinds shown in the modal', () => {
|
||||
|
||||
@@ -40,6 +40,20 @@ describe('dataSourceCapabilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps InterSystems IRIS as an editable SQL datasource capability', () => {
|
||||
expect(getDataSourceCapabilities({ type: 'iris' })).toMatchObject({
|
||||
type: 'iris',
|
||||
supportsQueryEditor: true,
|
||||
supportsSqlQueryExport: true,
|
||||
supportsCopyInsert: true,
|
||||
forceReadOnlyQueryResult: false,
|
||||
});
|
||||
expect(getDataSourceCapabilities({ type: 'custom', driver: 'intersystemsiris' })).toMatchObject({
|
||||
type: 'iris',
|
||||
supportsQueryEditor: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('treats OceanBase Oracle protocol as Oracle capabilities', () => {
|
||||
expect(getDataSourceCapabilities({
|
||||
type: 'oceanbase',
|
||||
|
||||
@@ -18,6 +18,11 @@ const normalizeDataSourceToken = (raw: string): string => {
|
||||
return 'opengauss';
|
||||
case 'dm':
|
||||
return 'dameng';
|
||||
case 'intersystems':
|
||||
case 'intersystemsiris':
|
||||
case 'inter-systems':
|
||||
case 'inter-systems-iris':
|
||||
return 'iris';
|
||||
default:
|
||||
return normalized;
|
||||
}
|
||||
@@ -52,6 +57,7 @@ const SQL_QUERY_EXPORT_TYPES = new Set([
|
||||
'vastbase',
|
||||
'opengauss',
|
||||
'sqlserver',
|
||||
'iris',
|
||||
'sqlite',
|
||||
'duckdb',
|
||||
'oracle',
|
||||
@@ -73,6 +79,7 @@ const COPY_INSERT_TYPES = new Set([
|
||||
'vastbase',
|
||||
'opengauss',
|
||||
'sqlserver',
|
||||
'iris',
|
||||
'sqlite',
|
||||
'duckdb',
|
||||
'oracle',
|
||||
|
||||
@@ -19,6 +19,8 @@ describe('driver import guidance', () => {
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('pgx');
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('open_gauss');
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('oceanbase');
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('Go database/sql');
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('ODBC/JDBC');
|
||||
expect(CUSTOM_CONNECTION_DRIVER_HELP).toContain('JDBC Jar');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,4 +7,4 @@ export const DRIVER_LOCAL_IMPORT_SINGLE_FILE_HELP =
|
||||
'行内“导入驱动包”仅用于单个驱动文件/总包(如 `mariadb-driver-agent`、`mariadb-driver-agent.exe`、`GoNavi-DriverAgents.zip`),不支持直接导入 JDBC Jar;批量导入请使用上方“导入驱动目录”。';
|
||||
|
||||
export const CUSTOM_CONNECTION_DRIVER_HELP =
|
||||
'已支持: mysql, starrocks, oceanbase, postgres, opengauss, sqlite, oracle, dm, kingbase;别名支持 postgresql/pgx、open_gauss/open-gauss、dm8、kingbase8/kingbasees/kingbasev8。当前不支持通过 JDBC Jar 扩展驱动。';
|
||||
'已支持: mysql, starrocks, oceanbase, postgres, opengauss, sqlite, oracle, dm, kingbase;别名支持 postgresql/pgx、open_gauss/open-gauss、dm8、kingbase8/kingbasees/kingbasev8。请填写 GoNavi 已注册的 Go database/sql 驱动名,不能直接填写系统 ODBC/JDBC 驱动名或导入 JDBC Jar。';
|
||||
|
||||
@@ -19,6 +19,8 @@ describe('sqlDialect', () => {
|
||||
expect(resolveSqlDialect('doris')).toBe('diros');
|
||||
expect(resolveSqlDialect('StarRocks')).toBe('starrocks');
|
||||
expect(resolveSqlDialect('dameng')).toBe('dameng');
|
||||
expect(resolveSqlDialect('InterSystems IRIS')).toBe('iris');
|
||||
expect(resolveSqlDialect('custom', 'intersystemsiris')).toBe('iris');
|
||||
expect(resolveSqlDialect('custom', 'kingbase8')).toBe('kingbase');
|
||||
expect(resolveSqlDialect('custom', 'dm8')).toBe('dameng');
|
||||
expect(resolveSqlDialect('custom', 'mariadb')).toBe('mariadb');
|
||||
@@ -43,6 +45,7 @@ describe('sqlDialect', () => {
|
||||
expect(values(resolveColumnTypeOptions('starrocks'))).toContain('PERCENTILE');
|
||||
expect(values(resolveColumnTypeOptions('sphinx'))).toContain('text');
|
||||
expect(values(resolveColumnTypeOptions('clickhouse'))).toContain('DateTime64(3)');
|
||||
expect(values(resolveColumnTypeOptions('iris'))).toContain('varchar(255)');
|
||||
expect(values(resolveColumnTypeOptions('tdengine'))).toContain('TIMESTAMP');
|
||||
expect(values(resolveColumnTypeOptions('duckdb'))).toContain('STRUCT');
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ export type SqlDialect =
|
||||
| 'oracle'
|
||||
| 'dameng'
|
||||
| 'sqlserver'
|
||||
| 'iris'
|
||||
| 'sqlite'
|
||||
| 'duckdb'
|
||||
| 'clickhouse'
|
||||
@@ -68,6 +69,12 @@ export const resolveSqlDialect = (
|
||||
case 'sql_server':
|
||||
case 'sql-server':
|
||||
return 'sqlserver';
|
||||
case 'intersystems':
|
||||
case 'intersystemsiris':
|
||||
case 'inter-systems':
|
||||
case 'inter-systems-iris':
|
||||
case 'iris':
|
||||
return 'iris';
|
||||
case 'doris':
|
||||
case 'diros':
|
||||
return 'diros';
|
||||
@@ -122,6 +129,7 @@ export const resolveSqlDialect = (
|
||||
if (source.includes('clickhouse')) return 'clickhouse';
|
||||
if (source.includes('tdengine')) return 'tdengine';
|
||||
if (source.includes('sqlserver') || source.includes('mssql')) return 'sqlserver';
|
||||
if (source.includes('iris') || source.includes('intersystems')) return 'iris';
|
||||
|
||||
return source;
|
||||
};
|
||||
@@ -479,6 +487,7 @@ export const resolveColumnTypeOptions = (dbType: string): ColumnTypeOption[] =>
|
||||
if (dialect === 'oracle') return ORACLE_TYPES;
|
||||
if (dialect === 'dameng') return DAMENG_TYPES;
|
||||
if (dialect === 'sqlserver') return SQLSERVER_TYPES;
|
||||
if (dialect === 'iris') return COMMON_TYPES;
|
||||
if (dialect === 'sqlite') return SQLITE_TYPES;
|
||||
if (dialect === 'duckdb') return DUCKDB_TYPES;
|
||||
if (dialect === 'clickhouse') return CLICKHOUSE_TYPES;
|
||||
|
||||
Reference in New Issue
Block a user