From dd5d74fa4fc8181dd862c0f3c1f4897e43e8e490 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 7 Aug 2026 11:39:13 +0800 Subject: [PATCH] fix(gbase8s): restore server name and table queries Closes #5573 --- .../components/connection/ConnectionDialog.vue | 1 + apps/desktop/src/components/grid/DataGrid.vue | 2 ++ .../src/components/objects/ObjectBrowser.vue | 1 + .../src/composables/useDataGridActions.ts | 1 + .../src/composables/useNavigationTargets.ts | 4 ++++ .../composables/useSidebarDataOpenRuntime.ts | 1 + .../connection/gbase8sConnectionDialog.spec.ts | 10 ++++++++++ .../lib/__tests__/table/tableSelectSql.spec.ts | 8 ++++---- apps/desktop/src/lib/export/databaseExport.ts | 2 ++ apps/desktop/src/lib/table/tableSelectSql.ts | 8 ++++++-- apps/desktop/src/stores/queryStore.ts | 2 ++ crates/dbx-core/src/sql_dialect/table_select.rs | 17 ++++++++++------- crates/dbx-core/src/sql_dialect/tests.rs | 12 ++++++++++++ crates/dbx-core/src/sql_dialect/types.rs | 2 ++ 14 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 apps/desktop/src/lib/__tests__/connection/gbase8sConnectionDialog.spec.ts diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index c0ab86a8bf..c07160f3bc 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -2270,6 +2270,7 @@ watch( redis_key_separator: config.redis_key_separator ?? ":", redis_scan_page_size: config.redis_scan_page_size ?? REDIS_SCAN_PAGE_SIZE_DEFAULT, etcd_endpoints: config.etcd_endpoints || "", + gbase_server: config.gbase_server || "", informix_server: config.informix_server || "", external_config: config.external_config, attached_databases: config.attached_databases || [], diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 5bcb38ef68..ddd959f200 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -4576,6 +4576,7 @@ async function applyOrderBySearch() { if (!tableMeta) return; const sql = await buildTableSelectSql({ databaseType: resolvedDatabaseType.value, + driverProfile: props.connectionId ? connectionStore.getConfig(props.connectionId)?.driver_profile : undefined, identifierQuote: connectionStore.connectionIdentifierQuote?.(props.connectionId), catalog: tableMeta.catalog, database: tableMeta.database, @@ -4610,6 +4611,7 @@ async function applyWhereFilter() { if (!tableMeta) return; const sql = await buildTableSelectSql({ databaseType: resolvedDatabaseType.value, + driverProfile: props.connectionId ? connectionStore.getConfig(props.connectionId)?.driver_profile : undefined, identifierQuote: connectionStore.connectionIdentifierQuote?.(props.connectionId), catalog: tableMeta.catalog, database: tableMeta.database, diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue index 50ec827beb..d6d2af3ca0 100644 --- a/apps/desktop/src/components/objects/ObjectBrowser.vue +++ b/apps/desktop/src/components/objects/ObjectBrowser.vue @@ -1179,6 +1179,7 @@ async function openNewQuery(row: ObjectBrowserRow) { tabId, await buildTableSelectSql({ databaseType: effectiveDatabaseType.value, + driverProfile: props.connection.driver_profile, identifierQuote: connectionStore.connectionIdentifierQuote?.(props.connection.id), catalog: props.catalog, database: props.database, diff --git a/apps/desktop/src/composables/useDataGridActions.ts b/apps/desktop/src/composables/useDataGridActions.ts index 0688cf9156..8103b04ac0 100644 --- a/apps/desktop/src/composables/useDataGridActions.ts +++ b/apps/desktop/src/composables/useDataGridActions.ts @@ -57,6 +57,7 @@ export function useDataGridActions(activeTab: ComputedRef) const realColumns = tab.tableMeta?.columns.length ? tab.tableMeta.columns : undefined; return buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config?.driver_profile, identifierQuote: connectionStore.connectionIdentifierQuote?.(tab.connectionId), database: tableMeta?.database, schema: tableMeta?.schema, diff --git a/apps/desktop/src/composables/useNavigationTargets.ts b/apps/desktop/src/composables/useNavigationTargets.ts index 10a26fc5b4..209e7cf466 100644 --- a/apps/desktop/src/composables/useNavigationTargets.ts +++ b/apps/desktop/src/composables/useNavigationTargets.ts @@ -109,6 +109,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab const primaryKeys = editableRowIdentifierColumns(effectiveDbType, columns, undefined, targetTableType); const sql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config.driver_profile, identifierQuote, schema: tableSchema, catalog: target.catalog, @@ -137,6 +138,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab } const sql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config.driver_profile, identifierQuote, schema: tableSchema, catalog: target.catalog, @@ -180,6 +182,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab if (fellBackToLimitZero) { const emptySql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config.driver_profile, identifierQuote, schema: tableSchema, catalog: target.catalog, @@ -224,6 +227,7 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab if (!fellBackToLimitZero && !firstQueryFailed && (useRowId || config.db_type === "tdengine")) { const newSql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config.driver_profile, identifierQuote, schema: tableSchema, catalog: target.catalog, diff --git a/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts b/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts index 6cec81274e..1853e7ea64 100644 --- a/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts +++ b/apps/desktop/src/composables/useSidebarDataOpenRuntime.ts @@ -307,6 +307,7 @@ export function useSidebarDataOpenRuntime() { const includeRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys, tableType); const sql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: config?.driver_profile, identifierQuote: connectionStore.connectionIdentifierQuote?.(node.connectionId), schema: tableSchema, database: node.database, diff --git a/apps/desktop/src/lib/__tests__/connection/gbase8sConnectionDialog.spec.ts b/apps/desktop/src/lib/__tests__/connection/gbase8sConnectionDialog.spec.ts new file mode 100644 index 0000000000..6e13849d7d --- /dev/null +++ b/apps/desktop/src/lib/__tests__/connection/gbase8sConnectionDialog.spec.ts @@ -0,0 +1,10 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8"); + +describe("GBase 8s connection dialog", () => { + it("hydrates DBSERVERNAME when editing a saved connection", () => { + expect(dialogSource).toContain('gbase_server: config.gbase_server || ""'); + }); +}); diff --git a/apps/desktop/src/lib/__tests__/table/tableSelectSql.spec.ts b/apps/desktop/src/lib/__tests__/table/tableSelectSql.spec.ts index 462b643c40..b56d08881a 100644 --- a/apps/desktop/src/lib/__tests__/table/tableSelectSql.spec.ts +++ b/apps/desktop/src/lib/__tests__/table/tableSelectSql.spec.ts @@ -35,13 +35,13 @@ describe("qualifiedTableName — SQLite attached databases", () => { }); describe("qualifiedTableName — GBase 8s", () => { - it("keeps the owner unquoted when the driver reports no identifier quote support", () => { - expect(qualifiedTableName({ databaseType: "informix", identifierQuote: "", schema: "gbasedbt", tableName: "connection_smoke" })).toBe("gbasedbt.connection_smoke"); + it("omits the metadata owner for GBase 8s table data", () => { + expect(qualifiedTableName({ databaseType: "informix", driverProfile: "gbase8s", identifierQuote: "", schema: "gbasedbt", tableName: "connection_smoke" })).toBe("connection_smoke"); expect(quoteTableDataIdentifier("informix", "connection_smoke", "")).toBe("connection_smoke"); }); - it("keeps native Informix qualification when no driver capability was loaded", () => { - expect(qualifiedTableName({ databaseType: "informix", schema: "gbasedbt", tableName: "connection_smoke" })).toBe("gbasedbt.connection_smoke"); + it("keeps native Informix owner qualification", () => { + expect(qualifiedTableName({ databaseType: "informix", identifierQuote: "", schema: "gbasedbt", tableName: "connection_smoke" })).toBe("gbasedbt.connection_smoke"); }); }); diff --git a/apps/desktop/src/lib/export/databaseExport.ts b/apps/desktop/src/lib/export/databaseExport.ts index fef86a7e05..4abf1501d5 100644 --- a/apps/desktop/src/lib/export/databaseExport.ts +++ b/apps/desktop/src/lib/export/databaseExport.ts @@ -46,6 +46,7 @@ export interface BuildExportInsertStatementsOptions { export interface BuildExportPageSqlOptions { databaseType?: DatabaseType; + driverProfile?: string; identifierQuote?: string; schema?: string; tableName: string; @@ -79,6 +80,7 @@ export function buildInsertStatements(options: BuildExportInsertStatementsOption export async function buildExportPageSql(options: BuildExportPageSqlOptions): Promise { return buildTableSelectSql({ databaseType: options.databaseType, + driverProfile: options.driverProfile, identifierQuote: options.identifierQuote, schema: options.schema, tableName: options.tableName, diff --git a/apps/desktop/src/lib/table/tableSelectSql.ts b/apps/desktop/src/lib/table/tableSelectSql.ts index 6cc1beab64..3af4e5bc61 100644 --- a/apps/desktop/src/lib/table/tableSelectSql.ts +++ b/apps/desktop/src/lib/table/tableSelectSql.ts @@ -6,6 +6,7 @@ import { isExplicitlyQuotedSqlIdentifier, quoteGaussDbJdbcIdentifier } from "@/l export interface BuildTableSelectSqlOptions { databaseType?: DatabaseType; + driverProfile?: string; identifierQuote?: string; schema?: string; tableName: string; @@ -50,8 +51,11 @@ function quoteCypherIdentifier(name: string): string { return `\`${name.replace(/`/g, "``")}\``; } -export function qualifiedTableName(options: Pick): string { - const { databaseType, identifierQuote, schema, tableName, catalog, database } = options; +export function qualifiedTableName(options: Pick): string { + const { databaseType, driverProfile, identifierQuote, schema, tableName, catalog, database } = options; + if (databaseType === "informix" && driverProfile?.trim().toLowerCase() === "gbase8s") { + return quoteTableDataIdentifier(databaseType, tableName, identifierQuote); + } // Doris / StarRocks multi-catalog: address external-catalog tables with the // 3-part `catalog.database.table` form, which the engines accept directly. if (catalog && catalog !== "internal" && (databaseType === "doris" || databaseType === "starrocks")) { diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index c548541d45..e7c6187b0a 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -2383,6 +2383,7 @@ export const useQueryStore = defineStore("query", () => { try { const sql = await buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: conn?.driver_profile, identifierQuote, database: tableMeta.database, schema: tableMeta.schema, @@ -5098,6 +5099,7 @@ export const useQueryStore = defineStore("query", () => { while (true) { const sql = await api.buildTableSelectSql({ databaseType: effectiveDbType, + driverProfile: conn?.driver_profile, identifierQuote, database: tableMeta.database, schema: tableMeta.schema, diff --git a/crates/dbx-core/src/sql_dialect/table_select.rs b/crates/dbx-core/src/sql_dialect/table_select.rs index d16a86fe72..8aa5d5f591 100644 --- a/crates/dbx-core/src/sql_dialect/table_select.rs +++ b/crates/dbx-core/src/sql_dialect/table_select.rs @@ -21,6 +21,13 @@ pub fn build_count_table_sql(database_type: Option, schema: Option pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String { let database_type = options.database_type; + let schema = if database_type == Some(DatabaseType::Informix) + && options.driver_profile.as_deref().is_some_and(|profile| profile.eq_ignore_ascii_case("gbase8s")) + { + None + } else { + options.schema.as_deref() + }; let limit = options.limit.unwrap_or(100); if database_type == Some(DatabaseType::Neo4j) { return build_neo4j_table_select_sql(&options, limit); @@ -31,17 +38,12 @@ pub fn build_table_data_select_sql(options: TableDataSelectSqlOptions) -> String // Doris / StarRocks multi-catalog: prefix the catalog for external-catalog tables. let table = if uses_connection_identifier_quote(database_type, options.identifier_quote.as_deref()) { - table_data_qualified_table_name( - database_type, - options.schema.as_deref(), - &options.table_name, - options.identifier_quote.as_deref(), - ) + table_data_qualified_table_name(database_type, schema, &options.table_name, options.identifier_quote.as_deref()) } else { qualified_table_name_with_catalog( database_type, options.catalog.as_deref(), - options.schema.as_deref(), + schema, options.database.as_deref(), &options.table_name, ) @@ -502,6 +504,7 @@ mod tests { ) -> TableDataSelectSqlOptions { TableDataSelectSqlOptions { database_type: Some(database_type), + driver_profile: None, identifier_quote: None, schema: None, table_name: table.to_string(), diff --git a/crates/dbx-core/src/sql_dialect/tests.rs b/crates/dbx-core/src/sql_dialect/tests.rs index 913d018b90..aac43a8d69 100644 --- a/crates/dbx-core/src/sql_dialect/tests.rs +++ b/crates/dbx-core/src/sql_dialect/tests.rs @@ -370,6 +370,18 @@ fn builds_table_data_where_and_schema_queries() { }), "SELECT * FROM \"App Schema\".\"ANALYZE\" LIMIT 100;" ); + assert_eq!( + build_table_data_select_sql(TableDataSelectSqlOptions { + database_type: Some(DatabaseType::Informix), + driver_profile: Some("gbase8s".to_string()), + identifier_quote: Some(String::new()), + schema: Some("gbasedbt".to_string()), + table_name: "connection_smoke".to_string(), + limit: Some(100), + ..Default::default() + }), + "SELECT FIRST 100 * FROM connection_smoke" + ); assert_eq!( build_table_data_select_sql(TableDataSelectSqlOptions { database_type: Some(DatabaseType::Informix), diff --git a/crates/dbx-core/src/sql_dialect/types.rs b/crates/dbx-core/src/sql_dialect/types.rs index 5e6fd1b37c..8b7141f428 100644 --- a/crates/dbx-core/src/sql_dialect/types.rs +++ b/crates/dbx-core/src/sql_dialect/types.rs @@ -22,6 +22,8 @@ pub struct TableDataSelectSqlOptions { #[serde(default, skip_serializing_if = "Option::is_none")] pub database_type: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + pub driver_profile: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub identifier_quote: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub schema: Option,