mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
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 <serge@jkiss.org> Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
This commit is contained in:
co-authored by
Serge Rider
EvgeniaBzzz
Daria Marutkina
parent
773edfc9c0
commit
f1507c3a7d
+27
-1
@@ -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<String, Object> savedAuthProperties;
|
||||
private transient List<WebNetworkHandlerConfigInput> savedNetworkCredentials;
|
||||
private transient List<DBRRunnableParametrized<WebConnectionInfo>> 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<WebConnectionInfo> 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<WebConnectionInfo> listener) {
|
||||
if (closeListeners == null) {
|
||||
closeListeners = new ArrayList<>();
|
||||
}
|
||||
closeListeners.add(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-1
@@ -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());
|
||||
}
|
||||
|
||||
+6
-3
@@ -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;
|
||||
|
||||
+1
-2
@@ -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<String, WebSQLContextInfo> contexts = new LinkedHashMap<>();
|
||||
|
||||
private AtomicInteger contextId = new AtomicInteger();
|
||||
private final AtomicInteger contextId = new AtomicInteger();
|
||||
|
||||
WebSQLProcessor(@NotNull WebSession webSession, @NotNull WebConnectionInfo connection) {
|
||||
this.webSession = webSession;
|
||||
|
||||
+4
@@ -258,6 +258,9 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL> i
|
||||
private static class WebSQLConfiguration {
|
||||
private final Map<WebConnectionInfo, WebSQLProcessor> 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<DBWServiceSQL> 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;
|
||||
|
||||
@@ -40,10 +40,7 @@ export class AppAuthService extends Bootstrap {
|
||||
resource: CachedResource<any, any, T, any, any>,
|
||||
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<T>(resource, map as any);
|
||||
|
||||
|
||||
@@ -163,11 +163,9 @@ export class UserInfoResource extends CachedDataResource<UserInfo | null, void,
|
||||
configuration,
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
this.resetIncludes();
|
||||
this.markOutdated();
|
||||
this.sessionDataResource.markOutdated();
|
||||
});
|
||||
this.resetIncludes();
|
||||
this.setData(await this.loader());
|
||||
this.sessionDataResource.markOutdated();
|
||||
}
|
||||
|
||||
async setConfigurationParameter(key: string, value: any): Promise<UserInfo | null> {
|
||||
|
||||
@@ -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]();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-2
@@ -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 {
|
||||
|
||||
+10
-14
@@ -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<string, IConnectionExecutionContextInfo> {
|
||||
constructor(
|
||||
private readonly graphQLService: GraphQLService,
|
||||
private readonly connectionInfoResource: ConnectionInfoResource,
|
||||
userInfoResource: UserInfoResource,
|
||||
appAuthService: AppAuthService,
|
||||
) {
|
||||
super();
|
||||
@@ -55,6 +59,9 @@ export class ConnectionExecutionContextResource extends CachedMapResource<string
|
||||
|
||||
appAuthService.requireAuthentication(this);
|
||||
|
||||
userInfoResource.onUserChange.addHandler(() => {
|
||||
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<string
|
||||
});
|
||||
}
|
||||
|
||||
async refreshAll(): Promise<IConnectionExecutionContextInfo[]> {
|
||||
this.resetIncludes();
|
||||
await this.refresh(CachedMapAllKey);
|
||||
return this.values;
|
||||
}
|
||||
|
||||
refreshAllLazy(): void {
|
||||
this.resetIncludes();
|
||||
this.markOutdated(CachedMapAllKey);
|
||||
}
|
||||
|
||||
protected async loader(originalKey: ResourceKey<string>): Promise<Map<string, IConnectionExecutionContextInfo>> {
|
||||
const contextsList: IConnectionExecutionContextInfo[] = [];
|
||||
let projectId: string | undefined;
|
||||
@@ -218,7 +214,7 @@ export class ConnectionExecutionContextResource extends CachedMapResource<string
|
||||
}
|
||||
}
|
||||
|
||||
function getBaseContext(context: SqlContextInfo): IConnectionExecutionContextInfo {
|
||||
function getBaseContext(context: IConnectionExecutionContextInfo): IConnectionExecutionContextInfo {
|
||||
return {
|
||||
...context,
|
||||
};
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import type { ITask } from '@cloudbeaver/core-executor';
|
||||
|
||||
import type { IConnectionExecutionContextInfo } from './IConnectionExecutionContextInfo';
|
||||
import type { IConnectionExecutionContextInfo } from './ConnectionExecutionContextResource';
|
||||
|
||||
export interface IConnectionExecutionContext {
|
||||
readonly context: IConnectionExecutionContextInfo | undefined;
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import type { SqlContextInfo } from '@cloudbeaver/core-sdk';
|
||||
|
||||
export type IConnectionExecutionContextInfo = SqlContextInfo & {
|
||||
defaultCatalog?: string | null;
|
||||
defaultSchema?: string | null;
|
||||
};
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { action, makeObservable, observable, runInAction } from 'mobx';
|
||||
|
||||
import { AppAuthService } from '@cloudbeaver/core-authentication';
|
||||
import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { ExecutorInterrupter, ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
|
||||
import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
|
||||
@@ -88,6 +88,7 @@ export class ConnectionInfoResource extends CachedMapResource<IConnectionInfoPar
|
||||
sessionDataResource: SessionDataResource,
|
||||
appAuthService: AppAuthService,
|
||||
connectionInfoEventHandler: ConnectionInfoEventHandler,
|
||||
userInfoResource: UserInfoResource,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -109,6 +110,9 @@ export class ConnectionInfoResource extends CachedMapResource<IConnectionInfoPar
|
||||
this.onItemDelete.addHandler(ExecutorInterrupter.interrupter(() => this.sessionUpdate));
|
||||
this.onConnectionCreate.addHandler(ExecutorInterrupter.interrupter(() => this.sessionUpdate));
|
||||
|
||||
userInfoResource.onUserChange.addHandler(() => {
|
||||
this.clear();
|
||||
});
|
||||
appAuthService.requireAuthentication(this);
|
||||
this.sync(
|
||||
this.projectInfoResource,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -49,7 +49,7 @@ export const ContextMenu = observer<IContextMenuProps, HTMLButtonElement>(
|
||||
|
||||
const menu = useRef<IMenuState>();
|
||||
|
||||
useAutoLoad(menuData.loaders, !lazy, menuVisible);
|
||||
useAutoLoad({ name: `${ContextMenu.name}(${menuData.menu.id})` }, menuData.loaders, !lazy, menuVisible);
|
||||
|
||||
const handlers = useObjectRef(
|
||||
() => ({
|
||||
|
||||
@@ -51,7 +51,7 @@ export const MenuBar = observer<IMenuBarProps, HTMLDivElement>(
|
||||
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;
|
||||
|
||||
@@ -32,7 +32,7 @@ export const SubMenuElement = observer<ISubMenuElementProps, HTMLButtonElement>(
|
||||
|
||||
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(
|
||||
() => ({
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ export const GrantedConnections: TabContainerPanelComponent<ITeamFormProps> = 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;
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ export const GrantedUsers: TabContainerPanelComponent<ITeamFormProps> = observer
|
||||
users.data.filter<AdminUser>((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;
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ export const OriginInfoPanel: TabContainerPanelComponent<IUserFormProps> = obser
|
||||
|
||||
const { selected } = useTab(tabId);
|
||||
|
||||
useAutoLoad(loadableState, selected);
|
||||
useAutoLoad(OriginInfoPanel, loadableState, selected);
|
||||
|
||||
if (!selected) {
|
||||
return null;
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const ConnectionAccess: TabContainerPanelComponent<IConnectionFormProps>
|
||||
|
||||
const { selected } = useTab(tabId);
|
||||
|
||||
useAutoLoad(state, selected);
|
||||
useAutoLoad(ConnectionAccess, state, selected);
|
||||
|
||||
const users = useResource(ConnectionAccess, UsersResource, CachedResourcePageListKey(0, 1000).setTarget(UsersResourceFilterKey()), {
|
||||
active: selected,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -79,6 +79,7 @@ export class ContainerDataSource extends DatabaseDataSource<IDataContainerOption
|
||||
}
|
||||
|
||||
const executionContext = await this.ensureContextCreated();
|
||||
const context = executionContext.context!;
|
||||
const offset = this.offset;
|
||||
const limit = this.count;
|
||||
|
||||
@@ -86,8 +87,8 @@ export class ContainerDataSource extends DatabaseDataSource<IDataContainerOption
|
||||
|
||||
if (
|
||||
prevResults.length === 1 &&
|
||||
prevResults[0].contextId === executionContext.context!.id &&
|
||||
prevResults[0].connectionId === executionContext.context?.connectionId &&
|
||||
prevResults[0].contextId === context.id &&
|
||||
prevResults[0].connectionId === context.connectionId &&
|
||||
prevResults[0].id !== null
|
||||
) {
|
||||
firstResultId = prevResults[0].id;
|
||||
@@ -95,8 +96,8 @@ export class ContainerDataSource extends DatabaseDataSource<IDataContainerOption
|
||||
|
||||
const task = this.asyncTaskInfoService.create(async () => {
|
||||
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: {
|
||||
|
||||
@@ -140,7 +140,7 @@ export class NavigationTabsService extends View<ITab> {
|
||||
currentTab: computed,
|
||||
currentTabId: computed,
|
||||
tabIdList: computed<string[]>({
|
||||
equals: isArraysEqual,
|
||||
equals: (a, b) => isArraysEqual(a, b, undefined, true),
|
||||
}),
|
||||
openTab: action,
|
||||
selectTab: action,
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ export const NavigationTreeFiltersDialog = observer<DialogComponentProps<Payload
|
||||
const navTreeResource = useService(NavTreeResource);
|
||||
const state = useFilters(payload.node.id);
|
||||
|
||||
useAutoLoad(state);
|
||||
useAutoLoad(NavigationTreeFiltersDialog, state);
|
||||
|
||||
async function submit() {
|
||||
try {
|
||||
|
||||
+9
-7
@@ -17,7 +17,7 @@ import { TaskScheduler } from '@cloudbeaver/core-executor';
|
||||
import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { getRmResourceKey, ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
|
||||
import { isResourceAlias, ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-sdk';
|
||||
import { debounce, getPathName, isArraysEqual, isObjectsEqual, isValuesEqual } from '@cloudbeaver/core-utils';
|
||||
import { debounce, getPathName, isArraysEqual, isNotNullDefined, isObjectsEqual, isValuesEqual } from '@cloudbeaver/core-utils';
|
||||
import { SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts';
|
||||
import { BaseSqlDataSource, ESqlDataSourceFeatures, SqlEditorService } from '@cloudbeaver/plugin-sql-editor';
|
||||
|
||||
@@ -75,10 +75,8 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
}
|
||||
|
||||
get executionContext(): IConnectionExecutionContextInfo | undefined {
|
||||
if (
|
||||
this.state.executionContext &&
|
||||
!this.connectionInfoResource.has(createConnectionParam(this.state.executionContext.projectId, this.state.executionContext.connectionId))
|
||||
) {
|
||||
const executionContext = this.state?.executionContext;
|
||||
if (!executionContext || !this.connectionInfoResource.has(createConnectionParam(executionContext.projectId, executionContext.connectionId))) {
|
||||
return undefined;
|
||||
}
|
||||
return this.state.executionContext;
|
||||
@@ -228,9 +226,13 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
|
||||
}
|
||||
|
||||
setExecutionContext(executionContext: IConnectionExecutionContextInfo | undefined): void {
|
||||
executionContext = JSON.parse(JSON.stringify(toJS(executionContext) ?? {}));
|
||||
if (executionContext) {
|
||||
executionContext = JSON.parse(JSON.stringify(toJS(executionContext) ?? {}));
|
||||
}
|
||||
|
||||
if (this.resourceKey && executionContext?.projectId && getRmResourceKey(this.resourceKey).projectId !== executionContext.projectId) {
|
||||
const projectId = executionContext?.projectId;
|
||||
|
||||
if (this.resourceKey && isNotNullDefined(projectId) && getRmResourceKey(this.resourceKey).projectId !== projectId) {
|
||||
throw new Error('Resource SQL Data Source and Execution context projects don\t match');
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ export class SqlEditorBootstrap extends Bootstrap {
|
||||
case ACTION_RENAME: {
|
||||
const state = context.get(DATA_CONTEXT_SQL_EDITOR_STATE);
|
||||
const dataSource = this.sqlDataSourceService.get(state.editorId);
|
||||
const executionContext = dataSource?.executionContext;
|
||||
|
||||
if (!dataSource) {
|
||||
return;
|
||||
@@ -131,11 +132,8 @@ export class SqlEditorBootstrap extends Bootstrap {
|
||||
|
||||
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(state, dataSource, connection);
|
||||
|
||||
@@ -101,13 +101,14 @@ export class SqlEditorNavigatorService {
|
||||
tab = this.navigationTabsService.findTab(
|
||||
isSQLEditorTab(tab => {
|
||||
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,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -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<ISqlEditorTabState> = 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);
|
||||
|
||||
@@ -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<ISqlEditorTabState>) {
|
||||
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<ISqlEditorTabState>): 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<ISqlEditorTabState>) {
|
||||
@@ -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<ISqlEditorTabState>) {
|
||||
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<ISqlEditorTabState>) {
|
||||
const dataSource = this.sqlDataSourceService.get(editorTab.handlerState.editorId);
|
||||
|
||||
if (dataSource?.executionContext) {
|
||||
await this.sqlEditorService.destroyContext(dataSource.executionContext);
|
||||
}
|
||||
await this.sqlDataSourceService.destroy(editorTab.handlerState.editorId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
@@ -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<void> {
|
||||
@@ -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<void> {
|
||||
@@ -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,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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<State> {
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -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<Props>(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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class ScriptPreviewService {
|
||||
}
|
||||
|
||||
private async tryGetScript(model: IDatabaseDataModel, resultIndex: number): Promise<string> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user