fix(editor): avoid metadata wait for wildcard queries
This commit is contained in:
parent
e381aa4ee1
commit
a8592af97a
|
|
@ -131,6 +131,44 @@ describe("queryStore hidden primary key editing", () => {
|
|||
expect(tab.queryEditabilityReason).toBeUndefined();
|
||||
});
|
||||
|
||||
it("starts a qualified MySQL star query before slow column metadata finishes", async () => {
|
||||
const columnsGate = deferred<Awaited<ReturnType<typeof getColumns>>>();
|
||||
getColumns.mockReturnValue(columnsGate.promise);
|
||||
analyzeEditableQueryEditability.mockResolvedValue({
|
||||
editable: true,
|
||||
analysis: {
|
||||
schema: undefined,
|
||||
tableName: "sys_dept",
|
||||
selectStar: true,
|
||||
columns: [],
|
||||
},
|
||||
});
|
||||
executeMulti.mockResolvedValue([
|
||||
{
|
||||
columns: ["dept_id", "dept_name"],
|
||||
rows: [[1, "Headquarters"]],
|
||||
affected_rows: 0,
|
||||
execution_time_ms: 12,
|
||||
},
|
||||
]);
|
||||
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab("mysql-1", "app", "Query");
|
||||
|
||||
const execution = store.executeTabSql(tabId, "SELECT sys_dept.* FROM sys_dept");
|
||||
await vi.waitFor(() => expect(executeMulti).toHaveBeenCalled());
|
||||
expect(executeMulti).toHaveBeenCalledWith("mysql-1", "app", "SELECT sys_dept.* FROM sys_dept", undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 }));
|
||||
|
||||
columnsGate.resolve([
|
||||
{ name: "dept_id", data_type: "bigint", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
|
||||
{ name: "dept_name", data_type: "varchar", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
|
||||
]);
|
||||
await execution;
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
await vi.waitFor(() => expect(tab.tableMeta?.tableName).toBe("sys_dept"));
|
||||
});
|
||||
|
||||
it("loads metadata from the connection default database when the query tab database is empty", async () => {
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
|
|
@ -208,6 +246,45 @@ describe("queryStore hidden primary key editing", () => {
|
|||
expect(tab.queryEditabilityReason).toBeUndefined();
|
||||
});
|
||||
|
||||
it("starts a MySQL JDBC star query before slow column metadata finishes", async () => {
|
||||
const columnsGate = deferred<Awaited<ReturnType<typeof getColumns>>>();
|
||||
getConnectionConfig.mockReturnValue({ id: "jdbc-1", name: "JDBC MySQL", db_type: "jdbc", connection_string: "jdbc:mysql://localhost:3306/app", database: "app", query_timeout_secs: 30 });
|
||||
getColumns.mockReturnValue(columnsGate.promise);
|
||||
analyzeEditableQueryEditability.mockResolvedValue({
|
||||
editable: true,
|
||||
analysis: {
|
||||
schema: undefined,
|
||||
tableName: "sys_dept",
|
||||
selectStar: true,
|
||||
columns: [],
|
||||
},
|
||||
});
|
||||
executeMulti.mockResolvedValue([
|
||||
{
|
||||
columns: ["dept_id", "dept_name"],
|
||||
rows: [[1, "Headquarters"]],
|
||||
affected_rows: 0,
|
||||
execution_time_ms: 12,
|
||||
},
|
||||
]);
|
||||
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
const tabId = store.createTab("jdbc-1", "", "Query");
|
||||
|
||||
const execution = store.executeTabSql(tabId, "SELECT * FROM sys_dept");
|
||||
await vi.waitFor(() => expect(executeMulti).toHaveBeenCalled());
|
||||
expect(executeMulti).toHaveBeenCalledWith("jdbc-1", "", "SELECT * FROM sys_dept", undefined, expect.any(String), expect.objectContaining({ timeoutSecs: 30 }));
|
||||
|
||||
columnsGate.resolve([
|
||||
{ name: "dept_id", data_type: "bigint", is_nullable: false, column_default: null, is_primary_key: true, extra: null },
|
||||
{ name: "dept_name", data_type: "varchar", is_nullable: true, column_default: null, is_primary_key: false, extra: null },
|
||||
]);
|
||||
await execution;
|
||||
const tab = store.tabs.find((item) => item.id === tabId)!;
|
||||
await vi.waitFor(() => expect(tab.tableMeta?.tableName).toBe("sys_dept"));
|
||||
});
|
||||
|
||||
it("keeps an explicitly selected database instead of falling back to the connection default", async () => {
|
||||
const { useQueryStore } = await import("@/stores/queryStore");
|
||||
const store = useQueryStore();
|
||||
|
|
|
|||
|
|
@ -339,6 +339,10 @@ function editableQuerySources(analysis: EditableQueryInfo): EditableQuerySource[
|
|||
];
|
||||
}
|
||||
|
||||
function projectsAllColumnsForSource(analysis: EditableQueryInfo, sourceKey: string): boolean {
|
||||
return analysis.selectStar || analysis.columns.some((column) => column.star && (!column.sourceKey || column.sourceKey === sourceKey));
|
||||
}
|
||||
|
||||
function cloneAnalysisForSource(analysis: EditableQueryInfo, source: EditableQuerySource): EditableQueryInfo {
|
||||
return {
|
||||
...analysis,
|
||||
|
|
@ -2729,6 +2733,9 @@ export const useQueryStore = defineStore("query", () => {
|
|||
const analysis = editability.analysis;
|
||||
const sources = editableQuerySources(analysis);
|
||||
if (sources.length !== 1 || analysis.distinct) return unchanged;
|
||||
// Whole-source projections already include declared primary keys. Only
|
||||
// Oracle needs preflight metadata here to add ROWID for a keyless table.
|
||||
if (databaseType !== "oracle" && projectsAllColumnsForSource(analysis, sources[0]!.key)) return unchanged;
|
||||
|
||||
const target = resolveEditableSourceMetadataTarget(tab, analysis, sources[0]!, conn, databaseType, executionDatabase);
|
||||
const cached = getCachedTableMetadata(target.request);
|
||||
|
|
@ -2743,8 +2750,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||
});
|
||||
void fullMetadataPromise.catch((error) => queryExecutionLog("warn", "metadata:table-prefetch:failed", { traceId, error, elapsed: elapsed() }));
|
||||
const indexes = await loadTableIndexes(target.request);
|
||||
const projectsAllColumns = target.analysis.selectStar || target.analysis.columns.some((column) => column.star && (!column.sourceKey || column.sourceKey === target.source.key));
|
||||
if (primaryKeyIndex(indexes) && projectsAllColumns) {
|
||||
if (primaryKeyIndex(indexes) && projectsAllColumnsForSource(target.analysis, target.source.key)) {
|
||||
return unchanged;
|
||||
}
|
||||
loaded = loadedEditableSourceFromMetadata(target, (await fullMetadataPromise).metadata);
|
||||
|
|
|
|||
Loading…
Reference in New Issue