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 c5bba65cbd..c8a54534a9 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 @@ -34,8 +34,6 @@ import io.cloudbeaver.utils.CBModelConstants; import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebDataSourceUtils; import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -284,7 +282,7 @@ public class WebSession extends BaseWebSession switch (type) { case DATASOURCE_CREATED -> { WebConnectionInfo connectionInfo = new WebConnectionInfo(this, ds); - this.connections.put(connectionInfo.getId(), connectionInfo); + this.connections.put(getConnectionId(ds), connectionInfo); sendDataSourceUpdatedEvent = true; } case DATASOURCE_UPDATED -> // if settings were changed we need to send event @@ -294,7 +292,7 @@ public class WebSession extends BaseWebSession if (registry instanceof DBPDataSourceRegistryCache dsrc) { dsrc.removeDataSourceFromList(ds); } - this.connections.remove(ds.getId()); + this.connections.remove(getConnectionId(ds)); sendDataSourceUpdatedEvent = true; } default -> { @@ -304,6 +302,16 @@ public class WebSession extends BaseWebSession return sendDataSourceUpdatedEvent; } + @NotNull + private String getConnectionId(@NotNull DBPDataSourceContainer container) { + return getConnectionId(container.getProject().getId(), container.getId()); + } + + @NotNull + private String getConnectionId(@NotNull String projectId, @NotNull String dsId) { + return projectId + ":" + dsId; + } + // Note: for admin use only public synchronized void resetUserState() throws DBException { clearAuthTokens(); @@ -474,7 +482,7 @@ public class WebSession extends BaseWebSession var registry = getProjectById(WebAppUtils.getGlobalProjectId()).getDataSourceRegistry(); var dataSource = registry.getDataSource(dsId); if (dataSource != null) { - connections.put(dsId, new WebConnectionInfo(this, dataSource)); + connections.put(getConnectionId(dataSource), new WebConnectionInfo(this, dataSource)); // reflect changes is navigator model registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_ADD, dataSource, true)); } @@ -485,7 +493,7 @@ public class WebSession extends BaseWebSession var dataSource = registry.getDataSource(dsId); if (dataSource != null) { this.accessibleConnectionIds.remove(dsId); - connections.remove(dsId); + connections.remove(getConnectionId(dataSource)); // reflect changes is navigator model registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_REMOVE, dataSource)); dataSource.dispose(); @@ -564,9 +572,21 @@ public class WebSession extends BaseWebSession @NotNull public WebConnectionInfo getWebConnectionInfo(@Nullable String projectId, String connectionID) throws DBWebException { - WebConnectionInfo connectionInfo; + WebConnectionInfo connectionInfo = null; synchronized (connections) { - connectionInfo = connections.get(connectionID); + if (projectId != null) { + connectionInfo = connections.get(getConnectionId(projectId, connectionID)); + } else { + addWarningMessage("Project id is not defined in request. Try to find it from connection cache"); + for (Map.Entry entry : connections.entrySet()) { + String k = entry.getKey(); + WebConnectionInfo v = entry.getValue(); + if (k.contains(connectionID)) { + connectionInfo = v; + break; + } + } + } } if (connectionInfo == null) { WebProjectImpl project = getProjectById(projectId); @@ -577,7 +597,7 @@ public class WebSession extends BaseWebSession if (dataSource != null) { connectionInfo = new WebConnectionInfo(this, dataSource); synchronized (connections) { - connections.put(connectionID, connectionInfo); + connections.put(getConnectionId(dataSource), connectionInfo); } } else { throw new DBWebException("Connection '" + connectionID + "' not found"); @@ -587,22 +607,22 @@ public class WebSession extends BaseWebSession } @Nullable - public WebConnectionInfo findWebConnectionInfo(String connectionID) { + public WebConnectionInfo findWebConnectionInfo(String projectId, String connectionId) { synchronized (connections) { - return connections.get(connectionID); + return connections.get(getConnectionId(projectId, connectionId)); } } public void addConnection(WebConnectionInfo connectionInfo) { synchronized (connections) { - connections.put(connectionInfo.getId(), connectionInfo); + connections.put(getConnectionId(connectionInfo.getDataSourceContainer()), connectionInfo); } } public void removeConnection(WebConnectionInfo connectionInfo) { connectionInfo.clearCache(); synchronized (connections) { - connections.remove(connectionInfo.getId()); + connections.remove(getConnectionId(connectionInfo.getDataSourceContainer())); } } @@ -929,7 +949,7 @@ public class WebSession extends BaseWebSession } configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo()); - WebConnectionInfo webConnectionInfo = findWebConnectionInfo(dataSourceContainer.getId()); + WebConnectionInfo webConnectionInfo = findWebConnectionInfo(dataSourceContainer.getProject().getId(), dataSourceContainer.getId()); if (webConnectionInfo != null) { WebDataSourceUtils.saveCredentialsInDataSource(webConnectionInfo, dataSourceContainer, configuration); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java index e71d249e58..0968876b3e 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSObjectPermissionUpdatedEventHandler.java @@ -81,7 +81,7 @@ public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler return; } if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) { - isAccessibleNow = webSession.findWebConnectionInfo(objectId) != null; + isAccessibleNow = webSession.findWebConnectionInfo(project.getId(), objectId) != null; if (isAccessibleNow) { return; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java index 39ac9240f1..ade211b040 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSUserSecretEventHandlerImpl.java @@ -40,7 +40,7 @@ public class WSUserSecretEventHandlerImpl extends WSDefaultEventHandler stream = webSession.getConnections().stream(); + if (projectId != null) { + stream = stream.filter(c -> c.getProjectId().equals(projectId)); + } if (connectionId != null) { - connectionInfo = webSession.findWebConnectionInfo(connectionId); + stream = stream.filter(c -> c.getId().equals(connectionId)); } else if (connectionName != null) { - connectionInfo = findConnection(webSession, t -> t.getName().equals(connectionName)); + stream = stream.filter(t -> t.getName().equals(connectionName)); } else if (connectionURL != null) { - connectionInfo = findConnection(webSession, t -> t.getUrl().equals(connectionURL)); - } - if (connectionInfo == null) { - throw new DBException("Connection is not found in the session"); + stream = stream.filter(t -> t.getUrl().equals(connectionURL)); } + WebConnectionInfo connectionInfo = stream.findFirst().orElseThrow(() -> new DBException("Connection is not found in the session")); WebServiceUtils.fireActionParametersOpenEditor(webSession, connectionInfo.getDataSourceContainer(), false); } - - private WebConnectionInfo findConnection(WebSession webSession, Predicate filter) { - List filteredConnections = webSession.getConnections().stream().filter(filter).collect(Collectors.toList()); - if (filteredConnections.size() != 1) { - return null; - } - return filteredConnections.get(0); - - } } diff --git a/webapp/packages/core-connections/src/ConnectionDialectResource.ts b/webapp/packages/core-connections/src/ConnectionDialectResource.ts index 9723ddd7c6..c56abbe7bb 100644 --- a/webapp/packages/core-connections/src/ConnectionDialectResource.ts +++ b/webapp/packages/core-connections/src/ConnectionDialectResource.ts @@ -44,6 +44,7 @@ export class ConnectionDialectResource extends CachedMapResource { const result = await this.graphQLService.sdk.formatSqlQuery({ + projectId: context.projectId, connectionId: context.connectionId, contextId: context.id, query, diff --git a/webapp/packages/core-sdk/src/queries/grid/asyncReadDataFromContainer.gql b/webapp/packages/core-sdk/src/queries/grid/asyncReadDataFromContainer.gql index d89081cecd..4540529ad8 100644 --- a/webapp/packages/core-sdk/src/queries/grid/asyncReadDataFromContainer.gql +++ b/webapp/packages/core-sdk/src/queries/grid/asyncReadDataFromContainer.gql @@ -1,5 +1,6 @@ mutation asyncReadDataFromContainer( + $projectId: ID! $connectionId: ID! $contextId: ID! $containerNodePath: ID! @@ -8,6 +9,7 @@ mutation asyncReadDataFromContainer( $dataFormat: ResultDataFormat ) { taskInfo: asyncReadDataFromContainer( + projectId: $projectId connectionId: $connectionId contextId: $contextId containerNodePath: $containerNodePath diff --git a/webapp/packages/core-sdk/src/queries/grid/asyncSqlExecuteQuery.gql b/webapp/packages/core-sdk/src/queries/grid/asyncSqlExecuteQuery.gql index 7aabbc989e..87a35e388e 100644 --- a/webapp/packages/core-sdk/src/queries/grid/asyncSqlExecuteQuery.gql +++ b/webapp/packages/core-sdk/src/queries/grid/asyncSqlExecuteQuery.gql @@ -1,4 +1,5 @@ mutation asyncSqlExecuteQuery( + $projectId: ID! $connectionId: ID! $contextId: ID! $query: String! @@ -8,6 +9,7 @@ mutation asyncSqlExecuteQuery( $readLogs: Boolean ) { taskInfo: asyncSqlExecuteQuery( + projectId: $projectId connectionId: $connectionId contextId: $contextId sql: $query diff --git a/webapp/packages/core-sdk/src/queries/grid/asyncSqlExplainExecutionPlan.gql b/webapp/packages/core-sdk/src/queries/grid/asyncSqlExplainExecutionPlan.gql index 1c7ea949f8..784939feb6 100644 --- a/webapp/packages/core-sdk/src/queries/grid/asyncSqlExplainExecutionPlan.gql +++ b/webapp/packages/core-sdk/src/queries/grid/asyncSqlExplainExecutionPlan.gql @@ -1,10 +1,12 @@ mutation asyncSqlExplainExecutionPlan( + $projectId: ID! $connectionId: ID! $contextId: ID! $query: String! $configuration: Object! ) { taskInfo: asyncSqlExplainExecutionPlan( + projectId: $projectId connectionId: $connectionId contextId: $contextId query: $query diff --git a/webapp/packages/core-sdk/src/queries/grid/closeResult.gql b/webapp/packages/core-sdk/src/queries/grid/closeResult.gql index bb6fc8801c..6026a69ea6 100644 --- a/webapp/packages/core-sdk/src/queries/grid/closeResult.gql +++ b/webapp/packages/core-sdk/src/queries/grid/closeResult.gql @@ -1,9 +1,11 @@ mutation closeResult( + $projectId: ID! $connectionId: ID! $contextId: ID! $resultId: ID! ) { result: sqlResultClose( + projectId: $projectId connectionId: $connectionId contextId: $contextId resultId: $resultId diff --git a/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql b/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql index 6eec6ae8e7..b1c90d95b8 100644 --- a/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql +++ b/webapp/packages/core-sdk/src/queries/grid/getResultsetDataURL.gql @@ -1,3 +1,3 @@ -mutation getResultsetDataURL($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: Int!, $row: SQLResultRow!) { - url: sqlReadLobValue(connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, lobColumnIndex: $lobColumnIndex, row: $row) +mutation getResultsetDataURL($projectId: ID!, $connectionId: ID!, $contextId: ID!, $resultsId: ID!, $lobColumnIndex: Int!, $row: SQLResultRow!) { + url: sqlReadLobValue(projectId: $projectId, connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, lobColumnIndex: $lobColumnIndex, row: $row) } diff --git a/webapp/packages/core-sdk/src/queries/sql-editor/formatSqlQuery.gql b/webapp/packages/core-sdk/src/queries/sql-editor/formatSqlQuery.gql index 5f4ca85846..ae87badc4f 100644 --- a/webapp/packages/core-sdk/src/queries/sql-editor/formatSqlQuery.gql +++ b/webapp/packages/core-sdk/src/queries/sql-editor/formatSqlQuery.gql @@ -1,9 +1,11 @@ query formatSqlQuery( + $projectId: ID! $connectionId: ID! $contextId: ID! $query: String! ) { query: sqlFormatQuery( + projectId: $projectId connectionId: $connectionId contextId: $contextId query: $query diff --git a/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLQuery.gql b/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLQuery.gql index 5610b1a4ed..16adc3a104 100644 --- a/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLQuery.gql +++ b/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLQuery.gql @@ -1,9 +1,11 @@ query parseSQLQuery( + $projectId: ID! $connectionId: ID! $script: String! $position: Int! ) { queryInfo: sqlParseQuery( + projectId: $projectId connectionId: $connectionId script: $script position: $position diff --git a/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLScript.gql b/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLScript.gql index f8f33c67fc..b171874461 100644 --- a/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLScript.gql +++ b/webapp/packages/core-sdk/src/queries/sql-editor/parseSQLScript.gql @@ -1,8 +1,10 @@ query parseSQLScript( + $projectId: ID! $connectionId: ID! $script: String! ) { scriptInfo: sqlParseScript( + projectId: $projectId connectionId: $connectionId script: $script ) { diff --git a/webapp/packages/core-sdk/src/queries/sql-editor/querySqlCompletionProposals.gql b/webapp/packages/core-sdk/src/queries/sql-editor/querySqlCompletionProposals.gql index f7e5737bea..b7647b539f 100644 --- a/webapp/packages/core-sdk/src/queries/sql-editor/querySqlCompletionProposals.gql +++ b/webapp/packages/core-sdk/src/queries/sql-editor/querySqlCompletionProposals.gql @@ -1,4 +1,5 @@ query querySqlCompletionProposals( + $projectId : ID! $connectionId: ID! $contextId: ID! $position: Int! @@ -7,6 +8,7 @@ query querySqlCompletionProposals( $maxResults: Int ) { proposals: sqlCompletionProposals( + projectId: $projectId connectionId: $connectionId contextId: $contextId query: $query diff --git a/webapp/packages/core-sdk/src/queries/sqlReadStringValue.gql b/webapp/packages/core-sdk/src/queries/sqlReadStringValue.gql index ce9b225e37..5101909fb8 100644 --- a/webapp/packages/core-sdk/src/queries/sqlReadStringValue.gql +++ b/webapp/packages/core-sdk/src/queries/sqlReadStringValue.gql @@ -1,3 +1,3 @@ -mutation sqlReadStringValue($connectionId: ID!, $contextId: ID!, $resultsId: ID!, $columnIndex: Int!, $row: SQLResultRow!) { - text: sqlReadStringValue(connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, columnIndex: $columnIndex, row: $row) +mutation sqlReadStringValue($projectId: ID!, $connectionId: ID!, $contextId: ID!, $resultsId: ID!, $columnIndex: Int!, $row: SQLResultRow!) { + text: sqlReadStringValue(projectId: $projectId, connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId, columnIndex: $columnIndex, row: $row) } diff --git a/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts b/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts index d8a6618eb0..431471c8cf 100644 --- a/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts +++ b/webapp/packages/plugin-data-viewer/src/ContainerDataSource.ts @@ -98,6 +98,7 @@ export class ContainerDataSource extends ResultSetDataSource { const { taskInfo } = await this.graphQLService.sdk.asyncReadDataFromContainer({ + projectId: context.projectId, connectionId: context.connectionId, contextId: context.id, containerNodePath: options.containerNodePath, @@ -237,6 +238,7 @@ export class ContainerDataSource extends ResultSetDataSource((result, index) => ({ id: result.resultSet?.id || '0', uniqueResultId: `${executionContextInfo.connectionId}_${executionContextInfo.id}_${index}`, + projectId: executionContextInfo.projectId, connectionId: executionContextInfo.connectionId, contextId: executionContextInfo.id, dataFormat: result.dataFormat!, diff --git a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataContentAction.ts b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataContentAction.ts index c32557fbf5..4d425571d8 100644 --- a/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataContentAction.ts +++ b/webapp/packages/plugin-data-viewer/src/DatabaseDataModel/Actions/ResultSet/ResultSetDataContentAction.ts @@ -194,6 +194,7 @@ export class ResultSetDataContentAction extends DatabaseDataAction { const { taskInfo } = await this.graphQLService.sdk.asyncSqlExecuteQuery({ + projectId: executionContextInfo.projectId, connectionId: executionContextInfo.connectionId, contextId: executionContextInfo.id, query: options.query, @@ -262,6 +263,7 @@ export class QueryDataSource((result, index) => ({ id: result.resultSet?.id || null, uniqueResultId: `${executionContextInfo.connectionId}_${executionContextInfo.id}_${index}`, + projectId: executionContextInfo.projectId, connectionId: executionContextInfo.connectionId, contextId: executionContextInfo.id, dataFormat: result.dataFormat!, diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts index 23361e73ff..1755d7dbb5 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts @@ -193,6 +193,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { } const hints = await this.sqlEditorService.getAutocomplete( + executionContext.projectId, executionContext.connectionId, executionContext.id, this.value, @@ -369,10 +370,11 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { if (!this.dataSource?.hasFeature(ESqlDataSourceFeatures.script)) { return; } + const projectId = this.dataSource.executionContext?.projectId; const connectionId = this.dataSource.executionContext?.connectionId; const script = this.parser.actualScript; - if (!connectionId || !script) { + if (!projectId || !connectionId || !script) { this.parser.setQueries([]); this.onUpdate.execute(); return; @@ -380,7 +382,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { const { queries } = await this.parseScript([connectionId, script], async () => { try { - return await this.sqlEditorService.parseSQLScript(connectionId, script); + return await this.sqlEditorService.parseSQLScript(projectId, connectionId, script); } catch (exception: any) { this.notificationService.logException(exception, 'Failed to parse SQL script'); throw exception; @@ -428,9 +430,10 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { }, async getResolvedSegment(): Promise { + const projectId = this.dataSource?.executionContext?.projectId; const connectionId = this.dataSource?.executionContext?.connectionId; - if (!connectionId || this.cursor.begin !== this.cursor.end) { + if (!projectId || !connectionId || this.cursor.begin !== this.cursor.end) { return this.getSubQuery(); } @@ -438,7 +441,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { return this.activeSegment; } - const result = await this.sqlEditorService.parseSQLQuery(connectionId, this.value, this.cursor.begin); + const result = await this.sqlEditorService.parseSQLQuery(projectId, connectionId, this.value, this.cursor.begin); const segment = this.parser.getSegment(result.start, result.end); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts index 1ea6d17d74..ec92bbcff0 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts @@ -72,8 +72,9 @@ export class SqlEditorService { }); } - async parseSQLScript(connectionId: string, script: string): Promise { + async parseSQLScript(projectId: string, connectionId: string, script: string): Promise { const result = await this.graphQLService.sdk.parseSQLScript({ + projectId, connectionId, script, }); @@ -81,8 +82,9 @@ export class SqlEditorService { return result.scriptInfo; } - async parseSQLQuery(connectionId: string, script: string, position: number) { + async parseSQLQuery(projectId: string, connectionId: string, script: string, position: number) { const result = await this.graphQLService.sdk.parseSQLQuery({ + projectId, connectionId, script, position, @@ -92,6 +94,7 @@ export class SqlEditorService { } async getAutocomplete( + projectId: string, connectionId: string, contextId: string, query: string, @@ -100,6 +103,7 @@ export class SqlEditorService { simple?: boolean, ): Promise { const { proposals } = await this.graphQLService.sdk.querySqlCompletionProposals({ + projectId, connectionId, contextId, query, diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.ts index 573d5cc041..7fcd20d77b 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.ts @@ -56,6 +56,7 @@ export class SqlExecutionPlanService { const asyncTask = this.asyncTaskInfoService.create(async () => { const { taskInfo } = await this.graphQLService.sdk.asyncSqlExplainExecutionPlan({ + projectId: contextInfo.projectId, connectionId: contextInfo.connectionId, contextId: contextInfo.id, query,