diff --git a/extensions/dacpac/src/test/testContext.ts b/extensions/dacpac/src/test/testContext.ts index ce8e0b1da37..da464cd9649 100644 --- a/extensions/dacpac/src/test/testContext.ts +++ b/extensions/dacpac/src/test/testContext.ts @@ -317,7 +317,8 @@ export function createViewContext(): ViewTestContext { separator: undefined!, propertiesContainer: undefined!, infoBox: undefined!, - slider: undefined! + slider: undefined!, + executionPlan: undefined!, } }; return { diff --git a/extensions/datavirtualization/src/test/stubs.ts b/extensions/datavirtualization/src/test/stubs.ts index 14d9cd17470..7447b523500 100644 --- a/extensions/datavirtualization/src/test/stubs.ts +++ b/extensions/datavirtualization/src/test/stubs.ts @@ -480,6 +480,9 @@ export class MockComponentBuilder implements azdata.ComponentBuilder { } export class MockModelBuilder implements azdata.ModelBuilder { + executionPlan(): azdata.ComponentBuilder { + throw new Error('Method not implemented.'); + } infoBox(): azdata.ComponentBuilder { throw new Error('Method not implemented.'); } diff --git a/extensions/machine-learning/src/test/views/utils.ts b/extensions/machine-learning/src/test/views/utils.ts index 9888e94c32b..e32a3c7b38b 100644 --- a/extensions/machine-learning/src/test/views/utils.ts +++ b/extensions/machine-learning/src/test/views/utils.ts @@ -264,7 +264,8 @@ export function createViewContext(): ViewTestContext { separator: undefined!, propertiesContainer: undefined!, infoBox: undefined!, - slider: undefined! + slider: undefined!, + executionPlan: undefined!, } }; let tab: azdata.window.DialogTab = { diff --git a/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts index 4ac738a6624..f0774d5b222 100644 --- a/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts +++ b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts @@ -303,7 +303,8 @@ describe('Manage Package Dialog', () => { separator: undefined!, propertiesContainer: undefined!, infoBox: undefined!, - slider: undefined! + slider: undefined!, + executionPlan: undefined!, } }; diff --git a/extensions/schema-compare/src/test/testContext.ts b/extensions/schema-compare/src/test/testContext.ts index 30c3b12809a..33fb23bc514 100644 --- a/extensions/schema-compare/src/test/testContext.ts +++ b/extensions/schema-compare/src/test/testContext.ts @@ -359,7 +359,8 @@ export function createViewContext(): ViewTestContext { separator: undefined!, propertiesContainer: undefined!, infoBox: undefined!, - slider: undefined! + slider: undefined!, + executionPlan: undefined!, } }; return { diff --git a/package.json b/package.json index 5bafed1e69f..f8ac88e0012 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azuredatastudio", "version": "1.45.0", - "distro": "8edcf26f189f3a51a08760d4ba37474efd402fa4", + "distro": "5b5e4db7e6626e7ef71996bed02cd46a060ca0c2", "author": { "name": "Microsoft Corporation" }, @@ -84,7 +84,7 @@ "angular2-grid": "2.0.6", "ansi_up": "^5.1.0", "applicationinsights": "1.4.2", - "azdataGraph": "github:Microsoft/azdataGraph#0.0.58", + "azdataGraph": "github:Microsoft/azdataGraph#0.0.59", "chart.js": "^4.3.0", "chartjs-adapter-moment": "^1.0.1", "chokidar": "3.5.1", diff --git a/remote/package.json b/remote/package.json index c7c8c5867bf..7d083a53a11 100755 --- a/remote/package.json +++ b/remote/package.json @@ -22,7 +22,7 @@ "applicationinsights": "1.4.2", "angular2-grid": "2.0.6", "ansi_up": "^5.1.0", - "azdataGraph": "github:Microsoft/azdataGraph#0.0.58", + "azdataGraph": "github:Microsoft/azdataGraph#0.0.59", "chart.js": "^4.3.0", "chartjs-adapter-moment": "^1.0.1", "cookie": "^0.4.0", diff --git a/remote/web/package.json b/remote/web/package.json index aa5834ca537..7af26f87a07 100755 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -17,7 +17,7 @@ "@vscode/vscode-languagedetection": "1.0.21", "angular2-grid": "2.0.6", "ansi_up": "^5.1.0", - "azdataGraph": "github:Microsoft/azdataGraph#0.0.58", + "azdataGraph": "github:Microsoft/azdataGraph#0.0.59", "chart.js": "^4.3.0", "chartjs-adapter-moment": "^1.0.1", "gridstack": "^3.1.3", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index a719d317076..49a8a672731 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -115,9 +115,9 @@ array-uniq@^1.0.2: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== -"azdataGraph@github:Microsoft/azdataGraph#0.0.58": - version "0.0.58" - resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/45c88554e98ed3b41cd283a672bd497af08c7ac3" +"azdataGraph@github:Microsoft/azdataGraph#0.0.59": + version "0.0.59" + resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/d8eebd0e0af55fae332a5922971c52772df7792d" chalk@^2.3.0, chalk@^2.4.1: version "2.4.2" diff --git a/remote/yarn.lock b/remote/yarn.lock index 10dc724d268..a314df59aa5 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -217,9 +217,9 @@ async-listener@^0.6.0: semver "^5.3.0" shimmer "^1.1.0" -"azdataGraph@github:Microsoft/azdataGraph#0.0.58": - version "0.0.58" - resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/45c88554e98ed3b41cd283a672bd497af08c7ac3" +"azdataGraph@github:Microsoft/azdataGraph#0.0.59": + version "0.0.59" + resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/d8eebd0e0af55fae332a5922971c52772df7792d" base64-js@^1.3.1: version "1.5.1" diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 61f658f5fc5..d9277ba130e 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -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; + } + export interface ListViewOption { /** * The optional accessibility label for the column. Default is the label for the list view option. diff --git a/src/sql/platform/dashboard/browser/interfaces.ts b/src/sql/platform/dashboard/browser/interfaces.ts index 16a7fb8da82..50a3fda8b4e 100644 --- a/src/sql/platform/dashboard/browser/interfaces.ts +++ b/src/sql/platform/dashboard/browser/interfaces.ts @@ -150,5 +150,6 @@ export enum ModelComponentTypes { Separator, PropertiesContainer, InfoBox, - Slider + Slider, + ExecutionPlan } diff --git a/src/sql/workbench/api/common/extHostModelView.ts b/src/sql/workbench/api/common/extHostModelView.ts index a50ce71daf0..fa47f3b749c 100644 --- a/src/sql/workbench/api/common/extHostModelView.ts +++ b/src/sql/workbench/api/common/extHostModelView.ts @@ -281,6 +281,14 @@ class ModelBuilderImpl implements azdata.ModelBuilder { return builder; } + executionPlan(): azdata.ComponentBuilder { + const id = this.getNextComponentId(); + const builder: ComponentBuilderImpl = this.getComponentBuilder(new ExecutionPlanComponentWrapper(this._proxy, this._handle, id, this.logService), id); + + this._componentBuilders.set(id, builder); + return builder; + } + getComponentBuilder(component: ComponentWrapper, id: string): ComponentBuilderImpl { let componentBuilder: ComponentBuilderImpl = new ComponentBuilderImpl(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); diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index 5141abaea78..ba0778d36bc 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -180,7 +180,8 @@ export enum ModelComponentTypes { Separator, PropertiesContainer, InfoBox, - Slider + Slider, + ExecutionPlan } export enum ModelViewAction { diff --git a/src/sql/workbench/browser/modelComponents/executionPlan.component.html b/src/sql/workbench/browser/modelComponents/executionPlan.component.html new file mode 100644 index 00000000000..2589de60be8 --- /dev/null +++ b/src/sql/workbench/browser/modelComponents/executionPlan.component.html @@ -0,0 +1,2 @@ +
+ diff --git a/src/sql/workbench/browser/modelComponents/executionPlan.component.ts b/src/sql/workbench/browser/modelComponents/executionPlan.component.ts new file mode 100644 index 00000000000..2fd36afdd39 --- /dev/null +++ b/src/sql/workbench/browser/modelComponents/executionPlan.component.ts @@ -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 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 ((this._data).graphFileContent !== undefined) { + this._executionPlanFileView.loadGraphFile(this._data); + } else { + this._executionPlanFileView.addGraphs(this._data); + } + } + } +} diff --git a/src/sql/workbench/contrib/executionPlan/browser/azdataGraphView.ts b/src/sql/workbench/contrib/executionPlan/browser/azdataGraphView.ts index d0c47051ef8..296ce921725 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/azdataGraphView.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/azdataGraphView.ts @@ -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 { diff --git a/src/sql/workbench/contrib/executionPlan/browser/executionPlanFileView.ts b/src/sql/workbench/contrib/executionPlan/browser/executionPlanFileView.ts index 0f2ff80743a..450ade4dd86 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/executionPlanFileView.ts +++ b/src/sql/workbench/contrib/executionPlan/browser/executionPlanFileView.ts @@ -28,7 +28,7 @@ export class ExecutionPlanFileView extends Disposable { private _planCache: Map = new Map(); constructor( - private _queryResultsView: QueryResultsView, + private _queryResultsView: QueryResultsView | undefined, @IInstantiationService private instantiationService: IInstantiationService, @IExecutionPlanService private executionPlanService: IExecutionPlanService ) { diff --git a/src/sql/workbench/contrib/executionPlan/browser/media/executionPlan.css b/src/sql/workbench/contrib/executionPlan/browser/media/executionPlan.css index e46c010191c..64fa8ed09d5 100644 --- a/src/sql/workbench/contrib/executionPlan/browser/media/executionPlan.css +++ b/src/sql/workbench/contrib/executionPlan/browser/media/executionPlan.css @@ -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 } diff --git a/src/sql/workbench/contrib/modelView/browser/components.contribution.ts b/src/sql/workbench/contrib/modelView/browser/components.contribution.ts index 3f50424a2b7..316b6e810af 100644 --- a/src/sql/workbench/contrib/modelView/browser/components.contribution.ts +++ b/src/sql/workbench/contrib/modelView/browser/components.contribution.ts @@ -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); diff --git a/yarn.lock b/yarn.lock index b2a172b7547..6596f6de147 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2149,9 +2149,9 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -"azdataGraph@github:Microsoft/azdataGraph#0.0.58": - version "0.0.58" - resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/45c88554e98ed3b41cd283a672bd497af08c7ac3" +"azdataGraph@github:Microsoft/azdataGraph#0.0.59": + version "0.0.59" + resolved "https://codeload.github.com/Microsoft/azdataGraph/tar.gz/d8eebd0e0af55fae332a5922971c52772df7792d" bach@^1.0.0: version "1.2.0"