feat(gaussdb): 新增 GaussDB 数据库连接支持

Refs #497
This commit is contained in:
Syngnat
2026-06-13 19:34:52 +08:00
parent f3dfffb8d1
commit d2f68acae8
70 changed files with 717 additions and 73 deletions

View File

@@ -20,6 +20,7 @@ describe('connectionDriverType', () => {
expect(normalizeDriverType('apache_iotdb')).toBe('iotdb');
expect(normalizeDriverType('doris')).toBe('diros');
expect(normalizeDriverType('open-gauss')).toBe('opengauss');
expect(normalizeDriverType('gauss-db')).toBe('gaussdb');
expect(normalizeDriverType('InterSystemsIRIS')).toBe('iris');
});
@@ -27,6 +28,7 @@ describe('connectionDriverType', () => {
expect(resolveConnectionDriverType('mysql', 'postgresql')).toBe('mysql');
expect(resolveConnectionDriverType('custom', 'postgresql')).toBe('postgres');
expect(resolveConnectionDriverType('custom', 'open_gauss')).toBe('opengauss');
expect(resolveConnectionDriverType('custom', 'gauss_db')).toBe('gaussdb');
expect(resolveConnectionDriverType('custom', '')).toBe('');
});
@@ -44,6 +46,7 @@ describe('connectionDriverType', () => {
expect(isPostgresSchemaDialect('postgres')).toBe(true);
expect(isPostgresSchemaDialect('kingbase')).toBe(true);
expect(isPostgresSchemaDialect('open-gauss')).toBe(true);
expect(isPostgresSchemaDialect('gauss-db')).toBe(true);
expect(isPostgresSchemaDialect('mysql')).toBe(false);
});
});

View File

@@ -24,6 +24,11 @@ export const normalizeDriverType = (value: string): string => {
normalized === 'open-gauss' ||
normalized === 'opengauss'
) return 'opengauss';
if (
normalized === 'gaussdb' ||
normalized === 'gauss_db' ||
normalized === 'gauss-db'
) return 'gaussdb';
if (
normalized === 'intersystems' ||
normalized === 'intersystemsiris' ||
@@ -46,5 +51,5 @@ export const resolveSavedConnectionDriverType = (conn: SavedConnection | undefin
};
export const isPostgresSchemaDialect = (dialect: string): boolean => (
['postgres', 'kingbase', 'highgo', 'vastbase', 'opengauss'].includes(normalizeDriverType(dialect))
['postgres', 'kingbase', 'highgo', 'vastbase', 'opengauss', 'gaussdb'].includes(normalizeDriverType(dialect))
);

View File

@@ -84,6 +84,7 @@ describe('connectionModalPresentation', () => {
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'iris',
'mongodb',
'elasticsearch',
@@ -183,6 +184,14 @@ describe('connectionModalPresentation', () => {
'credentials',
'databaseScope',
]);
expect(resolveConnectionConfigLayout('gaussdb').sections).toEqual([
'identity',
'uri',
'target',
'service',
'credentials',
'databaseScope',
]);
});
it('uses localized labels for layout kinds shown in the modal', () => {

View File

@@ -71,6 +71,7 @@ const postgresCompatibleTypes = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
]);
const fileDatabaseTypes = new Set(['sqlite', 'duckdb']);

View File

@@ -15,6 +15,7 @@ describe('connectionTypeCapabilities', () => {
it('keeps single-host URI scheme aliases for URI parsing', () => {
expect(singleHostUriSchemesByType.postgres).toEqual(['postgresql', 'postgres']);
expect(singleHostUriSchemesByType.opengauss).toContain('jdbc:opengauss');
expect(singleHostUriSchemesByType.gaussdb).toEqual(['gaussdb', 'postgresql', 'postgres']);
expect(singleHostUriSchemesByType.dameng).toEqual(['dameng', 'dm']);
expect(singleHostUriSchemesByType.elasticsearch).toEqual(['http', 'https']);
expect(singleHostUriSchemesByType.chroma).toEqual(['http', 'https', 'chroma']);
@@ -27,6 +28,7 @@ describe('connectionTypeCapabilities', () => {
expect(supportsSSLForType('redis')).toBe(true);
expect(supportsSSLForType('MongoDB')).toBe(true);
expect(supportsSSLForType('elasticsearch')).toBe(true);
expect(supportsSSLForType('gaussdb')).toBe(true);
expect(supportsSSLForType('chroma')).toBe(true);
expect(supportsSSLForType('qdrant')).toBe(true);
expect(supportsSSLForType('tdengine')).toBe(true);
@@ -38,6 +40,8 @@ describe('connectionTypeCapabilities', () => {
it('keeps CA path and client certificate support distinct', () => {
expect(supportsSSLCAPathForType('dameng')).toBe(false);
expect(supportsSSLClientCertificateForType('dameng')).toBe(true);
expect(supportsSSLCAPathForType('gaussdb')).toBe(true);
expect(supportsSSLClientCertificateForType('gaussdb')).toBe(true);
expect(supportsSSLCAPathForType('sqlserver')).toBe(true);
expect(supportsSSLClientCertificateForType('sqlserver')).toBe(false);
expect(supportsSSLCAPathForType('redis')).toBe(true);
@@ -51,6 +55,7 @@ describe('connectionTypeCapabilities', () => {
it('detects postgres-compatible SSL parameter dialects', () => {
expect(isPostgresCompatibleSSLType('postgres')).toBe(true);
expect(isPostgresCompatibleSSLType('kingbase')).toBe(true);
expect(isPostgresCompatibleSSLType('gaussdb')).toBe(true);
expect(isPostgresCompatibleSSLType('HighGo')).toBe(true);
expect(isPostgresCompatibleSSLType('mysql')).toBe(false);
});
@@ -68,6 +73,7 @@ describe('connectionTypeCapabilities', () => {
it('keeps advanced connection params enabled only for supported database types', () => {
expect(supportsConnectionParamsForType('mysql')).toBe(true);
expect(supportsConnectionParamsForType('postgres')).toBe(true);
expect(supportsConnectionParamsForType('gaussdb')).toBe(true);
expect(supportsConnectionParamsForType('oracle')).toBe(true);
expect(supportsConnectionParamsForType('mongodb')).toBe(true);
expect(supportsConnectionParamsForType('dameng')).toBe(true);

View File

@@ -1,6 +1,7 @@
export const singleHostUriSchemesByType: Record<string, string[]> = {
postgres: ["postgresql", "postgres"],
opengauss: ["opengauss", "jdbc:opengauss", "postgresql", "postgres"],
gaussdb: ["gaussdb", "postgresql", "postgres"],
clickhouse: ["clickhouse"],
oracle: ["oracle"],
sqlserver: ["sqlserver"],
@@ -39,6 +40,7 @@ const sslSupportedTypes = new Set([
"highgo",
"vastbase",
"opengauss",
"gaussdb",
"mongodb",
"redis",
"tdengine",
@@ -64,6 +66,7 @@ const sslCAPathSupportedTypes = new Set([
"highgo",
"vastbase",
"opengauss",
"gaussdb",
"mongodb",
"redis",
"elasticsearch",
@@ -85,6 +88,7 @@ const sslClientCertificateSupportedTypes = new Set([
"highgo",
"vastbase",
"opengauss",
"gaussdb",
"mongodb",
"redis",
]);
@@ -102,6 +106,7 @@ export const isPostgresCompatibleSSLType = (type: string) =>
"highgo",
"vastbase",
"opengauss",
"gaussdb",
].includes(normalizeConnectionType(type));
export const isFileDatabaseType = (type: string) =>
@@ -123,6 +128,7 @@ export const supportsConnectionParamsForType = (type: string) =>
type === "highgo" ||
type === "vastbase" ||
type === "opengauss" ||
type === "gaussdb" ||
type === "oracle" ||
type === "sqlserver" ||
type === "iris" ||

View File

@@ -21,6 +21,7 @@ describe('connectionTypeCatalog', () => {
const keys = getAllConnectionTypeCatalogItems().map((item) => item.key);
expect(keys).toContain('mysql');
expect(keys).toContain('oceanbase');
expect(keys).toContain('gaussdb');
expect(keys).toContain('mongodb');
expect(keys).toContain('redis');
expect(keys).toContain('elasticsearch');
@@ -37,6 +38,7 @@ describe('connectionTypeCatalog', () => {
expect(getConnectionTypeDefaultPort('oceanbase')).toBe(2881);
expect(getConnectionTypeDefaultPort('diros')).toBe(9030);
expect(getConnectionTypeDefaultPort('postgres')).toBe(5432);
expect(getConnectionTypeDefaultPort('gaussdb')).toBe(5432);
expect(getConnectionTypeDefaultPort('redis')).toBe(6379);
expect(getConnectionTypeDefaultPort('oracle')).toBe(1521);
expect(getConnectionTypeDefaultPort('mongodb')).toBe(27017);

View File

@@ -35,6 +35,7 @@ export const CONNECTION_TYPE_GROUPS: ConnectionTypeCatalogGroup[] = [
{ key: 'highgo', name: 'HighGo (瀚高)' },
{ key: 'vastbase', name: 'Vastbase (海量)' },
{ key: 'opengauss', name: 'OpenGauss' },
{ key: 'gaussdb', name: 'GaussDB' },
],
},
{
@@ -86,6 +87,7 @@ export const getConnectionTypeDefaultPort = (type: string): number => {
return 9000;
case 'postgres':
case 'opengauss':
case 'gaussdb':
return 5432;
case 'redis':
return 6379;

View File

@@ -54,6 +54,25 @@ describe('dataSourceCapabilities', () => {
});
});
it('treats GaussDB as an editable PostgreSQL-family datasource with database-level DDL actions', () => {
expect(getDataSourceCapabilities({ type: 'gaussdb' })).toMatchObject({
type: 'gaussdb',
supportsQueryEditor: true,
supportsSqlQueryExport: true,
supportsCopyInsert: true,
supportsCreateDatabase: true,
supportsRenameDatabase: true,
supportsDropDatabase: true,
forceReadOnlyQueryResult: false,
});
expect(getDataSourceCapabilities({ type: 'custom', driver: 'gauss-db' })).toMatchObject({
type: 'gaussdb',
supportsQueryEditor: true,
supportsCopyInsert: true,
supportsRenameDatabase: true,
});
});
it('treats Elasticsearch as a queryable read-only datasource', () => {
expect(getDataSourceCapabilities({ type: 'elasticsearch' })).toMatchObject({
type: 'elasticsearch',

View File

@@ -16,6 +16,10 @@ const normalizeDataSourceToken = (raw: string): string => {
case 'open_gauss':
case 'open-gauss':
return 'opengauss';
case 'gaussdb':
case 'gauss_db':
case 'gauss-db':
return 'gaussdb';
case 'dm':
return 'dameng';
case 'elastic':
@@ -68,6 +72,7 @@ const SQL_QUERY_EXPORT_TYPES = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'sqlserver',
'iris',
'sqlite',
@@ -90,6 +95,7 @@ const COPY_INSERT_TYPES = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'sqlserver',
'iris',
'sqlite',
@@ -131,6 +137,7 @@ const CREATE_DATABASE_TYPES = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'sqlserver',
'tdengine',
'clickhouse',
@@ -143,6 +150,7 @@ const RENAME_DATABASE_TYPES = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
]);
const DROP_DATABASE_TYPES = new Set([
@@ -156,6 +164,7 @@ const DROP_DATABASE_TYPES = new Set([
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'tdengine',
'clickhouse',
]);

View File

@@ -12,6 +12,7 @@ const resolveDdlFormatterLanguage = (dbType: string): SqlLanguage => {
case 'kingbase':
case 'highgo':
case 'opengauss':
case 'gaussdb':
case 'vastbase':
return 'postgresql';
case 'mariadb':

View File

@@ -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。请填写 GoNavi 已注册的 Go database/sql 驱动名,不能直接填写系统 ODBC/JDBC 驱动名或导入 JDBC Jar。';
'已支持: mysql, starrocks, oceanbase, postgres, opengauss, gaussdb, sqlite, oracle, dm, kingbase别名支持 postgresql/pgx、open_gauss/open-gauss、gauss_db/gauss-db、dm8、kingbase8/kingbasees/kingbasev8。请填写 GoNavi 已注册的 Go database/sql 驱动名,不能直接填写系统 ODBC/JDBC 驱动名或导入 JDBC Jar。';

View File

@@ -18,6 +18,7 @@ describe('applyQueryAutoLimit', () => {
'highgo',
'vastbase',
'opengauss',
'gaussdb',
'iris',
'intersystemsiris',
'sqlite',
@@ -63,6 +64,7 @@ describe('applyQueryAutoLimit', () => {
['dm8', 'SELECT * FROM (SELECT * FROM users) WHERE ROWNUM <= 500'],
['mssql', 'SELECT TOP 500 * FROM users'],
['postgresql', 'SELECT * FROM users LIMIT 500'],
['gauss-db', 'SELECT * FROM users LIMIT 500'],
['doris', 'SELECT * FROM users LIMIT 500'],
['starrocks', 'SELECT * FROM users LIMIT 500'],
['sqlite3', 'SELECT * FROM users LIMIT 500'],

View File

@@ -11,6 +11,7 @@ const normalizeSidebarConnectionDialect = (type: string, driver: string, oceanBa
const normalizedDriver = String(driver || '').trim().toLowerCase();
if (normalizedDriver === 'postgresql' || normalizedDriver === 'postgres' || normalizedDriver === 'pg') return 'postgres';
if (normalizedDriver === 'opengauss' || normalizedDriver === 'open_gauss' || normalizedDriver === 'open-gauss') return 'opengauss';
if (normalizedDriver === 'gaussdb' || normalizedDriver === 'gauss_db' || normalizedDriver === 'gauss-db') return 'gaussdb';
if (normalizedDriver === 'dameng' || normalizedDriver === 'dm' || normalizedDriver === 'dm8') return 'dm';
if (normalizedDriver === 'oceanbase') {
return normalizeOceanBaseProtocol(oceanBaseProtocol) === 'oracle' ? 'oracle' : 'mysql';
@@ -22,6 +23,7 @@ const normalizeSidebarConnectionDialect = (type: string, driver: string, oceanBa
return normalizeOceanBaseProtocol(oceanBaseProtocol) === 'oracle' ? 'oracle' : 'mysql';
}
if (normalizedType === 'open_gauss' || normalizedType === 'open-gauss') return 'opengauss';
if (normalizedType === 'gauss_db' || normalizedType === 'gauss-db') return 'gaussdb';
if (normalizedType === 'dameng') return 'dm';
return normalizedType;
};

View File

@@ -34,7 +34,7 @@ export const quoteIdentPart = (dbType: string, ident: string) => {
}
// 对于 KingBase/PostgreSQL只在必要时加引号
if (dbTypeLower === 'kingbase' || dbTypeLower === 'postgres' || dbTypeLower === 'opengauss') {
if (dbTypeLower === 'kingbase' || dbTypeLower === 'postgres' || dbTypeLower === 'opengauss' || dbTypeLower === 'gaussdb') {
if (needsQuote(raw)) {
return `"${raw.replace(/"/g, '""')}"`;
}

View File

@@ -15,6 +15,7 @@ describe('sqlDialect', () => {
it('normalizes datasource aliases without collapsing all dialects to mysql', () => {
expect(resolveSqlDialect('postgresql')).toBe('postgres');
expect(resolveSqlDialect('OpenGauss')).toBe('opengauss');
expect(resolveSqlDialect('GaussDB')).toBe('gaussdb');
expect(resolveSqlDialect('OceanBase')).toBe('oceanbase');
expect(resolveSqlDialect('doris')).toBe('diros');
expect(resolveSqlDialect('StarRocks')).toBe('starrocks');
@@ -28,6 +29,7 @@ describe('sqlDialect', () => {
expect(resolveSqlDialect('custom', 'goldendb')).toBe('mysql');
expect(resolveSqlDialect('custom', 'greatdb')).toBe('mysql');
expect(resolveSqlDialect('custom', 'open_gauss')).toBe('opengauss');
expect(resolveSqlDialect('custom', 'gauss_db')).toBe('gaussdb');
expect(resolveSqlDialect('Elasticsearch')).toBe('elasticsearch');
expect(resolveSqlDialect('custom', 'elastic')).toBe('elasticsearch');
expect(resolveSqlDialect('ChromaDB')).toBe('chroma');
@@ -50,6 +52,7 @@ describe('sqlDialect', () => {
expect(values(resolveColumnTypeOptions('dameng'))).toContain('VARCHAR2(255)');
expect(values(resolveColumnTypeOptions('kingbase'))).toContain('integer');
expect(values(resolveColumnTypeOptions('opengauss'))).toContain('integer');
expect(values(resolveColumnTypeOptions('gaussdb'))).toContain('integer');
expect(values(resolveColumnTypeOptions('oceanbase'))).toContain('varchar(255)');
expect(values(resolveColumnTypeOptions('kingbase'))).not.toContain('tinyint(1)');
expect(values(resolveColumnTypeOptions('diros'))).toContain('LARGEINT');
@@ -68,6 +71,12 @@ describe('sqlDialect', () => {
expect(resolveSqlKeywords('iotdb')).not.toEqual(expect.arrayContaining(['TAGS', 'USING']));
});
it('resolves GaussDB completion keywords and functions as a PostgreSQL-like dialect', () => {
expect(resolveSqlKeywords('gaussdb')).toEqual(expect.arrayContaining(['RETURNING', 'SERIAL', 'JSONB']));
expect(names(resolveSqlFunctions('gaussdb'))).toEqual(expect.arrayContaining(['STRING_AGG', 'TO_CHAR', 'CURRENT_DATABASE']));
expect(resolveSqlKeywords('gaussdb')).not.toEqual(expect.arrayContaining(['AUTO_INCREMENT', 'CHANGE']));
});
it('resolves oracle completion keywords and functions without mysql-only suggestions', () => {
expect(resolveSqlKeywords('oracle')).toEqual(expect.arrayContaining(['ROWNUM', 'FETCH', 'VARCHAR2', 'NUMBER']));
expect(resolveSqlKeywords('oracle')).not.toEqual(expect.arrayContaining(['AUTO_INCREMENT', 'CHANGE', 'LIMIT']));

View File

@@ -19,6 +19,7 @@ export type SqlDialect =
| 'highgo'
| 'vastbase'
| 'opengauss'
| 'gaussdb'
| 'oracle'
| 'dameng'
| 'sqlserver'
@@ -69,6 +70,10 @@ export const resolveSqlDialect = (
case 'open_gauss':
case 'open-gauss':
return 'opengauss';
case 'gaussdb':
case 'gauss_db':
case 'gauss-db':
return 'gaussdb';
case 'mssql':
case 'sql_server':
case 'sql-server':
@@ -135,6 +140,7 @@ export const resolveSqlDialect = (
}
if (source.includes('opengauss') || source.includes('open_gauss') || source.includes('open-gauss')) return 'opengauss';
if (source.includes('gaussdb') || source.includes('gauss_db') || source.includes('gauss-db')) return 'gaussdb';
if (source.includes('postgres')) return 'postgres';
if (source.includes('oceanbase')) return 'oceanbase';
if (source.includes('mariadb')) return 'mariadb';
@@ -167,7 +173,7 @@ export const isMysqlFamilyDialect = (dbType: string): boolean => (
);
export const isPgLikeDialect = (dbType: string): boolean => (
['postgres', 'kingbase', 'highgo', 'vastbase', 'opengauss'].includes(resolveSqlDialect(dbType))
['postgres', 'kingbase', 'highgo', 'vastbase', 'opengauss', 'gaussdb'].includes(resolveSqlDialect(dbType))
);
export const isOracleLikeDialect = (dbType: string): boolean => (