diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue
index 525024553..40486acce 100644
--- a/apps/desktop/src/App.vue
+++ b/apps/desktop/src/App.vue
@@ -1525,9 +1525,14 @@ function changeActiveDatabase(database: string) {
}
}
+function changeActiveCatalog(catalog: string | undefined, database: string) {
+ const tab = activeTab.value;
+ if (tab) queryStore.updateCatalog(tab.id, catalog, database);
+}
+
async function setActiveDatabaseAsDefault() {
const tab = activeTab.value;
- if (!tab || !tab.connectionId || !tab.database) return;
+ if (!tab || !tab.connectionId || !tab.database || tab.catalog) return;
await connectionStore.setDefaultDatabase(tab.connectionId, tab.database);
}
@@ -1557,9 +1562,11 @@ function ensureQueryTab(): string {
const tab = activeTab.value;
if (tab && tab.mode === "query") return tab.id;
const connId = connectionStore.activeConnectionId || connectionStore.connections[0]?.id || "";
- const db = tab?.connectionId === connId ? tab.database : connectionStore.getConfig(connId)?.database || "";
- const schema = tab?.connectionId === connId ? tab.schema : undefined;
- return queryStore.createTab(connId, db, undefined, "query", schema);
+ const sameConnectionTab = tab?.connectionId === connId ? tab : undefined;
+ const db = sameConnectionTab?.database || connectionStore.getConfig(connId)?.database || "";
+ const schema = sameConnectionTab?.schema ?? sameConnectionTab?.objectBrowser?.schema ?? sameConnectionTab?.tableMeta?.schema;
+ const catalog = sameConnectionTab?.catalog ?? sameConnectionTab?.objectBrowser?.catalog ?? sameConnectionTab?.tableMeta?.catalog;
+ return queryStore.createTab(connId, db, undefined, "query", schema, undefined, catalog);
}
function routeAiRedisCommand(command: string, execute: boolean): boolean {
@@ -2263,6 +2270,7 @@ onUnmounted(() => {
@paste-sql-in-condition="pasteClipboardAsSqlInCondition"
@change-connection="changeActiveConnection"
@change-database="changeActiveDatabase"
+ @change-catalog="changeActiveCatalog"
@change-schema="changeActiveSchema"
@set-default-database="setActiveDatabaseAsDefault"
@clear-default-database="clearActiveDefaultDatabase"
diff --git a/apps/desktop/src/components/layout/EditorToolbar.vue b/apps/desktop/src/components/layout/EditorToolbar.vue
index ead153066..8d93fb951 100644
--- a/apps/desktop/src/components/layout/EditorToolbar.vue
+++ b/apps/desktop/src/components/layout/EditorToolbar.vue
@@ -9,13 +9,14 @@ import TruncatedTextTooltip from "@/components/ui/TruncatedTextTooltip.vue";
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import ProductionContextBadge from "@/components/common/ProductionContextBadge.vue";
import { useConnectionStore } from "@/stores/connectionStore";
-import { useDatabaseOptions } from "@/composables/useDatabaseOptions";
+import { catalogDatabaseOptionsKey, databaseAfterCatalogChange, normalizedQueryTabCatalog, queryCatalogSelectorVisible, selectedQueryCatalogName, useDatabaseOptions } from "@/composables/useDatabaseOptions";
import { useSchemaOptions } from "@/composables/useSchemaOptions";
import { connectionIconType } from "@/lib/connection/connectionPresentation";
import { formatDatabaseLabel, isDefaultDatabase } from "@/lib/database/defaultDatabase";
import { connectionDisplayName } from "@/lib/tabs/tabPresentation";
import { useConnectionGroupLabel } from "@/composables/useConnectionGroupLabel";
import { isSingleDatabase, supportsClearableQuerySchema, supportsSqlInListPaste, supportsTransaction as supportsTransactionFeature } from "@/lib/database/databaseCapabilities";
+import { connectionIsDorisFamilyCatalogCapable } from "@/lib/database/databaseFeatureSupport";
import { hexToRgba } from "@/lib/common/color";
import { productionContextForDatabase } from "@/lib/database/productionSafety";
import type { QueryTab, ConnectionConfig } from "@/types/database";
@@ -46,6 +47,7 @@ const emit = defineEmits<{
importResultArchive: [];
pasteSqlInCondition: [];
changeConnection: [connectionId: string];
+ changeCatalog: [catalog: string | undefined, database: string];
changeDatabase: [database: string];
changeSchema: [schema: string | undefined];
setDefaultDatabase: [];
@@ -59,13 +61,30 @@ const emit = defineEmits<{
const { t } = useI18n();
const connectionStore = useConnectionStore();
-const { databaseOptions, loadingDatabaseOptions, loadDatabaseOptions } = useDatabaseOptions();
+const { databaseOptions, loadingDatabaseOptions, loadDatabaseOptions, catalogOptions, loadingCatalogOptions, loadCatalogOptions, catalogDatabaseOptions, loadingCatalogDatabaseOptions, loadCatalogDatabaseOptions } = useDatabaseOptions();
const { loadSchemaOptions, getSchemaOptionsForDb, isLoadingSchemas, isSchemaAware } = useSchemaOptions();
+const activeCatalogs = computed(() => {
+ const connection = props.activeConnection;
+ return connection ? (catalogOptions.value[connection.id] ?? []) : [];
+});
+const activeCatalogNames = computed(() => activeCatalogs.value.map((catalog) => catalog.name));
+const showCatalogSelector = computed(() => connectionIsDorisFamilyCatalogCapable(props.activeConnection) && queryCatalogSelectorVisible(activeCatalogs.value));
+const activeCatalogValue = computed(() => selectedQueryCatalogName(activeCatalogs.value, props.activeTab.catalog));
+const activeCatalogDatabaseKey = computed(() => (props.activeConnection && props.activeTab.catalog ? catalogDatabaseOptionsKey(props.activeConnection.id, props.activeTab.catalog) : ""));
const activeDatabaseOptions = computed(() => {
const connection = props.activeConnection;
- return connection ? (databaseOptions.value[connection.id] ?? []) : [];
+ if (!connection) return [];
+ if (props.activeTab.catalog) return catalogDatabaseOptions.value[activeCatalogDatabaseKey.value] ?? [];
+ return databaseOptions.value[connection.id] ?? [];
});
+const loadingActiveDatabaseOptions = computed(() => {
+ const connection = props.activeConnection;
+ if (!connection) return false;
+ if (props.activeTab.catalog) return loadingCatalogDatabaseOptions.value[activeCatalogDatabaseKey.value] ?? false;
+ return loadingDatabaseOptions.value[connection.id] ?? false;
+});
+const switchingCatalog = ref(false);
const connectionOptionIds = computed(() => connectionStore.connections.map((connection) => connection.id));
const { connectionGroupLabel } = useConnectionGroupLabel();
@@ -135,6 +154,18 @@ watchEffect(() => {
loadSchemaOptions(connection.id, schemaDatabaseKey.value).catch(() => {});
}
});
+watchEffect(() => {
+ const connection = props.activeConnection;
+ if (!connection || !connectionIsDorisFamilyCatalogCapable(connection)) return;
+ void loadCatalogOptions(connection.id).catch(() => {});
+});
+
+watchEffect(() => {
+ const connection = props.activeConnection;
+ const catalog = props.activeTab.catalog;
+ if (!connection || !catalog) return;
+ void loadCatalogDatabaseOptions(connection.id, catalog).catch(() => {});
+});
const isActiveDatabaseDefault = computed(() => isDefaultDatabase(props.activeConnection, activeDatabaseValue.value));
const toolbarStyle = computed(() => {
@@ -161,6 +192,18 @@ function databaseOptionIsProduction(database: string): boolean {
if (!database || props.activeConnection?.is_production) return false;
return productionContextForDatabase(props.activeConnection, database).reason === "database";
}
+async function changeCatalog(selectedCatalog: string) {
+ const connection = props.activeConnection;
+ if (!connection) return;
+ switchingCatalog.value = true;
+ try {
+ const catalog = normalizedQueryTabCatalog(activeCatalogs.value, selectedCatalog);
+ const databases = catalog ? await loadCatalogDatabaseOptions(connection.id, selectedCatalog) : await loadDatabaseOptions(connection.id).then(() => databaseOptions.value[connection.id] ?? []);
+ emit("changeCatalog", catalog, databaseAfterCatalogChange(props.activeTab.database, databases));
+ } finally {
+ switchingCatalog.value = false;
+ }
+}
@@ -364,6 +407,31 @@ function databaseOptionIsProduction(database: string): boolean {
+
+ {
+ if (open && activeConnection) loadCatalogOptions(activeConnection.id).catch(() => {});
+ }
+ "
+ >
+
+
+ {{ loading ? t("common.loading") : label }}
+
+
+
emit('changeDatabase', database)"
@update:open="
(open: boolean) => {
- if (open && activeConnection) loadDatabaseOptions(activeConnection.id).catch(() => {});
+ if (!open || !activeConnection) return;
+ if (activeTab.catalog) loadCatalogDatabaseOptions(activeConnection.id, activeTab.catalog).catch(() => {});
+ else loadDatabaseOptions(activeConnection.id).catch(() => {});
}
"
>
@@ -408,7 +478,7 @@ function databaseOptionIsProduction(database: string): boolean {
{{ t("editor.clearDatabase") }}
-