mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-29 19:19:39 +08:00
145 lines
4.3 KiB
TypeScript
145 lines
4.3 KiB
TypeScript
/*
|
|
* CloudBeaver - Cloud Database Manager
|
|
* Copyright (C) 2020-2021 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 { makeObservable, observable } from 'mobx';
|
|
|
|
import { injectable } from '@cloudbeaver/core-di';
|
|
import { NotificationService } from '@cloudbeaver/core-events';
|
|
import { GraphQLService, SqlExecutionPlan } from '@cloudbeaver/core-sdk';
|
|
import { EDeferredState, uuid } from '@cloudbeaver/core-utils';
|
|
|
|
import type { ISqlEditorTabState } from '../../ISqlEditorTabState';
|
|
import { SqlDialectInfoService } from '../../SqlDialectInfoService';
|
|
import type { SqlExecutionState } from '../../SqlExecutionState';
|
|
import { SQLExecutionPlanProcess } from './SQLExecutionPlanProcess';
|
|
|
|
interface IExecutionPlanData {
|
|
process: SQLExecutionPlanProcess;
|
|
executionPlan: SqlExecutionPlan | null;
|
|
}
|
|
|
|
@injectable()
|
|
export class SqlExecutionPlanService {
|
|
data: Map<string, IExecutionPlanData>;
|
|
|
|
constructor(
|
|
private sqlDialectInfoService: SqlDialectInfoService,
|
|
private graphQLService: GraphQLService,
|
|
private notificationService: NotificationService,
|
|
) {
|
|
makeObservable(this, {
|
|
data: observable,
|
|
});
|
|
this.data = new Map();
|
|
}
|
|
|
|
async executeExecutionPlan(
|
|
executionState: SqlExecutionState,
|
|
editorState: ISqlEditorTabState,
|
|
query: string,
|
|
): Promise<void> {
|
|
if (!editorState.executionContext) {
|
|
console.error('executeEditorQuery executionContext is not provided');
|
|
return;
|
|
}
|
|
|
|
const tabId = this.createExecutionPlanTab(editorState);
|
|
const subQuery = await this.getSubQuery(editorState.executionContext.connectionId, query);
|
|
const task = new SQLExecutionPlanProcess(this.graphQLService, this.notificationService);
|
|
this.data.set(tabId, {
|
|
process: task,
|
|
executionPlan: null,
|
|
});
|
|
|
|
try {
|
|
executionState.setExecutionTask(task);
|
|
editorState.currentTabId = tabId;
|
|
|
|
await task.start(
|
|
subQuery,
|
|
{
|
|
connectionId: editorState.executionContext.connectionId,
|
|
contextId: editorState.executionContext.contextId,
|
|
}
|
|
);
|
|
|
|
const executionPlan = await task.promise;
|
|
this.data.set(tabId, {
|
|
process: task,
|
|
executionPlan,
|
|
});
|
|
} catch (exception) {
|
|
const message = task.getState() === EDeferredState.CANCELLED ? 'Execution plan process has been canceled' : undefined;
|
|
this.notificationService.logException(exception, 'Execution plan Error', message);
|
|
this.removeTab(editorState, tabId);
|
|
}
|
|
}
|
|
|
|
async getSubQuery(connectionId: string, query: string): Promise<string> {
|
|
const dialectInfo = await this.sqlDialectInfoService.loadSqlDialectInfo(connectionId);
|
|
|
|
if (dialectInfo?.scriptDelimiter && query.endsWith(dialectInfo?.scriptDelimiter)) {
|
|
return query.slice(0, query.length - dialectInfo.scriptDelimiter.length);
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
private removeTab(state: ISqlEditorTabState, tabId: string) {
|
|
const tab = state.tabs.find(tab => tab.id === tabId);
|
|
if (tab) {
|
|
state.tabs.splice(state.tabs.indexOf(tab), 1);
|
|
}
|
|
this.removeExecutionPlanTab(state, tabId);
|
|
|
|
if (state.tabs.length > 0) {
|
|
state.currentTabId = state.tabs[0].id;
|
|
} else {
|
|
state.currentTabId = '';
|
|
}
|
|
}
|
|
|
|
removeExecutionPlanTab(state: ISqlEditorTabState, tabId: string): void {
|
|
const executionPlanTab = state.executionPlanTabs.find(executionPlanTab => executionPlanTab.tabId === tabId);
|
|
|
|
if (executionPlanTab) {
|
|
state.executionPlanTabs.splice(state.executionPlanTabs.indexOf(executionPlanTab), 1);
|
|
}
|
|
|
|
this.data.delete(tabId);
|
|
}
|
|
|
|
private createExecutionPlanTab(state: ISqlEditorTabState) {
|
|
if (!state.executionContext) {
|
|
throw new Error('ExecutionContext is not provided');
|
|
}
|
|
|
|
const id = uuid();
|
|
const order = Math.max(0, ...state.tabs.map(tab => tab.order + 1));
|
|
const nameOrder = Math.max(1, ...state.executionPlanTabs.map(tab => tab.order + 1));
|
|
|
|
state.executionPlanTabs.push({
|
|
tabId: id,
|
|
executionContext: {
|
|
connectionId: state.executionContext.connectionId,
|
|
contextId: state.executionContext.contextId,
|
|
},
|
|
order: nameOrder,
|
|
});
|
|
|
|
state.tabs.push({
|
|
id,
|
|
name: `Execution plan - ${nameOrder}`,
|
|
icon: 'execution-plan-tab',
|
|
order,
|
|
});
|
|
|
|
return id;
|
|
}
|
|
}
|