fix(gbase8s): omit owner qualifier in table admin SQL

This commit is contained in:
New2Niu
2026-09-14 14:52:13 +08:00
committed by GitHub
parent 6d866905b5
commit 1533064f1f
6 changed files with 37 additions and 10 deletions
@@ -72,7 +72,7 @@ import type { ColumnInfo, ConnectionConfig, ConstraintInfo, ForeignKeyInfo, Inde
import { sortTablesByFkDependency, type TableWithFk } from "@/lib/table/tableDependencySort";
import { isSchemaAware, supportsTableVacuum, supportsTransfer } from "@/lib/database/databaseCapabilities";
import { supportsAiAssistantContext, supportsSchemaDiagram, supportsTableImport, supportsTableStructureEditing, supportsTableTruncate } from "@/lib/database/databaseFeatureSupport";
import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, objectListSchemaForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionTableSqlSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, objectListSchemaForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { getTableMetadataCapabilities, type TableMetadataCapabilities } from "@/lib/table/tableMetadataCapabilities";
import { constraintsForConstraintsTab } from "@/lib/table/constraintPresentation";
import { buildTableSelectSql } from "@/lib/table/tableSelectSql";
@@ -2531,7 +2531,7 @@ async function confirmPasteTable() {
function tableAdminSqlOptions(row: ObjectBrowserRow, options?: { cascade?: boolean }): TableAdminSqlOptions {
const result: TableAdminSqlOptions = {
databaseType: effectiveDatabaseType.value,
schema: row.schema || selectedSchema.value,
schema: connectionTableSqlSchema(props.connection, row.schema || selectedSchema.value),
tableName: row.name,
// Cloud Spanner's dialect decides the quote; the static per-type mapping cannot.
identifierQuote: connectionStore.connectionIdentifierQuote?.(props.connection.id),
@@ -162,7 +162,7 @@ import { buildViewDdl } from "@/lib/table/viewDdl";
import { formatSqlForDisplay, sqlFormatDialectForDbType } from "@/lib/sql/sqlFormatter";
import { omitDdlIdentifierQuotes } from "@/lib/sql/ddlDisplay";
import { getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionTableSqlSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { hasTreeNodeDatabaseContext } from "@/lib/sidebar/treeNodeContext";
import {
defaultPasteTableMode,
@@ -2798,11 +2798,13 @@ function batchTruncateConfirmMessage(): string {
async function dropSqlForTreeNode(node: TreeNode, options?: { cascade?: boolean }): Promise<string | null> {
if (node.type === "table" && node.connectionId && node.database) {
const config = connectionStore.getConfig(node.connectionId);
return buildDropTableSql({
databaseType: databaseTypeForNode(node),
schema: node.schema,
schema: connectionTableSqlSchema(config, node.schema),
tableName: node.label,
cascade: options?.cascade && supportsDropTableCascade(databaseTypeForNode(node)),
identifierQuote: connectionStore.connectionIdentifierQuote?.(node.connectionId),
});
}
const objectOptions = dropObjectSqlOptionsForNode(node);
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { shallowRef } from "vue";
import type { TreeNode } from "@/types/database";
import type { ConnectionConfig, DatabaseType, TreeNode } from "@/types/database";
import { dropTableCascade, dropTablePreviewSql, emptyTablePreviewSql, sidebarDangerRunningCancel, sidebarDangerRunningExecutionId, sidebarDangerTarget, truncateTableCascade, truncateTablePreviewSql } from "@/components/sidebar/sidebarTreeDialogState";
const mocks = vi.hoisted(() => ({
@@ -49,19 +49,20 @@ function tableNode(database: string | null | undefined): TreeNode {
} as TreeNode;
}
function runtime(database: string | null | undefined) {
function runtime(database: string | null | undefined, config: Pick<ConnectionConfig, "db_type"> & Partial<Pick<ConnectionConfig, "driver_profile">> = { db_type: "saphana" }, databaseType: DatabaseType = "saphana") {
const node = tableNode(database);
const activeNode = shallowRef(node);
const connectionStore = {
ensureConnected: mocks.ensureConnected,
getConfig: () => config,
removeTreeNode: mocks.removeTreeNode,
} as any;
const feature = useSidebarTableMutationRuntime({
activeNode,
releaseActiveNodeReference: mocks.releaseActiveNodeReference,
connectionStore,
currentDatabaseType: () => "saphana",
databaseTypeForNode: () => "saphana",
currentDatabaseType: () => databaseType,
databaseTypeForNode: () => databaseType,
executeWithProductionGuard: mocks.executeWithProductionGuard,
closeDroppedTableObjectTabsForNode: mocks.closeDroppedTableObjectTabsForNode,
refreshMutatedTableDataTabsForNode: mocks.refreshMutatedTableDataTabsForNode,
@@ -121,6 +122,14 @@ describe("useSidebarTableMutationRuntime SAP HANA schema-scoped actions", () =>
expect(mocks.releaseActiveNodeReference).toHaveBeenCalledWith([node.id]);
});
it("omits the GBase 8s metadata owner from generated table SQL", async () => {
const { feature } = runtime("tenant", { db_type: "gbase", driver_profile: "gbase8s" }, "informix");
await feature.confirmDropTable();
expect(mocks.buildDropTableSql).toHaveBeenCalledWith(expect.objectContaining({ databaseType: "informix", schema: undefined, tableName: "ORDERS" }));
});
it.each(["confirmEmptyTable", "confirmTruncateTable"] as const)("refreshes data tabs after %s", async (action) => {
const { feature, node } = runtime("");
@@ -19,6 +19,7 @@ import {
type VacuumTableSqlOptions,
} from "@/lib/database/dbAdminSql";
import { isSqlServerLinkedNode } from "@/lib/database/sqlServerLinkedServers";
import { connectionTableSqlSchema } from "@/lib/database/jdbcDialect";
import { isQueryTimeoutErrorMessage } from "@/lib/sql/queryError";
import { uuid } from "@/lib/common/utils";
import * as api from "@/lib/backend/api";
@@ -74,9 +75,10 @@ export function useSidebarTableMutationRuntime(options: SidebarTableMutationRunt
const supportsMysqlAutoIncrement = computed(() => activeNode.value.type === "table" && supportsNativeMysqlAutoIncrement(activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined));
function tableAdminSqlOptions(optionsOverride?: { cascade?: boolean }): TableAdminSqlOptions {
const config = activeNode.value.connectionId ? connectionStore.getConfig(activeNode.value.connectionId) : undefined;
const result: TableAdminSqlOptions = {
databaseType: currentDatabaseType(),
schema: activeNode.value.schema,
schema: connectionTableSqlSchema(config, activeNode.value.schema),
tableName: activeNode.value.label,
// Cloud Spanner's two dialects quote differently, so admin SQL needs the quote the connected
// agent reported rather than the static per-type mapping.
@@ -87,9 +89,10 @@ export function useSidebarTableMutationRuntime(options: SidebarTableMutationRunt
}
function tableAdminSqlOptionsForNode(node: TreeNode, optionsOverride?: { cascade?: boolean }): TableAdminSqlOptions {
const config = node.connectionId ? connectionStore.getConfig(node.connectionId) : undefined;
const result: TableAdminSqlOptions = {
databaseType: databaseTypeForNode(node),
schema: node.schema,
schema: connectionTableSqlSchema(config, node.schema),
tableName: node.label,
identifierQuote: connectionStore.connectionIdentifierQuote?.(node.connectionId),
};
@@ -9,6 +9,7 @@ import {
connectionQueryExecutionSchema,
connectionShouldDiscoverJdbcSchemas,
connectionShouldLoadIdentifierQuote,
connectionTableSqlSchema,
connectionUsesConnectionRootSchemaMode,
connectionUsesDatabaseObjectTreeMode,
effectiveDatabaseTypeForConnection,
@@ -176,6 +177,12 @@ describe("jdbc dialect inference", () => {
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: "gbase", driver_profile: "gbase8a" }, "analytics")).toBe("analytics");
expect(connectionTableSqlSchema({ db_type: "informix", driver_profile: "informix" }, "informix")).toBe("informix");
});
it("recognizes GaussDB reached through PostgreSQL-compatible JDBC drivers", () => {
const connection = {
db_type: "jdbc" as const,
@@ -312,6 +312,12 @@ export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection
return isSchemaAware(type) ? schema || database : undefined;
}
/** 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;
return schema;
}
/** Maps a database type to the corresponding CodeMirror SQL dialect name used by QueryEditor and DdlViewDialog. */
export function codeMirrorSqlDialect(dbType: DatabaseType | undefined): "mysql" | "postgres" | "sqlserver" {
if (dbType === "postgres" || dbType === "gaussdb" || dbType === "kwdb" || dbType === "opengauss") return "postgres";