From f1507c3a7dd062dcad79950888a6e2f3fe56cf31 Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 29 Aug 2023 22:53:05 +0300 Subject: [PATCH] CB-3602 fix: sql context management (#1942) * CB-3602 fix: sql context management * CB-3711 fix: sql context lifecycle * CB-3602 Cleanup SQL processors/contexts on connection close * CB-3602 Cleanup SQL processors/contexts on connection close * CB-3602 fix: log sql script parsing errors * CB-3602 fix: sql context info convertation * CB-3602 fix: data concurrency loading --------- Co-authored-by: Serge Rider Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> --- .../cloudbeaver/model/WebConnectionInfo.java | 28 +++++++- .../cloudbeaver/model/session/WebSession.java | 6 +- .../service/core/impl/WebServiceCore.java | 9 ++- .../service/sql/WebSQLProcessor.java | 3 +- .../service/sql/WebServiceBindingSQL.java | 4 ++ .../core-authentication/src/AppAuthService.ts | 5 +- .../src/UserInfoResource.ts | 8 +-- .../core-blocks/src/Loader/useAutoLoad.ts | 33 +++++---- .../src/ConnectionDialectResource.ts | 3 +- .../ConnectionExecutionContext.ts | 3 +- .../ConnectionExecutionContextResource.ts | 24 +++---- .../IConnectionExecutionContext.ts | 2 +- .../IConnectionExecutionContextInfo.ts | 13 ---- .../src/ConnectionInfoResource.ts | 6 +- webapp/packages/core-connections/src/index.ts | 1 - .../core-ui/src/ContextMenu/ContextMenu.tsx | 2 +- .../src/ContextMenu/MenuBar/MenuBar.tsx | 2 +- .../src/ContextMenu/SubMenuElement.tsx | 2 +- .../GrantedConnections/GrantedConnections.tsx | 2 +- .../Users/Teams/GrantedUsers/GrantedUsers.tsx | 2 +- .../Users/UserForm/OriginInfoPanel.tsx | 2 +- .../ConnectionAccess/ConnectionAccess.tsx | 2 +- .../src/useGroupingDataModel.ts | 5 +- .../src/ContainerDataSource.ts | 9 +-- .../NavigationTabs/NavigationTabsService.ts | 2 +- .../NavigationTreeFiltersDialog.tsx | 2 +- .../src/ResourceSqlDataSource.ts | 16 +++-- .../src/SqlEditorBootstrap.ts | 8 +-- .../src/SqlEditorNavigatorService.ts | 5 +- .../src/SqlEditorTab.tsx | 10 ++- .../src/SqlEditorTabService.ts | 57 ++++++++------- .../src/SqlEditor/useSqlEditor.ts | 72 ++++++++++++------- .../src/SqlEditor/useTools.tsx | 11 ++- .../src/SqlEditorOverlay.tsx | 9 +-- .../plugin-sql-editor/src/SqlEditorService.ts | 23 +++--- .../src/ScriptPreview/ScriptPreviewService.ts | 8 +-- 36 files changed, 221 insertions(+), 178 deletions(-) delete mode 100644 webapp/packages/core-connections/src/ConnectionExecutionContext/IConnectionExecutionContextInfo.ts diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java index 5e48356fcd..91dbbce1bb 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebConnectionInfo.java @@ -25,6 +25,7 @@ import io.cloudbeaver.utils.CBModelConstants; import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebCommonUtils; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBConstants; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.DBPDataSourceContainer; @@ -40,6 +41,7 @@ import org.jkiss.dbeaver.model.navigator.DBNDataSource; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.model.preferences.DBPPropertySource; import org.jkiss.dbeaver.model.rm.RMProjectPermission; +import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized; import org.jkiss.dbeaver.runtime.DBWorkbench; import org.jkiss.utils.CommonUtils; @@ -52,6 +54,7 @@ import java.util.stream.Collectors; */ public class WebConnectionInfo { + private static final Log log = Log.getLog(WebConnectionInfo.class); public static final String SECURED_VALUE = "********"; private final WebSession session; private final DBPDataSourceContainer dataSourceContainer; @@ -63,6 +66,7 @@ public class WebConnectionInfo { private transient Map savedAuthProperties; private transient List savedNetworkCredentials; + private transient List> closeListeners = null; public WebConnectionInfo(WebSession session, DBPDataSourceContainer ds) { this.session = session; @@ -361,9 +365,23 @@ public class WebConnectionInfo { this.savedNetworkCredentials = networkCredentials; } - public void clearSavedCredentials() { + public void clearCache() { this.savedAuthProperties = null; this.savedNetworkCredentials = null; + this.fireCloseListeners(); + } + + public void fireCloseListeners() { + if (closeListeners != null) { + for (DBRRunnableParametrized listener : closeListeners) { + try { + listener.run(this); + } catch (Exception e) { + log.debug(e); + } + } + closeListeners = null; + } } @Property @@ -417,4 +435,12 @@ public class WebConnectionInfo { return appConfig.isShowReadOnlyConnectionInfo(); } + + public void addCloseListener(DBRRunnableParametrized listener) { + if (closeListeners == null) { + closeListeners = new ArrayList<>(); + } + closeListeners.add(listener); + } + } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index 77ebca158c..740c3eea6d 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -62,7 +62,10 @@ import org.jkiss.dbeaver.model.runtime.AbstractJob; import org.jkiss.dbeaver.model.runtime.BaseProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.ProxyProgressMonitor; -import org.jkiss.dbeaver.model.security.*; +import org.jkiss.dbeaver.model.security.SMAdminController; +import org.jkiss.dbeaver.model.security.SMConstants; +import org.jkiss.dbeaver.model.security.SMController; +import org.jkiss.dbeaver.model.security.SMObjectType; import org.jkiss.dbeaver.model.security.user.SMObjectPermissions; import org.jkiss.dbeaver.model.sql.DBQuotaException; import org.jkiss.dbeaver.model.websocket.event.WSEventType; @@ -593,6 +596,7 @@ public class WebSession extends BaseWebSession } public void removeConnection(WebConnectionInfo connectionInfo) { + connectionInfo.clearCache(); synchronized (connections) { connections.remove(connectionInfo.getId()); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index 632c436762..fc6685fe6d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -333,7 +333,7 @@ public class WebServiceCore implements DBWServiceCore { throw new DBWebException("Error connecting to database", e); } finally { dataSourceContainer.setSavePassword(oldSavePassword); - connectionInfo.clearSavedCredentials(); + connectionInfo.clearCache(); } // Mark all specified network configs as saved boolean[] saveConfig = new boolean[1]; @@ -748,7 +748,10 @@ public class WebServiceCore implements DBWServiceCore { @NotNull private WebConnectionInfo closeAndDeleteConnection( - WebSession webSession, String projectId, String connectionId, boolean forceDelete + @NotNull WebSession webSession, + @NotNull String projectId, + @NotNull String connectionId, + boolean forceDelete ) throws DBWebException { WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, connectionId); @@ -770,7 +773,7 @@ public class WebServiceCore implements DBWServiceCore { webSession.removeConnection(connectionInfo); } else { // Just reset saved credentials - connectionInfo.clearSavedCredentials(); + connectionInfo.clearCache(); } return connectionInfo; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java index 942ebc92ed..04df960500 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java @@ -25,7 +25,6 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.ModelPreferences; import org.jkiss.dbeaver.model.DBPDataKind; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.DBUtils; @@ -69,7 +68,7 @@ public class WebSQLProcessor implements WebSessionProvider { private final SQLRuleManager ruleManager; private final Map contexts = new LinkedHashMap<>(); - private AtomicInteger contextId = new AtomicInteger(); + private final AtomicInteger contextId = new AtomicInteger(); WebSQLProcessor(@NotNull WebSession webSession, @NotNull WebConnectionInfo connection) { this.webSession = webSession; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java index c52189dccf..1d125f05d5 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java @@ -258,6 +258,9 @@ public class WebServiceBindingSQL extends WebServiceBindingBase i private static class WebSQLConfiguration { private final Map processors = new HashMap<>(); + public WebSQLConfiguration() { + } + WebSQLProcessor getSQLProcessor(WebConnectionInfo connectionInfo) throws DBWebException { return WebServiceBindingSQL.getSQLProcessor(connectionInfo, true); } @@ -277,6 +280,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase i WebSQLProcessor processor = processors.get(connectionInfo); if (processor == null) { processor = new WebSQLProcessor(connectionInfo.getSession(), connectionInfo); + connectionInfo.addCloseListener(processors::remove); processors.put(connectionInfo, processor); } return processor; diff --git a/webapp/packages/core-authentication/src/AppAuthService.ts b/webapp/packages/core-authentication/src/AppAuthService.ts index 8c46114bed..80076210c5 100644 --- a/webapp/packages/core-authentication/src/AppAuthService.ts +++ b/webapp/packages/core-authentication/src/AppAuthService.ts @@ -40,10 +40,7 @@ export class AppAuthService extends Bootstrap { resource: CachedResource, map?: (param: T | undefined) => T, ): this { - resource - .preloadResource(this.userInfoResource, () => {}) - .preloadResource(this.serverConfigResource, () => {}) - .before(ExecutorInterrupter.interrupter(() => !this.authenticated)); + resource.preloadResource(this.userInfoResource, () => {}).before(ExecutorInterrupter.interrupter(() => !this.authenticated)); this.userInfoResource.outdateResource(resource, map as any); diff --git a/webapp/packages/core-authentication/src/UserInfoResource.ts b/webapp/packages/core-authentication/src/UserInfoResource.ts index 1d4a0f4a8b..0fe6bd6993 100644 --- a/webapp/packages/core-authentication/src/UserInfoResource.ts +++ b/webapp/packages/core-authentication/src/UserInfoResource.ts @@ -163,11 +163,9 @@ export class UserInfoResource extends CachedDataResource { - this.resetIncludes(); - this.markOutdated(); - this.sessionDataResource.markOutdated(); - }); + this.resetIncludes(); + this.setData(await this.loader()); + this.sessionDataResource.markOutdated(); } async setConfigurationParameter(key: string, value: any): Promise { diff --git a/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts b/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts index 419ec23bef..0dfd750c7f 100644 --- a/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts +++ b/webapp/packages/core-blocks/src/Loader/useAutoLoad.ts @@ -5,7 +5,7 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { ILoadableState, isLoadableStateHasException } from '@cloudbeaver/core-utils'; @@ -15,7 +15,8 @@ export interface IAutoLoadable extends ILoadableState { load: () => void; } -export function useAutoLoad(state: IAutoLoadable | IAutoLoadable[], enabled = true, lazy = false) { +export function useAutoLoad(component: { name: string }, state: IAutoLoadable | IAutoLoadable[], enabled = true, lazy = false) { + const [loadFunctionName] = useState(`${component.name}.useAutoLoad(...)` as const); if (!Array.isArray(state)) { state = [state]; } @@ -28,18 +29,24 @@ export function useAutoLoad(state: IAutoLoadable | IAutoLoadable[], enabled = tr } useEffect(() => { - if (!enabled) { - return; - } + const obj = { + [loadFunctionName]: () => { + if (!enabled) { + return; + } - for (const loader of state as IAutoLoadable[]) { - if (isLoadableStateHasException(loader) || (loader.lazy === true && !lazy)) { - continue; - } + for (const loader of state as IAutoLoadable[]) { + if (isLoadableStateHasException(loader) || (loader.lazy === true && !lazy)) { + continue; + } - if (!loader.isLoaded() || loader.isOutdated?.() === true) { - loader.load(); - } - } + if (!loader.isLoaded() || loader.isOutdated?.() === true) { + loader.load(); + } + } + }, + }; + + obj[loadFunctionName](); }); } diff --git a/webapp/packages/core-connections/src/ConnectionDialectResource.ts b/webapp/packages/core-connections/src/ConnectionDialectResource.ts index b85688ad00..5b3a0bef3f 100644 --- a/webapp/packages/core-connections/src/ConnectionDialectResource.ts +++ b/webapp/packages/core-connections/src/ConnectionDialectResource.ts @@ -12,14 +12,13 @@ import { CachedMapAllKey, CachedMapResource, GraphQLService, - isResourceKeyList, ResourceKey, resourceKeyList, ResourceKeyUtils, SqlDialectInfo, } from '@cloudbeaver/core-sdk'; -import type { IConnectionExecutionContextInfo } from './ConnectionExecutionContext/IConnectionExecutionContextInfo'; +import type { IConnectionExecutionContextInfo } from './ConnectionExecutionContext/ConnectionExecutionContextResource'; import { ConnectionInfoActiveProjectKey, ConnectionInfoProjectKey, diff --git a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContext.ts b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContext.ts index 57605d5a06..ca944573e6 100644 --- a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContext.ts +++ b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContext.ts @@ -9,9 +9,8 @@ import { computed, makeObservable, observable } from 'mobx'; import type { ITask, TaskScheduler } from '@cloudbeaver/core-executor'; -import type { ConnectionExecutionContextResource } from './ConnectionExecutionContextResource'; +import type { ConnectionExecutionContextResource, IConnectionExecutionContextInfo } from './ConnectionExecutionContextResource'; import type { IConnectionExecutionContext } from './IConnectionExecutionContext'; -import type { IConnectionExecutionContextInfo } from './IConnectionExecutionContextInfo'; export class ConnectionExecutionContext implements IConnectionExecutionContext { get context(): IConnectionExecutionContextInfo | undefined { diff --git a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts index 0926225562..c63d94f531 100644 --- a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts +++ b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts @@ -7,7 +7,7 @@ */ import { action, makeObservable, runInAction } from 'mobx'; -import { AppAuthService } from '@cloudbeaver/core-authentication'; +import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication'; import { injectable } from '@cloudbeaver/core-di'; import { CachedMapAllKey, @@ -25,17 +25,21 @@ import { flat } from '@cloudbeaver/core-utils'; import { ConnectionInfoActiveProjectKey, ConnectionInfoResource } from '../ConnectionInfoResource'; import type { IConnectionInfoParams } from '../IConnectionsResource'; -import type { IConnectionExecutionContextInfo } from './IConnectionExecutionContextInfo'; export const ConnectionExecutionContextProjectKey = resourceKeyAliasFactory('@connection-folder/project', (projectId: string) => ({ projectId })); export const NOT_INITIALIZED_CONTEXT_ID = '-1'; +export type IConnectionExecutionContextInfo = SqlContextInfo & { + defaultCatalog?: string | null; + defaultSchema?: string | null; +}; @injectable() export class ConnectionExecutionContextResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, private readonly connectionInfoResource: ConnectionInfoResource, + userInfoResource: UserInfoResource, appAuthService: AppAuthService, ) { super(); @@ -55,6 +59,9 @@ export class ConnectionExecutionContextResource extends CachedMapResource { + this.clear(); + }); connectionInfoResource.onItemUpdate.addHandler(this.updateConnectionContexts.bind(this)); connectionInfoResource.onItemDelete.addHandler(this.deleteConnectionContexts.bind(this)); @@ -128,17 +135,6 @@ export class ConnectionExecutionContextResource extends CachedMapResource { - this.resetIncludes(); - await this.refresh(CachedMapAllKey); - return this.values; - } - - refreshAllLazy(): void { - this.resetIncludes(); - this.markOutdated(CachedMapAllKey); - } - protected async loader(originalKey: ResourceKey): Promise> { const contextsList: IConnectionExecutionContextInfo[] = []; let projectId: string | undefined; @@ -218,7 +214,7 @@ export class ConnectionExecutionContextResource extends CachedMapResource this.sessionUpdate)); this.onConnectionCreate.addHandler(ExecutorInterrupter.interrupter(() => this.sessionUpdate)); + userInfoResource.onUserChange.addHandler(() => { + this.clear(); + }); appAuthService.requireAuthentication(this); this.sync( this.projectInfoResource, diff --git a/webapp/packages/core-connections/src/index.ts b/webapp/packages/core-connections/src/index.ts index c344d7f560..3f7752f722 100644 --- a/webapp/packages/core-connections/src/index.ts +++ b/webapp/packages/core-connections/src/index.ts @@ -2,7 +2,6 @@ export * from './ConnectionExecutionContext/ConnectionExecutionContext'; export * from './ConnectionExecutionContext/ConnectionExecutionContextResource'; export * from './ConnectionExecutionContext/ConnectionExecutionContextService'; export * from './ConnectionExecutionContext/IConnectionExecutionContext'; -export * from './ConnectionExecutionContext/IConnectionExecutionContextInfo'; export * from './extensions/IObjectCatalogProvider'; export * from './extensions/IObjectCatalogSetter'; diff --git a/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx b/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx index 37ee91209f..ef404f3d4d 100644 --- a/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/ContextMenu.tsx @@ -49,7 +49,7 @@ export const ContextMenu = observer( const menu = useRef(); - useAutoLoad(menuData.loaders, !lazy, menuVisible); + useAutoLoad({ name: `${ContextMenu.name}(${menuData.menu.id})` }, menuData.loaders, !lazy, menuVisible); const handlers = useObjectRef( () => ({ diff --git a/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx b/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx index 3101d8caa0..4ea1fc8173 100644 --- a/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/MenuBar/MenuBar.tsx @@ -51,7 +51,7 @@ export const MenuBar = observer( forwardRef(function MenuBar({ menu, nestedMenuSettings, rtl, className, ...props }, ref) { const styles = useS(style); const items = menu.items; - useAutoLoad(menu.loaders); + useAutoLoad(MenuBar, menu.loaders); if (!items.length) { return null; diff --git a/webapp/packages/core-ui/src/ContextMenu/SubMenuElement.tsx b/webapp/packages/core-ui/src/ContextMenu/SubMenuElement.tsx index 5a173f0549..459fefbb18 100644 --- a/webapp/packages/core-ui/src/ContextMenu/SubMenuElement.tsx +++ b/webapp/packages/core-ui/src/ContextMenu/SubMenuElement.tsx @@ -32,7 +32,7 @@ export const SubMenuElement = observer( const handler = subMenuData.handler; const hidden = getComputed(() => handler?.isHidden?.(subMenuData.context)); - useAutoLoad(subMenuData.loaders, !hidden, visible); + useAutoLoad(SubMenuElement, subMenuData.loaders, !hidden, visible); const handlers = useObjectRef( () => ({ diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx index 30133af03c..f7926a1a78 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedConnections/GrantedConnections.tsx @@ -70,7 +70,7 @@ export const GrantedConnections: TabContainerPanelComponent = ob const grantedConnections = getComputed(() => connections.filter(connection => state.state.grantedSubjects.includes(connection.id))); - useAutoLoad(state, selected && !loaded); + useAutoLoad(GrantedConnections, state, selected && !loaded); if (!selected) { return null; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx index d6a0df89a8..4d47d1f3a6 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/Teams/GrantedUsers/GrantedUsers.tsx @@ -61,7 +61,7 @@ export const GrantedUsers: TabContainerPanelComponent = observer users.data.filter((user): user is AdminUser => !!user && state.state.grantedUsers.includes(user.userId)), ); - useAutoLoad(state, selected && !state.state.loaded); + useAutoLoad(GrantedUsers, state, selected && !state.state.loaded); if (!selected) { return null; diff --git a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/OriginInfoPanel.tsx b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/OriginInfoPanel.tsx index 139755a787..0802aacc92 100644 --- a/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/OriginInfoPanel.tsx +++ b/webapp/packages/plugin-authentication-administration/src/Administration/Users/UserForm/OriginInfoPanel.tsx @@ -114,7 +114,7 @@ export const OriginInfoPanel: TabContainerPanelComponent = obser const { selected } = useTab(tabId); - useAutoLoad(loadableState, selected); + useAutoLoad(OriginInfoPanel, loadableState, selected); if (!selected) { return null; diff --git a/webapp/packages/plugin-connections-administration/src/ConnectionForm/ConnectionAccess/ConnectionAccess.tsx b/webapp/packages/plugin-connections-administration/src/ConnectionForm/ConnectionAccess/ConnectionAccess.tsx index 3bbada2521..fc6b866265 100644 --- a/webapp/packages/plugin-connections-administration/src/ConnectionForm/ConnectionAccess/ConnectionAccess.tsx +++ b/webapp/packages/plugin-connections-administration/src/ConnectionForm/ConnectionAccess/ConnectionAccess.tsx @@ -54,7 +54,7 @@ export const ConnectionAccess: TabContainerPanelComponent const { selected } = useTab(tabId); - useAutoLoad(state, selected); + useAutoLoad(ConnectionAccess, state, selected); const users = useResource(ConnectionAccess, UsersResource, CachedResourcePageListKey(0, 1000).setTarget(UsersResourceFilterKey()), { active: selected, diff --git a/webapp/packages/plugin-data-viewer-result-set-grouping/src/useGroupingDataModel.ts b/webapp/packages/plugin-data-viewer-result-set-grouping/src/useGroupingDataModel.ts index f563394d22..270ddf0bf8 100755 --- a/webapp/packages/plugin-data-viewer-result-set-grouping/src/useGroupingDataModel.ts +++ b/webapp/packages/plugin-data-viewer-result-set-grouping/src/useGroupingDataModel.ts @@ -91,9 +91,10 @@ export function useGroupingDataModel( if (columns.length !== 0 && functions.length !== 0 && sourceResultId) { const executionContext = sourceModel.source.executionContext; model.model.source.setExecutionContext(executionContext).setSupportedDataFormats(connectionInfo?.supportedDataFormats ?? []); + const context = executionContext?.context; - if (executionContext?.context) { - const connectionKey = createConnectionParam(executionContext.context.projectId, executionContext.context.connectionId); + if (context) { + const connectionKey = createConnectionParam(context.projectId, context.connectionId); model.model .setOptions({ diff --git a/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts b/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts index 0c20a6ccf7..04adc6951a 100644 --- a/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts +++ b/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts @@ -79,6 +79,7 @@ export class ContainerDataSource extends DatabaseDataSource { const { taskInfo } = await this.graphQLService.sdk.asyncReadDataFromContainer({ - connectionId: executionContext.context!.connectionId, - contextId: executionContext.context!.id, + connectionId: context.connectionId, + contextId: context.id, containerNodePath: options.containerNodePath, resultId: firstResultId, filter: { diff --git a/webapp/packages/plugin-navigation-tabs/src/NavigationTabs/NavigationTabsService.ts b/webapp/packages/plugin-navigation-tabs/src/NavigationTabs/NavigationTabsService.ts index 4eb9afc3eb..61a5167c93 100644 --- a/webapp/packages/plugin-navigation-tabs/src/NavigationTabs/NavigationTabsService.ts +++ b/webapp/packages/plugin-navigation-tabs/src/NavigationTabs/NavigationTabsService.ts @@ -140,7 +140,7 @@ export class NavigationTabsService extends View { currentTab: computed, currentTabId: computed, tabIdList: computed({ - equals: isArraysEqual, + equals: (a, b) => isArraysEqual(a, b, undefined, true), }), openTab: action, selectTab: action, diff --git a/webapp/packages/plugin-navigation-tree-filters/src/NavigationTreeFiltersDialog/NavigationTreeFiltersDialog.tsx b/webapp/packages/plugin-navigation-tree-filters/src/NavigationTreeFiltersDialog/NavigationTreeFiltersDialog.tsx index 0df83df628..20c3046093 100644 --- a/webapp/packages/plugin-navigation-tree-filters/src/NavigationTreeFiltersDialog/NavigationTreeFiltersDialog.tsx +++ b/webapp/packages/plugin-navigation-tree-filters/src/NavigationTreeFiltersDialog/NavigationTreeFiltersDialog.tsx @@ -32,7 +32,7 @@ export const NavigationTreeFiltersDialog = observer { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId); + const executionContext = dataSource?.executionContext; return ( tab.handlerState.source === SQL_EDITOR_SOURCE_ACTION && - dataSource?.executionContext !== undefined && + executionContext !== undefined && data.connectionKey !== undefined && this.connectionInfoResource.isKeyEqual( - createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId), + createConnectionParam(executionContext.projectId, executionContext.connectionId), data.connectionKey, ) ); diff --git a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTab.tsx b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTab.tsx index 15a20ecc27..42e07dc5d1 100644 --- a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTab.tsx +++ b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTab.tsx @@ -10,7 +10,7 @@ import { useContext } from 'react'; import styled from 'reshadow'; import { s, useStyles } from '@cloudbeaver/core-blocks'; -import { Connection, ConnectionInfoResource } from '@cloudbeaver/core-connections'; +import { Connection, ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; import { ITabData, Tab, TabIcon, TabTitle } from '@cloudbeaver/core-ui'; import { CaptureViewContext, useDataContext } from '@cloudbeaver/core-view'; @@ -32,12 +32,10 @@ export const SqlEditorTab: TabHandlerTabComponent = observer const dataSource = sqlDataSourceService.get(tab.handlerState.editorId); let connection: Connection | undefined; + const executionContext = dataSource?.executionContext; - if (dataSource?.executionContext) { - connection = connectionInfo.get({ - projectId: dataSource.executionContext.projectId, - connectionId: dataSource.executionContext.connectionId, - }); + if (executionContext) { + connection = connectionInfo.get(createConnectionParam(executionContext.projectId, executionContext.connectionId)); } const name = getSqlEditorName(tab.handlerState, dataSource, connection); diff --git a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTabService.ts b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTabService.ts index 9f6fa6eda6..400126f0e3 100644 --- a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTabService.ts +++ b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorTabService.ts @@ -155,9 +155,10 @@ export class SqlEditorTabService extends Bootstrap { for (const tab of tabs) { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId); + const executionContext = dataSource?.executionContext; - if (dataSource?.executionContext) { - const contextConnection = createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + if (executionContext) { + const contextConnection = createConnectionParam(executionContext.projectId, executionContext.connectionId); if (this.connectionInfoResource.isIntersect(key, contextConnection)) { this.resetConnectionInfo(tab); @@ -168,12 +169,13 @@ export class SqlEditorTabService extends Bootstrap { private getNavNode(tab: ITab) { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId); + const executionContext = dataSource?.executionContext; - if (!dataSource?.executionContext) { + if (!executionContext) { return; } - const { projectId, connectionId, defaultCatalog, defaultSchema } = dataSource.executionContext; + const { projectId, connectionId, defaultCatalog, defaultSchema } = executionContext; const connectionKey = createConnectionParam(projectId, connectionId); const connection = this.connectionInfoResource.get(connectionKey); @@ -222,19 +224,20 @@ export class SqlEditorTabService extends Bootstrap { for (const tab of tabs) { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId)!; - const executionContext = this.connectionExecutionContextService.get(dataSource.executionContext!.id); + const executionContext = this.connectionExecutionContextService.get(dataSource.executionContext!.id)?.context; - if (!executionContext?.context) { - if (dataSource.executionContext) { - const contextConnection = createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + if (!executionContext) { + const executionContext = dataSource.executionContext; + if (executionContext) { + const contextConnection = createConnectionParam(executionContext.projectId, executionContext.connectionId); if (!this.connectionInfoResource.has(contextConnection)) { this.resetConnectionInfo(tab); } } } else { - dataSource.setExecutionContext({ ...executionContext.context }); - this.attachToProject(tab, executionContext.context.projectId); + dataSource.setExecutionContext({ ...executionContext }); + this.attachToProject(tab, executionContext.projectId); } } } @@ -250,11 +253,12 @@ export class SqlEditorTabService extends Bootstrap { for (const tab of tabs) { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId)!; + const executionContext = dataSource.executionContext; - if (dataSource.executionContext) { - const contextConnection = createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + if (executionContext) { + const contextConnection = createConnectionParam(executionContext.projectId, executionContext.connectionId); - if (ResourceKeyUtils.isIntersect(key, dataSource.executionContext!.id) && !this.connectionInfoResource.has(contextConnection)) { + if (ResourceKeyUtils.isIntersect(key, executionContext.id) && !this.connectionInfoResource.has(contextConnection)) { this.resetConnectionInfo(tab); } } @@ -286,11 +290,12 @@ export class SqlEditorTabService extends Bootstrap { } const dataSource = this.sqlDataSourceService.create(tab.handlerState, tab.handlerState.datasourceKey); + const executionContext = dataSource.executionContext; - if (dataSource.executionContext) { + if (executionContext) { await this.connectionInfoResource.load(ConnectionInfoActiveProjectKey); - const contextConnection = createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + const contextConnection = createConnectionParam(executionContext.projectId, executionContext.connectionId); if (!this.connectionInfoResource.has(contextConnection)) { this.resetConnectionInfo(tab); @@ -321,13 +326,13 @@ export class SqlEditorTabService extends Bootstrap { } private getConnectionId(tab: ITab): IConnectionInfoParams | undefined { - const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId); + const context = this.sqlDataSourceService.get(tab.handlerState.editorId)?.executionContext; - if (!dataSource?.executionContext) { + if (!context) { return undefined; } - return createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + return createConnectionParam(context.projectId, context.connectionId); } private getObjectCatalogId(tab: ITab) { @@ -422,12 +427,13 @@ export class SqlEditorTabService extends Bootstrap { if (data.state === 'before') { for (const tab of this.sqlEditorTabs) { const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId); + const executionContext = dataSource?.executionContext; - if (!dataSource?.executionContext) { + if (!executionContext) { continue; } - const connectionKey = createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId); + const connectionKey = createConnectionParam(executionContext.projectId, executionContext.connectionId); if (!this.connectionInfoResource.isIntersect(connectionsKey, connectionKey)) { continue; @@ -481,12 +487,6 @@ export class SqlEditorTabService extends Bootstrap { } private async handleTabUnload(editorTab: ITab) { - const dataSource = this.sqlDataSourceService.get(editorTab.handlerState.editorId); - - if (dataSource?.executionContext) { - await this.sqlEditorService.destroyContext(dataSource.executionContext); - } - await this.sqlDataSourceService.unload(editorTab.handlerState.editorId); this.sqlResultTabsService.removeResultTabs(editorTab.handlerState); @@ -504,6 +504,11 @@ export class SqlEditorTabService extends Bootstrap { } private async handleTabClose(editorTab: ITab) { + const dataSource = this.sqlDataSourceService.get(editorTab.handlerState.editorId); + + if (dataSource?.executionContext) { + await this.sqlEditorService.destroyContext(dataSource.executionContext); + } await this.sqlDataSourceService.destroy(editorTab.handlerState.editorId); } } diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts index f1f7807bea..a313d6475a 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts @@ -12,6 +12,7 @@ import { useExecutor, useObservableRef } from '@cloudbeaver/core-blocks'; import { ConnectionExecutionContextService, createConnectionParam } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; import { CommonDialogService, ConfirmationDialog, DialogueStateResult } from '@cloudbeaver/core-dialogs'; +import { NotificationService } from '@cloudbeaver/core-events'; import { SyncExecutor } from '@cloudbeaver/core-executor'; import type { SqlCompletionProposal, SqlDialectInfo, SqlScriptInfoFragment } from '@cloudbeaver/core-sdk'; import { createLastPromiseGetter, LastPromiseGetter, throttleAsync } from '@cloudbeaver/core-utils'; @@ -34,6 +35,7 @@ interface ISQLEditorDataPrivate extends ISQLEditorData { readonly connectionExecutionContextService: ConnectionExecutionContextService; readonly sqlQueryService: SqlQueryService; readonly sqlEditorService: SqlEditorService; + readonly notificationService: NotificationService; readonly sqlExecutionPlanService: SqlExecutionPlanService; readonly commonDialogService: CommonDialogService; readonly sqlResultTabsService: SqlResultTabsService; @@ -61,6 +63,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { const sqlQueryService = useService(SqlQueryService); const sqlDialectInfoService = useService(SqlDialectInfoService); const sqlEditorService = useService(SqlEditorService); + const notificationService = useService(NotificationService); const sqlExecutionPlanService = useService(SqlExecutionPlanService); const sqlResultTabsService = useService(SqlResultTabsService); const commonDialogService = useService(CommonDialogService); @@ -72,13 +75,12 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { return sqlDataSourceService.get(this.state.editorId); }, get dialect(): SqlDialectInfo | undefined { - if (!this.dataSource?.executionContext) { + const executionContext = this.dataSource?.executionContext; + if (!executionContext) { return undefined; } - return this.sqlDialectInfoService.getDialectInfo( - createConnectionParam(this.dataSource.executionContext.projectId, this.dataSource.executionContext.connectionId), - ); + return this.sqlDialectInfoService.getDialectInfo(createConnectionParam(executionContext.projectId, executionContext.connectionId)); }, get activeSegmentMode(): ISQLEditorMode { @@ -155,15 +157,17 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { this.parser.setScript(this.value); this.reactionDisposer = autorun(() => { - if (this.dataSource?.executionContext?.id) { - const context = this.connectionExecutionContextService.get(this.dataSource.executionContext.id); - + const executionContext = this.dataSource?.executionContext; + if (executionContext) { + const context = this.connectionExecutionContextService.get(executionContext.id)?.context; if (context) { - const key = createConnectionParam(this.dataSource.executionContext.projectId, this.dataSource.executionContext.connectionId); + const key = createConnectionParam(context.projectId, context.connectionId); untracked(() => { - this.sqlDialectInfoService.loadSqlDialectInfo(key).then(async dialect => { - await this.updateParserScriptsThrottle(); + this.sqlDialectInfoService.loadSqlDialectInfo(key).then(async () => { + try { + await this.updateParserScriptsThrottle(); + } catch {} }); }); } @@ -187,13 +191,19 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { parseScript: createLastPromiseGetter(), getHintProposals: throttleAsync(async function getHintProposals(this: ISQLEditorDataPrivate, position, simple) { - if (!this.dataSource?.executionContext) { + const executionContext = this.dataSource?.executionContext; + if (!executionContext) { return []; } - const { connectionId, id } = this.dataSource.executionContext; - - const hints = await this.sqlEditorService.getAutocomplete(connectionId, id, this.value, position, MAX_HINTS_LIMIT, simple); + const hints = await this.sqlEditorService.getAutocomplete( + executionContext.connectionId, + executionContext.id, + this.value, + position, + MAX_HINTS_LIMIT, + simple, + ); this.hintsLimitIsMet = hints.length >= MAX_HINTS_LIMIT; @@ -233,9 +243,11 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { } const query = this.getSubQuery(); - await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => - this.sqlQueryService.executeEditorQuery(this.state, query.query, false), - ); + try { + await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => + this.sqlQueryService.executeEditorQuery(this.state, query.query, false), + ); + } catch {} }, async loadDatabaseDataModels(): Promise { @@ -262,9 +274,11 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { } const query = this.getSubQuery(); - await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => - this.sqlQueryService.executeEditorQuery(this.state, query.query, true), - ); + try { + await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => + this.sqlQueryService.executeEditorQuery(this.state, query.query, true), + ); + } catch {} }, async showExecutionPlan(): Promise { @@ -277,9 +291,11 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { const query = this.getSubQuery(); - await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => - this.sqlExecutionPlanService.executeExecutionPlan(this.state, query.query), - ); + try { + await this.executeQueryAction(await this.executeQueryAction(query, () => this.getResolvedSegment()), query => + this.sqlExecutionPlanService.executeExecutionPlan(this.state, query.query), + ); + } catch {} }, async switchEditing(): Promise { @@ -363,7 +379,14 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { return; } - const { queries } = await this.parseScript([connectionId, script], () => this.sqlEditorService.parseSQLScript(connectionId, script)); + const { queries } = await this.parseScript([connectionId, script], async () => { + try { + return await this.sqlEditorService.parseSQLScript(connectionId, script); + } catch (exception: any) { + this.notificationService.logException(exception, 'Failed to parse SQL script'); + throw exception; + } + }); if (this.parser.actualScript === script) { this.parser.setQueries(queries); @@ -462,6 +485,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { sqlEditorService, sqlExecutionPlanService, sqlResultTabsService, + notificationService, commonDialogService, }, ); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useTools.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditor/useTools.tsx index c8931201a6..c7899727c9 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useTools.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useTools.tsx @@ -8,7 +8,7 @@ import { action } from 'mobx'; import { useObservableRef } from '@cloudbeaver/core-blocks'; -import { Connection, ConnectionInfoResource } from '@cloudbeaver/core-connections'; +import { Connection, ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs'; import { NotificationService } from '@cloudbeaver/core-events'; @@ -101,14 +101,11 @@ export function useTools(state: ISqlEditorTabState): Readonly { }); const dataSource = sqlDataSourceService.get(this.state.editorId); - + const executionContext = dataSource?.executionContext; let connection: Connection | undefined; - if (dataSource?.executionContext) { - connection = this.connectionInfoResource.get({ - projectId: dataSource.executionContext.projectId, - connectionId: dataSource.executionContext.connectionId, - }); + if (executionContext) { + connection = this.connectionInfoResource.get(createConnectionParam(executionContext.projectId, executionContext.connectionId)); } const name = getSqlEditorName(this.state, dataSource, connection); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx index 1d982a32a1..779b2b245a 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx @@ -12,7 +12,6 @@ import styled, { css } from 'reshadow'; import { Button, getComputed, - Loader, Overlay, OverlayActions, OverlayHeader, @@ -20,10 +19,7 @@ import { OverlayHeaderSubTitle, OverlayHeaderTitle, OverlayMessage, - useExecutor, useResource, - useSplitUserState, - useStyles, useTranslate, } from '@cloudbeaver/core-blocks'; import { @@ -39,7 +35,6 @@ import { NodeManagerUtils } from '@cloudbeaver/core-navigation-tree'; import type { ISqlEditorTabState } from './ISqlEditorTabState'; import { SqlDataSourceService } from './SqlDataSource/SqlDataSourceService'; import { SqlEditorService } from './SqlEditorService'; -import { useDataSource } from './useDataSource'; const viewerStyles = css` OverlayActions { @@ -57,12 +52,12 @@ export const SqlEditorOverlay = observer(function SqlEditorOverlay({ stat const sqlDataSourceService = useService(SqlDataSourceService); const dataSource = sqlDataSourceService.get(state.editorId); const executionContextId = dataSource?.executionContext?.id; + const executionContext = dataSource?.executionContext; - useDataSource(dataSource); const connection = useResource( SqlEditorOverlay, ConnectionInfoResource, - dataSource?.executionContext ? createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId) : null, + executionContext ? createConnectionParam(executionContext.projectId, executionContext.connectionId) : null, ); const driver = useResource(SqlEditorOverlay, DBDriverResource, connection.tryGetData?.driverId ?? null); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts index fc50692cfe..07d65961e8 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts @@ -118,13 +118,11 @@ export class SqlEditorService { getName(tabState: ISqlEditorTabState): string { const dataSource = this.sqlDataSourceService.get(tabState.editorId); + const executionContext = dataSource?.executionContext; let connection: Connection | undefined; - if (dataSource?.executionContext) { - connection = this.connectionInfoResource.get({ - projectId: dataSource.executionContext.projectId, - connectionId: dataSource.executionContext.connectionId, - }); + if (executionContext) { + connection = this.connectionInfoResource.get(createConnectionParam(executionContext.projectId, executionContext.connectionId)); } return getSqlEditorName(tabState, dataSource, connection); @@ -186,21 +184,22 @@ export class SqlEditorService { return this.sqlDataSourceService.executeAction( state.editorId, async dataSource => { - if (!dataSource.executionContext) { + const executionContext = dataSource?.executionContext; + if (!executionContext) { console.error('executeEditorQuery executionContext is not provided'); return; } - await this.connectionExecutionContextResource.load(ConnectionExecutionContextProjectKey(dataSource.executionContext.projectId)); + await this.connectionExecutionContextResource.load(ConnectionExecutionContextProjectKey(executionContext.projectId)); - if (this.connectionExecutionContextResource.has(dataSource.executionContext.id)) { - return this.connectionExecutionContextService.get(dataSource.executionContext.id); + if (this.connectionExecutionContextResource.has(executionContext.id)) { + return this.connectionExecutionContextService.get(executionContext.id); } const context = await this.initContext( - createConnectionParam(dataSource.executionContext.projectId, dataSource.executionContext.connectionId), - dataSource.executionContext.defaultCatalog, - dataSource.executionContext.defaultSchema, + createConnectionParam(executionContext.projectId, executionContext.connectionId), + executionContext.defaultCatalog, + executionContext.defaultSchema, ); if (!context?.context) { diff --git a/webapp/packages/plugin-sql-generator/src/ScriptPreview/ScriptPreviewService.ts b/webapp/packages/plugin-sql-generator/src/ScriptPreview/ScriptPreviewService.ts index 5bc6c41a16..6fff8baaa2 100644 --- a/webapp/packages/plugin-sql-generator/src/ScriptPreview/ScriptPreviewService.ts +++ b/webapp/packages/plugin-sql-generator/src/ScriptPreview/ScriptPreviewService.ts @@ -35,7 +35,7 @@ export class ScriptPreviewService { } private async tryGetScript(model: IDatabaseDataModel, resultIndex: number): Promise { - const executionContext = model.source.executionContext; + const executionContext = model.source.executionContext?.context; if (!executionContext) { throw new Error('Execution context is not provided'); @@ -48,9 +48,9 @@ export class ScriptPreviewService { } const updateVariables: UpdateResultsDataBatchScriptMutationVariables = { - projectId: executionContext.context!.projectId, - connectionId: executionContext.context!.connectionId, - contextId: executionContext.context!.id, + projectId: executionContext.projectId, + connectionId: executionContext.connectionId, + contextId: executionContext.id, resultsId: result.id, }; let editor: ResultSetEditAction | DocumentEditAction | undefined;