separate schema selector in frontend

This commit is contained in:
Jan Prochazka
2024-09-19 15:19:16 +02:00
parent cb7224ac94
commit 4431d08a88
14 changed files with 90 additions and 37 deletions

View File

@@ -1,14 +1,19 @@
import { SchemaInfo, SqlDialect } from 'dbgate-types'; import { SchemaInfo, SqlDialect } from 'dbgate-types';
export function findDefaultSchema(schemaList: SchemaInfo[], dialect: SqlDialect) { export function findDefaultSchema(schemaList: SchemaInfo[], dialect: SqlDialect, schemaInStorage: string = null) {
if (!schemaList) { if (!schemaList) {
return null; return null;
} }
if (schemaInStorage && schemaList.find(x => x.schemaName == schemaInStorage)) {
return schemaInStorage;
}
const dynamicDefaultSchema = schemaList.find(x => x.isDefault); const dynamicDefaultSchema = schemaList.find(x => x.isDefault);
if (dynamicDefaultSchema) { if (dynamicDefaultSchema) {
return dynamicDefaultSchema.schemaName; return dynamicDefaultSchema.schemaName;
} }
if (dialect.defaultSchemaName && schemaList.find(x => x.schemaName == dialect.defaultSchemaName)) { if (dialect?.defaultSchemaName && schemaList.find(x => x.schemaName == dialect.defaultSchemaName)) {
return dialect.defaultSchemaName; return dialect.defaultSchemaName;
} }
return schemaList[0]?.schemaName; return schemaList[0]?.schemaName;
@@ -26,3 +31,7 @@ export function splitCompositeDbName(name: string) {
export function extractDbNameFromComposite(name: string) { export function extractDbNameFromComposite(name: string) {
return isCompositeDbName(name) ? splitCompositeDbName(name).database : name; return isCompositeDbName(name) ? splitCompositeDbName(name).database : name;
} }
export function extractSchemaNameFromComposite(name: string) {
return splitCompositeDbName(name)?.schema;
}

View File

@@ -14,7 +14,7 @@
export function openConnection(connection) { export function openConnection(connection) {
const config = getCurrentConfig(); const config = getCurrentConfig();
if (connection.singleDatabase) { if (connection.singleDatabase) {
currentDatabase.set({ connection, name: connection.defaultDatabase }); switchCurrentDatabase({ connection, name: connection.defaultDatabase });
apiCall('database-connections/refresh', { apiCall('database-connections/refresh', {
conid: connection._id, conid: connection._id,
database: connection.defaultDatabase, database: connection.defaultDatabase,
@@ -60,7 +60,7 @@
if (electron) { if (electron) {
apiCall('database-connections/disconnect', { conid, database: currentDb.name }); apiCall('database-connections/disconnect', { conid, database: currentDb.name });
} }
currentDatabase.set(null); switchCurrentDatabase(null);
} }
closeMultipleTabs(closeCondition); closeMultipleTabs(closeCondition);
// if (data.unsaved) { // if (data.unsaved) {
@@ -107,6 +107,7 @@
import { tick } from 'svelte'; import { tick } from 'svelte';
import { getConnectionLabel } from 'dbgate-tools'; import { getConnectionLabel } from 'dbgate-tools';
import hasPermission from '../utility/hasPermission'; import hasPermission from '../utility/hasPermission';
import { switchCurrentDatabase } from '../utility/common';
export let data; export let data;
export let passProps; export let passProps;
@@ -142,7 +143,7 @@
return; return;
} }
if ($openedSingleDatabaseConnections.includes(data._id)) { if ($openedSingleDatabaseConnections.includes(data._id)) {
currentDatabase.set({ connection: data, name: data.defaultDatabase }); switchCurrentDatabase({ connection: data, name: data.defaultDatabase });
return; return;
} }
if ($openedConnections.includes(data._id)) { if ($openedConnections.includes(data._id)) {

View File

@@ -26,7 +26,7 @@
apiCall('database-connections/disconnect', { conid, database }); apiCall('database-connections/disconnect', { conid, database });
} }
if (getCurrentDatabase()?.connection?._id == conid && getCurrentDatabase()?.name == database) { if (getCurrentDatabase()?.connection?._id == conid && getCurrentDatabase()?.name == database) {
currentDatabase.set(null); switchCurrentDatabase(null);
} }
openedSingleDatabaseConnections.update(list => list.filter(x => x != conid)); openedSingleDatabaseConnections.update(list => list.filter(x => x != conid));
closeMultipleTabs(closeCondition); closeMultipleTabs(closeCondition);
@@ -374,7 +374,7 @@
import openNewTab from '../utility/openNewTab'; import openNewTab from '../utility/openNewTab';
import AppObjectCore from './AppObjectCore.svelte'; import AppObjectCore from './AppObjectCore.svelte';
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar'; import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
import { findEngineDriver, getConnectionLabel } from 'dbgate-tools'; import { extractDbNameFromComposite, findEngineDriver, getConnectionLabel } from 'dbgate-tools';
import InputTextModal from '../modals/InputTextModal.svelte'; import InputTextModal from '../modals/InputTextModal.svelte';
import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders'; import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders';
import { openJsonDocument } from '../tabs/JsonTab.svelte'; import { openJsonDocument } from '../tabs/JsonTab.svelte';
@@ -391,6 +391,7 @@
import hasPermission from '../utility/hasPermission'; import hasPermission from '../utility/hasPermission';
import { openImportExportTab } from '../utility/importExportTools'; import { openImportExportTab } from '../utility/importExportTools';
import newTable from '../tableeditor/newTable'; import newTable from '../tableeditor/newTable';
import { switchCurrentDatabase } from '../utility/common';
export let data; export let data;
export let passProps; export let passProps;
@@ -419,8 +420,8 @@
colorMark={passProps?.connectionColorFactory && colorMark={passProps?.connectionColorFactory &&
passProps?.connectionColorFactory({ conid: _.get(data.connection, '_id'), database: data.name }, null, null, false)} passProps?.connectionColorFactory({ conid: _.get(data.connection, '_id'), database: data.name }, null, null, false)}
isBold={_.get($currentDatabase, 'connection._id') == _.get(data.connection, '_id') && isBold={_.get($currentDatabase, 'connection._id') == _.get(data.connection, '_id') &&
_.get($currentDatabase, 'name') == data.name} extractDbNameFromComposite(_.get($currentDatabase, 'name')) == data.name}
on:click={() => ($currentDatabase = data)} on:click={() => switchCurrentDatabase(data)}
on:dragstart on:dragstart
on:dragenter on:dragenter
on:dragend on:dragend

View File

@@ -46,7 +46,7 @@
databaseList.push({ databaseList.push({
text: `${db.name} on ${getConnectionLabel(connection)}`, text: `${db.name} on ${getConnectionLabel(connection)}`,
icon: 'img database', icon: 'img database',
onClick: () => currentDatabase.set({ connection, name: db.name }), onClick: () => switchCurrentDatabase({ connection, name: db.name }),
}); });
} }
} }
@@ -80,7 +80,7 @@
import { useConnectionList, useDatabaseInfo } from '../utility/metadataLoaders'; import { useConnectionList, useDatabaseInfo } from '../utility/metadataLoaders';
import { getLocalStorage } from '../utility/storageCache'; import { getLocalStorage } from '../utility/storageCache';
import registerCommand from './registerCommand'; import registerCommand from './registerCommand';
import { formatKeyText } from '../utility/common'; import { formatKeyText, switchCurrentDatabase } from '../utility/common';
let domInput; let domInput;
let filter = ''; let filter = '';

View File

@@ -3,6 +3,7 @@ import { currentDatabase, getCurrentDatabase } from '../stores';
import getElectron from '../utility/getElectron'; import getElectron from '../utility/getElectron';
import registerCommand from './registerCommand'; import registerCommand from './registerCommand';
import { apiCall } from '../utility/api'; import { apiCall } from '../utility/api';
import { switchCurrentDatabase } from '../utility/common';
registerCommand({ registerCommand({
id: 'database.changeState', id: 'database.changeState',
@@ -40,7 +41,7 @@ registerCommand({
onClick: () => { onClick: () => {
const electron = getElectron(); const electron = getElectron();
if (electron) apiCall('database-connections/disconnect', dbid); if (electron) apiCall('database-connections/disconnect', dbid);
currentDatabase.set(null); switchCurrentDatabase(null);
}, },
}, },
]; ];

View File

@@ -2,6 +2,7 @@ import _ from 'lodash';
import { recentDatabases, currentDatabase, getRecentDatabases } from '../stores'; import { recentDatabases, currentDatabase, getRecentDatabases } from '../stores';
import registerCommand from './registerCommand'; import registerCommand from './registerCommand';
import { getConnectionLabel } from 'dbgate-tools'; import { getConnectionLabel } from 'dbgate-tools';
import { switchCurrentDatabase } from '../utility/common';
currentDatabase.subscribe(value => { currentDatabase.subscribe(value => {
if (!value) return; if (!value) return;
@@ -17,7 +18,7 @@ currentDatabase.subscribe(value => {
function switchDatabaseCommand(db) { function switchDatabaseCommand(db) {
return { return {
text: `${db.name} on ${getConnectionLabel(db?.connection, { allowExplicitDatabase: false })}`, text: `${db.name} on ${getConnectionLabel(db?.connection, { allowExplicitDatabase: false })}`,
onClick: () => currentDatabase.set(db), onClick: () => switchCurrentDatabase(db),
}; };
} }

View File

@@ -35,7 +35,7 @@ import { apiCall } from '../utility/api';
import runCommand from './runCommand'; import runCommand from './runCommand';
import { openWebLink } from '../utility/exportFileTools'; import { openWebLink } from '../utility/exportFileTools';
import { getSettings } from '../utility/metadataLoaders'; import { getSettings } from '../utility/metadataLoaders';
import { isMac } from '../utility/common'; import { isMac, switchCurrentDatabase } from '../utility/common';
import { doLogout, internalRedirectTo } from '../clientAuth'; import { doLogout, internalRedirectTo } from '../clientAuth';
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte'; import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
import UploadErrorModal from '../modals/UploadErrorModal.svelte'; import UploadErrorModal from '../modals/UploadErrorModal.svelte';
@@ -347,7 +347,7 @@ registerCommand({
onConfirm: async file => { onConfirm: async file => {
const resp = await apiCall('connections/new-sqlite-database', { file }); const resp = await apiCall('connections/new-sqlite-database', { file });
const connection = resp; const connection = resp;
currentDatabase.set({ connection, name: `${file}.sqlite` }); switchCurrentDatabase({ connection, name: `${file}.sqlite` });
}, },
}); });
}, },

View File

@@ -8,6 +8,7 @@ import _ from 'lodash';
import { safeJsonParse } from 'dbgate-tools'; import { safeJsonParse } from 'dbgate-tools';
import { apiCall } from './utility/api'; import { apiCall } from './utility/api';
import { getOpenedTabsStorageName, isAdminPage } from './utility/pageDefs'; import { getOpenedTabsStorageName, isAdminPage } from './utility/pageDefs';
import { switchCurrentDatabase } from './utility/common';
export interface TabDefinition { export interface TabDefinition {
title: string; title: string;
@@ -296,7 +297,7 @@ export function subscribeApiDependendStores() {
currentConfigValue = value; currentConfigValue = value;
invalidateCommands(); invalidateCommands();
if (value.singleDbConnection) { if (value.singleDbConnection) {
currentDatabase.set(value.singleDbConnection); switchCurrentDatabase(value.singleDbConnection);
} }
}); });
} }

View File

@@ -285,7 +285,7 @@
draggingTabTarget, draggingTabTarget,
} from '../stores'; } from '../stores';
import tabs from '../tabs'; import tabs from '../tabs';
import { setSelectedTab } from '../utility/common'; import { setSelectedTab, switchCurrentDatabase } from '../utility/common';
import contextMenu from '../utility/contextMenu'; import contextMenu from '../utility/contextMenu';
import { isElectronAvailable } from '../utility/getElectron'; import { isElectronAvailable } from '../utility/getElectron';
import { getConnectionInfo, useConnectionList } from '../utility/metadataLoaders'; import { getConnectionInfo, useConnectionList } from '../utility/metadataLoaders';
@@ -420,11 +420,11 @@
if (conid) { if (conid) {
const connection = await getConnectionInfo({ conid, database }); const connection = await getConnectionInfo({ conid, database });
if (connection) { if (connection) {
$currentDatabase = { connection, name: database }; switchCurrentDatabase({ connection, name: database });
return; return;
} }
} }
$currentDatabase = null; switchCurrentDatabase(null);
}; };
async function scrollInViewTab(tabid) { async function scrollInViewTab(tabid) {

View File

@@ -3,6 +3,7 @@ import { currentDatabase, getCurrentDatabase, getLockedDatabaseMode, openedTabs
import { shouldShowTab } from '../tabpanel/TabsPanel.svelte'; import { shouldShowTab } from '../tabpanel/TabsPanel.svelte';
import { callWhenAppLoaded, getAppLoaded } from './appLoadManager'; import { callWhenAppLoaded, getAppLoaded } from './appLoadManager';
import { getConnectionInfo } from './metadataLoaders'; import { getConnectionInfo } from './metadataLoaders';
import { switchCurrentDatabase } from './common';
let lastCurrentTab = null; let lastCurrentTab = null;
@@ -20,7 +21,7 @@ openedTabs.subscribe(value => {
if (conid && database && (conid != lastTab?.props?.conid || database != lastTab?.props?.database)) { if (conid && database && (conid != lastTab?.props?.conid || database != lastTab?.props?.database)) {
const doWork = async () => { const doWork = async () => {
const connection = await getConnectionInfo({ conid }); const connection = await getConnectionInfo({ conid });
currentDatabase.set({ switchCurrentDatabase({
connection, connection,
name: database, name: database,
}); });

View File

@@ -1,5 +1,7 @@
import { getOpenedTabs, openedTabs } from '../stores'; import { findDefaultSchema, findEngineDriver, isCompositeDbName } from 'dbgate-tools';
import { currentDatabase, getExtensions, getOpenedTabs, openedTabs } from '../stores';
import _ from 'lodash'; import _ from 'lodash';
import { getSchemaList } from './metadataLoaders';
export class LoadingToken { export class LoadingToken {
isCanceled = false; isCanceled = false;
@@ -82,3 +84,21 @@ export function isCtrlOrCommandKey(event) {
} }
return event.ctrlKey; return event.ctrlKey;
} }
export async function switchCurrentDatabase(data) {
if (data?.connection?.useSeparateSchemas && !isCompositeDbName(data.name)) {
const conid = data.connection._id;
const database = data.name;
const storageKey = `selected-schema-${conid}-${database}`;
const schemaInStorage = localStorage.getItem(storageKey);
const schemas = await getSchemaList({ conid, database });
const driver = findEngineDriver(data.connection, getExtensions());
const defaultSchema = findDefaultSchema(schemas, driver?.dialect, schemaInStorage);
currentDatabase.set({
...data,
name: `${data.name}::${defaultSchema}`,
});
} else {
currentDatabase.set(data);
}
}

View File

@@ -12,6 +12,7 @@ import { SAVED_FILE_HANDLERS } from '../appobj/SavedFileAppObject.svelte';
import _ from 'lodash'; import _ from 'lodash';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte'; import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import { openImportExportTab } from './importExportTools'; import { openImportExportTab } from './importExportTools';
import { switchCurrentDatabase } from './common';
export function canOpenByElectron(file, extensions) { export function canOpenByElectron(file, extensions) {
if (!file) return false; if (!file) return false;
@@ -38,7 +39,7 @@ export async function openSqliteFile(filePath) {
singleDatabase: true, singleDatabase: true,
defaultDatabase, defaultDatabase,
}); });
currentDatabase.set({ switchCurrentDatabase({
connection: resp, connection: resp,
name: getDatabaseFileLabel(filePath), name: getDatabaseFileLabel(filePath),
}); });

View File

@@ -8,16 +8,22 @@
import ConfirmModal from '../modals/ConfirmModal.svelte'; import ConfirmModal from '../modals/ConfirmModal.svelte';
import { runOperationOnDatabase } from '../modals/ConfirmSqlModal.svelte'; import { runOperationOnDatabase } from '../modals/ConfirmSqlModal.svelte';
import InputTextModal from '../modals/InputTextModal.svelte'; import InputTextModal from '../modals/InputTextModal.svelte';
import { appliedCurrentSchema } from '../stores'; import { appliedCurrentSchema, currentDatabase } from '../stores';
import { switchCurrentDatabase } from '../utility/common';
import { extractDbNameFromComposite, extractSchemaNameFromComposite, findDefaultSchema } from 'dbgate-tools';
export let schemaList; export let schemaList;
export let selectedSchema;
export let objectList; export let objectList;
export let valueStorageKey;
export let conid; export let conid;
export let database; export let database;
export let connection;
export let driver;
let selectedSchema = null;
$: valueStorageKey = `selected-schema-${conid}-${database}`;
$: { $: {
if (selectedSchema != null) { if (selectedSchema != null) {
@@ -86,7 +92,12 @@
}); });
} }
$: selectedSchema = localStorage.getItem(valueStorageKey ?? ''); $: if (connection?.useSeparateSchemas) {
selectedSchema =
extractSchemaNameFromComposite($currentDatabase?.name) ?? findDefaultSchema(schemaList, driver?.dialect);
} else {
selectedSchema = localStorage.getItem(valueStorageKey ?? '');
}
</script> </script>
{#if realSchemaList.length > 0} {#if realSchemaList.length > 0}
@@ -94,15 +105,22 @@
<div class="mr-1">Schema:</div> <div class="mr-1">Schema:</div>
<SelectField <SelectField
isNative isNative
options={[ options={connection?.useSeparateSchemas
? (schemaList?.map(x => ({ label: x.schemaName, value: x.schemaName })) ?? [])
: [
{ label: `All schemas (${objectList?.length ?? 0})`, value: '' }, { label: `All schemas (${objectList?.length ?? 0})`, value: '' },
...realSchemaList.map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })), ...realSchemaList.map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })),
// ...schemaList.filter(x => countBySchema[x]).map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })),
// ...schemaList.filter(x => !countBySchema[x]).map(x => ({ label: `${x} (${countBySchema[x] ?? 0})`, value: x })),
]} ]}
value={selectedSchema ?? $appliedCurrentSchema ?? ''} value={selectedSchema ?? $appliedCurrentSchema ?? ''}
on:change={e => { on:change={e => {
if (connection?.useSeparateSchemas) {
switchCurrentDatabase({
connection,
name: `${extractDbNameFromComposite(database)}::${e.detail}`,
});
} else {
selectedSchema = e.detail; selectedSchema = e.detail;
}
localStorage.setItem(valueStorageKey, e.detail); localStorage.setItem(valueStorageKey, e.detail);
}} }}
selectClass="schema-select" selectClass="schema-select"

View File

@@ -48,7 +48,6 @@
export let database; export let database;
let filter = ''; let filter = '';
let selectedSchema = null;
$: objects = useDatabaseInfo({ conid, database }); $: objects = useDatabaseInfo({ conid, database });
$: status = useDatabaseStatus({ conid, database }); $: status = useDatabaseStatus({ conid, database });
@@ -153,11 +152,11 @@
</SearchBoxWrapper> </SearchBoxWrapper>
<SchemaSelector <SchemaSelector
schemaList={$schemaList} schemaList={$schemaList}
bind:selectedSchema
objectList={flatFilteredList} objectList={flatFilteredList}
valueStorageKey={`sql-object-list-schema-${conid}-${database}`} connection={$connection}
{conid} {conid}
{database} {database}
{driver}
/> />
<WidgetsInnerContainer> <WidgetsInnerContainer>