fix(sql): honor include-database-name setting for schema-aware engines

This commit is contained in:
U1 authored and GitHub committed 2026-09-15 13:28:40 +08:00
1 parent 1464d33677
commit 14bcde806f
4 files changed
+131 -20

No files matched your search

+29 -11
View File
@@ -204,16 +204,21 @@ describe("buildSelectAllSql", () => {
expect(buildSelectAllSql("mysql", { database: "mydb", tableName: "users" }, undefined, undefined, true)).toBe("SELECT * FROM `mydb`.`users`");
});
it("qualifies and quotes a PostgreSQL table with its schema", () => {
expect(buildSelectAllSql("postgres", { schema: "public", tableName: "users" })).toBe('SELECT * FROM "public"."users"');
it("omits the PostgreSQL schema qualifier by default; including it is opt-in (#9110)", () => {
expect(buildSelectAllSql("postgres", { schema: "public", tableName: "users" })).toBe('SELECT * FROM "users"');
expect(buildSelectAllSql("postgres", { schema: "public", tableName: "users" }, undefined, undefined, true)).toBe('SELECT * FROM "public"."users"');
});
it("can omit identifier quotes while preserving schema qualification", () => {
expect(buildSelectAllSql("postgres", { schema: "public", tableName: "users" }, undefined, undefined, false, false)).toBe("SELECT * FROM public.users");
it("can omit identifier quotes for a PostgreSQL table", () => {
expect(buildSelectAllSql("postgres", { schema: "public", tableName: "users" }, undefined, undefined, true, false)).toBe("SELECT * FROM public.users");
});
it("preserves the Phoenix schema for new-query prefill", () => {
expect(buildSelectAllSql("jdbc", { schema: "APP", tableName: "USERS" }, '"', "phoenix")).toBe('SELECT * FROM "APP"."USERS"');
it("preserves the Phoenix schema for new-query prefill when qualification is on", () => {
expect(buildSelectAllSql("jdbc", { schema: "APP", tableName: "USERS" }, '"', "phoenix", true)).toBe('SELECT * FROM "APP"."USERS"');
});
it("prefills Phoenix tables unqualified by default (#9110)", () => {
expect(buildSelectAllSql("jdbc", { schema: "APP", tableName: "USERS" }, '"', "phoenix")).toBe('SELECT * FROM "USERS"');
});
it("scopes InfluxDB 1.x / 2.x prefill to a rolling InfluxQL window", () => {
@@ -243,13 +248,15 @@ describe("buildSelectAllSql", () => {
expect(buildSelectAllSql("starrocks", { catalog: "paimon_catalog", database: "bi", tableName: "events" })).toBe("SELECT * FROM `paimon_catalog`.`bi`.`events`");
});
it("uses the driver-reported identifier quote for Kingbase MySQL compat mode", () => {
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, "`")).toBe("SELECT * FROM `audit_schema`.`events`");
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, "`", undefined, true)).toBe("SELECT * FROM `audit_schema`.`events`");
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, "`")).toBe("SELECT * FROM `events`");
});
it("uses the driver-reported identifier quote for Kingbase PostgreSQL mode", () => {
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, '"')).toBe('SELECT * FROM "audit_schema"."events"');
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, '"', undefined, true)).toBe('SELECT * FROM "audit_schema"."events"');
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, '"')).toBe('SELECT * FROM "events"');
});
it("falls back to double quotes for Kingbase when no identifier quote is reported", () => {
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" })).toBe('SELECT * FROM "audit_schema"."events"');
expect(buildSelectAllSql("kingbase", { schema: "audit_schema", tableName: "events" }, undefined, undefined, true)).toBe('SELECT * FROM "audit_schema"."events"');
});
});
@@ -272,7 +279,7 @@ describe("isNewQueryPrefillSupported", () => {
});
describe("resolveNewQueryInitialSql", () => {
it("prefills SQL from the active table when enabled", () => {
it("prefills bare table SQL from the active table by default; the database name is opt-in", () => {
expect(
resolveNewQueryInitialSql({
activeTab: dataTab(),
@@ -281,6 +288,16 @@ describe("resolveNewQueryInitialSql", () => {
targetDatabase: "app_db",
databaseType: "postgres",
}),
).toBe('SELECT * FROM "users"');
expect(
resolveNewQueryInitialSql({
activeTab: dataTab(),
prefillEnabled: true,
targetConnectionId: "conn-1",
targetDatabase: "app_db",
databaseType: "postgres",
includeDatabaseName: true,
}),
).toBe('SELECT * FROM "public"."users"');
});
@@ -292,6 +309,7 @@ describe("resolveNewQueryInitialSql", () => {
targetConnectionId: "conn-1",
targetDatabase: "app_db",
databaseType: "postgres",
includeDatabaseName: true,
quoteIdentifiers: false,
}),
).toBe("SELECT * FROM public.users");
@@ -308,7 +326,7 @@ describe("resolveNewQueryInitialSql", () => {
driverProfile: "phoenix",
identifierQuote: '"',
}),
).toBe('SELECT * FROM "APP"."USERS"');
).toBe('SELECT * FROM "USERS"');
});
it("leaves new queries empty when the setting is disabled", () => {
@@ -141,6 +141,32 @@ describe("qualifiedTableName — schema-aware JDBC profiles", () => {
});
});
describe("qualifiedTableName — includeDatabaseName on quoted-identifier paths (#9110)", () => {
it("drops the schema qualifier for JDBC-reported Postgres-family connections", () => {
expect(qualifiedTableName({ databaseType: "postgres", identifierQuote: '"', schema: "public", tableName: "Users", includeDatabaseName: false })).toBe('"Users"');
});
it("keeps the schema qualifier by default for quoted-identifier connections", () => {
expect(qualifiedTableName({ databaseType: "postgres", identifierQuote: '"', schema: "public", tableName: "Users" })).toBe('public."Users"');
});
it("drops the schema qualifier for kingbase JDBC connections", () => {
expect(qualifiedTableName({ databaseType: "kingbase", identifierQuote: '"', schema: "app", tableName: "orders", includeDatabaseName: false })).toBe('"orders"');
});
it("drops the schema qualifier for generic JDBC profiles that qualify schemas", () => {
expect(qualifiedTableName({ databaseType: "jdbc", driverProfile: "phoenix", identifierQuote: '"', schema: "MY_SCHEMA", tableName: "ORDER", includeDatabaseName: false })).toBe('"ORDER"');
});
it("drops the schema qualifier for native Informix connections", () => {
expect(qualifiedTableName({ databaseType: "informix", identifierQuote: "", schema: "gbasedbt", tableName: "connection_smoke", includeDatabaseName: false })).toBe("connection_smoke");
});
it("keeps the qualifier for databases that require fully qualified names", () => {
expect(qualifiedTableName({ databaseType: "sqlserver", schema: "dbo", tableName: "users", includeDatabaseName: false })).toBe("[dbo].[users]");
});
});
describe("qualifiedTableName — GBase 8s", () => {
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");
@@ -1,5 +1,6 @@
import type { DatabaseType } from "@/types/database.ts";
import { isSchemaAware, usesDatabaseObjectTreeMode } from "@/lib/database/databaseCapabilities.ts";
import { DATABASE_SCHEMA_QUALIFIED_TYPES } from "@/lib/database/databaseCapabilitySets";
import { jdbcDriverProfileUsesSchemaQualification } from "@/lib/database/jdbcDialect";
import * as api from "@/lib/backend/api.ts";
import { parseSqlServerLinkedSchema, sqlServerLinkedTableName } from "@/lib/database/sqlServerLinkedServers.ts";
@@ -39,6 +40,14 @@ export interface BuildTableSelectSqlOptions {
const DATABASE_QUALIFIED_TABLE_TYPES = new Set<DatabaseType>(["mysql", "clickhouse", "doris", "starrocks", "goldendb"]);
// `includeDatabaseName === false` drops the schema qualifier — the "database
// name" on schema-aware engines — except for databases that can only address
// objects through their full qualified name (`catalog.schema.table` /
// `database.schema.table`), where dropping it would break the query.
function dropsSchemaQualifier(databaseType: DatabaseType | undefined, includeDatabaseName?: boolean): boolean {
return includeDatabaseName === false && databaseType !== undefined && !DATABASE_SCHEMA_QUALIFIED_TYPES.has(databaseType);
}
function sqlStatementSpans(sql: string, dialectId: string): Array<{ start: number; end: number }> {
const spans: Array<{ start: number; end: number }> = [];
let start = 0;
@@ -193,6 +202,7 @@ export function qualifiedTableName(options: Pick<BuildTableSelectSqlOptions, "da
}
if ((databaseType === "gaussdb" || databaseType === "opengauss" || databaseType === "postgres" || databaseType === "kingbase") && identifierQuote != null) {
const quotedTable = quoteTableData(tableName);
if (dropsSchemaQualifier(databaseType, includeDatabaseName)) return quotedTable;
const trimmedSchema = schema?.trim();
if (trimmedSchema) {
return `${quoteTableData(trimmedSchema)}.${quotedTable}`;
@@ -201,6 +211,7 @@ export function qualifiedTableName(options: Pick<BuildTableSelectSqlOptions, "da
}
if (databaseType === "jdbc" && jdbcDriverProfileUsesSchemaQualification(driverProfile)) {
const quotedTable = quoteTableData(tableName);
if (dropsSchemaQualifier(databaseType, includeDatabaseName)) return quotedTable;
const trimmedSchema = schema?.trim();
return trimmedSchema ? `${quoteTableData(trimmedSchema)}.${quotedTable}` : quotedTable;
}
@@ -223,6 +234,7 @@ export function qualifiedTableName(options: Pick<BuildTableSelectSqlOptions, "da
}
if (databaseType === "informix" && identifierQuote != null) {
const quotedTable = quoteTableData(tableName);
if (dropsSchemaQualifier(databaseType, includeDatabaseName)) return quotedTable;
const trimmedSchema = schema?.trim();
return trimmedSchema ? `${quoteTableData(trimmedSchema)}.${quotedTable}` : quotedTable;
}
@@ -233,6 +245,12 @@ export function qualifiedTableName(options: Pick<BuildTableSelectSqlOptions, "da
return quoteIdentifiers === false ? [linked.server, linked.catalog, linked.schema, tableName].map((name) => quoteTableIdentifierIfNeeded(databaseType, name)).join(".") : sqlServerLinkedTableName(linked, tableName);
}
}
// The schema qualifier is the "database name" on schema-aware engines
// (Oracle's SYSTEM, PG's public, ...). `dropsSchemaQualifier` keeps it
// for databases whose queries would not resolve without it.
if (dropsSchemaQualifier(databaseType, includeDatabaseName)) {
return quoteTable(tableName);
}
return `${quoteTable(schema)}.${quoteTable(tableName)}`;
}
// MySQL-style engines use the selected database as their table namespace.