Adding execution plan component to model view. (#23620)

This commit is contained in:
Aasim Khan
2023-07-05 13:33:28 -07:00
committed by GitHub
parent 373dc5a366
commit 28e59f44e2
21 changed files with 175 additions and 21 deletions

View File

@@ -703,6 +703,25 @@ declare module 'azdata' {
headerFilter?: boolean,
}
export type ExecutionPlanData = executionPlan.ExecutionPlanGraphInfo | executionPlan.ExecutionPlanGraph[];
export interface ExecutionPlanComponentProperties extends ComponentProperties {
/**
* Provide the execution plan file to be displayed. In case of execution plan graph info, the file type will determine the provider to be used to generate execution plan graphs
*/
data?: ExecutionPlanData;
}
/**
* Defines the executionPlan component
*/
export interface ExecutionPlanComponent extends Component, ExecutionPlanComponentProperties {
}
export interface ModelBuilder {
executionPlan(): ComponentBuilder<ExecutionPlanComponent, ExecutionPlanComponentProperties>;
}
export interface ListViewOption {
/**
* The optional accessibility label for the column. Default is the label for the list view option.

View File

@@ -150,5 +150,6 @@ export enum ModelComponentTypes {
Separator,
PropertiesContainer,
InfoBox,
Slider
Slider,
ExecutionPlan
}

View File

@@ -281,6 +281,14 @@ class ModelBuilderImpl implements azdata.ModelBuilder {
return builder;
}
executionPlan(): azdata.ComponentBuilder<azdata.ExecutionPlanComponent, azdata.ExecutionPlanComponentProperties> {
const id = this.getNextComponentId();
const builder: ComponentBuilderImpl<azdata.ExecutionPlanComponent, azdata.ExecutionPlanComponentProperties> = this.getComponentBuilder(new ExecutionPlanComponentWrapper(this._proxy, this._handle, id, this.logService), id);
this._componentBuilders.set(id, builder);
return builder;
}
getComponentBuilder<T extends azdata.Component, TPropertyBag extends azdata.ComponentProperties>(component: ComponentWrapper, id: string): ComponentBuilderImpl<T, TPropertyBag> {
let componentBuilder: ComponentBuilderImpl<T, TPropertyBag> = new ComponentBuilderImpl<T, TPropertyBag>(component);
this._componentBuilders.set(id, componentBuilder);
@@ -2226,6 +2234,21 @@ class SliderComponentWrapper extends ComponentWrapper implements azdata.SliderCo
}
}
class ExecutionPlanComponentWrapper extends ComponentWrapper implements azdata.ExecutionPlanComponent {
constructor(proxy: MainThreadModelViewShape, handle: number, id: string, logService: ILogService) {
super(proxy, handle, ModelComponentTypes.ExecutionPlan, id, logService);
this.properties = {};
}
public get data(): azdata.ExecutionPlanData {
return this.properties['data'];
}
public set data(v: azdata.ExecutionPlanData) {
this.setProperty('data', v);
}
}
class GroupContainerComponentWrapper extends ComponentWrapper implements azdata.GroupContainer {
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string, logService: ILogService) {
super(proxy, handle, type, id, logService);

View File

@@ -180,7 +180,8 @@ export enum ModelComponentTypes {
Separator,
PropertiesContainer,
InfoBox,
Slider
Slider,
ExecutionPlan
}
export enum ModelViewAction {

View File

@@ -0,0 +1,2 @@
<div #executionPlanContainer style="width: 100%; height: 100%; overflow: scroll"></div>

View File

@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ContainerBase } from 'sql/workbench/browser/modelComponents/componentBase';
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Inject, Input, OnDestroy, ViewChild } from '@angular/core';
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
import { ILogService } from 'vs/platform/log/common/log';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ExecutionPlanFileView } from 'sql/workbench/contrib/executionPlan/browser/executionPlanFileView';
import { equals } from 'vs/base/common/objects';
import * as DOM from 'vs/base/browser/dom';
@Component({
selector: 'modelview-executionplan',
templateUrl: decodeURI(require.toUrl('./executionPlan.component.html'))
})
export default class ExecutionPlanComponent extends ContainerBase<any, azdata.ExecutionPlanComponentProperties> implements
IComponent, OnDestroy, AfterViewInit {
//no-op
override ngAfterViewInit(): void {
}
//no-op
override setLayout(layout: any): void {
}
@Input() descriptor: IComponentDescriptor;
@Input() modelStore: IModelStore;
@ViewChild('executionPlanContainer') private _container: ElementRef;
private _data: azdata.ExecutionPlanData | undefined;
private _executionPlanFileView: ExecutionPlanFileView;
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(ILogService) logService: ILogService,
@Inject(IInstantiationService) private _instantiationService: IInstantiationService) {
super(changeRef, el, logService);
}
ngOnInit(): void {
this.baseInit();
this._executionPlanFileView = this._instantiationService.createInstance(ExecutionPlanFileView, undefined);
this._executionPlanFileView.render(this._container.nativeElement);
if (this._data) {
this.loadData(this._data);
}
}
override ngOnDestroy(): void {
this.baseDestroy();
this._executionPlanFileView.dispose();
this._data = undefined;
}
public override setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
if (properties.data) {
if (equals(this._data, properties.data) === false) {
this._data = properties.data;
this.clearExecutionPlan();
this.loadData(this._data);
}
}
}
public clearExecutionPlan(): void {
if (this._executionPlanFileView) {
DOM.clearNode(this._container.nativeElement);
this._executionPlanFileView.dispose();
this._executionPlanFileView = this._instantiationService.createInstance(ExecutionPlanFileView, undefined);
this._executionPlanFileView.render(this._container.nativeElement);
}
}
public loadData(data: azdata.ExecutionPlanData): void {
if (this._executionPlanFileView) {
if ((<azdata.executionPlan.ExecutionPlanGraphInfo>this._data).graphFileContent !== undefined) {
this._executionPlanFileView.loadGraphFile(<azdata.executionPlan.ExecutionPlanGraphInfo>this._data);
} else {
this._executionPlanFileView.addGraphs(<azdata.executionPlan.ExecutionPlanGraph[]>this._data);
}
}
}
}

View File

@@ -497,6 +497,14 @@ export class AzdataGraphView extends Disposable {
public disableNodeCollapse(disable: boolean): void {
this._diagram.disableNodeCollapse(disable);
}
public override dispose(): void {
super.dispose();
if (this._diagram) {
this._diagram.graph.destroy();
this._diagram = null;
}
}
}
export interface InternalExecutionPlanEdge extends azdata.executionPlan.ExecutionPlanEdge {

View File

@@ -28,7 +28,7 @@ export class ExecutionPlanFileView extends Disposable {
private _planCache: Map<string, azdata.executionPlan.ExecutionPlanGraph[]> = new Map();
constructor(
private _queryResultsView: QueryResultsView,
private _queryResultsView: QueryResultsView | undefined,
@IInstantiationService private instantiationService: IInstantiationService,
@IExecutionPlanService private executionPlanService: IExecutionPlanService
) {

View File

@@ -73,7 +73,7 @@ However we always want it to be the width of the container it is resizing.
}
.eps-container .execution-plan .plan .plan-action-container .search-node-widget .property-name-label {
flex: 0 60px;
flex: 0 80px;
line-height: 26px
}

View File

@@ -36,6 +36,7 @@ import PropertiesContainerComponent from 'sql/workbench/browser/modelComponents/
import ListViewComponent from 'sql/workbench/browser/modelComponents/listView.component';
import InfoBoxComponent from 'sql/workbench/browser/modelComponents/infoBox.component';
import SliderComponent from 'sql/workbench/browser/modelComponents/slider.component';
import ExecutionPlanComponent from 'sql/workbench/browser/modelComponents/executionPlan.component';
export const DIV_CONTAINER = 'div-container';
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
@@ -130,3 +131,6 @@ registerComponentType(INFOBOX_COMPONENT, ModelComponentTypes.InfoBox, InfoBoxCom
export const SLIDER_COMPONENT = 'slider-component';
registerComponentType(SLIDER_COMPONENT, ModelComponentTypes.Slider, SliderComponent);
export const EXECUTION_PLAN_COMPONENT = 'executionplan-component';
registerComponentType(EXECUTION_PLAN_COMPONENT, ModelComponentTypes.ExecutionPlan, ExecutionPlanComponent);