fix(sql): honor include-database-name setting for schema-aware engines
This commit is contained in:
4 files changed
+131
-20
No files matched your search
+29
-11
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user