mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
@@ -9241,7 +9241,7 @@ function openExternalUrl(url: string) {
|
||||
<Input v-model="elasticsearchIndexGroupingPattern" class="col-span-3" :placeholder="t('connection.elasticsearchIndexGroupingPatternPlaceholder')" @input="resetTestState" />
|
||||
</div>
|
||||
|
||||
<div v-if="form.driver_profile === 'gbase8s'" class="grid grid-cols-4 items-center gap-4">
|
||||
<div v-if="form.db_type === 'gbase8s'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelSmallClass">{{ t("connection.gbaseServer") }}</Label>
|
||||
<div class="col-span-3 space-y-1">
|
||||
<Input v-model="form.gbase_server" placeholder="gbase01" />
|
||||
|
||||
@@ -4440,7 +4440,7 @@ const canSetCreateDatabaseCharset = computed(() => {
|
||||
|
||||
const canSetCreateDatabaseLocale = computed(() => {
|
||||
const config = activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined;
|
||||
return connectionNamespaceCreationTarget(config) === "database" && supportsCreateDatabaseLocale(config?.db_type, config?.driver_profile);
|
||||
return connectionNamespaceCreationTarget(config) === "database" && supportsCreateDatabaseLocale(config?.db_type);
|
||||
});
|
||||
|
||||
const canDropDatabase = computed(() => {
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ describe("useSidebarTableMutationRuntime SAP HANA schema-scoped actions", () =>
|
||||
});
|
||||
|
||||
it("omits the GBase 8s metadata owner from generated table SQL", async () => {
|
||||
const { feature } = runtime("tenant", { db_type: "gbase", driver_profile: "gbase8s" }, "informix");
|
||||
const { feature } = runtime("tenant", { db_type: "gbase8s", driver_profile: "gbase8s" }, "informix");
|
||||
|
||||
await feature.confirmDropTable();
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ describe("buildConnectionUrlCopy JDBC URL", () => {
|
||||
it("selects dialects by driver profile", () => {
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "gaussdb", driver_profile: "gaussdb-m" }), "jdbcUrl")).toBe("jdbc:gaussdb://db.example.com:5432/appdb");
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "gaussdb" }), "jdbcUrl")).toBe("jdbc:postgresql://db.example.com:5432/appdb");
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "gbase", port: 9088, driver_profile: "gbase8s" }), "jdbcUrl")).toBe("jdbc:gbasedbt-sqli://db.example.com:9088/appdb");
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "gbase8s", port: 9088, driver_profile: "gbase8s" }), "jdbcUrl")).toBe("jdbc:gbasedbt-sqli://db.example.com:9088/appdb");
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "gbase", port: 5258, driver_profile: "gbase8a" }), "jdbcUrl")).toBeNull();
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "tdengine", port: 6041 }), "jdbcUrl")).toBe("jdbc:TAOS-RS://db.example.com:6041/appdb");
|
||||
expect(buildConnectionUrlCopy(config({ db_type: "tdengine", port: 6030 }), "jdbcUrl")).toBe("jdbc:TAOS://db.example.com:6030/appdb");
|
||||
|
||||
@@ -28,7 +28,7 @@ function selectedProfileHarness(selectedTypeValue: string, driverProfile: string
|
||||
gbase8c: { label: "南大通用 GBase 8c" },
|
||||
},
|
||||
{ value: selectedTypeValue },
|
||||
{ value: { db_type: driverProfile === "gbase8c" ? "gbase8c" : "gbase", driver_profile: driverProfile } },
|
||||
{ value: { db_type: driverProfile === "gbase8c" ? "gbase8c" : driverProfile === "gbase8s" ? "gbase8s" : "gbase", driver_profile: driverProfile } },
|
||||
() => null,
|
||||
{ value: null },
|
||||
gbaseProfileFromConfig,
|
||||
|
||||
@@ -11,6 +11,9 @@ describe("gbaseProfiles", () => {
|
||||
it("resolves the toggle profile from a saved connection", () => {
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase8c", driver_profile: "gbase8c" })).toBe("gbase8c");
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase8c" })).toBe("gbase8c");
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase8s", driver_profile: "gbase8s" })).toBe("gbase8s");
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase8s" })).toBe("gbase8s");
|
||||
// A not-yet-migrated in-memory config still resolves to the 8s toggle.
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase", driver_profile: "gbase8s" })).toBe("gbase8s");
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase", driver_profile: "gbase8a" })).toBe("gbase8a");
|
||||
expect(gbaseProfileFromConfig({ db_type: "gbase", driver_profile: "gbase" })).toBe("gbase8a");
|
||||
|
||||
@@ -3,19 +3,19 @@ import { supportsCreateDatabaseCharset, supportsCreateDatabaseLocale } from "@/l
|
||||
import { DEFAULT_GBASE8S_DATABASE_LOCALE, defaultGbase8sDatabaseLocale, GBASE8S_DATABASE_LOCALES } from "@/lib/database/createDatabaseCharsetOptions";
|
||||
|
||||
describe("GBase 8s / Informix create-database locale support", () => {
|
||||
it("offers a locale picker only for the gbase8s profile", () => {
|
||||
expect(supportsCreateDatabaseLocale("gbase", "gbase8s")).toBe(true);
|
||||
it("offers a locale picker only for the gbase8s type", () => {
|
||||
expect(supportsCreateDatabaseLocale("gbase8s")).toBe(true);
|
||||
// Plain Informix's agent has no directive handling, so the locale picker must stay hidden
|
||||
// there until InformixAgent implements it; showing it would silently ignore the choice.
|
||||
expect(supportsCreateDatabaseLocale("informix", undefined)).toBe(false);
|
||||
expect(supportsCreateDatabaseLocale("informix")).toBe(false);
|
||||
});
|
||||
|
||||
it("does not treat GBase 8a or MySQL as locale-picking", () => {
|
||||
expect(supportsCreateDatabaseLocale("gbase", "gbase8a")).toBe(false);
|
||||
expect(supportsCreateDatabaseLocale("mysql", "mysql")).toBe(false);
|
||||
expect(supportsCreateDatabaseLocale("gbase")).toBe(false);
|
||||
expect(supportsCreateDatabaseLocale("mysql")).toBe(false);
|
||||
// The MySQL charset path stays independent of the Informix locale path.
|
||||
expect(supportsCreateDatabaseCharset("mysql", "mysql")).toBe(true);
|
||||
expect(supportsCreateDatabaseCharset("gbase", "gbase8s")).toBe(false);
|
||||
expect(supportsCreateDatabaseCharset("gbase8s", "gbase8s")).toBe(false);
|
||||
});
|
||||
|
||||
it("defaults to a Chinese-capable UTF-8 locale", () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ const databaseNode = { type: "database" as const, database: "dcss" };
|
||||
|
||||
describe("database namespace creation targets", () => {
|
||||
it("offers Create Database (not Create Schema) for GBase 8s", () => {
|
||||
const gbase8s = { db_type: "gbase", driver_profile: "gbase8s" } as Conn;
|
||||
const gbase8s = { db_type: "gbase8s", driver_profile: "gbase8s" } as Conn;
|
||||
|
||||
expect(connectionNamespaceCreationTarget(gbase8s)).toBe("database");
|
||||
expect(databaseNodeNamespaceCreationTarget(gbase8s, databaseNode)).toBeNull();
|
||||
|
||||
@@ -159,7 +159,7 @@ describe("jdbc dialect inference", () => {
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "kingbase" })).toBe(true);
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "kyuubi" })).toBe(true);
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "gaussdb" })).toBe(true);
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "gbase", driver_profile: "gbase8s" })).toBe(true);
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "gbase8s", driver_profile: "gbase8s" })).toBe(true);
|
||||
expect(connectionShouldLoadIdentifierQuote({ db_type: "gbase", driver_profile: "gbase8a" })).toBe(false);
|
||||
// Cloud Spanner reports a backtick for GoogleSQL and a double quote for the
|
||||
// PostgreSQL dialect; both are only known from the connection.
|
||||
@@ -175,12 +175,12 @@ describe("jdbc dialect inference", () => {
|
||||
});
|
||||
|
||||
it("falls back to a flat table tree when GBase 8s reports no schemas", () => {
|
||||
expect(connectionShouldDiscoverJdbcSchemas({ db_type: "gbase", driver_profile: "gbase8s" })).toBe(true);
|
||||
expect(connectionShouldDiscoverJdbcSchemas({ db_type: "gbase8s", driver_profile: "gbase8s" })).toBe(true);
|
||||
expect(connectionShouldDiscoverJdbcSchemas({ db_type: "gbase", driver_profile: "gbase8a" })).toBe(false);
|
||||
});
|
||||
|
||||
it("omits the metadata owner from GBase 8s table SQL", () => {
|
||||
expect(connectionTableSqlSchema({ db_type: "gbase", driver_profile: "gbase8s" }, "gbasedbt")).toBeUndefined();
|
||||
expect(connectionTableSqlSchema({ db_type: "gbase8s", driver_profile: "gbase8s" }, "gbasedbt")).toBeUndefined();
|
||||
expect(connectionTableSqlSchema({ db_type: "gbase", driver_profile: "gbase8a" }, "analytics")).toBe("analytics");
|
||||
expect(connectionTableSqlSchema({ db_type: "informix", driver_profile: "informix" }, "informix")).toBe("informix");
|
||||
});
|
||||
@@ -485,7 +485,7 @@ describe("object tree node schema", () => {
|
||||
|
||||
it.each([
|
||||
{ db_type: "jdbc" as const, connection_string: "jdbc:informix-sqli://localhost:9088/prulife" },
|
||||
{ db_type: "gbase" as const, driver_profile: "gbase8s" },
|
||||
{ db_type: "gbase8s" as const, driver_profile: "gbase8s" },
|
||||
])("keeps compatible Informix metadata on the login owner", (connection) => {
|
||||
expect(connectionObjectTreeQuerySchema(connection, "prulife")).toBe("");
|
||||
expect(connectionObjectTreeNodeSchema(connection, "prulife")).toBeUndefined();
|
||||
|
||||
@@ -17,7 +17,7 @@ describe("sidebar database storage", () => {
|
||||
}
|
||||
expect(supportsSidebarTableStorage(config("jdbc"))).toBe(false);
|
||||
expect(supportsSidebarTableStorage({ ...config("postgres"), driver_profile: "cockroachdb" })).toBe(false);
|
||||
expect(supportsSidebarTableStorage({ ...config("gbase"), driver_profile: "gbase8s" })).toBe(false);
|
||||
expect(supportsSidebarTableStorage({ ...config("gbase8s"), driver_profile: "gbase8s" })).toBe(false);
|
||||
});
|
||||
|
||||
it("requests and applies only visible database nodes", () => {
|
||||
|
||||
@@ -50,7 +50,7 @@ export function agentDriverInstallKey(dbType: DatabaseType | undefined, driverPr
|
||||
}
|
||||
if (dbType === "mongodb") return "mongodb";
|
||||
if (dbType === "dameng") return "dameng";
|
||||
if (dbType === "gbase") return driverProfile === "gbase8s" ? "gbase8s" : "gbase8a";
|
||||
if (dbType === "gbase") return "gbase8a";
|
||||
if (dbType === "mq") {
|
||||
if (driverProfile === "kafka") return "kafka";
|
||||
if (driverProfile === "rocketmq") return "rocketmq";
|
||||
|
||||
@@ -24,6 +24,7 @@ const DRIVER_STARTUP_FLOOR_TYPES = new Set<DatabaseType>([
|
||||
"exasol",
|
||||
"oceanbase-oracle",
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
"access",
|
||||
"oracle",
|
||||
"h2",
|
||||
|
||||
@@ -74,7 +74,7 @@ const SCHEME_PROFILES: Record<string, ConnectionProfile> = {
|
||||
gaussdb: { type: "gaussdb", profile: "gaussdb", label: "GaussDB", defaultPort: 5432 },
|
||||
kwdb: { type: "kwdb", profile: "kwdb", label: "KWDB", defaultPort: 26257 },
|
||||
gbase: { type: "gbase", profile: "gbase", label: "南大通用 GBase", defaultPort: 5258 },
|
||||
"gbasedbt-sqli": { type: "gbase", profile: "gbase8s", label: "南大通用 GBase 8s", defaultPort: 9088 },
|
||||
"gbasedbt-sqli": { type: "gbase8s", profile: "gbase8s", label: "南大通用 GBase 8s", defaultPort: 9088 },
|
||||
"informix-sqli": { type: "informix", profile: "informix", label: "Informix", defaultPort: 9088 },
|
||||
yashandb: { type: "yashandb", profile: "yashandb", label: "YashanDB", defaultPort: 1688 },
|
||||
opengauss: { type: "gaussdb", profile: "opengauss", label: "openGauss", defaultPort: 5432 },
|
||||
@@ -599,7 +599,7 @@ function parseJdbcGbase8sUrl(source: string): ParsedConnectionUrl | null {
|
||||
const host = match.groups.host.replace(/^\[/, "").replace(/\]$/, "");
|
||||
|
||||
return {
|
||||
dbType: "gbase",
|
||||
dbType: "gbase8s",
|
||||
driverProfile: "gbase8s",
|
||||
driverLabel: "南大通用 GBase 8s",
|
||||
host,
|
||||
|
||||
@@ -301,8 +301,10 @@ function jdbcPrefixFor(config: ConnectionUrlCopyConfig): string | null {
|
||||
return profile === GAUSSDB_M_JDBC_DRIVER_PROFILE ? "jdbc:gaussdb" : "jdbc:postgresql";
|
||||
case "iris":
|
||||
return profile === "cache" ? "jdbc:Cache" : "jdbc:IRIS";
|
||||
case "gbase8s":
|
||||
return "jdbc:gbasedbt-sqli";
|
||||
case "gbase":
|
||||
return profile === "gbase8s" ? "jdbc:gbasedbt-sqli" : null;
|
||||
return null;
|
||||
case "tdengine":
|
||||
return Number(config.port) === 6030 ? "jdbc:TAOS" : "jdbc:TAOS-RS";
|
||||
default:
|
||||
@@ -426,6 +428,7 @@ function buildSpecialJdbcUrl(config: ConnectionUrlCopyConfig, database: string,
|
||||
return `jdbc:inceptor2://${formatHostForUrl(host)}${portPart}/${database || "default"}${props ? `;${props}` : ""}${ssl}`;
|
||||
}
|
||||
case "informix":
|
||||
case "gbase8s":
|
||||
case "gbase": {
|
||||
const prefix = config.db_type === "informix" ? "jdbc:informix-sqli" : jdbcPrefixFor(config);
|
||||
if (!prefix) return config.db_type === "gbase" ? null : undefined;
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { ConnectionConfig } from "@/types/database";
|
||||
|
||||
/**
|
||||
* GBase 8a / 8s / 8c share one picker entry and one version toggle, but they are
|
||||
* different engines: 8a and 8s are profiles of the `gbase` db type, while 8c is
|
||||
* PostgreSQL-shaped and keeps its own `gbase8c` db type (the same split as
|
||||
* OceanBase MySQL/Oracle mode). The toggle therefore switches `db_type` too.
|
||||
* different engines with different SQL dialects: 8a is the MySQL-based `gbase` type,
|
||||
* 8s is Informix-derived (`gbase8s`) and 8c is PostgreSQL-shaped (`gbase8c`), the same
|
||||
* split as OceanBase MySQL/Oracle mode. The toggle therefore switches `db_type` too.
|
||||
*/
|
||||
export const GBASE_PROFILES = ["gbase8a", "gbase8s", "gbase8c"] as const;
|
||||
|
||||
@@ -16,5 +16,6 @@ export function isGbaseProfile(value: string | undefined | null): value is Gbase
|
||||
|
||||
export function gbaseProfileFromConfig(config: Pick<ConnectionConfig, "db_type" | "driver_profile">): GbaseProfile {
|
||||
if (config.db_type === "gbase8c" || config.driver_profile === "gbase8c") return "gbase8c";
|
||||
return config.driver_profile === "gbase8s" ? "gbase8s" : "gbase8a";
|
||||
if (config.db_type === "gbase8s" || config.driver_profile === "gbase8s") return "gbase8s";
|
||||
return "gbase8a";
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ export function supportsCreateDatabaseCharset(databaseType?: DatabaseType, drive
|
||||
// no charset clause in the SQL. The create dialog still offers a locale picker, and the chosen
|
||||
// value is applied by the gbase8s agent opening the CREATE DATABASE session with that DB_LOCALE.
|
||||
// Plain Informix is excluded until InformixAgent learns to honor the locale directive.
|
||||
export function supportsCreateDatabaseLocale(databaseType?: DatabaseType, driverProfile?: string | null): boolean {
|
||||
return databaseType === "gbase" && driverProfile === "gbase8s";
|
||||
export function supportsCreateDatabaseLocale(databaseType?: DatabaseType): boolean {
|
||||
return databaseType === "gbase8s";
|
||||
}
|
||||
|
||||
export function buildCreateDatabaseSql(options: CreateDatabaseSqlOptions): Promise<string> {
|
||||
|
||||
@@ -6,9 +6,7 @@ import { isSqlServerLinkedNode } from "@/lib/database/sqlServerLinkedServers";
|
||||
import type { ConnectionConfig, TreeNode } from "@/types/database";
|
||||
|
||||
export function canDropDatabaseNode(node: TreeNode, connection?: ConnectionConfig): boolean {
|
||||
return (
|
||||
node.type === "database" && !!node.connectionId && !!node.database && !node.catalog && !isSqlServerLinkedNode(node) && !connectionIsEffectivelyReadOnly(connection) && (supportsDatabaseCreation(connection?.db_type) || supportsCreateDatabaseLocale(connection?.db_type, connection?.driver_profile))
|
||||
);
|
||||
return node.type === "database" && !!node.connectionId && !!node.database && !node.catalog && !isSqlServerLinkedNode(node) && !connectionIsEffectivelyReadOnly(connection) && (supportsDatabaseCreation(connection?.db_type) || supportsCreateDatabaseLocale(connection?.db_type));
|
||||
}
|
||||
|
||||
export interface DatabaseBrowserMutation {
|
||||
|
||||
@@ -64,6 +64,8 @@ export const DATABASE_NAMESPACE_CREATION_MATRIX = {
|
||||
"oceanbase-oracle": { deferred: "Oracle-mode schemas are users; use a dedicated user workflow" },
|
||||
questdb: { deferred: "single database model in DBX" },
|
||||
gbase: { database: "schema" },
|
||||
// Informix-derived: no `CREATE SCHEMA <name>` (a schema is the table owner), but `CREATE DATABASE` works.
|
||||
gbase8s: { connection: "database" },
|
||||
// GBase 8c creates databases with an explicit DBCOMPATIBILITY mode that is fixed at
|
||||
// creation time, so the generic PostgreSQL-shaped create path cannot pick a correct mode.
|
||||
gbase8c: { deferred: "database creation needs an explicit DBCOMPATIBILITY mode fixed at creation time" },
|
||||
@@ -108,13 +110,6 @@ export const DATABASE_NAMESPACE_CREATION_MATRIX = {
|
||||
} satisfies Record<DatabaseType, DatabaseNamespaceCreationMatrixEntry>;
|
||||
|
||||
function namespaceCreationMatrixEntry(connection: NonNullable<CreationConnection>): DatabaseNamespaceCreationMatrixEntry {
|
||||
// GBase 8s shares `db_type: "gbase"` with the MySQL-based GBase 8a but is Informix-derived:
|
||||
// it has no `CREATE SCHEMA <name>` (a schema is the table owner) yet does support
|
||||
// `CREATE DATABASE`. Route it to the Informix-family semantics instead of the shared `gbase`
|
||||
// entry, which is written for GBase 8a.
|
||||
if (connection.db_type === "gbase" && connection.driver_profile === "gbase8s") {
|
||||
return { connection: "database" };
|
||||
}
|
||||
return DATABASE_NAMESPACE_CREATION_MATRIX[connection.db_type];
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ export const DATABASE_PROPERTY_EDITING_MATRIX = {
|
||||
"oceanbase-oracle": { deferred: "Oracle-mode schemas are users; use a dedicated user workflow" },
|
||||
questdb: { deferred: "single database model in DBX" },
|
||||
gbase: { deferred: "schema properties need product-specific handling" },
|
||||
gbase8s: { deferred: "schema properties need product-specific handling" },
|
||||
gbase8c: { deferred: "database and schema comment editing not verified for first pass" },
|
||||
access: { deferred: "file-backed database properties are not edited in-place" },
|
||||
h2: { deferred: "schema properties need product-specific handling" },
|
||||
|
||||
@@ -78,6 +78,7 @@ const NAVICAT_STYLE_TABLE_DATA_TYPES = new Set<DatabaseType>([
|
||||
"questdb",
|
||||
"oceanbase-oracle",
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
"access",
|
||||
"h2",
|
||||
"snowflake",
|
||||
|
||||
@@ -92,7 +92,7 @@ export function jdbcConnectionUsesDriverRowOffset(connection: JdbcDialectConnect
|
||||
|
||||
export function effectiveDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
||||
if (!connection) return undefined;
|
||||
if (connection.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return "informix";
|
||||
if (connection.db_type === "gbase8s") return "informix";
|
||||
if (connection.db_type === "gbase") return "mysql";
|
||||
// MySQL-protocol connections to Doris/StarRocks (db_type=mysql with a
|
||||
// starrocks/doris driver_profile) must use the Doris/StarRocks SQL dialect so
|
||||
@@ -123,7 +123,7 @@ export function transferDatabaseTypeForConnection(connection?: JdbcDialectConnec
|
||||
// the GBase driver manifest explicitly disables data transfer. Keep that
|
||||
// capability restriction attached to the raw connection type instead of
|
||||
// exposing the mapped MySQL type to transfer callers.
|
||||
if (connection?.db_type === "gbase") return undefined;
|
||||
if (connection?.db_type === "gbase" || connection?.db_type === "gbase8s") return undefined;
|
||||
const effective = effectiveDatabaseTypeForConnection(connection);
|
||||
if (effective === "doris" || effective === "starrocks") return connection?.db_type;
|
||||
return effective;
|
||||
@@ -136,7 +136,7 @@ export function connectionUsesConnectionRootSchemaMode(connection?: JdbcDialectC
|
||||
|
||||
export function connectionShouldLoadIdentifierQuote(connection: JdbcDialectConnection | undefined): boolean {
|
||||
if (!connection) return false;
|
||||
if (connection.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return true;
|
||||
if (connection.db_type === "gbase8s") return true;
|
||||
if (connection.db_type === "kingbase") return true;
|
||||
if (connection.db_type === "kyuubi") return true;
|
||||
// Cloud Spanner is dual-dialect: the agent reports a backtick for GoogleSQL and
|
||||
@@ -233,7 +233,7 @@ export function sqlSnippetDatabaseTypeForConnection(connection?: JdbcDialectConn
|
||||
|
||||
export function tableStructureDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
||||
if (!connection) return undefined;
|
||||
if (connection.db_type === "gbase" && !isGbase8sProfile(connection.driver_profile)) return "gbase";
|
||||
if (connection.db_type === "gbase") return "gbase";
|
||||
return effectiveDatabaseTypeForConnection(connection);
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ export function connectionUsesDatabaseObjectTreeMode(connection?: JdbcDialectCon
|
||||
export function connectionShouldDiscoverJdbcSchemas(connection?: JdbcDialectConnection): boolean {
|
||||
// GBase 8s exposes owner schemas only when the current database can use them
|
||||
// in DML; non-ANSI databases fall back to the flat table tree.
|
||||
if (connection?.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return true;
|
||||
if (connection?.db_type === "gbase8s") return true;
|
||||
return connection?.db_type === "jdbc" && !inferJdbcDialect(connection);
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection
|
||||
|
||||
/** GBase 8s reports the table owner as a schema, but does not accept it in table DML/DDL names. */
|
||||
export function connectionTableSqlSchema(connection: JdbcDialectConnection | undefined, schema?: string): string | undefined {
|
||||
if (connection?.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return undefined;
|
||||
if (connection?.db_type === "gbase8s") return undefined;
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ const SCHEMA_FIRST_QUALIFIER_TYPES = new Set<DatabaseType>([
|
||||
"xugu",
|
||||
"oscar",
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
"saphana",
|
||||
"sqlserver",
|
||||
"snowflake",
|
||||
|
||||
@@ -238,7 +238,7 @@ export function filterDatabaseNamesForConnection(databaseNames: string[], connec
|
||||
}
|
||||
|
||||
export function filterDatabaseNamesForVisiblePicker(databaseNames: string[], connection: VisibleDatabaseConnection | undefined): string[] {
|
||||
if (connection?.db_type === "gbase" && connection.driver_profile === "gbase8s") {
|
||||
if (connection?.db_type === "gbase8s") {
|
||||
return databaseNames;
|
||||
}
|
||||
return databaseNames.filter((name) => !isSystemDatabaseName(effectiveDatabaseTypeForConnection(connection), name));
|
||||
|
||||
@@ -8,7 +8,6 @@ export function supportsSidebarDatabaseStorage(connection: ConnectionConfig | un
|
||||
|
||||
export function supportsSidebarTableStorage(connection: ConnectionConfig | undefined): boolean {
|
||||
if (!connection || !sidebarTableStorageTypes.has(connection.db_type)) return false;
|
||||
if (connection.db_type === "gbase" && connection.driver_profile === "gbase8s") return false;
|
||||
return connection.db_type !== "postgres" || connection.driver_profile !== "cockroachdb";
|
||||
}
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ function gbase8sConnection(): ConnectionConfig {
|
||||
...informixConnection(),
|
||||
id: "gbase8s-1",
|
||||
name: "GBase 8s",
|
||||
db_type: "gbase",
|
||||
db_type: "gbase8s",
|
||||
driver_profile: "gbase8s",
|
||||
database: "dbx_test",
|
||||
} as ConnectionConfig;
|
||||
|
||||
@@ -64,7 +64,7 @@ export const CONNECTION_PROFILES = {
|
||||
exasol: { type: "exasol", port: 8563, user: "sys", label: "Exasol", icon: "exasol" },
|
||||
gbase: { type: "gbase", port: 5258, user: "gbasedbt", label: "南大通用 GBase 8a", icon: "gbase" },
|
||||
gbase8a: { type: "gbase", port: 5258, user: "gbasedbt", label: "南大通用 GBase 8a", icon: "gbase" },
|
||||
gbase8s: { type: "gbase", port: 9088, user: "gbasedbt", label: "南大通用 GBase 8s", icon: "gbase" },
|
||||
gbase8s: { type: "gbase8s", port: 9088, user: "gbasedbt", label: "南大通用 GBase 8s", icon: "gbase" },
|
||||
gbase8c: { type: "gbase8c", port: 5432, user: "gbase", label: "南大通用 GBase 8c", icon: "gbase" },
|
||||
opengauss: { type: "opengauss", port: 5432, user: "gaussdb", label: "openGauss", icon: "opengauss" },
|
||||
gaussdb: { type: "gaussdb", port: 5432, user: "gaussdb", label: "GaussDB", icon: "gaussdb" },
|
||||
|
||||
@@ -47,6 +47,7 @@ export const DATABASE_TYPES = [
|
||||
"questdb",
|
||||
"oceanbase-oracle",
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
"gbase8c",
|
||||
"access",
|
||||
"h2",
|
||||
|
||||
@@ -1785,13 +1785,6 @@
|
||||
"driverStoreVisible": true,
|
||||
"driverStoreOrder": 15,
|
||||
"driverProfiles": [
|
||||
{
|
||||
"profile": "gbase8s",
|
||||
"agentKey": "gbase8s",
|
||||
"label": "南大通用 GBase 8s",
|
||||
"storeVisible": true,
|
||||
"storeOrder": 16
|
||||
},
|
||||
{
|
||||
"profile": "gbase8a",
|
||||
"agentKey": "gbase8a",
|
||||
@@ -1828,6 +1821,47 @@
|
||||
"driverManagement": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"dbType": "gbase8s",
|
||||
"compareModes": [
|
||||
"schema",
|
||||
"data"
|
||||
],
|
||||
"label": "南大通用 GBase 8s",
|
||||
"dialect": "Informix",
|
||||
"runtimeMode": "agent",
|
||||
"mcpMode": "bridge",
|
||||
"agentKey": "gbase8s",
|
||||
"driverStoreVisible": true,
|
||||
"driverStoreOrder": 16,
|
||||
"singleConnectionPool": false,
|
||||
"metadataConnectionScoped": false,
|
||||
"skipTcpProbe": true,
|
||||
"defaultPort": 9088,
|
||||
"traits": {
|
||||
"schemaAware": true,
|
||||
"treeSchema": true
|
||||
},
|
||||
"supportLevel": "operate",
|
||||
"capabilities": {
|
||||
"queryExecution": true,
|
||||
"metadataBrowse": true,
|
||||
"objectBrowser": true,
|
||||
"objectSource": false,
|
||||
"schemaSearch": true,
|
||||
"diagram": true,
|
||||
"tableDataEdit": true,
|
||||
"tableStructureEdit": true,
|
||||
"tableImport": false,
|
||||
"dataTransfer": false,
|
||||
"sqlFileExecution": true,
|
||||
"databaseCreate": false,
|
||||
"fieldLineage": false,
|
||||
"sqlExplain": false,
|
||||
"userAdmin": false,
|
||||
"driverManagement": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"dbType": "gbase8c",
|
||||
"compareModes": [
|
||||
|
||||
@@ -332,6 +332,7 @@ macro_rules! agent_connection_pool_database_type {
|
||||
| DatabaseType::Exasol
|
||||
| DatabaseType::OceanbaseOracle
|
||||
| DatabaseType::Gbase
|
||||
| DatabaseType::Gbase8s
|
||||
| DatabaseType::Oracle
|
||||
| DatabaseType::H2
|
||||
| DatabaseType::Snowflake
|
||||
|
||||
@@ -373,16 +373,10 @@ struct TableExportSqlContext<'a> {
|
||||
schema: Option<&'a str>,
|
||||
}
|
||||
|
||||
fn table_export_sql_context<'a>(
|
||||
database_type: DatabaseType,
|
||||
driver_profile: Option<&str>,
|
||||
schema: Option<&'a str>,
|
||||
) -> TableExportSqlContext<'a> {
|
||||
fn table_export_sql_context<'a>(database_type: DatabaseType, schema: Option<&'a str>) -> TableExportSqlContext<'a> {
|
||||
// GBase8s selects from the active database and rejects the owner-qualified
|
||||
// names produced for MySQL-family GBase connections.
|
||||
if database_type == DatabaseType::Gbase
|
||||
&& driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s"))
|
||||
{
|
||||
// names produced for other connection types.
|
||||
if database_type == DatabaseType::Gbase8s {
|
||||
TableExportSqlContext { database_type: DatabaseType::Informix, schema: None }
|
||||
} else {
|
||||
TableExportSqlContext { database_type, schema }
|
||||
@@ -1612,15 +1606,15 @@ async fn export_table_data_core_inner(
|
||||
cancelled: Arc<AtomicBool>,
|
||||
cancel_token: CancellationToken,
|
||||
) -> Result<(), String> {
|
||||
// 1. Get database type and the profile that can refine its SQL dialect.
|
||||
let (db_type, driver_profile) = {
|
||||
// 1. Get database type
|
||||
let db_type = {
|
||||
let configs = state.configs.read().await;
|
||||
let config = configs
|
||||
.get(&request.connection_id)
|
||||
.ok_or_else(|| format!("Connection config not found: {}", request.connection_id))?;
|
||||
(config.db_type, config.driver_profile.clone())
|
||||
config.db_type
|
||||
};
|
||||
let sql_context = table_export_sql_context(db_type, driver_profile.as_deref(), request.schema.as_deref());
|
||||
let sql_context = table_export_sql_context(db_type, request.schema.as_deref());
|
||||
|
||||
// 2. Get pool
|
||||
let client_session_id = table_export_client_session_id(&request.export_id);
|
||||
@@ -3059,7 +3053,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, request.schema.as_deref());
|
||||
let columns = vec!["Time".to_string(), "root.test.device2.temperature".to_string()];
|
||||
let query_columns = table_export_query_columns(&request, &context, &columns).unwrap();
|
||||
assert_eq!(query_columns.as_ref(), &["temperature".to_string()]);
|
||||
@@ -3127,7 +3121,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, request.schema.as_deref());
|
||||
let columns = vec!["tImE".to_string(), "temperature".to_string()];
|
||||
let query_columns = table_export_query_columns(&request, &context, &columns).unwrap();
|
||||
|
||||
@@ -3171,7 +3165,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, request.schema.as_deref());
|
||||
let error = table_export_query_columns(&request, &context, &["TIME".to_string()]).unwrap_err();
|
||||
assert_eq!(error, "IoTDB table export requires at least one non-Time column");
|
||||
}
|
||||
@@ -3210,7 +3204,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Iotdb, request.schema.as_deref());
|
||||
let columns = vec![
|
||||
"Time".to_string(),
|
||||
"root.test.device2.temperature".to_string(),
|
||||
@@ -3271,7 +3265,7 @@ mod tests {
|
||||
"SELECT \"Time\", \"value\" FROM \"samples\" LIMIT 25 OFFSET 10",
|
||||
),
|
||||
] {
|
||||
let context = table_export_sql_context(database_type, None, None);
|
||||
let context = table_export_sql_context(database_type, None);
|
||||
let query_columns = table_export_query_columns(&request, &context, &columns).unwrap();
|
||||
assert!(matches!(query_columns, Cow::Borrowed(_)));
|
||||
assert_eq!(table_cursor_sql(&request, &context, query_columns.as_ref(), &[], &[]), cursor_sql);
|
||||
@@ -3316,7 +3310,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Oracle, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Oracle, request.schema.as_deref());
|
||||
|
||||
let sql = table_cursor_sql(
|
||||
&request,
|
||||
@@ -3371,7 +3365,7 @@ mod tests {
|
||||
};
|
||||
let columns = vec!["id".to_string(), "payload".to_string()];
|
||||
let primary_keys = vec!["id".to_string()];
|
||||
let context = table_export_sql_context(DatabaseType::Gbase, Some("gbase8s"), request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Gbase8s, request.schema.as_deref());
|
||||
|
||||
assert_eq!(context.database_type, DatabaseType::Informix);
|
||||
assert_eq!(context.schema, None);
|
||||
@@ -3399,7 +3393,7 @@ mod tests {
|
||||
"SELECT COUNT(*) FROM orders"
|
||||
);
|
||||
|
||||
let regular_gbase = table_export_sql_context(DatabaseType::Gbase, Some("gbase8a"), request.schema.as_deref());
|
||||
let regular_gbase = table_export_sql_context(DatabaseType::Gbase, request.schema.as_deref());
|
||||
assert_eq!(regular_gbase.database_type, DatabaseType::Gbase);
|
||||
assert_eq!(regular_gbase.schema, Some("gbasedbt"));
|
||||
assert_eq!(
|
||||
@@ -3407,7 +3401,7 @@ mod tests {
|
||||
"SELECT \"id\", \"payload\" FROM \"gbasedbt\".\"orders\" ORDER BY \"id\" LIMIT 50 OFFSET 100"
|
||||
);
|
||||
|
||||
let informix = table_export_sql_context(DatabaseType::Informix, None, request.schema.as_deref());
|
||||
let informix = table_export_sql_context(DatabaseType::Informix, request.schema.as_deref());
|
||||
assert_eq!(informix.database_type, DatabaseType::Informix);
|
||||
assert_eq!(informix.schema, Some("gbasedbt"));
|
||||
assert_eq!(
|
||||
@@ -3452,7 +3446,7 @@ mod tests {
|
||||
};
|
||||
let columns = vec!["id".to_string(), "DisplayName".to_string()];
|
||||
let primary_keys = vec!["id".to_string()];
|
||||
let context = table_export_sql_context(DatabaseType::Gaussdb, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Gaussdb, request.schema.as_deref());
|
||||
|
||||
assert_eq!(
|
||||
table_cursor_sql(&request, &context, &columns, &[], &primary_keys),
|
||||
@@ -3516,7 +3510,7 @@ mod tests {
|
||||
let columns = vec!["id".to_string(), "geom".to_string(), "name".to_string()];
|
||||
let column_types = vec![Some("int".to_string()), Some("geometry".to_string()), Some("varchar".to_string())];
|
||||
let primary_keys = vec!["id".to_string()];
|
||||
let context = table_export_sql_context(DatabaseType::Mysql, None, None);
|
||||
let context = table_export_sql_context(DatabaseType::Mysql, None);
|
||||
|
||||
let cursor_sql = table_cursor_sql(&request, &context, &columns, &column_types, &primary_keys);
|
||||
assert!(cursor_sql.contains("ST_SRID(`geom`), ':', HEX(ST_AsWKB(`geom`))"));
|
||||
@@ -3578,7 +3572,7 @@ mod tests {
|
||||
split_max_mb: None,
|
||||
omit_database_qualifier: None,
|
||||
};
|
||||
let context = table_export_sql_context(DatabaseType::Oracle, None, request.schema.as_deref());
|
||||
let context = table_export_sql_context(DatabaseType::Oracle, request.schema.as_deref());
|
||||
let sql = table_cursor_sql(&request, &context, &columns, &[], &primary_keys);
|
||||
assert_eq!(sql, "SELECT \"ID\", \"NAME\" FROM \"APP\".\"USERS\"");
|
||||
|
||||
|
||||
@@ -534,6 +534,7 @@ fn schema_first_qualifier_type(db_type: &DatabaseType) -> bool {
|
||||
| DatabaseType::Xugu
|
||||
| DatabaseType::Oscar
|
||||
| DatabaseType::Gbase
|
||||
| DatabaseType::Gbase8s
|
||||
| DatabaseType::SapHana
|
||||
| DatabaseType::SqlServer
|
||||
| DatabaseType::Snowflake
|
||||
|
||||
@@ -7298,9 +7298,7 @@ async fn list_object_statistics_once(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if db_config.as_ref().is_some_and(|config| {
|
||||
config.db_type == DatabaseType::Gbase && config.driver_profile.as_deref() != Some("gbase8s")
|
||||
}) {
|
||||
if db_config.as_ref().is_some_and(|config| config.db_type == DatabaseType::Gbase) {
|
||||
let sql = gbase8a_object_statistics_sql(database);
|
||||
return agent_list_object_statistics(
|
||||
client,
|
||||
|
||||
@@ -113,6 +113,7 @@ fn maps_agent_database_types_to_driver_keys() {
|
||||
assert_eq!(agent_key(&DatabaseType::Exasol, None), Some("exasol"));
|
||||
assert_eq!(agent_key(&DatabaseType::OceanbaseOracle, None), Some("oceanbase-oracle"));
|
||||
assert_eq!(agent_key(&DatabaseType::Gbase, None), Some("gbase8a"));
|
||||
assert_eq!(agent_key(&DatabaseType::Gbase8s, None), Some("gbase8s"));
|
||||
assert_eq!(agent_key(&DatabaseType::Access, None), Some("access"));
|
||||
assert_eq!(agent_key(&DatabaseType::Oracle, None), Some("oracle"));
|
||||
assert_eq!(agent_key(&DatabaseType::Databend, None), Some("databend"));
|
||||
|
||||
@@ -551,7 +551,7 @@ mod tests {
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Oracle, Some("oracle-10g")), Some("oracle"));
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Oracle, Some("oracle-legacy")), Some("oracle"));
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Oracle, None), Some("oracle"));
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Gbase, Some("gbase8s")), Some("gbase8s"));
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Gbase8s, None), Some("gbase8s"));
|
||||
assert_eq!(AgentManager::db_type_to_agent_key(&DatabaseType::Gbase, None), Some("gbase8a"));
|
||||
manager.stop_daemon_by_key("oracle").await;
|
||||
manager.stop_daemon_by_key("gbase8s").await;
|
||||
|
||||
@@ -4803,6 +4803,7 @@ fn uses_keyless_row_predicate(database_type: Option<DatabaseType>) -> bool {
|
||||
| DatabaseType::Questdb
|
||||
| DatabaseType::OceanbaseOracle
|
||||
| DatabaseType::Gbase
|
||||
| DatabaseType::Gbase8s
|
||||
| DatabaseType::Access
|
||||
| DatabaseType::H2
|
||||
| DatabaseType::Snowflake
|
||||
|
||||
@@ -82,9 +82,9 @@ pub fn build_database_structure_search_sql(
|
||||
tdengine_structure_search_sql(&options.database, &tdengine_like_pattern_literal(&term), limit)
|
||||
}
|
||||
DatabaseType::Snowflake => snowflake_structure_search_sql(options.schema.as_deref(), &pattern_literal, limit),
|
||||
// gbase8s is Informix-compatible (see the table_structure_sql dialects)
|
||||
// and has no information_schema; only MySQL-dialect Gbase 8a takes this path.
|
||||
DatabaseType::Gbase if !driver_profile_is_gbase8s(options.driver_profile.as_deref()) => {
|
||||
// GBase 8s is its own `Gbase8s` type (Informix-compatible, no information_schema)
|
||||
// and falls through to the data search; only MySQL-dialect Gbase 8a takes this path.
|
||||
DatabaseType::Gbase => {
|
||||
gbase_structure_search_sql(&options.database, options.schema.as_deref(), &pattern_literal, limit)
|
||||
}
|
||||
DatabaseType::Jdbc if driver_profile_is_mysql(options.driver_profile.as_deref()) => {
|
||||
@@ -117,10 +117,6 @@ fn driver_profile_is_postgres(profile: Option<&str>) -> bool {
|
||||
profile.contains("postgres") || profile.contains("kingbase") || profile.contains("opengauss")
|
||||
}
|
||||
|
||||
fn driver_profile_is_gbase8s(profile: Option<&str>) -> bool {
|
||||
profile.map(str::trim).is_some_and(|profile| profile.to_ascii_lowercase().contains("gbase8s"))
|
||||
}
|
||||
|
||||
fn driver_profile_is_oracle(profile: Option<&str>) -> bool {
|
||||
let profile = profile.map(str::trim).unwrap_or("").to_ascii_lowercase();
|
||||
profile.contains("oracle") || profile.contains("dameng") || profile.contains("dm")
|
||||
@@ -596,15 +592,15 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
build_database_structure_search_sql(DatabaseStructureSearchSqlOptions {
|
||||
database_type: Some(DatabaseType::Gbase),
|
||||
driver_profile: Some("gbase8s".to_string()),
|
||||
database_type: Some(DatabaseType::Gbase8s),
|
||||
driver_profile: None,
|
||||
database: "app".to_string(),
|
||||
schema: None,
|
||||
term: "email".to_string(),
|
||||
limit: Some(10),
|
||||
}),
|
||||
None,
|
||||
"gbase8s is Informix-compatible and must fall back to data search"
|
||||
"GBase 8s is Informix-compatible and must fall back to data search"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
|
||||
| DatabaseType::OpenGauss
|
||||
| DatabaseType::OceanbaseOracle
|
||||
| DatabaseType::Gbase
|
||||
| DatabaseType::Gbase8s
|
||||
| DatabaseType::Databend
|
||||
| DatabaseType::Jdbc
|
||||
| DatabaseType::H2
|
||||
@@ -134,7 +135,7 @@ pub fn pagination_strategy(database_type: Option<DatabaseType>, context: Paginat
|
||||
Some(DatabaseType::Db2) => TablePaginationStrategy::Db2FetchFirst,
|
||||
Some(DatabaseType::SqlServer) => TablePaginationStrategy::SqlServerTop,
|
||||
Some(DatabaseType::Iris) => TablePaginationStrategy::IrisTop,
|
||||
Some(DatabaseType::Informix) => TablePaginationStrategy::InformixFirst,
|
||||
Some(DatabaseType::Informix | DatabaseType::Gbase8s) => TablePaginationStrategy::InformixFirst,
|
||||
Some(DatabaseType::Firebird) => TablePaginationStrategy::FirebirdRows,
|
||||
Some(DatabaseType::OceanbaseOracle) => TablePaginationStrategy::Rownum,
|
||||
Some(DatabaseType::Questdb) => TablePaginationStrategy::QuestDbLimit,
|
||||
|
||||
@@ -898,7 +898,7 @@ pub fn profile_for(db_type: DatabaseType) -> DdlDialectProfile {
|
||||
ClickHouse => clickhouse_profile(),
|
||||
|
||||
// Engines whose DROP TABLE grammar has no `IF EXISTS`.
|
||||
Db2 | Informix | SapHana | Teradata => {
|
||||
Db2 | Informix | Gbase8s | SapHana | Teradata => {
|
||||
let mut profile = conservative_ansi(db_type);
|
||||
profile.drop_table_supports_if_exists = false;
|
||||
profile
|
||||
|
||||
@@ -189,7 +189,7 @@ pub fn quote_table_identifier(database_type: Option<DatabaseType>, name: &str) -
|
||||
Some(db_type) if uses_backtick_identifier_quoting(Some(db_type)) => {
|
||||
format!("`{}`", name.replace('`', "``"))
|
||||
}
|
||||
Some(DatabaseType::Informix) if is_simple_informix_identifier(name) => name.to_string(),
|
||||
Some(DatabaseType::Informix | DatabaseType::Gbase8s) if is_simple_informix_identifier(name) => name.to_string(),
|
||||
// SUNDB folds unquoted identifiers to uppercase (its JDBC driver reports
|
||||
// storesUpperCaseIdentifiers=true / supportsMixedCaseIdentifiers=false), so
|
||||
// `select * from rs_salest_budget` resolves to RS_SALEST_BUDGET. Quoting the
|
||||
|
||||
@@ -201,8 +201,11 @@ pub fn table_data_schema<'a>(
|
||||
driver_profile: Option<&str>,
|
||||
schema: Option<&'a str>,
|
||||
) -> Option<&'a str> {
|
||||
if database_type == Some(DatabaseType::Informix)
|
||||
&& driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s"))
|
||||
// The frontend reports GBase 8s through the Informix dialect (see `connectionSqlDialect`), so
|
||||
// the owner-free rule applies to both the dialect-mapped and the native `gbase8s` type.
|
||||
if database_type == Some(DatabaseType::Gbase8s)
|
||||
|| (database_type == Some(DatabaseType::Informix)
|
||||
&& driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s")))
|
||||
{
|
||||
None
|
||||
} else {
|
||||
@@ -599,7 +602,7 @@ pub fn uses_connection_identifier_quote(database_type: Option<DatabaseType>, ide
|
||||
// Kyuubi normally uses Hive-family backticks, but a Trino-backed
|
||||
// session reports the ANSI double quote through connection info.
|
||||
|| (database_type == Some(DatabaseType::Kyuubi) && identifier_quote.is_some())
|
||||
|| (database_type == Some(DatabaseType::Informix) && identifier_quote.is_some())
|
||||
|| (matches!(database_type, Some(DatabaseType::Informix | DatabaseType::Gbase8s)) && identifier_quote.is_some())
|
||||
|| (matches!(database_type, Some(DatabaseType::Gaussdb | DatabaseType::OpenGauss | DatabaseType::Postgres))
|
||||
&& identifier_quote.is_some())
|
||||
}
|
||||
|
||||
@@ -298,12 +298,12 @@ fn build_create_database_statement(options: &CreateDatabaseSqlOptions) -> Result
|
||||
quote_table_identifier(options.database_type, &options.name)
|
||||
));
|
||||
}
|
||||
if is_informix_family(options.database_type, options.driver_profile.as_deref()) {
|
||||
if is_informix_family(options.database_type) {
|
||||
// Informix / GBase 8s accept only a bare `CREATE DATABASE <name>`. The new database
|
||||
// inherits the instance default locale, and the MySQL `CHARACTER SET`/`COLLATE`
|
||||
// clauses are invalid syntax here. Database names are ordinary identifiers, so quote
|
||||
// them with the Informix rule (unquoted for simple identifiers) rather than the
|
||||
// default double-quote path that `DatabaseType::Gbase` would otherwise take.
|
||||
// default double-quote path that other types would otherwise take.
|
||||
let name = quote_table_identifier(Some(DatabaseType::Informix), &options.name);
|
||||
let locale = clean_sql_option(options.charset.as_deref());
|
||||
if locale.is_empty() {
|
||||
@@ -325,23 +325,18 @@ fn build_create_database_statement(options: &CreateDatabaseSqlOptions) -> Result
|
||||
Ok(format!("CREATE DATABASE {name} CHARACTER SET {charset}{collate_clause};"))
|
||||
}
|
||||
|
||||
/// Whether the connection belongs to the Informix family: standalone Informix, or GBase 8s
|
||||
/// (which shares `DatabaseType::Gbase` with the MySQL-based GBase 8a and is only distinguishable
|
||||
/// through the `gbase8s` driver profile). Informix-family servers accept `CREATE DATABASE` but
|
||||
/// have no `CREATE SCHEMA <name>` statement (a "schema" is the table owner), so the create
|
||||
/// targets differ from the rest of the `Gbase` family.
|
||||
fn is_informix_family(database_type: Option<DatabaseType>, driver_profile: Option<&str>) -> bool {
|
||||
match database_type {
|
||||
Some(DatabaseType::Informix) => true,
|
||||
Some(DatabaseType::Gbase) => driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s")),
|
||||
_ => false,
|
||||
}
|
||||
/// Whether the connection belongs to the Informix family: standalone Informix, or GBase 8s.
|
||||
/// Informix-family servers accept `CREATE DATABASE` but have no `CREATE SCHEMA <name>`
|
||||
/// statement (a "schema" is the table owner), so the create targets differ from the MySQL-based
|
||||
/// GBase 8a (`DatabaseType::Gbase`).
|
||||
fn is_informix_family(database_type: Option<DatabaseType>) -> bool {
|
||||
matches!(database_type, Some(DatabaseType::Informix | DatabaseType::Gbase8s))
|
||||
}
|
||||
|
||||
pub fn supports_create_database_target(database_type: Option<DatabaseType>, driver_profile: Option<&str>) -> bool {
|
||||
// Informix / GBase 8s create namespaces with `CREATE DATABASE`, so they are valid targets
|
||||
// even though they are absent from the explicit list below.
|
||||
if is_informix_family(database_type, driver_profile) {
|
||||
if is_informix_family(database_type) {
|
||||
return true;
|
||||
}
|
||||
matches!(
|
||||
@@ -1559,12 +1554,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn builds_informix_family_create_database_without_mysql_clause() {
|
||||
// GBase 8s shares DatabaseType::Gbase with GBase 8a and is only identified by the
|
||||
// gbase8s driver profile; with no locale chosen it emits a bare, unquoted CREATE DATABASE.
|
||||
// With no locale chosen, GBase 8s emits a bare, unquoted CREATE DATABASE.
|
||||
assert_eq!(
|
||||
build_create_database_sql(CreateDatabaseSqlOptions {
|
||||
database_type: Some(DatabaseType::Gbase),
|
||||
driver_profile: Some("gbase8s".to_string()),
|
||||
database_type: Some(DatabaseType::Gbase8s),
|
||||
driver_profile: None,
|
||||
target: None,
|
||||
parent: None,
|
||||
name: "app_db".to_string(),
|
||||
@@ -1596,8 +1590,8 @@ mod tests {
|
||||
// because Informix cannot express a codeset in CREATE DATABASE.
|
||||
assert_eq!(
|
||||
build_create_database_sql(CreateDatabaseSqlOptions {
|
||||
database_type: Some(DatabaseType::Gbase),
|
||||
driver_profile: Some("gbase8s".to_string()),
|
||||
database_type: Some(DatabaseType::Gbase8s),
|
||||
driver_profile: None,
|
||||
target: None,
|
||||
parent: None,
|
||||
name: "app_db".to_string(),
|
||||
@@ -1637,8 +1631,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_database_target_distinguishes_gbase_profiles() {
|
||||
assert!(supports_create_database_target(Some(DatabaseType::Gbase), Some("gbase8s")));
|
||||
fn create_database_target_distinguishes_gbase_types() {
|
||||
assert!(supports_create_database_target(Some(DatabaseType::Gbase8s), None));
|
||||
assert!(!supports_create_database_target(Some(DatabaseType::Gbase), Some("gbase8a")));
|
||||
assert!(!supports_create_database_target(Some(DatabaseType::Gbase), None));
|
||||
assert!(supports_create_database_target(Some(DatabaseType::Informix), None));
|
||||
|
||||
@@ -147,11 +147,9 @@ pub(super) fn capabilities_for(
|
||||
comment: true,
|
||||
..base
|
||||
},
|
||||
// GBase 8s is Informix-compatible, unlike the rest of the `Gbase`
|
||||
// family (GBase 8a and friends) which speak MySQL wire protocol and
|
||||
// SQL dialect. `driver_profile` is the only signal that distinguishes
|
||||
// them (see the same check in `table_export.rs` / `table_select.rs`).
|
||||
Some(DatabaseType::Gbase) if driver_profile.is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s")) => {
|
||||
// GBase 8s is Informix-compatible, unlike GBase 8a (`Gbase`), which speaks
|
||||
// the MySQL wire protocol and SQL dialect.
|
||||
Some(DatabaseType::Gbase8s) => {
|
||||
TableStructureCapabilities {
|
||||
dialect: StructureDialect::Informix,
|
||||
add_column: true,
|
||||
|
||||
@@ -2809,8 +2809,8 @@ fn gbase8a_allows_mysql_style_column_reorder() {
|
||||
#[test]
|
||||
fn gbase8s_uses_informix_ddl_not_mysql() {
|
||||
let result = build_create_table_sql(TableStructureSqlOptions {
|
||||
database_type: Some(DatabaseType::Gbase),
|
||||
driver_profile: Some("gbase8s".to_string()),
|
||||
database_type: Some(DatabaseType::Gbase8s),
|
||||
driver_profile: None,
|
||||
schema: Some("gbasedbt".to_string()),
|
||||
table_name: "issue8003_repro".to_string(),
|
||||
columns: vec![
|
||||
|
||||
@@ -887,6 +887,7 @@ impl<'de> Deserialize<'de> for ConnectionConfig {
|
||||
{
|
||||
let mut value = Value::deserialize(deserializer)?;
|
||||
migrate_legacy_transport_layers(&mut value);
|
||||
migrate_legacy_gbase8s_profile(&mut value);
|
||||
let data = ConnectionConfigData::deserialize(value).map_err(serde::de::Error::custom)?;
|
||||
if let Some(grouping) = &data.redis_key_grouping {
|
||||
let mut ids = std::collections::HashSet::new();
|
||||
@@ -909,6 +910,20 @@ impl<'de> Deserialize<'de> for ConnectionConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// GBase 8s used to be the `gbase8s` driver profile of the MySQL-based `gbase` type. It is its own
|
||||
/// `gbase8s` type now (Informix dialect), so saved and in-flight configs are rewritten on load.
|
||||
fn migrate_legacy_gbase8s_profile(value: &mut Value) {
|
||||
let Some(object) = value.as_object_mut() else {
|
||||
return;
|
||||
};
|
||||
let is_gbase = object.get("db_type").and_then(Value::as_str) == Some("gbase");
|
||||
let is_8s_profile =
|
||||
object.get("driver_profile").and_then(Value::as_str).is_some_and(|p| p.eq_ignore_ascii_case("gbase8s"));
|
||||
if is_gbase && is_8s_profile {
|
||||
object.insert("db_type".to_string(), Value::String("gbase8s".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_legacy_transport_layers(value: &mut Value) {
|
||||
let Some(object) = value.as_object_mut() else {
|
||||
return;
|
||||
@@ -1324,6 +1339,7 @@ impl ConnectionConfig {
|
||||
DatabaseType::Ignite => format!("ignite://{host}:{port}{db_part}"),
|
||||
DatabaseType::Ignite3 => format!("ignite3://{host}:{port}{db_part}"),
|
||||
DatabaseType::Gbase => format!("gbase://{host}:{port}{db_part}"),
|
||||
DatabaseType::Gbase8s => format!("gbase8s://{host}:{port}{db_part}"),
|
||||
DatabaseType::H2 => format!("h2://{host}:{port}{db_part}"),
|
||||
DatabaseType::Snowflake => format!("snowflake://{host}/{db_part}"),
|
||||
DatabaseType::Trino => format!("trino://{host}:{port}{db_part}"),
|
||||
@@ -1555,6 +1571,9 @@ impl ConnectionConfig {
|
||||
DatabaseType::Gbase => {
|
||||
format!("gbase://{}:{}@{host}:{port}{db_part}", username, password)
|
||||
}
|
||||
DatabaseType::Gbase8s => {
|
||||
format!("gbase8s://{}:{}@{host}:{port}{db_part}", username, password)
|
||||
}
|
||||
DatabaseType::H2 => {
|
||||
format!("h2://{}:{}@{host}:{port}{db_part}", username, password)
|
||||
}
|
||||
@@ -3131,6 +3150,33 @@ mod tests {
|
||||
assert_eq!(database_info_from_protocol_value(&serde_json::json!({ "ok": true })), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_gbase8s_profile_connections_load_as_the_gbase8s_type() {
|
||||
let config = |db_type: &str, driver_profile: Option<&str>| {
|
||||
let mut value = serde_json::json!({
|
||||
"id": "id",
|
||||
"name": "GBase",
|
||||
"db_type": db_type,
|
||||
"host": "localhost",
|
||||
"port": 9088,
|
||||
"username": "gbasedbt",
|
||||
"password": "",
|
||||
"database": "app"
|
||||
});
|
||||
if let Some(profile) = driver_profile {
|
||||
value["driver_profile"] = serde_json::json!(profile);
|
||||
}
|
||||
serde_json::from_value::<ConnectionConfig>(value).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(config("gbase", Some("gbase8s")).db_type, DatabaseType::Gbase8s);
|
||||
assert_eq!(config("gbase", Some("GBASE8S")).db_type, DatabaseType::Gbase8s);
|
||||
assert_eq!(config("gbase8s", Some("gbase8s")).db_type, DatabaseType::Gbase8s);
|
||||
assert_eq!(config("gbase", Some("gbase8a")).db_type, DatabaseType::Gbase);
|
||||
assert_eq!(config("gbase", None).db_type, DatabaseType::Gbase);
|
||||
assert_eq!(config("informix", Some("gbase8s")).db_type, DatabaseType::Informix);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_schema_is_optional_and_round_trips() {
|
||||
let base = serde_json::json!({
|
||||
|
||||
@@ -95,6 +95,7 @@ export const databaseSupport: DatabaseSupportItem[] = [
|
||||
{ id: "xugu", name: "XuguDB", icon: "/icons/database/xugu.png", tone: "#84cc16" },
|
||||
{ id: "gbase", name: "GBase", icon: "/icons/database/gbase.png", tone: "#06b6d4" },
|
||||
{ id: "gbase8c", name: "GBase 8c", icon: "/icons/database/gbase.png", tone: "#06b6d4" },
|
||||
{ id: "gbase8s", name: "GBase 8s", icon: "/icons/database/gbase.png", tone: "#06b6d4" },
|
||||
{ id: "access", name: "Access", icon: "/icons/database/access.png", tone: "#a53346" },
|
||||
{ id: "h2", name: "H2", icon: "/icons/database/h2.svg", tone: "#f7a81b" },
|
||||
{ id: "etcd", name: "Etcd", icon: "/icons/database/etcd.svg", tone: "#419eda" },
|
||||
|
||||
+5
-4
@@ -51,13 +51,14 @@ test("maps GBase profiles to their matching agent driver keys", () => {
|
||||
assert.equal(agentDriverInstallKey("gbase"), "gbase8a");
|
||||
assert.equal(agentDriverInstallKey("gbase", "gbase"), "gbase8a");
|
||||
assert.equal(agentDriverInstallKey("gbase", "gbase8a"), "gbase8a");
|
||||
assert.equal(agentDriverInstallKey("gbase", "gbase8s"), "gbase8s");
|
||||
assert.equal(agentDriverInstallKey("gbase8s"), "gbase8s");
|
||||
assert.equal(agentDriverInstallKey("gbase8s", "gbase8s"), "gbase8s");
|
||||
});
|
||||
|
||||
test("uses the selected GBase 8s agent for install hints", () => {
|
||||
assert.equal(
|
||||
showAgentDriverInstallHint(
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
[
|
||||
{ db_type: "gbase", installed: true },
|
||||
{ db_type: "gbase8s", installed: false },
|
||||
@@ -68,7 +69,7 @@ test("uses the selected GBase 8s agent for install hints", () => {
|
||||
);
|
||||
assert.equal(
|
||||
showAgentDriverInstallHint(
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
[
|
||||
{ db_type: "gbase", installed: false },
|
||||
{ db_type: "gbase8s", installed: true },
|
||||
@@ -89,7 +90,7 @@ test("uses unified Oracle and selected profile keys for update hints", () => {
|
||||
assert.equal(hasAgentDriverUpdate("oracle", [{ db_type: "oracle", installed: true, update_available: true }], "oracle-10g"), true);
|
||||
assert.equal(
|
||||
hasAgentDriverUpdate(
|
||||
"gbase",
|
||||
"gbase8s",
|
||||
[
|
||||
{ db_type: "gbase", installed: true, update_available: false },
|
||||
{ db_type: "gbase8s", installed: true, update_available: true },
|
||||
|
||||
+1
-1
@@ -416,7 +416,7 @@ test("parses Apache IoTDB JDBC URLs", () => {
|
||||
test("parses GBase 8s JDBC URLs", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:gbasedbt-sqli://gbasedbt:secret@gbase.example.com:20013/testdb:GBASEDBTSERVER=gbase01;CLIENT_LOCALE=zh_cn.utf8");
|
||||
|
||||
assert.equal(parsed.dbType, "gbase");
|
||||
assert.equal(parsed.dbType, "gbase8s");
|
||||
assert.equal(parsed.driverProfile, "gbase8s");
|
||||
assert.equal(parsed.driverLabel, "南大通用 GBase 8s");
|
||||
assert.equal(parsed.host, "gbase.example.com");
|
||||
|
||||
@@ -12,11 +12,6 @@ agentKey: gbase8a
|
||||
driverStoreVisible: true
|
||||
driverStoreOrder: 15
|
||||
driverProfiles:
|
||||
- profile: gbase8s
|
||||
agentKey: gbase8s
|
||||
label: 南大通用 GBase 8s
|
||||
storeVisible: true
|
||||
storeOrder: 16
|
||||
- profile: gbase8a
|
||||
agentKey: gbase8a
|
||||
label: 南大通用 GBase 8a
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
schemaVersion: 1
|
||||
order: 452
|
||||
dbType: gbase8s
|
||||
compareModes: [schema, data]
|
||||
rustVariant: Gbase8s
|
||||
label: 南大通用 GBase 8s
|
||||
dialect: Informix
|
||||
runtimeMode: agent
|
||||
mcpMode: bridge
|
||||
agentKey: gbase8s
|
||||
driverStoreVisible: true
|
||||
driverStoreOrder: 16
|
||||
singleConnectionPool: false
|
||||
metadataConnectionScoped: false
|
||||
skipTcpProbe: true
|
||||
defaultPort: 9088
|
||||
traits:
|
||||
schemaAware: true
|
||||
treeSchema: true
|
||||
supportLevel: operate
|
||||
capabilities:
|
||||
queryExecution: true
|
||||
metadataBrowse: true
|
||||
objectBrowser: true
|
||||
objectSource: false
|
||||
schemaSearch: true
|
||||
diagram: true
|
||||
tableDataEdit: true
|
||||
tableStructureEdit: true
|
||||
tableImport: false
|
||||
dataTransfer: false
|
||||
sqlFileExecution: true
|
||||
databaseCreate: false
|
||||
fieldLineage: false
|
||||
sqlExplain: false
|
||||
userAdmin: false
|
||||
driverManagement: true
|
||||
@@ -312,7 +312,7 @@ profiles:
|
||||
port: 5258
|
||||
user: gbasedbt
|
||||
- id: gbase8s
|
||||
dbType: gbase
|
||||
dbType: gbase8s
|
||||
label: 南大通用 GBase 8s
|
||||
icon: gbase
|
||||
port: 9088
|
||||
|
||||
Reference in New Issue
Block a user