fix(sql): restore SQL file schema for sidebar locate
This commit is contained in:
1 parent
440c41f5b9
commit
e9a90173a6
11 files changed
+175
-53
No files matched your search
@@ -1260,7 +1260,7 @@ async function writeExternalSqlTab(tab: QueryTab, options: { closeAfterSave?: bo
|
||||
expectedMissing: options.expectedMissing,
|
||||
});
|
||||
if (result.kind !== "written") return "retry";
|
||||
rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database: tab.database, catalog: tab.catalog });
|
||||
rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database: tab.database, catalog: tab.catalog, schema: tab.schema });
|
||||
queryStore.markExternalSqlFileSaved(tab.id, result.version);
|
||||
toast(t("savedSql.saved"), 2000);
|
||||
if (options.closeAfterSave) queryStore.closeTab(tab.id, { force: true });
|
||||
@@ -1593,7 +1593,7 @@ async function saveExternalSqlTabAs(tab: QueryTab): Promise<boolean> {
|
||||
const saved = await api.saveExternalSqlFile(defaultSavedSqlName(tab.title), await formattedSqlForSave(tab));
|
||||
if (!saved) return false;
|
||||
queryStore.linkExternalSqlPath(tab.id, saved.path, sqlFileTitleFromPath(saved.path), saved.version);
|
||||
rememberExternalSqlFileTarget(saved.path, { connectionId: tab.connectionId, database: tab.database, catalog: tab.catalog });
|
||||
rememberExternalSqlFileTarget(saved.path, { connectionId: tab.connectionId, database: tab.database, catalog: tab.catalog, schema: tab.schema });
|
||||
invalidateSaveSqlFolderSelection();
|
||||
showSaveSqlDialog.value = false;
|
||||
closePendingSavedTab();
|
||||
@@ -1619,6 +1619,9 @@ function applyExternalSqlFileTarget(tab: QueryTab, path: string) {
|
||||
if (target.catalog !== undefined || tab.catalog !== undefined) queryStore.updateCatalog(tab.id, target.catalog, target.database);
|
||||
else queryStore.updateDatabase(tab.id, target.database);
|
||||
}
|
||||
// updateConnection/updateCatalog/updateDatabase all reset the schema, so the
|
||||
// remembered schema has to be reapplied after them.
|
||||
if (target.schema !== tab.schema) queryStore.updateSchema(tab.id, target.schema);
|
||||
}
|
||||
|
||||
async function openSqlFile() {
|
||||
@@ -1689,7 +1692,7 @@ async function openSqlFilePath(path: string) {
|
||||
await desktopOpenTabsRestorationBarrier?.settled;
|
||||
const snapshot = await api.readExternalSqlFileSnapshot(path);
|
||||
const target = resolveExternalSqlFileTarget(path, (savedConnectionId) => !!connectionStore.getConfig(savedConnectionId), unassociatedExternalSqlFileTarget());
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, snapshot.content, snapshot.version, target.catalog);
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, snapshot.content, snapshot.version, target.catalog, target.schema);
|
||||
} catch (e: any) {
|
||||
toast(t("toolbar.sqlOpenFailed", { message: externalSqlFileOpenErrorMessage(e, (key, params) => t(key, params)) }), 5000);
|
||||
}
|
||||
@@ -2146,14 +2149,14 @@ async function changeActiveConnection(connectionId: string) {
|
||||
if (!connection) return;
|
||||
const initialDatabase = resolveDefaultDatabase(connection, []);
|
||||
queryStore.updateConnection(tab.id, connectionId, initialDatabase);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId, database: initialDatabase, catalog: undefined });
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId, database: initialDatabase, catalog: undefined, schema: undefined });
|
||||
connectionStore.activeConnectionId = connectionId;
|
||||
try {
|
||||
await connectionStore.ensureConnected(connectionId);
|
||||
const options = await getDatabaseOptions(connectionId);
|
||||
const database = resolveDefaultDatabase(connection, options);
|
||||
queryStore.updateDatabase(tab.id, database);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId, database, catalog: undefined });
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId, database, catalog: undefined, schema: undefined });
|
||||
if (connection.default_schema || connection.db_type === "oracle") {
|
||||
try {
|
||||
// A configured default wins. Otherwise Oracle returns the session's current schema first.
|
||||
@@ -2161,6 +2164,7 @@ async function changeActiveConnection(connectionId: string) {
|
||||
const schema = schemaAfterConnectionSwitch(connection.db_type, orderedSchemas, connection.default_schema);
|
||||
if (schema && activeTab.value?.id === tab.id && activeTab.value.connectionId === connectionId) {
|
||||
queryStore.updateSchema(tab.id, schema);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId, database, catalog: undefined, schema });
|
||||
}
|
||||
} catch {
|
||||
// Schema metadata failure must not turn a successful connection switch into a connection error.
|
||||
@@ -2180,7 +2184,7 @@ function changeActiveDatabase(database: string) {
|
||||
const tab = activeTab.value;
|
||||
if (tab) {
|
||||
queryStore.updateDatabase(tab.id, database);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database, catalog: tab.catalog });
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database, catalog: tab.catalog, schema: tab.schema });
|
||||
if (databaseRequiredTabId.value === tab.id && database) {
|
||||
databaseRequiredTabId.value = null;
|
||||
}
|
||||
@@ -2191,7 +2195,7 @@ function changeActiveCatalog(catalog: string | undefined, database: string) {
|
||||
const tab = activeTab.value;
|
||||
if (tab) {
|
||||
queryStore.updateCatalog(tab.id, catalog, database);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database, catalog });
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database, catalog, schema: tab.schema });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2209,7 +2213,9 @@ async function clearActiveDefaultDatabase() {
|
||||
|
||||
function changeActiveSchema(schema: string | undefined) {
|
||||
const tab = activeTab.value;
|
||||
if (tab) queryStore.updateSchema(tab.id, schema);
|
||||
if (!tab) return;
|
||||
queryStore.updateSchema(tab.id, schema);
|
||||
if (tab.externalSqlPath) rememberExternalSqlFileTarget(tab.externalSqlPath, { connectionId: tab.connectionId, database: tab.database, catalog: tab.catalog, schema });
|
||||
}
|
||||
|
||||
function openGitHub() {
|
||||
@@ -2332,7 +2338,7 @@ async function handleQuickOpenSelect(item: any) {
|
||||
try {
|
||||
const snapshot = await api.readExternalSqlFileSnapshot(item.filePath);
|
||||
const target = resolveExternalSqlFileTarget(item.filePath, (savedConnectionId) => !!connectionStore.getConfig(savedConnectionId), unassociatedExternalSqlFileTarget());
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, item.filePath, snapshot.content, snapshot.version, target.catalog);
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, item.filePath, snapshot.content, snapshot.version, target.catalog, target.schema);
|
||||
} catch (e: any) {
|
||||
toast(
|
||||
externalSqlFileOpenErrorMessage(e, (key, params) => t(key, params)),
|
||||
|
||||
@@ -255,7 +255,7 @@ async function openFile(path: string) {
|
||||
try {
|
||||
const snapshot = await api.readExternalSqlFileSnapshot(path);
|
||||
const target = resolveExternalSqlFileTarget(path, (savedConnectionId) => !!connectionStore.getConfig(savedConnectionId), unassociatedExternalSqlFileTarget());
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, snapshot.content, snapshot.version, target.catalog);
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, snapshot.content, snapshot.version, target.catalog, target.schema);
|
||||
} catch (e: any) {
|
||||
if (isExternalSqlFileTooLargeError(e)) {
|
||||
executeFile(path);
|
||||
|
||||
@@ -27,7 +27,7 @@ export function useFileDrop() {
|
||||
async function openDroppedSqlFile(name: string, content: string, path?: string, version?: ExternalSqlFileVersion) {
|
||||
if (path) {
|
||||
const target = resolveExternalSqlFileTarget(path, (savedConnectionId) => !!connectionStore.getConfig(savedConnectionId), unassociatedExternalSqlFileTarget());
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, content, version, target.catalog);
|
||||
queryStore.openExternalSqlFile(target.connectionId, target.database, path, content, version, target.catalog, target.schema);
|
||||
} else {
|
||||
const connectionId = connectionStore.activeConnectionId || connectionStore.connections[0]?.id || "";
|
||||
const connection = connectionId ? connectionStore.getConfig(connectionId) : undefined;
|
||||
|
||||
@@ -427,6 +427,41 @@ describe("object tree node schema", () => {
|
||||
expect(metadataSchemaForConnection(connection, resourcePath, resourcePath)).toBe("");
|
||||
});
|
||||
|
||||
it("never uses the database name as the schema when a schema-tree engine has no schema yet", () => {
|
||||
// Query tabs can reach locate/metadata paths before a schema is picked (a
|
||||
// toolbar "new query" tab, or an external .sql file reopened from disk).
|
||||
// PostgreSQL and its relatives keep tables under a separate schema level, so
|
||||
// `schema || database` sends "dbx_test" as the schema and matches nothing —
|
||||
// locate in the sidebar silently does nothing (issue #7648). The blank schema
|
||||
// lets the backend resolve the session default instead.
|
||||
for (const dbType of ["postgres", "gaussdb", "opengauss", "kingbase", "redshift", "duckdb"] as const) {
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: dbType }, "dbx_test")).toBe("");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: dbType }, "dbx_test")).toBeUndefined();
|
||||
expect(metadataSchemaForConnection({ db_type: dbType }, "dbx_test")).toBe("");
|
||||
// An explicit schema still wins, and the database name is a legitimate
|
||||
// schema name when the user really did select it.
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: dbType }, "dbx_test", "public")).toBe("public");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: dbType }, "dbx_test", "public")).toBe("public");
|
||||
}
|
||||
// SQL Server keeps its own dbo default for metadata lookups.
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: "sqlserver" }, "dbx_test")).toBe("");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "sqlserver" }, "dbx_test")).toBeUndefined();
|
||||
expect(metadataSchemaForConnection({ db_type: "sqlserver" }, "dbx_test")).toBe("dbo");
|
||||
});
|
||||
|
||||
it("keeps the database-as-schema fallback for engines whose database is the schema", () => {
|
||||
// Oracle, Dameng and the Hive family address objects as schema.table with no
|
||||
// separate schema level in the tree, so the database name is the schema there
|
||||
// and must survive the fix above.
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "oracle" }, "DBX_TEST")).toBe("DBX_TEST");
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: "oracle" }, "DBX_TEST")).toBe("DBX_TEST");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "dameng" }, "DBX_TEST")).toBe("DBX_TEST");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "hive" }, "CS")).toBe("CS");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "spark" }, "CS")).toBe("CS");
|
||||
// Flat engines keep returning no tree schema at all.
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "mysql" }, "shop")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps the database-as-schema fallback for flat engines and blanks it for Cloud Spanner", () => {
|
||||
// Editor completion paths send the database name as the metadata schema when a
|
||||
// connection has no schema level; only Spanner must send a blank schema instead.
|
||||
|
||||
@@ -26,6 +26,26 @@ describe("external SQL file targets", () => {
|
||||
expect(resolveExternalSqlFileTarget("/work/iceberg.sql", () => true, unassociatedExternalSqlFileTarget()).catalog).toBe("iceberg");
|
||||
});
|
||||
|
||||
it("restores the schema the file was saved from", () => {
|
||||
// Reopening a .sql file used to drop the schema, leaving the tab on the
|
||||
// connection default: sidebar locate then looked for the table in the wrong
|
||||
// namespace and silently did nothing (issue #7648).
|
||||
rememberExternalSqlFileTarget("/work/report.sql", { connectionId: "saved-connection", database: "dbx_test", schema: "analytics" });
|
||||
|
||||
expect(resolveExternalSqlFileTarget("/work/report.sql", () => true, unassociatedExternalSqlFileTarget())).toEqual({
|
||||
connectionId: "saved-connection",
|
||||
database: "dbx_test",
|
||||
catalog: undefined,
|
||||
schema: "analytics",
|
||||
});
|
||||
});
|
||||
|
||||
it("treats historical records without a schema as unset", () => {
|
||||
localStorage.setItem(EXTERNAL_SQL_FILE_TARGETS_STORAGE_KEY, JSON.stringify([{ path: "/work/legacy.sql", connectionId: "saved-connection", database: "sales", updatedAt: 1 }]));
|
||||
|
||||
expect(resolveExternalSqlFileTarget("/work/legacy.sql", () => true, unassociatedExternalSqlFileTarget()).schema).toBeUndefined();
|
||||
});
|
||||
|
||||
it("treats historical records without catalog as the default catalog", () => {
|
||||
localStorage.setItem(EXTERNAL_SQL_FILE_TARGETS_STORAGE_KEY, JSON.stringify([{ path: "/work/legacy.sql", connectionId: "saved-connection", database: "sales", updatedAt: 1 }]));
|
||||
|
||||
|
||||
@@ -237,12 +237,28 @@ export function connectionQueryExecutionSchema(connection: JdbcDialectConnection
|
||||
return type && DATABASE_AS_EXECUTION_SCHEMA_TYPES.has(type) ? database || undefined : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the database name is a wrong guess for an unknown schema. Engines that keep tables under a
|
||||
* dedicated schema level in the tree (PostgreSQL and relatives, SQL Server, DB2, Trino, generic JDBC)
|
||||
* address objects as database.schema.table, so the database name matches no schema there. Oracle,
|
||||
* Dameng and the Hive family are schema-aware without that level — their database *is* the schema —
|
||||
* and must keep the `schema || database` fallback.
|
||||
*/
|
||||
function databaseNameIsNotASchema(type: DatabaseType | undefined): boolean {
|
||||
return isSchemaAware(type) && usesTreeSchemaMode(type);
|
||||
}
|
||||
|
||||
export function connectionObjectTreeQuerySchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string {
|
||||
if (connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend") return schema || database;
|
||||
if (connectionUsesDatabaseObjectTreeMode(connection)) return "";
|
||||
const type = effectiveDatabaseTypeForConnection(connection);
|
||||
if (type === "informix") return schema || "";
|
||||
if (type === "spanner") return spannerObjectTreeSchema(schema);
|
||||
// A query tab that never picked a schema (toolbar "new query", a reopened .sql
|
||||
// file) would otherwise send the database name and match no objects at all, so
|
||||
// sidebar locate silently did nothing (issue #7648). The blank schema is the
|
||||
// established "resolve it on the backend" value used by the completion paths.
|
||||
if (!schema && databaseNameIsNotASchema(type)) return "";
|
||||
return schema || database;
|
||||
}
|
||||
|
||||
@@ -272,6 +288,7 @@ export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection
|
||||
if (type === "sqlite") return schema || database;
|
||||
if (type === "spanner") return spannerObjectTreeSchema(schema);
|
||||
if (!type) return schema;
|
||||
if (!schema && databaseNameIsNotASchema(type)) return undefined;
|
||||
return isSchemaAware(type) ? schema || database : undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,5 +7,6 @@ export function savedSqlImportTarget(sourceTarget: ExternalSqlFileTarget, folder
|
||||
connectionId: folder.connectionId,
|
||||
database: sourceTarget.connectionId === folder.connectionId ? sourceTarget.database : "",
|
||||
catalog: sourceTarget.connectionId === folder.connectionId ? sourceTarget.catalog : undefined,
|
||||
schema: sourceTarget.connectionId === folder.connectionId ? sourceTarget.schema : undefined,
|
||||
};
|
||||
}
|
||||
@@ -7,10 +7,11 @@ export interface ExternalSqlFileTarget {
|
||||
connectionId: string;
|
||||
database: string;
|
||||
catalog?: string;
|
||||
schema?: string;
|
||||
}
|
||||
|
||||
export function unassociatedExternalSqlFileTarget(): ExternalSqlFileTarget {
|
||||
return { connectionId: "", database: "", catalog: undefined };
|
||||
return { connectionId: "", database: "", catalog: undefined, schema: undefined };
|
||||
}
|
||||
|
||||
interface StoredExternalSqlFileTarget extends ExternalSqlFileTarget {
|
||||
@@ -23,8 +24,18 @@ function loadExternalSqlFileTargets(): StoredExternalSqlFileTarget[] {
|
||||
const parsed = JSON.parse(localStorage.getItem(EXTERNAL_SQL_FILE_TARGETS_STORAGE_KEY) || "[]");
|
||||
if (!Array.isArray(parsed)) return [];
|
||||
return parsed
|
||||
.filter((item) => typeof item?.path === "string" && typeof item?.connectionId === "string" && typeof item?.database === "string" && (item?.catalog === undefined || typeof item.catalog === "string") && typeof item?.updatedAt === "number")
|
||||
.map((item) => ({ ...item, catalog: item.catalog })) as StoredExternalSqlFileTarget[];
|
||||
.filter(
|
||||
(item) =>
|
||||
typeof item?.path === "string" &&
|
||||
typeof item?.connectionId === "string" &&
|
||||
typeof item?.database === "string" &&
|
||||
(item?.catalog === undefined || typeof item.catalog === "string") &&
|
||||
// Targets written before schemas were remembered carry no schema key at
|
||||
// all, so an absent schema stays valid and simply restores as undefined.
|
||||
(item?.schema === undefined || typeof item.schema === "string") &&
|
||||
typeof item?.updatedAt === "number",
|
||||
)
|
||||
.map((item) => ({ ...item, catalog: item.catalog, schema: item.schema })) as StoredExternalSqlFileTarget[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -71,5 +82,5 @@ export function resolveExternalSqlFileTarget(path: string, connectionExists: (co
|
||||
const normalizedPath = normalizeExternalSqlPath(path);
|
||||
const saved = loadExternalSqlFileTargets().find((item) => item.path === normalizedPath);
|
||||
if (!saved || !connectionExists(saved.connectionId)) return fallback;
|
||||
return { connectionId: saved.connectionId, database: saved.database, catalog: saved.catalog };
|
||||
return { connectionId: saved.connectionId, database: saved.database, catalog: saved.catalog, schema: saved.schema };
|
||||
}
|
||||
@@ -5955,44 +5955,61 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
async () => {
|
||||
await ensureConnected(target.connectionId);
|
||||
|
||||
const querySchema = connectionObjectTreeQuerySchema(config, target.database, target.schema);
|
||||
const effectiveSchema = connectionObjectTreeNodeSchema(config, target.database, target.schema);
|
||||
const pageSize = sidebarObjectGroupPageSize();
|
||||
const simpleObjectDisplay = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
|
||||
// A query tab can reach locate without a schema (a toolbar "new query" tab,
|
||||
// or a reopened .sql file), and on a schema tree the table then lives under
|
||||
// one of the database's schema nodes. Resolve the schema per node instead of
|
||||
// guessing a single one for the whole database: the old `schema || database`
|
||||
// guess matched nothing at all (issue #7648), and a single resolved schema
|
||||
// would merge one schema's tables into every other schema's node.
|
||||
const schemaForNode = (nodeSchema?: string) => target.schema ?? nodeSchema;
|
||||
let loaded = false;
|
||||
|
||||
if (simpleObjectDisplay) {
|
||||
const parentId = target.schema ? `${target.connectionId}:${target.database}:${target.schema}` : `${target.connectionId}:${target.database}`;
|
||||
const parent = findNode(treeNodes.value, parentId);
|
||||
if (!parent) return false;
|
||||
let load = beginTreeNodeLoad(parent);
|
||||
try {
|
||||
load = reclaimTreeNodeLoad(load, parent);
|
||||
const page = await loadPagedSimpleTableChildren({
|
||||
nodeId: parentId,
|
||||
connectionId: target.connectionId,
|
||||
database: target.database,
|
||||
querySchema,
|
||||
effectiveSchema,
|
||||
nonTableObjectTypes: [],
|
||||
offset: 0,
|
||||
pageSize,
|
||||
searchFilter: target.tableName,
|
||||
force: false,
|
||||
});
|
||||
if (!page.children.length) return false;
|
||||
const targetParent = treeNodeLoadTarget(load);
|
||||
if (!targetParent) return false;
|
||||
const currentChildren = withoutLoadMoreNodes(targetParent.children);
|
||||
const loadMoreNodes = (targetParent.children || []).filter((child) => child.type === "load-more");
|
||||
const mergedChildren = mergeLocatedTreeChildren(targetParent, currentChildren, page.children, target.connectionId, target.database);
|
||||
setChildren(targetParent, [...mergedChildren, ...loadMoreNodes]);
|
||||
targetParent.objectCount = Math.max(targetParent.objectCount ?? currentChildren.length, mergedChildren.length);
|
||||
targetParent.isExpanded = true;
|
||||
return true;
|
||||
} finally {
|
||||
finishTreeNodeLoad(load);
|
||||
const schemaParents = target.schema
|
||||
? [findNode(treeNodes.value, `${target.connectionId}:${target.database}:${target.schema}`)]
|
||||
: findTreeNodes(treeNodes.value, (node) => node.type === "schema" && node.connectionId === target.connectionId && sameSidebarObjectName(node.database, target.database));
|
||||
const parents = schemaParents.filter((node): node is TreeNode => !!node);
|
||||
if (!parents.length) {
|
||||
const databaseParent = target.schema ? null : findNode(treeNodes.value, `${target.connectionId}:${target.database}`);
|
||||
if (databaseParent) parents.push(databaseParent);
|
||||
}
|
||||
if (!parents.length) return false;
|
||||
|
||||
for (const parent of parents) {
|
||||
const parentSchema = schemaForNode(parent.type === "schema" ? (parent.schema ?? parent.label) : undefined);
|
||||
let load = beginTreeNodeLoad(parent);
|
||||
try {
|
||||
load = reclaimTreeNodeLoad(load, parent);
|
||||
const page = await loadPagedSimpleTableChildren({
|
||||
nodeId: parent.id,
|
||||
connectionId: target.connectionId,
|
||||
database: target.database,
|
||||
querySchema: connectionObjectTreeQuerySchema(config, target.database, parentSchema),
|
||||
effectiveSchema: connectionObjectTreeNodeSchema(config, target.database, parentSchema),
|
||||
nonTableObjectTypes: [],
|
||||
offset: 0,
|
||||
pageSize,
|
||||
searchFilter: target.tableName,
|
||||
force: false,
|
||||
});
|
||||
if (!page.children.length) continue;
|
||||
const targetParent = treeNodeLoadTarget(load);
|
||||
if (!targetParent) continue;
|
||||
const currentChildren = withoutLoadMoreNodes(targetParent.children);
|
||||
const loadMoreNodes = (targetParent.children || []).filter((child) => child.type === "load-more");
|
||||
const mergedChildren = mergeLocatedTreeChildren(targetParent, currentChildren, page.children, target.connectionId, target.database);
|
||||
setChildren(targetParent, [...mergedChildren, ...loadMoreNodes]);
|
||||
targetParent.objectCount = Math.max(targetParent.objectCount ?? currentChildren.length, mergedChildren.length);
|
||||
targetParent.isExpanded = true;
|
||||
loaded = true;
|
||||
} finally {
|
||||
finishTreeNodeLoad(load);
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
const matchingGroups = findTreeNodes(treeNodes.value, (node) => {
|
||||
@@ -6012,11 +6029,12 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
let load = beginTreeNodeLoad(group);
|
||||
try {
|
||||
load = reclaimTreeNodeLoad(load, group);
|
||||
const groupSchema = schemaForNode(group.schema);
|
||||
const page = await loadPagedTableGroupChildren({
|
||||
node: group,
|
||||
parentNodeId,
|
||||
querySchema,
|
||||
effectiveSchema,
|
||||
querySchema: connectionObjectTreeQuerySchema(config, group.database || target.database, groupSchema),
|
||||
effectiveSchema: connectionObjectTreeNodeSchema(config, group.database || target.database, groupSchema),
|
||||
objectTypes,
|
||||
offset: 0,
|
||||
pageSize,
|
||||
|
||||
@@ -1920,7 +1920,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
});
|
||||
}
|
||||
|
||||
function openExternalSqlFile(connectionId: string, database: string, path: string, sql: string, version?: QueryTab["externalSqlFileVersion"], catalog?: string) {
|
||||
function openExternalSqlFile(connectionId: string, database: string, path: string, sql: string, version?: QueryTab["externalSqlFileVersion"], catalog?: string, schema?: string) {
|
||||
const normalizedPath = normalizeExternalSqlPath(path);
|
||||
const existing = tabs.value.find((tab) => tab.mode === "query" && tab.externalSqlPath && normalizeExternalSqlPath(tab.externalSqlPath) === normalizedPath);
|
||||
if (existing) {
|
||||
@@ -1939,6 +1939,10 @@ export const useQueryStore = defineStore("query", () => {
|
||||
connectionId,
|
||||
database,
|
||||
catalog,
|
||||
// Restoring the schema keeps the reopened file on the namespace it was
|
||||
// saved from. Without it the tab has no schema, so sidebar locate and the
|
||||
// metadata paths fall back to the connection default (issue #7648).
|
||||
schema,
|
||||
sql,
|
||||
originalSql: sql,
|
||||
externalSqlPath: path,
|
||||
|
||||
Reference in new issue
Block a user