feat(goldendb): 新增 GoldenDB 数据库连接支持

Refs #477
This commit is contained in:
Syngnat
2026-06-13 21:42:18 +08:00
parent 0ff17dc27c
commit 12fbc7ecf4
39 changed files with 324 additions and 34 deletions

View File

@@ -101,6 +101,18 @@ describe('ConnectionModal data source registry', () => {
expect(source).toContain('? "gaussdb"');
expect(source).toContain('dbType === "gaussdb"');
});
it('exposes GoldenDB in the create-connection picker with MySQL-compatible defaults', () => {
expect(source).toContain("case 'goldendb':");
expect(source).toContain('return 1523;');
expect(source).toContain("key: 'goldendb'");
expect(source).toContain("name: 'GoldenDB'");
expect(source).toContain('type === "goldendb"');
expect(source).toContain("return 'MySQL 兼容 / 分布式事务';");
expect(source).toContain('dbType === "goldendb" ? "goldendb" : "mysql"');
expect(source).toContain('type === "goldendb" ? "goldendb" : "mysql"');
expect(source).toContain('? "goldendb"');
});
});
describe('ConnectionModal Redis Sentinel configuration', () => {

View File

@@ -1458,6 +1458,9 @@ const ConnectionModal: React.FC<{
const mysqlDefaultPort = getDefaultPortByType(type);
const parsed =
parseMultiHostUri(trimmedUri, "mysql") ||
parseMultiHostUri(trimmedUri, "goldendb") ||
parseMultiHostUri(trimmedUri, "greatdb") ||
parseMultiHostUri(trimmedUri, "gdb") ||
parseMultiHostUri(trimmedUri, "jdbc:mysql") ||
parseMultiHostUri(trimmedUri, "oceanbase") ||
parseMultiHostUri(trimmedUri, "jdbc:oceanbase") ||
@@ -1909,7 +1912,7 @@ const ConnectionModal: React.FC<{
if (isMySQLCompatibleType(dbType)) {
const defaultPort = getDefaultPortByType(dbType);
const scheme =
dbType === "diros" ? "doris" : dbType === "starrocks" ? "starrocks" : dbType === "oceanbase" ? "oceanbase" : "mysql";
dbType === "diros" ? "doris" : dbType === "starrocks" ? "starrocks" : dbType === "oceanbase" ? "oceanbase" : dbType === "goldendb" ? "goldendb" : "mysql";
if (dbType === "oceanbase") {
return `${scheme}://sys%40oracle001:pass@127.0.0.1:${defaultPort}/SERVICE_NAME?protocol=oracle`;
}
@@ -2053,7 +2056,7 @@ const ConnectionModal: React.FC<{
const dbPath = database ? `/${encodeURIComponent(database)}` : "/";
const query = params.toString();
const scheme =
type === "diros" ? "doris" : type === "starrocks" ? "starrocks" : type === "oceanbase" ? "oceanbase" : "mysql";
type === "diros" ? "doris" : type === "starrocks" ? "starrocks" : type === "oceanbase" ? "oceanbase" : type === "goldendb" ? "goldendb" : "mysql";
return `${scheme}://${encodedAuth}${hosts.join(",")}${dbPath}${query ? `?${query}` : ""}`;
}
@@ -2450,6 +2453,7 @@ const ConnectionModal: React.FC<{
: primaryAddress?.port || Number(config.port || defaultPort);
const mysqlReplicaHosts =
configType === "mysql" ||
configType === "goldendb" ||
configType === "mariadb" ||
configType === "oceanbase" ||
configType === "diros" ||

View File

@@ -514,7 +514,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
const dbType = resolveDataSourceType(config);
const dbTypeLower = String(dbType || '').trim().toLowerCase();
const isMySQLFamily = dbTypeLower === 'mysql' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros';
const isMySQLFamily = dbTypeLower === 'mysql' || dbTypeLower === 'goldendb' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros';
const normalizedQuickWhereCondition = normalizeQuickWhereCondition(quickWhereCondition);
const quickWhereValidation = validateQuickWhereCondition(normalizedQuickWhereCondition);
if (!quickWhereValidation.ok) {

View File

@@ -53,6 +53,13 @@ describe('DatabaseIcons', () => {
expect(markup).toContain('>GS</text>');
});
it('includes GoldenDB in the selectable database icons', () => {
expect(DB_ICON_TYPES).toContain('goldendb');
expect(getDbIconLabel('goldendb')).toBe('GoldenDB');
const markup = renderToStaticMarkup(<>{getDbIcon('goldendb', undefined, 22)}</>);
expect(markup).toContain('>GD</text>');
});
it('wraps database icons in a consistent frame for sidebar sizing', () => {
const mysqlMarkup = renderToStaticMarkup(<>{getDbIcon('mysql', undefined, 22)}</>);
const jvmMarkup = renderToStaticMarkup(<>{getDbIcon('jvm', undefined, 22)}</>);

View File

@@ -47,6 +47,7 @@ const DB_DEFAULT_COLORS: Record<string, string> = {
vastbase: '#0066CC',
opengauss: '#2446A8',
gaussdb: '#0B7FAB',
goldendb: '#D97706',
highgo: '#00A86B',
iris: '#1F6FEB',
tdengine: '#2962FF',
@@ -177,6 +178,9 @@ const OpenGaussIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
const GaussDBIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.gaussdb} label="GS" />
);
const GoldenDBIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.goldendb} label="GD" />
);
const HighGoIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.highgo} label="HG" />
);
@@ -249,6 +253,7 @@ const DB_ICON_MAP: Record<string, React.FC<DbIconProps>> = {
vastbase: VastBaseIcon,
opengauss: OpenGaussIcon,
gaussdb: GaussDBIcon,
goldendb: GoldenDBIcon,
highgo: HighGoIcon,
iris: IrisIcon,
tdengine: TDengineIcon,
@@ -264,7 +269,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', 'gaussdb', 'highgo', 'iris', 'tdengine', 'iotdb', 'kafka', 'chroma', 'qdrant', 'elasticsearch', 'custom',
'kingbase', 'dameng', 'vastbase', 'opengauss', 'gaussdb', 'goldendb', 'highgo', 'iris', 'tdengine', 'iotdb', 'kafka', 'chroma', 'qdrant', 'elasticsearch', 'custom',
];
/** 该类型是否有品牌 SVG 文件 */
@@ -286,7 +291,7 @@ export const getDbIconLabel = (type: string): string => {
sqlserver: 'SQL Server', clickhouse: 'ClickHouse', sqlite: 'SQLite',
starrocks: 'StarRocks',
duckdb: 'DuckDB', kingbase: '金仓', dameng: '达梦',
vastbase: 'VastBase', opengauss: 'OpenGauss', gaussdb: 'GaussDB', highgo: '瀚高', iris: 'InterSystems IRIS', tdengine: 'TDengine', iotdb: 'Apache IoTDB', kafka: 'Kafka',
vastbase: 'VastBase', opengauss: 'OpenGauss', gaussdb: 'GaussDB', goldendb: 'GoldenDB', highgo: '瀚高', iris: 'InterSystems IRIS', tdengine: 'TDengine', iotdb: 'Apache IoTDB', kafka: 'Kafka',
chroma: 'Chroma',
qdrant: 'Qdrant',
elasticsearch: 'Elasticsearch',

View File

@@ -96,13 +96,14 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
if (type === 'custom') {
const driver = String(conn?.config?.driver || '').trim().toLowerCase();
if (driver === 'diros' || driver === 'doris') return 'mysql';
if (driver === 'goldendb' || driver === 'greatdb' || driver === 'gdb') return 'mysql';
if (driver === 'oceanbase') return normalizeOceanBaseProtocol(conn?.config?.oceanBaseProtocol) === 'oracle' ? 'oracle' : 'mysql';
if (driver === 'opengauss' || driver === 'open_gauss' || driver === 'open-gauss') return 'opengauss';
if (driver === 'gaussdb' || driver === 'gauss_db' || driver === 'gauss-db') return 'gaussdb';
return driver;
}
if (type === 'oceanbase' && normalizeOceanBaseProtocol(conn?.config?.oceanBaseProtocol) === 'oracle') return 'oracle';
if (type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'goldendb' || type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'dameng') return 'dm';
return type;
};

View File

@@ -186,7 +186,7 @@ const isSystemMetadataQueryResult = (tableRef: QueryResultTableRef, dbType: stri
const metadataDbName = stripQueryIdentifierQuotes(tableRef.metadataDbName).toLowerCase();
const metadataTableName = stripQueryIdentifierQuotes(tableRef.metadataTableName).toLowerCase();
if (['mysql', 'mariadb', 'oceanbase', 'diros', 'starrocks', 'sphinx', 'tidb'].includes(normalizedDbType)) {
if (['mysql', 'goldendb', 'mariadb', 'oceanbase', 'diros', 'starrocks', 'sphinx', 'tidb'].includes(normalizedDbType)) {
return MYSQL_SYSTEM_METADATA_SCHEMAS.has(metadataDbName);
}
if (['postgres', 'kingbase', 'highgo', 'vastbase', 'opengauss', 'gaussdb'].includes(normalizedDbType)) {

View File

@@ -120,13 +120,14 @@ const getMetadataDialect = (connType: string, driver?: string, oceanBaseProtocol
if (type === 'custom') {
const d = (driver || '').trim().toLowerCase();
if (d === 'diros' || d === 'doris') return 'mysql';
if (d === 'goldendb' || d === 'greatdb' || d === 'gdb') return 'mysql';
if (d === 'oceanbase') return normalizeOceanBaseProtocol(oceanBaseProtocol) === 'oracle' ? 'oracle' : 'mysql';
if (d === 'opengauss' || d === 'open_gauss' || d === 'open-gauss') return 'opengauss';
if (d === 'gaussdb' || d === 'gauss_db' || d === 'gauss-db') return 'gaussdb';
return d;
}
if (type === 'oceanbase' && normalizeOceanBaseProtocol(oceanBaseProtocol) === 'oracle') return 'oracle';
if (type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'goldendb' || type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'dameng') return 'dm';
return type;
};

View File

@@ -136,13 +136,14 @@ const TriggerViewer: React.FC<TriggerViewerProps> = ({ tab }) => {
if (type === 'custom') {
const driver = String(conn?.config?.driver || '').trim().toLowerCase();
if (driver === 'diros' || driver === 'doris') return 'mysql';
if (driver === 'goldendb' || driver === 'greatdb' || driver === 'gdb') return 'mysql';
if (driver === 'oceanbase') return normalizeOceanBaseProtocol(conn?.config?.oceanBaseProtocol) === 'oracle' ? 'oracle' : 'mysql';
if (driver === 'opengauss' || driver === 'open_gauss' || driver === 'open-gauss') return 'opengauss';
if (driver === 'gaussdb' || driver === 'gauss_db' || driver === 'gauss-db') return 'gaussdb';
return driver;
}
if (type === 'oceanbase' && normalizeOceanBaseProtocol(conn?.config?.oceanBaseProtocol) === 'oracle') return 'oracle';
if (type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'goldendb' || type === 'mariadb' || type === 'oceanbase' || type === 'diros' || type === 'sphinx') return 'mysql';
if (type === 'dameng') return 'dm';
return type;
};

View File

@@ -5,12 +5,14 @@ import { supportsTableTruncateAction } from './tableDataDangerActions';
describe('tableDataDangerActions', () => {
it('supports native truncate for known relational dialects', () => {
expect(supportsTableTruncateAction('mysql')).toBe(true);
expect(supportsTableTruncateAction('goldendb')).toBe(true);
expect(supportsTableTruncateAction('oceanbase')).toBe(true);
expect(supportsTableTruncateAction('postgres')).toBe(true);
expect(supportsTableTruncateAction('opengauss')).toBe(true);
expect(supportsTableTruncateAction('gaussdb')).toBe(true);
expect(supportsTableTruncateAction('iris')).toBe(true);
expect(supportsTableTruncateAction('custom', 'postgresql')).toBe(true);
expect(supportsTableTruncateAction('custom', 'greatdb')).toBe(true);
expect(supportsTableTruncateAction('custom', 'gauss_db')).toBe(true);
expect(supportsTableTruncateAction('custom', 'kingbase8')).toBe(true);
expect(supportsTableTruncateAction('custom', 'intersystemsiris')).toBe(true);

View File

@@ -17,6 +17,10 @@ const resolveCustomDriverDialect = (driver: string): string => {
case 'gauss_db':
case 'gauss-db':
return 'gaussdb';
case 'goldendb':
case 'greatdb':
case 'gdb':
return 'goldendb';
case 'dm':
case 'dameng':
case 'dm8':
@@ -54,6 +58,7 @@ const resolveCustomDriverDialect = (driver: string): string => {
if (normalized.includes('opengauss') || normalized.includes('open_gauss') || normalized.includes('open-gauss')) return 'opengauss';
if (normalized.includes('gaussdb') || normalized.includes('gauss_db') || normalized.includes('gauss-db')) return 'gaussdb';
if (normalized.includes('goldendb') || normalized.includes('greatdb')) return 'goldendb';
if (normalized.includes('postgres')) return 'postgres';
if (normalized.includes('oceanbase')) return 'oceanbase';
if (normalized.includes('kingbase')) return 'kingbase';
@@ -78,6 +83,7 @@ export const resolveTableDataActionDBType = (type: string, driver?: string): str
export const supportsTableTruncateAction = (type: string, driver?: string): boolean => {
switch (resolveTableDataActionDBType(type, driver)) {
case 'mysql':
case 'goldendb':
case 'mariadb':
case 'oceanbase':
case 'starrocks':

View File

@@ -279,6 +279,7 @@ const resolveOceanBaseProtocol = (
};
const SUPPORTED_CONNECTION_TYPES = new Set([
"mysql",
"goldendb",
"mariadb",
"oceanbase",
"doris",
@@ -309,6 +310,7 @@ const SUPPORTED_CONNECTION_TYPES = new Set([
]);
const SSL_SUPPORTED_CONNECTION_TYPES = new Set([
"mysql",
"goldendb",
"mariadb",
"oceanbase",
"diros",
@@ -338,6 +340,8 @@ const getDefaultPortByType = (type: string): number => {
case "mysql":
case "mariadb":
return 3306;
case "goldendb":
return 1523;
case "oceanbase":
return 2881;
case "doris":
@@ -529,6 +533,9 @@ const normalizeConnectionType = (value: unknown): string => {
if (type === "gaussdb" || type === "gauss_db" || type === "gauss-db") {
return "gaussdb";
}
if (type === "goldendb" || type === "greatdb" || type === "gdb") {
return "goldendb";
}
if (type === "kafka" || type === "apache-kafka" || type === "apache_kafka") {
return "kafka";
}

View File

@@ -23,6 +23,8 @@ describe('connectionDriverType', () => {
expect(normalizeDriverType('doris')).toBe('diros');
expect(normalizeDriverType('open-gauss')).toBe('opengauss');
expect(normalizeDriverType('gauss-db')).toBe('gaussdb');
expect(normalizeDriverType('greatdb')).toBe('goldendb');
expect(normalizeDriverType('gdb')).toBe('goldendb');
expect(normalizeDriverType('InterSystemsIRIS')).toBe('iris');
});
@@ -31,6 +33,7 @@ describe('connectionDriverType', () => {
expect(resolveConnectionDriverType('custom', 'postgresql')).toBe('postgres');
expect(resolveConnectionDriverType('custom', 'open_gauss')).toBe('opengauss');
expect(resolveConnectionDriverType('custom', 'gauss_db')).toBe('gaussdb');
expect(resolveConnectionDriverType('custom', 'goldendb')).toBe('goldendb');
expect(resolveConnectionDriverType('custom', '')).toBe('');
});

View File

@@ -30,6 +30,11 @@ export const normalizeDriverType = (value: string): string => {
normalized === 'gauss_db' ||
normalized === 'gauss-db'
) return 'gaussdb';
if (
normalized === 'goldendb' ||
normalized === 'greatdb' ||
normalized === 'gdb'
) return 'goldendb';
if (
normalized === 'intersystems' ||
normalized === 'intersystemsiris' ||

View File

@@ -67,6 +67,7 @@ describe('connectionModalPresentation', () => {
it('assigns card-based configuration sections to every supported data source type', () => {
const allTypes = [
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'doris',
@@ -117,6 +118,15 @@ describe('connectionModalPresentation', () => {
'credentials',
'databaseScope',
]);
expect(resolveConnectionConfigLayout('goldendb').sections).toEqual([
'identity',
'uri',
'target',
'connectionMode',
'replica',
'credentials',
'databaseScope',
]);
expect(resolveConnectionConfigLayout('mongodb').sections).toEqual([
'identity',
'uri',

View File

@@ -58,6 +58,7 @@ type ConnectionConfigSectionCopy = {
const mysqlCompatibleTypes = new Set([
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'doris',

View File

@@ -29,6 +29,7 @@ describe('connectionTypeCapabilities', () => {
expect(supportsSSLForType('MongoDB')).toBe(true);
expect(supportsSSLForType('elasticsearch')).toBe(true);
expect(supportsSSLForType('gaussdb')).toBe(true);
expect(supportsSSLForType('greatdb')).toBe(true);
expect(supportsSSLForType('chroma')).toBe(true);
expect(supportsSSLForType('qdrant')).toBe(true);
expect(supportsSSLForType('kafka')).toBe(true);
@@ -68,6 +69,7 @@ describe('connectionTypeCapabilities', () => {
expect(isFileDatabaseType('duckdb')).toBe(true);
expect(isFileDatabaseType('DuckDB')).toBe(false);
expect(isMySQLCompatibleType('mysql')).toBe(true);
expect(isMySQLCompatibleType('goldendb')).toBe(true);
expect(isMySQLCompatibleType('oceanbase')).toBe(true);
expect(isMySQLCompatibleType('diros')).toBe(true);
expect(isMySQLCompatibleType('postgres')).toBe(false);
@@ -75,6 +77,7 @@ describe('connectionTypeCapabilities', () => {
it('keeps advanced connection params enabled only for supported database types', () => {
expect(supportsConnectionParamsForType('mysql')).toBe(true);
expect(supportsConnectionParamsForType('gdb')).toBe(true);
expect(supportsConnectionParamsForType('postgres')).toBe(true);
expect(supportsConnectionParamsForType('gaussdb')).toBe(true);
expect(supportsConnectionParamsForType('oracle')).toBe(true);

View File

@@ -19,12 +19,23 @@ export const singleHostUriSchemesByType: Record<string, string[]> = {
};
const normalizeConnectionType = (type: string) =>
String(type || "")
.trim()
.toLowerCase();
{
const normalized = String(type || "")
.trim()
.toLowerCase();
switch (normalized) {
case "goldendb":
case "greatdb":
case "gdb":
return "goldendb";
default:
return normalized;
}
};
const sslSupportedTypes = new Set([
"mysql",
"goldendb",
"mariadb",
"oceanbase",
"doris",
@@ -55,6 +66,7 @@ export const supportsSSLForType = (type: string) =>
const sslCAPathSupportedTypes = new Set([
"mysql",
"goldendb",
"mariadb",
"oceanbase",
"diros",
@@ -78,6 +90,7 @@ const sslCAPathSupportedTypes = new Set([
const sslClientCertificateSupportedTypes = new Set([
"mysql",
"goldendb",
"mariadb",
"oceanbase",
"diros",
@@ -116,13 +129,14 @@ export const isFileDatabaseType = (type: string) =>
type === "sqlite" || type === "duckdb";
export const isMySQLCompatibleType = (type: string) =>
type === "mysql" ||
type === "mariadb" ||
type === "oceanbase" ||
type === "doris" ||
type === "diros" ||
type === "starrocks" ||
type === "sphinx";
normalizeConnectionType(type) === "mysql" ||
normalizeConnectionType(type) === "goldendb" ||
normalizeConnectionType(type) === "mariadb" ||
normalizeConnectionType(type) === "oceanbase" ||
normalizeConnectionType(type) === "doris" ||
normalizeConnectionType(type) === "diros" ||
normalizeConnectionType(type) === "starrocks" ||
normalizeConnectionType(type) === "sphinx";
export const supportsConnectionParamsForType = (type: string) =>
isMySQLCompatibleType(type) ||

View File

@@ -23,6 +23,7 @@ describe('connectionTypeCatalog', () => {
expect(keys).toContain('mysql');
expect(keys).toContain('oceanbase');
expect(keys).toContain('gaussdb');
expect(keys).toContain('goldendb');
expect(keys).toContain('mongodb');
expect(keys).toContain('redis');
expect(keys).toContain('elasticsearch');
@@ -38,6 +39,7 @@ describe('connectionTypeCatalog', () => {
it('returns the existing default port mapping for supported connection types', () => {
expect(getConnectionTypeDefaultPort('mysql')).toBe(3306);
expect(getConnectionTypeDefaultPort('oceanbase')).toBe(2881);
expect(getConnectionTypeDefaultPort('goldendb')).toBe(1523);
expect(getConnectionTypeDefaultPort('diros')).toBe(9030);
expect(getConnectionTypeDefaultPort('postgres')).toBe(5432);
expect(getConnectionTypeDefaultPort('gaussdb')).toBe(5432);
@@ -63,6 +65,7 @@ describe('connectionTypeCatalog', () => {
expect(getConnectionTypeHint('iotdb')).toContain('Timeseries');
expect(getConnectionTypeHint('kafka')).toContain('Consumer Group');
expect(getConnectionTypeHint('oceanbase')).toBe('MySQL / Oracle 租户');
expect(getConnectionTypeHint('goldendb')).toBe('MySQL 兼容 / 分布式事务');
expect(getConnectionTypeHint('duckdb')).toBe('本地文件连接');
expect(getConnectionTypeHint('mysql')).toBe('标准连接配置');
});

View File

@@ -36,6 +36,7 @@ export const CONNECTION_TYPE_GROUPS: ConnectionTypeCatalogGroup[] = [
{ key: 'vastbase', name: 'Vastbase (海量)' },
{ key: 'opengauss', name: 'OpenGauss' },
{ key: 'gaussdb', name: 'GaussDB' },
{ key: 'goldendb', name: 'GoldenDB' },
],
},
{
@@ -83,6 +84,8 @@ export const getConnectionTypeDefaultPort = (type: string): number => {
return 3306;
case 'oceanbase':
return 2881;
case 'goldendb':
return 1523;
case 'doris':
case 'diros':
case 'starrocks':
@@ -157,6 +160,8 @@ export const getConnectionTypeHint = (type: string): string => {
return 'Broker / Topic / Consumer Group';
case 'oceanbase':
return 'MySQL / Oracle 租户';
case 'goldendb':
return 'MySQL 兼容 / 分布式事务';
case 'sqlite':
case 'duckdb':
return '本地文件连接';

View File

@@ -30,6 +30,24 @@ describe('dataSourceCapabilities', () => {
});
});
it('treats GoldenDB as an editable MySQL-family datasource with database-level DDL actions', () => {
expect(getDataSourceCapabilities({ type: 'goldendb' })).toMatchObject({
type: 'goldendb',
supportsQueryEditor: true,
supportsSqlQueryExport: true,
supportsCopyInsert: true,
supportsCreateDatabase: true,
supportsRenameDatabase: false,
supportsDropDatabase: true,
forceReadOnlyQueryResult: false,
});
expect(getDataSourceCapabilities({ type: 'custom', driver: 'greatdb' })).toMatchObject({
type: 'goldendb',
supportsQueryEditor: true,
supportsCopyInsert: true,
});
});
it('keeps StarRocks as an independent SQL datasource capability', () => {
expect(getDataSourceCapabilities({ type: 'starrocks' })).toMatchObject({
type: 'starrocks',

View File

@@ -20,6 +20,10 @@ const normalizeDataSourceToken = (raw: string): string => {
case 'gauss_db':
case 'gauss-db':
return 'gaussdb';
case 'goldendb':
case 'greatdb':
case 'gdb':
return 'goldendb';
case 'dm':
return 'dameng';
case 'elastic':
@@ -66,6 +70,7 @@ export const resolveDataSourceType = (config: ConnectionLike): string => {
const SQL_QUERY_EXPORT_TYPES = new Set([
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'diros',
@@ -89,6 +94,7 @@ const SQL_QUERY_EXPORT_TYPES = new Set([
const COPY_INSERT_TYPES = new Set([
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'diros',
@@ -132,6 +138,7 @@ export type DataSourceCapabilities = {
const CREATE_DATABASE_TYPES = new Set([
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'diros',
@@ -159,6 +166,7 @@ const RENAME_DATABASE_TYPES = new Set([
const DROP_DATABASE_TYPES = new Set([
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'diros',

View File

@@ -18,6 +18,7 @@ const resolveDdlFormatterLanguage = (dbType: string): SqlLanguage => {
case 'mariadb':
return 'mariadb';
case 'mysql':
case 'goldendb':
case 'sphinx':
return 'mysql';
case 'sqlserver':

View File

@@ -5,6 +5,7 @@ import { applyQueryAutoLimit } from './queryAutoLimit';
describe('applyQueryAutoLimit', () => {
const limitDialects = [
'mysql',
'goldendb',
'mariadb',
'oceanbase',
'diros',

View File

@@ -25,6 +25,7 @@ describe('sidebarMetadata', () => {
});
it('uses MySQL metadata queries for custom MySQL-compatible domestic drivers', () => {
expect(resolveSidebarMetadataDialect('goldendb')).toBe('mysql');
expect(resolveSidebarMetadataDialect('custom', 'gdb')).toBe('mysql');
expect(resolveSidebarMetadataDialect('custom', 'goldendb')).toBe('mysql');
expect(resolveSidebarMetadataDialect('custom', 'greatdb')).toBe('mysql');

View File

@@ -64,6 +64,11 @@ describe('quoteQualifiedIdent', () => {
.toBe('"logs.app-1"');
});
it('quotes GoldenDB identifiers with MySQL-style backticks', () => {
expect(quoteQualifiedIdent('goldendb', 'ledger.entries'))
.toBe('`ledger`.`entries`');
});
it('does not split dots inside quoted DuckDB identifiers', () => {
expect(quoteQualifiedIdent('duckdb', '"daily.events"."2026.06"'))
.toBe('"daily.events"."2026.06"');

View File

@@ -29,7 +29,7 @@ export const quoteIdentPart = (dbType: string, ident: string) => {
if (!raw) return raw;
const dbTypeLower = (dbType || '').toLowerCase();
if (dbTypeLower === 'mysql' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros' || dbTypeLower === 'starrocks' || dbTypeLower === 'sphinx' || dbTypeLower === 'tdengine' || dbTypeLower === 'iotdb' || dbTypeLower === 'clickhouse') {
if (dbTypeLower === 'mysql' || dbTypeLower === 'goldendb' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros' || dbTypeLower === 'starrocks' || dbTypeLower === 'sphinx' || dbTypeLower === 'tdengine' || dbTypeLower === 'iotdb' || dbTypeLower === 'clickhouse') {
return `\`${raw.replace(/`/g, '``')}\``;
}
@@ -149,7 +149,7 @@ export const buildOrderBySQL = (
// 部分数据源在无显式排序需求时强制 ORDER BY即使按主键会显著放大大表预览成本
// MySQL/MariaDB 可能触发 filesort 和 sort memory 错误DuckDB 大文件可能被排序拖到连接超时。
// 因此仅在用户主动点击排序时下发 ORDER BY默认分页查询不加兜底排序。
if (dbTypeLower === 'mysql' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros' || dbTypeLower === 'starrocks' || dbTypeLower === 'duckdb') {
if (dbTypeLower === 'mysql' || dbTypeLower === 'goldendb' || dbTypeLower === 'mariadb' || dbTypeLower === 'oceanbase' || dbTypeLower === 'diros' || dbTypeLower === 'starrocks' || dbTypeLower === 'duckdb') {
return '';
}