fix(grid): resolve oracle jdbc edits with folded table names
Closes #8368
This commit is contained in:
3 files changed
+114
-5
No files matched your search
@@ -612,6 +612,81 @@ describe("queryStore hidden primary key editing", () => {
|
||||
expect(tab.queryEditabilityReason).toBeUndefined();
|
||||
}, 10_000);
|
||||
|
||||
it.each([
|
||||
{
|
||||
connection: { id: "oracle-1", name: "Oracle", db_type: "oracle", database: "XE", query_timeout_secs: 30 },
|
||||
label: "native Oracle",
|
||||
},
|
||||
{
|
||||
connection: { id: "oracle-jdbc-1", name: "Oracle JDBC", db_type: "jdbc", connection_string: "jdbc:oracle:thin:@//localhost:1521/XE", database: "XE", query_timeout_secs: 30 },
|
||||
label: "Oracle-inferred JDBC",
|
||||
},
|
||||
])("edits an unqualified lowercase query through the folded current-schema table for $label", async ({ connection }) => {
|
||||
getConnectionConfig.mockReturnValue(connection);
|
||||
getColumns.mockResolvedValue([
|
||||
{ name: "ID", data_type: "NUMBER", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
|
||||
{ name: "NAME", data_type: "VARCHAR2(100)", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
|
||||
]);
|
||||
listIndexes.mockResolvedValue([]);
|
||||
analyzeEditableQueryEditability.mockResolvedValue({
|
||||
editable: true,
|
||||
analysis: {
|
||||
schema: undefined,
|
||||
tableName: "imp_t",
|
||||
selectStar: true,
|
||||
columns: [],
|
||||
},
|
||||
});
|
||||
executeMulti.mockResolvedValue([{ columns: ["ID", "NAME"], rows: [[1, "Alice"]], affected_rows: 0, execution_time_ms: 1 }]);
|
||||
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab(connection.id, "XE", "Query");
|
||||
store.setAutoCommit(tabId, true);
|
||||
|
||||
await store.executeTabSql(tabId, "select * from imp_t");
|
||||
|
||||
// The service name must not leak into the metadata schema, and the stored
|
||||
// table spelling has to fold to uppercase so the generated UPDATE quotes
|
||||
// the object Oracle actually resolved for the unqualified SELECT.
|
||||
expect(getColumns).toHaveBeenCalledWith(connection.id, "XE", "", "IMP_T", undefined);
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
await vi.waitFor(() => expect(tab.tableMeta?.tableName).toBe("IMP_T"));
|
||||
expect(tab.tableMeta?.schema).toBeUndefined();
|
||||
expect(tab.queryEditabilityReason).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps an unqualified lowercase MySQL JDBC query on its unfolded table name", async () => {
|
||||
getConnectionConfig.mockReturnValue({ id: "jdbc-mysql-1", name: "MySQL JDBC", db_type: "jdbc", connection_string: "jdbc:mysql://localhost:3306/app", database: "app", query_timeout_secs: 30 });
|
||||
getColumns.mockResolvedValue([
|
||||
{ name: "id", data_type: "int", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
|
||||
{ name: "name", data_type: "varchar(64)", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
|
||||
]);
|
||||
analyzeEditableQueryEditability.mockResolvedValue({
|
||||
editable: true,
|
||||
analysis: {
|
||||
schema: undefined,
|
||||
tableName: "users",
|
||||
selectStar: true,
|
||||
columns: [],
|
||||
},
|
||||
});
|
||||
executeMulti.mockResolvedValue([{ columns: ["id", "name"], rows: [[1, "Alice"]], affected_rows: 0, execution_time_ms: 1 }]);
|
||||
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab("jdbc-mysql-1", "app", "Query");
|
||||
store.setAutoCommit(tabId, true);
|
||||
|
||||
await store.executeTabSql(tabId, "select * from users");
|
||||
|
||||
expect(getColumns).toHaveBeenCalledWith("jdbc-mysql-1", "app", "", "users", undefined);
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
await vi.waitFor(() => expect(tab.tableMeta?.tableName).toBe("users"));
|
||||
expect(tab.tableMeta?.schema).toBeUndefined();
|
||||
expect(tab.queryEditabilityReason).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses the configured current schema to keep an unqualified Oracle base-table query editable", async () => {
|
||||
getConnectionConfig.mockReturnValue({ id: "oracle-1", name: "Oracle", db_type: "oracle", database: "ORCL", default_schema: "APP", query_timeout_secs: 30 });
|
||||
getColumns.mockResolvedValue([
|
||||
|
||||
@@ -4684,6 +4684,12 @@ export const useQueryStore = defineStore("query", () => {
|
||||
}
|
||||
|
||||
function resolveEditableSourceMetadataTarget(tab: QueryTab, analysis: EditableQueryInfo, source: EditableQuerySource, conn: ConnectionConfig | undefined, dbType: string, executionDatabase: string): EditableSourceMetadataTarget {
|
||||
// Oracle-family metadata rules (schema-less resolution + uppercase folding)
|
||||
// must follow the connection's effective database type. Callers built from
|
||||
// the raw connection db_type pass "jdbc" for a JDBC Oracle connection,
|
||||
// which would keep the service-name schema fallback and the query's
|
||||
// lowercase table spelling, so the write targets a non-existent table.
|
||||
const metadataDbType = effectiveDatabaseTypeForConnection(conn) || dbType;
|
||||
// Metadata must resolve in the same namespace as the query execution. An
|
||||
// empty query-tab database still executes in the connection's default DB,
|
||||
// while database-tree dialects and SQL Server 3-part names may override it
|
||||
@@ -4707,10 +4713,10 @@ export const useQueryStore = defineStore("query", () => {
|
||||
// connection's search_path. Keep the metadata request unqualified when no
|
||||
// schema was selected instead of assuming public (or the database name).
|
||||
const useCurrentPostgresSchema = (dbType === "postgres" || dbType === "kwdb") && !source.schema && !tab.schema;
|
||||
const resolvedSchema = (dbType === "sqlserver" && !source.schema) || (ORACLE_LIKE_METADATA_TYPES.has(dbType) && !schema) || resolveAgentSearchPathSchema || useCurrentPostgresSchema ? "" : metadataSchemaForConnection(conn, metadataDatabase, schema || undefined);
|
||||
const metadataSchema = normalizeUppercaseFoldedMetadataIdentifier(dbType, resolvedSchema || undefined, source.schema ? source.schemaQuoted : false) || "";
|
||||
const metadataTableName = normalizeUppercaseFoldedMetadataIdentifier(dbType, source.tableName, source.tableNameQuoted)!;
|
||||
const metadataCatalog = normalizeUppercaseFoldedMetadataIdentifier(dbType, source.catalog, source.catalogQuoted);
|
||||
const resolvedSchema = (dbType === "sqlserver" && !source.schema) || (ORACLE_LIKE_METADATA_TYPES.has(metadataDbType) && !schema) || resolveAgentSearchPathSchema || useCurrentPostgresSchema ? "" : metadataSchemaForConnection(conn, metadataDatabase, schema || undefined);
|
||||
const metadataSchema = normalizeUppercaseFoldedMetadataIdentifier(metadataDbType, resolvedSchema || undefined, source.schema ? source.schemaQuoted : false) || "";
|
||||
const metadataTableName = normalizeUppercaseFoldedMetadataIdentifier(metadataDbType, source.tableName, source.tableNameQuoted)!;
|
||||
const metadataCatalog = normalizeUppercaseFoldedMetadataIdentifier(metadataDbType, source.catalog, source.catalogQuoted);
|
||||
const metadataSource: EditableQuerySource = {
|
||||
...source,
|
||||
catalog: metadataCatalog,
|
||||
@@ -4723,7 +4729,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const knownTableType = tab.tableMeta?.tableName.toLowerCase() === metadataTableName.toLowerCase() && normalizeOptionalSchema(tab.tableMeta.schema) === normalizeOptionalSchema(metadataSchema) ? tab.tableMeta.tableType : undefined;
|
||||
return {
|
||||
source: metadataSource,
|
||||
analysis: normalizeUppercaseFoldedQueryAnalysis(dbType, cloneAnalysisForSource(analysis, metadataSource), metadataSchema || undefined, metadataTableName),
|
||||
analysis: normalizeUppercaseFoldedQueryAnalysis(metadataDbType, cloneAnalysisForSource(analysis, metadataSource), metadataSchema || undefined, metadataTableName),
|
||||
writeSchema,
|
||||
request: {
|
||||
connectionId: tab.connectionId!,
|
||||
|
||||
Reference in new issue
Block a user